🎇发布 4.6.0 新增 数据库加解密 与 通用翻译 功能

This commit is contained in:
疯狂的狮子li 2023-03-13 10:10:47 +08:00
parent f196010416
commit 36ac478624
151 changed files with 2951 additions and 1115 deletions

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.5.0" /> <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.6.0" />
<option name="buildOnly" value="true" /> <option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" /> <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
</settings> </settings>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="ruoyi/ruoyi-server:4.5.0" /> <option name="imageTag" value="ruoyi/ruoyi-server:4.6.0" />
<option name="buildOnly" value="true" /> <option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" /> <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
</settings> </settings>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-xxl-job-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="ruoyi-xxl-job-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.5.0" /> <option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.6.0" />
<option name="buildOnly" value="true" /> <option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile" /> <option name="sourceFilePath" value="ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile" />
</settings> </settings>

View File

@ -4,7 +4,7 @@
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br> <br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.5.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) [![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.6.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.7-blue.svg)]() [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.7-blue.svg)]()
[![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]() [![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]()
[![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]() [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()

31
pom.xml
View File

@ -6,45 +6,46 @@
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<version>4.5.0</version> <version>4.6.0</version>
<name>RuoYi-Vue-Plus</name> <name>RuoYi-Vue-Plus</name>
<url>https://gitee.com/JavaLionLi/RuoYi-Vue-Plus</url> <url>https://gitee.com/JavaLionLi/RuoYi-Vue-Plus</url>
<description>RuoYi-Vue-Plus后台管理系统</description> <description>RuoYi-Vue-Plus后台管理系统</description>
<properties> <properties>
<ruoyi-vue-plus.version>4.5.0</ruoyi-vue-plus.version> <ruoyi-vue-plus.version>4.6.0</ruoyi-vue-plus.version>
<spring-boot.version>2.7.7</spring-boot.version> <spring-boot.version>2.7.9</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version> <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
<spring-boot.mybatis>2.2.2</spring-boot.mybatis> <spring-boot.mybatis>2.2.2</spring-boot.mybatis>
<springdoc.version>1.6.14</springdoc.version> <springdoc.version>1.6.15</springdoc.version>
<poi.version>5.2.3</poi.version> <poi.version>5.2.3</poi.version>
<easyexcel.version>3.1.5</easyexcel.version> <easyexcel.version>3.2.1</easyexcel.version>
<velocity.version>2.3</velocity.version> <velocity.version>2.3</velocity.version>
<satoken.version>1.34.0</satoken.version> <satoken.version>1.34.0</satoken.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version> <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version> <p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.11</hutool.version> <hutool.version>5.8.15</hutool.version>
<okhttp.version>4.10.0</okhttp.version> <okhttp.version>4.10.0</okhttp.version>
<spring-boot-admin.version>2.7.10</spring-boot-admin.version> <spring-boot-admin.version>2.7.10</spring-boot-admin.version>
<redisson.version>3.19.1</redisson.version> <redisson.version>3.20.0</redisson.version>
<lock4j.version>2.2.3</lock4j.version> <lock4j.version>2.2.3</lock4j.version>
<dynamic-ds.version>3.5.2</dynamic-ds.version> <dynamic-ds.version>3.5.2</dynamic-ds.version>
<alibaba-ttl.version>2.14.2</alibaba-ttl.version> <alibaba-ttl.version>2.14.2</alibaba-ttl.version>
<xxl-job.version>2.3.1</xxl-job.version> <xxl-job.version>2.3.1</xxl-job.version>
<lombok.version>1.18.24</lombok.version> <lombok.version>1.18.26</lombok.version>
<bouncycastle.version>1.72</bouncycastle.version>
<!-- 临时修复 snakeyaml 漏洞 --> <!-- 临时修复 snakeyaml 漏洞 -->
<snakeyaml.version>1.33</snakeyaml.version> <snakeyaml.version>1.33</snakeyaml.version>
<!-- OSS 配置 --> <!-- OSS 配置 -->
<aws-java-sdk-s3.version>1.12.373</aws-java-sdk-s3.version> <aws-java-sdk-s3.version>1.12.400</aws-java-sdk-s3.version>
<!-- SMS 配置 --> <!-- SMS 配置 -->
<aliyun.sms.version>2.0.23</aliyun.sms.version> <aliyun.sms.version>2.0.23</aliyun.sms.version>
<tencent.sms.version>3.1.660</tencent.sms.version> <tencent.sms.version>3.1.687</tencent.sms.version>
</properties> </properties>
<profiles> <profiles>
@ -237,6 +238,7 @@
<artifactId>redisson-spring-data-27</artifactId> <artifactId>redisson-spring-data-27</artifactId>
<version>${redisson.version}</version> <version>${redisson.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId> <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
@ -263,6 +265,13 @@
<version>${snakeyaml.version}</version> <version>${snakeyaml.version}</version>
</dependency> </dependency>
<!-- 加密包引入 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
@ -361,3 +370,5 @@
</pluginRepositories> </pluginRepositories>
</project> </project>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.5.0</version> <version>4.6.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-extend</artifactId> <artifactId>ruoyi-extend</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-extend</artifactId> <artifactId>ruoyi-extend</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.5.0</version> <version>4.6.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<artifactId>ruoyi-extend</artifactId> <artifactId>ruoyi-extend</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.5.0</version> <version>4.6.0</version>
</parent> </parent>
<artifactId>ruoyi-xxl-job-admin</artifactId> <artifactId>ruoyi-xxl-job-admin</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@ -1,6 +1,6 @@
{ {
"name": "ruoyi-vue-plus", "name": "ruoyi-vue-plus",
"version": "4.5.0", "version": "4.6.0",
"description": "RuoYi-Vue-Plus后台管理系统", "description": "RuoYi-Vue-Plus后台管理系统",
"author": "LionLi", "author": "LionLi",
"license": "MIT", "license": "MIT",
@ -40,7 +40,7 @@
"clipboard": "2.0.8", "clipboard": "2.0.8",
"core-js": "3.25.3", "core-js": "3.25.3",
"echarts": "5.4.0", "echarts": "5.4.0",
"element-ui": "2.15.10", "element-ui": "2.15.12",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fuse.js": "6.4.3", "fuse.js": "6.4.3",
"highlight.js": "9.18.5", "highlight.js": "9.18.5",

View File

@ -87,7 +87,7 @@ export default {
}, },
isFirstView() { isFirstView() {
try { try {
return this.selectedTag.fullPath === this.visitedViews[1].fullPath || this.selectedTag.fullPath === '/index' return this.selectedTag.fullPath === '/index' || this.selectedTag.fullPath === this.visitedViews[1].fullPath
} catch (err) { } catch (err) {
return false return false
} }

View File

@ -8,7 +8,7 @@ import { isRelogin } from '@/utils/request'
NProgress.configure({ showSpinner: false }) NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/auth-redirect', '/bind', '/register'] const whiteList = ['/login', '/register']
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
NProgress.start() NProgress.start()

View File

@ -17,9 +17,9 @@ export default {
url: url, url: url,
responseType: 'blob', responseType: 'blob',
headers: { 'Authorization': 'Bearer ' + getToken() } headers: { 'Authorization': 'Bearer ' + getToken() }
}).then(async (res) => { }).then((res) => {
const isLogin = await blobValidate(res.data); const isBlob = blobValidate(res.data);
if (isLogin) { if (isBlob) {
const blob = new Blob([res.data], { type: 'application/octet-stream' }) const blob = new Blob([res.data], { type: 'application/octet-stream' })
this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
} else { } else {
@ -42,9 +42,9 @@ export default {
'Authorization': 'Bearer ' + getToken(), 'Authorization': 'Bearer ' + getToken(),
'datasource': localStorage.getItem("dataName") 'datasource': localStorage.getItem("dataName")
} }
}).then(async (res) => { }).then((res) => {
const isLogin = await blobValidate(res.data); const isBlob = blobValidate(res.data);
if (isLogin) { if (isBlob) {
const blob = new Blob([res.data], { type: 'application/zip' }) const blob = new Blob([res.data], { type: 'application/zip' })
this.saveAs(blob, name) this.saveAs(blob, name)
} else { } else {

View File

@ -74,7 +74,7 @@ service.interceptors.response.use(res => {
// 获取错误信息 // 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default'] const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回 // 二进制数据则直接返回
if(res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer'){ if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data return res.data
} }
if (code === 401) { if (code === 401) {
@ -127,8 +127,8 @@ export function download(url, params, filename, config) {
responseType: 'blob', responseType: 'blob',
...config ...config
}).then(async (data) => { }).then(async (data) => {
const isLogin = await blobValidate(data); const isBlob = blobValidate(data);
if (isLogin) { if (isBlob) {
const blob = new Blob([data]) const blob = new Blob([data])
saveAs(blob, filename) saveAs(blob, filename)
} else { } else {

View File

@ -228,12 +228,6 @@ export function tansParams(params) {
} }
// 验证是否为blob格式 // 验证是否为blob格式
export async function blobValidate(data) { export function blobValidate(data) {
try { return data.type !== 'application/json'
const text = await data.text();
JSON.parse(text);
return false;
} catch (error) {
return true;
}
} }

View File

@ -24,11 +24,12 @@
v-model="daterangeCreateTime" v-model="daterangeCreateTime"
size="small" size="small"
style="width: 240px" style="width: 240px"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>

View File

@ -15,11 +15,12 @@
v-model="daterangeCreateTime" v-model="daterangeCreateTime"
size="small" size="small"
style="width: 240px" style="width: 240px"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>

View File

@ -114,7 +114,7 @@ export default {
data() { data() {
return { return {
// //
version: "4.5.0", version: "4.6.0",
}; };
}, },
methods: { methods: {

View File

@ -56,7 +56,7 @@
</el-form> </el-form>
<!-- 底部 --> <!-- 底部 -->
<div class="el-login-footer"> <div class="el-login-footer">
<span>Copyright © 2018-2022 ruoyi.vip All Rights Reserved.</span> <span>Copyright © 2018-2023 ruoyi.vip All Rights Reserved.</span>
</div> </div>
</div> </div>
</template> </template>

View File

@ -3,7 +3,7 @@
<el-row> <el-row>
<el-col :span="24" class="card-box"> <el-col :span="24" class="card-box">
<el-card> <el-card>
<div slot="header"><span>基本信息</span></div> <div slot="header"><span><i class="el-icon-monitor"></i> 基本信息</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%"> <table cellspacing="0" style="width: 100%">
<tbody> <tbody>
@ -45,7 +45,7 @@
<el-col :span="12" class="card-box"> <el-col :span="12" class="card-box">
<el-card> <el-card>
<div slot="header"><span>命令统计</span></div> <div slot="header"><span><i class="el-icon-pie-chart"></i> 命令统计</span></div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="commandstats" style="height: 420px" /> <div ref="commandstats" style="height: 420px" />
</div> </div>
@ -54,9 +54,7 @@
<el-col :span="12" class="card-box"> <el-col :span="12" class="card-box">
<el-card> <el-card>
<div slot="header"> <div slot="header"><span><i class="el-icon-odometer"></i> 内存信息</span></div>
<span>内存信息</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="usedmemory" style="height: 420px" /> <div ref="usedmemory" style="height: 420px" />
</div> </div>

View File

@ -4,7 +4,7 @@
<el-col :span="8"> <el-col :span="8">
<el-card style="height: calc(100vh - 125px)"> <el-card style="height: calc(100vh - 125px)">
<div slot="header"> <div slot="header">
<span>缓存列表</span> <span><i class="el-icon-collection"></i> 缓存列表</span>
<el-button <el-button
style="float: right; padding: 3px 0" style="float: right; padding: 3px 0"
type="text" type="text"
@ -62,7 +62,7 @@
<el-col :span="8"> <el-col :span="8">
<el-card style="height: calc(100vh - 125px)"> <el-card style="height: calc(100vh - 125px)">
<div slot="header"> <div slot="header">
<span>键名列表</span> <span><i class="el-icon-key"></i> 键名列表</span>
<el-button <el-button
style="float: right; padding: 3px 0" style="float: right; padding: 3px 0"
type="text" type="text"
@ -112,7 +112,7 @@
<el-col :span="8"> <el-col :span="8">
<el-card :bordered="false" style="height: calc(100vh - 125px)"> <el-card :bordered="false" style="height: calc(100vh - 125px)">
<div slot="header"> <div slot="header">
<span>缓存内容</span> <span><i class="el-icon-document"></i> 缓存内容</span>
<el-button <el-button
style="float: right; padding: 3px 0" style="float: right; padding: 3px 0"
type="text" type="text"

View File

@ -38,11 +38,12 @@
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
style="width: 240px" style="width: 240px"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>

View File

@ -53,11 +53,12 @@
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
style="width: 240px" style="width: 240px"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>

View File

@ -61,7 +61,7 @@
</el-form> </el-form>
<!-- 底部 --> <!-- 底部 -->
<div class="el-register-footer"> <div class="el-register-footer">
<span>Copyright © 2018-2022 ruoyi.vip All Rights Reserved.</span> <span>Copyright © 2018-2023 ruoyi.vip All Rights Reserved.</span>
</div> </div>
</div> </div>
</template> </template>

View File

@ -33,11 +33,12 @@
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
style="width: 240px" style="width: 240px"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -107,7 +108,7 @@
<el-table-column label="参数主键" align="center" prop="configId" /> <el-table-column label="参数主键" align="center" prop="configId" />
<el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" /> <el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" />
<el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" /> <el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" />
<el-table-column label="参数键值" align="center" prop="configValue" /> <el-table-column label="参数键值" align="center" prop="configValue" :show-overflow-tooltip="true" />
<el-table-column label="系统内置" align="center" prop="configType"> <el-table-column label="系统内置" align="center" prop="configType">
<template slot-scope="scope"> <template slot-scope="scope">
<dict-tag :options="dict.type.sys_yes_no" :value="scope.row.configType"/> <dict-tag :options="dict.type.sys_yes_no" :value="scope.row.configType"/>

View File

@ -38,11 +38,12 @@
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
style="width: 240px" style="width: 240px"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>

View File

@ -19,14 +19,10 @@
@keyup.enter.native="handleQuery" @keyup.enter.native="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="是否默认" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small"> <el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
<el-option <el-option key="0" label="是" value="0"/>
v-for="dict in dict.type.sys_normal_disable" <el-option key="1" label="否" value="1"/>
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -87,7 +83,7 @@
<el-tag type="info" v-if="scope.row.accessPolicy === '2'">custom</el-tag> <el-tag type="info" v-if="scope.row.accessPolicy === '2'">custom</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="状态" align="center" prop="status"> <el-table-column label="是否默认" align="center" prop="status">
<template slot-scope="scope"> <template slot-scope="scope">
<el-switch <el-switch
v-model="scope.row.status" v-model="scope.row.status"
@ -193,7 +189,7 @@ import {
export default { export default {
name: "OssConfig", name: "OssConfig",
dicts: ['sys_yes_no', 'sys_normal_disable'], dicts: ['sys_yes_no'],
data() { data() {
return { return {
// loading // loading

View File

@ -33,11 +33,12 @@
v-model="daterangeCreateTime" v-model="daterangeCreateTime"
size="small" size="small"
style="width: 240px" style="width: 240px"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item label="上传人" prop="createBy"> <el-form-item label="上传人" prop="createBy">
@ -129,9 +130,9 @@
<el-table-column label="文件后缀" align="center" prop="fileSuffix" /> <el-table-column label="文件后缀" align="center" prop="fileSuffix" />
<el-table-column label="文件展示" align="center" prop="url"> <el-table-column label="文件展示" align="center" prop="url">
<template slot-scope="scope"> <template slot-scope="scope">
<el-image <ImagePreview
v-if="previewListResource && checkFileSuffix(scope.row.fileSuffix)" v-if="previewListResource && checkFileSuffix(scope.row.fileSuffix)"
style="width: 100px; height: 100px;" :width=100 :height=100
:src="scope.row.url" :src="scope.row.url"
:preview-src-list="[scope.row.url]"/> :preview-src-list="[scope.row.url]"/>
<span v-text="scope.row.url" <span v-text="scope.row.url"

View File

@ -38,11 +38,12 @@
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
style="width: 240px" style="width: 240px"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>

View File

@ -67,11 +67,12 @@
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
style="width: 240px" style="width: 240px"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.5.0</version> <version>4.6.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging> <packaging>jar</packaging>
@ -205,6 +205,12 @@
<artifactId>lock4j-redisson-spring-boot-starter</artifactId> <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
</dependency> </dependency>
<!-- 加密包引入 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
</dependency>
<!-- xxl-job-core --> <!-- xxl-job-core -->
<dependency> <dependency>
<groupId>com.xuxueli</groupId> <groupId>com.xuxueli</groupId>

View File

@ -13,7 +13,9 @@ import java.lang.annotation.Target;
* 字典数据映射注解 * 字典数据映射注解
* *
* @author itino * @author itino
* @deprecated 建议使用通用翻译注解
*/ */
@Deprecated
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD}) @Target({ElementType.FIELD, ElementType.METHOD})
@JacksonAnnotationsInside @JacksonAnnotationsInside

View File

@ -0,0 +1,44 @@
package com.ruoyi.common.annotation;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
import java.lang.annotation.*;
/**
* 字段加密注解
*
* @author 老马
*/
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
/**
* 加密算法
*/
AlgorithmType algorithm() default AlgorithmType.DEFAULT;
/**
* 秘钥AESSM4需要
*/
String password() default "";
/**
* 公钥RSASM2需要
*/
String publicKey() default "";
/**
* 公钥RSASM2需要
*/
String privateKey() default "";
/**
* 编码方式对加密算法为BASE64的不起作用
*/
EncodeType encode() default EncodeType.DEFAULT;
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.common.annotation; package com.ruoyi.common.annotation;
import com.ruoyi.common.utils.StringUtils;
import java.lang.annotation.*; import java.lang.annotation.*;
/** /**
@ -25,6 +27,6 @@ public @interface ExcelDictFormat {
/** /**
* 分隔符读取字符串组内容 * 分隔符读取字符串组内容
*/ */
String separator() default ","; String separator() default StringUtils.SEPARATOR;
} }

View File

@ -0,0 +1,30 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.*;
/**
* 枚举格式化
*
* @author Liang
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelEnumFormat {
/**
* 字典枚举类型
*/
Class<? extends Enum<?>> enumClass();
/**
* 字典枚举类中对应的code属性名称默认为code
*/
String codeField() default "code";
/**
* 字典枚举类中对应的text属性名称默认为text
*/
String textField() default "text";
}

View File

@ -38,4 +38,10 @@ public @interface Log {
* 是否保存响应的参数 * 是否保存响应的参数
*/ */
boolean isSaveResponseData() default true; boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
String[] excludeParamNames() default {};
} }

View File

@ -1,6 +1,5 @@
package com.ruoyi.common.annotation; package com.ruoyi.common.annotation;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.enums.LimitType; import com.ruoyi.common.enums.LimitType;
import java.lang.annotation.*; import java.lang.annotation.*;
@ -15,9 +14,10 @@ import java.lang.annotation.*;
@Documented @Documented
public @interface RateLimiter { public @interface RateLimiter {
/** /**
* 限流key * 限流key,支持使用Spring el表达式来动态获取方法上的参数值
* 格式类似于 #code.id #{#code}
*/ */
String key() default CacheConstants.RATE_LIMIT_KEY; String key() default "";
/** /**
* 限流时间,单位秒 * 限流时间,单位秒
@ -33,4 +33,9 @@ public @interface RateLimiter {
* 限流类型 * 限流类型
*/ */
LimitType limitType() default LimitType.DEFAULT; LimitType limitType() default LimitType.DEFAULT;
/**
* 提示消息 支持国际化 格式为 {code}
*/
String message() default "{rate.limiter.message}";
} }

View File

@ -0,0 +1,39 @@
package com.ruoyi.common.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.translation.handler.TranslationHandler;
import java.lang.annotation.*;
/**
* 通用翻译注解
*
* @author Lion Li
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Documented
@JacksonAnnotationsInside
@JsonSerialize(using = TranslationHandler.class)
public @interface Translation {
/**
* 类型 (需与实现类上的 {@link com.ruoyi.common.annotation.TranslationType} 注解type对应)
* <p>
* 默认取当前字段的值 如果设置了 @{@link Translation#mapper()} 则取映射字段的值
*/
String type();
/**
* 映射字段 (如果不为空则取此字段的值)
*/
String mapper() default "";
/**
* 其他条件 例如: 字典type(sys_user_sex)
*/
String other() default "";
}

View File

@ -0,0 +1,21 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.*;
/**
* 翻译类型注解 (标注到{@link com.ruoyi.common.translation.TranslationInterface} 的实现类)
*
* @author Lion Li
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface TranslationType {
/**
* 类型
*/
String type();
}

View File

@ -30,6 +30,16 @@ public interface CacheNames {
*/ */
String SYS_DICT = "sys_dict"; String SYS_DICT = "sys_dict";
/**
* 用户账户
*/
String SYS_USER_NAME = "sys_user_name#30d";
/**
* 部门
*/
String SYS_DEPT = "sys_dept#30d";
/** /**
* OSS内容 * OSS内容
*/ */

View File

@ -0,0 +1,30 @@
package com.ruoyi.common.constant;
/**
* 翻译常量
*
* @author Lion Li
*/
public interface TransConstant {
/**
* 用户id转账号
*/
String USER_ID_TO_NAME = "user_id_to_name";
/**
* 部门id转名称
*/
String DEPT_ID_TO_NAME = "dept_id_to_name";
/**
* 字典type转label
*/
String DICT_TYPE_TO_LABEL = "dict_type_to_label";
/**
* ossId转url
*/
String OSS_ID_TO_URL = "oss_id_to_url";
}

View File

@ -112,12 +112,6 @@ public interface UserConstants {
*/ */
String INNER_LINK = "InnerLink"; String INNER_LINK = "InnerLink";
/**
* 校验返回结果码
*/
String UNIQUE = "0";
String NOT_UNIQUE = "1";
/** /**
* 用户名长度限制 * 用户名长度限制
*/ */

View File

@ -0,0 +1,75 @@
package com.ruoyi.common.convert;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.ruoyi.common.annotation.ExcelEnumFormat;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* 枚举格式化转换处理
*
* @author Liang
*/
@Slf4j
public class ExcelEnumConvert implements Converter<Object> {
@Override
public Class<Object> supportJavaTypeKey() {
return Object.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return null;
}
@Override
public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
Object codeValue = cellData.getData();
// 如果是空值
if (ObjectUtil.isNull(codeValue)) {
return null;
}
Map<Object, String> enumValueMap = beforeConvert(contentProperty);
String textValue = enumValueMap.get(codeValue);
return Convert.convert(contentProperty.getField().getType(), textValue);
}
@Override
public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (ObjectUtil.isNull(object)) {
return new WriteCellData<>("");
}
Map<Object, String> enumValueMap = beforeConvert(contentProperty);
String value = Convert.toStr(enumValueMap.get(object), "");
return new WriteCellData<>(value);
}
private Map<Object, String> beforeConvert(ExcelContentProperty contentProperty) {
ExcelEnumFormat anno = getAnnotation(contentProperty.getField());
Map<Object, String> enumValueMap = new HashMap<>();
Enum<?>[] enumConstants = anno.enumClass().getEnumConstants();
for (Enum<?> enumConstant : enumConstants) {
Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField());
String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField());
enumValueMap.put(codeValue, textValue);
}
return enumValueMap;
}
private ExcelEnumFormat getAnnotation(Field field) {
return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class);
}
}

View File

@ -87,8 +87,8 @@ public class PageQuery implements Serializable {
// 兼容前端排序类型 // 兼容前端排序类型
isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"}); isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"});
String[] orderByArr = orderBy.split(","); String[] orderByArr = orderBy.split(StringUtils.SEPARATOR);
String[] isAscArr = isAsc.split(","); String[] isAscArr = isAsc.split(StringUtils.SEPARATOR);
if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) { if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {
throw new ServiceException("排序参数有误"); throw new ServiceException("排序参数有误");
} }

View File

@ -34,7 +34,7 @@ public class SysDept extends TreeEntity<SysDept> {
* 部门名称 * 部门名称
*/ */
@NotBlank(message = "部门名称不能为空") @NotBlank(message = "部门名称不能为空")
@Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") @Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符")
private String deptName; private String deptName;
/** /**
@ -51,14 +51,14 @@ public class SysDept extends TreeEntity<SysDept> {
/** /**
* 联系电话 * 联系电话
*/ */
@Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") @Size(min = 0, max = 11, message = "联系电话长度不能超过{max}个字符")
private String phone; private String phone;
/** /**
* 邮箱 * 邮箱
*/ */
@Email(message = "邮箱格式不正确") @Email(message = "邮箱格式不正确")
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
private String email; private String email;
/** /**

View File

@ -44,7 +44,7 @@ public class SysDictData extends BaseEntity {
*/ */
@ExcelProperty(value = "字典标签") @ExcelProperty(value = "字典标签")
@NotBlank(message = "字典标签不能为空") @NotBlank(message = "字典标签不能为空")
@Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") @Size(min = 0, max = 100, message = "字典标签长度不能超过{max}个字符")
private String dictLabel; private String dictLabel;
/** /**
@ -52,7 +52,7 @@ public class SysDictData extends BaseEntity {
*/ */
@ExcelProperty(value = "字典键值") @ExcelProperty(value = "字典键值")
@NotBlank(message = "字典键值不能为空") @NotBlank(message = "字典键值不能为空")
@Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") @Size(min = 0, max = 100, message = "字典键值长度不能超过{max}个字符")
private String dictValue; private String dictValue;
/** /**
@ -60,13 +60,13 @@ public class SysDictData extends BaseEntity {
*/ */
@ExcelProperty(value = "字典类型") @ExcelProperty(value = "字典类型")
@NotBlank(message = "字典类型不能为空") @NotBlank(message = "字典类型不能为空")
@Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") @Size(min = 0, max = 100, message = "字典类型长度不能超过{max}个字符")
private String dictType; private String dictType;
/** /**
* 样式属性其他样式扩展 * 样式属性其他样式扩展
*/ */
@Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") @Size(min = 0, max = 100, message = "样式属性长度不能超过{max}个字符")
private String cssClass; private String cssClass;
/** /**

View File

@ -38,7 +38,7 @@ public class SysDictType extends BaseEntity {
*/ */
@ExcelProperty(value = "字典名称") @ExcelProperty(value = "字典名称")
@NotBlank(message = "字典名称不能为空") @NotBlank(message = "字典名称不能为空")
@Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") @Size(min = 0, max = 100, message = "字典类型名称长度不能超过{max}个字符")
private String dictName; private String dictName;
/** /**
@ -46,7 +46,7 @@ public class SysDictType extends BaseEntity {
*/ */
@ExcelProperty(value = "字典类型") @ExcelProperty(value = "字典类型")
@NotBlank(message = "字典类型不能为空") @NotBlank(message = "字典类型不能为空")
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") @Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符")
@Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
private String dictType; private String dictType;

View File

@ -32,7 +32,7 @@ public class SysMenu extends TreeEntity<SysMenu> {
* 菜单名称 * 菜单名称
*/ */
@NotBlank(message = "菜单名称不能为空") @NotBlank(message = "菜单名称不能为空")
@Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") @Size(min = 0, max = 50, message = "菜单名称长度不能超过{max}个字符")
private String menuName; private String menuName;
/** /**
@ -44,13 +44,13 @@ public class SysMenu extends TreeEntity<SysMenu> {
/** /**
* 路由地址 * 路由地址
*/ */
@Size(min = 0, max = 200, message = "路由地址不能超过200个字符") @Size(min = 0, max = 200, message = "路由地址不能超过{max}个字符")
private String path; private String path;
/** /**
* 组件路径 * 组件路径
*/ */
@Size(min = 0, max = 200, message = "组件路径不能超过255个字符") @Size(min = 0, max = 200, message = "组件路径不能超过{max}个字符")
private String component; private String component;
/** /**
@ -88,7 +88,7 @@ public class SysMenu extends TreeEntity<SysMenu> {
* 权限字符串 * 权限字符串
*/ */
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") @Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符")
private String perms; private String perms;
/** /**

View File

@ -17,7 +17,6 @@ import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import java.util.Set;
/** /**
* 角色表 sys_role * 角色表 sys_role
@ -44,7 +43,7 @@ public class SysRole extends BaseEntity {
*/ */
@ExcelProperty(value = "角色名称") @ExcelProperty(value = "角色名称")
@NotBlank(message = "角色名称不能为空") @NotBlank(message = "角色名称不能为空")
@Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") @Size(min = 0, max = 30, message = "角色名称长度不能超过{max}个字符")
private String roleName; private String roleName;
/** /**
@ -52,7 +51,7 @@ public class SysRole extends BaseEntity {
*/ */
@ExcelProperty(value = "角色权限") @ExcelProperty(value = "角色权限")
@NotBlank(message = "权限字符不能为空") @NotBlank(message = "权限字符不能为空")
@Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") @Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符")
private String roleKey; private String roleKey;
/** /**
@ -115,12 +114,6 @@ public class SysRole extends BaseEntity {
@TableField(exist = false) @TableField(exist = false)
private Long[] deptIds; private Long[] deptIds;
/**
* 角色菜单权限
*/
@TableField(exist = false)
private Set<String> permissions;
public SysRole(Long roleId) { public SysRole(Long roleId) {
this.roleId = roleId; this.roleId = roleId;
} }

View File

@ -1,6 +1,8 @@
package com.ruoyi.common.core.domain.entity; package com.ruoyi.common.core.domain.entity;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.ruoyi.common.annotation.Sensitive; import com.ruoyi.common.annotation.Sensitive;
import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.domain.BaseEntity;
@ -44,14 +46,14 @@ public class SysUser extends BaseEntity {
*/ */
@Xss(message = "用户账号不能包含脚本字符") @Xss(message = "用户账号不能包含脚本字符")
@NotBlank(message = "用户账号不能为空") @NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符")
private String userName; private String userName;
/** /**
* 用户昵称 * 用户昵称
*/ */
@Xss(message = "用户昵称不能包含脚本字符") @Xss(message = "用户昵称不能包含脚本字符")
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符")
private String nickName; private String nickName;
/** /**
@ -64,7 +66,7 @@ public class SysUser extends BaseEntity {
*/ */
@Sensitive(strategy = SensitiveStrategy.EMAIL) @Sensitive(strategy = SensitiveStrategy.EMAIL)
@Email(message = "邮箱格式不正确") @Email(message = "邮箱格式不正确")
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
private String email; private String email;
/** /**
@ -91,6 +93,8 @@ public class SysUser extends BaseEntity {
updateStrategy = FieldStrategy.NOT_EMPTY, updateStrategy = FieldStrategy.NOT_EMPTY,
whereStrategy = FieldStrategy.NOT_EMPTY whereStrategy = FieldStrategy.NOT_EMPTY
) )
@JsonIgnore
@JsonProperty
private String password; private String password;
/** /**

View File

@ -1,7 +1,6 @@
package com.ruoyi.common.core.domain.model; package com.ruoyi.common.core.domain.model;
import com.ruoyi.common.core.domain.dto.RoleDTO; import com.ruoyi.common.core.domain.dto.RoleDTO;
import com.ruoyi.common.helper.LoginHelper;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -111,7 +110,7 @@ public class LoginUser implements Serializable {
if (userId == null) { if (userId == null) {
throw new IllegalArgumentException("用户ID不能为空"); throw new IllegalArgumentException("用户ID不能为空");
} }
return userType + LoginHelper.JOIN_CODE + userId; return userType + ":" + userId;
} }
} }

View File

@ -0,0 +1,18 @@
package com.ruoyi.common.core.service;
/**
* 通用 部门服务
*
* @author Lion Li
*/
public interface DeptService {
/**
* 通过部门ID查询部门名称
*
* @param deptIds 部门ID串逗号分隔
* @return 部门名称串逗号分隔
*/
String selectDeptNameByIds(String deptIds);
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.common.core.service;
/**
* 通用 OSS服务
*
* @author Lion Li
*/
public interface OssService {
/**
* 通过ossId查询对应的url
*
* @param ossIds ossId串逗号分隔
* @return url串逗号分隔
*/
String selectUrlByIds(String ossIds);
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.common.core.service;
/**
* 通用 用户服务
*
* @author Lion Li
*/
public interface UserService {
/**
* 通过用户ID查询用户账户
*
* @param userId 用户ID
* @return 用户账户
*/
String selectUserNameById(Long userId);
}

View File

@ -0,0 +1,41 @@
package com.ruoyi.common.encrypt;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
import lombok.Data;
/**
* 加密上下文 用于encryptor传递必要的参数
*
* @author 老马
* @version 4.6.0
*/
@Data
public class EncryptContext {
/**
* 默认算法
*/
private AlgorithmType algorithm;
/**
* 安全秘钥
*/
private String password;
/**
* 公钥
*/
private String publicKey;
/**
* 私钥
*/
private String privateKey;
/**
* 编码方式base64/hex
*/
private EncodeType encode;
}

View File

@ -0,0 +1,35 @@
package com.ruoyi.common.encrypt;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
/**
* 加解者
*
* @author 老马
* @version 4.6.0
*/
public interface IEncryptor {
/**
* 获得当前算法
*/
AlgorithmType algorithm();
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
* @return 加密后的字符串
*/
String encrypt(String value, EncodeType encodeType);
/**
* 解密
*
* @param value 待加密字符串
* @return 解密后的字符串
*/
String decrypt(String value);
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.common.encrypt.encryptor;
import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.encrypt.IEncryptor;
/**
* 所有加密执行者的基类
*
* @author 老马
* @version 4.6.0
*/
public abstract class AbstractEncryptor implements IEncryptor {
public AbstractEncryptor(EncryptContext context) {
// 用户配置校验与配置注入
}
}

View File

@ -0,0 +1,69 @@
package com.ruoyi.common.encrypt.encryptor;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
import java.nio.charset.StandardCharsets;
/**
* AES算法实现
*
* @author 老马
* @version 4.6.0
*/
public class AesEncryptor extends AbstractEncryptor {
private final AES aes;
public AesEncryptor(EncryptContext context) {
super(context);
String password = context.getPassword();
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES没有获得秘钥信息");
}
// aes算法的秘钥要求是16位24位32位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES秘钥长度应该为16位、24位、32位实际为" + password.length() + "");
}
aes = SecureUtil.aes(context.getPassword().getBytes(StandardCharsets.UTF_8));
}
/**
* 获得当前算法
*/
@Override
public AlgorithmType algorithm() {
return AlgorithmType.AES;
}
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
*/
@Override
public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) {
return aes.encryptHex(value);
} else {
return aes.encryptBase64(value);
}
}
/**
* 解密
*
* @param value 待加密字符串
*/
@Override
public String decrypt(String value) {
return this.aes.decryptStr(value);
}
}

View File

@ -0,0 +1,48 @@
package com.ruoyi.common.encrypt.encryptor;
import cn.hutool.core.codec.Base64;
import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
/**
* Base64算法实现
*
* @author 老马
* @version 4.6.0
*/
public class Base64Encryptor extends AbstractEncryptor {
public Base64Encryptor(EncryptContext context) {
super(context);
}
/**
* 获得当前算法
*/
@Override
public AlgorithmType algorithm() {
return AlgorithmType.BASE64;
}
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
*/
@Override
public String encrypt(String value, EncodeType encodeType) {
return Base64.encode(value);
}
/**
* 解密
*
* @param value 待加密字符串
*/
@Override
public String decrypt(String value) {
return Base64.decodeStr(value);
}
}

View File

@ -0,0 +1,65 @@
package com.ruoyi.common.encrypt.encryptor;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
import com.ruoyi.common.utils.StringUtils;
/**
* RSA算法实现
*
* @author 老马
* @version 4.6.0
*/
public class RsaEncryptor extends AbstractEncryptor {
private final RSA rsa;
public RsaEncryptor(EncryptContext context) {
super(context);
String privateKey = context.getPrivateKey();
String publicKey = context.getPublicKey();
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
throw new IllegalArgumentException("RSA公私钥均需要提供公钥加密私钥解密。");
}
this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey));
}
/**
* 获得当前算法
*/
@Override
public AlgorithmType algorithm() {
return AlgorithmType.RSA;
}
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
*/
@Override
public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) {
return rsa.encryptHex(value, KeyType.PublicKey);
} else {
return rsa.encryptBase64(value, KeyType.PublicKey);
}
}
/**
* 解密
*
* @param value 待加密字符串
*/
@Override
public String decrypt(String value) {
return this.rsa.decryptStr(value, KeyType.PrivateKey);
}
}

View File

@ -0,0 +1,65 @@
package com.ruoyi.common.encrypt.encryptor;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
import com.ruoyi.common.utils.StringUtils;
/**
* sm2算法实现
*
* @author 老马
* @version 4.6.0
*/
public class Sm2Encryptor extends AbstractEncryptor {
private final SM2 sm2;
public Sm2Encryptor(EncryptContext context) {
super(context);
String privateKey = context.getPrivateKey();
String publicKey = context.getPublicKey();
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
throw new IllegalArgumentException("SM2公私钥均需要提供公钥加密私钥解密。");
}
this.sm2 = SmUtil.sm2(Base64.decode(privateKey), Base64.decode(publicKey));
}
/**
* 获得当前算法
*/
@Override
public AlgorithmType algorithm() {
return AlgorithmType.SM2;
}
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
*/
@Override
public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) {
return sm2.encryptHex(value, KeyType.PublicKey);
} else {
return sm2.encryptBase64(value, KeyType.PublicKey);
}
}
/**
* 解密
*
* @param value 待加密字符串
*/
@Override
public String decrypt(String value) {
return this.sm2.decryptStr(value, KeyType.PrivateKey);
}
}

View File

@ -0,0 +1,67 @@
package com.ruoyi.common.encrypt.encryptor;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
import java.nio.charset.StandardCharsets;
/**
* sm4算法实现
*
* @author 老马
* @version 4.6.0
*/
public class Sm4Encryptor extends AbstractEncryptor {
private final SM4 sm4;
public Sm4Encryptor(EncryptContext context) {
super(context);
String password = context.getPassword();
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4没有获得秘钥信息");
}
// sm4算法的秘钥要求是16位长度
if (16 != password.length()) {
throw new IllegalArgumentException("SM4秘钥长度应该为16位实际为" + password.length() + "");
}
this.sm4 = SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8));
}
/**
* 获得当前算法
*/
@Override
public AlgorithmType algorithm() {
return AlgorithmType.SM4;
}
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
*/
@Override
public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) {
return sm4.encryptHex(value);
} else {
return sm4.encryptBase64(value);
}
}
/**
* 解密
*
* @param value 待加密字符串
*/
@Override
public String decrypt(String value) {
return this.sm4.decryptStr(value);
}
}

