Commit a0f7f0ff121661116ed5b8de14e112e261254d0f

Authored by 芋道源码
Committed by Gitee
2 parents 6d30d566 c0b1f62a

!211 Redis 监控添加缓存列表

Merge pull request !211 from 圆梦巨人/feature/cache-list
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java
... ... @@ -4,21 +4,25 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
5 5 import cn.iocoder.yudao.framework.redis.core.RedisKeyRegistry;
6 6 import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyRespVO;
  7 +import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyValueRespVO;
7 8 import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;
8 9 import cn.iocoder.yudao.module.infra.convert.redis.RedisConvert;
9 10 import io.swagger.annotations.Api;
10 11 import io.swagger.annotations.ApiOperation;
11 12 import org.springframework.data.redis.connection.RedisServerCommands;
  13 +import org.springframework.data.redis.core.Cursor;
12 14 import org.springframework.data.redis.core.RedisCallback;
  15 +import org.springframework.data.redis.core.ScanOptions;
13 16 import org.springframework.data.redis.core.StringRedisTemplate;
14 17 import org.springframework.security.access.prepost.PreAuthorize;
15   -import org.springframework.web.bind.annotation.GetMapping;
16   -import org.springframework.web.bind.annotation.RequestMapping;
17   -import org.springframework.web.bind.annotation.RestController;
  18 +import org.springframework.web.bind.annotation.*;
18 19  
19 20 import javax.annotation.Resource;
  21 +import java.nio.charset.StandardCharsets;
  22 +import java.util.HashSet;
20 23 import java.util.List;
21 24 import java.util.Properties;
  25 +import java.util.Set;
22 26  
23 27 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
24 28  
... ... @@ -45,11 +49,68 @@ public class RedisController {
45 49 }
46 50  
47 51 @GetMapping("/get-key-list")
48   - @ApiOperation("获得 Redis Key 列表")
  52 + @ApiOperation("获得 Redis Key 模板列表")
49 53 @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
50 54 public CommonResult<List<RedisKeyRespVO>> getKeyList() {
51 55 List<RedisKeyDefine> keyDefines = RedisKeyRegistry.list();
52 56 return success(RedisConvert.INSTANCE.convertList(keyDefines));
53 57 }
54 58  
  59 + @GetMapping("/get-key-Defines")
  60 + @ApiOperation("获得 Redis keys 键名列表")
  61 + @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
  62 + public CommonResult<Set<String>> getKeyDefines(@RequestParam("keyDefine") String keyDefine) {
  63 + Set<String> keys = new HashSet<>();
  64 + stringRedisTemplate.execute((RedisCallback<Set<String>>) connection -> {
  65 + try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions()
  66 + .match(keyDefine + "*")
  67 + .count(Integer.MAX_VALUE).build())) {
  68 + while (cursor.hasNext()) {
  69 + keys.add(new String(cursor.next(), StandardCharsets.UTF_8));
  70 + }
  71 + } catch (Exception e) {
  72 + throw new RuntimeException(e);
  73 + }
  74 + return keys;
  75 + });
  76 + return success(keys);
  77 + }
  78 +
  79 + @DeleteMapping("/delete-key-defines")
  80 + @ApiOperation("删除 Redis Key 根据模板")
  81 + @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
  82 + public CommonResult<Boolean> deleteKeyDefines(@RequestParam("keyDefine") String keyDefine) {
  83 + Set<String> keys = stringRedisTemplate.keys(keyDefine + "*");
  84 + if(keys != null && keys.isEmpty()){
  85 + stringRedisTemplate.delete(keys);
  86 + }
  87 + return success(Boolean.TRUE);
  88 + }
  89 +
  90 + @GetMapping("/get-key-value")
  91 + @ApiOperation("获得 Redis key 内容")
  92 + @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
  93 + public CommonResult<RedisKeyValueRespVO> getKeyValue(@RequestParam("keyDefine") String keyDefine, @RequestParam("cacheKey") String cacheKey) {
  94 + String cacheValue = stringRedisTemplate.opsForValue().get(cacheKey);
  95 + return success(RedisKeyValueRespVO.of(keyDefine, cacheKey, cacheValue));
  96 + }
  97 +
  98 + @DeleteMapping("/delete-key-value")
  99 + @ApiOperation("删除 Redis Key 根据key")
  100 + @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
  101 + public CommonResult<Boolean> deleteKeyValue(@RequestParam("cacheKey") String cacheKey) {
  102 + stringRedisTemplate.delete(cacheKey);
  103 + return success(Boolean.TRUE);
  104 + }
  105 +
  106 + @DeleteMapping("/delete-cache-all")
  107 + @ApiOperation(value="删除 所有缓存", notes="不使用该接口")
  108 + @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
  109 + public CommonResult<Boolean> deleteCacheAll() {
  110 + return success(stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
  111 + connection.flushAll();
  112 + return Boolean.TRUE;
  113 + }));
  114 + }
  115 +
