发布 v2.3.2

This commit is contained in:
疯狂的狮子li 2021-06-11 10:00:05 +08:00
parent 801f7cd8f7
commit 3e5b850e25
57 changed files with 459 additions and 1124 deletions

View File

@ -18,6 +18,9 @@
* 文档框架 knife4j 美化接口文档
* 代码生成器 一键生成前后端代码
## 参考文档
[参考文档 Wiki](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
## 修改RuoYi功能
### 依赖改动
@ -43,6 +46,8 @@
* 项目修改为 maven多环境配置
* 项目配置修改为 application.yml 统一管理
* 数据权限修改为 适配支持单表、多表
* 使用 redisson 实现 spring-cache 整合
* 增加 mybatis-plus 二级缓存 redis 存储
### 其他
@ -59,38 +64,6 @@
<img src="https://images.gitee.com/uploads/images/2021/0525/101654_451e4523_1766278.jpeg" width="300px" height="450px" />
<img src="https://images.gitee.com/uploads/images/2021/0525/101713_3d18b119_1766278.jpeg" width="300px" height="450px" />
## 重点注意事项
若依文档对事务注解的描述 [关于事务](https://doc.ruoyi.vip/ruoyi/document/htsc.html#%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86) 以下对多数据源事务做补充:
* 同一个事务下是无法切换数据源的
* 禁止 父方法使用 @Transactional 创建事务 子方法使用 @DataSource 切换数据源
* 正确用法: 子方法单独创建事务 或 父方法使用 @Transactional(propagation = Propagation.REQUIRES_NEW) 为所有子方法创建新事务
关于如何使用Tomcat
* 查看ruoyi-framework模块的pom.xml文件,根据注释更改依赖
* 查看ruoyi-admin模块中的application.yml文件,根据注释更改配置
关于如何创建新模块
* 参考ruoyi-demo模块
* 需要改动: 父pom 与 admin模块pom
关于树表生成
* 直接在mysql表中 添加 parentId orderNum 等字段(根据需要参考 TreeEntity类)
* 代码生成选择树表生成即可
关于数据权限
* 创建表 需预留 dept_id 字段 如需用户权限 还需预留 user_id 字段
* 支持 Mybatis-Plus 方式注入 参考 demo 模块用法(需导入 test.sql 文件)
* 支持 XML 方式注入 参考 system 模块用法
关于vue与boot整合部署
* [前端静态资源如何整合到后端访问](https://doc.ruoyi.vip/ruoyi-vue/other/faq.html#前端静态资源如何整合到后端访问)
关于修改包名
* 将文件夹全部修改为 com.xxx
* 使用IDEA全局替换 com.ruoyi 替换为 com.xxx
* 严禁手动修改
## 内置功能
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。

View File

@ -1,6 +1,6 @@
{
"name": "ruoyi-vue-plus",
"version": "2.3.1",
"version": "2.3.2",
"description": "RuoYi-Vue-Plus后台管理系统",
"author": "LionLi",
"license": "MIT",

View File

@ -1,53 +0,0 @@
import request from '@/utils/request'
// 查询测试单表列表
export function listDemo(query) {
return request({
url: '/demo/demo/list',
method: 'get',
params: query
})
}
// 查询测试单表详细
export function getDemo(id) {
return request({
url: '/demo/demo/' + id,
method: 'get'
})
}
// 新增测试单表
export function addDemo(data) {
return request({
url: '/demo/demo',
method: 'post',
data: data
})
}
// 修改测试单表
export function updateDemo(data) {
return request({
url: '/demo/demo',
method: 'put',
data: data
})
}
// 删除测试单表
export function delDemo(id) {
return request({
url: '/demo/demo/' + id,
method: 'delete'
})
}
// 导出测试单表
export function exportDemo(query) {
return request({
url: '/demo/demo/export',
method: 'get',
params: query
})
}

View File

@ -75,7 +75,7 @@ export default {
[{ color: [] }, { background: [] }], //
[{ align: [] }], //
["clean"], //
["link", "image"] //
["link", "image", "video"] //
],
},
placeholder: "请输入内容",

View File

@ -8,7 +8,7 @@
<div class="right-menu">
<template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" />
<el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
@ -104,7 +104,7 @@ export default {
this.$store.dispatch('LogOut').then(() => {
location.href = '/index';
})
})
}).catch(() => {});
}
}
}

View File