View File

@ -0,0 +1,48 @@
package com.ruoyi.common.enums;
import com.ruoyi.common.encrypt.encryptor.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 算法名称
*
* @author 老马
* @version 4.6.0
*/
@Getter
@AllArgsConstructor
public enum AlgorithmType {
/**
* 默认走yml配置
*/
DEFAULT(null),
/**
* base64
*/
BASE64(Base64Encryptor.class),
/**
* aes
*/
AES(AesEncryptor.class),
/**
* rsa
*/
RSA(RsaEncryptor.class),
/**
* sm2
*/
SM2(Sm2Encryptor.class),
/**
* sm4
*/
SM4(Sm4Encryptor.class);
private final Class<? extends AbstractEncryptor> clazz;
}

View File

@ -0,0 +1,26 @@
package com.ruoyi.common.enums;
/**
* 编码类型
*
* @author 老马
* @version 4.6.0
*/
public enum EncodeType {
/**
* 默认使用yml配置
*/
DEFAULT,
/**
* base64编码
*/
BASE64,
/**
* 16进制编码
*/
HEX;
}

View File

@ -25,7 +25,7 @@ public class XssFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException { public void init(FilterConfig filterConfig) throws ServletException {
String tempExcludes = filterConfig.getInitParameter("excludes"); String tempExcludes = filterConfig.getInitParameter("excludes");
if (StringUtils.isNotEmpty(tempExcludes)) { if (StringUtils.isNotEmpty(tempExcludes)) {
String[] url = tempExcludes.split(","); String[] url = tempExcludes.split(StringUtils.SEPARATOR);
for (int i = 0; url != null && i < url.length; i++) { for (int i = 0; url != null && i < url.length; i++) {
excludes.add(url[i]); excludes.add(url[i]);
} }

View File

@ -3,6 +3,8 @@ package com.ruoyi.common.helper;
import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaStorage; import cn.dev33.satoken.context.model.SaStorage;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -44,4 +46,19 @@ public class DataPermissionHelper {
} }
throw new NullPointerException("data permission context type exception"); throw new NullPointerException("data permission context type exception");
} }
/**
* 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭)
*/
public static void enableIgnore() {
InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build());
}
/**
* 关闭忽略数据权限
*/
public static void disableIgnore() {
InterceptorIgnoreHelper.clearIgnoreStrategy();
}
} }

