瀏覽代碼

feat(server): 优化脚本日志和 SSH 命令日志支持批量删除

- 新增脚本日志批量删除功能
- 新增 SSH 命令日志批量删除功能
bwcx_jzy 4 月之前
父節點
當前提交
2e8a9e8c31

+ 1 - 0
CHANGELOG-BETA.md

@@ -10,6 +10,7 @@
 
 1. 【server】优化 数据库表支持配置前缀 `jpom.db.table-prefix` (感谢@ccx2480)
 2. 【server】修复 终端输入命令,按Backspace 会退出终端(感谢[@dgs](https://gitee.com/dgs0924) [Gitee issues ICA57K](https://gitee.com/dromara/Jpom/issues/ICA57K) )
+3. 【server】优化 脚本日志和 SSH 命令日志支持批量删除(感谢[@lin_yeqi](https://gitee.com/lin_yeqi) [Gitee issues IBIM6W](https://gitee.com/dromara/Jpom/issues/IBIM6W) )
 
 ------
 

+ 34 - 9
modules/server/src/main/java/org/dromara/jpom/controller/script/ScriptLogController.java

@@ -37,6 +37,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletRequest;
 import java.io.File;
+import java.util.List;
 
 /**
  * @author bwcx_jzy
@@ -51,8 +52,7 @@ public class ScriptLogController extends BaseServerController {
     private final ScriptExecuteLogServer scriptExecuteLogServer;
     private final ScriptServer scriptServer;
 
-    public ScriptLogController(ScriptExecuteLogServer scriptExecuteLogServer,
-                               ScriptServer scriptServer) {
+    public ScriptLogController(ScriptExecuteLogServer scriptExecuteLogServer, ScriptServer scriptServer) {
         this.scriptExecuteLogServer = scriptExecuteLogServer;
         this.scriptServer = scriptServer;
     }
@@ -78,9 +78,7 @@ public class ScriptLogController extends BaseServerController {
      */
     @RequestMapping(value = "del_log", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
     @Feature(method = MethodFeature.DEL)
-    public IJsonMessage<Object> delLog(@ValidatorItem() String id,
-                                       @ValidatorItem() String executeId,
-                                       HttpServletRequest request) {
+    public IJsonMessage<Object> delLog(@ValidatorItem() String id, @ValidatorItem() String executeId, HttpServletRequest request) {
         ScriptModel item = null;
         try {
             item = scriptServer.getByKeyAndGlobal(id, request, "ignore");
@@ -96,6 +94,36 @@ public class ScriptLogController extends BaseServerController {
         return JsonMessage.success(I18nMessageUtil.get("i18n.delete_success.0007"));
     }
 
+    /**
+     * 批量删除日志
+     *
+     * @param ids id+
+     * @return json
+     */
+    @RequestMapping(value = "batch_del_log", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    @Feature(method = MethodFeature.DEL)
+    public IJsonMessage<Object> delLog(@ValidatorItem() String ids, HttpServletRequest request) {
+        List<String> list = StrUtil.splitTrim(ids, StrUtil.COMMA);
+        ScriptModel item = null;
+        for (String itemId : list) {
+            String[] list1 = StrUtil.splitToArray(itemId, StrUtil.COLON);
+            String id = list1[1];
+            String executeId = list1[0];
+            try {
+                item = scriptServer.getByKeyAndGlobal(id, request, "ignore");
+            } catch (IllegalArgumentException | IllegalStateException e) {
+                if (!StrUtil.equals("ignore", e.getMessage())) {
+                    throw e;
+                }
+            }
+            File logFile = item == null ? ScriptModel.logFile(id, executeId) : item.logFile(executeId);
+            boolean fastDel = CommandUtil.systemFastDel(logFile);
+            Assert.state(!fastDel, I18nMessageUtil.get("i18n.delete_log_file_failure.bf0b"));
+            scriptExecuteLogServer.delByKey(executeId);
+        }
+        return JsonMessage.success(I18nMessageUtil.get("i18n.delete_success.0007"));
+    }
+
     /**
      * 获取的日志
      *
@@ -106,10 +134,7 @@ public class ScriptLogController extends BaseServerController {
      */
     @RequestMapping(value = "log", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
     @Feature(method = MethodFeature.LIST)
-    public IJsonMessage<JSONObject> getNowLog(@ValidatorItem() String id,
-                                              @ValidatorItem() String executeId,
-                                              @ValidatorItem(value = ValidatorRule.POSITIVE_INTEGER, msg = "i18n.line_number_error.c65d") int line,
-                                              HttpServletRequest request) {
+    public IJsonMessage<JSONObject> getNowLog(@ValidatorItem() String id, @ValidatorItem() String executeId, @ValidatorItem(value = ValidatorRule.POSITIVE_INTEGER, msg = "i18n.line_number_error.c65d") int line, HttpServletRequest request) {
         ScriptModel item = scriptServer.getByKey(id, request);
         Assert.notNull(item, I18nMessageUtil.get("i18n.no_data_found.4ffb"));
         File logFile = item.logFile(executeId);

+ 12 - 9
modules/server/src/main/java/org/dromara/jpom/controller/ssh/CommandLogController.java

@@ -10,6 +10,7 @@
 package org.dromara.jpom.controller.ssh;
 
 import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.servlet.ServletUtil;
 import cn.keepbx.jpom.IJsonMessage;
 import cn.keepbx.jpom.model.JsonMessage;
@@ -77,13 +78,16 @@ public class CommandLogController extends BaseServerController {
     @RequestMapping(value = "del", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
     @Feature(method = MethodFeature.DEL)
     public IJsonMessage<String> del(String id, HttpServletRequest request) {
-        CommandExecLogModel execLogModel = commandExecLogService.getByKey(id, request);
-        Assert.notNull(execLogModel, I18nMessageUtil.get("i18n.no_record.ff41"));
-        File logFile = execLogModel.logFile();
-        boolean fastDel = CommandUtil.systemFastDel(logFile);
-        Assert.state(!fastDel, I18nMessageUtil.get("i18n.log_file_cleanup_failed.3a3b"));
-        //
-        commandExecLogService.delByKey(id);
+        List<String> list = StrUtil.splitTrim(id, StrUtil.COMMA);
+        for (String id1 : list) {
+            CommandExecLogModel execLogModel = commandExecLogService.getByKey(id1, request);
+            Assert.notNull(execLogModel, I18nMessageUtil.get("i18n.no_record.ff41"));
+            File logFile = execLogModel.logFile();
+            boolean fastDel = CommandUtil.systemFastDel(logFile);
+            Assert.state(!fastDel, I18nMessageUtil.get("i18n.log_file_cleanup_failed.3a3b"));
+            //
+            commandExecLogService.delByKey(id1);
+        }
         return JsonMessage.success(I18nMessageUtil.get("i18n.operation_succeeded.3313"));
     }
 
@@ -137,8 +141,7 @@ public class CommandLogController extends BaseServerController {
      */
     @RequestMapping(value = "log", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
     @Feature(method = MethodFeature.LIST)
-    public IJsonMessage<JSONObject> log(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "i18n.no_data.1ac0") String id,
-                                        @ValidatorItem(value = ValidatorRule.POSITIVE_INTEGER, msg = "i18n.line_number_error.c65d") int line, HttpServletRequest request) {
+    public IJsonMessage<JSONObject> log(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "i18n.no_data.1ac0") String id, @ValidatorItem(value = ValidatorRule.POSITIVE_INTEGER, msg = "i18n.line_number_error.c65d") int line, HttpServletRequest request) {
         CommandExecLogModel item = commandExecLogService.getByKey(id, request);
         Assert.notNull(item, I18nMessageUtil.get("i18n.no_data_found.4ffb"));
 

+ 9 - 0
web-vue/src/api/server-script.ts

@@ -81,6 +81,15 @@ export function scriptDel(params) {
   })
 }
 
+// 批量删除执行记录
+export function scriptBatchDel(params) {
+  return axios({
+    url: '/script_log/batch_del_log',
+    method: 'post',
+    data: params
+  })
+}
+
 //执行记录 详情
 export function scriptLog(params) {
   return axios({

+ 68 - 6
web-vue/src/pages/script/script-log.vue

@@ -1,17 +1,28 @@
 <template>
   <div class="">
     <!-- 数据表格 -->
-    <a-table
+    <CustomTable
+      is-show-tools
+      default-auto-refresh
+      :auto-refresh-time="30"
+      :active-page="activePage"
+      table-name="server-script-log-list"
       :data-source="list"
       size="middle"
       :columns="columns"
       :pagination="pagination"
       bordered
-      row-key="id"
+      :row-key="
+        (record) => {
+          return record.id + ':' + record.scriptId
+        }
+      "
       :scroll="{
         x: 'max-content'
       }"
+      :row-selection="rowSelection"
       @change="changePage"
+      @refresh="loadData"
     >
       <template #title>
         <a-space wrap class="search-box">
@@ -80,9 +91,17 @@
           <a-tooltip :title="$t('i18n_4838a3bd20')">
             <a-button type="primary" :loading="loading" @click="loadData">{{ $t('i18n_e5f71fc31e') }}</a-button>
           </a-tooltip>
+          <a-button
+            type="primary"
+            danger
+            :disabled="!tableSelections || tableSelections.length <= 0"
+            @click="handleBatchDelete"
+          >
+            {{ $t('i18n_7fb62b3011') }}
+          </a-button>
         </a-space>
       </template>
-      <template #bodyCell="{ column, text, record }">
+      <template #tableBodyCell="{ column, text, record }">
         <template v-if="column.dataIndex === 'scriptName'">
           <a-tooltip placement="topLeft" :title="text">
             <span>{{ text }}</span>
@@ -122,7 +141,7 @@
           </a-space>
         </template>
       </template>
-    </a-table>
+    </CustomTable>
     <!-- 日志 -->
 
     <script-log-view
@@ -138,7 +157,7 @@
   </div>
 </template>
 <script>
-import { getScriptLogList, scriptDel, triggerExecTypeMap } from '@/api/server-script'
+import { getScriptLogList, scriptDel, triggerExecTypeMap, scriptBatchDel } from '@/api/server-script'
 import ScriptLogView from '@/pages/script/script-log-view'
 import { statusMap } from '@/api/command'
 import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime } from '@/utils/const'
@@ -215,12 +234,25 @@ export default {
 
           width: '150px'
         }
-      ]
+      ],
+      tableSelections: []
     }
   },
   computed: {
     pagination() {
       return COMPUTED_PAGINATION(this.listQuery)
+    },
+    activePage() {
+      return this.$attrs.routerUrl === this.$route.path
+    },
+    rowSelection() {
+      return {
+        onChange: (selectedRowKeys) => {
+          this.tableSelections = selectedRowKeys
+        },
+        selectedRowKeys: this.tableSelections,
+        type: 'checkbox'
+      }
     }
   },
   mounted() {
@@ -271,6 +303,36 @@ export default {
     changePage(pagination, filters, sorter) {
       this.listQuery = CHANGE_PAGE(this.listQuery, { pagination, sorter })
       this.loadData()
+    },
+    // 批量删除
+    handleBatchDelete() {
+      if (!this.tableSelections || this.tableSelections.length <= 0) {
+        $notification.warning({
+          message: this.$t('i18n_5d817c403e')
+        })
+        return
+      }
+      $confirm({
+        title: this.$t('i18n_c4535759ee'),
+        zIndex: 1009,
+        content: '真的要删除这些脚本日志吗?',
+        okText: this.$t('i18n_e83a256e4f'),
+        cancelText: this.$t('i18n_625fb26b4b'),
+        onOk: () => {
+          // 删除
+          return scriptBatchDel({
+            ids: this.tableSelections.join(',')
+          }).then((res) => {
+            if (res.code === 200) {
+              $notification.success({
+                message: res.msg
+              })
+              this.tableSelections = []
+              this.loadData()
+            }
+          })
+        }
+      })
     }
   }
 }

+ 61 - 4
web-vue/src/pages/ssh/command-log.vue

@@ -1,6 +1,11 @@
 <template>
   <div>
-    <a-table
+    <CustomTable
+      is-show-tools
+      default-auto-refresh
+      :auto-refresh-time="30"
+      :active-page="activePage"
+      table-name="ssh-command-log-list"
       size="middle"
       :data-source="commandList"
       :columns="columns"
@@ -9,7 +14,10 @@
       :scroll="{
         x: 'max-content'
       }"
+      row-key="id"
+      :row-selection="rowSelection"
       @change="changePage"
+      @refresh="getCommandLogData"
     >
       <template #title>
         <a-space wrap class="search-box">
@@ -68,9 +76,17 @@
               $t('i18n_e5f71fc31e')
             }}</a-button>
           </a-tooltip>
+          <a-button
+            type="primary"
+            danger
+            :disabled="!tableSelections || tableSelections.length <= 0"
+            @click="handleBatchDelete"
+          >
+            {{ $t('i18n_7fb62b3011') }}
+          </a-button>
         </a-space>
       </template>
-      <template #bodyCell="{ column, text, record }">
+      <template #tableBodyCell="{ column, text, record }">
         <template v-if="column.dataIndex === 'sshName'">
           <a-tooltip placement="topLeft" :title="text">
             <span>{{ text }}</span>
@@ -106,7 +122,7 @@
           </a-space>
         </template>
       </template>
-    </a-table>
+    </CustomTable>
     <!-- 构建日志 -->
     <CustomModal
       v-if="logVisible"
@@ -204,7 +220,8 @@ export default {
           fixed: 'right',
           width: '200px'
         }
-      ]
+      ],
+      tableSelections: []
     }
   },
   computed: {
@@ -214,6 +231,18 @@ export default {
     },
     style() {
       return this.getFullscreenViewLogStyle()
+    },
+    activePage() {
+      return this.$attrs.routerUrl === this.$route.path
+    },
+    rowSelection() {
+      return {
+        onChange: (selectedRowKeys) => {
+          this.tableSelections = selectedRowKeys
+        },
+        selectedRowKeys: this.tableSelections,
+        type: 'checkbox'
+      }
     }
   },
   created() {},
@@ -263,6 +292,34 @@ export default {
         }
       })
     },
+    // 批量删除
+    handleBatchDelete() {
+      if (!this.tableSelections || this.tableSelections.length <= 0) {
+        $notification.warning({
+          message: this.$t('i18n_5d817c403e')
+        })
+        return
+      }
+      $confirm({
+        title: this.$t('i18n_c4535759ee'),
+        zIndex: 1009,
+        content: '真的要删除这些执行记录吗?',
+        okText: this.$t('i18n_e83a256e4f'),
+        cancelText: this.$t('i18n_625fb26b4b'),
+        onOk: () => {
+          // 删除
+          return deleteCommandLog(this.tableSelections.join(',')).then((res) => {
+            if (res.code === 200) {
+              $notification.success({
+                message: res.msg
+              })
+              this.tableSelections = []
+              this.getCommandLogData()
+            }
+          })
+        }
+      })
+    },
     // 下载构建日志
     handleDownload(record) {
       window.open(downloadLog(record.id), '_blank')