@ -63,7 +63,7 @@ service.interceptors.response.use(res => {
store.dispatch('LogOut').then(() => {
location.href = '/index';
})
})
}).catch(() => {});
} else if (code === 500) {
Message({
message: msg,

View File

@ -1,348 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="key键" prop="testKey">
<el-input
v-model="queryParams.testKey"
placeholder="请输入key键"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="值" prop="value">
<el-input
v-model="queryParams.value"
placeholder="请输入值"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
v-model="daterangeCreateTime"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['demo:demo:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['demo:demo:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['demo:demo:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
:loading="exportLoading"
@click="handleExport"
v-hasPermi="['demo:demo:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键" align="center" prop="id" v-if="false"/>
<el-table-column label="部门id" align="center" prop="deptId" />
<el-table-column label="用户id" align="center" prop="userId" />
<el-table-column label="排序号" align="center" prop="orderNum" />
<el-table-column label="key键" align="center" prop="testKey" />
<el-table-column label="值" align="center" prop="value" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="创建人" align="center" prop="createBy" />
<el-table-column label="更新时间" align="center" prop="updateTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="更新人" align="center" prop="updateBy" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['demo:demo:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['demo:demo:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改测试单表对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="部门id" prop="deptId">
<el-input v-model="form.deptId" placeholder="请输入部门id" />
</el-form-item>
<el-form-item label="用户id" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户id" />
</el-form-item>
<el-form-item label="排序号" prop="orderNum">
<el-input v-model="form.orderNum" placeholder="请输入排序号" />
</el-form-item>
<el-form-item label="key键" prop="testKey">
<el-input v-model="form.testKey" placeholder="请输入key键" />
</el-form-item>
<el-form-item label="值" prop="value">
<el-input v-model="form.value" placeholder="请输入值" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listDemo, getDemo, delDemo, addDemo, updateDemo, exportDemo } from "@/api/demo/demo";
export default {
name: "Demo",
components: {
},
data() {
return {
//loading
buttonLoading: false,
//
loading: true,
//
exportLoading: false,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
demoList: [],
//
title: "",
//
open: false,
//
daterangeCreateTime: [],
//
queryParams: {
pageNum: 1,
pageSize: 10,
testKey: undefined,
value: undefined,
createTime: undefined,
},
//
form: {},
//
rules: {
testKey: [
{ required: true, message: "key键不能为空", trigger: "blur" }
],
value: [
{ required: true, message: "值不能为空", trigger: "blur" }
],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询测试单表列表 */
getList() {
this.loading = true;
this.queryParams.params = {};
if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {
this.queryParams.params["beginCreateTime"] = this.daterangeCreateTime[0];
this.queryParams.params["endCreateTime"] = this.daterangeCreateTime[1];
}
listDemo(this.queryParams).then(response => {
this.demoList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: undefined,
deptId: undefined,
userId: undefined,
orderNum: undefined,
testKey: undefined,
value: undefined,
version: undefined,
createTime: undefined,
createBy: undefined,
updateTime: undefined,
updateBy: undefined,
delFlag: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.daterangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加测试单表";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.loading = true;
this.reset();
const id = row.id || this.ids
getDemo(id).then(response => {
this.loading = false;
this.form = response.data;
this.open = true;
this.title = "修改测试单表";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
if (this.form.id != null) {
updateDemo(this.form).then(response => {
this.buttonLoading = false;
this.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addDemo(this.form).then(response => {
this.buttonLoading = false;
this.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$confirm('是否确认删除测试单表编号为"' + ids + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.loading = true;
return delDemo(ids);
}).then(() => {
this.loading = false;
this.getList();
this.msgSuccess("删除成功");
})
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$confirm('是否确认导出所有测试单表数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.exportLoading = true;
return exportDemo(queryParams);
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}
}
};
</script>

View File

@ -80,6 +80,17 @@
<span>更新日志</span>
</div>
<el-collapse accordion>
<el-collapse-item title="v2.3.2 - 2021-6-11">
<ol>
<li>add redis锁工具类编写</li>
<li>update spring-cache 整合 redisson</li>
<li>update MybatisPlus整合Redis二级缓存</li>
<li>update swagger 升级为 3.0.0 使用 OAS_30 协议</li>
<li>update 优化 代码生成器 增加表单防重注解</li>
<li>update 优化 锁切面代码 key到常量类</li>
<li>fix 修复相对路径上传异常问题</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v2.3.1 - 2021-6-4">
<ol>
<li>add 增加 redisson 分布式锁 注解与demo案例</li>

View File

@ -410,7 +410,7 @@ export default {
return runJob(row.jobId, row.jobGroup);
}).then(() => {
this.msgSuccess("执行成功");
})
}).catch(() => {});
},
/** 任务详细信息 */
handleView(row) {
@ -471,7 +471,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
@ -486,7 +486,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
}
}
};

View File

@ -269,7 +269,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 清空按钮操作 */
handleClean() {
@ -282,7 +282,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("清空成功");
})
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
@ -297,7 +297,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
}
}
};

View File

@ -202,7 +202,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 清空按钮操作 */
handleClean() {
@ -215,7 +215,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("清空成功");
})
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
@ -230,7 +230,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
}
}
};

View File

@ -120,7 +120,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("强退成功");
})
}).catch(() => {});
}
}
};

View File

@ -290,7 +290,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 清空按钮操作 */
handleClean() {
@ -303,7 +303,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("清空成功");
})
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
@ -318,7 +318,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
}
}
};

View File

@ -338,7 +338,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
@ -353,7 +353,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
},
/** 刷新缓存按钮操作 */
handleRefreshCache() {

View File

@ -57,17 +57,17 @@
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:dept:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-plus"
<el-button
size="mini"
type="text"
icon="el-icon-plus"
@click="handleAdd(scope.row)"
v-hasPermi="['system:dept:add']"
>新增</el-button>
@ -310,8 +310,8 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
}
}
};
</script>
</script>

View File

@ -337,7 +337,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
@ -352,7 +352,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
}
}
};

View File