View File

@ -1,25 +1,25 @@
package com.ruoyi.common.helper; package com.ruoyi.common.helper;
import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.DeviceType; import com.ruoyi.common.enums.DeviceType;
import com.ruoyi.common.enums.UserType; import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.exception.UtilException;
import com.ruoyi.common.utils.StringUtils;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
/** /**
* 登录鉴权助手 * 登录鉴权助手
* * <p>
* user_type 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app * user_type 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
* deivce 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios * deivce 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
* 可以组成 用户类型与设备类型多对多的 权限灵活控制 * 可以组成 用户类型与设备类型多对多的 权限灵活控制
* * <p>
* 多用户体系 针对 多种用户类型 但权限控制不一致 * 多用户体系 针对 多种用户类型 但权限控制不一致
* 可以组成 多用户类型表与多设备类型 分别控制权限 * 可以组成 多用户类型表与多设备类型 分别控制权限
* *
@ -28,8 +28,8 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class LoginHelper { public class LoginHelper {
public static final String JOIN_CODE = ":";
public static final String LOGIN_USER_KEY = "loginUser"; public static final String LOGIN_USER_KEY = "loginUser";
public static final String USER_KEY = "userId";
/** /**
* 登录系统 * 登录系统
@ -37,9 +37,7 @@ public class LoginHelper {
* @param loginUser 登录用户信息 * @param loginUser 登录用户信息
*/ */
public static void login(LoginUser loginUser) { public static void login(LoginUser loginUser) {
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); loginByDevice(loginUser, null);
StpUtil.login(loginUser.getLoginId());
setLoginUser(loginUser);
} }
/** /**
@ -49,15 +47,14 @@ public class LoginHelper {
* @param loginUser 登录用户信息 * @param loginUser 登录用户信息
*/ */
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) { public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); SaStorage storage = SaHolder.getStorage();
StpUtil.login(loginUser.getLoginId(), deviceType.getDevice()); storage.set(LOGIN_USER_KEY, loginUser);
setLoginUser(loginUser); storage.set(USER_KEY, loginUser.getUserId());
} SaLoginModel model = new SaLoginModel();
if (ObjectUtil.isNotNull(deviceType)) {
/** model.setDevice(deviceType.getDevice());
* 设置用户数据(多级缓存) }
*/ StpUtil.login(loginUser.getLoginId(), model.setExtra(USER_KEY, loginUser.getUserId()));
public static void setLoginUser(LoginUser loginUser) {
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser); StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
} }
@ -74,27 +71,28 @@ public class LoginHelper {
return loginUser; return loginUser;
} }
/**
* 获取用户基于token
*/
public static LoginUser getLoginUser(String token) {
return (LoginUser) StpUtil.getTokenSessionByToken(token).get(LOGIN_USER_KEY);
}
/** /**
* 获取用户id * 获取用户id
*/ */
public static Long getUserId() { public static Long getUserId() {
LoginUser loginUser = getLoginUser(); Long userId;
if (ObjectUtil.isNull(loginUser)) { try {
String loginId = StpUtil.getLoginIdAsString(); userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY));
String userId = null; if (ObjectUtil.isNull(userId)) {
for (UserType value : UserType.values()) { userId = Convert.toLong(StpUtil.getExtra(USER_KEY));
if (StringUtils.contains(loginId, value.getUserType())) { SaHolder.getStorage().set(USER_KEY, userId);
String[] strs = StringUtils.split(loginId, JOIN_CODE);
// 用户id在总是在最后
userId = strs[strs.length - 1];
}
} }
if (StringUtils.isBlank(userId)) { } catch (Exception e) {
throw new UtilException("登录用户: LoginId异常 => " + loginId); return null;
}
return Long.parseLong(userId);
} }
return loginUser.getUserId(); return userId;
} }
/** /**

View File

@ -22,7 +22,9 @@ import java.util.Objects;
* 字典数据json序列化工具 * 字典数据json序列化工具
* *
* @author itino * @author itino
* @deprecated 建议使用通用翻译注解
*/ */
@Deprecated
@Slf4j @Slf4j
public class DictDataJsonSerializer extends JsonSerializer<String> implements ContextualSerializer { public class DictDataJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {

View File

@ -0,0 +1,17 @@
package com.ruoyi.common.translation;
/**
* 翻译接口 (实现类需标注 {@link com.ruoyi.common.annotation.TranslationType} 注解标明翻译类型)
*
* @author Lion Li
*/
public interface TranslationInterface<T> {
/**
* 翻译
*
* @param key 需要被翻译的键(不为空)
* @return 返回键对应的值
*/
T translation(Object key, String other);
}

View File

@ -0,0 +1,29 @@
package com.ruoyi.common.translation.handler;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.util.List;
/**
* Bean 序列化修改器 解决 Null 被单独处理问题
*
* @author Lion Li
*/
public class TranslationBeanSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter writer : beanProperties) {
// 如果序列化器为 TranslationHandler 的话 Null 值也交给他处理
if (writer.getSerializer() instanceof TranslationHandler) {
writer.assignNullSerializer(writer.getSerializer());
}
}
return beanProperties;
}
}

