This is an automated email from the ASF dual-hosted git repository.

vernedeng pushed a commit to branch branch-1.10
in repository https://gitbox.apache.org/repos/asf/inlong.git

commit ef16f73ca19de48c51dd3f6f92b0208a9caa8327
Author: Lizhen <88174078+bluew...@users.noreply.github.com>
AuthorDate: Fri Dec 8 16:37:51 2023 +0800

    [INLONG-9439][Dashboard] Support module audit function (#9446)
    
    (cherry picked from commit 18329baa8dd0f0756e75a792a8585775d9508156)
---
 inlong-dashboard/src/configs/menus/conf.tsx        |  12 ++
 inlong-dashboard/src/configs/routes/conf.ts        |   4 +
 inlong-dashboard/src/i18n.ts                       |   4 +
 inlong-dashboard/src/ui/locales/cn.json            |   5 +-
 inlong-dashboard/src/ui/locales/en.json            |   5 +-
 .../src/ui/pages/ModuleAuditDashboard/config.tsx   | 179 +++++++++++++++++++++
 .../src/ui/pages/ModuleAuditDashboard/index.tsx    | 118 ++++++++++++++
 7 files changed, 325 insertions(+), 2 deletions(-)

diff --git a/inlong-dashboard/src/configs/menus/conf.tsx 
b/inlong-dashboard/src/configs/menus/conf.tsx
index 00b1bb441f..29cde12f70 100644
--- a/inlong-dashboard/src/configs/menus/conf.tsx
+++ b/inlong-dashboard/src/configs/menus/conf.tsx
@@ -27,6 +27,7 @@ import {
   SafetyOutlined,
   ShopOutlined,
   InteractionOutlined,
+  ProfileOutlined,
 } from '@ant-design/icons';
 import type { MenuItemType } from '.';
 
@@ -91,6 +92,17 @@ const conf: MenuItemType[] = [
       },
     ],
   },
+  {
+    name: i18n.t('configs.menus.SystemOperation'),
+    icon: <ProfileOutlined />,
+    isAdmin: true,
+    children: [
+      {
+        path: '/system',
+        name: i18n.t('configs.menus.ModuleAudit'),
+      },
+    ],
+  },
 ];
 
 export default conf;
diff --git a/inlong-dashboard/src/configs/routes/conf.ts 
b/inlong-dashboard/src/configs/routes/conf.ts
index 667f0c3f91..cc73f36988 100644
--- a/inlong-dashboard/src/configs/routes/conf.ts
+++ b/inlong-dashboard/src/configs/routes/conf.ts
@@ -115,6 +115,10 @@ const conf: RouteProps[] = [
     path: '/tenant',
     component: () => import('@/ui/pages/TenantManagement'),
   },
+  {
+    path: '/system',
+    component: () => import('@/ui/pages/ModuleAuditDashboard'),
+  },
   {
     component: () => import('@/ui/pages/Error/404'),
   },
diff --git a/inlong-dashboard/src/i18n.ts b/inlong-dashboard/src/i18n.ts
index 36457c022d..6a0d7f6de4 100644
--- a/inlong-dashboard/src/i18n.ts
+++ b/inlong-dashboard/src/i18n.ts
@@ -36,6 +36,8 @@ const resources = {
       'configs.menus.Nodes': 'DataNodes',
       'configs.menus.DataSynchronize': 'Synchronization',
       'configs.menus.TenantManagement': 'Tenant Management',
+      'configs.menus.SystemOperation': 'Operation',
+      'configs.menus.ModuleAudit': 'Module audit',
     },
   },
   cn: {
@@ -51,6 +53,8 @@ const resources = {
       'configs.menus.Nodes': '数据节点',
       'configs.menus.DataSynchronize': '数据同步',
       'configs.menus.TenantManagement': '租户管理',
+      'configs.menus.SystemOperation': '系统运维',
+      'configs.menus.ModuleAudit': '模块审计',
     },
   },
 };
diff --git a/inlong-dashboard/src/ui/locales/cn.json 
b/inlong-dashboard/src/ui/locales/cn.json
index 540c485122..340f1f2c43 100644
--- a/inlong-dashboard/src/ui/locales/cn.json
+++ b/inlong-dashboard/src/ui/locales/cn.json
@@ -830,5 +830,8 @@
   "pages.Tenant.config.Admin": "租户管理员",
   "pages.Tenant.config.GeneralUser": "普通用户",
   "pages.Tenant.config.Creator": "创建人",
-  "pages.Tenant.config.CreateTime": "创建时间"
+  "pages.Tenant.config.CreateTime": "创建时间",
+  "pages.ModuleAuditDashboard.config.Ip": "机器 IP",
+  "pages.ModuleAuditDashboard.config.BenchmarkIndicator": "基准指标",
+  "pages.ModuleAuditDashboard.config.ComparativeIndicators": "对比指标"
 }
