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

aloyszhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git


The following commit(s) were added to refs/heads/master by this push:
     new f3dee08a52 [INLONG-10691][Dashboard] Add a metrics query page (#10692)
f3dee08a52 is described below

commit f3dee08a52a2f6ef6b11791b58d4f8f83956c673
Author: kamianlaida <[email protected]>
AuthorDate: Tue Jul 23 12:27:34 2024 +0800

    [INLONG-10691][Dashboard] Add a metrics query page (#10692)
---
 inlong-dashboard/src/ui/locales/cn.json            |   3 +
 inlong-dashboard/src/ui/locales/en.json            |   3 +
 .../src/ui/pages/GroupDetail/Audit/config.tsx      |   2 +-
 .../src/ui/pages/GroupDetail/Delay/config.tsx      |   2 +-
 .../Audit => ModuleAudit/AuditModule}/config.tsx   |  64 +++++----
 .../src/ui/pages/ModuleAudit/AuditModule/index.tsx | 155 +++++++++++++++++++++
 .../src/ui/pages/ModuleAudit/index.tsx             |   7 +-
 .../pages/SynchronizeDetail/SyncAudit/config.tsx   |   2 +-
 8 files changed, 208 insertions(+), 30 deletions(-)

diff --git a/inlong-dashboard/src/ui/locales/cn.json 
b/inlong-dashboard/src/ui/locales/cn.json
index 565f5fd6ac..88f10feb5d 100644
--- a/inlong-dashboard/src/ui/locales/cn.json
+++ b/inlong-dashboard/src/ui/locales/cn.json
@@ -559,6 +559,7 @@
   "components.Layout.UserManual": "用户手册",
   "components.Layout.Feedback": "建议反馈",
   "components.HighSelect.Customize": "自定义",
+  "components.HighRadio.Customize": "自定义(ascii码)",
   "components.HighSelect.SearchPlaceholder": "请输入关键字搜索",
   "components.NodeSelect.Create": "新建节点",
   "components.FieldList.SinkFieldName": "目标字段",
@@ -656,6 +657,7 @@
   "pages.GroupDetail.Audit.Send": "发送成功",
   "pages.GroupDetail.Audit.TimeStaticsDim": "粒度",
   "pages.GroupDetail.Audit.Item": "审计项",
+  "pages.GroupDetail.Metric.Item": "指标项",
   "pages.GroupDetail.Audit.Min": "分钟",
   "pages.GroupDetail.Audit.Hour": "小时",
   "pages.GroupDetail.Audit.Day": "天",
@@ -890,6 +892,7 @@
   "pages.Tenant.config.CreateTime": "创建时间",
   "pages.ModuleAudit.Ip": "IP 查询",
   "pages.ModuleAudit.Id": "ID 查询",
+  "pages.ModuleAudit.Metric": "指标查询",
   "pages.ModuleAudit.config.Ip": "机器 IP",
   "pages.ModuleAudit.config.BenchmarkIndicator": "基准指标",
   "pages.ModuleAudit.config.ComparativeIndicators": "对比指标",
diff --git a/inlong-dashboard/src/ui/locales/en.json 
b/inlong-dashboard/src/ui/locales/en.json
index 809d676b54..8998a2d13f 100644
--- a/inlong-dashboard/src/ui/locales/en.json
+++ b/inlong-dashboard/src/ui/locales/en.json
@@ -559,6 +559,7 @@
   "components.Layout.UserManual": "User manual",
   "components.Layout.Feedback": "Feedback",
   "components.HighSelect.Customize": "Customize",
+  "components.HighRadio.Customize": "Customize (ascii code)",
   "components.HighSelect.SearchPlaceholder": "Please enter keyword...",
   "components.NodeSelect.Create": "Create node",
   "components.FieldList.SinkFieldName": "Sink field name",
@@ -660,6 +661,7 @@
   "pages.GroupDetail.Audit.Day": "Day",
   "pages.GroupDetail.Audit.Sink": "Sink",
   "pages.GroupDetail.Audit.Item": "Audit item",
+  "pages.GroupDetail.Metric.Item": "Metric item",
   "pages.GroupDetail.Audit.Total": "Total",
   "pages.GroupDetail.Audit.DatepickerRule": "Out of selectable time range",
   "pages.GroupDetail.Delay.QueryDate": "Query date",
@@ -890,6 +892,7 @@
   "pages.Tenant.config.CreateTime": "Create time",
   "pages.ModuleAudit.Ip": "Query by ip",
   "pages.ModuleAudit.Id": "Query by id",
+  "pages.ModuleAudit.Metric": "Query by Metric",
   "pages.ModuleAudit.config.Ip": "Machine ip",
   "pages.ModuleAudit.config.BenchmarkIndicator": "Benchmark indicator",
   "pages.ModuleAudit.config.ComparativeIndicators": "Comparative indicator",
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx 
b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
index b61d119020..10cc589d50 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
@@ -160,7 +160,7 @@ let endTimeVisible = true;
 export const getFormContent = (inlongGroupId, initialValues, onSearch, 
onDataStreamSuccess) => [
   {
     type: 'select',
-    label: i18n.t('pages.GroupDetail.Audit.DataStream'),
+    label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
     name: 'inlongStreamId',
     props: {
       dropdownMatchSelectWidth: false,
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx 
b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx
index 3cee25ee8c..4ebe19d8f5 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx
@@ -99,7 +99,7 @@ export const toTableData = (source, sourceDataMap) => {
 export const getFormContent = (inlongGroupId, initialValues, onSearch, 
onDataStreamSuccess) => [
   {
     type: 'select',
-    label: i18n.t('pages.GroupDetail.Audit.DataStream'),
+    label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
     name: 'inlongStreamId',
     props: {
       dropdownMatchSelectWidth: false,
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx 
b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/config.tsx
similarity index 87%
copy from inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
copy to inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/config.tsx
index b61d119020..81d05bd15d 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/config.tsx
@@ -69,10 +69,11 @@ function getAuditLabel(auditId: number, nodeType?: string) {
 }
 
 export const toChartData = (source, sourceDataMap) => {
-  const xAxisData = Object.keys(sourceDataMap);
+  console.log(source, sourceDataMap);
+  const xAxisData = Object.keys(sourceDataMap ? sourceDataMap : '12345');
   return {
     legend: {
-      data: source.map(item => item.auditName),
+      data: source.map(item => item?.auditName),
     },
     tooltip: {
       trigger: 'axis',
@@ -85,9 +86,9 @@ export const toChartData = (source, sourceDataMap) => {
       type: 'value',
     },
     series: source.map(item => ({
-      name: item.auditName,
+      name: item?.auditName ? item?.auditName : '1233',
       type: 'line',
-      data: xAxisData.map(logTs => sourceDataMap[logTs]?.[item.auditId] || 0),
+      data: xAxisData.map(logTs => sourceDataMap[logTs]?.[item?.auditId] || 0),
     })),
   };
 };
@@ -157,69 +158,73 @@ export const getSourceDataWithCommas = sourceData => {
 
 let endTimeVisible = true;
 
-export const getFormContent = (inlongGroupId, initialValues, onSearch, 
onDataStreamSuccess) => [
+export const getFormContent = (initialValues, onSearch, onDataStreamSuccess) 
=> [
   {
     type: 'select',
-    label: i18n.t('pages.GroupDetail.Audit.DataStream'),
-    name: 'inlongStreamId',
-    props: {
+    label: i18n.t('pages.ModuleAudit.config.InlongGroupId'),
+    name: 'inlongGroupId',
+    props: values => ({
       dropdownMatchSelectWidth: false,
       showSearch: true,
+      allowClear: true,
       options: {
         requestAuto: true,
         requestTrigger: ['onOpen', 'onSearch'],
         requestService: keyword => ({
-          url: '/stream/list',
+          url: '/group/list',
           method: 'POST',
           data: {
             keyword,
             pageNum: 1,
             pageSize: 100,
-            inlongGroupId,
+            inlongGroupMode: 0,
           },
         }),
         requestParams: {
           formatResult: result =>
             result?.list.map(item => ({
-              label: item.inlongStreamId,
-              value: item.inlongStreamId,
+              label: item.inlongGroupId,
+              value: item.inlongGroupId,
             })) || [],
-          onSuccess: onDataStreamSuccess,
         },
       },
-    },
+    }),
     rules: [{ required: true }],
   },
   {
     type: 'select',
-    label: i18n.t('pages.GroupDetail.Audit.Sink'),
-    name: 'sinkId',
+    label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
+    name: 'inlongStreamId',
     props: values => ({
       dropdownMatchSelectWidth: false,
       showSearch: true,
+      allowClear: true,
+      disabled: !Boolean(values.inlongGroupId),
       options: {
+        requestAuto: true,
         requestTrigger: ['onOpen', 'onSearch'],
         requestService: keyword => ({
-          url: '/sink/list',
+          url: '/stream/list',
           method: 'POST',
           data: {
             keyword,
             pageNum: 1,
             pageSize: 100,
-            inlongGroupId,
-            inlongStreamId: values.inlongStreamId,
+            inlongGroupId: values.inlongGroupId,
           },
         }),
         requestParams: {
           formatResult: result =>
             result?.list.map(item => ({
-              label: item.sinkName + ` ( ${sinks.find(c => c.value === 
item.sinkType)?.label} )`,
-              value: item.id,
+              label: item.inlongStreamId,
+              value: item.inlongStreamId,
             })) || [],
         },
       },
     }),
+    rules: [{ required: true }],
   },
+
   {
     type: 'datepicker',
     label: i18n.t('pages.GroupDetail.Audit.StartDate'),
@@ -287,8 +292,9 @@ export const getFormContent = (inlongGroupId, 
initialValues, onSearch, onDataStr
   },
   {
     type: 'select',
-    label: i18n.t('pages.GroupDetail.Audit.Item'),
+    label: i18n.t('pages.GroupDetail.Metric.Item'),
     name: 'auditIds',
+    rules: [{ required: true }],
     props: {
       style: {
         width: 200,
@@ -301,7 +307,12 @@ export const getFormContent = (inlongGroupId, 
initialValues, onSearch, onDataStr
         requestAuto: true,
         requestTrigger: ['onOpen'],
         requestService: () => {
-          return request('/audit/getAuditBases');
+          return request({
+            url: '/audit/getAuditBases',
+            params: {
+              isMetric: true,
+            },
+          });
         },
         requestParams: {
           formatResult: result => {
@@ -339,12 +350,13 @@ export const getTableColumns = (source, dim) => {
     title: item.auditName,
     dataIndex: item.auditId,
     render: text => {
+      let color = 'black';
       if (text?.includes('+')) {
-        return <span style={{ color: 'red' }}>{text}</span>;
+        color = 'red';
       } else if (text?.includes('-')) {
-        return <span style={{ color: 'green' }}>{text}</span>;
+        color = 'green';
       }
-      return <span>{text}</span>;
+      return <span style={{ color: color }}>{text}</span>;
     },
   }));
   return [
diff --git a/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/index.tsx 
b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/index.tsx
new file mode 100644
index 0000000000..5dbed68dd7
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/index.tsx
@@ -0,0 +1,155 @@
+/*
+ * 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 FormGenerator, { useForm } from '@/ui/components/FormGenerator';
+import HighTable from '@/ui/components/HighTable';
+import { useRequest } from '@/ui/hooks';
+import { timestampFormat } from '@/core/utils';
+import Charts from '@/ui/components/Charts';
+import {
+  getFormContent,
+  toChartData,
+  toTableData,
+  getTableColumns,
+  timeStaticsDimList,
+} from './config';
+import { Table } from 'antd';
+import i18n from '@/i18n';
+
+export const auditModule = 'audit';
+const Comp: React.FC = () => {
+  const [form] = useForm();
+
+  const [query, setQuery] = useState({
+    inlongGroupId: '',
+    inlongStreamId: '',
+    startDate: +new Date(),
+    endDate: +new Date(),
+    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'),
+      },
+    },
+    {
+      ready: Boolean(query.inlongStreamId),
+      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 () => {
+    let values = await form.validateFields();
+    if (values.timeStaticsDim == 'MINUTE') {
+      setQuery(prev => ({ ...prev, endDate: prev.startDate }));
+    }
+    run();
+  };
+
+  const onDataStreamSuccess = data => {
+    const defaultDataStream = data[0]?.value;
+    if (defaultDataStream) {
+      form.setFieldsValue({ inlongStreamId: defaultDataStream });
+      setQuery(prev => ({ ...prev, inlongStreamId: defaultDataStream }));
+      run();
+    }
+  };
+
+  return (
+    <>
+      <div style={{ marginBottom: 40 }}>
+        <FormGenerator
+          form={form}
+          layout="inline"
+          content={getFormContent(query, onSearch, onDataStreamSuccess)}
+          style={{ marginBottom: 30 }}
+          onFilter={allValues =>
+            setQuery({
+              ...allValues,
+              startDate: +allValues.startDate.$d,
+              endDate: +allValues.endDate.$d,
+            })
+          }
+        />
+        <Charts height={400} option={toChartData(sourceData, sourceDataMap)} 
forceUpdate={true} />
+      </div>
+
+      <HighTable
+        table={{
+          columns: getTableColumns(sourceData, query.timeStaticsDim),
+          dataSource: toTableData(sourceData, sourceDataMap),
+          rowKey: 'logTs',
+          summary: () => (
+            <Table.Summary fixed>
+              <Table.Summary.Row>
+                <Table.Summary.Cell index={0}>
+                  {i18n.t('pages.GroupDetail.Audit.Total')}
+                </Table.Summary.Cell>
+                {sourceData.map((row, index) => (
+                  <Table.Summary.Cell key={index} index={index + 1}>
+                    {row.auditSet.reduce((total, item) => total + item.count, 
0).toLocaleString()}
+                  </Table.Summary.Cell>
+                ))}
+              </Table.Summary.Row>
+            </Table.Summary>
+          ),
+        }}
+      />
+    </>
+  );
+};
+
+export default Comp;
diff --git a/inlong-dashboard/src/ui/pages/ModuleAudit/index.tsx 
b/inlong-dashboard/src/ui/pages/ModuleAudit/index.tsx
index 52385292b7..f0fc7b826e 100644
--- a/inlong-dashboard/src/ui/pages/ModuleAudit/index.tsx
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/index.tsx
@@ -24,7 +24,7 @@ import { useHistory, useParams } from '@/ui/hooks';
 import i18n from '@/i18n';
 import IpModule, { ipModule as ipModuleName } from './IpModule';
 import IdModule, { idModule as idModuleName } from './IdModule';
-
+import AuditModule, { auditModule as auditModuleName } from 
'@/ui/pages/ModuleAudit/AuditModule';
 const tabList = [
   {
     tab: i18n.t('pages.ModuleAudit.Id'),
@@ -36,6 +36,11 @@ const tabList = [
     key: ipModuleName,
     content: <IpModule />,
   },
+  {
+    tab: i18n.t('pages.ModuleAudit.Metric'),
+    key: auditModuleName,
+    content: <AuditModule />,
+  },
 ];
 
 const tabListMap = tabList.reduce(
diff --git 
a/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncAudit/config.tsx 
b/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncAudit/config.tsx
index b6c6ded33a..e5b29f7f4e 100644
--- a/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncAudit/config.tsx
+++ b/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncAudit/config.tsx
@@ -95,7 +95,7 @@ export const toTableData = (source, sourceDataMap) => {
 export const getFormContent = (inlongGroupId, initialValues, onSearch, 
onDataStreamSuccess) => [
   {
     type: 'select',
-    label: i18n.t('pages.GroupDetail.Audit.DataStream'),
+    label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
     name: 'inlongStreamId',
     props: {
       dropdownMatchSelectWidth: false,

Reply via email to