View File

@ -0,0 +1,65 @@
package com.ruoyi.common.translation.handler;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.ruoyi.common.annotation.Translation;
import com.ruoyi.common.translation.TranslationInterface;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* 翻译处理器
*
* @author Lion Li
*/
@Slf4j
public class TranslationHandler extends JsonSerializer<Object> implements ContextualSerializer {
/**
* 全局翻译实现类映射器
*/
public static final Map<String, TranslationInterface<?>> TRANSLATION_MAPPER = new ConcurrentHashMap<>();
private Translation translation;
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
TranslationInterface<?> trans = TRANSLATION_MAPPER.get(translation.type());
if (ObjectUtil.isNotNull(trans)) {
// 如果映射字段不为空 则取映射字段的值
if (StringUtils.isNotBlank(translation.mapper())) {
value = ReflectUtils.invokeGetter(gen.getCurrentValue(), translation.mapper());
}
// 如果为 null 直接写出
if (ObjectUtil.isNull(value)) {
gen.writeNull();
return;
}
Object result = trans.translation(value, translation.other());
gen.writeObject(result);
} else {
gen.writeObject(value);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
Translation translation = property.getAnnotation(Translation.class);
if (Objects.nonNull(translation)) {
this.translation = translation;
return this;
}
return prov.findValueSerializer(property.getType(), property);
}
}