55 116 }
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisKeyValueRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.redis.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Builder;
  7 +import lombok.Data;
  8 +import org.apache.commons.lang3.StringUtils;
  9 +
  10 +@ApiModel("管理后台 - Redis Key Value onse VO")
  11 +@Data
  12 +@Builder
  13 +@AllArgsConstructor
  14 +public class RedisKeyValueRespVO {
  15 +
  16 + @ApiModelProperty(value = "oauth2_access_token:%s", required = true, example = "String")
  17 + private String keyTemplate;
  18 +
  19 + @ApiModelProperty(value = "c5f6990767804a928f4bb96ca249febf", required = true, example = "String")
  20 + private String key;
  21 +
  22 + @ApiModelProperty(required = true, example = "String")
  23 + private String value;
  24 +
  25 + public static RedisKeyValueRespVO of(String keyTemplate, String key, String value){
  26 + return RedisKeyValueRespVO.builder()
  27 + .keyTemplate(StringUtils.replace(keyTemplate, ":", ""))
  28 + .key(StringUtils.replace(key, keyTemplate, ""))
  29 + .value(value)
  30 + .build();
  31 + }
  32 +
  33 +
  34 +}
... ...
yudao-ui-admin/src/api/infra/redis.js
... ... @@ -8,10 +8,35 @@ export function getCache() {
8 8 })
9 9 }
10 10  
11   -// TODO
  11 +// 获取模块
12 12 export function getKeyList() {
13 13 return request({
14 14 url: '/infra/redis/get-key-list',
15 15 method: 'get'
16 16 })
17 17 }
  18 +
  19 +// 获取键名列表
  20 +export function getKeyDefines(keyDefine) {
  21 + return request({
  22 + url: '/infra/redis/get-key-Defines?keyDefine=' + keyDefine,
  23 + method: 'get'
  24 + })
  25 +}
  26 +
  27 +// 获取缓存内容
  28 +export function getKeyValue(keyDefine, key) {
  29 + return request({
  30 + url: '/infra/redis/get-key-value?keyDefine=' + keyDefine + "&cacheKey=" + key,
  31 + method: 'get'
  32 + })
  33 +}
  34 +
  35 +// 根据键名删除缓存
  36 +export function deleteKeyValue(key) {
  37 + return request({
  38 + url: '/infra/redis/delete-key-value?cacheKey=' + key,
  39 + method: 'delete'
  40 + })
  41 +}
  42 +
... ...
yudao-ui-admin/src/views/infra/redis/index.vue
... ... @@ -67,32 +67,133 @@
67 67 </el-row>
68 68  
69 69 <el-table
70   - v-loading="keyListLoad"
71   - :data="keyList"
72   - row-key="id"
  70 + v-loading="keyListLoad"
  71 + :data="keyList"
  72 + row-key="id"
  73 + @row-click="openCacheInfo"
73 74 >
74 75 <el-table-column prop="keyTemplate" label="Key 模板" width="200" />
75 76 <el-table-column prop="keyType" label="Key 类型" width="100" />
76 77 <el-table-column prop="valueType" label="Value 类型" />
77 78 <el-table-column prop="timeoutType" label="超时时间" width="200">
78 79 <template slot-scope="scope">
79   - <dict-tag :type="DICT_TYPE.INFRA_REDIS_TIMEOUT_TYPE" :value="scope.row.timeoutType" />
80   - <span v-if="scope.row.timeout > 0">({{ scope.row.timeout / 1000 }} 秒)</span>
  80 + <dict-tag
  81 + :type="DICT_TYPE.INFRA_REDIS_TIMEOUT_TYPE"
  82 + :value="scope.row.timeoutType"
  83 + />
  84 + <span v-if="scope.row.timeout > 0"
  85 + >({{ scope.row.timeout / 1000 }} 秒)</span
  86 + >
