Browse Source

!211 Redis 监控添加缓存列表
Merge pull request !211 from 圆梦巨人/feature/cache-list

芋道源码 2 years ago
parent
commit
a0f7f0ff12

+ 65 - 4
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;
 import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
 import cn.iocoder.yudao.framework.redis.core.RedisKeyRegistry;
 import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyRespVO;
+import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyValueRespVO;
 import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;
 import cn.iocoder.yudao.module.infra.convert.redis.RedisConvert;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.data.redis.connection.RedisServerCommands;
+import org.springframework.data.redis.core.Cursor;
 import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.ScanOptions;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Properties;
+import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
@@ -45,11 +49,68 @@ public class RedisController {
     }
 
     @GetMapping("/get-key-list")
-    @ApiOperation("获得 Redis Key 列表")
+    @ApiOperation("获得 Redis Key 模板列表")
     @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
     public CommonResult<List<RedisKeyRespVO>> getKeyList() {
         List<RedisKeyDefine> keyDefines = RedisKeyRegistry.list();
         return success(RedisConvert.INSTANCE.convertList(keyDefines));
     }
 
+    @GetMapping("/get-key-Defines")
+    @ApiOperation("获得 Redis keys 键名列表")
+    @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
+    public CommonResult<Set<String>> getKeyDefines(@RequestParam("keyDefine") String keyDefine) {
+        Set<String> keys = new HashSet<>();
+         stringRedisTemplate.execute((RedisCallback<Set<String>>) connection -> {
+            try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions()
+                    .match(keyDefine + "*")
+                    .count(Integer.MAX_VALUE).build())) {
+                while (cursor.hasNext()) {
+                    keys.add(new String(cursor.next(), StandardCharsets.UTF_8));
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+            return keys;
+        });
+        return success(keys);
+    }
+
+    @DeleteMapping("/delete-key-defines")
+    @ApiOperation("删除 Redis Key 根据模板")
+    @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
+    public CommonResult<Boolean> deleteKeyDefines(@RequestParam("keyDefine") String keyDefine) {
+        Set<String> keys = stringRedisTemplate.keys(keyDefine + "*");
+        if(keys != null && keys.isEmpty()){
+             stringRedisTemplate.delete(keys);
+        }
+        return success(Boolean.TRUE);
+    }
+
+    @GetMapping("/get-key-value")
+    @ApiOperation("获得 Redis key 内容")
+    @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
+    public CommonResult<RedisKeyValueRespVO> getKeyValue(@RequestParam("keyDefine") String keyDefine, @RequestParam("cacheKey") String cacheKey) {
+        String cacheValue = stringRedisTemplate.opsForValue().get(cacheKey);
+        return success(RedisKeyValueRespVO.of(keyDefine, cacheKey, cacheValue));
+    }
+
+    @DeleteMapping("/delete-key-value")
+    @ApiOperation("删除 Redis Key 根据key")
+    @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
+    public CommonResult<Boolean> deleteKeyValue(@RequestParam("cacheKey") String cacheKey) {
+        stringRedisTemplate.delete(cacheKey);
+        return success(Boolean.TRUE);
+    }
+
+    @DeleteMapping("/delete-cache-all")
+    @ApiOperation(value="删除 所有缓存", notes="不使用该接口")
+    @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
+    public CommonResult<Boolean> deleteCacheAll() {
+        return success(stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
+            connection.flushAll();
+            return Boolean.TRUE;
+        }));
+    }
+
 }

+ 34 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisKeyValueRespVO.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.controller.admin.redis.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+@ApiModel("管理后台 - Redis Key Value onse VO")
+@Data
+@Builder
+@AllArgsConstructor
+public class RedisKeyValueRespVO {
+
+    @ApiModelProperty(value = "oauth2_access_token:%s", required = true, example = "String")
+    private String keyTemplate;
+
+    @ApiModelProperty(value = "c5f6990767804a928f4bb96ca249febf", required = true, example = "String")
+    private String key;
+
+    @ApiModelProperty(required = true, example = "String")
+    private String value;
+
+    public static RedisKeyValueRespVO of(String keyTemplate, String key, String value){
+        return RedisKeyValueRespVO.builder()
+                .keyTemplate(StringUtils.replace(keyTemplate, ":", ""))
+                .key(StringUtils.replace(key, keyTemplate, ""))
+                .value(value)
+                .build();
+    }
+
+
+}