View File

@ -0,0 +1,25 @@
package com.ruoyi.common.translation.impl;
import com.ruoyi.common.annotation.TranslationType;
import com.ruoyi.common.constant.TransConstant;
import com.ruoyi.common.core.service.DeptService;
import com.ruoyi.common.translation.TranslationInterface;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 部门翻译实现
*
* @author Lion Li
*/
@Component
@AllArgsConstructor
@TranslationType(type = TransConstant.DEPT_ID_TO_NAME)
public class DeptNameTranslationImpl implements TranslationInterface<String> {
private final DeptService deptService;
public String translation(Object key, String other) {
return deptService.selectDeptNameByIds(key.toString());
}
}

View File

@ -0,0 +1,29 @@
package com.ruoyi.common.translation.impl;
import com.ruoyi.common.annotation.TranslationType;
import com.ruoyi.common.constant.TransConstant;
import com.ruoyi.common.core.service.DictService;
import com.ruoyi.common.translation.TranslationInterface;
import com.ruoyi.common.utils.StringUtils;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 字典翻译实现
*
* @author Lion Li
*/
@Component
@AllArgsConstructor
@TranslationType(type = TransConstant.DICT_TYPE_TO_LABEL)
public class DictTypeTranslationImpl implements TranslationInterface<String> {
private final DictService dictService;
public String translation(Object key, String other) {
if (key instanceof String && StringUtils.isNotBlank(other)) {
return dictService.getDictLabel(other, key.toString());
}
return null;
}
}

View File

@ -0,0 +1,25 @@
package com.ruoyi.common.translation.impl;
import com.ruoyi.common.annotation.TranslationType;
import com.ruoyi.common.constant.TransConstant;
import com.ruoyi.common.core.service.OssService;
import com.ruoyi.common.translation.TranslationInterface;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
/**
* OSS翻译实现
*
* @author Lion Li
*/
@Component
@AllArgsConstructor
@TranslationType(type = TransConstant.OSS_ID_TO_URL)
public class OssUrlTranslationImpl implements TranslationInterface<String> {
private final OssService ossService;
public String translation(Object key, String other) {
return ossService.selectUrlByIds(key.toString());
}
}

View File

@ -0,0 +1,28 @@
package com.ruoyi.common.translation.impl;
import com.ruoyi.common.annotation.TranslationType;
import com.ruoyi.common.constant.TransConstant;
import com.ruoyi.common.core.service.UserService;
import com.ruoyi.common.translation.TranslationInterface;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 用户名翻译实现
*
* @author Lion Li
*/
@Component
@AllArgsConstructor
@TranslationType(type = TransConstant.USER_ID_TO_NAME)
public class UserNameTranslationImpl implements TranslationInterface<String> {
private final UserService userService;
public String translation(Object key, String other) {
if (key instanceof Long) {
return userService.selectUserNameById((Long) key);
}
return null;
}
}

View File