81 87 </template>
82 88 </el-table-column>
83 89 <el-table-column prop="memo" label="备注" />
84 90 </el-table>
85   - </div>
86 91  
  92 + <!-- 缓存模块信息框 -->
  93 + <el-dialog
  94 + :title="keyTemplate + '模块'"
  95 + :visible.sync="open"
  96 + width="60vw"
  97 + append-to-body
  98 + >
  99 + <el-row :gutter="10">
  100 + <el-col :span="10" class="card-box">
  101 + <el-card style="height: 70vh">
  102 + <div slot="header">
  103 + <span>键名列表</span>
  104 + <el-button
  105 + style="float: right; padding: 3px 0"
  106 + type="text"
  107 + icon="el-icon-refresh-right"
  108 + @click="refreshCacheKeys"
  109 + ></el-button>
  110 + </div>
  111 + <el-table
  112 + :data="cachekeys"
  113 + style="width: 100%"
  114 + @row-click="handleCacheValue"
  115 + >
  116 + <el-table-column
  117 + label="序号"
  118 + width="60"
  119 + type="index"
  120 + ></el-table-column>
  121 + <el-table-column
  122 + label="缓存键名"
  123 + align="center"
  124 + :show-overflow-tooltip="true"
  125 + :formatter="keyFormatter"
  126 + >
  127 + </el-table-column>
  128 + <el-table-column
  129 + label="操作"
  130 + width="60"
  131 + align="center"
  132 + class-name="small-padding fixed-width"
  133 + >
  134 + <template slot-scope="scope">
  135 + <el-button
  136 + size="mini"
  137 + type="text"
  138 + icon="el-icon-delete"
  139 + @click="handleClearCacheKey(scope.row)"
  140 + ></el-button>
  141 + </template>
  142 + </el-table-column>
  143 + </el-table>
  144 + </el-card>
  145 + </el-col>
  146 +
  147 + <el-col :span="14">
  148 + <el-card :bordered="false" style="height: 70vh">
  149 + <div slot="header">
  150 + <span>缓存内容</span>
  151 + <!-- <el-button
  152 + style="float: right; padding: 3px 0"
  153 + type="text"
  154 + icon="el-icon-refresh-right"
  155 + @click="handleClearCacheAll()"
  156 + >清理全部</el-button>
  157 + -->
  158 + </div>
  159 + <el-form :model="cacheForm">
  160 + <el-row :gutter="32">
  161 + <el-col :offset="1" :span="22">
  162 + <el-form-item label="缓存名称:" prop="keyTemplate">
  163 + <el-input v-model="cacheForm.keyTemplate" :readOnly="true" />
  164 + </el-form-item>
  165 + </el-col>
  166 + <el-col :offset="1" :span="22">
  167 + <el-form-item label="缓存键名:" prop="key">
  168 + <el-input v-model="cacheForm.key" :readOnly="true" />
  169 + </el-form-item>
  170 + </el-col>
  171 + <el-col :offset="1" :span="22">
  172 + <el-form-item label="缓存内容:" prop="value">
  173 + <el-input
  174 + v-model="cacheForm.value"
  175 + type="textarea"
  176 + :rows="12"
  177 + :readOnly="true"
  178 + />
  179 + </el-form-item>
  180 + </el-col>
  181 + </el-row>
  182 + </el-form>
  183 + </el-card>
  184 + </el-col>
  185 + </el-row>
  186 + </el-dialog>
  187 + </div>
87 188 </template>
88 189  
89 190 <script>
90   -import { getCache, getKeyList } from "@/api/infra/redis";
  191 +import { getCache, getKeyList, getKeyDefines, getKeyValue, deleteKeyValue } from "@/api/infra/redis";
91 192 import echarts from "echarts";
92 193  
93 194 export default {
94 195 name: "Server",
95   - data() {
  196 + data () {
96 197 return {
97 198 // 统计命令信息
98 199 commandstats: null,
... ... @@ -103,15 +204,20 @@ export default {
103 204 // key 列表
104 205 keyListLoad: true,
105 206 keyList: [],
  207 + // 模块弹出框
  208 + open: false,
  209 + keyTemplate: "",
  210 + cachekeys: [],
  211 + cacheForm: {}
106 212 };
107 213 },
108   - created() {
  214 + created () {
109 215 this.getList();
110 216 this.openLoading();
111 217 },
112 218 methods: {
113 219 /** 查缓存询信息 */
114   - getList() {
  220 + getList () {
115 221 // 查询 Redis 监控信息
116 222 getCache().then((response) => {
117 223 this.cache = response.data;
... ... @@ -174,10 +280,55 @@ export default {
174 280 this.keyListLoad = false;
175 281 });
176 282 },
  283 +
177 284 // 打开加载层
178   - openLoading() {
  285 + openLoading () {
179 286 this.$modal.loading("正在加载缓存监控数据,请稍后!");
180 287 },
  288 +
  289 + // 打开缓存弹窗
  290 + openCacheInfo (e) {
  291 + this.open = true;
  292 + let keyDefine = e.keyTemplate.substring(0, e.keyTemplate.length - 2);
  293 + this.keyTemplate = keyDefine;
  294 + // 加载键名列表
  295 + this.handleCacheKeys(keyDefine);
  296 + },
  297 +
  298 + /** 键名前缀去除 */
  299 + keyFormatter (cacheKey) {
  300 + return cacheKey.replace(this.keyTemplate, "");
  301 + },
  302 +
  303 + // 获取键名列表
  304 + handleCacheKeys (keyDefine){
  305 + const cacheName = keyDefine !== undefined ? keyDefine : this.keyTemplate;
  306 + getKeyDefines(cacheName).then(response => {
  307 + this.cachekeys = response.data
  308 + this.cacheForm = {}
  309 + })
  310 + },
  311 +
  312 + // 获取缓存值
  313 + handleCacheValue (e){
  314 + getKeyValue(this.keyTemplate, e).then(response => {
  315 + this.cacheForm = response.data
  316 + })
  317 + },
  318 +
  319 + // 刷新键名列表
  320 + refreshCacheKeys(){
  321 + this.$modal.msgSuccess("刷新键名列表成功");
  322 + this.handleCacheKeys();
  323 + },
  324 +
  325 + // 删除缓存
  326 + handleClearCacheKey(key){
  327 + deleteKeyValue(key).then(response =>{
  328 + this.$modal.msgSuccess("清理缓存键名[" + key + "]成功");
  329 + this.handleCacheKeys();
  330 + })
  331 + },
181 332 },
182 333 };
183 334 </script>
... ...