+ 26 - 1
yudao-ui-admin/src/api/infra/redis.js

@@ -8,10 +8,35 @@ export function getCache() {
   })
 }
 
-// TODO
+// 获取模块
 export function getKeyList() {
   return request({
     url: '/infra/redis/get-key-list',
     method: 'get'
   })
 }
+
+// 获取键名列表
+export function getKeyDefines(keyDefine) {
+  return request({
+    url: '/infra/redis/get-key-Defines?keyDefine=' + keyDefine,
+    method: 'get'
+  })
+}
+
+// 获取缓存内容
+export function getKeyValue(keyDefine, key) {
+  return request({
+    url: '/infra/redis/get-key-value?keyDefine=' + keyDefine + "&cacheKey=" + key,
+    method: 'get'
+  })
+}
+
+// 根据键名删除缓存
+export function deleteKeyValue(key) {
+  return request({
+    url: '/infra/redis/delete-key-value?cacheKey=' + key,
+    method: 'delete'
+  })
+}
+

+ 162 - 11
yudao-ui-admin/src/views/infra/redis/index.vue

@@ -67,32 +67,133 @@
     </el-row>
 
     <el-table
-        v-loading="keyListLoad"
-        :data="keyList"
-        row-key="id"
+      v-loading="keyListLoad"
+      :data="keyList"
+      row-key="id"
+      @row-click="openCacheInfo"
     >
       <el-table-column prop="keyTemplate" label="Key 模板" width="200" />
       <el-table-column prop="keyType" label="Key 类型" width="100" />
       <el-table-column prop="valueType" label="Value 类型" />
       <el-table-column prop="timeoutType" label="超时时间" width="200">
         <template slot-scope="scope">
-          <dict-tag :type="DICT_TYPE.INFRA_REDIS_TIMEOUT_TYPE" :value="scope.row.timeoutType" />
-          <span v-if="scope.row.timeout > 0">({{ scope.row.timeout / 1000 }} 秒)</span>
+          <dict-tag
+            :type="DICT_TYPE.INFRA_REDIS_TIMEOUT_TYPE"
+            :value="scope.row.timeoutType"
+          />
+          <span v-if="scope.row.timeout > 0"
+            >({{ scope.row.timeout / 1000 }} 秒)</span
+          >
         </template>
       </el-table-column>
       <el-table-column prop="memo" label="备注" />
     </el-table>
-  </div>
 
