diff --git a/README.md b/README.md index 6a291106..468d9286 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ ## 平台简介 +若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。 + * 采用前后端分离的模式,微服务版本前端(基于 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue))。 * 后端采用Spring Boot、Spring Cloud & Alibaba。 -* 注册中心、配置中心选型Nacos,权限认证使用OAuth2。 -* 流量控制框架选型Sentinel。 -* 感谢[ruoyi-cloud-design](https://gitee.com/zhangmrit/ruoyi-cloud),[pig](https://gitee.com/log4j/pig)。 +* 注册中心、配置中心选型Nacos,权限认证使用Redis。 +* 流量控制框架选型Sentinel,分布式事务选型Seata。 * 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi),如需分离应用,请移步 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) -* 阿里云优惠券:[点我进入](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)   +* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)   +* 阿里云优惠券:[点我领取](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-Cloud](https://gitee.com/zhangmrit/ruoyi-cloud) Ant Design版本。 ## 系统模块 @@ -21,6 +24,7 @@ com.ruoyi ├── ruoyi-common // 通用模块 │ └── ruoyi-common-core // 核心模块 │ └── ruoyi-common-datascope // 权限范围 +│ └── ruoyi-common-datasource // 多数据源 │ └── ruoyi-common-log // 日志记录 │ └── ruoyi-common-redis // 缓存服务 │ └── ruoyi-common-security // 安全模块 @@ -29,6 +33,7 @@ com.ruoyi │ └── ruoyi-system // 系统模块 [9201] │ └── ruoyi-gen // 代码生成 [9202] │ └── ruoyi-job // 定时任务 [9203] +│ └── ruoyi-file // 文件服务 [9300] ├── ruoyi-visual // 图形化管理模块 │ └── ruoyi-visual-monitor // 监控中心 [9100] ├──pom.xml // 公共依赖 @@ -36,7 +41,7 @@ com.ruoyi ## 架构图 - + ## 内置功能 @@ -74,27 +79,27 @@ com.ruoyi - - + + - - + + - - + + - - + + - + @@ -102,12 +107,12 @@ com.ruoyi - - + + ## 若依微服务交流群 -QQ群: [![加入QQ群](https://img.shields.io/badge/已满-42799195-blue.svg)](https://jq.qq.com/?_wv=1027&k=yqInfq0S) [![加入QQ群](https://img.shields.io/badge/170157040-blue.svg)](https://jq.qq.com/?_wv=1027&k=Oy1mb3p8) 点击按钮入群。 \ No newline at end of file +QQ群: [![加入QQ群](https://img.shields.io/badge/已满-42799195-blue.svg)](https://jq.qq.com/?_wv=1027&k=yqInfq0S) [![加入QQ群](https://img.shields.io/badge/已满-170157040-blue.svg)](https://jq.qq.com/?_wv=1027&k=Oy1mb3p8) [![加入QQ群](https://img.shields.io/badge/130643120-blue.svg)](https://jq.qq.com/?_wv=1027&k=rvxkJtXK) 点击按钮入群。 \ No newline at end of file diff --git a/bin/run-auth.bat b/bin/run-auth.bat new file mode 100644 index 00000000..6f2f9763 --- /dev/null +++ b/bin/run-auth.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [Ϣ] auth̡ +echo. + +cd %~dp0 +cd ../ruoyi-auth/target + +set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m + +java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-auth.jar + +cd bin +pause \ No newline at end of file diff --git a/bin/run-gateway.bat b/bin/run-gateway.bat new file mode 100644 index 00000000..0a9d776a --- /dev/null +++ b/bin/run-gateway.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [Ϣ] gateway̡ +echo. + +cd %~dp0 +cd ../ruoyi-gateway/target + +set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m + +java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-gateway.jar + +cd bin +pause \ No newline at end of file diff --git a/bin/run-modules-file.bat b/bin/run-modules-file.bat new file mode 100644 index 00000000..8ff427ad --- /dev/null +++ b/bin/run-modules-file.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [Ϣ] modules-file̡ +echo. + +cd %~dp0 +cd ../ruoyi-modules/ruoyi-file/target + +set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m + +java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-file.jar + +cd bin +pause \ No newline at end of file diff --git a/bin/run-modules-gen.bat b/bin/run-modules-gen.bat new file mode 100644 index 00000000..7f8e95a2 --- /dev/null +++ b/bin/run-modules-gen.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [Ϣ] modules-gen̡ +echo. + +cd %~dp0 +cd ../ruoyi-modules/ruoyi-gen/target + +set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m + +java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-gen.jar + +cd bin +pause \ No newline at end of file diff --git a/bin/run-modules-job.bat b/bin/run-modules-job.bat new file mode 100644 index 00000000..1f6f7e7f --- /dev/null +++ b/bin/run-modules-job.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [Ϣ] modules-job̡ +echo. + +cd %~dp0 +cd ../ruoyi-modules/ruoyi-job/target + +set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m + +java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-job.jar + +cd bin +pause \ No newline at end of file diff --git a/bin/run-modules-system.bat b/bin/run-modules-system.bat new file mode 100644 index 00000000..d43e08cb --- /dev/null +++ b/bin/run-modules-system.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [Ϣ] modules-system̡ +echo. + +cd %~dp0 +cd ../ruoyi-modules/ruoyi-system/target + +set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m + +java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-system.jar + +cd bin +pause \ No newline at end of file diff --git a/bin/run-monitor.bat b/bin/run-monitor.bat new file mode 100644 index 00000000..d90a0c7a --- /dev/null +++ b/bin/run-monitor.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [Ϣ] monitor̡ +echo. + +cd %~dp0 +cd ../ruoyi-visual/ruoyi-monitor/target + +set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m + +java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-visual-monitor.jar + +cd bin +pause \ No newline at end of file diff --git a/pom.xml b/pom.xml index ac7bbabe..292ae495 100644 --- a/pom.xml +++ b/pom.xml @@ -6,31 +6,35 @@ com.ruoyi ruoyi - 2.0.0 + 2.5.0 ruoyi http://www.ruoyi.vip 若依微服务系统 - 2.0.0 + 2.5.0 UTF-8 UTF-8 1.8 - 2.2.6.RELEASE - Hoxton.SR4 - 2.2.3 - 2.1.2 - 1.3.0 + 2.3.7.RELEASE + Hoxton.SR9 + 2.2.5.RELEASE + 2.3.1 + 2.1.3 2.9.2 1.5.24 + 1.26.5 2.3.2 - 1.2.12 + 1.3.0 + 1.2.4 + 3.2.1 2.5 1.3.3 1.7 - 1.2.70 - 3.17 + 1.2.75 + 8.0.3 + 4.1.2 2.6.2 @@ -51,18 +55,11 @@ com.alibaba.cloud spring-cloud-alibaba-dependencies - 2.2.1.RELEASE + ${spring-cloud-alibaba.version} pom import - - - com.alibaba.nacos - nacos-client - ${nacos.version} - - org.springframework.boot @@ -79,6 +76,13 @@ ${spring-boot-admin.version} + + + com.github.tobato + fastdfs-client + ${tobato.version} + + org.mybatis.spring.boot @@ -181,6 +185,13 @@ ruoyi-common-datascope ${ruoyi.version} + + + + com.ruoyi + ruoyi-common-datasource + ${ruoyi.version} + diff --git a/ruoyi-api/pom.xml b/ruoyi-api/pom.xml index 2437cfe0..1c24fbc5 100644 --- a/ruoyi-api/pom.xml +++ b/ruoyi-api/pom.xml @@ -4,7 +4,7 @@ com.ruoyi ruoyi - 2.0.0 + 2.5.0 4.0.0 diff --git a/ruoyi-api/ruoyi-api-system/pom.xml b/ruoyi-api/ruoyi-api-system/pom.xml index 6f9997c9..5c8b3af0 100644 --- a/ruoyi-api/ruoyi-api-system/pom.xml +++ b/ruoyi-api/ruoyi-api-system/pom.xml @@ -5,7 +5,7 @@ com.ruoyi ruoyi-api - 2.0.0 + 2.5.0 4.0.0 diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteFileService.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteFileService.java new file mode 100644 index 00000000..ae56a54a --- /dev/null +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteFileService.java @@ -0,0 +1,29 @@ +package com.ruoyi.system.api; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.core.constant.ServiceNameConstants; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.system.api.domain.SysFile; +import com.ruoyi.system.api.factory.RemoteFileFallbackFactory; + +/** + * 文件服务 + * + * @author ruoyi + */ +@FeignClient(contextId = "remoteFileService", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class) +public interface RemoteFileService +{ + /** + * 上传文件 + * + * @param file 文件信息 + * @return 结果 + */ + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R upload(@RequestPart(value = "file") MultipartFile file); +} diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java index 64bcd53b..5140d416 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java @@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.PathVariable; import com.ruoyi.common.core.constant.ServiceNameConstants; import com.ruoyi.common.core.domain.R; import com.ruoyi.system.api.factory.RemoteUserFallbackFactory; -import com.ruoyi.system.api.model.UserInfo; +import com.ruoyi.system.api.model.LoginUser; /** * 用户服务 @@ -23,5 +23,5 @@ public interface RemoteUserService * @return 结果 */ @GetMapping(value = "/user/info/{username}") - public R getUserInfo(@PathVariable("username") String username); + public R getUserInfo(@PathVariable("username") String username); } diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysFile.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysFile.java new file mode 100644 index 00000000..440df49d --- /dev/null +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysFile.java @@ -0,0 +1,50 @@ +package com.ruoyi.system.api.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 文件信息 + * + * @author ruoyi + */ +public class SysFile +{ + /** + * 文件名称 + */ + private String name; + + /** + * 文件地址 + */ + private String url; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getUrl() + { + return url; + } + + public void setUrl(String url) + { + this.url = url; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("name", getName()) + .append("url", getUrl()) + .toString(); + } +} diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java index c15c9e6c..2f9bcb08 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java @@ -37,6 +37,12 @@ public class SysRole extends BaseEntity @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限") private String dataScope; + /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ + private boolean menuCheckStrictly; + + /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ + private boolean deptCheckStrictly; + /** 角色状态(0正常 1停用) */ @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") private String status; @@ -128,6 +134,26 @@ public class SysRole extends BaseEntity this.dataScope = dataScope; } + public boolean isMenuCheckStrictly() + { + return menuCheckStrictly; + } + + public void setMenuCheckStrictly(boolean menuCheckStrictly) + { + this.menuCheckStrictly = menuCheckStrictly; + } + + public boolean isDeptCheckStrictly() + { + return deptCheckStrictly; + } + + public void setDeptCheckStrictly(boolean deptCheckStrictly) + { + this.deptCheckStrictly = deptCheckStrictly; + } + public String getStatus() { return status; @@ -185,6 +211,8 @@ public class SysRole extends BaseEntity .append("roleKey", getRoleKey()) .append("roleSort", getRoleSort()) .append("dataScope", getDataScope()) + .append("menuCheckStrictly", isMenuCheckStrictly()) + .append("deptCheckStrictly", isDeptCheckStrictly()) .append("status", getStatus()) .append("delFlag", getDelFlag()) .append("createBy", getCreateBy()) diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java index e0b84ce1..a9bc15e0 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java @@ -67,12 +67,12 @@ public class SysUser extends BaseEntity /** 删除标志(0代表存在 2代表删除) */ private String delFlag; - /** 最后登陆IP */ - @Excel(name = "最后登陆IP", type = Type.EXPORT) + /** 最后登录IP */ + @Excel(name = "最后登录IP", type = Type.EXPORT) private String loginIp; - /** 最后登陆时间 */ - @Excel(name = "最后登陆时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + /** 最后登录时间 */ + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) private Date loginDate; /** 部门对象 */ diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteFileFallbackFactory.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteFileFallbackFactory.java new file mode 100644 index 00000000..0654256b --- /dev/null +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteFileFallbackFactory.java @@ -0,0 +1,35 @@ +package com.ruoyi.system.api.factory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.system.api.RemoteFileService; +import com.ruoyi.system.api.domain.SysFile; +import feign.hystrix.FallbackFactory; + +/** + * 文件服务降级处理 + * + * @author ruoyi + */ +@Component +public class RemoteFileFallbackFactory implements FallbackFactory +{ + private static final Logger log = LoggerFactory.getLogger(RemoteFileFallbackFactory.class); + + @Override + public RemoteFileService create(Throwable throwable) + { + log.error("文件服务调用失败:{}", throwable.getMessage()); + return new RemoteFileService() + { + @Override + public R upload(MultipartFile file) + { + return R.fail("上传文件失败:" + throwable.getMessage()); + } + }; + } +} diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteUserFallbackFactory.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteUserFallbackFactory.java index 59041e2f..6254ac7c 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteUserFallbackFactory.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteUserFallbackFactory.java @@ -5,7 +5,7 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.ruoyi.common.core.domain.R; import com.ruoyi.system.api.RemoteUserService; -import com.ruoyi.system.api.model.UserInfo; +import com.ruoyi.system.api.model.LoginUser; import feign.hystrix.FallbackFactory; /** @@ -25,9 +25,9 @@ public class RemoteUserFallbackFactory implements FallbackFactory getUserInfo(String username) + public R getUserInfo(String username) { - return null; + return R.fail("获取用户失败:" + throwable.getMessage()); } }; } diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUser.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUser.java new file mode 100644 index 00000000..2f00e102 --- /dev/null +++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUser.java @@ -0,0 +1,150 @@ +package com.ruoyi.system.api.model; + +import java.io.Serializable; +import java.util.Set; +import com.ruoyi.system.api.domain.SysUser; + +/** + * 用户信息 + * + * @author ruoyi + */ +public class LoginUser implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 用户名id + */ + private Long userid; + + /** + * 用户名 + */ + private String username; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 角色列表 + */ + private Set roles; + + /** + * 用户信息 + */ + private SysUser sysUser; + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + public Long getUserid() + { + return userid; + } + + public void setUserid(Long userid) + { + this.userid = userid; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + public Set getRoles() + { + return roles; + } + + public void setRoles(Set roles) + { + this.roles = roles; + } + + public SysUser getSysUser() + { + return sysUser; + } + + public void setSysUser(SysUser sysUser) + { + this.sysUser = sysUser; + } +} diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/UserInfo.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/UserInfo.java deleted file mode 100644 index 3a7225ba..00000000 --- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/UserInfo.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.ruoyi.system.api.model; - -import java.io.Serializable; -import java.util.Set; -import com.ruoyi.system.api.domain.SysUser; - -/** - * 用户信息 - * - * @author ruoyi - */ -public class UserInfo implements Serializable -{ - private static final long serialVersionUID = 1L; - - /** - * 用户基本信息 - */ - private SysUser sysUser; - - /** - * 权限标识集合 - */ - private Set permissions; - - /** - * 角色集合 - */ - private Set roles; - - public SysUser getSysUser() - { - return sysUser; - } - - public void setSysUser(SysUser sysUser) - { - this.sysUser = sysUser; - } - - public Set getPermissions() - { - return permissions; - } - - public void setPermissions(Set permissions) - { - this.permissions = permissions; - } - - public Set getRoles() - { - return roles; - } - - public void setRoles(Set roles) - { - this.roles = roles; - } -} diff --git a/ruoyi-api/ruoyi-api-system/src/main/resources/META-INF/spring.factories b/ruoyi-api/ruoyi-api-system/src/main/resources/META-INF/spring.factories index e9bf345c..19bdae5d 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/resources/META-INF/spring.factories +++ b/ruoyi-api/ruoyi-api-system/src/main/resources/META-INF/spring.factories @@ -1,3 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.ruoyi.system.api.factory.RemoteUserFallbackFactory,\ - com.ruoyi.system.api.factory.RemoteLogFallbackFactory + com.ruoyi.system.api.factory.RemoteLogFallbackFactory, \ + com.ruoyi.system.api.factory.RemoteFileFallbackFactory diff --git a/ruoyi-auth/pom.xml b/ruoyi-auth/pom.xml index e70cd30c..12bf28cf 100644 --- a/ruoyi-auth/pom.xml +++ b/ruoyi-auth/pom.xml @@ -4,7 +4,7 @@ com.ruoyi ruoyi - 2.0.0 + 2.5.0 4.0.0 @@ -28,10 +28,10 @@ spring-cloud-starter-alibaba-nacos-config - + - org.springframework.cloud - spring-cloud-starter-netflix-hystrix + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel @@ -40,6 +40,12 @@ spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + mysql @@ -52,15 +58,10 @@ ruoyi-common-security - - - com.ruoyi - ruoyi-common-redis - - + ${project.artifactId} org.springframework.boot diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/config/AuthServerConfig.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/config/AuthServerConfig.java deleted file mode 100644 index d1ee69e2..00000000 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/config/AuthServerConfig.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.ruoyi.auth.config; - -import java.util.LinkedHashMap; -import java.util.Map; -import javax.sql.DataSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; -import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; -import org.springframework.security.oauth2.provider.token.TokenEnhancer; -import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; -import com.ruoyi.auth.exception.CustomWebResponseExceptionTranslator; -import com.ruoyi.common.core.constant.CacheConstants; -import com.ruoyi.common.core.constant.SecurityConstants; -import com.ruoyi.common.security.domain.LoginUser; -import com.ruoyi.common.security.service.RedisClientDetailsService; - -/** - * OAuth2 认证服务配置 - * - * @author ruoyi - */ -@Configuration -@EnableAuthorizationServer -public class AuthServerConfig extends AuthorizationServerConfigurerAdapter -{ - @Autowired - private AuthenticationManager authenticationManager; - - @Autowired - private DataSource dataSource; - - @Autowired - private RedisConnectionFactory redisConnectionFactory; - - @Autowired - private UserDetailsService userDetailsService; - - @Autowired - private TokenEnhancer tokenEnhancer; - - /** - * 定义授权和令牌端点以及令牌服务 - */ - @Override - public void configure(AuthorizationServerEndpointsConfigurer endpoints) - { - endpoints - // 请求方式 - .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) - // 指定token存储位置 - .tokenStore(tokenStore()) - // 自定义生成令牌 - .tokenEnhancer(tokenEnhancer) - // 用户账号密码认证 - .userDetailsService(userDetailsService) - // 指定认证管理器 - .authenticationManager(authenticationManager) - // 是否重复使用 refresh_token - .reuseRefreshTokens(false) - // 自定义异常处理 - .exceptionTranslator(new CustomWebResponseExceptionTranslator()); - } - - /** - * 配置令牌端点(Token Endpoint)的安全约束 - */ - @Override - public void configure(AuthorizationServerSecurityConfigurer oauthServer) - { - oauthServer.allowFormAuthenticationForClients().checkTokenAccess("permitAll()"); - } - - /** - * 声明 ClientDetails实现 - */ - public RedisClientDetailsService clientDetailsService() - { - RedisClientDetailsService clientDetailsService = new RedisClientDetailsService(dataSource); - return clientDetailsService; - } - - /** - * 配置客户端详情 - */ - @Override - public void configure(ClientDetailsServiceConfigurer clients) throws Exception - { - clients.withClientDetails(clientDetailsService()); - } - - /** - * 基于 Redis 实现,令牌保存到缓存 - */ - @Bean - public TokenStore tokenStore() - { - RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory); - tokenStore.setPrefix(CacheConstants.OAUTH_ACCESS); - return tokenStore; - } - - /** - * 自定义生成令牌 - */ - @Bean - public TokenEnhancer tokenEnhancer() - { - return (accessToken, authentication) -> { - if (authentication.getUserAuthentication() != null) - { - Map additionalInformation = new LinkedHashMap(); - LoginUser user = (LoginUser) authentication.getUserAuthentication().getPrincipal(); - additionalInformation.put(SecurityConstants.DETAILS_USER_ID, user.getUserId()); - additionalInformation.put(SecurityConstants.DETAILS_USERNAME, user.getUsername()); - ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation); - } - return accessToken; - }; - } -} diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/config/WebSecurityConfig.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/config/WebSecurityConfig.java deleted file mode 100644 index df0bc04d..00000000 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/config/WebSecurityConfig.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.ruoyi.auth.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; - -/** - * Security 安全认证相关配置 - * Oauth2依赖于Security 默认情况下WebSecurityConfig执行比ResourceServerConfig优先 - * - * @author ruoyi - */ -@Order(99) -@Configuration -public class WebSecurityConfig extends WebSecurityConfigurerAdapter -{ - @Autowired - private UserDetailsService userDetailsService; - - @Bean - public PasswordEncoder passwordEncoder() - { - return new BCryptPasswordEncoder(); - } - - @Bean - @Override - public AuthenticationManager authenticationManagerBean() throws Exception - { - return super.authenticationManagerBean(); - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception - { - auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); - } - - @Override - protected void configure(HttpSecurity http) throws Exception - { - http - .authorizeRequests() - .antMatchers( - "/actuator/**", - "/oauth/*", - "/token/**").permitAll() - .anyRequest().authenticated() - .and().csrf().disable(); - } -} diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java index 4058f758..b90c8002 100644 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java @@ -1,20 +1,17 @@ package com.ruoyi.auth.controller; -import java.util.Map; +import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2RefreshToken; -import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import com.ruoyi.common.core.constant.Constants; -import com.ruoyi.common.core.constant.SecurityConstants; +import com.ruoyi.auth.form.LoginBody; +import com.ruoyi.auth.service.SysLoginService; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.utils.StringUtils; -import com.ruoyi.system.api.RemoteLogService; +import com.ruoyi.common.security.service.TokenService; +import com.ruoyi.system.api.model.LoginUser; /** * token 控制 @@ -22,42 +19,47 @@ import com.ruoyi.system.api.RemoteLogService; * @author ruoyi */ @RestController -@RequestMapping("/token") public class TokenController { @Autowired - private TokenStore tokenStore; + private TokenService tokenService; @Autowired - private RemoteLogService remoteLogService; + private SysLoginService sysLoginService; - @DeleteMapping("/logout") - public R logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) + @PostMapping("login") + public R login(@RequestBody LoginBody form) { - if (StringUtils.isEmpty(authHeader)) - { - return R.ok(); - } + // 用户登录 + LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword()); + // 获取登录token + return R.ok(tokenService.createToken(userInfo)); + } - String tokenValue = authHeader.replace(OAuth2AccessToken.BEARER_TYPE, StringUtils.EMPTY).trim(); - OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue); - if (accessToken == null || StringUtils.isEmpty(accessToken.getValue())) + @DeleteMapping("logout") + public R logout(HttpServletRequest request) + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) { - return R.ok(); + String username = loginUser.getUsername(); + // 删除用户缓存记录 + tokenService.delLoginUser(loginUser.getToken()); + // 记录用户退出日志 + sysLoginService.logout(username); } + return R.ok(); + } - // 清空 access token - tokenStore.removeAccessToken(accessToken); - - // 清空 refresh token - OAuth2RefreshToken refreshToken = accessToken.getRefreshToken(); - tokenStore.removeRefreshToken(refreshToken); - Map map = accessToken.getAdditionalInformation(); - if (map.containsKey(SecurityConstants.DETAILS_USERNAME)) + @PostMapping("refresh") + public R refresh(HttpServletRequest request) + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) { - String username = (String) map.get(SecurityConstants.DETAILS_USERNAME); - // 记录用户退出日志 - remoteLogService.saveLogininfor(username, Constants.LOGOUT, "退出成功"); + // 刷新令牌有效期 + tokenService.refreshToken(loginUser); + return R.ok(); } return R.ok(); } diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/UserController.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/UserController.java deleted file mode 100644 index d4705842..00000000 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/UserController.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.ruoyi.auth.controller; - -import java.security.Principal; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * 身份信息获取 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/oauth") -public class UserController -{ - @RequestMapping("/user") - public Principal user(Principal user) - { - return user; - } -} diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomOauthException.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomOauthException.java deleted file mode 100644 index 7ead34a2..00000000 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomOauthException.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.ruoyi.auth.exception; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; - -/** - * oauth2自定义异常 - * - * @author ruoyi - **/ -@JsonSerialize(using = CustomOauthExceptionSerializer.class) -public class CustomOauthException extends OAuth2Exception -{ - private static final long serialVersionUID = 1L; - - public CustomOauthException(String msg) - { - super(msg); - } -} \ No newline at end of file diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomOauthExceptionSerializer.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomOauthExceptionSerializer.java deleted file mode 100644 index 79d4472f..00000000 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomOauthExceptionSerializer.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.ruoyi.auth.exception; - -import java.io.IOException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.ruoyi.common.core.constant.HttpStatus; -import com.ruoyi.common.core.utils.StringUtils; -import com.ruoyi.common.core.web.domain.AjaxResult; - -/** - * 自定义异常返回 - * - * @author ruoyi - **/ -public class CustomOauthExceptionSerializer extends StdSerializer -{ - private static final long serialVersionUID = 1L; - - private static final Logger log = LoggerFactory.getLogger(CustomOauthExceptionSerializer.class); - - public static final String BAD_CREDENTIALS = "Bad credentials"; - - public CustomOauthExceptionSerializer() - { - super(CustomOauthException.class); - } - - @Override - public void serialize(CustomOauthException e, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) - throws IOException - { - jsonGenerator.writeStartObject(); - jsonGenerator.writeNumberField(AjaxResult.CODE_TAG, HttpStatus.ERROR); - if (StringUtils.equals(e.getMessage(), BAD_CREDENTIALS)) - { - jsonGenerator.writeStringField(AjaxResult.MSG_TAG, "用户名或密码错误"); - } - else - { - log.warn("oauth2 认证异常 {} ", e); - jsonGenerator.writeStringField(AjaxResult.MSG_TAG, e.getMessage()); - } - jsonGenerator.writeEndObject(); - } -} \ No newline at end of file diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomWebResponseExceptionTranslator.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomWebResponseExceptionTranslator.java deleted file mode 100644 index 9b56c084..00000000 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomWebResponseExceptionTranslator.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.ruoyi.auth.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; -import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator; - -/** - * OAuth2 自定义异常处理 - * - * @author ruoyi - */ -public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator -{ - @Override - public ResponseEntity translate(Exception e) - { - return ResponseEntity.status(HttpStatus.OK).body(new CustomOauthException(e.getMessage())); - } -} diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/form/LoginBody.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/form/LoginBody.java new file mode 100644 index 00000000..b12fb314 --- /dev/null +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/form/LoginBody.java @@ -0,0 +1,39 @@ +package com.ruoyi.auth.form; + +/** + * 用户登录对象 + * + * @author ruoyi + */ +public class LoginBody +{ + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } +} diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/handler/AuthenticationSuccessEventHandler.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/handler/AuthenticationSuccessEventHandler.java deleted file mode 100644 index f6fda372..00000000 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/handler/AuthenticationSuccessEventHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.ruoyi.auth.handler; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationListener; -import org.springframework.security.authentication.event.AuthenticationSuccessEvent; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Component; -import com.ruoyi.common.core.constant.Constants; -import com.ruoyi.common.core.utils.StringUtils; -import com.ruoyi.common.security.domain.LoginUser; -import com.ruoyi.system.api.RemoteLogService; - -/** - * 认证成功处理 - * - * @author ruoyi - */ -@Component -public class AuthenticationSuccessEventHandler implements ApplicationListener -{ - @Autowired - private RemoteLogService remoteLogService; - - @Override - public void onApplicationEvent(AuthenticationSuccessEvent event) - { - Authentication authentication = (Authentication) event.getSource(); - if (StringUtils.isNotEmpty(authentication.getAuthorities()) - && authentication.getPrincipal() instanceof LoginUser) - { - LoginUser user = (LoginUser) authentication.getPrincipal(); - - String username = user.getUsername(); - - // 记录用户登录日志 - remoteLogService.saveLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); - } - } -} diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java new file mode 100644 index 00000000..a8662048 --- /dev/null +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java @@ -0,0 +1,95 @@ +package com.ruoyi.auth.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.common.core.constant.Constants; +import com.ruoyi.common.core.constant.UserConstants; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.enums.UserStatus; +import com.ruoyi.common.core.exception.BaseException; +import com.ruoyi.common.core.utils.SecurityUtils; +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.system.api.RemoteLogService; +import com.ruoyi.system.api.RemoteUserService; +import com.ruoyi.system.api.domain.SysUser; +import com.ruoyi.system.api.model.LoginUser; + +/** + * 登录校验方法 + * + * @author ruoyi + */ +@Component +public class SysLoginService +{ + @Autowired + private RemoteLogService remoteLogService; + + @Autowired + private RemoteUserService remoteUserService; + + /** + * 登录 + */ + public LoginUser login(String username, String password) + { + // 用户名或密码为空 错误 + if (StringUtils.isAnyBlank(username, password)) + { + remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); + throw new BaseException("用户/密码必须填写"); + } + // 密码如果不在指定范围内 错误 + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); + throw new BaseException("用户密码不在指定范围"); + } + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); + throw new BaseException("用户名不在指定范围"); + } + // 查询用户信息 + R userResult = remoteUserService.getUserInfo(username); + + if (R.FAIL == userResult.getCode()) + { + throw new BaseException(userResult.getMsg()); + } + + if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) + { + remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); + throw new BaseException("登录用户:" + username + " 不存在"); + } + LoginUser userInfo = userResult.getData(); + SysUser user = userResult.getData().getSysUser(); + if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); + + throw new BaseException("对不起,您的账号:" + username + " 已被删除"); + } + if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); + throw new BaseException("对不起,您的账号:" + username + " 已停用"); + } + if (!SecurityUtils.matchesPassword(password, user.getPassword())) + { + remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户密码错误"); + throw new BaseException("用户不存在/密码错误"); + } + remoteLogService.saveLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); + return userInfo; + } + + public void logout(String loginName) + { + remoteLogService.saveLogininfor(loginName, Constants.LOGOUT, "退出成功"); + } +} \ No newline at end of file diff --git a/ruoyi-auth/src/main/resources/bootstrap.yml b/ruoyi-auth/src/main/resources/bootstrap.yml index 037cd7a7..f456b03c 100644 --- a/ruoyi-auth/src/main/resources/bootstrap.yml +++ b/ruoyi-auth/src/main/resources/bootstrap.yml @@ -21,4 +21,5 @@ spring: # 配置文件格式 file-extension: yml # 共享配置 - shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/ruoyi-auth/src/main/resources/logback.xml b/ruoyi-auth/src/main/resources/logback.xml new file mode 100644 index 00000000..098a610b --- /dev/null +++ b/ruoyi-auth/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 77c0a0a1..df16e1da 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -4,7 +4,7 @@ com.ruoyi ruoyi - 2.0.0 + 2.5.0 4.0.0 @@ -15,6 +15,7 @@ ruoyi-common-swagger ruoyi-common-security ruoyi-common-datascope + ruoyi-common-datasource ruoyi-common diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml index 09ebe3ef..5c437f09 100644 --- a/ruoyi-common/ruoyi-common-core/pom.xml +++ b/ruoyi-common/ruoyi-common-core/pom.xml @@ -5,7 +5,7 @@ com.ruoyi ruoyi-common - 2.0.0 + 2.5.0 4.0.0 @@ -16,13 +16,13 @@ - + org.springframework.cloud spring-cloud-starter-openfeign - + org.springframework @@ -34,73 +34,73 @@ org.springframework spring-web - + org.apache.commons commons-pool2 - + com.github.pagehelper pagehelper-spring-boot-starter - - + + - javax.validation - validation-api + org.springframework.boot + spring-boot-starter-validation - + com.fasterxml.jackson.core jackson-databind - + com.alibaba fastjson - + org.apache.commons commons-lang3 - + commons-io commons-io - + commons-fileupload commons-fileupload - + org.apache.poi poi-ooxml - + javax.servlet javax.servlet-api - + io.swagger swagger-annotations - + - + diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excel.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excel.java index f6d4cd20..975eca3f 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excel.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excel.java @@ -4,6 +4,7 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.math.BigDecimal; /** * 自定义导出Excel数据注解 @@ -34,6 +35,21 @@ public @interface Excel */ public String readConverterExp() default ""; + /** + * 分隔符,读取字符串组内容 + */ + public String separator() default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale() default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; + /** * 导出类型(0数字 1字符串) */ @@ -79,6 +95,32 @@ public @interface Excel */ public String targetAttr() default ""; + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics() default false; + + /** + * 导出字段对齐方式(0:默认;1:靠左;2:居中;3:靠右) + */ + Align align() default Align.AUTO; + + public enum Align + { + AUTO(0), LEFT(1), CENTER(2), RIGHT(3); + private final int value; + + Align(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + /** * 字段类型(0:导出导入;1:仅导出;2:仅导入) */ @@ -102,7 +144,7 @@ public @interface Excel public enum ColumnType { - NUMERIC(0), STRING(1); + NUMERIC(0), STRING(1), IMAGE(2); private final int value; ColumnType(int value) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java index c43465be..8f666dd1 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java @@ -8,12 +8,32 @@ package com.ruoyi.common.core.constant; public class CacheConstants { /** - * oauth 缓存前缀 + * 令牌自定义标识 */ - public static final String OAUTH_ACCESS = "oauth:access:"; + public static final String HEADER = "Authorization"; /** - * oauth 客户端信息 + * 令牌前缀 */ - public static final String CLIENT_DETAILS_KEY = "oauth:client:details"; + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 权限缓存前缀 + */ + public final static String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 用户ID字段 + */ + public static final String DETAILS_USER_ID = "user_id"; + + /** + * 用户名字段 + */ + public static final String DETAILS_USERNAME = "username"; + + /** + * 授权信息字段 + */ + public static final String AUTHORIZATION_HEADER = "authorization"; } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java index 572d329a..4225eaa1 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java @@ -85,7 +85,12 @@ public class Constants /** * 验证码有效期(分钟) */ - public static final Integer CAPTCHA_EXPIRATION = 2; + public static final long CAPTCHA_EXPIRATION = 2; + + /** + * 令牌有效期(分钟) + */ + public final static long TOKEN_EXPIRE = 720; /** * 参数管理 cache key diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java index 5b0a2c9c..122c4631 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java @@ -13,6 +13,9 @@ public class GenConstants /** 树表(增删改查) */ public static final String TPL_TREE = "tree"; + /** 主子表(增删改查) */ + public static final String TPL_SUB = "sub"; + /** 树编码字段 */ public static final String TREE_CODE = "treeCode"; @@ -22,16 +25,24 @@ public class GenConstants /** 树名称字段 */ public static final String TREE_NAME = "treeName"; + /** 上级菜单ID字段 */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** 上级菜单名称字段 */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + /** 数据库字符串类型 */ - public static final String[] COLUMNTYPE_STR = { "char", "varchar", "narchar", "varchar2", "tinytext", "text", - "mediumtext", "longtext" }; + public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; + + /** 数据库文本类型 */ + public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; /** 数据库时间类型 */ public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; /** 数据库数字类型 */ public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", - "bigint", "float", "float", "double", "decimal" }; + "bigint", "float", "double", "decimal" }; /** 页面不需要编辑字段 */ public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; @@ -68,6 +79,15 @@ public class GenConstants /** 日期控件 */ public static final String HTML_DATETIME = "datetime"; + /** 图片上传控件 */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** 文件上传控件 */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** 富文本控件 */ + public static final String HTML_EDITOR = "editor"; + /** 字符串类型 */ public static final String TYPE_STRING = "String"; diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java deleted file mode 100644 index 9e0123d3..00000000 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.ruoyi.common.core.constant; - -/** - * 权限相关通用常量 - * - * @author ruoyi - */ -public class SecurityConstants -{ - /** - * 令牌类型 - */ - public static final String BEARER_TOKEN_TYPE = "Bearer"; - - /** - * 授权token url - */ - public static final String AUTH_TOKEN = "/oauth/token"; - - /** - * 注销token url - */ - public static final String TOKEN_LOGOUT = "/token/logout"; - - /** - * 用户ID字段 - */ - public static final String DETAILS_USER_ID = "user_id"; - - /** - * 用户名字段 - */ - public static final String DETAILS_USERNAME = "username"; - - /** - * sys_oauth_client_details 表的字段,不包括client_id、client_secret - */ - public static final String CLIENT_FIELDS = "client_id, client_secret, resource_ids, scope, " - + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, " - + "refresh_token_validity, additional_information, autoapprove"; - - /** - * JdbcClientDetailsService 查询语句 - */ - public static final String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details"; - - /** - * 按条件client_id 查询 - */ - public static final String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?"; - - /** - * 默认的查询语句 - */ - public static final String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id"; -} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ServiceNameConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ServiceNameConstants.java index 4c44b89a..a7dcfdd6 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ServiceNameConstants.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ServiceNameConstants.java @@ -16,4 +16,9 @@ public class ServiceNameConstants * 系统模块的serviceid */ public static final String SYSTEM_SERVICE = "ruoyi-system"; + + /** + * 文件服务的serviceid + */ + public static final String FILE_SERVICE = "ruoyi-file"; } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java index a71ec392..cb01ba81 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java @@ -54,7 +54,25 @@ public class UserConstants /** Layout组件标识 */ public final static String LAYOUT = "Layout"; + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + /** 校验返回结果码 */ public final static String UNIQUE = "0"; + public final static String NOT_UNIQUE = "1"; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + + public static final int PASSWORD_MAX_LENGTH = 20; } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/PreAuthorizeException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/PreAuthorizeException.java new file mode 100644 index 00000000..3d420c41 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/PreAuthorizeException.java @@ -0,0 +1,15 @@ +package com.ruoyi.common.core.exception; + +/** + * 权限异常 + * + * @author ruoyi + */ +public class PreAuthorizeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public PreAuthorizeException() + { + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileException.java new file mode 100644 index 00000000..3ced31db --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.core.exception.file; + +import com.ruoyi.common.core.exception.BaseException; + +/** + * 文件信息异常类 + * + * @author ruoyi + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileNameLengthLimitExceededException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 00000000..c425acdf --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.core.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author ruoyi + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileSizeLimitExceededException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 00000000..0426067a --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.core.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author ruoyi + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/InvalidExtensionException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/InvalidExtensionException.java new file mode 100644 index 00000000..33b6912a --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/InvalidExtensionException.java @@ -0,0 +1,81 @@ +package com.ruoyi.common.core.exception.file; + +import java.util.Arrays; +import org.apache.commons.fileupload.FileUploadException; + +/** + * 文件上传 误异常类 + * + * @author ruoyi + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/CharsetKit.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/CharsetKit.java index 31eef05a..662f817f 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/CharsetKit.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/CharsetKit.java @@ -66,7 +66,7 @@ public class CharsetKit if (null == destCharset) { - srcCharset = StandardCharsets.UTF_8; + destCharset = StandardCharsets.UTF_8; } if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/SecurityUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SecurityUtils.java similarity index 55% rename from ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/SecurityUtils.java rename to ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SecurityUtils.java index b0c1e8d3..f2225bea 100644 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/SecurityUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SecurityUtils.java @@ -1,92 +1,88 @@ -package com.ruoyi.common.security.utils; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import com.ruoyi.common.security.domain.LoginUser; - -/** - * 权限获取工具类 - * - * @author ruoyi - */ -public class SecurityUtils -{ - /** - * 获取Authentication - */ - public static Authentication getAuthentication() - { - return SecurityContextHolder.getContext().getAuthentication(); - } - - /** - * 获取用户 - */ - public static String getUsername() - { - return getLoginUser().getUsername(); - } - - /** - * 获取用户 - */ - public static LoginUser getLoginUser(Authentication authentication) - { - Object principal = authentication.getPrincipal(); - if (principal instanceof LoginUser) - { - return (LoginUser) principal; - } - return null; - } - - /** - * 获取用户 - */ - public static LoginUser getLoginUser() - { - Authentication authentication = getAuthentication(); - if (authentication == null) - { - return null; - } - return getLoginUser(authentication); - } - - /** - * 生成BCryptPasswordEncoder密码 - * - * @param password 密码 - * @return 加密字符串 - */ - public static String encryptPassword(String password) - { - BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - return passwordEncoder.encode(password); - } - - /** - * 判断密码是否相同 - * - * @param rawPassword 真实密码 - * @param encodedPassword 加密后字符 - * @return 结果 - */ - public static boolean matchesPassword(String rawPassword, String encodedPassword) - { - BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - return passwordEncoder.matches(rawPassword, encodedPassword); - } - - /** - * 是否为管理员 - * - * @param userId 用户ID - * @return 结果 - */ - public static boolean isAdmin(Long userId) - { - return userId != null && 1L == userId; - } -} +package com.ruoyi.common.core.utils; + +import javax.servlet.http.HttpServletRequest; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import com.ruoyi.common.core.constant.CacheConstants; +import com.ruoyi.common.core.text.Convert; + +/** + * 权限获取工具类 + * + * @author ruoyi + */ +public class SecurityUtils +{ + /** + * 获取用户 + */ + public static String getUsername() + { + String username = ServletUtils.getRequest().getHeader(CacheConstants.DETAILS_USERNAME); + return ServletUtils.urlDecode(username); + } + + /** + * 获取用户ID + */ + public static Long getUserId() + { + return Convert.toLong(ServletUtils.getRequest().getHeader(CacheConstants.DETAILS_USER_ID)); + } + + /** + * 获取请求token + */ + public static String getToken() + { + return getToken(ServletUtils.getRequest()); + } + + /** + * 根据request获取请求token + */ + public static String getToken(HttpServletRequest request) + { + String token = ServletUtils.getRequest().getHeader(CacheConstants.HEADER); + if (StringUtils.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX)) + { + token = token.replace(CacheConstants.TOKEN_PREFIX, ""); + } + return token; + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java index 7de6fcbb..6809f29f 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java @@ -1,12 +1,19 @@ package com.ruoyi.common.core.utils; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import com.ruoyi.common.core.constant.Constants; import com.ruoyi.common.core.text.Convert; /** @@ -53,7 +60,14 @@ public class ServletUtils */ public static HttpServletRequest getRequest() { - return getRequestAttributes().getRequest(); + try + { + return getRequestAttributes().getRequest(); + } + catch (Exception e) + { + return null; + } } /** @@ -61,7 +75,14 @@ public class ServletUtils */ public static HttpServletResponse getResponse() { - return getRequestAttributes().getResponse(); + try + { + return getRequestAttributes().getResponse(); + } + catch (Exception e) + { + return null; + } } /** @@ -74,8 +95,31 @@ public class ServletUtils public static ServletRequestAttributes getRequestAttributes() { - RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); - return (ServletRequestAttributes) attributes; + try + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + catch (Exception e) + { + return null; + } + } + + public static Map getHeaders(HttpServletRequest request) + { + Map map = new LinkedHashMap<>(); + Enumeration enumeration = request.getHeaderNames(); + if (enumeration != null) + { + while (enumeration.hasMoreElements()) + { + String key = enumeration.nextElement(); + String value = request.getHeader(key); + map.put(key, value); + } + } + return map; } /** @@ -133,4 +177,40 @@ public class ServletUtils } return false; } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return ""; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return ""; + } + } } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java index a0d0c918..e0bc15a2 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java @@ -1,6 +1,7 @@ package com.ruoyi.common.core.utils; import java.util.Collection; +import java.util.List; import java.util.Map; import com.ruoyi.common.core.text.StrFormatter; @@ -17,6 +18,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** 下划线 */ private static final char SEPARATOR = '_'; + /** 星号 */ + private static final String START = "*"; + /** * 获取参数不为空值 * @@ -396,6 +400,121 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils return sb.toString(); } + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String testStr : strs) + { + if (matches(str, testStr)) + { + return true; + } + } + return false; + } + + /** + * 查找指定字符串是否匹配指定字符串数组中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, String... strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String testStr : strs) + { + if (matches(str, testStr)) + { + return true; + } + } + return false; + } + + /** + * 查找指定字符串是否匹配 + * + * @param str 指定字符串 + * @param pattern 需要检查的字符串 + * @return 是否匹配 + */ + public static boolean matches(String str, String pattern) + { + if (isEmpty(pattern) || isEmpty(str)) + { + return false; + } + + pattern = pattern.replaceAll("\\s*", ""); // 替换空格 + int beginOffset = 0; // pattern截取开始位置 + int formerStarOffset = -1; // 前星号的偏移位置 + int latterStarOffset = -1; // 后星号的偏移位置 + + String remainingURI = str; + String prefixPattern = ""; + String suffixPattern = ""; + + boolean result = false; + do + { + formerStarOffset = indexOf(pattern, START, beginOffset); + prefixPattern = substring(pattern, beginOffset, formerStarOffset > -1 ? formerStarOffset : pattern.length()); + + // 匹配前缀Pattern + result = remainingURI.contains(prefixPattern); + // 已经没有星号,直接返回 + if (formerStarOffset == -1) + { + return result; + } + + // 匹配失败,直接返回 + if (!result) + return false; + + if (!isEmpty(prefixPattern)) + { + remainingURI = substringAfter(str, prefixPattern); + } + + // 匹配后缀Pattern + latterStarOffset = indexOf(pattern, START, formerStarOffset + 1); + suffixPattern = substring(pattern, formerStarOffset + 1, latterStarOffset > -1 ? latterStarOffset : pattern.length()); + + result = remainingURI.contains(suffixPattern); + // 匹配失败,直接返回 + if (!result) + return false; + + if (!isEmpty(suffixPattern)) + { + remainingURI = substringAfter(str, suffixPattern); + } + + // 移动指针 + beginOffset = latterStarOffset + 1; + + } + while (!isEmpty(suffixPattern) && !isEmpty(remainingURI)); + + return true; + } + @SuppressWarnings("unchecked") public static T cast(Object obj) { diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/FileTypeUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/FileTypeUtils.java new file mode 100644 index 00000000..1629fcd4 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/FileTypeUtils.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.core.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author ruoyi + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} \ No newline at end of file diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/FileUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/FileUtils.java new file mode 100644 index 00000000..9dfca168 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/FileUtils.java @@ -0,0 +1,260 @@ +package com.ruoyi.common.core.utils.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.core.utils.StringUtils; + +/** + * 文件处理工具类 + * + * @author ruoyi + */ +public class FileUtils extends org.apache.commons.io.FileUtils +{ + /** 字符常量:斜杠 {@code '/'} */ + public static final char SLASH = '/'; + + /** 字符常量:反斜杠 {@code '\\'} */ + public static final char BACKSLASH = '\\'; + + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * @return + */ + public static void writeBytes(String filePath, OutputStream os) throws IOException + { + FileInputStream fis = null; + try + { + File file = new File(filePath); + if (!file.exists()) + { + throw new FileNotFoundException(filePath); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) + { + os.write(b, 0, length); + } + } + catch (IOException e) + { + throw e; + } + finally + { + if (os != null) + { + try + { + os.close(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + } + if (fis != null) + { + try + { + fis.close(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + } + } + } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) + { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) + { + file.delete(); + flag = true; + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 返回文件名 + * + * @param filePath 文件 + * @return 文件名 + */ + public static String getName(String filePath) + { + if (null == filePath) + { + return null; + } + int len = filePath.length(); + if (0 == len) + { + return filePath; + } + if (isFileSeparator(filePath.charAt(len - 1))) + { + // 以分隔符结尾的去掉结尾分隔符 + len--; + } + + int begin = 0; + char c; + for (int i = len - 1; i > -1; i--) + { + c = filePath.charAt(i); + if (isFileSeparator(c)) + { + // 查找最后一个路径分隔符(/或者\) + begin = i + 1; + break; + } + } + + return filePath.substring(begin, len); + } + + /** + * 是否为Windows或者Linux(Unix)文件分隔符
+ * Windows平台下分隔符为\,Linux(Unix)为/ + * + * @param c 字符 + * @return 是否为Windows或者Linux(Unix)文件分隔符 + */ + public static boolean isFileSeparator(char c) + { + return SLASH == c || BACKSLASH == c; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + * @return + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.setHeader("Content-disposition", contentDispositionValue.toString()); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/ImageUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/ImageUtils.java new file mode 100644 index 00000000..f8fa776a --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/ImageUtils.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.core.utils.file; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 图片处理工具类 + * + * @author ruoyi + */ +public class ImageUtils +{ + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage(String imagePath) + { + InputStream is = getFile(imagePath); + try + { + return IOUtils.toByteArray(is); + } + catch (Exception e) + { + log.error("图片加载异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile(String imagePath) + { + try + { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } + catch (Exception e) + { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param key 地址 + * @return 字节数据 + */ + public static byte[] readFile(String url) + { + InputStream in = null; + ByteArrayOutputStream baos = null; + try + { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + return IOUtils.toByteArray(in); + } + catch (Exception e) + { + log.error("访问文件异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(baos); + } + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/MimeTypeUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/MimeTypeUtils.java new file mode 100644 index 00000000..b8a3bc39 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/MimeTypeUtils.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.core.utils.file; + +/** + * 媒体类型工具类 + * + * @author ruoyi + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/IpUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/IpUtils.java index 335ed914..be8712f4 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/IpUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/IpUtils.java @@ -14,34 +14,43 @@ public class IpUtils { public static String getIpAddr(HttpServletRequest request) { - if (request == null) + String ip = null; + + // X-Forwarded-For:Squid 服务代理 + String ipAddresses = request.getHeader("X-Forwarded-For"); + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { - return "unknown"; + // Proxy-Client-IP:apache 服务代理 + ipAddresses = request.getHeader("Proxy-Client-IP"); } - String ip = request.getHeader("x-forwarded-for"); - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { - ip = request.getHeader("Proxy-Client-IP"); + // WL-Proxy-Client-IP:weblogic 服务代理 + ipAddresses = request.getHeader("WL-Proxy-Client-IP"); } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { - ip = request.getHeader("X-Forwarded-For"); + // HTTP_CLIENT_IP:有些代理服务器 + ipAddresses = request.getHeader("HTTP_CLIENT_IP"); } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { - ip = request.getHeader("WL-Proxy-Client-IP"); + // X-Real-IP:nginx服务代理 + ipAddresses = request.getHeader("X-Real-IP"); } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + + // 有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP + if (ipAddresses != null && ipAddresses.length() != 0) { - ip = request.getHeader("X-Real-IP"); + ip = ipAddresses.split(",")[0]; } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + // 还是不能获取到,最后再通过request.getRemoteAddr();获取 + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { ip = request.getRemoteAddr(); } - - return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; + return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip; } public static boolean internalIp(String ip) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/poi/ExcelUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/poi/ExcelUtil.java index 783aa8e4..f754a2ca 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/poi/ExcelUtil.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/poi/ExcelUtil.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.math.BigDecimal; import java.text.DecimalFormat; import java.util.ArrayList; @@ -14,17 +13,19 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; -import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.DataValidation; import org.apache.poi.ss.usermodel.DataValidationConstraint; import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.HorizontalAlignment; @@ -36,6 +37,7 @@ import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFDataValidation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +48,8 @@ import com.ruoyi.common.core.annotation.Excels; import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.utils.DateUtils; import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.common.core.utils.file.FileTypeUtils; +import com.ruoyi.common.core.utils.file.ImageUtils; import com.ruoyi.common.core.utils.reflect.ReflectUtils; /** @@ -97,6 +101,21 @@ public class ExcelUtil */ private List fields; + /** + * 最大高度 + */ + private short maxHeight; + + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + /** * 实体对象 */ @@ -194,7 +213,10 @@ public class ExcelUtil // 设置类的私有字段属性可访问. field.setAccessible(true); Integer column = cellMap.get(attr.name()); - fieldsMap.put(column, field); + if (column != null) + { + fieldsMap.put(column, field); + } } } for (int i = 1; i < rows; i++) @@ -221,22 +243,30 @@ public class ExcelUtil } else { - val = Convert.toStr(val); + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) + { + val = DateUtils.parseDateToStr(dateFormat, (Date) val); + } + else + { + val = Convert.toStr(val); + } } } - else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) + else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) { val = Convert.toInt(val); } - else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) + else if (Long.TYPE == fieldType || Long.class == fieldType) { val = Convert.toLong(val); } - else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) + else if (Double.TYPE == fieldType || Double.class == fieldType) { val = Convert.toDouble(val); } - else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) + else if (Float.TYPE == fieldType || Float.class == fieldType) { val = Convert.toFloat(val); } @@ -255,6 +285,10 @@ public class ExcelUtil val = DateUtil.getJavaDate((Double) val); } } + else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) + { + val = Convert.toBool(val, false); + } if (StringUtils.isNotNull(fieldType)) { Excel attr = field.getAnnotation(Excel.class); @@ -265,7 +299,7 @@ public class ExcelUtil } else if (StringUtils.isNotEmpty(attr.readConverterExp())) { - val = reverseByExp(String.valueOf(val), attr.readConverterExp()); + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); } ReflectUtils.invokeSetter(entity, propertyName, val); } @@ -334,6 +368,7 @@ public class ExcelUtil if (Type.EXPORT.equals(type)) { fillExcelData(index, row); + addStatisticsRow(); } } wb.write(outputStream); @@ -436,6 +471,30 @@ public class ExcelUtil headerFont.setColor(IndexedColors.WHITE.getIndex()); style.setFont(headerFont); styles.put("header", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.LEFT); + styles.put("data1", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + styles.put("data2", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.RIGHT); + styles.put("data3", style); return styles; } @@ -465,14 +524,53 @@ public class ExcelUtil { if (ColumnType.STRING == attr.cellType()) { - cell.setCellType(CellType.NUMERIC); cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix()); } else if (ColumnType.NUMERIC == attr.cellType()) { - cell.setCellType(CellType.NUMERIC); - cell.setCellValue(Integer.parseInt(value + "")); + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + else if (ColumnType.IMAGE == attr.cellType()) + { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), + cell.getRow().getRowNum() + 1); + String imagePath = Convert.toStr(value); + if (StringUtils.isNotEmpty(imagePath)) + { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch(Sheet sheet) + { + if (sheet.getDrawingPatriarch() == null) + { + sheet.createDrawingPatriarch(); } + return sheet.getDrawingPatriarch(); + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType(byte[] value) + { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_JPEG; + } + else if ("PNG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; } /** @@ -488,7 +586,6 @@ public class ExcelUtil { // 设置列宽 sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); - row.setHeight((short) (attr.height() * 20)); } // 如果设置了提示信息则鼠标放上去提示. if (StringUtils.isNotEmpty(attr.prompt())) @@ -513,31 +610,38 @@ public class ExcelUtil try { // 设置行高 - row.setHeight((short) (attr.height() * 20)); + row.setHeight(maxHeight); // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. if (attr.isExport()) { // 创建cell cell = row.createCell(column); - cell.setCellStyle(styles.get("data")); + int align = attr.align().value(); + cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : ""))); // 用于读取对象中的属性 Object value = getTargetValue(vo, field, attr); String dateFormat = attr.dateFormat(); String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) { cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value)); } else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) { - cell.setCellValue(convertByExp(String.valueOf(value), readConverterExp)); + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } + else if (value instanceof BigDecimal && -1 != attr.scale()) + { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString()); } else { // 设置列类型 setCellVo(value, attr, cell); } + addStatisticsData(column, Convert.toStr(value), attr); } } catch (Exception e) @@ -609,28 +713,36 @@ public class ExcelUtil * * @param propertyValue 参数值 * @param converterExp 翻译注解 + * @param separator 分隔符 * @return 解析后值 - * @throws Exception */ - public static String convertByExp(String propertyValue, String converterExp) throws Exception + public static String convertByExp(String propertyValue, String converterExp, String separator) { - try + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { - String[] convertSource = converterExp.split(","); - for (String item : convertSource) + String[] itemArray = item.split("="); + if (StringUtils.containsAny(separator, propertyValue)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[0].equals(value)) + { + propertyString.append(itemArray[1] + separator); + break; + } + } + } + else { - String[] itemArray = item.split("="); if (itemArray[0].equals(propertyValue)) { return itemArray[1]; } } } - catch (Exception e) - { - throw e; - } - return propertyValue; + return StringUtils.stripEnd(propertyString.toString(), separator); } /** @@ -638,28 +750,83 @@ public class ExcelUtil * * @param propertyValue 参数值 * @param converterExp 翻译注解 + * @param separator 分隔符 * @return 解析后值 - * @throws Exception */ - public static String reverseByExp(String propertyValue, String converterExp) throws Exception + public static String reverseByExp(String propertyValue, String converterExp, String separator) { - try + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { - String[] convertSource = converterExp.split(","); - for (String item : convertSource) + String[] itemArray = item.split("="); + if (StringUtils.containsAny(separator, propertyValue)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[1].equals(value)) + { + propertyString.append(itemArray[0] + separator); + break; + } + } + } + else { - String[] itemArray = item.split("="); if (itemArray[1].equals(propertyValue)) { return itemArray[0]; } } } - catch (Exception e) + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData(Integer index, String text, Excel entity) + { + if (entity != null && entity.isStatistics()) + { + Double temp = 0D; + if (!statistics.containsKey(index)) + { + statistics.put(index, temp); + } + try + { + temp = Double.valueOf(text); + } + catch (NumberFormatException e) + { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow() + { + if (statistics.size() > 0) { - throw e; + Cell cell = null; + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) + { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); + } + statistics.clear(); } - return propertyValue; } /** @@ -703,12 +870,12 @@ public class ExcelUtil */ private Object getValue(Object o, String name) throws Exception { - if (StringUtils.isNotEmpty(name)) + if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) { Class clazz = o.getClass(); - String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); - Method method = clazz.getMethod(methodName); - o = method.invoke(o); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); } return o; } @@ -742,6 +909,21 @@ public class ExcelUtil } } this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight() + { + double maxHeight = 0; + for (Object[] os : this.fields) + { + Excel excel = (Excel) os[1]; + maxHeight = maxHeight > excel.height() ? maxHeight : excel.height(); + } + return (short) (maxHeight * 20); } /** @@ -803,18 +985,18 @@ public class ExcelUtil Cell cell = row.getCell(column); if (StringUtils.isNotNull(cell)) { - if (cell.getCellTypeEnum() == CellType.NUMERIC || cell.getCellTypeEnum() == CellType.FORMULA) + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) { val = cell.getNumericCellValue(); - if (HSSFDateUtil.isCellDateFormatted(cell)) + if (DateUtil.isCellDateFormatted(cell)) { val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 } else { - if ((Double) val % 1 > 0) + if ((Double) val % 1 != 0) { - val = new DecimalFormat("0.00").format(val); + val = new BigDecimal(val.toString()); } else { @@ -822,15 +1004,15 @@ public class ExcelUtil } } } - else if (cell.getCellTypeEnum() == CellType.STRING) + else if (cell.getCellType() == CellType.STRING) { val = cell.getStringCellValue(); } - else if (cell.getCellTypeEnum() == CellType.BOOLEAN) + else if (cell.getCellType() == CellType.BOOLEAN) { val = cell.getBooleanCellValue(); } - else if (cell.getCellTypeEnum() == CellType.ERROR) + else if (cell.getCellType() == CellType.ERROR) { val = cell.getErrorCellValue(); } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java index ee27ae0a..3d8382b2 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java @@ -204,6 +204,10 @@ public class ReflectUtils args[i] = DateUtil.getJavaDate((Double) args[i]); } } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } } } return (E) method.invoke(obj, args); diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java index 25f842e9..b84f4e76 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java @@ -1,5 +1,6 @@ package com.ruoyi.common.core.utils.sql; +import com.ruoyi.common.core.exception.BaseException; import com.ruoyi.common.core.utils.StringUtils; /** @@ -10,9 +11,9 @@ import com.ruoyi.common.core.utils.StringUtils; public class SqlUtil { /** - * 仅支持字母、数字、下划线、空格、逗号(支持多个字段排序) + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) */ - public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,]+"; + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; /** * 检查字符,防止注入绕过 @@ -21,7 +22,7 @@ public class SqlUtil { if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) { - return StringUtils.EMPTY; + throw new BaseException("参数不符合规范,不能进行查询"); } return value; } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/AjaxResult.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/AjaxResult.java index 022aede8..626f4afb 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/AjaxResult.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/AjaxResult.java @@ -57,6 +57,20 @@ public class AjaxResult extends HashMap super.put(DATA_TAG, data); } } + + /** + * 方便链式调用 + * + * @param key + * @param value + * @return + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } /** * 返回成功消息 diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java index 79bcee7c..b316f893 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java @@ -5,7 +5,6 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonIgnore; /** * Entity基类 @@ -36,14 +35,6 @@ public class BaseEntity implements Serializable /** 备注 */ private String remark; - /** 开始时间 */ - @JsonIgnore - private String beginTime; - - /** 结束时间 */ - @JsonIgnore - private String endTime; - /** 请求参数 */ private Map params; @@ -107,26 +98,6 @@ public class BaseEntity implements Serializable this.remark = remark; } - public String getBeginTime() - { - return beginTime; - } - - public void setBeginTime(String beginTime) - { - this.beginTime = beginTime; - } - - public String getEndTime() - { - return endTime; - } - - public void setEndTime(String endTime) - { - this.endTime = endTime; - } - public Map getParams() { if (params == null) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/PageDomain.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/PageDomain.java index 367efa69..9f1ea8e0 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/PageDomain.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/PageDomain.java @@ -17,9 +17,9 @@ public class PageDomain /** 排序列 */ private String orderByColumn; - /** 排序的方向 "desc" 或者 "asc". */ - private String isAsc; + /** 排序的方向desc或者asc */ + private String isAsc = "asc"; public String getOrderBy() { diff --git a/ruoyi-common/ruoyi-common-datascope/pom.xml b/ruoyi-common/ruoyi-common-datascope/pom.xml index b89792e1..2fbc8f61 100644 --- a/ruoyi-common/ruoyi-common-datascope/pom.xml +++ b/ruoyi-common/ruoyi-common-datascope/pom.xml @@ -5,7 +5,7 @@ com.ruoyi ruoyi-common - 2.0.0 + 2.5.0 4.0.0 diff --git a/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/aspect/DataScopeAspect.java b/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/aspect/DataScopeAspect.java index b03674c1..72aafcdd 100644 --- a/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/aspect/DataScopeAspect.java +++ b/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/aspect/DataScopeAspect.java @@ -12,10 +12,10 @@ import org.springframework.stereotype.Component; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.web.domain.BaseEntity; import com.ruoyi.common.datascope.annotation.DataScope; -import com.ruoyi.common.datascope.service.AwaitUserService; +import com.ruoyi.common.security.service.TokenService; import com.ruoyi.system.api.domain.SysRole; import com.ruoyi.system.api.domain.SysUser; -import com.ruoyi.system.api.model.UserInfo; +import com.ruoyi.system.api.model.LoginUser; /** * 数据过滤处理 @@ -57,7 +57,7 @@ public class DataScopeAspect public static final String DATA_SCOPE = "dataScope"; @Autowired - private AwaitUserService awaitUserService; + private TokenService tokenService; // 配置织入点 @Pointcut("@annotation(com.ruoyi.common.datascope.annotation.DataScope)") @@ -80,12 +80,12 @@ public class DataScopeAspect return; } // 获取当前的用户 - UserInfo loginUser = awaitUserService.info(); - SysUser currentUser = loginUser.getSysUser(); - if (currentUser != null) + LoginUser loginUser = tokenService.getLoginUser(); + if (StringUtils.isNotNull(loginUser)) { + SysUser currentUser = loginUser.getSysUser(); // 如果是超级管理员,则不过滤数据 - if (!currentUser.isAdmin()) + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) { dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias()); @@ -145,8 +145,12 @@ public class DataScopeAspect if (StringUtils.isNotBlank(sqlString.toString())) { - BaseEntity baseEntity = (BaseEntity) joinPoint.getArgs()[0]; - baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + 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) + ")"); + } } } diff --git a/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/service/AwaitUserService.java b/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/service/AwaitUserService.java deleted file mode 100644 index 446b3f2e..00000000 --- a/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/service/AwaitUserService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.ruoyi.common.datascope.service; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import com.ruoyi.common.core.domain.R; -import com.ruoyi.common.core.utils.StringUtils; -import com.ruoyi.common.security.utils.SecurityUtils; -import com.ruoyi.system.api.RemoteUserService; -import com.ruoyi.system.api.model.UserInfo; - -/** - * 同步调用用户服务 - * - * @author ruoyi - */ -@Service -public class AwaitUserService -{ - private static final Logger log = LoggerFactory.getLogger(AwaitUserService.class); - - @Autowired - private RemoteUserService remoteUserService; - - /** - * 查询当前用户信息 - * - * @return 用户基本信息 - */ - public UserInfo info() - { - String username = SecurityUtils.getUsername(); - R userResult = remoteUserService.getUserInfo(username); - if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) - { - log.info("数据权限范围查询用户:{} 不存在.", username); - return null; - } - return userResult.getData(); - } -} diff --git a/ruoyi-common/ruoyi-common-datascope/src/main/resources/META-INF/spring.factories b/ruoyi-common/ruoyi-common-datascope/src/main/resources/META-INF/spring.factories index 52508bdd..a9bc860f 100644 --- a/ruoyi-common/ruoyi-common-datascope/src/main/resources/META-INF/spring.factories +++ b/ruoyi-common/ruoyi-common-datascope/src/main/resources/META-INF/spring.factories @@ -1,5 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.ruoyi.common.datascope.service.AwaitUserService,\ com.ruoyi.common.datascope.aspect.DataScopeAspect diff --git a/ruoyi-common/ruoyi-common-datasource/pom.xml b/ruoyi-common/ruoyi-common-datasource/pom.xml new file mode 100644 index 00000000..ca063068 --- /dev/null +++ b/ruoyi-common/ruoyi-common-datasource/pom.xml @@ -0,0 +1,41 @@ + + + + com.ruoyi + ruoyi-common + 2.5.0 + + 4.0.0 + + ruoyi-common-datasource + + + ruoyi-common-datasource多数据源 + + + + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + + com.baomidou + dynamic-datasource-spring-boot-starter + ${dynamic-ds.version} + + + + + io.seata + seata-spring-boot-starter + + + + \ No newline at end of file diff --git a/ruoyi-common/ruoyi-common-datasource/src/main/java/com/ruoyi/common/datasource/annotation/Master.java b/ruoyi-common/ruoyi-common-datasource/src/main/java/com/ruoyi/common/datasource/annotation/Master.java new file mode 100644 index 00000000..6ebec02c --- /dev/null +++ b/ruoyi-common/ruoyi-common-datasource/src/main/java/com/ruoyi/common/datasource/annotation/Master.java @@ -0,0 +1,22 @@ +package com.ruoyi.common.datasource.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.baomidou.dynamic.datasource.annotation.DS; + +/** + * 主库数据源 + * + * @author ruoyi + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("master") +public @interface Master +{ + +} \ No newline at end of file diff --git a/ruoyi-common/ruoyi-common-datasource/src/main/java/com/ruoyi/common/datasource/annotation/Slave.java b/ruoyi-common/ruoyi-common-datasource/src/main/java/com/ruoyi/common/datasource/annotation/Slave.java new file mode 100644 index 00000000..9ee09413 --- /dev/null +++ b/ruoyi-common/ruoyi-common-datasource/src/main/java/com/ruoyi/common/datasource/annotation/Slave.java @@ -0,0 +1,22 @@ +package com.ruoyi.common.datasource.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.baomidou.dynamic.datasource.annotation.DS; + +/** + * 从库数据源 + * + * @author ruoyi + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("slave") +public @interface Slave +{ + +} \ No newline at end of file diff --git a/ruoyi-common/ruoyi-common-log/pom.xml b/ruoyi-common/ruoyi-common-log/pom.xml index a75f289a..fe322d1c 100644 --- a/ruoyi-common/ruoyi-common-log/pom.xml +++ b/ruoyi-common/ruoyi-common-log/pom.xml @@ -5,7 +5,7 @@ com.ruoyi ruoyi-common - 2.0.0 + 2.5.0 4.0.0 diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java index 17fbe1e6..e76018e7 100644 --- a/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java +++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java @@ -1,6 +1,8 @@ package com.ruoyi.common.log.aspect; import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Iterator; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -17,16 +19,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.servlet.HandlerMapping; import com.alibaba.fastjson.JSON; +import com.ruoyi.common.core.utils.SecurityUtils; import com.ruoyi.common.core.utils.ServletUtils; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.ip.IpUtils; import com.ruoyi.common.log.annotation.Log; import com.ruoyi.common.log.enums.BusinessStatus; import com.ruoyi.common.log.service.AsyncLogService; -import com.ruoyi.common.security.domain.LoginUser; -import com.ruoyi.common.security.utils.SecurityUtils; import com.ruoyi.system.api.domain.SysOperLog; /** @@ -83,9 +83,6 @@ public class LogAspect return; } - // 获取当前的用户 - LoginUser loginUser = SecurityUtils.getLoginUser(); - // *========数据库日志=========*// SysOperLog operLog = new SysOperLog(); operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); @@ -96,9 +93,10 @@ public class LogAspect operLog.setJsonResult(JSON.toJSONString(jsonResult)); operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); - if (loginUser != null) + String username = SecurityUtils.getUsername(); + if (StringUtils.isNotBlank(username)) { - operLog.setOperName(loginUser.getUsername()); + operLog.setOperName(username); } if (e != null) @@ -163,11 +161,6 @@ public class LogAspect String params = argsArrayToString(joinPoint.getArgs()); operLog.setOperParam(StringUtils.substring(params, 0, 2000)); } - else - { - Map paramsMap = (Map) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); - operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000)); - } } /** @@ -198,8 +191,14 @@ public class LogAspect { if (!isFilterObject(paramsArray[i])) { - Object jsonObj = JSON.toJSON(paramsArray[i]); - params += jsonObj.toString() + " "; + try + { + Object jsonObj = JSON.toJSON(paramsArray[i]); + params += jsonObj.toString() + " "; + } + catch (Exception e) + { + } } } } @@ -212,8 +211,31 @@ public class LogAspect * @param o 对象信息。 * @return 如果是需要过滤的对象,则返回true;否则返回false。 */ + @SuppressWarnings("rawtypes") public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Iterator iter = collection.iterator(); iter.hasNext();) + { + return iter.next() instanceof MultipartFile; + } + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) + { + Map.Entry entry = (Map.Entry) iter.next(); + return entry.getValue() instanceof MultipartFile; + } + } return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse; } } diff --git a/ruoyi-common/ruoyi-common-redis/pom.xml b/ruoyi-common/ruoyi-common-redis/pom.xml index ecea0c59..e498f15b 100644 --- a/ruoyi-common/ruoyi-common-redis/pom.xml +++ b/ruoyi-common/ruoyi-common-redis/pom.xml @@ -5,7 +5,7 @@ com.ruoyi ruoyi-common - 2.0.0 + 2.5.0 4.0.0 diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/service/RedisService.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/service/RedisService.java index 6c702eaa..4bf7f708 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/service/RedisService.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/service/RedisService.java @@ -1,11 +1,13 @@ package com.ruoyi.common.redis.service; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; @@ -42,7 +44,7 @@ public class RedisService * @param timeout 时间 * @param timeUnit 时间颗粒度 */ - public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) + public void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit) { redisTemplate.opsForValue().set(key, value, timeout, timeUnit); } @@ -109,7 +111,7 @@ public class RedisService * 缓存List数据 * * @param key 缓存的键值 - * @param values 待缓存的List数据 + * @param dataList 待缓存的List数据 * @return 缓存的对象 */ public long setCacheList(final String key, final List dataList) @@ -136,10 +138,15 @@ public class RedisService * @param dataSet 缓存的数据 * @return 缓存数据的对象 */ - public long setCacheSet(final String key, final Set dataSet) + public BoundSetOperations setCacheSet(final String key, final Set dataSet) { - Long count = redisTemplate.opsForSet().add(key, dataSet); - return count == null ? 0 : count; + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; } /** diff --git a/ruoyi-common/ruoyi-common-security/pom.xml b/ruoyi-common/ruoyi-common-security/pom.xml index e12fd09f..3ee61b18 100644 --- a/ruoyi-common/ruoyi-common-security/pom.xml +++ b/ruoyi-common/ruoyi-common-security/pom.xml @@ -4,7 +4,7 @@ com.ruoyi ruoyi-common - 2.0.0 + 2.5.0 4.0.0 @@ -16,16 +16,16 @@ - + - org.springframework.cloud - spring-cloud-starter-oauth2 + com.ruoyi + ruoyi-api-system - + com.ruoyi - ruoyi-api-system + ruoyi-common-redis diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableCustomConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableCustomConfig.java index 15681a4f..fa2a8b29 100644 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableCustomConfig.java +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableCustomConfig.java @@ -1,13 +1,17 @@ package com.ruoyi.common.security.annotation; -import java.lang.annotation.*; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.Import; import org.springframework.scheduling.annotation.EnableAsync; -import com.ruoyi.common.security.feign.OAuth2FeignConfig; import com.ruoyi.common.security.config.ApplicationConfig; -import com.ruoyi.common.security.config.SecurityImportBeanDefinitionRegistrar; +import com.ruoyi.common.security.feign.FeignAutoConfiguration; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @@ -20,7 +24,7 @@ import com.ruoyi.common.security.config.SecurityImportBeanDefinitionRegistrar; // 开启线程异步执行 @EnableAsync // 自动加载类 -@Import({ SecurityImportBeanDefinitionRegistrar.class, OAuth2FeignConfig.class, ApplicationConfig.class }) +@Import({ ApplicationConfig.class, FeignAutoConfiguration.class }) public @interface EnableCustomConfig { diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/PreAuthorize.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/PreAuthorize.java new file mode 100644 index 00000000..c9f10b6d --- /dev/null +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/PreAuthorize.java @@ -0,0 +1,46 @@ +package com.ruoyi.common.security.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 权限注解 + * + * @author ruoyi + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface PreAuthorize +{ + /** + * 验证用户是否具备某权限 + */ + public String hasPermi() default ""; + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + */ + public String lacksPermi() default ""; + + /** + * 验证用户是否具有以下任意一个权限 + */ + public String[] hasAnyPermi() default {}; + + /** + * 判断用户是否拥有某个角色 + */ + public String hasRole() default ""; + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反 + */ + public String lacksRole() default ""; + + /** + * 验证用户是否具有以下任意一个角色 + */ + public String[] hasAnyRoles() default {}; +} \ No newline at end of file diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/aspect/PreAuthorizeAspect.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/aspect/PreAuthorizeAspect.java new file mode 100644 index 00000000..3a44d82b --- /dev/null +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/aspect/PreAuthorizeAspect.java @@ -0,0 +1,225 @@ +package com.ruoyi.common.security.aspect; + +import java.lang.reflect.Method; +import java.util.Collection; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.Signature; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.PatternMatchUtils; +import org.springframework.util.StringUtils; +import com.ruoyi.common.core.exception.PreAuthorizeException; +import com.ruoyi.common.security.annotation.PreAuthorize; +import com.ruoyi.common.security.service.TokenService; +import com.ruoyi.system.api.model.LoginUser; + +/** + * 自定义权限实现 + * + * @author ruoyi + */ +@Aspect +@Component +public class PreAuthorizeAspect +{ + @Autowired + private TokenService tokenService; + + /** 所有权限标识 */ + private static final String ALL_PERMISSION = "*:*:*"; + + /** 管理员角色权限标识 */ + private static final String SUPER_ADMIN = "admin"; + + /** 数组为0时 */ + private static final Integer ARRAY_EMPTY = 0; + + @Around("@annotation(com.ruoyi.common.security.annotation.PreAuthorize)") + public Object around(ProceedingJoinPoint point) throws Throwable + { + Signature signature = point.getSignature(); + MethodSignature methodSignature = (MethodSignature) signature; + Method method = methodSignature.getMethod(); + PreAuthorize annotation = method.getAnnotation(PreAuthorize.class); + if (annotation == null) + { + return point.proceed(); + } + + if (!StringUtils.isEmpty(annotation.hasPermi())) + { + if (hasPermi(annotation.hasPermi())) + { + return point.proceed(); + } + throw new PreAuthorizeException(); + } + else if (!StringUtils.isEmpty(annotation.lacksPermi())) + { + if (lacksPermi(annotation.lacksPermi())) + { + return point.proceed(); + } + throw new PreAuthorizeException(); + } + else if (ARRAY_EMPTY < annotation.hasAnyPermi().length) + { + if (hasAnyPermi(annotation.hasAnyPermi())) + { + return point.proceed(); + } + throw new PreAuthorizeException(); + } + else if (!StringUtils.isEmpty(annotation.hasRole())) + { + if (hasRole(annotation.hasRole())) + { + return point.proceed(); + } + throw new PreAuthorizeException(); + } + else if (!StringUtils.isEmpty(annotation.lacksRole())) + { + if (lacksRole(annotation.lacksRole())) + { + return point.proceed(); + } + throw new PreAuthorizeException(); + } + else if (ARRAY_EMPTY < annotation.hasAnyRoles().length) + { + if (hasAnyRoles(annotation.hasAnyRoles())) + { + return point.proceed(); + } + throw new PreAuthorizeException(); + } + + return point.proceed(); + } + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + LoginUser userInfo = tokenService.getLoginUser(); + if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getPermissions())) + { + return false; + } + return hasPermissions(userInfo.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String[] permissions) + { + LoginUser userInfo = tokenService.getLoginUser(); + if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getPermissions())) + { + return false; + } + Collection authorities = userInfo.getPermissions(); + for (String permission : permissions) + { + if (permission != null && hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + LoginUser userInfo = tokenService.getLoginUser(); + if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getRoles())) + { + return false; + } + for (String roleKey : userInfo.getRoles()) + { + if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(role)) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String[] roles) + { + LoginUser userInfo = tokenService.getLoginUser(); + if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getRoles())) + { + return false; + } + for (String role : roles) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param authorities 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Collection authorities, String permission) + { + return authorities.stream().filter(StringUtils::hasText) + .anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(permission, x)); + } +} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/AuthIgnoreConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/AuthIgnoreConfig.java deleted file mode 100644 index d2d5ae0b..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/AuthIgnoreConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.ruoyi.common.security.config; - -import java.util.ArrayList; -import java.util.List; -import org.springframework.beans.factory.annotation.Configurable; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -/** - * 忽略服务间的认证 - * - * @author ruoyi - **/ -@Component -@Configurable -@ConfigurationProperties(prefix = "security.oauth2.ignore") -public class AuthIgnoreConfig -{ - private List urls = new ArrayList<>(); - - public List getUrls() - { - return urls; - } - - public void setUrls(List urls) - { - this.urls = urls; - } -} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/CommonUserConverter.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/CommonUserConverter.java deleted file mode 100644 index 29f2afbe..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/CommonUserConverter.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.ruoyi.common.security.config; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter; -import org.springframework.util.StringUtils; -import com.ruoyi.common.core.constant.SecurityConstants; -import com.ruoyi.common.core.text.Convert; -import com.ruoyi.common.security.domain.LoginUser; - -/** - * https://my.oschina.net/giegie/blog/3023768 根据checktoken 的结果转化用户信息 - * - * @author lengleng - */ -public class CommonUserConverter implements UserAuthenticationConverter -{ - private static final String N_A = "N/A"; - - /** - * 将授权信息返回到资源服务 - */ - @Override - public Map convertUserAuthentication(Authentication userAuthentication) - { - Map authMap = new LinkedHashMap<>(); - authMap.put(USERNAME, userAuthentication.getName()); - if (userAuthentication.getAuthorities() != null && !userAuthentication.getAuthorities().isEmpty()) - { - authMap.put(AUTHORITIES, AuthorityUtils.authorityListToSet(userAuthentication.getAuthorities())); - } - return authMap; - } - - /** - * 获取用户认证信息 - */ - @Override - public Authentication extractAuthentication(Map map) - { - if (map.containsKey(USERNAME)) - { - Collection authorities = getAuthorities(map); - - Long userId = Convert.toLong(map.get(SecurityConstants.DETAILS_USER_ID)); - String username = (String) map.get(SecurityConstants.DETAILS_USERNAME); - LoginUser user = new LoginUser(userId, username, N_A, true, true, true, true, authorities); - return new UsernamePasswordAuthenticationToken(user, N_A, authorities); - } - return null; - } - - /** - * 获取权限资源信息 - */ - private Collection getAuthorities(Map map) - { - Object authorities = map.get(AUTHORITIES); - if (authorities instanceof String) - { - return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities); - } - if (authorities instanceof Collection) - { - return AuthorityUtils.commaSeparatedStringToAuthorityList( - StringUtils.collectionToCommaDelimitedString((Collection) authorities)); - } - throw new IllegalArgumentException("Authorities must be either a String or a Collection"); - } -} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/MethodSecurityConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/MethodSecurityConfig.java deleted file mode 100644 index 07fabf32..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/MethodSecurityConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.ruoyi.common.security.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; - -/** - * - * @EnableGlobalMethodSecurity(securedEnabled=true) - * 开启@Secured 注解过滤权限 - * - * @EnableGlobalMethodSecurity(jsr250Enabled=true) - * 开启@RolesAllowed 注解过滤权限 - * - * @EnableGlobalMethodSecurity(prePostEnabled=true) - * 使用表达式时间方法级别的安全性 4个注解可用 - * -@PreAuthorize 在方法调用之前,基于表达式的计算结果来限制对方法的访问 - * -@PostAuthorize 允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常 - * -@PostFilter 允许方法调用,但必须按照表达式来过滤方法的结果 - * -@PreFilter 允许方法调用,但必须在进入方法之前过滤输入值 - * - */ -@Configuration -@EnableGlobalMethodSecurity(prePostEnabled = true) -public class MethodSecurityConfig -{ - -} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/ResourceServerConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/ResourceServerConfig.java deleted file mode 100644 index 6aa3ba89..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/ResourceServerConfig.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.ruoyi.common.security.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; -import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; -import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; -import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; -import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; -import org.springframework.security.oauth2.provider.token.RemoteTokenServices; -import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; -import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter; -import org.springframework.web.client.DefaultResponseErrorHandler; -import org.springframework.web.client.RestTemplate; - -/** - * oauth2 服务配置 - * - * @author ruoyi - */ -@Configuration -@EnableResourceServer -public class ResourceServerConfig extends ResourceServerConfigurerAdapter -{ - @Autowired - private ResourceServerProperties resourceServerProperties; - - @Autowired - private OAuth2ClientProperties oAuth2ClientProperties; - - @Bean - public AuthIgnoreConfig authIgnoreConfig() - { - return new AuthIgnoreConfig(); - } - - @Bean - @LoadBalanced - public RestTemplate restTemplate() - { - RestTemplate restTemplate = new RestTemplate(); - restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); - return restTemplate; - } - - @Bean - public ResourceServerTokenServices tokenServices() - { - RemoteTokenServices remoteTokenServices = new RemoteTokenServices(); - DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); - UserAuthenticationConverter userTokenConverter = new CommonUserConverter(); - accessTokenConverter.setUserTokenConverter(userTokenConverter); - remoteTokenServices.setCheckTokenEndpointUrl(resourceServerProperties.getTokenInfoUri()); - remoteTokenServices.setClientId(oAuth2ClientProperties.getClientId()); - remoteTokenServices.setClientSecret(oAuth2ClientProperties.getClientSecret()); - remoteTokenServices.setRestTemplate(restTemplate()); - remoteTokenServices.setAccessTokenConverter(accessTokenConverter); - return remoteTokenServices; - } - - @Override - public void configure(HttpSecurity http) throws Exception - { - http.csrf().disable(); - ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = http - .authorizeRequests(); - // 不登录可以访问 - authIgnoreConfig().getUrls().forEach(url -> registry.antMatchers(url).permitAll()); - registry.anyRequest().authenticated(); - } - - @Override - public void configure(ResourceServerSecurityConfigurer resources) - { - resources.tokenServices(tokenServices()); - } -} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/SecurityImportBeanDefinitionRegistrar.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/SecurityImportBeanDefinitionRegistrar.java deleted file mode 100644 index 386bce00..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/SecurityImportBeanDefinitionRegistrar.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.ruoyi.common.security.config; - -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; -import org.springframework.core.type.AnnotationMetadata; -import com.ruoyi.common.core.utils.StringUtils; - -/** - * 导入 SecurityImportBeanDefinitionRegistrar 自动加载类 - * - * @author ruoyi - */ -public class SecurityImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar -{ - @Override - public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) - { - Class aClass = ResourceServerConfig.class; - String beanName = StringUtils.uncapitalize(aClass.getSimpleName()); - BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ResourceServerConfig.class); - registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition()); - } -} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/domain/LoginUser.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/domain/LoginUser.java deleted file mode 100644 index 1056fece..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/domain/LoginUser.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.ruoyi.common.security.domain; - -import java.util.Collection; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.User; - -/** - * 登录用户身份权限 - * - * @author ruoyi - */ -public class LoginUser extends User -{ - private static final long serialVersionUID = 1L; - - /** - * 用户ID - */ - private Long userId; - - public LoginUser(Long userId, String username, String password, boolean enabled, boolean accountNonExpired, - boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities) - { - super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); - this.userId = userId; - } - - public Long getUserId() - { - return userId; - } - - public void setUserId(Long userId) - { - this.userId = userId; - } -} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/FeignAutoConfiguration.java similarity index 71% rename from ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignConfig.java rename to ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/FeignAutoConfiguration.java index e5c598b2..934a9598 100644 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignConfig.java +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/FeignAutoConfiguration.java @@ -5,16 +5,16 @@ import org.springframework.context.annotation.Configuration; import feign.RequestInterceptor; /** - * Feign配置注册 + * Feign 配置注册 * * @author ruoyi **/ @Configuration -public class OAuth2FeignConfig +public class FeignAutoConfiguration { @Bean public RequestInterceptor requestInterceptor() { - return new OAuth2FeignRequestInterceptor(); + return new FeignRequestInterceptor(); } } diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/FeignRequestInterceptor.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/FeignRequestInterceptor.java new file mode 100644 index 00000000..2642dd12 --- /dev/null +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/FeignRequestInterceptor.java @@ -0,0 +1,45 @@ +package com.ruoyi.common.security.feign; + +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import com.ruoyi.common.core.constant.CacheConstants; +import com.ruoyi.common.core.utils.ServletUtils; +import com.ruoyi.common.core.utils.StringUtils; +import feign.RequestInterceptor; +import feign.RequestTemplate; + +/** + * feign 请求拦截器 + * + * @author ruoyi + */ +@Component +public class FeignRequestInterceptor implements RequestInterceptor +{ + @Override + public void apply(RequestTemplate requestTemplate) + { + HttpServletRequest httpServletRequest = ServletUtils.getRequest(); + if (StringUtils.isNotNull(httpServletRequest)) + { + Map headers = ServletUtils.getHeaders(httpServletRequest); + // 传递用户信息请求头,防止丢失 + String userId = headers.get(CacheConstants.DETAILS_USER_ID); + if (StringUtils.isNotEmpty(userId)) + { + requestTemplate.header(CacheConstants.DETAILS_USER_ID, userId); + } + String userName = headers.get(CacheConstants.DETAILS_USERNAME); + if (StringUtils.isNotEmpty(userName)) + { + requestTemplate.header(CacheConstants.DETAILS_USERNAME, userName); + } + String authentication = headers.get(CacheConstants.AUTHORIZATION_HEADER); + if (StringUtils.isNotEmpty(authentication)) + { + requestTemplate.header(CacheConstants.AUTHORIZATION_HEADER, authentication); + } + } + } +} \ No newline at end of file diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignRequestInterceptor.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignRequestInterceptor.java deleted file mode 100644 index baf60835..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignRequestInterceptor.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.ruoyi.common.security.feign; - -import org.springframework.http.HttpHeaders; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; -import org.springframework.stereotype.Component; -import com.ruoyi.common.core.constant.SecurityConstants; -import feign.RequestInterceptor; -import feign.RequestTemplate; - -/** - * feign 请求拦截器 - * - * @author ruoyi - */ -@Component -public class OAuth2FeignRequestInterceptor implements RequestInterceptor -{ - @Override - public void apply(RequestTemplate requestTemplate) - { - SecurityContext securityContext = SecurityContextHolder.getContext(); - Authentication authentication = securityContext.getAuthentication(); - if (authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails) - { - OAuth2AuthenticationDetails dateils = (OAuth2AuthenticationDetails) authentication.getDetails(); - requestTemplate.header(HttpHeaders.AUTHORIZATION, - String.format("%s %s", SecurityConstants.BEARER_TOKEN_TYPE, dateils.getTokenValue())); - } - } -} \ No newline at end of file diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/CustomAccessDeniedHandler.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/CustomAccessDeniedHandler.java deleted file mode 100644 index 0ba69aeb..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/CustomAccessDeniedHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.ruoyi.common.security.handler; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler; -import org.springframework.stereotype.Component; -import com.alibaba.fastjson.JSON; -import com.ruoyi.common.core.constant.HttpStatus; -import com.ruoyi.common.core.domain.R; -import com.ruoyi.common.core.utils.ServletUtils; - -/** - * 自定义访问无权限资源时的异常 - * - * @author ruoyi - */ -@Component -public class CustomAccessDeniedHandler extends OAuth2AccessDeniedHandler -{ - private final Logger logger = LoggerFactory.getLogger(CustomAccessDeniedHandler.class); - - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException authException) - { - logger.info("权限不足,请联系管理员 {}", request.getRequestURI()); - - String msg = authException.getMessage(); - ServletUtils.renderString(response, JSON.toJSONString(R.fail(HttpStatus.FORBIDDEN, msg))); - } -} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java index ac1ae39d..1a3070c9 100644 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java @@ -2,18 +2,14 @@ package com.ruoyi.common.security.handler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.AccountExpiredException; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.validation.BindException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.servlet.NoHandlerFoundException; -import com.ruoyi.common.core.constant.HttpStatus; import com.ruoyi.common.core.exception.BaseException; import com.ruoyi.common.core.exception.CustomException; import com.ruoyi.common.core.exception.DemoModeException; +import com.ruoyi.common.core.exception.PreAuthorizeException; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.web.domain.AjaxResult; @@ -33,7 +29,7 @@ public class GlobalExceptionHandler @ExceptionHandler(BaseException.class) public AjaxResult baseException(BaseException e) { - return AjaxResult.error(e.getMessage()); + return AjaxResult.error(e.getDefaultMessage()); } /** @@ -49,34 +45,6 @@ public class GlobalExceptionHandler return AjaxResult.error(e.getCode(), e.getMessage()); } - @ExceptionHandler(NoHandlerFoundException.class) - public AjaxResult handlerNoFoundException(Exception e) - { - log.error(e.getMessage(), e); - return AjaxResult.error(HttpStatus.NOT_FOUND, "路径不存在,请检查路径是否正确"); - } - - @ExceptionHandler(AccessDeniedException.class) - public AjaxResult handleAuthorizationException(AccessDeniedException e) - { - log.error(e.getMessage()); - return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); - } - - @ExceptionHandler(AccountExpiredException.class) - public AjaxResult handleAccountExpiredException(AccountExpiredException e) - { - log.error(e.getMessage(), e); - return AjaxResult.error(e.getMessage()); - } - - @ExceptionHandler(UsernameNotFoundException.class) - public AjaxResult handleUsernameNotFoundException(UsernameNotFoundException e) - { - log.error(e.getMessage(), e); - return AjaxResult.error(e.getMessage()); - } - @ExceptionHandler(Exception.class) public AjaxResult handleException(Exception e) { @@ -105,7 +73,16 @@ public class GlobalExceptionHandler String message = e.getBindingResult().getFieldError().getDefaultMessage(); return AjaxResult.error(message); } - + + /** + * 权限异常 + */ + @ExceptionHandler(PreAuthorizeException.class) + public AjaxResult preAuthorizeException(PreAuthorizeException e) + { + return AjaxResult.error("没有权限,请联系管理员授权"); + } + /** * 演示模式异常 */ diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/PermissionService.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/PermissionService.java deleted file mode 100644 index cbfc9bb7..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/PermissionService.java +++ /dev/null @@ -1,167 +0,0 @@ -package com.ruoyi.common.security.service; - -import java.util.Collection; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; -import org.springframework.util.PatternMatchUtils; -import org.springframework.util.StringUtils; -import com.ruoyi.common.security.domain.LoginUser; -import com.ruoyi.common.security.utils.SecurityUtils; - -/** - * 自定义权限实现 - * - * @author ruoyi - */ -@Service("ss") -public class PermissionService -{ - /** 所有权限标识 */ - private static final String ALL_PERMISSION = "*:*:*"; - - /** 管理员角色权限标识 */ - private static final String SUPER_ADMIN = "admin"; - - private static final String ROLE_DELIMETER = ","; - - private static final String PERMISSION_DELIMETER = ","; - - /** - * 验证用户是否具备某权限 - * - * @param permission 权限字符串 - * @return 用户是否具备某权限 - */ - public boolean hasPermi(String permission) - { - if (StringUtils.isEmpty(permission)) - { - return false; - } - LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities())) - { - return false; - } - return hasPermissions(loginUser.getAuthorities(), permission); - } - - /** - * 验证用户是否不具备某权限,与 hasPermi逻辑相反 - * - * @param permission 权限字符串 - * @return 用户是否不具备某权限 - */ - public boolean lacksPermi(String permission) - { - return hasPermi(permission) != true; - } - - /** - * 验证用户是否具有以下任意一个权限 - * - * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表 - * @return 用户是否具有以下任意一个权限 - */ - public boolean hasAnyPermi(String permissions) - { - if (StringUtils.isEmpty(permissions)) - { - return false; - } - LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities())) - { - return false; - } - Collection authorities = loginUser.getAuthorities(); - for (String permission : permissions.split(PERMISSION_DELIMETER)) - { - if (permission != null && hasPermissions(authorities, permission)) - { - return true; - } - } - return false; - } - - /** - * 判断用户是否拥有某个角色 - * - * @param role 角色字符串 - * @return 用户是否具备某角色 - */ - public boolean hasRole(String role) - { - if (StringUtils.isEmpty(role)) - { - return false; - } - LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities())) - { - return false; - } - for (GrantedAuthority authorities : loginUser.getAuthorities()) - { - String roleKey = authorities.getAuthority(); - if (SUPER_ADMIN.contains(roleKey) || roleKey.contains(role)) - { - return true; - } - } - return false; - } - - /** - * 验证用户是否不具备某角色,与 isRole逻辑相反。 - * - * @param role 角色名称 - * @return 用户是否不具备某角色 - */ - public boolean lacksRole(String role) - { - return hasRole(role) != true; - } - - /** - * 验证用户是否具有以下任意一个角色 - * - * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 - * @return 用户是否具有以下任意一个角色 - */ - public boolean hasAnyRoles(String roles) - { - if (StringUtils.isEmpty(roles)) - { - return false; - } - LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities())) - { - return false; - } - for (String role : roles.split(ROLE_DELIMETER)) - { - if (hasRole(role)) - { - return true; - } - } - return false; - } - - /** - * 判断是否包含权限 - * - * @param authorities 权限列表 - * @param permission 权限字符串 - * @return 用户是否具备某权限 - */ - private boolean hasPermissions(Collection authorities, String permission) - { - return authorities.stream().map(GrantedAuthority::getAuthority).filter(StringUtils::hasText) - .anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(permission, x)); - } -} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/RedisClientDetailsService.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/RedisClientDetailsService.java deleted file mode 100644 index 5f0700f0..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/RedisClientDetailsService.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.ruoyi.common.security.service; - -import javax.sql.DataSource; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.security.oauth2.provider.ClientDetails; -import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService; -import com.ruoyi.common.core.constant.CacheConstants; -import com.ruoyi.common.core.constant.SecurityConstants; - -/** - * 重写原生方法支持redis缓存 - * - * @author ruoyi - */ -public class RedisClientDetailsService extends JdbcClientDetailsService -{ - public RedisClientDetailsService(DataSource dataSource) - { - super(dataSource); - super.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT); - super.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT); - } - - @Override - @Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless = "#result == null") - public ClientDetails loadClientByClientId(String clientId) - { - return super.loadClientByClientId(clientId); - } -} diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java new file mode 100644 index 00000000..a4e84283 --- /dev/null +++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java @@ -0,0 +1,123 @@ +package com.ruoyi.common.security.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.common.core.constant.CacheConstants; +import com.ruoyi.common.core.constant.Constants; +import com.ruoyi.common.core.utils.IdUtils; +import com.ruoyi.common.core.utils.SecurityUtils; +import com.ruoyi.common.core.utils.ServletUtils; +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.common.core.utils.ip.IpUtils; +import com.ruoyi.common.redis.service.RedisService; +import com.ruoyi.system.api.model.LoginUser; + +/** + * token验证处理 + * + * @author ruoyi + */ +@Component +public class TokenService +{ + @Autowired + private RedisService redisService; + + private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60; + + private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY; + + protected static final long MILLIS_SECOND = 1000; + + /** + * 创建令牌 + */ + public Map createToken(LoginUser loginUser) + { + // 生成token + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + loginUser.setUserid(loginUser.getSysUser().getUserId()); + loginUser.setUsername(loginUser.getSysUser().getUserName()); + loginUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest())); + refreshToken(loginUser); + + // 保存或更新用户token + Map map = new HashMap(); + map.put("access_token", token); + map.put("expires_in", EXPIRE_TIME); + redisService.setCacheObject(ACCESS_TOKEN + token, loginUser, EXPIRE_TIME, TimeUnit.SECONDS); + return map; + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser() + { + return getLoginUser(ServletUtils.getRequest()); + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = SecurityUtils.getToken(request); + if (StringUtils.isNotEmpty(token)) + { + String userKey = getTokenKey(token); + LoginUser user = redisService.getCacheObject(userKey); + return user; + } + return null; + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + public void delLoginUser(String token) + { + if (StringUtils.isNotEmpty(token)) + { + String userKey = getTokenKey(token); + redisService.deleteObject(userKey); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + EXPIRE_TIME * MILLIS_SECOND); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisService.setCacheObject(userKey, loginUser, EXPIRE_TIME, TimeUnit.SECONDS); + } + + private String getTokenKey(String token) + { + return ACCESS_TOKEN + token; + } +} \ No newline at end of file diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/UserDetailsServiceImpl.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/UserDetailsServiceImpl.java deleted file mode 100644 index 28abc4be..00000000 --- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/UserDetailsServiceImpl.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.ruoyi.common.security.service; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; -import com.ruoyi.common.core.domain.R; -import com.ruoyi.common.core.enums.UserStatus; -import com.ruoyi.common.core.exception.BaseException; -import com.ruoyi.common.core.utils.StringUtils; -import com.ruoyi.common.security.domain.LoginUser; -import com.ruoyi.system.api.RemoteUserService; -import com.ruoyi.system.api.domain.SysUser; -import com.ruoyi.system.api.model.UserInfo; - -/** - * 用户信息处理 - * - * @author ruoyi - */ -@Service -public class UserDetailsServiceImpl implements UserDetailsService -{ - private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); - - @Autowired - private RemoteUserService remoteUserService; - - @Override - public UserDetails loadUserByUsername(String username) - { - R userResult = remoteUserService.getUserInfo(username); - checkUser(userResult, username); - return getUserDetails(userResult); - } - - public void checkUser(R userResult, String username) - { - if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) - { - log.info("登录用户:{} 不存在.", username); - throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); - } - else if (UserStatus.DELETED.getCode().equals(userResult.getData().getSysUser().getDelFlag())) - { - log.info("登录用户:{} 已被删除.", username); - throw new BaseException("对不起,您的账号:" + username + " 已被删除"); - } - else if (UserStatus.DISABLE.getCode().equals(userResult.getData().getSysUser().getStatus())) - { - log.info("登录用户:{} 已被停用.", username); - throw new BaseException("对不起,您的账号:" + username + " 已停用"); - } - } - - private UserDetails getUserDetails(R result) - { - UserInfo info = result.getData(); - Set dbAuthsSet = new HashSet(); - if (StringUtils.isNotEmpty(info.getRoles())) - { - // 获取角色 - dbAuthsSet.addAll(info.getRoles()); - // 获取权限 - dbAuthsSet.addAll(info.getPermissions()); - } - - Collection authorities = AuthorityUtils - .createAuthorityList(dbAuthsSet.toArray(new String[0])); - SysUser user = info.getSysUser(); - - return new LoginUser(user.getUserId(), user.getUserName(), user.getPassword(), true, true, true, true, - authorities); - } -} diff --git a/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring.factories b/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring.factories index e569c125..1f6338d7 100644 --- a/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring.factories +++ b/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring.factories @@ -1,8 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.ruoyi.common.security.service.UserDetailsServiceImpl,\ - com.ruoyi.common.security.service.PermissionService,\ - com.ruoyi.common.security.config.MethodSecurityConfig,\ - com.ruoyi.common.security.handler.CustomAccessDeniedHandler,\ + com.ruoyi.common.security.service.TokenService,\ + com.ruoyi.common.security.aspect.PreAuthorizeAspect,\ com.ruoyi.common.security.handler.GlobalExceptionHandler - - diff --git a/ruoyi-common/ruoyi-common-swagger/pom.xml b/ruoyi-common/ruoyi-common-swagger/pom.xml index 9de85993..e58f4413 100644 --- a/ruoyi-common/ruoyi-common-swagger/pom.xml +++ b/ruoyi-common/ruoyi-common-swagger/pom.xml @@ -5,7 +5,7 @@ com.ruoyi ruoyi-common - 2.0.0 + 2.5.0 4.0.0 diff --git a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java index 204c2c86..1a9597dc 100644 --- a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java +++ b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java @@ -2,7 +2,6 @@ package com.ruoyi.common.swagger.config; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -15,11 +14,9 @@ import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; import springfox.documentation.service.AuthorizationScope; import springfox.documentation.service.Contact; -import springfox.documentation.service.GrantType; -import springfox.documentation.service.OAuth; -import springfox.documentation.service.ResourceOwnerPasswordCredentialsGrant; import springfox.documentation.service.SecurityReference; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; @@ -66,54 +63,55 @@ public class SwaggerAutoConfiguration List> excludePath = new ArrayList<>(); swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path))); - //noinspection Guava + //noinspection Guava return new Docket(DocumentationType.SWAGGER_2) .host(swaggerProperties.getHost()) .apiInfo(apiInfo(swaggerProperties)).select() .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())) .paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath))) .build() - .securitySchemes(Collections.singletonList(securitySchema())) - .securityContexts(Collections.singletonList(securityContext())) + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()) .pathMapping("/"); } - /** - * 配置默认的全局鉴权策略的开关,通过正则表达式进行匹配;默认匹配所有URL - * - * @return - */ - private SecurityContext securityContext() + /** + * 安全模式,这里指定token通过Authorization头请求头传递 + */ + private List securitySchemes() { - return SecurityContext.builder() - .securityReferences(defaultAuth()) - .forPaths(PathSelectors.regex(swaggerProperties().getAuthorization().getAuthRegex())) - .build(); + List apiKeyList = new ArrayList(); + apiKeyList.add(new ApiKey("Authorization", "Authorization", "header")); + return apiKeyList; } - /** - * 默认的全局鉴权策略 - * - * @return - */ - private List defaultAuth() + /** + * 安全上下文 + */ + private List securityContexts() { - ArrayList authorizationScopeList = new ArrayList<>(); - swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription()))); - AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()]; - return Collections.singletonList(SecurityReference.builder() - .reference(swaggerProperties().getAuthorization().getName()) - .scopes(authorizationScopeList.toArray(authorizationScopes)) - .build()); + List securityContexts = new ArrayList<>(); + securityContexts.add( + SecurityContext.builder() + .securityReferences(defaultAuth()) + .forPaths(PathSelectors.regex("^(?!auth).*$")) + .build()); + return securityContexts; } - private OAuth securitySchema() + /** + * 默认的全局鉴权策略 + * + * @return + */ + private List defaultAuth() { - ArrayList authorizationScopeList = new ArrayList<>(); - swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription()))); - ArrayList grantTypes = new ArrayList<>(); - swaggerProperties().getAuthorization().getTokenUrlList().forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl))); - return new OAuth(swaggerProperties().getAuthorization().getName(), authorizationScopeList, grantTypes); + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); + return securityReferences; } private ApiInfo apiInfo(SwaggerProperties swaggerProperties) @@ -128,5 +126,4 @@ public class SwaggerAutoConfiguration .version(swaggerProperties.getVersion()) .build(); } - } - +} diff --git a/ruoyi-gateway/pom.xml b/ruoyi-gateway/pom.xml index 98668782..54605582 100644 --- a/ruoyi-gateway/pom.xml +++ b/ruoyi-gateway/pom.xml @@ -4,7 +4,7 @@ com.ruoyi ruoyi - 2.0.0 + 2.5.0 4.0.0 @@ -85,6 +85,7 @@ + ${project.artifactId} org.springframework.boot diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java index d1f6ec14..af0fcc84 100644 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java +++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java @@ -28,4 +28,4 @@ public class RuoYiGatewayApplication " | | \\ / \\ / \n" + " ''-' `'-' `-..-' "); } -} \ No newline at end of file +} diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java index c642ffeb..963425e6 100644 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java +++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java @@ -5,6 +5,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.util.Config; +import static com.google.code.kaptcha.Constants.*; /** * 验证码配置 @@ -14,42 +15,67 @@ import com.google.code.kaptcha.util.Config; @Configuration public class CaptchaConfig { + @Bean(name = "captchaProducer") + public DefaultKaptcha getKaptchaBean() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + @Bean(name = "captchaProducerMath") public DefaultKaptcha getKaptchaBeanMath() { DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); Properties properties = new Properties(); // 是否有边框 默认为true 我们可以自己设置yes,no - properties.setProperty("kaptcha.border", "yes"); + properties.setProperty(KAPTCHA_BORDER, "yes"); // 边框颜色 默认为Color.BLACK - properties.setProperty("kaptcha.border.color", "105,179,90"); + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); // 验证码文本字符颜色 默认为Color.BLACK - properties.setProperty("kaptcha.textproducer.font.color", "blue"); + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); // 验证码图片宽度 默认为200 - properties.setProperty("kaptcha.image.width", "160"); + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); // 验证码图片高度 默认为50 - properties.setProperty("kaptcha.image.height", "60"); + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); // 验证码文本字符大小 默认为40 - properties.setProperty("kaptcha.textproducer.font.size", "35"); + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); // KAPTCHA_SESSION_KEY - properties.setProperty("kaptcha.session.key", "kaptchaCodeMath"); + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); // 验证码文本生成器 - properties.setProperty("kaptcha.textproducer.impl", "com.ruoyi.gateway.config.KaptchaTextCreator"); + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.gateway.config.KaptchaTextCreator"); // 验证码文本字符间距 默认为2 - properties.setProperty("kaptcha.textproducer.char.space", "3"); + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); // 验证码文本字符长度 默认为5 - properties.setProperty("kaptcha.textproducer.char.length", "6"); - // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, - // fontSize) - properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier"); + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); // 验证码噪点颜色 默认为Color.BLACK - properties.setProperty("kaptcha.noise.color", "white"); + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); // 干扰实现类 - properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise"); - // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple - // 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy - // 阴影com.google.code.kaptcha.impl.ShadowGimpy - properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy"); + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); Config config = new Config(properties); defaultKaptcha.setConfig(config); return defaultKaptcha; diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java index c0bdd7a4..0da5fa06 100644 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java +++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java @@ -10,6 +10,11 @@ import org.springframework.stereotype.Component; import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResourcesProvider; +/** + * 聚合系统接口 + * + * @author ruoyi + */ @Component public class SwaggerProvider implements SwaggerResourcesProvider { diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreWhiteProperties.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreWhiteProperties.java new file mode 100644 index 00000000..fbfd04cc --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreWhiteProperties.java @@ -0,0 +1,33 @@ +package com.ruoyi.gateway.config.properties; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +/** + * 放行白名单配置 + * + * @author ruoyi + */ +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "ignore") +public class IgnoreWhiteProperties +{ + /** + * 放行白名单配置,网关不校验此处的白名单 + */ + private List whites = new ArrayList<>(); + + public List getWhites() + { + return whites; + } + + public void setWhites(List whites) + { + this.whites = whites; + } +} diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java new file mode 100644 index 00000000..1bd39940 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java @@ -0,0 +1,125 @@ +package com.ruoyi.gateway.filter; + +import javax.annotation.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.core.constant.CacheConstants; +import com.ruoyi.common.core.constant.Constants; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.utils.ServletUtils; +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.common.redis.service.RedisService; +import com.ruoyi.gateway.config.properties.IgnoreWhiteProperties; +import reactor.core.publisher.Mono; + +/** + * 网关鉴权 + * + * @author ruoyi + */ +@Component +public class AuthFilter implements GlobalFilter, Ordered +{ + private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); + + private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60; + + // 排除过滤的 uri 地址,nacos自行添加 + @Autowired + private IgnoreWhiteProperties ignoreWhite; + + @Resource(name = "stringRedisTemplate") + private ValueOperations sops; + + @Autowired + private RedisService redisService; + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) + { + String url = exchange.getRequest().getURI().getPath(); + // 跳过不需要验证的路径 + if (StringUtils.matches(url, ignoreWhite.getWhites())) + { + return chain.filter(exchange); + } + String token = getToken(exchange.getRequest()); + if (StringUtils.isBlank(token)) + { + return setUnauthorizedResponse(exchange, "令牌不能为空"); + } + String userStr = sops.get(getTokenKey(token)); + if (StringUtils.isNull(userStr)) + { + return setUnauthorizedResponse(exchange, "登录状态已过期"); + } + JSONObject obj = JSONObject.parseObject(userStr); + String userid = obj.getString("userid"); + String username = obj.getString("username"); + if (StringUtils.isBlank(userid) || StringUtils.isBlank(username)) + { + return setUnauthorizedResponse(exchange, "令牌验证失败"); + } + + // 设置过期时间 + redisService.expire(getTokenKey(token), EXPIRE_TIME); + // 设置用户信息到请求 + ServerHttpRequest mutableReq = exchange.getRequest().mutate().header(CacheConstants.DETAILS_USER_ID, userid) + .header(CacheConstants.DETAILS_USERNAME, ServletUtils.urlEncode(username)).build(); + ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build(); + + return chain.filter(mutableExchange); + } + + private Mono setUnauthorizedResponse(ServerWebExchange exchange, String msg) + { + ServerHttpResponse response = exchange.getResponse(); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + response.setStatusCode(HttpStatus.OK); + + log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); + + return response.writeWith(Mono.fromSupplier(() -> { + DataBufferFactory bufferFactory = response.bufferFactory(); + return bufferFactory.wrap(JSON.toJSONBytes(R.fail(msg))); + })); + } + + private String getTokenKey(String token) + { + return CacheConstants.LOGIN_TOKEN_KEY + token; + } + + /** + * 获取请求token + */ + private String getToken(ServerHttpRequest request) + { + String token = request.getHeaders().getFirst(CacheConstants.HEADER); + if (StringUtils.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX)) + { + token = token.replace(CacheConstants.TOKEN_PREFIX, ""); + } + return token; + } + + @Override + public int getOrder() + { + return -200; + } +} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/CacheRequestFilter.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/CacheRequestFilter.java new file mode 100644 index 00000000..2045d3ed --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/CacheRequestFilter.java @@ -0,0 +1,100 @@ +package com.ruoyi.gateway.filter; + +import java.util.Collections; +import java.util.List; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpMethod; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Component +public class CacheRequestFilter extends AbstractGatewayFilterFactory +{ + public CacheRequestFilter() + { + super(Config.class); + } + + @Override + public String name() + { + return "CacheRequestFilter"; + } + + @Override + public GatewayFilter apply(Config config) + { + CacheRequestGatewayFilter cacheRequestGatewayFilter = new CacheRequestGatewayFilter(); + Integer order = config.getOrder(); + if (order == null) + { + return cacheRequestGatewayFilter; + } + return new OrderedGatewayFilter(cacheRequestGatewayFilter, order); + } + + public static class CacheRequestGatewayFilter implements GatewayFilter + { + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) + { + // GET DELETE 不过滤 + HttpMethod method = exchange.getRequest().getMethod(); + if (method == null || method.matches("GET") || method.matches("DELETE")) + { + return chain.filter(exchange); + } + return DataBufferUtils.join(exchange.getRequest().getBody()).map(dataBuffer -> { + byte[] bytes = new byte[dataBuffer.readableByteCount()]; + dataBuffer.read(bytes); + DataBufferUtils.release(dataBuffer); + return bytes; + }).defaultIfEmpty(new byte[0]).flatMap(bytes -> { + DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory(); + ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) + { + @Override + public Flux getBody() + { + if (bytes.length > 0) + { + return Flux.just(dataBufferFactory.wrap(bytes)); + } + return Flux.empty(); + } + }; + return chain.filter(exchange.mutate().request(decorator).build()); + }); + } + } + + @Override + public List shortcutFieldOrder() + { + return Collections.singletonList("order"); + } + + static class Config + { + private Integer order; + + public Integer getOrder() + { + return order; + } + + public void setOrder(Integer order) + { + this.order = order; + } + } +} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java index cc4d673c..561111ba 100644 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java +++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java @@ -1,16 +1,22 @@ package com.ruoyi.gateway.filter; +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; -import org.springframework.http.HttpHeaders; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.gateway.service.ValidateCodeService; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** @@ -21,13 +27,11 @@ import reactor.core.publisher.Mono; @Component public class ValidateCodeFilter extends AbstractGatewayFilterFactory { - private final static String AUTH_URL = "/oauth/token"; + private final static String AUTH_URL = "/auth/login"; @Autowired private ValidateCodeService validateCodeService; - private static final String BASIC_ = "Basic "; - private static final String CODE = "code"; private static final String UUID = "uuid"; @@ -44,25 +48,33 @@ public class ValidateCodeFilter extends AbstractGatewayFilterFactory return chain.filter(exchange); } - // 消息头存在内容,且不存在验证码参数,不处理 - String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION); - if (StringUtils.isNotEmpty(header) && StringUtils.startsWith(header, BASIC_) - && !request.getQueryParams().containsKey(CODE) && !request.getQueryParams().containsKey(UUID)) - { - return chain.filter(exchange); - } try { - validateCodeService.checkCapcha(request.getQueryParams().getFirst(CODE), - request.getQueryParams().getFirst(UUID)); + String rspStr = resolveBodyFromRequest(request); + JSONObject obj = JSONObject.parseObject(rspStr); + validateCodeService.checkCapcha(obj.getString(CODE), obj.getString(UUID)); } catch (Exception e) { ServerHttpResponse response = exchange.getResponse(); + response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); return exchange.getResponse().writeWith( Mono.just(response.bufferFactory().wrap(JSON.toJSONBytes(AjaxResult.error(e.getMessage()))))); } return chain.filter(exchange); }; } + + private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) + { + // 获取请求体 + Flux body = serverHttpRequest.getBody(); + AtomicReference bodyRef = new AtomicReference<>(); + body.subscribe(buffer -> { + CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); + DataBufferUtils.release(buffer); + bodyRef.set(charBuffer.toString()); + }); + return bodyRef.get(); + } } diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SentinelFallbackHandler.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SentinelFallbackHandler.java index 60a006f2..80c64605 100644 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SentinelFallbackHandler.java +++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SentinelFallbackHandler.java @@ -12,7 +12,7 @@ import reactor.core.publisher.Mono; /** * 自定义限流异常处理 - * + * * @author ruoyi */ public class SentinelFallbackHandler implements WebExceptionHandler @@ -21,7 +21,7 @@ public class SentinelFallbackHandler implements WebExceptionHandler { ServerHttpResponse serverHttpResponse = exchange.getResponse(); serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); - byte[] datas = "{\"status\":429,\"message\":\"请求超过最大数,请稍后再试\"}".getBytes(StandardCharsets.UTF_8); + byte[] datas = "{\"code\":429,\"msg\":\"请求超过最大数,请稍后再试\"}".getBytes(StandardCharsets.UTF_8); DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas); return serverHttpResponse.writeWith(Mono.just(buffer)); } diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java index 3af3f25d..873ef39b 100644 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java +++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java @@ -3,6 +3,7 @@ package com.ruoyi.gateway.service.impl; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; import javax.imageio.ImageIO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -25,28 +26,46 @@ import com.ruoyi.gateway.service.ValidateCodeService; @Service public class ValidateCodeServiceImpl implements ValidateCodeService { - @Autowired - private Producer producer; + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; @Autowired private RedisService redisService; + // 验证码类型 + private String captchaType = "math"; + /** * 生成验证码 */ @Override public AjaxResult createCapcha() throws IOException, CaptchaException { - // 生成验证码 - String capText = producer.createText(); - String capStr = capText.substring(0, capText.lastIndexOf("@")); - String verifyCode = capText.substring(capText.lastIndexOf("@") + 1); - BufferedImage image = producer.createImage(capStr); // 保存验证码信息 String uuid = IdUtils.simpleUUID(); String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; - redisService.setCacheObject(verifyKey, verifyCode, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + String capStr = null, code = null; + BufferedImage image = null; + + // 生成验证码 + if ("math".equals(captchaType)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(captchaType)) + { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + + redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); // 转换流信息写出 FastByteArrayOutputStream os = new FastByteArrayOutputStream(); try diff --git a/ruoyi-gateway/src/main/resources/bootstrap.yml b/ruoyi-gateway/src/main/resources/bootstrap.yml index 5f9f269e..0aa81c22 100644 --- a/ruoyi-gateway/src/main/resources/bootstrap.yml +++ b/ruoyi-gateway/src/main/resources/bootstrap.yml @@ -23,7 +23,8 @@ spring: # 配置文件格式 file-extension: yml # 共享配置 - shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} sentinel: # 取消控制台懒加载 eager: true diff --git a/ruoyi-gateway/src/main/resources/logback.xml b/ruoyi-gateway/src/main/resources/logback.xml new file mode 100644 index 00000000..f8e7f8af --- /dev/null +++ b/ruoyi-gateway/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-modules/pom.xml b/ruoyi-modules/pom.xml index 876391dd..0a4eb86d 100644 --- a/ruoyi-modules/pom.xml +++ b/ruoyi-modules/pom.xml @@ -4,7 +4,7 @@ com.ruoyi ruoyi - 2.0.0 + 2.5.0 4.0.0 @@ -12,6 +12,7 @@ ruoyi-system ruoyi-gen ruoyi-job + ruoyi-file ruoyi-modules diff --git a/ruoyi-modules/ruoyi-file/pom.xml b/ruoyi-modules/ruoyi-file/pom.xml new file mode 100644 index 00000000..3abc27dc --- /dev/null +++ b/ruoyi-modules/ruoyi-file/pom.xml @@ -0,0 +1,95 @@ + + + + com.ruoyi + ruoyi-modules + 2.5.0 + + 4.0.0 + + ruoyi-modules-file + + + ruoyi-modules-file文件服务 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + io.springfox + springfox-swagger-ui + ${swagger.fox.version} + + + + + com.github.tobato + fastdfs-client + + + + + io.minio + minio + ${minio.version} + + + + + com.ruoyi + ruoyi-api-system + + + + + com.ruoyi + ruoyi-common-swagger + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/RuoYFileApplication.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/RuoYFileApplication.java new file mode 100644 index 00000000..c2c29148 --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/RuoYFileApplication.java @@ -0,0 +1,33 @@ +package com.ruoyi.file; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2; + +/** + * 文件服务 + * + * @author ruoyi + */ +@EnableCustomSwagger2 +@EnableDiscoveryClient +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) +public class RuoYFileApplication +{ + public static void main(String[] args) + { + SpringApplication.run(RuoYFileApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 文件服务模块启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/config/MinioConfig.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/config/MinioConfig.java new file mode 100644 index 00000000..57826e3e --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/config/MinioConfig.java @@ -0,0 +1,82 @@ +package com.ruoyi.file.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import io.minio.MinioClient; + +/** + * Minio 配置信息 + * + * @author ruoiy + */ +@Configuration +@ConfigurationProperties(prefix = "minio") +public class MinioConfig +{ + /** + * 服务地址 + */ + private String url; + + /** + * 用户名 + */ + private String accessKey; + + /** + * 密码 + */ + private String secretKey; + + /** + * 存储桶名称 + */ + private String bucketName; + + public String getUrl() + { + return url; + } + + public void setUrl(String url) + { + this.url = url; + } + + public String getAccessKey() + { + return accessKey; + } + + public void setAccessKey(String accessKey) + { + this.accessKey = accessKey; + } + + public String getSecretKey() + { + return secretKey; + } + + public void setSecretKey(String secretKey) + { + this.secretKey = secretKey; + } + + public String getBucketName() + { + return bucketName; + } + + public void setBucketName(String bucketName) + { + this.bucketName = bucketName; + } + + @Bean + public MinioClient getMinioClient() + { + return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build(); + } +} diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/config/ResourcesConfig.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/config/ResourcesConfig.java new file mode 100644 index 00000000..7e443309 --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/config/ResourcesConfig.java @@ -0,0 +1,36 @@ +package com.ruoyi.file.config; + +import java.io.File; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 通用映射配置 + * + * @author ruoyi + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + /** + * 上传文件存储在本地的根路径 + */ + @Value("${file.path}") + private String localFilePath; + + /** + * 资源映射路径 前缀 + */ + @Value("${file.prefix}") + public String localFilePrefix; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** 本地文件上传路径 */ + registry.addResourceHandler(localFilePrefix + "/**") + .addResourceLocations("file:" + localFilePath + File.separator); + } +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/controller/SysFileController.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/controller/SysFileController.java new file mode 100644 index 00000000..318db416 --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/controller/SysFileController.java @@ -0,0 +1,48 @@ +package com.ruoyi.file.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.utils.file.FileUtils; +import com.ruoyi.file.service.ISysFileService; +import com.ruoyi.system.api.domain.SysFile; + +/** + * 文件请求处理 + * + * @author ruoyi + */ +@RestController +public class SysFileController +{ + private static final Logger log = LoggerFactory.getLogger(SysFileController.class); + + @Autowired + private ISysFileService sysFileService; + + /** + * 文件上传请求 + */ + @PostMapping("upload") + public R upload(MultipartFile file) + { + try + { + // 上传并返回访问地址 + String url = sysFileService.uploadFile(file); + SysFile sysFile = new SysFile(); + sysFile.setName(FileUtils.getName(url)); + sysFile.setUrl(url); + return R.ok(sysFile); + } + catch (Exception e) + { + log.error("上传文件失败", e); + return R.fail(e.getMessage()); + } + } +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsSysFileServiceImpl.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsSysFileServiceImpl.java new file mode 100644 index 00000000..deee22ee --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsSysFileServiceImpl.java @@ -0,0 +1,42 @@ +package com.ruoyi.file.service; + +import org.apache.commons.io.FilenameUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import com.github.tobato.fastdfs.domain.fdfs.StorePath; +import com.github.tobato.fastdfs.service.FastFileStorageClient; + +/** + * FastDFS 文件存储 + * + * @author ruoyi + */ +@Service +public class FastDfsSysFileServiceImpl implements ISysFileService +{ + /** + * 域名或本机访问地址 + */ + @Value("${fdfs.domain}") + public String domain; + + @Autowired + private FastFileStorageClient storageClient; + + /** + * FastDfs文件上传接口 + * + * @param file 上传的文件 + * @return 访问地址 + * @throws Exception + */ + @Override + public String uploadFile(MultipartFile file) throws Exception + { + StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(), + FilenameUtils.getExtension(file.getOriginalFilename()), null); + return domain + "/" + storePath.getFullPath(); + } +} diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/ISysFileService.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/ISysFileService.java new file mode 100644 index 00000000..5a353489 --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/ISysFileService.java @@ -0,0 +1,20 @@ +package com.ruoyi.file.service; + +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件上传接口 + * + * @author ruoyi + */ +public interface ISysFileService +{ + /** + * 文件上传接口 + * + * @param file 上传的文件 + * @return 访问地址 + * @throws Exception + */ + public String uploadFile(MultipartFile file) throws Exception; +} diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/LocalSysFileServiceImpl.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/LocalSysFileServiceImpl.java new file mode 100644 index 00000000..f416f98d --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/LocalSysFileServiceImpl.java @@ -0,0 +1,49 @@ +package com.ruoyi.file.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.file.utils.FileUploadUtils; + +/** + * 本地文件存储 + * + * @author ruoyi + */ +@Primary +@Service +public class LocalSysFileServiceImpl implements ISysFileService +{ + /** + * 资源映射路径 前缀 + */ + @Value("${file.prefix}") + public String localFilePrefix; + + /** + * 域名或本机访问地址 + */ + @Value("${file.domain}") + public String domain; + + /** + * 上传文件存储在本地的根路径 + */ + @Value("${file.path}") + private String localFilePath; + + /** + * 本地文件上传接口 + * + * @param file 上传的文件 + * @return 访问地址 + * @throws Exception + */ + public String uploadFile(MultipartFile file) throws Exception + { + String name = FileUploadUtils.upload(localFilePath, file); + String url = domain + localFilePrefix + name; + return url; + } +} diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/MinioSysFileServiceImpl.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/MinioSysFileServiceImpl.java new file mode 100644 index 00000000..4a6f2081 --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/MinioSysFileServiceImpl.java @@ -0,0 +1,44 @@ +package com.ruoyi.file.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.file.config.MinioConfig; +import com.ruoyi.file.utils.FileUploadUtils; +import io.minio.MinioClient; +import io.minio.PutObjectArgs; + +/** + * Minio 文件存储 + * + * @author ruoyi + */ +@Service +public class MinioSysFileServiceImpl implements ISysFileService +{ + @Autowired + private MinioConfig minioConfig; + + @Autowired + private MinioClient client; + + /** + * 本地文件上传接口 + * + * @param file 上传的文件 + * @return 访问地址 + * @throws Exception + */ + public String uploadFile(MultipartFile file) throws Exception + { + String fileName = FileUploadUtils.extractFilename(file); + PutObjectArgs args = PutObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(fileName) + .stream(file.getInputStream(), file.getSize(), -1) + .contentType(file.getContentType()) + .build(); + client.putObject(args); + return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName; + } +} diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/utils/FileUploadUtils.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/utils/FileUploadUtils.java new file mode 100644 index 00000000..a49168c2 --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/utils/FileUploadUtils.java @@ -0,0 +1,197 @@ +package com.ruoyi.file.utils; + +import java.io.File; +import java.io.IOException; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.core.exception.file.FileNameLengthLimitExceededException; +import com.ruoyi.common.core.exception.file.FileSizeLimitExceededException; +import com.ruoyi.common.core.exception.file.InvalidExtensionException; +import com.ruoyi.common.core.utils.DateUtils; +import com.ruoyi.common.core.utils.IdUtils; +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.common.core.utils.file.MimeTypeUtils; + +/** + * 文件上传工具类 + * + * @author ruoyi + */ +public class FileUploadUtils +{ + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param extension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = file.getOriginalFilename().length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + File desc = getAbsoluteFile(baseDir, fileName); + file.transferTo(desc); + String pathFileName = getPathFileName(fileName); + return pathFileName; + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) + { + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; + return fileName; + } + + private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) + { + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + } + return desc.isAbsolute() ? desc : desc.getAbsoluteFile(); + } + + private static final String getPathFileName(String fileName) throws IOException + { + String pathFileName = "/" + fileName; + return pathFileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(file.getContentType()); + } + return extension; + } +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-file/src/main/resources/banner.txt b/ruoyi-modules/ruoyi-file/src/main/resources/banner.txt new file mode 100644 index 00000000..12892dd4 --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} + _ __ _ _ + (_) / _|(_)| | + _ __ _ _ ___ _ _ _ ______ | |_ _ | | ___ +| '__|| | | | / _ \ | | | || ||______|| _|| || | / _ \ +| | | |_| || (_) || |_| || | | | | || || __/ +|_| \__,_| \___/ \__, ||_| |_| |_||_| \___| + __/ | + |___/ \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-file/src/main/resources/bootstrap.yml b/ruoyi-modules/ruoyi-file/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..55ff4112 --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/resources/bootstrap.yml @@ -0,0 +1,25 @@ +# Tomcat +server: + port: 9300 + +# Spring +spring: + application: + # 应用名称 + name: ruoyi-file + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: 127.0.0.1:8848 + config: + # 配置中心地址 + server-addr: 127.0.0.1:8848 + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/ruoyi-modules/ruoyi-file/src/main/resources/logback.xml b/ruoyi-modules/ruoyi-file/src/main/resources/logback.xml new file mode 100644 index 00000000..9c38c7f7 --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-gen/pom.xml b/ruoyi-modules/ruoyi-gen/pom.xml index 62228dee..ca5369bb 100644 --- a/ruoyi-modules/ruoyi-gen/pom.xml +++ b/ruoyi-modules/ruoyi-gen/pom.xml @@ -5,7 +5,7 @@ com.ruoyi ruoyi-modules - 2.0.0 + 2.5.0 4.0.0 @@ -35,10 +35,10 @@ spring-cloud-starter-alibaba-sentinel - + org.springframework.boot - spring-boot-starter-web + spring-boot-starter-actuator @@ -60,19 +60,13 @@ mysql-connector-java - - - com.ruoyi - ruoyi-common-security - - - + com.ruoyi ruoyi-common-log - + com.ruoyi ruoyi-common-swagger @@ -81,6 +75,7 @@ + ${project.artifactId} org.springframework.boot diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/controller/GenController.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/controller/GenController.java index f4492f5d..49c146df 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/controller/GenController.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/controller/GenController.java @@ -7,7 +7,6 @@ import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -23,6 +22,7 @@ import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.web.page.TableDataInfo; import com.ruoyi.common.log.annotation.Log; import com.ruoyi.common.log.enums.BusinessType; +import com.ruoyi.common.security.annotation.PreAuthorize; import com.ruoyi.gen.domain.GenTable; import com.ruoyi.gen.domain.GenTableColumn; import com.ruoyi.gen.service.IGenTableColumnService; @@ -46,7 +46,7 @@ public class GenController extends BaseController /** * 查询代码生成列表 */ - @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @PreAuthorize(hasPermi = "tool:gen:list") @GetMapping("/list") public TableDataInfo genList(GenTable genTable) { @@ -58,22 +58,24 @@ public class GenController extends BaseController /** * 修改代码生成业务 */ - @PreAuthorize("@ss.hasPermi('tool:gen:query')") + @PreAuthorize(hasPermi = "tool:gen:query") @GetMapping(value = "/{talbleId}") public AjaxResult getInfo(@PathVariable Long talbleId) { GenTable table = genTableService.selectGenTableById(talbleId); + List tables = genTableService.selectGenTableAll(); List list = genTableColumnService.selectGenTableColumnListByTableId(talbleId); Map map = new HashMap(); map.put("info", table); map.put("rows", list); + map.put("tables", tables); return AjaxResult.success(map); } /** * 查询数据库列表 */ - @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @PreAuthorize(hasPermi = "tool:gen:list") @GetMapping("/db/list") public TableDataInfo dataList(GenTable genTable) { @@ -85,7 +87,6 @@ public class GenController extends BaseController /** * 查询数据表字段列表 */ - @PreAuthorize("@ss.hasPermi('tool:gen:list')") @GetMapping(value = "/column/{talbleId}") public TableDataInfo columnList(Long tableId) { @@ -99,7 +100,7 @@ public class GenController extends BaseController /** * 导入表结构(保存) */ - @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @PreAuthorize(hasPermi = "tool:gen:list") @Log(title = "代码生成", businessType = BusinessType.IMPORT) @PostMapping("/importTable") public AjaxResult importTableSave(String tables) @@ -114,7 +115,7 @@ public class GenController extends BaseController /** * 修改保存代码生成业务 */ - @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @PreAuthorize(hasPermi = "tool:gen:edit") @Log(title = "代码生成", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult editSave(@Validated @RequestBody GenTable genTable) @@ -127,7 +128,7 @@ public class GenController extends BaseController /** * 删除代码生成 */ - @PreAuthorize("@ss.hasPermi('tool:gen:remove')") + @PreAuthorize(hasPermi = "tool:gen:remove") @Log(title = "代码生成", businessType = BusinessType.DELETE) @DeleteMapping("/{tableIds}") public AjaxResult remove(@PathVariable Long[] tableIds) @@ -139,7 +140,7 @@ public class GenController extends BaseController /** * 预览代码 */ - @PreAuthorize("@ss.hasPermi('tool:gen:preview')") + @PreAuthorize(hasPermi = "tool:gen:preview") @GetMapping("/preview/{tableId}") public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException { @@ -148,27 +149,51 @@ public class GenController extends BaseController } /** - * 生成代码 + * 生成代码(下载方式) */ - @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @PreAuthorize(hasPermi = "tool:gen:code") @Log(title = "代码生成", businessType = BusinessType.GENCODE) - @GetMapping("/genCode/{tableName}") - public void genCode(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException + @GetMapping("/download/{tableName}") + public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException { - byte[] data = genTableService.generatorCode(tableName); + byte[] data = genTableService.downloadCode(tableName); genCode(response, data); } + /** + * 生成代码(自定义路径) + */ + @PreAuthorize(hasPermi = "tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public AjaxResult genCode(@PathVariable("tableName") String tableName) + { + genTableService.generatorCode(tableName); + return AjaxResult.success(); + } + + /** + * 同步数据库 + */ + @PreAuthorize(hasPermi = "tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public AjaxResult synchDb(@PathVariable("tableName") String tableName) + { + genTableService.synchDb(tableName); + return AjaxResult.success(); + } + /** * 批量生成代码 */ - @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @PreAuthorize(hasPermi = "tool:gen:code") @Log(title = "代码生成", businessType = BusinessType.GENCODE) @GetMapping("/batchGenCode") public void batchGenCode(HttpServletResponse response, String tables) throws IOException { String[] tableNames = Convert.toStrArray(tables); - byte[] data = genTableService.generatorCode(tableNames); + byte[] data = genTableService.downloadCode(tableNames); genCode(response, data); } @@ -183,4 +208,4 @@ public class GenController extends BaseController response.setContentType("application/octet-stream; charset=UTF-8"); IOUtils.write(data, response.getOutputStream()); } -} \ No newline at end of file +} diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTable.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTable.java index 86fc86ad..d08b7325 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTable.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTable.java @@ -28,11 +28,17 @@ public class GenTable extends BaseEntity @NotBlank(message = "表描述不能为空") private String tableComment; + /** 关联父表的表名 */ + private String subTableName; + + /** 本表关联父表的外键名 */ + private String subTableFkName; + /** 实体类名称(首字母大写) */ @NotBlank(message = "实体类名称不能为空") private String className; - /** 使用的模板(crud单表操作 tree树表操作) */ + /** 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ private String tplCategory; /** 生成包路径 */ @@ -55,9 +61,18 @@ public class GenTable extends BaseEntity @NotBlank(message = "作者不能为空") private String functionAuthor; + /** 生成代码方式(0zip压缩包 1自定义路径) */ + private String genType; + + /** 生成路径(不填默认项目路径) */ + private String genPath; + /** 主键信息 */ private GenTableColumn pkColumn; + /** 子表信息 */ + private GenTable subTable; + /** 表列信息 */ @Valid private List columns; @@ -74,6 +89,12 @@ public class GenTable extends BaseEntity /** 树名称字段 */ private String treeName; + /** 上级菜单ID字段 */ + private String parentMenuId; + + /** 上级菜单名称字段 */ + private String parentMenuName; + public Long getTableId() { return tableId; @@ -104,6 +125,26 @@ public class GenTable extends BaseEntity this.tableComment = tableComment; } + public String getSubTableName() + { + return subTableName; + } + + public void setSubTableName(String subTableName) + { + this.subTableName = subTableName; + } + + public String getSubTableFkName() + { + return subTableFkName; + } + + public void setSubTableFkName(String subTableFkName) + { + this.subTableFkName = subTableFkName; + } + public String getClassName() { return className; @@ -174,6 +215,26 @@ public class GenTable extends BaseEntity this.functionAuthor = functionAuthor; } + public String getGenType() + { + return genType; + } + + public void setGenType(String genType) + { + this.genType = genType; + } + + public String getGenPath() + { + return genPath; + } + + public void setGenPath(String genPath) + { + this.genPath = genPath; + } + public GenTableColumn getPkColumn() { return pkColumn; @@ -184,6 +245,15 @@ public class GenTable extends BaseEntity this.pkColumn = pkColumn; } + public GenTable getSubTable() + { + return subTable; + } + + public void setSubTable(GenTable subTable) + { + this.subTable = subTable; + } public List getColumns() { return columns; @@ -234,6 +304,35 @@ public class GenTable extends BaseEntity this.treeName = treeName; } + public String getParentMenuId() + { + return parentMenuId; + } + + public void setParentMenuId(String parentMenuId) + { + this.parentMenuId = parentMenuId; + } + + public String getParentMenuName() + { + return parentMenuName; + } + + public void setParentMenuName(String parentMenuName) + { + this.parentMenuName = parentMenuName; + } + + public boolean isSub() + { + return isSub(this.tplCategory); + } + + public static boolean isSub(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); + } public boolean isTree() { return isTree(this.tplCategory); @@ -268,4 +367,4 @@ public class GenTable extends BaseEntity } return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); } -} \ No newline at end of file +} diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTableColumn.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTableColumn.java index d69e219e..2f3e99df 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTableColumn.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTableColumn.java @@ -60,7 +60,7 @@ public class GenTableColumn extends BaseEntity /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ private String queryType; - /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件) */ + /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ private String htmlType; /** 字典类型 */ @@ -139,6 +139,11 @@ public class GenTableColumn extends BaseEntity return javaField; } + public String getCapJavaField() + { + return StringUtils.capitalize(javaField); + } + public void setIsPk(String isPk) { this.isPk = isPk; @@ -341,7 +346,7 @@ public class GenTableColumn extends BaseEntity public static boolean isUsableColumn(String javaField) { // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 - return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum"); + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); } public String readConverterExp() diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableColumnMapper.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableColumnMapper.java index 20fb80e0..e5df0027 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableColumnMapper.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableColumnMapper.java @@ -17,7 +17,7 @@ public interface GenTableColumnMapper * @return 列信息 */ public List selectDbTableColumnsByName(String tableName); - + /** * 查询业务字段列表 * @@ -42,6 +42,14 @@ public interface GenTableColumnMapper */ public int updateGenTableColumn(GenTableColumn genTableColumn); + /** + * 删除业务字段 + * + * @param genTableColumns 列数据 + * @return 结果 + */ + public int deleteGenTableColumns(List genTableColumns); + /** * 批量删除业务字段 * diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableMapper.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableMapper.java index b5ef2985..3d36d88e 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableMapper.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableMapper.java @@ -34,6 +34,13 @@ public interface GenTableMapper */ public List selectDbTableListByNames(String[] tableNames); + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + /** * 查询表ID业务信息 * diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableServiceImpl.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableServiceImpl.java index 817dd3a7..1e656200 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableServiceImpl.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableServiceImpl.java @@ -1,11 +1,13 @@ package com.ruoyi.gen.service; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.io.IOUtils; @@ -22,8 +24,10 @@ import com.alibaba.fastjson.JSONObject; import com.ruoyi.common.core.constant.Constants; import com.ruoyi.common.core.constant.GenConstants; import com.ruoyi.common.core.exception.CustomException; +import com.ruoyi.common.core.text.CharsetKit; +import com.ruoyi.common.core.utils.SecurityUtils; import com.ruoyi.common.core.utils.StringUtils; -import com.ruoyi.common.security.utils.SecurityUtils; +import com.ruoyi.common.core.utils.file.FileUtils; import com.ruoyi.gen.domain.GenTable; import com.ruoyi.gen.domain.GenTableColumn; import com.ruoyi.gen.mapper.GenTableColumnMapper; @@ -98,6 +102,17 @@ public class GenTableServiceImpl implements IGenTableService return genTableMapper.selectDbTableListByNames(tableNames); } + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() + { + return genTableMapper.selectGenTableAll(); + } + /** * 修改业务 * @@ -175,14 +190,16 @@ public class GenTableServiceImpl implements IGenTableService * @param tableId 表编号 * @return 预览数据列表 */ + @Override public Map previewCode(Long tableId) { Map dataMap = new LinkedHashMap<>(); // 查询表信息 GenTable table = genTableMapper.selectGenTableById(tableId); - // 查询列信息 - List columns = table.getColumns(); - setPkColumn(table, columns); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); VelocityInitializer.initVelocity(); VelocityContext context = VelocityUtils.prepareContext(table); @@ -201,13 +218,13 @@ public class GenTableServiceImpl implements IGenTableService } /** - * 生成代码 + * 生成代码(下载方式) * * @param tableName 表名称 * @return 数据 */ @Override - public byte[] generatorCode(String tableName) + public byte[] downloadCode(String tableName) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ZipOutputStream zip = new ZipOutputStream(outputStream); @@ -217,13 +234,90 @@ public class GenTableServiceImpl implements IGenTableService } /** - * 批量生成代码 + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode(String tableName) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) + { + if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) + { + // 渲染模板 + 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 CustomException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Override + @Transactional + public void synchDb(String tableName) + { + GenTable table = genTableMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + List tableColumnNames = tableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (StringUtils.isEmpty(dbTableColumns)) + { + throw new CustomException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + dbTableColumns.forEach(column -> { + if (!tableColumnNames.contains(column.getColumnName())) + { + GenUtils.initColumnField(column, table); + genTableColumnMapper.insertGenTableColumn(column); + } + }); + + List delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); + if (StringUtils.isNotEmpty(delColumns)) + { + genTableColumnMapper.deleteGenTableColumns(delColumns); + } + } + + /** + * 批量生成代码(下载方式) * * @param tableNames 表数组 * @return 数据 */ @Override - public byte[] generatorCode(String[] tableNames) + public byte[] downloadCode(String[] tableNames) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ZipOutputStream zip = new ZipOutputStream(outputStream); @@ -242,9 +336,10 @@ public class GenTableServiceImpl implements IGenTableService { // 查询表信息 GenTable table = genTableMapper.selectGenTableByName(tableName); - // 查询列信息 - List columns = table.getColumns(); - setPkColumn(table, columns); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); VelocityInitializer.initVelocity(); @@ -298,6 +393,17 @@ public class GenTableServiceImpl implements IGenTableService { throw new CustomException("树名称字段不能为空"); } + else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) + { + if (StringUtils.isEmpty(genTable.getSubTableName())) + { + throw new CustomException("关联子表的表名不能为空"); + } + else if (StringUtils.isEmpty(genTable.getSubTableFkName())) + { + throw new CustomException("子表关联的外键名不能为空"); + } + } } } @@ -305,11 +411,10 @@ public class GenTableServiceImpl implements IGenTableService * 设置主键列信息 * * @param table 业务表信息 - * @param columns 业务字段列表 */ - public void setPkColumn(GenTable table, List columns) + public void setPkColumn(GenTable table) { - for (GenTableColumn column : columns) + for (GenTableColumn column : table.getColumns()) { if (column.isPk()) { @@ -319,7 +424,35 @@ public class GenTableServiceImpl implements IGenTableService } if (StringUtils.isNull(table.getPkColumn())) { - table.setPkColumn(columns.get(0)); + table.setPkColumn(table.getColumns().get(0)); + } + if (GenConstants.TPL_SUB.equals(table.getTplCategory())) + { + for (GenTableColumn column : table.getSubTable().getColumns()) + { + if (column.isPk()) + { + table.getSubTable().setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getSubTable().getPkColumn())) + { + table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); + } + } + } + /** + * 设置主子表信息 + * + * @param table 业务表信息 + */ + public void setSubTable(GenTable table) + { + String subTableName = table.getSubTableName(); + if (StringUtils.isNotEmpty(subTableName)) + { + table.setSubTable(genTableMapper.selectGenTableByName(subTableName)); } } @@ -336,9 +469,31 @@ public class GenTableServiceImpl implements IGenTableService String treeCode = paramsObj.getString(GenConstants.TREE_CODE); String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE); String treeName = paramsObj.getString(GenConstants.TREE_NAME); + String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID); + String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME); + genTable.setTreeCode(treeCode); genTable.setTreeParentCode(treeParentCode); genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * @return 生成地址 + */ + public static String getGenPath(GenTable table, String template) + { + String genPath = table.getGenPath(); + if (StringUtils.equals(genPath, "/")) + { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); } + return genPath + File.separator + VelocityUtils.getFileName(template, table); } -} \ No newline at end of file +} diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableService.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableService.java index 050646cc..3d6e0ff6 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableService.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableService.java @@ -35,6 +35,13 @@ public interface IGenTableService */ public List selectDbTableListByNames(String[] tableNames); + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + /** * 查询业务信息 * @@ -75,20 +82,35 @@ public interface IGenTableService public Map previewCode(Long tableId); /** - * 生成代码 + * 生成代码(下载方式) * * @param tableName 表名称 * @return 数据 */ - public byte[] generatorCode(String tableName); + public byte[] downloadCode(String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * @return 数据 + */ + public void generatorCode(String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + public void synchDb(String tableName); /** - * 批量生成代码 + * 批量生成代码(下载方式) * * @param tableNames 表数组 * @return 数据 */ - public byte[] generatorCode(String[] tableNames); + public byte[] downloadCode(String[] tableNames); /** * 修改保存参数校验 diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/GenUtils.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/GenUtils.java index 39fdd423..aa22ddbb 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/GenUtils.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/GenUtils.java @@ -40,13 +40,14 @@ public class GenUtils column.setCreateBy(table.getCreateBy()); // 设置java字段名 column.setJavaField(StringUtils.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); - if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType)) + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) { - column.setJavaType(GenConstants.TYPE_STRING); // 字符串长度超过500设置为文本域 Integer columnLength = getColumnLength(column.getColumnType()); - String htmlType = columnLength >= 500 ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; column.setHtmlType(htmlType); } else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) @@ -58,7 +59,7 @@ public class GenUtils { column.setHtmlType(GenConstants.HTML_INPUT); - // 如果是浮点型 + // 如果是浮点型 统一用BigDecimal String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) { @@ -111,6 +112,21 @@ public class GenUtils { column.setHtmlType(GenConstants.HTML_SELECT); } + // 图片字段设置图片上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) + { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "file")) + { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "content")) + { + column.setHtmlType(GenConstants.HTML_EDITOR); + } } /** diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityUtils.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityUtils.java index 6902e62c..8fe36a83 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityUtils.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityUtils.java @@ -24,9 +24,12 @@ public class VelocityUtils /** mybatis空间路径 */ private static final String MYBATIS_PATH = "main/resources/mapper"; + /** 默认上级菜单,系统工具 */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + /** * 设置模板变量信息 - * + * * @return 模板列表 */ public static VelocityContext prepareContext(GenTable genTable) @@ -51,17 +54,30 @@ public class VelocityUtils velocityContext.put("author", genTable.getFunctionAuthor()); velocityContext.put("datetime", DateUtils.getDate()); velocityContext.put("pkColumn", genTable.getPkColumn()); - velocityContext.put("importList", getImportList(genTable.getColumns())); + 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_TREE.equals(tplCategory)) { setTreeVelocityContext(velocityContext, genTable); } + if (GenConstants.TPL_SUB.equals(tplCategory)) + { + setSubVelocityContext(velocityContext, genTable); + } return velocityContext; } + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSONObject.parseObject(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) { String options = genTable.getOptions(); @@ -84,9 +100,27 @@ public class VelocityUtils } } + public static void setSubVelocityContext(VelocityContext context, GenTable genTable) + { + GenTable subTable = genTable.getSubTable(); + String subTableName = genTable.getSubTableName(); + String subTableFkName = genTable.getSubTableFkName(); + String subClassName = genTable.getSubTable().getClassName(); + String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); + + context.put("subTable", subTable); + context.put("subTableName", subTableName); + context.put("subTableFkName", subTableFkName); + context.put("subTableFkClassName", subTableFkClassName); + context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); + context.put("subClassName", subClassName); + context.put("subclassName", StringUtils.uncapitalize(subClassName)); + context.put("subImportList", getImportList(genTable.getSubTable())); + } + /** * 获取模板信息 - * + * * @return 模板列表 */ public static List getTemplateList(String tplCategory) @@ -108,6 +142,11 @@ public class VelocityUtils { templates.add("vm/vue/index-tree.vue.vm"); } + else if (GenConstants.TPL_SUB.equals(tplCategory)) + { + templates.add("vm/vue/index.vue.vm"); + templates.add("vm/java/sub-domain.java.vm"); + } return templates; } @@ -135,6 +174,10 @@ public class VelocityUtils { fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); } + if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); + } else if (template.contains("mapper.java.vm")) { fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); @@ -176,7 +219,7 @@ public class VelocityUtils /** * 获取包前缀 - * + * * @param packageName 包名称 * @return 包前缀名称 */ @@ -190,12 +233,18 @@ public class VelocityUtils /** * 根据列类型获取导入包 * - * @param columns 列集合 + * @param genTable 业务表对象 * @return 返回需要导入的包列表 */ - public static HashSet getImportList(List columns) + public static HashSet getImportList(GenTable genTable) { + List columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); HashSet importList = new HashSet(); + if (StringUtils.isNotNull(subGenTable)) + { + importList.add("java.util.List"); + } for (GenTableColumn column : columns) { if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) @@ -213,7 +262,7 @@ public class VelocityUtils /** * 获取权限前缀 - * + * * @param moduleName 模块名称 * @param businessName 业务名称 * @return 返回权限前缀 @@ -221,12 +270,26 @@ public class VelocityUtils public static String getPermissionPrefix(String moduleName, String businessName) { return StringUtils.format("{}:{}", moduleName, businessName); + } + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(JSONObject paramsObj) + { + if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)) + { + return paramsObj.getString(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; } /** * 获取树编码 - * + * * @param paramsObj 生成其他选项 * @return 树编码 */ @@ -236,12 +299,12 @@ public class VelocityUtils { return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); } - return ""; + return StringUtils.EMPTY; } /** * 获取树父编码 - * + * * @param paramsObj 生成其他选项 * @return 树父编码 */ @@ -251,12 +314,12 @@ public class VelocityUtils { return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); } - return ""; + return StringUtils.EMPTY; } /** * 获取树名称 - * + * * @param paramsObj 生成其他选项 * @return 树名称 */ @@ -266,12 +329,12 @@ public class VelocityUtils { return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); } - return ""; + return StringUtils.EMPTY; } /** * 获取需要在哪一列上面显示展开按钮 - * + * * @param genTable 业务表对象 * @return 展开按钮列序号 */ @@ -295,4 +358,4 @@ public class VelocityUtils } return num; } -} \ No newline at end of file +} diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/bootstrap.yml b/ruoyi-modules/ruoyi-gen/src/main/resources/bootstrap.yml index 02971165..0ef5a457 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/bootstrap.yml +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/bootstrap.yml @@ -21,55 +21,5 @@ spring: # 配置文件格式 file-extension: yml # 共享配置 - shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} - -#请求处理的超时时间 -ribbon: - ReadTimeout: 10000 - ConnectTimeout: 10000 - eureka: - enable: true - -# feign 配置 -feign: - sentinel: - enabled: true - okhttp: - enabled: true - httpclient: - enabled: false - client: - config: - default: - connectTimeout: 10000 - readTimeout: 10000 - compression: - request: - enabled: true - response: - enabled: true - -# 暴露监控端点 -management: - endpoints: - web: - exposure: - include: '*' - -# 认证配置 -security: - oauth2: - client: - client-id: ruoyi - client-secret: 123456 - scope: server - resource: - loadBalanced: true - token-info-uri: http://ruoyi-auth/oauth/check_token - ignore: - urls: - - /v2/api-docs - - /actuator/** - - /user/info/* - - /operlog - - /logininfor \ No newline at end of file + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/logback.xml b/ruoyi-modules/ruoyi-gen/src/main/resources/logback.xml index bd4abac1..250c080f 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/logback.xml +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/logback.xml @@ -1,9 +1,9 @@ - + - :ss} %-5level ${springAppName:-} %thread %logger %msg%n"/> + diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml index 6a8b523f..83565467 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -117,4 +117,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + delete from gen_table_column where column_id in + + #{item.columnId} + + + \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableMapper.xml index abb636e3..a243a02d 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableMapper.xml +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableMapper.xml @@ -5,22 +5,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + @@ -50,7 +54,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select table_id, table_name, table_comment, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, options, create_by, create_time, update_by, update_time, remark from gen_table + select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table @@ -76,6 +86,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND lower(table_comment) like lower(concat('%', #{tableComment}, '%')) + + AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + + + AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + + + insert into gen_table ( table_name, @@ -120,6 +144,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" business_name, function_name, function_author, + gen_type, + gen_path, remark, create_by, create_time @@ -133,6 +159,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{businessName}, #{functionName}, #{functionAuthor}, + #{genType}, + #{genPath}, #{remark}, #{createBy}, sysdate() @@ -144,8 +172,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" table_name = #{tableName}, table_comment = #{tableComment}, + sub_table_name = #{subTableName}, + sub_table_fk_name = #{subTableFkName}, class_name = #{className}, function_author = #{functionAuthor}, + gen_type = #{genType}, + gen_path = #{genPath}, tpl_category = #{tplCategory}, package_name = #{packageName}, module_name = #{moduleName}, @@ -166,4 +198,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - \ No newline at end of file + \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm index 93df940c..7ac19872 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm @@ -3,7 +3,6 @@ package ${packageName}.controller; import java.util.List; import java.io.IOException; import javax.servlet.http.HttpServletResponse; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -15,12 +14,13 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ruoyi.common.log.annotation.Log; import com.ruoyi.common.log.enums.BusinessType; +import com.ruoyi.common.security.annotation.PreAuthorize; import ${packageName}.domain.${ClassName}; import ${packageName}.service.I${ClassName}Service; import com.ruoyi.common.core.web.controller.BaseController; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.utils.poi.ExcelUtil; -#if($table.crud) +#if($table.crud || $table.sub) import com.ruoyi.common.core.web.page.TableDataInfo; #elseif($table.tree) #end @@ -41,9 +41,9 @@ public class ${ClassName}Controller extends BaseController /** * 查询${functionName}列表 */ - @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") + @PreAuthorize(hasPermi = "${permissionPrefix}:list") @GetMapping("/list") -#if($table.crud) +#if($table.crud || $table.sub) public TableDataInfo list(${ClassName} ${className}) { startPage(); @@ -61,7 +61,7 @@ public class ${ClassName}Controller extends BaseController /** * 导出${functionName}列表 */ - @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") + @PreAuthorize(hasPermi = "${permissionPrefix}:export") @Log(title = "${functionName}", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, ${ClassName} ${className}) throws IOException @@ -74,7 +74,7 @@ public class ${ClassName}Controller extends BaseController /** * 获取${functionName}详细信息 */ - @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @PreAuthorize(hasPermi = "${permissionPrefix}:query") @GetMapping(value = "/{${pkColumn.javaField}}") public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) { @@ -84,7 +84,7 @@ public class ${ClassName}Controller extends BaseController /** * 新增${functionName} */ - @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @PreAuthorize(hasPermi = "${permissionPrefix}:add") @Log(title = "${functionName}", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@RequestBody ${ClassName} ${className}) @@ -95,7 +95,7 @@ public class ${ClassName}Controller extends BaseController /** * 修改${functionName} */ - @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @PreAuthorize(hasPermi = "${permissionPrefix}:edit") @Log(title = "${functionName}", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@RequestBody ${ClassName} ${className}) @@ -106,7 +106,7 @@ public class ${ClassName}Controller extends BaseController /** * 删除${functionName} */ - @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @PreAuthorize(hasPermi = "${permissionPrefix}:remove") @Log(title = "${functionName}", businessType = BusinessType.DELETE) @DeleteMapping("/{${pkColumn.javaField}s}") public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/domain.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/domain.java.vm index 92b089a9..3e6be024 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/domain.java.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/domain.java.vm @@ -6,7 +6,7 @@ import ${import}; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.ruoyi.common.core.annotation.Excel; -#if($table.crud) +#if($table.crud || $table.sub) import com.ruoyi.common.core.web.domain.BaseEntity; #elseif($table.tree) import com.ruoyi.common.core.web.domain.TreeEntity; @@ -18,7 +18,7 @@ import com.ruoyi.common.core.web.domain.TreeEntity; * @author ${author} * @date ${datetime} */ -#if($table.crud) +#if($table.crud || $table.sub) #set($Entity="BaseEntity") #elseif($table.tree) #set($Entity="TreeEntity") @@ -49,6 +49,11 @@ public class ${ClassName} extends ${Entity} private $column.javaType $column.javaField; #end +#end +#if($table.sub) + /** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + #end #foreach ($column in $columns) #if(!$table.isSuperColumn($column.javaField)) @@ -69,6 +74,18 @@ public class ${ClassName} extends ${Entity} #end #end +#if($table.sub) + public List<${subClassName}> get${subClassName}List() + { + return ${subclassName}List; + } + + public void set${subClassName}List(List<${subClassName}> ${subclassName}List) + { + this.${subclassName}List = ${subclassName}List; + } + +#end @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) @@ -79,6 +96,9 @@ public class ${ClassName} extends ${Entity} #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) #end .append("${column.javaField}", get${AttrName}()) +#end +#if($table.sub) + .append("${subclassName}List", get${subClassName}List()) #end .toString(); } diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/mapper.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/mapper.java.vm index 5502baf6..4c02b0f4 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/mapper.java.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/mapper.java.vm @@ -2,6 +2,9 @@ package ${packageName}.mapper; import java.util.List; import ${packageName}.domain.${ClassName}; +#if($table.sub) +import ${packageName}.domain.${subClassName}; +#end /** * ${functionName}Mapper接口 @@ -58,4 +61,31 @@ public interface ${ClassName}Mapper * @return 结果 */ public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s); +#if($table.sub) + + /** + * 批量删除${subTable.functionName} + * + * @param customerIds 需要删除的数据ID + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 批量新增${subTable.functionName} + * + * @param ${subclassName}List ${subTable.functionName}列表 + * @return 结果 + */ + public int batch${subClassName}(List<${subClassName}> ${subclassName}List); + + + /** + * 通过${functionName}ID删除${subTable.functionName}信息 + * + * @param roleId 角色ID + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField}); +#end } diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm index f6cc71ce..2e928e03 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm @@ -9,6 +9,12 @@ import com.ruoyi.common.core.utils.DateUtils; #end import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.ruoyi.common.core.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import ${packageName}.domain.${subClassName}; +#end import ${packageName}.mapper.${ClassName}Mapper; import ${packageName}.domain.${ClassName}; import ${packageName}.service.I${ClassName}Service; @@ -55,6 +61,9 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service * @param ${className} ${functionName} * @return 结果 */ +#if($table.sub) + @Transactional +#end @Override public int insert${ClassName}(${ClassName} ${className}) { @@ -63,7 +72,13 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service ${className}.setCreateTime(DateUtils.getNowDate()); #end #end +#if($table.sub) + int rows = ${className}Mapper.insert${ClassName}(${className}); + insert${subClassName}(${className}); + return rows; +#else return ${className}Mapper.insert${ClassName}(${className}); +#end } /** @@ -72,6 +87,9 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service * @param ${className} ${functionName} * @return 结果 */ +#if($table.sub) + @Transactional +#end @Override public int update${ClassName}(${ClassName} ${className}) { @@ -79,6 +97,10 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service #if($column.javaField == 'updateTime') ${className}.setUpdateTime(DateUtils.getNowDate()); #end +#end +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); + insert${subClassName}(${className}); #end return ${className}Mapper.update${ClassName}(${className}); } @@ -89,9 +111,15 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service * @param ${pkColumn.javaField}s 需要删除的${functionName}ID * @return 结果 */ +#if($table.sub) + @Transactional +#end @Override public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s) { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); +#end return ${className}Mapper.delete${ClassName}ByIds(${pkColumn.javaField}s); } @@ -104,6 +132,35 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service @Override public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField}) { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); +#end return ${className}Mapper.delete${ClassName}ById(${pkColumn.javaField}); } +#if($table.sub) + + /** + * 新增${subTable.functionName}信息 + * + * @param ${className} ${functionName}对象 + */ + public void insert${subClassName}(${ClassName} ${className}) + { + List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); + Long ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); + if (StringUtils.isNotNull(${subclassName}List)) + { + List<${subClassName}> list = new ArrayList<${subClassName}>(); + for (${subClassName} ${subclassName} : ${subclassName}List) + { + ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); + list.add(${subclassName}); + } + if (list.size() > 0) + { + ${className}Mapper.batch${subClassName}(list); + } + } + } +#end } diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/sub-domain.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/sub-domain.java.vm new file mode 100644 index 00000000..2e039ff6 --- /dev/null +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/sub-domain.java.vm @@ -0,0 +1,76 @@ +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.annotation.Excel; +import com.ruoyi.common.core.web.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $subTable.columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end + .toString(); + } +} diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/sql/sql.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/sql/sql.vm index ab17d1be..d4196645 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/sql/sql.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/sql/sql.vm @@ -1,22 +1,22 @@ -- 菜单 SQL -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}', '3', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '${functionName}菜单'); +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '${functionName}菜单'); -- 按钮父菜单ID SELECT @parentId := LAST_INSERT_ID(); -- 按钮 SQL -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}查询', @parentId, '1', '#', '', 1, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', ''); +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', ''); -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}新增', @parentId, '2', '#', '', 1, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', ''); +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', ''); -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}修改', @parentId, '3', '#', '', 1, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', ''); +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', ''); -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}删除', @parentId, '4', '#', '', 1, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', ''); +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', ''); -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}导出', @parentId, '5', '#', '', 1, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', ''); \ No newline at end of file +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', ''); \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm index fb10ed6d..d4c5e42f 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm @@ -1,6 +1,6 @@