@ -12,6 +12,7 @@ import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.beans.BeanMap; import org.springframework.cglib.beans.BeanMap;
import org.springframework.cglib.core.Converter; import org.springframework.cglib.core.Converter;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -135,6 +136,25 @@ public class BeanCopyUtils {
return bean; return bean;
} }
/**
* map拷贝到map
*
* @param map 数据来源
* @param clazz 返回的对象类型
* @return map对象
*/
public static <T, V> Map<String, V> mapToMap(Map<String, T> map, Class<V> clazz) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(clazz)) {
return null;
}
Map<String, V> copyMap = new LinkedHashMap<>(map.size());
map.forEach((k, v) -> copyMap.put(k, copy(v, clazz)));
return copyMap;
}
/** /**
* BeanCopier属性缓存<br> * BeanCopier属性缓存<br>
* 缓存用于防止多次反射造成的性能问题 * 缓存用于防止多次反射造成的性能问题
@ -174,7 +194,7 @@ public class BeanCopyUtils {
private String genKey(Class<?> srcClass, Class<?> targetClass, Converter converter) { private String genKey(Class<?> srcClass, Class<?> targetClass, Converter converter) {
final StringBuilder key = StrUtil.builder() final StringBuilder key = StrUtil.builder()
.append(srcClass.getName()).append('#').append(targetClass.getName()); .append(srcClass.getName()).append('#').append(targetClass.getName());
if(null != converter){ if (null != converter) {
key.append('#').append(converter.getClass().getName()); key.append('#').append(converter.getClass().getName());
} }
return key.toString(); return key.toString();

View File

@ -94,7 +94,7 @@ public class ServletUtils extends ServletUtil {
public static Map<String, String> getParamMap(ServletRequest request) { public static Map<String, String> getParamMap(ServletRequest request) {
Map<String, String> params = new HashMap<>(); Map<String, String> params = new HashMap<>();
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) { for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); params.put(entry.getKey(), StringUtils.join(entry.getValue(), StringUtils.SEPARATOR));
} }
return params; return params;
} }

View File

@ -41,7 +41,7 @@ public class StreamUtils {
* @return 拼接后的list * @return 拼接后的list
*/ */
public static <E> String join(Collection<E> collection, Function<E, String> function) { public static <E> String join(Collection<E> collection, Function<E, String> function) {
return join(collection, function, ","); return join(collection, function, StringUtils.SEPARATOR);
} }
/** /**

View File

@ -1,16 +1,16 @@
package com.ruoyi.common.utils; package com.ruoyi.common.utils;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator; import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import java.util.ArrayList; import java.util.*;
import java.util.HashSet; import java.util.function.Function;
import java.util.List; import java.util.stream.Collectors;
import java.util.Set;
/** /**
* 字符串工具类 * 字符串工具类
@ -20,6 +20,8 @@ import java.util.Set;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StringUtils extends org.apache.commons.lang3.StringUtils { public class StringUtils extends org.apache.commons.lang3.StringUtils {
public static final String SEPARATOR = ",";
/** /**
* 获取参数不为空值 * 获取参数不为空值
* *
@ -224,7 +226,6 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* *
* @param pattern 匹配规则 * @param pattern 匹配规则
* @param url 需要匹配的url * @param url 需要匹配的url
* @return
*/ */
public static boolean isMatch(String pattern, String url) { public static boolean isMatch(String pattern, String url) {
AntPathMatcher matcher = new AntPathMatcher(); AntPathMatcher matcher = new AntPathMatcher();
@ -234,23 +235,23 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/** /**
* 数字左边补齐0使之达到指定长度注意如果数字转换为字符串后长度大于size则只保留 最后size个字符 * 数字左边补齐0使之达到指定长度注意如果数字转换为字符串后长度大于size则只保留 最后size个字符
* *
* @param num 数字对象 * @param num 数字对象
* @param size 字符串指定长度 * @param size 字符串指定长度
* @return 返回数字的字符串格式该字符串为指定长度 * @return 返回数字的字符串格式该字符串为指定长度
*/ */
public static final String padl(final Number num, final int size) { public static String padl(final Number num, final int size) {
return padl(num.toString(), size, '0'); return padl(num.toString(), size, '0');
} }
/** /**
* 字符串左补齐如果原始字符串s长度大于size则只保留最后size个字符 * 字符串左补齐如果原始字符串s长度大于size则只保留最后size个字符
* *
* @param s 原始字符串 * @param s 原始字符串
* @param size 字符串指定长度 * @param size 字符串指定长度
* @param c 用于补齐的字符 * @param c 用于补齐的字符
* @return 返回指定长度的字符串由原字符串左补齐或截取得到 * @return 返回指定长度的字符串由原字符串左补齐或截取得到
*/ */
public static final String padl(final String s, final int size, final char c) { public static String padl(final String s, final int size, final char c) {
final StringBuilder sb = new StringBuilder(size); final StringBuilder sb = new StringBuilder(size);
if (s != null) { if (s != null) {
final int len = s.length(); final int len = s.length();
@ -270,4 +271,55 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
return sb.toString(); return sb.toString();
} }
/**
* 切分字符串(分隔符默认逗号)
*
* @param str 被切分的字符串
* @return 分割后的数据列表
*/
public static List<String> splitList(String str) {
return splitTo(str, Convert::toStr);
}
/**
* 切分字符串
*
* @param str 被切分的字符串
* @param separator 分隔符
* @return 分割后的数据列表
*/
public static List<String> splitList(String str, String separator) {
return splitTo(str, separator, Convert::toStr);
}
/**
* 切分字符串自定义转换(分隔符默认逗号)
*
* @param str 被切分的字符串
* @param mapper 自定义转换
* @return 分割后的数据列表
*/
public static <T> List<T> splitTo(String str, Function<? super Object, T> mapper) {
return splitTo(str, SEPARATOR, mapper);
}
/**
* 切分字符串自定义转换
*
* @param str 被切分的字符串
* @param separator 分隔符
* @param mapper 自定义转换
* @return 分割后的数据列表
*/
public static <T> List<T> splitTo(String str, String separator, Function<? super Object, T> mapper) {
if (isBlank(str)) {
return new ArrayList<>(0);
}
return StrUtil.split(str, separator)
.stream()
.filter(Objects::nonNull)
.map(mapper)
.collect(Collectors.toList());
}
} }

View File

@ -270,7 +270,7 @@ public class ExcelUtil {
*/ */
public static String convertByExp(String propertyValue, String converterExp, String separator) { public static String convertByExp(String propertyValue, String converterExp, String separator) {
StringBuilder propertyString = new StringBuilder(); StringBuilder propertyString = new StringBuilder();
String[] convertSource = converterExp.split(","); String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
for (String item : convertSource) { for (String item : convertSource) {
String[] itemArray = item.split("="); String[] itemArray = item.split("=");
if (StringUtils.containsAny(propertyValue, separator)) { if (StringUtils.containsAny(propertyValue, separator)) {
@ -299,7 +299,7 @@ public class ExcelUtil {
*/ */
public static String reverseByExp(String propertyValue, String converterExp, String separator) { public static String reverseByExp(String propertyValue, String converterExp, String separator) {
StringBuilder propertyString = new StringBuilder(); StringBuilder propertyString = new StringBuilder();
String[] convertSource = converterExp.split(","); String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
for (String item : convertSource) { for (String item : convertSource) {
String[] itemArray = item.split("="); String[] itemArray = item.split("=");
if (StringUtils.containsAny(propertyValue, separator)) { if (StringUtils.containsAny(propertyValue, separator)) {

View File

@ -0,0 +1,55 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.demo.domain.TestDemoEncrypt;
import com.ruoyi.demo.mapper.TestDemoEncryptMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* 测试数据库加解密功能
*
* @author Lion Li
*/
@Validated
@RestController
@RequestMapping("/demo/encrypt")
public class TestEncryptController {
@Autowired
private TestDemoEncryptMapper mapper;
@Value("${mybatis-encryptor.enable}")
private Boolean encryptEnable;
/**
* 测试数据库加解密
*
* @param key 测试key
* @param value 测试value
*/
@GetMapping()
public R<Map<String, TestDemoEncrypt>> test(String key, String value) {
if (!encryptEnable) {
throw new RuntimeException("加密功能未开启!");
}
Map<String, TestDemoEncrypt> map = new HashMap<>(2);
TestDemoEncrypt demo = new TestDemoEncrypt();
demo.setTestKey(key);
demo.setValue(value);
mapper.insert(demo);
map.put("加密", demo);
TestDemoEncrypt testDemo = mapper.selectById(demo.getId());
map.put("解密", testDemo);
return R.ok(map);
}
}

View File

@ -0,0 +1,29 @@
package com.ruoyi.demo.domain;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.annotation.EncryptField;
import com.ruoyi.common.enums.AlgorithmType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("test_demo")
public class TestDemoEncrypt extends TestDemo {
/**
* key键
*/
// @EncryptField(algorithm=AlgorithmType.SM2, privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgZSlOvw8FBiH+aFJWLYZP/VRjg9wjfRarTkGBZd/T3N+gCgYIKoEcz1UBgi2hRANCAAR5DGuQwJqkxnbCsP+iPSDoHWIF4RwcR5EsSvT8QPxO1wRkR2IhCkzvRb32x2CUgJFdvoqVqfApFDPZzShqzBwX", publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEeQxrkMCapMZ2wrD/oj0g6B1iBeEcHEeRLEr0/ED8TtcEZEdiIQpM70W99sdglICRXb6KlanwKRQz2c0oaswcFw==")
@EncryptField(algorithm = AlgorithmType.RSA, privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANBBEeueWlXlkkj2+WY5l+IWe42d8b5K28g+G/CFKC/yYAEHtqGlCsBOrb+YBkG9mPzmuYA/n9k0NFIc8E8yY5vZQaroyFBrTTWEzG9RY2f7Y3svVyybs6jpXSUs4xff8abo7wL1Y/wUaeatTViamxYnyTvdTmLm3d+JjRij68rxAgMBAAECgYAB0TnhXraSopwIVRfmboea1b0upl+BUdTJcmci412UjrKr5aE695ZLPkXbFXijVu7HJlyyv94NVUdaMACV7Ku/S2RuNB70M7YJm8rAjHFC3/i2ZeIM60h1Ziy4QKv0XM3pRATlDCDNhC1WUrtQCQSgU8kcp6eUUppruOqDzcY04QJBAPm9+sBP9CwDRgy3e5+V8aZtJkwDstb0lVVV/KY890cydVxiCwvX3fqVnxKMlb+x0YtH0sb9v+71xvK2lGobaRECQQDVePU6r/cCEfpc+nkWF6osAH1f8Mux3rYv2DoBGvaPzV2BGfsLed4neRfCwWNCKvGPCdW+L0xMJg8+RwaoBUPhAkAT5kViqXxFPYWJYd1h2+rDXhMdH3ZSlm6HvDBDdrwlWinr0Iwcx3iSjPV93uHXwm118aUj4fg3LDJMCKxOwBxhAkByrQXfvwOMYygBprRBf/j0plazoWFrbd6lGR0f1uI5IfNnFRPdeFw1DEINZ2Hw+6zEUF44SqRMC+4IYJNc02dBAkBCgy7RvfyV/A7N6kKXxTHauY0v6XwSSvpeKtRJkbIcRWOdIYvaHO9L7cklj3vIEdwjSUp9K4VTBYYlmAz1xh03", publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQQRHrnlpV5ZJI9vlmOZfiFnuNnfG+StvIPhvwhSgv8mABB7ahpQrATq2/mAZBvZj85rmAP5/ZNDRSHPBPMmOb2UGq6MhQa001hMxvUWNn+2N7L1csm7Oo6V0lLOMX3/Gm6O8C9WP8FGnmrU1YmpsWJ8k73U5i5t3fiY0Yo+vK8QIDAQAB")
private String testKey;
/**
*
*/
// @EncryptField // 什么也不写走默认yml配置
// @EncryptField(algorithm = AlgorithmType.SM4, password = "10rfylhtccpuyke5")
@EncryptField(algorithm = AlgorithmType.AES, password = "10rfylhtccpuyke5")
private String value;
}

View File

@ -0,0 +1,13 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.common.core.mapper.BaseMapperPlus;
import com.ruoyi.demo.domain.TestDemoEncrypt;
/**
* 测试加密功能
*
* @author Lion Li
*/
public interface TestDemoEncryptMapper extends BaseMapperPlus<TestDemoEncryptMapper, TestDemoEncrypt, TestDemoEncrypt> {
}

View File

@ -112,7 +112,7 @@ public class LogAspect {
// 是否需要保存request参数和值 // 是否需要保存request参数和值
if (log.isSaveRequestData()) { if (log.isSaveRequestData()) {
// 获取参数的信息传入到数据库中 // 获取参数的信息传入到数据库中
setRequestValue(joinPoint, operLog); setRequestValue(joinPoint, operLog, log.excludeParamNames());
} }
// 是否需要保存response参数和值 // 是否需要保存response参数和值
if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) { if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) {
@ -126,14 +126,16 @@ public class LogAspect {
* @param operLog 操作日志 * @param operLog 操作日志
* @throws Exception 异常 * @throws Exception 异常
*/ */
private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog) throws Exception { private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception {
Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
String requestMethod = operLog.getRequestMethod(); String requestMethod = operLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { if (MapUtil.isEmpty(paramsMap)
String params = argsArrayToString(joinPoint.getArgs()); && HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
operLog.setOperParam(StringUtils.substring(params, 0, 2000)); operLog.setOperParam(StringUtils.substring(params, 0, 2000));
} else { } else {
Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES); MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES);
MapUtil.removeAny(paramsMap, excludeParamNames);
operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000)); operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000));
} }
} }
@ -141,7 +143,7 @@ public class LogAspect {
/** /**
* 参数拼装 * 参数拼装
*/ */
private String argsArrayToString(Object[] paramsArray) { private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) {
StringBuilder params = new StringBuilder(); StringBuilder params = new StringBuilder();
if (paramsArray != null && paramsArray.length > 0) { if (paramsArray != null && paramsArray.length > 0) {
for (Object o : paramsArray) { for (Object o : paramsArray) {
@ -151,6 +153,7 @@ public class LogAspect {
Dict dict = JsonUtils.parseMap(str); Dict dict = JsonUtils.parseMap(str);
if (MapUtil.isNotEmpty(dict)) { if (MapUtil.isNotEmpty(dict)) {
MapUtil.removeAny(dict, EXCLUDE_PROPERTIES); MapUtil.removeAny(dict, EXCLUDE_PROPERTIES);
MapUtil.removeAny(dict, excludeParamNames);
str = JsonUtils.toJsonString(dict); str = JsonUtils.toJsonString(dict);
} }
params.append(str).append(" "); params.append(str).append(" ");

View File

@ -1,10 +1,13 @@
package com.ruoyi.framework.aspectj; package com.ruoyi.framework.aspectj;
import cn.hutool.core.util.ArrayUtil;
import com.ruoyi.common.annotation.RateLimiter; import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.enums.LimitType; import com.ruoyi.common.enums.LimitType;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils; import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.RedisUtils; import com.ruoyi.common.utils.redis.RedisUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
@ -12,6 +15,14 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature; import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RateType; import org.redisson.api.RateType;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -26,6 +37,23 @@ import java.lang.reflect.Method;
@Component @Component
public class RateLimiterAspect { public class RateLimiterAspect {
/**
* 定义spel表达式解析器
*/
private final ExpressionParser parser = new SpelExpressionParser();
/**
* 定义spel解析模版
*/
private final ParserContext parserContext = new TemplateParserContext();
/**
* 定义spel上下文对象进行解析
*/
private final EvaluationContext context = new StandardEvaluationContext();
/**
* 方法参数解析器
*/
private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
@Before("@annotation(rateLimiter)") @Before("@annotation(rateLimiter)")
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable { public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {
int time = rateLimiter.time(); int time = rateLimiter.time();
@ -38,29 +66,56 @@ public class RateLimiterAspect {
} }
long number = RedisUtils.rateLimiter(combineKey, rateType, count, time); long number = RedisUtils.rateLimiter(combineKey, rateType, count, time);
if (number == -1) { if (number == -1) {
throw new ServiceException(MessageUtils.message("rate.limiter.message")); String message = rateLimiter.message();
if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1));
}
throw new ServiceException(message);
} }
log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", count, number, combineKey); log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", count, number, combineKey);
} catch (ServiceException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("服务器限流异常,请稍候再试"); if (e instanceof ServiceException) {
throw e;
} else {
throw new RuntimeException("服务器限流异常,请稍候再试");
}
} }
} }
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
StringBuilder stringBuffer = new StringBuilder(rateLimiter.key()); String key = rateLimiter.key();
if (rateLimiter.limitType() == LimitType.IP) { // 获取方法(通过方法签名来获取)
// 获取请求ip
stringBuffer.append(ServletUtils.getClientIP()).append("-");
} else if (rateLimiter.limitType() == LimitType.CLUSTER) {
// 获取客户端实例id
stringBuffer.append(RedisUtils.getClient().getId()).append("-");
}
MethodSignature signature = (MethodSignature) point.getSignature(); MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod(); Method method = signature.getMethod();
Class<?> targetClass = method.getDeclaringClass(); Class<?> targetClass = method.getDeclaringClass();
stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); // 判断是否是spel格式
return stringBuffer.toString(); if (StringUtils.containsAny(key, "#")) {
// 获取参数值
Object[] args = point.getArgs();
// 获取方法上参数的名称
String[] parameterNames = pnd.getParameterNames(method);
if (ArrayUtil.isEmpty(parameterNames)) {
throw new ServiceException("限流key解析异常!请联系管理员!");
}
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
// 解析返回给key
try {
key = parser.parseExpression(key, parserContext).getValue(context, String.class) + ":";
} catch (Exception e) {
throw new ServiceException("限流key解析异常!请联系管理员!");
}
}
StringBuilder stringBuffer = new StringBuilder(CacheConstants.RATE_LIMIT_KEY);
stringBuffer.append(ServletUtils.getRequest().getRequestURI()).append(":");
if (rateLimiter.limitType() == LimitType.IP) {
// 获取请求ip
stringBuffer.append(ServletUtils.getClientIP()).append(":");
} else if (rateLimiter.limitType() == LimitType.CLUSTER) {
// 获取客户端实例id
stringBuffer.append(RedisUtils.getClient().getId()).append(":");
}
return stringBuffer.append(key).toString();
} }
} }

View File

@ -0,0 +1,39 @@
package com.ruoyi.framework.config;
import com.ruoyi.framework.config.properties.EncryptorProperties;
import com.ruoyi.framework.manager.EncryptorManager;
import com.ruoyi.framework.encrypt.MybatisDecryptInterceptor;
import com.ruoyi.framework.encrypt.MybatisEncryptInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 加解密配置
*
* @author 老马
* @version 4.6.0
*/
@Configuration
@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true")
public class EncryptorConfig {
@Autowired
private EncryptorProperties properties;
@Bean
public EncryptorManager encryptorManager() {
return new EncryptorManager();
}
@Bean
public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) {
return new MybatisEncryptInterceptor(encryptorManager, properties);
}
@Bean
public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
return new MybatisDecryptInterceptor(encryptorManager, properties);
}
}

View File

@ -32,7 +32,7 @@ public class FilterConfig {
FilterRegistrationBean registration = new FilterRegistrationBean(); FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST); registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter()); registration.setFilter(new XssFilter());
registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), ",")); registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), StringUtils.SEPARATOR));
registration.setName("xssFilter"); registration.setName("xssFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
Map<String, String> initParameters = new HashMap<String, String>(); Map<String, String> initParameters = new HashMap<String, String>();

View File

@ -1,11 +1,17 @@
package com.ruoyi.framework.config; package com.ruoyi.framework.config;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.interceptor.SaInterceptor; import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple; import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.SecurityProperties; import com.ruoyi.framework.config.properties.SecurityProperties;
import com.ruoyi.framework.handler.AllUrlHandler;
import com.ruoyi.framework.satoken.dao.PlusSaTokenDao;
import com.ruoyi.framework.satoken.service.SaPermissionImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -32,10 +38,11 @@ public class SaTokenConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
// 注册路由拦截器自定义验证规则 // 注册路由拦截器自定义验证规则
registry.addInterceptor(new SaInterceptor(handler -> { registry.addInterceptor(new SaInterceptor(handler -> {
AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class);
// 登录验证 -- 排除多个路径 // 登录验证 -- 排除多个路径
SaRouter SaRouter
// 获取所有的 // 获取所有的
.match("/**") .match(allUrlHandler.getUrls())
// 对未排除的路径进行检查 // 对未排除的路径进行检查
.check(() -> { .check(() -> {
// 检查是否登录 是否有token // 检查是否登录 是否有token
@ -59,4 +66,20 @@ public class SaTokenConfig implements WebMvcConfigurer {
return new StpLogicJwtForSimple(); return new StpLogicJwtForSimple();
} }
/**
* 权限接口实现(使用bean注入方便用户替换)
*/
@Bean
public StpInterface stpInterface() {
return new SaPermissionImpl();
}
/**
* 自定义dao层存储
*/
@Bean
public SaTokenDao saTokenDao() {
return new PlusSaTokenDao();
}
} }

View File

@ -33,7 +33,7 @@ import java.util.Set;
@RequiredArgsConstructor @RequiredArgsConstructor
@Configuration @Configuration
@AutoConfigureBefore(SpringDocConfiguration.class) @AutoConfigureBefore(SpringDocConfiguration.class)
@ConditionalOnProperty(name = "swagger.enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true)
public class SwaggerConfig { public class SwaggerConfig {
private final SwaggerProperties swaggerProperties; private final SwaggerProperties swaggerProperties;

View File

@ -0,0 +1,50 @@
package com.ruoyi.framework.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.annotation.TranslationType;
import com.ruoyi.common.translation.TranslationInterface;
import com.ruoyi.common.translation.handler.TranslationBeanSerializerModifier;
import com.ruoyi.common.translation.handler.TranslationHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 翻译模块配置类
*
* @author Lion Li
*/
@Slf4j
@Configuration
public class TranslationConfig {
@Autowired
private List<TranslationInterface<?>> list;
@Autowired
private ObjectMapper objectMapper;
@PostConstruct
public void init() {
Map<String, TranslationInterface<?>> map = new HashMap<>(list.size());
for (TranslationInterface<?> trans : list) {
if (trans.getClass().isAnnotationPresent(TranslationType.class)) {
TranslationType annotation = trans.getClass().getAnnotation(TranslationType.class);
map.put(annotation.type(), trans);
} else {
log.warn(trans.getClass().getName() + " 翻译实现类未标注 TranslationType 注解!");
}
}
TranslationHandler.TRANSLATION_MAPPER.putAll(map);
// 设置 Bean 序列化修改器
objectMapper.setSerializerFactory(
objectMapper.getSerializerFactory()
.withSerializerModifier(new TranslationBeanSerializerModifier()));
}
}

View File

@ -0,0 +1,50 @@
package com.ruoyi.framework.config.properties;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 加解密属性配置类
*
* @author 老马
* @version 4.6.0
*/
@Data
@Component
@ConfigurationProperties(prefix = "mybatis-encryptor")
public class EncryptorProperties {
/**
* 过滤开关
*/
private Boolean enable;
/**
* 默认算法
*/
private AlgorithmType algorithm;
/**
* 安全秘钥
*/
private String password;
/**
* 公钥
*/
private String publicKey;
/**
* 私钥
*/
private String privateKey;
/**
* 编码方式base64/hex
*/
private EncodeType encode;
}

View File

@ -23,11 +23,6 @@ import java.util.List;
@ConfigurationProperties(prefix = "swagger") @ConfigurationProperties(prefix = "swagger")
public class SwaggerProperties { public class SwaggerProperties {
/**
* 是否开启 openApi 文档
*/
private Boolean enabled = true;
/** /**
* 文档基本信息 * 文档基本信息
*/ */

View File

@ -0,0 +1,113 @@
package com.ruoyi.framework.encrypt;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.annotation.EncryptField;
import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.properties.EncryptorProperties;
import com.ruoyi.framework.manager.EncryptorManager;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.*;
/**
* 出参解密拦截器
*
* @author 老马
* @version 4.6.0
*/
@Slf4j
@Intercepts({@Signature(
type = ResultSetHandler.class,
method = "handleResultSets",
args = {Statement.class})
})
@AllArgsConstructor
public class MybatisDecryptInterceptor implements Interceptor {
private final EncryptorManager encryptorManager;
private final EncryptorProperties defaultProperties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取执行mysql执行结果
Object result = invocation.proceed();
if (result == null) {
return null;
}
decryptHandler(result);
return result;
}
/**
* 解密对象
*
* @param sourceObject 待加密对象
*/
private void decryptHandler(Object sourceObject) {
if (ObjectUtil.isNull(sourceObject)) {
return;
}
if (sourceObject instanceof Map<?, ?>) {
new HashSet<>(((Map<?, ?>) sourceObject).values()).forEach(this::decryptHandler);
return;
}
if (sourceObject instanceof List<?>) {
List<?> sourceList = (List<?>) sourceObject;
if(CollectionUtil.isEmpty(sourceList)) {
return;
}
// 判断第一个元素是否含有注解如果没有直接返回提高效率
Object firstItem = sourceList.get(0);
if (CollectionUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
return;
}
((List<?>) sourceObject).forEach(this::decryptHandler);
return;
}
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
try {
for (Field field : fields) {
field.set(sourceObject, this.decryptField(String.valueOf(field.get(sourceObject)), field));
}
} catch (Exception e) {
log.error("处理解密字段时出错", e);
}
}
/**
* 字段值进行加密通过字段的批注注册新的加密算法
*
* @param value 待加密的值
* @param field 待加密字段
* @return 加密后结果
*/
private String decryptField(String value, Field field) {
EncryptField encryptField = field.getAnnotation(EncryptField.class);
EncryptContext encryptContext = new EncryptContext();
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
return this.encryptorManager.decrypt(value, encryptContext);
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}

View File

@ -0,0 +1,118 @@
package com.ruoyi.framework.encrypt;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.annotation.EncryptField;
import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.properties.EncryptorProperties;
import com.ruoyi.framework.manager.EncryptorManager;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.*;
/**
* 入参加密拦截器
*
* @author 老马
* @version 4.6.0
*/
@Slf4j
@Intercepts({@Signature(
type = ParameterHandler.class,
method = "setParameters",
args = {PreparedStatement.class})
})
@AllArgsConstructor
public class MybatisEncryptInterceptor implements Interceptor {
private final EncryptorManager encryptorManager;
private final EncryptorProperties defaultProperties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
return invocation;
}
@Override
public Object plugin(Object target) {
if (target instanceof ParameterHandler) {
// 进行加密操作
ParameterHandler parameterHandler = (ParameterHandler) target;
Object parameterObject = parameterHandler.getParameterObject();
if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
this.encryptHandler(parameterObject);
}
}
return target;
}
/**
* 加密对象
*
* @param sourceObject 待加密对象
*/
private void encryptHandler(Object sourceObject) {
if (ObjectUtil.isNull(sourceObject)) {
return;
}
if (sourceObject instanceof Map<?, ?>) {
new HashSet<>(((Map<?, ?>) sourceObject).values()).forEach(this::encryptHandler);
return;
}
if (sourceObject instanceof List<?>) {
List<?> sourceList = (List<?>) sourceObject;
if(CollectionUtil.isEmpty(sourceList)) {
return;
}
// 判断第一个元素是否含有注解如果没有直接返回提高效率
Object firstItem = sourceList.get(0);
if (CollectionUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
return;
}
((List<?>) sourceObject).forEach(this::encryptHandler);
return;
}
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
try {
for (Field field : fields) {
field.set(sourceObject, this.encryptField(String.valueOf(field.get(sourceObject)), field));
}
} catch (Exception e) {
log.error("处理加密字段时出错", e);
}
}
/**
* 字段值进行加密通过字段的批注注册新的加密算法
*
* @param value 待加密的值
* @param field 待加密字段
* @return 加密后结果
*/
private String encryptField(String value, Field field) {
EncryptField encryptField = field.getAnnotation(EncryptField.class);
EncryptContext encryptContext = new EncryptContext();
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
return this.encryptorManager.encrypt(value, encryptContext);
}
@Override
public void setProperties(Properties properties) {
}
}

View File

@ -0,0 +1,41 @@
package com.ruoyi.framework.handler;
import cn.hutool.core.util.ReUtil;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.Data;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.*;
import java.util.regex.Pattern;
/**
* 获取所有Url配置
*
* @author Lion Li
*/
@Data
@Component
public class AllUrlHandler implements InitializingBean {
private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
private List<String> urls = new ArrayList<>();
@Override
public void afterPropertiesSet() {
Set<String> set = new HashSet<>();
RequestMappingHandlerMapping mapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
map.keySet().forEach(info -> {
// 获取注解上边的 path 替代 path variable *
Objects.requireNonNull(info.getPathPatternsCondition().getPatterns())
.forEach(url -> set.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, "*")));
});
urls.addAll(set);
}
}

View File

@ -0,0 +1,96 @@
package com.ruoyi.framework.manager;
import cn.hutool.core.util.ReflectUtil;
import com.ruoyi.common.annotation.EncryptField;
import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.encrypt.IEncryptor;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 加密管理类
*
* @author 老马
* @version 4.6.0
*/
@Slf4j
public class EncryptorManager {
/**
* 缓存加密器
*/
Map<EncryptContext, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
/**
* 类加密字段缓存
*/
Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();
/**
* 获取类加密字段缓存
*/
public Set<Field> getFieldCache(Class<?> sourceClazz) {
return fieldCache.computeIfAbsent(sourceClazz, clazz -> {
Field[] declaredFields = clazz.getDeclaredFields();
Set<Field> fieldSet = Arrays.stream(declaredFields).filter(field ->
field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
.collect(Collectors.toSet());
for (Field field : fieldSet) {
field.setAccessible(true);
}
return fieldSet;
});
}
/**
* 注册加密执行者到缓存
*
* @param encryptContext 加密执行者需要的相关配置参数
*/
public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {
if (encryptorMap.containsKey(encryptContext)) {
return encryptorMap.get(encryptContext);
}
IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);
encryptorMap.put(encryptContext, encryptor);
return encryptor;
}
/**
* 移除缓存中的加密执行者
*
* @param encryptContext 加密执行者需要的相关配置参数
*/
public void removeEncryptor(EncryptContext encryptContext) {
this.encryptorMap.remove(encryptContext);
}
/**
* 根据配置进行加密会进行本地缓存对应的算法和对应的秘钥信息
*
* @param value 待加密的值
* @param encryptContext 加密相关的配置信息
*/
public String encrypt(String value, EncryptContext encryptContext) {
IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
return encryptor.encrypt(value, encryptContext.getEncode());
}
/**
* 根据配置进行解密
*
* @param value 待解密的值
* @param encryptContext 加密相关的配置信息
*/
public String decrypt(String value, EncryptContext encryptContext) {
IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
return encryptor.decrypt(value);
}
}

View File

@ -3,7 +3,6 @@ package com.ruoyi.framework.satoken.dao;
import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaFoxUtil;
import com.ruoyi.common.utils.redis.RedisUtils; import com.ruoyi.common.utils.redis.RedisUtils;
import org.springframework.stereotype.Component;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
@ -15,7 +14,6 @@ import java.util.List;
* *
* @author Lion Li * @author Lion Li
*/ */
@Component
public class PlusSaTokenDao implements SaTokenDao { public class PlusSaTokenDao implements SaTokenDao {
/** /**

View File

@ -4,7 +4,6 @@ import cn.dev33.satoken.stp.StpInterface;
import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserType; import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.common.helper.LoginHelper;
import org.springframework.stereotype.Component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -14,7 +13,6 @@ import java.util.List;
* *
* @author Lion Li * @author Lion Li
*/ */
@Component
public class SaPermissionImpl implements StpInterface { public class SaPermissionImpl implements StpInterface {
/** /**

View File

@ -212,7 +212,7 @@ public class GenTableColumn extends BaseEntity {
if (StringUtils.isNotEmpty(value)) { if (StringUtils.isNotEmpty(value)) {
Object startStr = value.subSequence(0, 1); Object startStr = value.subSequence(0, 1);
String endStr = value.substring(1); String endStr = value.substring(1);
sb.append("").append(startStr).append("=").append(endStr).append(","); sb.append(StringUtils.EMPTY).append(startStr).append("=").append(endStr).append(StringUtils.SEPARATOR);
} }
} }
return sb.deleteCharAt(sb.length() - 1).toString(); return sb.deleteCharAt(sb.length() - 1).toString();

View File

@ -58,7 +58,7 @@ public class GenUtils {
column.setHtmlType(GenConstants.HTML_INPUT); column.setHtmlType(GenConstants.HTML_INPUT);
// 如果是浮点型 统一用BigDecimal // 如果是浮点型 统一用BigDecimal
String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), StringUtils.SEPARATOR);
if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) { if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) {
column.setJavaType(GenConstants.TYPE_BIGDECIMAL); column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
} }
@ -167,7 +167,7 @@ public class GenUtils {
boolean autoRemovePre = GenConfig.getAutoRemovePre(); boolean autoRemovePre = GenConfig.getAutoRemovePre();
String tablePrefix = GenConfig.getTablePrefix(); String tablePrefix = GenConfig.getTablePrefix();
if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) { if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) {
String[] searchList = StringUtils.split(tablePrefix, ","); String[] searchList = StringUtils.split(tablePrefix, StringUtils.SEPARATOR);
tableName = replaceFirst(tableName, searchList); tableName = replaceFirst(tableName, searchList);
} }
return StringUtils.convertToCamelCase(tableName); return StringUtils.convertToCamelCase(tableName);
@ -184,7 +184,7 @@ public class GenUtils {
String text = replacementm; String text = replacementm;
for (String searchString : searchList) { for (String searchString : searchList) {
if (replacementm.startsWith(searchString)) { if (replacementm.startsWith(searchString)) {
text = replacementm.replaceFirst(searchString, ""); text = replacementm.replaceFirst(searchString, StringUtils.EMPTY);
break; break;
} }
} }

View File

@ -232,7 +232,7 @@ public class VelocityUtils {
public static HashSet<String> getImportList(GenTable genTable) { public static HashSet<String> getImportList(GenTable genTable) {
List<GenTableColumn> columns = genTable.getColumns(); List<GenTableColumn> columns = genTable.getColumns();
GenTable subGenTable = genTable.getSubTable(); GenTable subGenTable = genTable.getSubTable();
HashSet<String> importList = new HashSet<String>(); HashSet<String> importList = new HashSet<>();
if (ObjectUtil.isNotNull(subGenTable)) { if (ObjectUtil.isNotNull(subGenTable)) {
importList.add("java.util.List"); importList.add("java.util.List");
} }

Some files were not shown because too many files have changed in this diff Show More