+    <!-- 缓存模块信息框 -->
+    <el-dialog
+      :title="keyTemplate + '模块'"
+      :visible.sync="open"
+      width="60vw"
+      append-to-body
+    >
+      <el-row :gutter="10">
+        <el-col :span="10" class="card-box">
+          <el-card style="height: 70vh">
+            <div slot="header">
+              <span>键名列表</span>
+              <el-button
+                style="float: right; padding: 3px 0"
+                type="text"
+                icon="el-icon-refresh-right"
+                @click="refreshCacheKeys"
+              ></el-button>
+            </div>
+            <el-table
+                :data="cachekeys"
+                style="width: 100%"
+                @row-click="handleCacheValue"
+            >
+              <el-table-column
+                label="序号"
+                width="60"
+                type="index"
+              ></el-table-column>
+              <el-table-column
+                label="缓存键名"
+                align="center"
+                :show-overflow-tooltip="true"
+                :formatter="keyFormatter"
+              >
+              </el-table-column>
+              <el-table-column
+                label="操作"
+                width="60"
+                align="center"
+                class-name="small-padding fixed-width"
+              >
+                <template slot-scope="scope">
+                  <el-button
+                    size="mini"
+                    type="text"
+                    icon="el-icon-delete"
+                    @click="handleClearCacheKey(scope.row)"
+                  ></el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-card>
+        </el-col>
+
+        <el-col :span="14">
+          <el-card :bordered="false" style="height: 70vh">
+            <div slot="header">
+              <span>缓存内容</span>
+              <!-- <el-button
+                style="float: right; padding: 3px 0"
+                type="text"
+                icon="el-icon-refresh-right"
+                @click="handleClearCacheAll()"
+                >清理全部</el-button>
+                -->
+            </div>
+          <el-form :model="cacheForm">
+            <el-row :gutter="32">
+              <el-col :offset="1" :span="22">
+                <el-form-item label="缓存名称:" prop="keyTemplate">
+                  <el-input v-model="cacheForm.keyTemplate" :readOnly="true" />
+                </el-form-item>
+              </el-col>
+              <el-col :offset="1" :span="22">
+                <el-form-item label="缓存键名:" prop="key">
+                  <el-input v-model="cacheForm.key" :readOnly="true" />
+                </el-form-item>
+                </el-col>
+                <el-col :offset="1" :span="22">
+                  <el-form-item label="缓存内容:" prop="value">
+                    <el-input
+                      v-model="cacheForm.value"
+                      type="textarea"
+                      :rows="12"
+                      :readOnly="true"
+                    />
+                  </el-form-item>
+                </el-col>
+              </el-row>
+            </el-form>
+          </el-card>
+        </el-col>
+      </el-row>
+    </el-dialog>
+  </div>
 </template>
 
 <script>
-import { getCache, getKeyList } from "@/api/infra/redis";
+import { getCache, getKeyList, getKeyDefines, getKeyValue, deleteKeyValue } from "@/api/infra/redis";
 import echarts from "echarts";
 
 export default {
   name: "Server",
-  data() {
+  data () {
     return {
       // 统计命令信息
       commandstats: null,
@@ -103,15 +204,20 @@ export default {
       // key 列表
       keyListLoad: true,
       keyList: [],
+      // 模块弹出框
+      open: false,
+      keyTemplate: "",
+      cachekeys: [],
+      cacheForm: {}
     };
   },
-  created() {
+  created () {
     this.getList();
     this.openLoading();
   },
   methods: {
     /** 查缓存询信息 */
-    getList() {
+    getList () {
       // 查询 Redis 监控信息
       getCache().then((response) => {
         this.cache = response.data;
@@ -174,10 +280,55 @@ export default {
         this.keyListLoad = false;
       });
     },
+
     // 打开加载层
-    openLoading() {
+    openLoading () {
       this.$modal.loading("正在加载缓存监控数据,请稍后!");
     },
+
+    // 打开缓存弹窗
+    openCacheInfo (e) {
+      this.open = true;
+      let keyDefine = e.keyTemplate.substring(0, e.keyTemplate.length - 2);
+      this.keyTemplate = keyDefine;
+      // 加载键名列表
+      this.handleCacheKeys(keyDefine);
+    },
+
+    /** 键名前缀去除 */
+    keyFormatter (cacheKey) {
+      return cacheKey.replace(this.keyTemplate, "");
+    },
+
+    // 获取键名列表
+    handleCacheKeys (keyDefine){
+      const cacheName = keyDefine !== undefined ? keyDefine : this.keyTemplate;
+      getKeyDefines(cacheName).then(response => {
+        this.cachekeys = response.data
+        this.cacheForm = {}
+      })
+    },
+
+    // 获取缓存值
+    handleCacheValue (e){
+      getKeyValue(this.keyTemplate, e).then(response => {
+        this.cacheForm = response.data
+      })
+    },
+
+    // 刷新键名列表
+    refreshCacheKeys(){
+      this.$modal.msgSuccess("刷新键名列表成功");
+      this.handleCacheKeys();
+    },
+
+    // 删除缓存
+    handleClearCacheKey(key){
+      deleteKeyValue(key).then(response =>{
+        this.$modal.msgSuccess("清理缓存键名[" + key + "]成功");
+        this.handleCacheKeys();
+      })
+    },
   },
 };
 </script>