@ -342,7 +342,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
@ -357,7 +357,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
},
/** 刷新缓存按钮操作 */
handleRefreshCache() {

View File

@ -163,7 +163,7 @@
</el-col>
<el-col :span="12">
<el-form-item v-if="form.menuType != 'M'" label="权限标识">
<el-input v-model="form.perms" placeholder="请权限标识" maxlength="100" />
<el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
</el-form-item>
</el-col>
<el-col :span="12">
@ -393,7 +393,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
}
}
};

View File

@ -336,7 +336,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
}
}
};

View File

@ -309,7 +309,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
@ -324,7 +324,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
}
}
};

View File

@ -593,7 +593,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
@ -608,7 +608,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
}
}
};

View File

@ -631,7 +631,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
@ -646,7 +646,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
},
/** 导入按钮操作 */
handleImport() {

View File

@ -283,7 +283,7 @@ export default {
return synchDb(tableName);
}).then(() => {
this.msgSuccess("同步成功");
})
}).catch(() => {});
},
/** 打开导入表弹窗 */
openImportTable() {
@ -333,7 +333,7 @@ export default {
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
}
}
};

View File

@ -13,7 +13,7 @@
<description>RuoYi-Vue-Plus后台管理系统</description>
<properties>
<ruoyi-vue-plus.version>2.3.0</ruoyi-vue-plus.version>
<ruoyi-vue-plus.version>2.3.2</ruoyi-vue-plus.version>
<spring-boot.version>2.3.11.RELEASE</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
@ -21,7 +21,6 @@
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<druid.version>1.2.6</druid.version>
<knife4j.version>3.0.2</knife4j.version>
<swagger-annotations.version>1.5.22</swagger-annotations.version>
<fastjson.version>1.2.76</fastjson.version>
<poi.version>4.1.2</poi.version>
<velocity.version>1.7</velocity.version>
@ -120,18 +119,6 @@
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
<exclusions>
<exclusion>
<artifactId>swagger-annotations</artifactId>
<groupId>io.swagger</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>${swagger-annotations.version}</version>
</dependency>
<!-- Mysql驱动包 -->

View File

@ -2,7 +2,7 @@ package com.ruoyi.common.constant;
/**
* 通用常量信息
*
*
* @author ruoyi
*/
public class Constants
@ -126,4 +126,9 @@ public class Constants
* 资源映射路径 前缀
*/
public static final String RESOURCE_PREFIX = "/profile";
/**
* 资源映射路径 前缀
*/
public static final String REDIS_LOCK_KEY = "redis_lock:";
}

View File

@ -17,7 +17,7 @@ import java.util.Map;
/**
* 角色表 sys_role
*
*
* @author ruoyi
*/
@ -51,8 +51,8 @@ public class SysRole implements Serializable
@NotBlank(message = "显示顺序不能为空")
private String roleSort;
/** 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限 */
@Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限")
/** 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限5仅本人数据权限 */
@Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
private String dataScope;
/** 菜单树选择项是否关联显示( 0父子不互相关联显示 1父子互相关联显示 */

View File

@ -0,0 +1,102 @@
package com.ruoyi.common.core.mybatisplus;
import cn.hutool.extra.spring.SpringUtil;
import com.ruoyi.common.core.redis.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* mybatis-redis 二级缓存
*
* @author Lion Li
*/
@Slf4j
public class MybatisPlusRedisCache implements Cache {
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
private RedisCache redisCache;
private String id;
public MybatisPlusRedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
public void putObject(Object key, Object value) {
if (redisCache == null) {
redisCache = SpringUtil.getBean(RedisCache.class);
}
if (value != null) {
redisCache.setCacheObject(key.toString(), value);
}
}
@Override
public Object getObject(Object key) {
if (redisCache == null) {
//由于启动期间注入失败只能运行期间注入这段代码可以删除
redisCache = SpringUtil.getBean(RedisCache.class);
}
try {
if (key != null) {
return redisCache.getCacheObject(key.toString());
}
} catch (Exception e) {
e.printStackTrace();
log.error("缓存出错");
}
return null;
}
@Override
public Object removeObject(Object key) {
if (redisCache == null) {
redisCache = SpringUtil.getBean(RedisCache.class);
}
if (key != null) {
redisCache.deleteObject(key.toString());
}
return null;
}
@Override
public void clear() {
log.debug("清空缓存");
if (redisCache == null) {
redisCache = SpringUtil.getBean(RedisCache.class);
}
Collection<String> keys = redisCache.keys("*:" + this.id + "*");
if (!CollectionUtils.isEmpty(keys)) {
redisCache.deleteObject(keys);
}
}
@Override
public int getSize() {
RedisTemplate<String, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
Long size = redisTemplate.execute(RedisServerCommands::dbSize);
return size.intValue();
}
@Override
public ReadWriteLock getReadWriteLock() {
return this.readWriteLock;
}
}

View File