diff --git a/inlong-dashboard/src/ui/locales/en.json 
b/inlong-dashboard/src/ui/locales/en.json
index 4203413bc5..28d20ba465 100644
--- a/inlong-dashboard/src/ui/locales/en.json
+++ b/inlong-dashboard/src/ui/locales/en.json
@@ -829,5 +829,8 @@
   "pages.Tenant.config.Admin": "Tenant admin",
   "pages.Tenant.config.GeneralUser": "General user",
   "pages.Tenant.config.Creator": "Creator",
-  "pages.Tenant.config.CreateTime": "Create time"
+  "pages.Tenant.config.CreateTime": "Create time",
+  "pages.ModuleAuditDashboard.config.Ip": "Machine ip",
+  "pages.ModuleAuditDashboard.config.BenchmarkIndicator": "Benchmark 
indicator",
+  "pages.ModuleAuditDashboard.config.ComparativeIndicators": "Comparative 
indicator"
 }
diff --git a/inlong-dashboard/src/ui/pages/ModuleAuditDashboard/config.tsx 
b/inlong-dashboard/src/ui/pages/ModuleAuditDashboard/config.tsx
new file mode 100644
index 0000000000..43c119ec36
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/ModuleAuditDashboard/config.tsx
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import dayjs from 'dayjs';
+import i18n from '@/i18n';
+
+export const timeStaticsDimList = [
+  {
+    label: i18n.t('pages.GroupDetail.Audit.Min'),
+    value: 'MINUTE',
+  },
+  {
+    label: i18n.t('pages.GroupDetail.Audit.Hour'),
+    value: 'HOUR',
+  },
+  {
+    label: i18n.t('pages.GroupDetail.Audit.Day'),
+    value: 'DAY',
+  },
+];
+
+export const toChartData = (source, sourceDataMap) => {
+  const xAxisData = Object.keys(sourceDataMap);
+  return {
+    legend: {
+      data: source.map(item => item.auditName),
+    },
+    tooltip: {
+      trigger: 'axis',
+    },
+    xAxis: {
+      type: 'category',
+      data: xAxisData,
+    },
+    yAxis: {
+      type: 'value',
+    },
+    series: source.map(item => ({
+      name: item.auditName,
+      type: 'line',
+      data: xAxisData.map(logTs => sourceDataMap[logTs]?.[item.auditId] || 0),
+    })),
+  };
+};
+
+export const toTableData = (source, sourceDataMap) => {
+  return Object.keys(sourceDataMap)
+    .reverse()
+    .map(logTs => ({
+      ...sourceDataMap[logTs],
+      logTs,
+    }));
+};
+
+export const getFormContent = (initialValues, onSearch) => [
+  {
+    type: 'inputsearch',
+    label: i18n.t('pages.ModuleAuditDashboard.config.Ip'),
+    name: 'ip',
+  },
+  {
+    type: 'datepicker',
+    label: i18n.t('pages.GroupDetail.Audit.StartDate'),
+    name: 'startDate',
+    initialValue: dayjs(initialValues.startDate),
+    props: {
+      allowClear: false,
+      format: 'YYYY-MM-DD',
+    },
+  },
+  {
+    type: 'datepicker',
+    label: i18n.t('pages.GroupDetail.Audit.EndDate'),
+    name: 'endDate',
+    initialValues: dayjs(initialValues.endDate),
+    props: {
+      allowClear: false,
+      format: 'YYYY-MM-DD',
+      disabledDate: current => {
+        const start = dayjs(initialValues.startDate);
+        const dim = initialValues.timeStaticsDim;
+        if (dim === 'HOUR' || dim === 'DAY') {
+          const tooLate = current && current <= start.endOf('day');
+          const tooEarly = start && current > start.add(7, 'd').endOf('day');
+          return tooLate || tooEarly;
+        }
+        const tooLate = current && current >= start.endOf('day');
+        const tooEarly = start && current < start.add(-1, 'd').endOf('day');
+        return tooLate || tooEarly;
+      },
+    },
+  },
+  {
+    type: 'select',
+    label: i18n.t('pages.GroupDetail.Audit.TimeStaticsDim'),
+    name: 'timeStaticsDim',
+    initialValue: initialValues.timeStaticsDim,
+    props: {
+      dropdownMatchSelectWidth: false,
+      options: timeStaticsDimList,
+    },
+  },
+  {
+    type: 'select',
+    label: i18n.t('pages.ModuleAuditDashboard.config.BenchmarkIndicator'),
+    name: 'benchmark',
+    props: {
+      allowClear: true,
+      dropdownMatchSelectWidth: false,
+      options: {
+        requestAuto: true,
+        requestService: {
+          url: '/audit/getAuditBases',
+          method: 'GET',
+        },
+        requestParams: {
+          formatResult: result =>
+            result?.map(item => ({
+              label: item.name,
+              value: item.auditId,
+            })) || [],
+        },
+      },
+    },
+  },
+  {
+    type: 'select',
+    label: i18n.t('pages.ModuleAuditDashboard.config.ComparativeIndicators'),
+    name: 'compared',
+    props: {
+      allowClear: true,
+      dropdownMatchSelectWidth: false,
+      options: {
+        requestAuto: true,
+        requestService: {
+          url: '/audit/getAuditBases',
+          method: 'GET',
+        },
+        requestParams: {
+          formatResult: result =>
+            result?.map(item => ({
+              label: item.name,
+              value: item.auditId,
+            })) || [],
+        },
+      },
+    },
+  },
+];
+
+export const getTableColumns = source => {
+  const data = source.map(item => ({
+    title: item.auditName,
+    dataIndex: item.auditId,
+    render: text => text || 0,
+  }));
+  return [
+    {
+      title: i18n.t('pages.GroupDetail.Audit.Time'),
+      dataIndex: 'logTs',
+    },
+  ].concat(data);
+};
diff --git a/inlong-dashboard/src/ui/pages/ModuleAuditDashboard/index.tsx 
b/inlong-dashboard/src/ui/pages/ModuleAuditDashboard/index.tsx
new file mode 100644
index 0000000000..8136960163
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/ModuleAuditDashboard/index.tsx
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, { useMemo, useState } from 'react';
+import { useForm } from '@/ui/components/FormGenerator';
+import HighTable from '@/ui/components/HighTable';
+import { useRequest } from '@/ui/hooks';
+import { timestampFormat } from '@/core/utils';
+import { getFormContent, toTableData, getTableColumns, timeStaticsDimList } 
from './config';
+
+const Comp: React.FC = () => {
+  const [form] = useForm();
+
+  const [query, setQuery] = useState({
+    startDate: +new Date(),
+    endDate: +new Date(),
+    auditIds: ['3', '4'],
+    timeStaticsDim: timeStaticsDimList[0].value,
+  });
+
+  const { data: sourceData = [], run } = useRequest(
+    {
+      url: '/audit/list',
+      method: 'POST',
+      data: {
+        ...query,
+        startDate: timestampFormat(query.startDate, 'yyyy-MM-dd'),
+        endDate: timestampFormat(query.endDate, 'yyyy-MM-dd'),
+      },
+    },
+    {
+      refreshDeps: [query],
+      formatResult: result => result.sort((a, b) => (a.auditId - b.auditId > 0 
? 1 : -1)),
+    },
+  );
+
+  const sourceDataMap = useMemo(() => {
+    const flatArr = sourceData
+      .reduce(
+        (acc, cur) =>
+          acc.concat(
+            cur.auditSet.map(item => ({
+              ...item,
+              auditId: cur.auditId,
+            })),
+          ),
+        [],
+      )
+      .sort((a, b) => {
+        const aT = +new Date(query.timeStaticsDim === 'HOUR' ? `${a.logTs}:00` 
: a.logTs);
+        const bT = +new Date(query.timeStaticsDim === 'HOUR' ? `${b.logTs}:00` 
: b.logTs);
+        return aT - bT;
+      });
+    const output = flatArr.reduce((acc, cur) => {
+      if (!acc[cur.logTs]) {
+        acc[cur.logTs] = {};
+      }
+      acc[cur.logTs] = {
+        ...acc[cur.logTs],
+        [cur.auditId]: cur.count,
+      };
+      return acc;
+    }, {});
+    return output;
+  }, [sourceData, query.timeStaticsDim]);
+
+  const onSearch = async () => {
+    await form.validateFields();
+    run();
+  };
+
+  const onFilter = keyword => {
+    setQuery({
+      ...query,
+      ...keyword,
+      auditIds:
+        keyword.benchmark !== undefined && keyword.compared !== undefined
+          ? [keyword.benchmark, keyword.compared]
+          : ['3', '4'],
+      startDate: +keyword.startDate.$d,
+      endDate: +keyword.startDate.$d,
+    });
+  };
+
+  return (
+    <>
+      <HighTable
+        filterForm={{
+          content: getFormContent(query, onSearch),
+          onFilter,
+        }}
+        table={{
+          columns: getTableColumns(sourceData),
+          dataSource: toTableData(sourceData, sourceDataMap),
+          rowKey: 'logTs',
+        }}
+      />
+    </>
+  );
+};
+
+export default Comp;

Reply via email to