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

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

commit fa05e0290a962c92aa82b1c66bbcaa9fa25fc24e
Author: Lizhen <88174078+bluew...@users.noreply.github.com>
AuthorDate: Tue Jul 18 17:39:27 2023 +0800

    [INLONG-8548][Dashboard] Data access supports displaying transmission delay 
(#8555)
    
    Co-authored-by: Charles Zhang <dockerzh...@apache.org>
    Co-authored-by: healchow <healc...@gmail.com>
    (cherry picked from commit b2a25b57a96db02421d3c1a639cff7dd3b60a785)
---
 inlong-dashboard/src/ui/locales/cn.json            |   4 +
 inlong-dashboard/src/ui/locales/en.json            |   4 +
 .../src/ui/pages/GroupDetail/Audit/index.tsx       |   2 -
 .../src/ui/pages/GroupDetail/Delay/config.tsx      | 211 +++++++++++++++++++++
 .../pages/GroupDetail/{Audit => Delay}/index.tsx   |  64 ++++++-
 .../src/ui/pages/GroupDetail/index.tsx             |   7 +
 6 files changed, 287 insertions(+), 5 deletions(-)

diff --git a/inlong-dashboard/src/ui/locales/cn.json 
b/inlong-dashboard/src/ui/locales/cn.json
index e2d567bbae..67d8004dc8 100644
--- a/inlong-dashboard/src/ui/locales/cn.json
+++ b/inlong-dashboard/src/ui/locales/cn.json
@@ -617,6 +617,7 @@
   "pages.GroupDetail.Sinks": "数据目标",
   "pages.GroupDetail.Audit": "审计对账",
   "pages.GroupDetail.Resource": "资源详情",
+  "pages.GroupDetail.Delay": "传输时延",
   "pages.GroupDetail.Resource.Info": " 信息",
   "pages.GroupDetail.Audit.DataStream": "数据流",
   "pages.GroupDetail.Audit.StartDate": "开始日期",
@@ -631,6 +632,9 @@
   "pages.GroupDetail.Audit.Hour": "小时",
   "pages.GroupDetail.Audit.Day": "天",
   "pages.GroupDetail.Audit.Sink": "数据目标",
+  "pages.GroupDetail.Delay.QueryDate": "查询日期",
+  "pages.GroupDetail.Delay.AverageTitle": "平均传输时延 (ms)",
+  "pages.GroupDetail.Delay.RealTimeTitle": "传输时延 (ms)",
   "pages.GroupDetail.Stream.Preview": "数据预览",
   "pages.GroupDetail.Stream.Dt": "数据时间 (dt)",
   "pages.GroupDetail.Stream.Content": "数据内容",
diff --git a/inlong-dashboard/src/ui/locales/en.json 
b/inlong-dashboard/src/ui/locales/en.json
index 4542a6bd57..60e49aa8df 100644
--- a/inlong-dashboard/src/ui/locales/en.json
+++ b/inlong-dashboard/src/ui/locales/en.json
@@ -616,6 +616,7 @@
   "pages.GroupDetail.Sources": "Sources",
   "pages.GroupDetail.Sinks": "Sinks",
   "pages.GroupDetail.Audit": "Audit",
+  "pages.GroupDetail.Delay": "Transmission Delay",
   "pages.GroupDetail.Resource": "Resource Detail",
   "pages.GroupDetail.Resource.Info": " Info",
   "pages.GroupDetail.Audit.DataStream": "DataStream",
@@ -631,6 +632,9 @@
   "pages.GroupDetail.Audit.Hour": "Hour",
   "pages.GroupDetail.Audit.Day": "Day",
   "pages.GroupDetail.Audit.Sink": "Sink",
+  "pages.GroupDetail.Delay.QueryDate": "Query Date",
+  "pages.GroupDetail.Delay.AverageTitle": "Average Transmission Delay (ms)",
+  "pages.GroupDetail.Delay.RealTimeTitle": "Transmission Delay (ms)",
   "pages.GroupDetail.Stream.Preview": "Data Preview",
   "pages.GroupDetail.Stream.Dt": "Data Time(dt)",
   "pages.GroupDetail.Stream.Content": "Data Content",
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx 
b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
index 93d1a26a51..3b368c03ad 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
@@ -81,12 +81,10 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
     const output = flatArr.reduce((acc, cur) => {
       if (!acc[cur.logTs]) {
         acc[cur.logTs] = {};
-        acc[cur.delay] = '';
       }
       acc[cur.logTs] = {
         ...acc[cur.logTs],
         [cur.auditId]: cur.count,
-        [cur.delay]: cur.delay,
       };
       return acc;
     }, {});
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx 
b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx
new file mode 100644
index 0000000000..67554a1372
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx
@@ -0,0 +1,211 @@
+/*
+ * 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 from 'react';
+import { Button } from 'antd';
+import dayjs from 'dayjs';
+import i18n from '@/i18n';
+import { sinks } from '@/plugins/sinks';
+
+export const timeStaticsDimList = [
+  {
+    label: i18n.t('pages.GroupDetail.Audit.Min'),
+    value: 'MINUTE',
+  },
+  {
+    label: i18n.t('pages.GroupDetail.Audit.Hour'),
+    value: 'HOUR',
+  },
+];
+
+const auditList = ['Agent', 'DataProxy', 'Sort'].reduce((acc, item, index) => {
+  return acc.concat([
+    {
+      label: `${item} ${i18n.t('pages.GroupDetail.Audit.Receive')}`,
+      value: index * 2 + 3,
+    },
+    {
+      label: `${item} ${i18n.t('pages.GroupDetail.Audit.Send')}`,
+      value: index * 2 + 4,
+    },
+  ]);
+}, []);
+
+const auditMap = auditList.reduce(
+  (acc, cur) => ({
+    ...acc,
+    [cur.value]: cur,
+  }),
+  {},
+);
+
+function getAuditLabel(auditId: number, nodeType?: string) {
+  const id = +auditId;
+  const item = id >= 9 ? auditMap[id % 2 ? 7 : 8] : auditMap[id];
+  const label = item?.label || id;
+  const sinkLabel = sinks.find(c => c.value === nodeType)?.label;
+  return nodeType ? `${label}(${sinkLabel})` : label;
+}
+
+export const toChartData = (source, sourceDataMap) => {
+  const xAxisData = Object.keys(sourceDataMap);
+  return {
+    legend: {
+      data: source.map(item => getAuditLabel(item.auditId, item.nodeType)),
+    },
+    tooltip: {
+      trigger: 'axis',
+    },
+    xAxis: {
+      type: 'category',
+      data: xAxisData,
+    },
+    yAxis: {
+      type: 'value',
+    },
+    series: source.map(item => ({
+      name: getAuditLabel(item.auditId, item.nodeType),
+      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 = (inlongGroupId, initialValues, onSearch, 
onDataStreamSuccess) => [
+  {
+    type: 'select',
+    label: i18n.t('pages.GroupDetail.Audit.DataStream'),
+    name: 'inlongStreamId',
+    props: {
+      dropdownMatchSelectWidth: false,
+      options: {
+        requestAuto: true,
+        requestService: {
+          url: '/stream/list',
+          method: 'POST',
+          data: {
+            pageNum: 1,
+            pageSize: 1000,
+            inlongGroupId,
+          },
+        },
+        requestParams: {
+          formatResult: result =>
+            result?.list.map(item => ({
+              label: item.inlongStreamId,
+              value: item.inlongStreamId,
+            })) || [],
+          onSuccess: onDataStreamSuccess,
+        },
+      },
+    },
+    rules: [{ required: true }],
+  },
+  {
+    type: 'select',
+    label: i18n.t('pages.GroupDetail.Audit.Sink'),
+    name: 'sinkId',
+    props: values => ({
+      dropdownMatchSelectWidth: false,
+      options: {
+        requestService: {
+          url: '/sink/list',
+          method: 'POST',
+          data: {
+            pageNum: 1,
+            pageSize: 1000,
+            inlongGroupId,
+            inlongStreamId: values.inlongStreamId,
+          },
+        },
+        requestParams: {
+          formatResult: result =>
+            result?.list.map(item => ({
+              label: item.sinkName + ` ( ${sinks.find(c => c.value === 
item.sinkType)?.label} )`,
+              value: item.id,
+            })) || [],
+        },
+      },
+    }),
+  },
+  {
+    type: 'datepicker',
+    label: i18n.t('pages.GroupDetail.Delay.QueryDate'),
+    name: 'startDate',
+    initialValue: dayjs(initialValues.startDate),
+    props: {
+      allowClear: false,
+      format: 'YYYY-MM-DD',
+    },
+  },
+  {
+    type: 'select',
+    label: i18n.t('pages.GroupDetail.Audit.TimeStaticsDim'),
+    name: 'timeStaticsDim',
+    initialValue: initialValues.timeStaticsDim,
+    props: {
+      dropdownMatchSelectWidth: false,
+      options: timeStaticsDimList,
+    },
+  },
+  {
+    type: (
+      <Button type="primary" onClick={onSearch}>
+        {i18n.t('pages.GroupDetail.Audit.Search')}
+      </Button>
+    ),
+  },
+];
+
+export const getTableColumns = source => {
+  const data = source.map(item => ({
+    title: getAuditLabel(item.auditId, item.nodeType),
+    dataIndex: item.auditId,
+    render: text => text || 0,
+  }));
+  return [
+    {
+      title: i18n.t('pages.GroupDetail.Audit.Time'),
+      dataIndex: 'logTs',
+    },
+  ].concat(data);
+};
+
+export const getDelayColumns = source => {
+  const data = source.map(item => ({
+    title: getAuditLabel(item.auditId, item.nodeType),
+    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/GroupDetail/Audit/index.tsx 
b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/index.tsx
similarity index 67%
copy from inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
copy to inlong-dashboard/src/ui/pages/GroupDetail/Delay/index.tsx
index 93d1a26a51..1aa6d96237 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/index.tsx
@@ -31,6 +31,8 @@ import {
   getTableColumns,
   timeStaticsDimList,
 } from './config';
+import { Divider, Table } from 'antd';
+import i18n from '@/i18n';
 
 type Props = CommonInterface;
 
@@ -61,6 +63,24 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
     },
   );
 
+  const { data: dayData = [], run: getDayData } = useRequest(
+    {
+      url: '/audit/list',
+      method: 'POST',
+      data: {
+        ...query,
+        timeStaticsDim: 'DAY',
+        startDate: timestampFormat(query.startDate, 'yyyy-MM-dd'),
+        endDate: timestampFormat(query.startDate, 'yyyy-MM-dd'),
+        inlongGroupId,
+      },
+    },
+    {
+      ready: Boolean(query.inlongStreamId),
+      formatResult: result => result.sort((a, b) => (a.auditId - b.auditId > 0 
? 1 : -1)),
+    },
+  );
+
   const sourceDataMap = useMemo(() => {
     const flatArr = sourceData
       .reduce(
@@ -81,21 +101,50 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
     const output = flatArr.reduce((acc, cur) => {
       if (!acc[cur.logTs]) {
         acc[cur.logTs] = {};
-        acc[cur.delay] = '';
       }
       acc[cur.logTs] = {
         ...acc[cur.logTs],
-        [cur.auditId]: cur.count,
-        [cur.delay]: cur.delay,
+        [cur.auditId]: cur.count === 0 ? cur.delay : Math.floor(cur.delay / 
cur.count),
       };
       return acc;
     }, {});
     return output;
   }, [sourceData, query.timeStaticsDim]);
 
+  const dayDataMap = useMemo(() => {
+    const flatArr = dayData
+      .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 === 0 ? cur.delay : Math.floor(cur.delay / 
cur.count),
+      };
+      return acc;
+    }, {});
+    return output;
+  }, [dayData, query.timeStaticsDim]);
+
   const onSearch = async () => {
     await form.validateFields();
     run();
+    getDayData();
   };
 
   const onDataStreamSuccess = data => {
@@ -104,6 +153,7 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
       form.setFieldsValue({ inlongStreamId: defaultDataStream });
       setQuery(prev => ({ ...prev, inlongStreamId: defaultDataStream }));
       run();
+      getDayData();
     }
   };
 
@@ -126,6 +176,14 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
         <Charts height={400} option={toChartData(sourceData, sourceDataMap)} />
       </div>
 
+      <Divider>{i18n.t('pages.GroupDetail.Delay.AverageTitle')}</Divider>
+      <Table
+        columns={getTableColumns(dayData)}
+        dataSource={toTableData(dayData, dayDataMap)}
+        pagination={false}
+      />
+
+      <Divider style={{ marginTop: 50 
}}>{i18n.t('pages.GroupDetail.Delay.RealTimeTitle')}</Divider>
       <HighTable
         table={{
           columns: getTableColumns(sourceData),
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx 
b/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx
index 98704a094d..f42fc8d433 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx
@@ -28,6 +28,7 @@ import Info from './Info';
 import DataStream from './DataStream';
 import Audit from './Audit';
 import ResourceInfo from './ResourceInfo';
+import Delay from './Delay';
 
 const Comp: React.FC = () => {
   const { t } = useTranslation();
@@ -83,6 +84,12 @@ const Comp: React.FC = () => {
           content: ResourceInfo,
           hidden: isReadonly || isCreate,
         },
+        {
+          label: t('pages.GroupDetail.Delay'),
+          value: 'Delay',
+          content: Delay,
+          hidden: isReadonly || isCreate,
+        },
       ].filter(item => !item.hidden),
     [isReadonly, isCreate, t],
   );

Reply via email to