@ -10,6 +10,13 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 分页 Page 增强对象
*
* @param <T> 数据库实体
* @param <K> vo实体
* @author Lion Li
*/
@Data
@Accessors(chain = true)
public class PagePlus<T,K> implements IPage<T> {

View File

@ -0,0 +1,170 @@
package com.ruoyi.common.core.redis;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* redis 锁管理类
*
* @author shenxinquan
*/
@Component
public class RedisLockManager {
@Autowired
private RedissonClient redissonClient;
/**
* 通用锁
*/
private final static Integer BASE_LOCK = 1;
/**
* 公平锁
*/
private final static Integer FAIR_LOCK = 2;
/**
* 存放当前线程获取锁的类型
*/
private final ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
/**
* 获取锁
*/
private RLock getLock(String key, Integer lockType) {
Assert.isTrue(StrUtil.isNotBlank(key), "key不能为空");
threadLocal.set(lockType);
RLock lock;
if (BASE_LOCK.equals(lockType)) {
lock = redissonClient.getLock(key);
} else if (FAIR_LOCK.equals(lockType)) {
lock = redissonClient.getFairLock(key);
} else {
throw new RuntimeException("锁不存在!");
}
return lock;
}
/**
* 获取锁不用设置超时时间一直等待
*/
public boolean getLock(String key) {
RLock lock = getLock(key, BASE_LOCK);
return lock.tryLock();
}
/**
* 设置过期时间
*
* @param key
* @param time 过期时间
* @param expireUnit 时间单位
*/
public boolean getLock(String key, long time, TimeUnit expireUnit) {
Assert.isTrue(time > 0, "过期时间必须大于0");
Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空");
RLock lock = getLock(key, BASE_LOCK);
try {
return lock.tryLock(time, expireUnit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 设置过期时间
*
* @param key
* @param waitTime 获取锁等待时间
* @param leaseTime 保留锁的时间
* @param expireUnit 时间单位
*/
public boolean getLock(String key, long waitTime, long leaseTime, TimeUnit expireUnit) {
Assert.isTrue(waitTime > 0, "获取锁等待时间必须大于0");
Assert.isTrue(leaseTime > 0, "保留锁的时间必须大于0");
Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空");
RLock lock = getLock(key, BASE_LOCK);
try {
return lock.tryLock(waitTime, leaseTime, expireUnit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 获取计数器锁
*
* @param key
* @param count countDownLatch 的数量
*/
public RCountDownLatch getCountDownLatch(String key, long count) {
Assert.isTrue(count >= 0, "count数量必须大于等于0");
RCountDownLatch rCountDownLatch = redissonClient.getCountDownLatch(key);
rCountDownLatch.trySetCount(count);
return rCountDownLatch;
}
/**
* 获取公平锁
*
* @param key
* @param waitTime 获取锁等待时间
* @param leaseTime 持有锁的时间
* @param expireUnit 时间单位
* @return
* @throws InterruptedException
*/
public boolean getFairLock(String key, long waitTime, long leaseTime, TimeUnit expireUnit) {
Assert.isTrue(waitTime > 0, "获取锁等待时间必须大于0");
Assert.isTrue(leaseTime > 0, "保留锁的时间必须大于0");
Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空");
RLock lock = getLock(key, FAIR_LOCK);
try {
return lock.tryLock(waitTime, leaseTime, expireUnit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 获取公平锁
*
* @param key
* @param leaseTime 持有锁的时间
* @param expireUnit 时间单位
*/
public boolean getFairLock(String key, long leaseTime, TimeUnit expireUnit) {
Assert.isTrue(leaseTime > 0, "保留锁的时间必须大于0");
Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空");
RLock lock = getLock(key, FAIR_LOCK);
try {
return lock.tryLock(leaseTime, expireUnit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 释放锁(统一释放)
*/
public void unLock(String key) {
Integer lockType = threadLocal.get();
RLock lock = getLock(key, lockType);
lock.unlock();
threadLocal.remove();
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.utils.file;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
@ -113,7 +114,8 @@ public class FileUploadUtils
String fileName = extractFilename(file);
File desc = getAbsoluteFile(baseDir, fileName);
file.transferTo(desc);
desc = FileUtil.touch(desc);
FileUtil.writeFromStream(file.getInputStream(), desc);
String pathFileName = getPathFileName(baseDir, fileName);
return pathFileName;
}

View File

@ -1,50 +0,0 @@
package com.ruoyi.demo.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;
/**
* 测试添加对象 chkj_test
*
* @author Lion Li
* @date 2021-05-14
*/
@Data
@ApiModel("测试添加对象")
public class ChkjTestAddBo {
/** key键 */
@ApiModelProperty("key键")
@NotBlank(message = "key键不能为空")
private String testKey;
/** 值 */
@ApiModelProperty("")
@NotBlank(message = "值不能为空")
private String value;
/** 版本 */
@ApiModelProperty("版本")
private Long version;
/** 创建时间 */
@ApiModelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 删除标志 */
@ApiModelProperty("删除标志")
private Long deleted;
/** 父id */
@ApiModelProperty("父id")
@NotNull(message = "父id不能为空")
private Long parentId;
/** 排序号 */
@ApiModelProperty("排序号")
@NotNull(message = "排序号不能为空")
private Long orderNum;
}

View File

@ -1,61 +0,0 @@
package com.ruoyi.demo.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;
/**
* 测试编辑对象 chkj_test
*
* @author Lion Li
* @date 2021-05-14
*/
@Data
@ApiModel("测试编辑对象")
public class ChkjTestEditBo {
/** 主键 */
@ApiModelProperty("主键")
@NotNull(message = "主键不能为空")
private Long id;
/** key键 */
@ApiModelProperty("key键")
@NotBlank(message = "key键不能为空")
private String testKey;
/** 值 */
@ApiModelProperty("")
@NotBlank(message = "值不能为空")
private String value;
/** 版本 */
@ApiModelProperty("版本")
private Long version;
/** 创建时间 */
@ApiModelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 删除标志 */
@ApiModelProperty("删除标志")
private Long deleted;
/** 父id */
@ApiModelProperty("父id")
@NotNull(message = "父id不能为空")
private Long parentId;
/** 排序号 */
@ApiModelProperty("排序号")
@NotNull(message = "排序号不能为空")
private Long orderNum;
}

View File

@ -1,53 +0,0 @@
package com.ruoyi.demo.bo;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 测试分页查询对象 chkj_test
*
* @author Lion Li
* @date 2021-05-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("测试分页查询对象")
public class ChkjTestQueryBo extends BaseEntity {
/** 分页大小 */
@ApiModelProperty("分页大小")
private Integer pageSize;
/** 当前页数 */
@ApiModelProperty("当前页数")
private Integer pageNum;
/** 排序列 */
@ApiModelProperty("排序列")
private String orderByColumn;
/** 排序的方向desc或者asc */
@ApiModelProperty(value = "排序的方向", example = "asc,desc")
private String isAsc;
/** key键 */
@ApiModelProperty("key键")
private String testKey;
/** 值 */
@ApiModelProperty("")
private String value;
/** 版本 */
@ApiModelProperty("版本")
private Long version;
/** 删除标志 */
@ApiModelProperty("删除标志")
private Long deleted;
/** 父id */
@ApiModelProperty("父id")
private Long parentId;
/** 排序号 */
@ApiModelProperty("排序号")
private Long orderNum;
}

View File

@ -1,108 +0,0 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.demo.bo.ChkjTestAddBo;
import com.ruoyi.demo.bo.ChkjTestEditBo;
import com.ruoyi.demo.bo.ChkjTestQueryBo;
import com.ruoyi.demo.service.IChkjTestService;
import com.ruoyi.demo.vo.ChkjTestVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 测试Controller
*
* @author Lion Li
* @date 2021-05-14
*/
@Api(value = "测试控制器", tags = {"测试管理"})
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@RestController
@RequestMapping("/demo/test")
public class ChkjTestController extends BaseController {
private final IChkjTestService iChkjTestService;
/**
* 查询测试列表
*/
@ApiOperation("查询测试列表")
@PreAuthorize("@ss.hasPermi('demo:test:list')")
@GetMapping("/list")
public TableDataInfo<ChkjTestVo> list(@Validated ChkjTestQueryBo bo) {
return iChkjTestService.queryPageList(bo);
}
/**
* 导出测试列表
*/
@ApiOperation("导出测试列表")
@PreAuthorize("@ss.hasPermi('demo:test:export')")
@Log(title = "测试", businessType = BusinessType.EXPORT)
@GetMapping("/export")
public AjaxResult<ChkjTestVo> export(@Validated ChkjTestQueryBo bo) {
List<ChkjTestVo> list = iChkjTestService.queryList(bo);
ExcelUtil<ChkjTestVo> util = new ExcelUtil<ChkjTestVo>(ChkjTestVo.class);
return util.exportExcel(list, "测试");
}
/**
* 获取测试详细信息
*/
@ApiOperation("获取测试详细信息")
@PreAuthorize("@ss.hasPermi('demo:test:query')")
@GetMapping("/{id}")
public AjaxResult<ChkjTestVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable("id") Long id) {
return AjaxResult.success(iChkjTestService.queryById(id));
}
/**
* 新增测试
*/
@ApiOperation("新增测试")
@PreAuthorize("@ss.hasPermi('demo:test:add')")
@Log(title = "测试", businessType = BusinessType.INSERT)
@PostMapping()
public AjaxResult<Void> add(@Validated @RequestBody ChkjTestAddBo bo) {
return toAjax(iChkjTestService.insertByAddBo(bo) ? 1 : 0);
}
/**
* 修改测试
*/
@ApiOperation("修改测试")
@PreAuthorize("@ss.hasPermi('demo:test:edit')")
@Log(title = "测试", businessType = BusinessType.UPDATE)
@PutMapping()
public AjaxResult<Void> edit(@Validated @RequestBody ChkjTestEditBo bo) {
return toAjax(iChkjTestService.updateByEditBo(bo) ? 1 : 0);
}
/**
* 删除测试
*/
@ApiOperation("删除测试")
@PreAuthorize("@ss.hasPermi('demo:test:remove')")
@Log(title = "测试" , businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(iChkjTestService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0);
}
}

View File

@ -2,26 +2,36 @@ package com.ruoyi.demo.controller;
import com.ruoyi.common.annotation.RedisLock;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisLockManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* 测试分布式锁的样例
*
* @author shenxinquan
*/
@Slf4j
@RestController
@RequestMapping("/demo/redisLock")
public class RedisLockController {
@Autowired
private RedisLockManager redisLockManager;
/**
* #p0 标识取第一个参数为redis锁的key
*/
@GetMapping("/getLock")
@GetMapping("/testLock1")
@RedisLock(expireTime = 10, key = "#p0")
public AjaxResult<String> getLock(String key, String value) {
public AjaxResult<String> testLock1(String key, String value) {
try {
// 同时请求排队
// Thread.sleep(5000);
@ -32,4 +42,34 @@ public class RedisLockController {
}
return AjaxResult.success("操作成功",value);
}
/**
* 测试锁工具类
*/
@GetMapping("/testLock2")
public AjaxResult<Void> testLock(String key, Long time) {
try {
boolean flag = redisLockManager.getLock(key, time, TimeUnit.SECONDS);
if (flag) {
log.info("获取锁成功: " + key);
Thread.sleep(3000);
redisLockManager.unLock(key);
log.info("释放锁成功: " + key);
} else {
log.error("获取锁失败: " + key);
}
} catch (InterruptedException e) {
log.error(e.getMessage());
}
return AjaxResult.success();
}
/**
* 测试spring-cache注解
*/
@Cacheable(value = "test", key = "#key")
@GetMapping("/testCache")
public AjaxResult<String> testCache(String key) {
return AjaxResult.success("操作成功", key);
}
}

View File

@ -1,55 +0,0 @@
package com.ruoyi.demo.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 测试对象 chkj_test
*
* @author Lion Li
* @date 2021-05-14
*/
@Data
@NoArgsConstructor
@Accessors(chain = true)
@TableName("chkj_test")
public class ChkjTest implements Serializable {
private static final long serialVersionUID=1L;
/** 主键 */
@TableId(value = "id")
private Long id;
/** key键 */
private String testKey;
/** 值 */
private String value;
/** 版本 */
private Long version;
/** 创建时间 */
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/** 删除标志 */
private Long deleted;
/** 父id */
private Long parentId;
/** 排序号 */
private Long orderNum;
}

View File

@ -1,14 +0,0 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.demo.domain.ChkjTest;
import com.ruoyi.common.core.page.BaseMapperPlus;
/**
* 测试Mapper接口
*
* @author Lion Li
* @date 2021-05-14
*/
public interface ChkjTestMapper extends BaseMapperPlus<ChkjTest> {
}

View File

@ -1,7 +1,9 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.demo.domain.TestDemo;
import org.apache.ibatis.annotations.CacheNamespace;
/**
* 测试单表Mapper接口
@ -9,6 +11,7 @@ import com.ruoyi.common.core.page.BaseMapperPlus;
* @author Lion Li
* @date 2021-05-30
*/
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface TestDemoMapper extends BaseMapperPlus<TestDemo> {
}

View File

@ -1,7 +1,9 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.demo.domain.TestTree;
import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.demo.domain.TestTree;
import org.apache.ibatis.annotations.CacheNamespace;
/**
* 测试树表Mapper接口
@ -9,6 +11,7 @@ import com.ruoyi.common.core.page.BaseMapperPlus;
* @author Lion Li
* @date 2021-05-30
*/
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface TestTreeMapper extends BaseMapperPlus<TestTree> {
}

View File

@ -1,57 +0,0 @@
package com.ruoyi.demo.service;
import com.ruoyi.demo.domain.ChkjTest;
import com.ruoyi.demo.vo.ChkjTestVo;
import com.ruoyi.demo.bo.ChkjTestQueryBo;
import com.ruoyi.demo.bo.ChkjTestAddBo;
import com.ruoyi.demo.bo.ChkjTestEditBo;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.Collection;
import java.util.List;
/**
* 测试Service接口
*
* @author Lion Li
* @date 2021-05-14
*/
public interface IChkjTestService extends IServicePlus<ChkjTest> {
/**
* 查询单个
* @return
*/
ChkjTestVo queryById(Long id);
/**
* 查询列表
*/
TableDataInfo<ChkjTestVo> queryPageList(ChkjTestQueryBo bo);
/**
* 查询列表
*/
List<ChkjTestVo> queryList(ChkjTestQueryBo bo);
/**
* 根据新增业务对象插入测试
* @param bo 测试新增业务对象
* @return
*/
Boolean insertByAddBo(ChkjTestAddBo bo);
/**
* 根据编辑业务对象修改测试
* @param bo 测试编辑业务对象
* @return
*/
Boolean updateByEditBo(ChkjTestEditBo bo);
/**
* 校验并删除数据
* @param ids 主键集合
* @param isValid 是否校验,true-删除前校验,false-不校验
* @return
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@ -1,93 +0,0 @@
package com.ruoyi.demo.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.page.PagePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.demo.bo.ChkjTestAddBo;
import com.ruoyi.demo.bo.ChkjTestEditBo;
import com.ruoyi.demo.bo.ChkjTestQueryBo;
import com.ruoyi.demo.domain.ChkjTest;
import com.ruoyi.demo.mapper.ChkjTestMapper;
import com.ruoyi.demo.service.IChkjTestService;
import com.ruoyi.demo.vo.ChkjTestVo;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 测试Service业务层处理
*
* @author Lion Li
* @date 2021-05-14
*/
@Service
public class ChkjTestServiceImpl extends ServiceImpl<ChkjTestMapper, ChkjTest> implements IChkjTestService {
@Override
public ChkjTestVo queryById(Long id){
return getVoById(id, obj -> BeanUtil.toBean(obj, ChkjTestVo.class));
}
@Override
public TableDataInfo<ChkjTestVo> queryPageList(ChkjTestQueryBo bo) {
PagePlus<ChkjTest, ChkjTestVo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo), ChkjTestVo.class);
return PageUtils.buildDataInfo(result);
}
@Override
public List<ChkjTestVo> queryList(ChkjTestQueryBo bo) {
return listVo(buildQueryWrapper(bo), ChkjTestVo.class);
}
private LambdaQueryWrapper<ChkjTest> buildQueryWrapper(ChkjTestQueryBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<ChkjTest> lqw = Wrappers.lambdaQuery();
lqw.like(StrUtil.isNotBlank(bo.getTestKey()), ChkjTest::getTestKey, bo.getTestKey());
lqw.eq(StrUtil.isNotBlank(bo.getValue()), ChkjTest::getValue, bo.getValue());
lqw.eq(bo.getVersion() != null, ChkjTest::getVersion, bo.getVersion());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
ChkjTest::getCreateTime ,params.get("beginCreateTime"), params.get("endCreateTime"));
lqw.eq(bo.getDeleted() != null, ChkjTest::getDeleted, bo.getDeleted());
lqw.eq(bo.getParentId() != null, ChkjTest::getParentId, bo.getParentId());
lqw.eq(bo.getOrderNum() != null, ChkjTest::getOrderNum, bo.getOrderNum());
return lqw;
}
@Override
public Boolean insertByAddBo(ChkjTestAddBo bo) {
ChkjTest add = BeanUtil.toBean(bo, ChkjTest.class);
validEntityBeforeSave(add);
return save(add);
}
@Override
public Boolean updateByEditBo(ChkjTestEditBo bo) {
ChkjTest update = BeanUtil.toBean(bo, ChkjTest.class);
validEntityBeforeSave(update);
return updateById(update);
}
/**
* 保存前的数据校验
*
* @param entity 实体类数据
*/
private void validEntityBeforeSave(ChkjTest entity){
//TODO 做一些数据校验,如唯一约束
}
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return removeByIds(ids);
}
}

View File

@ -1,57 +0,0 @@
package com.ruoyi.demo.vo;
import com.ruoyi.common.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* 测试视图对象 mall_package
*
* @author Lion Li
* @date 2021-05-14
*/
@Data
@ApiModel("测试视图对象")
public class ChkjTestVo {
private static final long serialVersionUID = 1L;
/** 主键 */
@ApiModelProperty("主键")
private Long id;
/** key键 */
@Excel(name = "key键")
@ApiModelProperty("key键")
private String testKey;
/** 值 */
@Excel(name = "")
@ApiModelProperty("")
private String value;
/** 版本 */
@Excel(name = "版本")
@ApiModelProperty("版本")
private Long version;
/** 创建时间 */
@Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("创建时间")
private Date createTime;
/** 删除标志 */
@Excel(name = "删除标志")
@ApiModelProperty("删除标志")
private Long deleted;
/** 父id */
@Excel(name = "父id")
@ApiModelProperty("父id")
private Long parentId;
/** 排序号 */
@Excel(name = "排序号")
@ApiModelProperty("排序号")
private Long orderNum;
}

View File

@ -2,6 +2,8 @@ package com.ruoyi.framework.aspectj;
import com.ruoyi.common.annotation.RedisLock;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisLockManager;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
@ -32,9 +34,7 @@ import java.util.concurrent.TimeUnit;
public class RedisLockAspect {
@Autowired
private RedissonClient redissonClient;
private static final String LOCK_TITLE = "RedisLock_";
private RedisLockManager redisLockManager;
@Pointcut("@annotation(com.ruoyi.common.annotation.RedisLock)")
public void annotationPointcut() {
@ -67,16 +67,20 @@ public class RedisLockAspect {
throw new RuntimeException("redis分布式锁注解参数异常", e);
}
// 声明锁名称
key = Constants.REDIS_LOCK_KEY + key;
Object res;
try {
if (acquire(key, expireTime, TimeUnit.SECONDS)) {
if (redisLockManager.getLock(key, expireTime, TimeUnit.SECONDS)) {
log.info("lock => key : " + key + " , ThreadName : " + Thread.currentThread().getName());
try {
res = joinPoint.proceed();
return res;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
release(key);
redisLockManager.unLock(key);
log.info("unlock => key : " + key + " , ThreadName : " + Thread.currentThread().getName());
}
} else {
throw new RuntimeException("redis分布式锁注解参数异常");
@ -132,36 +136,4 @@ public class RedisLockAspect {
return listPar;
}
/**
* 加锁RLock带超时时间的
*/
private boolean acquire(String key, long expire, TimeUnit expireUnit) {
//声明key对象
key = LOCK_TITLE + key;
try {
//获取锁对象
RLock mylock = redissonClient.getLock(key);
//加锁并且设置锁过期时间防止死锁的产生
mylock.tryLock(expire, expire, expireUnit);
} catch (InterruptedException e) {
return false;
}
log.info("lock => key : " + key + " , ThreadName : " + Thread.currentThread().getName());
//加锁成功
return true;
}
/**
* 锁的释放
*/
private void release(String lockName) {
//必须是和加锁时的同一个key
String key = LOCK_TITLE + lockName;
//获取所对象
RLock mylock = redissonClient.getLock(key);
//释放锁解锁
mylock.unlock();
log.info("unlock => key : " + key + " , ThreadName : " + Thread.currentThread().getName());
}
}

View File

@ -11,6 +11,11 @@ import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* 异步配置
*
* @author Lion Li
*/
@EnableAsync
@Configuration
public class AsyncConfig extends AsyncConfigurerSupport {

View File

@ -6,15 +6,20 @@ import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonSpringCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* redis配置
@ -67,4 +72,15 @@ public class RedisConfig extends CachingConfigurerSupport {
.setDnsMonitoringInterval(singleServerConfig.getDnsMonitoringInterval());
return Redisson.create(config);
}
/**
* 整合spring-cache
*/
@Bean
public CacheManager cacheManager(RedissonClient redissonClient) {
Map<String, CacheConfig> config = new HashMap<>();
config.put("redissonCacheMap", new CacheConfig(30*60*1000, 10*60*1000));
return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE);
}
}

View File

@ -15,7 +15,7 @@ import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
/**
* 通用配置
*
*
* @author ruoyi
*/
@Configuration
@ -31,8 +31,7 @@ public class ResourcesConfig implements WebMvcConfigurer
registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");
/** swagger配置 */
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
}
/**
@ -63,4 +62,4 @@ public class ResourcesConfig implements WebMvcConfigurer
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
}

View File

@ -3,6 +3,7 @@ package com.ruoyi.framework.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.ruoyi.framework.config.properties.SwaggerProperties;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -13,18 +14,16 @@ import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* Swagger2的接口配置
* Swagger 文档配置
*
* @author Lion Li
*/
@Configuration
@EnableSwagger2
@EnableKnife4j
public class SwaggerConfig {
@ -36,7 +35,7 @@ public class SwaggerConfig {
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
return new Docket(DocumentationType.OAS_30)
.enable(swaggerProperties.getEnabled())
// 用来创建该API的基本信息展示在文档的页面中自定义展示的信息
.apiInfo(apiInfo())
@ -60,7 +59,7 @@ public class SwaggerConfig {
*/
private List<SecurityScheme> securitySchemes() {
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
return apiKeyList;
}
@ -72,7 +71,7 @@ public class SwaggerConfig {
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("^(?!auth).*$"))
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
.build());
return securityContexts;
}

View File

@ -6,7 +6,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 验证码 配置属性
* swagger 配置属性
*
* @author Lion Li
*/

View File

@ -5,7 +5,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 验证码 配置属性
* 线程池 配置属性
*
* @author Lion Li
*/

View File

@ -4,6 +4,11 @@ import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* token 配置属性
*
* @author Lion Li
*/
@Data
@Component
@ConfigurationProperties(prefix = "token")

View File

@ -5,7 +5,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 验证码 配置属性
* xss过滤 配置属性
*
* @author Lion Li
*/

View File

@ -288,8 +288,6 @@ spring:
url: http://localhost:${server.port}${spring.boot.admin.context-path}
instance:
prefer-ip: true # 注册实例时,优先使用 IP
# username: ruoyi
# password: 123456
# Spring Boot Admin Server 服务端的相关配置
context-path: /admin # 配置 Spring

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.demo.mapper.ChkjTestMapper">
<resultMap type="com.ruoyi.demo.domain.ChkjTest" id="ChkjTestResult">
<result property="id" column="id"/>
<result property="testKey" column="test_key"/>
<result property="value" column="value"/>
<result property="version" column="version"/>
<result property="createTime" column="create_time"/>
<result property="deleted" column="deleted"/>
<result property="parentId" column="parent_id"/>
<result property="orderNum" column="order_num"/>
</resultMap>
</mapper>

View File

@ -9,6 +9,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
@ -28,7 +29,7 @@ import io.swagger.annotations.ApiOperation;
/**
* ${functionName}Controller
*
*
* @author ${author}
* @date ${datetime}
*/
@ -87,6 +88,7 @@ public class ${ClassName}Controller extends BaseController {
@ApiOperation("新增${functionName}")
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")
@Log(title = "${functionName}", businessType = BusinessType.INSERT)
@RepeatSubmit
@PostMapping()
public AjaxResult<Void> add(@Validated @RequestBody ${ClassName}AddBo bo) {
return toAjax(i${ClassName}Service.insertByAddBo(bo) ? 1 : 0);
@ -98,6 +100,7 @@ public class ${ClassName}Controller extends BaseController {
@ApiOperation("修改${functionName}")
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")
@Log(title = "${functionName}", businessType = BusinessType.UPDATE)
@RepeatSubmit
@PutMapping()
public AjaxResult<Void> edit(@Validated @RequestBody ${ClassName}EditBo bo) {
return toAjax(i${ClassName}Service.updateByEditBo(bo) ? 1 : 0);

View File

@ -2,6 +2,8 @@ package ${packageName}.mapper;
import ${packageName}.domain.${ClassName};
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache;
import org.apache.ibatis.annotations.CacheNamespace;
/**
* ${functionName}Mapper接口
@ -9,6 +11,7 @@ import com.ruoyi.common.core.page.BaseMapperPlus;
* @author ${author}
* @date ${datetime}
*/
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}> {
}

View File

@ -541,7 +541,7 @@ export default {
this.loading = false;
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
}
}
};

View File

@ -599,7 +599,7 @@ export default {
this.loading = false;
this.getList();
this.msgSuccess("删除成功");
})
}).catch(() => {});
},
#if($table.sub)
/** ${subTable.functionName}序号 */
@ -648,7 +648,7 @@ export default {
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
}).catch(() => {});
}
}
};