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 fc4695f669 [INLONG-11186][Dashboard] Export audit indicator data to
csv (#11438)
fc4695f669 is described below
commit fc4695f669e326b4f87a6df0dc4e4cac447a0971
Author: kamianlaida <[email protected]>
AuthorDate: Fri Nov 1 10:51:09 2024 +0800
[INLONG-11186][Dashboard] Export audit indicator data to csv (#11438)
---
inlong-dashboard/package-lock.json | 5 +
inlong-dashboard/package.json | 1 +
inlong-dashboard/src/ui/locales/cn.json | 2 +
inlong-dashboard/src/ui/locales/en.json | 2 +
.../src/ui/pages/ClusterTags/TagDetailModal.tsx | 1 -
.../src/ui/pages/GroupDataTemplate/index.tsx | 1 -
.../src/ui/pages/GroupDetail/Audit/config.tsx | 34 ++++-
.../src/ui/pages/GroupDetail/Audit/index.tsx | 56 +++++++-
.../src/ui/pages/GroupDetail/index.tsx | 1 -
.../ui/pages/ModuleAudit/AuditModule/config.tsx | 32 ++++-
.../src/ui/pages/ModuleAudit/AuditModule/index.tsx | 58 ++++++++-
.../src/ui/pages/ModuleAudit/IdModule/config.tsx | 92 ++++++++++++-
.../src/ui/pages/ModuleAudit/IdModule/index.tsx | 131 +++++++++++++++----
.../src/ui/pages/ModuleAudit/IpModule/config.tsx | 96 ++++++++++++--
.../src/ui/pages/ModuleAudit/IpModule/index.tsx | 143 +++++++++++++++++----
15 files changed, 561 insertions(+), 94 deletions(-)
diff --git a/inlong-dashboard/package-lock.json
b/inlong-dashboard/package-lock.json
index efa9d0a413..599ba43811 100644
--- a/inlong-dashboard/package-lock.json
+++ b/inlong-dashboard/package-lock.json
@@ -13290,6 +13290,11 @@
"whatwg-fetch": "^3.6.2"
}
},
+ "react-csv": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/react-csv/-/react-csv-2.2.2.tgz",
+ "integrity":
"sha512-RG5hOcZKZFigIGE8LxIEV/OgS1vigFQT4EkaHeKgyuCbUAu9Nbd/1RYq++bJcJJ9VOqO/n9TZRADsXNDR4VEpw=="
+ },
"react-dev-utils": {
"version": "12.0.1",
"resolved":
"https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
diff --git a/inlong-dashboard/package.json b/inlong-dashboard/package.json
index c287ad670b..7c61492967 100644
--- a/inlong-dashboard/package.json
+++ b/inlong-dashboard/package.json
@@ -14,6 +14,7 @@
"nprogress": "^0.2.0",
"path-to-regexp": "^6.2.0",
"react": "17.0.1",
+ "react-csv": "^2.2.2",
"react-dom": "17.0.1",
"react-i18next": "^11.10.0",
"react-redux": "^7.2.0",
diff --git a/inlong-dashboard/src/ui/locales/cn.json
b/inlong-dashboard/src/ui/locales/cn.json
index 778c687270..7a8e2ea1dd 100644
--- a/inlong-dashboard/src/ui/locales/cn.json
+++ b/inlong-dashboard/src/ui/locales/cn.json
@@ -712,6 +712,7 @@
"pages.GroupDetail.Audit.Sink": "数据目标",
"pages.GroupDetail.Audit.Total": "总计",
"pages.GroupDetail.Audit.DatepickerRule": "超出可选范围",
+ "pages.GroupDetail.Audit.ExportCSV": "导出 CSV 文件",
"pages.GroupDetail.Delay.QueryDate": "查询日期",
"pages.GroupDetail.Delay.AverageTitle": "平均传输时延 (ms)",
"pages.GroupDetail.Delay.RealTimeTitle": "传输时延 (ms)",
@@ -977,6 +978,7 @@
"pages.ModuleAudit.Id": "ID 查询",
"pages.ModuleAudit.Metric": "指标查询",
"pages.ModuleAudit.config.Ip": "机器 IP",
+ "pages.ModuleAudit.config.SubValue": "差异值",
"pages.ModuleAudit.config.BenchmarkIndicator": "基准指标",
"pages.ModuleAudit.config.ComparativeIndicators": "对比指标",
"pages.ModuleAudit.config.InlongGroupId": "数据流组ID",
diff --git a/inlong-dashboard/src/ui/locales/en.json
b/inlong-dashboard/src/ui/locales/en.json
index 8bb5c15ab5..ce06b61f3c 100644
--- a/inlong-dashboard/src/ui/locales/en.json
+++ b/inlong-dashboard/src/ui/locales/en.json
@@ -712,6 +712,7 @@
"pages.GroupDetail.Metric.Item": "Metric item",
"pages.GroupDetail.Audit.Total": "Total",
"pages.GroupDetail.Audit.DatepickerRule": "Out of selectable time range",
+ "pages.GroupDetail.Audit.ExportCSV": "Export CSV File",
"pages.GroupDetail.Delay.QueryDate": "Query date",
"pages.GroupDetail.Delay.AverageTitle": "Average transmission delay (ms)",
"pages.GroupDetail.Delay.RealTimeTitle": "Transmission delay (ms)",
@@ -977,6 +978,7 @@
"pages.ModuleAudit.Id": "Query by id",
"pages.ModuleAudit.Metric": "Query by Metric",
"pages.ModuleAudit.config.Ip": "Machine ip",
+ "pages.ModuleAudit.config.SubValue": "Sub Value",
"pages.ModuleAudit.config.BenchmarkIndicator": "Benchmark indicator",
"pages.ModuleAudit.config.ComparativeIndicators": "Comparative indicator",
"pages.ModuleAudit.config.InlongGroupId": "Inlong group id",
diff --git a/inlong-dashboard/src/ui/pages/ClusterTags/TagDetailModal.tsx
b/inlong-dashboard/src/ui/pages/ClusterTags/TagDetailModal.tsx
index 743e3089a3..0375da7700 100644
--- a/inlong-dashboard/src/ui/pages/ClusterTags/TagDetailModal.tsx
+++ b/inlong-dashboard/src/ui/pages/ClusterTags/TagDetailModal.tsx
@@ -107,7 +107,6 @@ const TagDetailModal: React.FC<TagDetailModalProps> = ({
id, ...modalProps }) =>
maxTagCount: 9,
maxTagTextLength: 20,
maxTagPlaceholder: omittedValues => {
- console.log('omittedValues', omittedValues);
return (
<span>
{i18n.t('miscellaneous.total')}
diff --git a/inlong-dashboard/src/ui/pages/GroupDataTemplate/index.tsx
b/inlong-dashboard/src/ui/pages/GroupDataTemplate/index.tsx
index 1a3326b179..52851f3b63 100644
--- a/inlong-dashboard/src/ui/pages/GroupDataTemplate/index.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDataTemplate/index.tsx
@@ -104,7 +104,6 @@ const Comp: React.FC = () => {
);
const onDelete = useCallback(
record => {
- console.log(record);
Modal.confirm({
title: i18n.t('basic.DeleteConfirm'),
onOk: async () => {
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
index 10cc589d50..fb571449d5 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
@@ -23,6 +23,8 @@ import dayjs from 'dayjs';
import i18n from '@/i18n';
import { sinks } from '@/plugins/sinks';
import request from '@/core/utils/request';
+import { CSVLink } from 'react-csv';
+import audit from '@/ui/pages/GroupDetail/Audit/index';
export const timeStaticsDimList = [
{
@@ -131,11 +133,11 @@ export const getSourceDataWithPercent = (sourceKeys,
sourceMap) => {
export const getDiff = (first, current) => {
if (first === 0) {
- return '0%';
+ return first.toFixed(4) + '%';
}
let result;
- const diff = Math.ceil((current / first - 1) * 100);
- result = diff > 0 ? '+' + diff + '%' : diff + '%';
+ const diff = (current / first - 1) * 100;
+ result = diff > 0 ? '+' + diff.toFixed(4) + '%' : diff.toFixed(4) + '%';
return result;
};
@@ -154,10 +156,17 @@ export const getSourceDataWithCommas = sourceData => {
});
return sourceData;
};
-
let endTimeVisible = true;
-
-export const getFormContent = (inlongGroupId, initialValues, onSearch,
onDataStreamSuccess) => [
+export const getFormContent = (
+ inlongGroupId,
+ initialValues,
+ onSearch,
+ onDataStreamSuccess,
+ sourceData,
+ csvData,
+ fileName,
+ setInlongStreamID,
+) => [
{
type: 'select',
label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
@@ -165,6 +174,9 @@ export const getFormContent = (inlongGroupId,
initialValues, onSearch, onDataStr
props: {
dropdownMatchSelectWidth: false,
showSearch: true,
+ onChange: (value, option) => {
+ setInlongStreamID(value);
+ },
options: {
requestAuto: true,
requestTrigger: ['onOpen', 'onSearch'],
@@ -244,7 +256,6 @@ export const getFormContent = (inlongGroupId,
initialValues, onSearch, onDataStr
return Promise.resolve();
}
const timeDiff = value - getFieldValue('startDate');
- console.log('timeDiff', value, getFieldValue('startDate'), timeDiff);
if (timeDiff >= 0) {
const isHourDiff = dim === 'HOUR' && timeDiff < 1000 * 60 * 60 *
24 * 3;
const isDayDiff = dim === 'DAY' && timeDiff < 1000 * 60 * 60 * 24
* 7;
@@ -332,6 +343,15 @@ export const getFormContent = (inlongGroupId,
initialValues, onSearch, onDataStr
</Button>
),
},
+ {
+ type: (
+ <Button type="primary" disabled={!(sourceData.length > 0)}>
+ <CSVLink data={csvData} filename={fileName}>
+ {i18n.t('pages.GroupDetail.Audit.ExportCSV')}
+ </CSVLink>
+ </Button>
+ ),
+ },
];
export const getTableColumns = (source, dim) => {
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
index f4ba9fbbc4..c7eebf1895 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
-import React, { useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import FormGenerator, { useForm } from '@/ui/components/FormGenerator';
import HighTable from '@/ui/components/HighTable';
import { useRequest } from '@/ui/hooks';
@@ -38,14 +38,13 @@ type Props = CommonInterface;
const Comp: React.FC<Props> = ({ inlongGroupId }) => {
const [form] = useForm();
-
const [query, setQuery] = useState({
inlongStreamId: '',
startDate: +new Date(),
endDate: +new Date(),
timeStaticsDim: timeStaticsDimList[0].value,
});
-
+ const [inlongStreamID, setInlongStreamID] = useState('');
const { data: sourceData = [], run } = useRequest(
{
url: '/audit/list',
@@ -104,20 +103,67 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
const onDataStreamSuccess = data => {
const defaultDataStream = data[0]?.value;
if (defaultDataStream) {
+ setInlongStreamID(defaultDataStream);
form.setFieldsValue({ inlongStreamId: defaultDataStream });
setQuery(prev => ({ ...prev, inlongStreamId: defaultDataStream }));
run();
}
};
+ const numToName = useCallback(
+ num => {
+ let obj = {};
+ sourceData.forEach(item => {
+ obj = { ...obj, [item.auditId]: item.auditName };
+ });
+ obj = { ...obj, logTs: i18n.t('pages.GroupDetail.Audit.Time') };
+ return obj[num];
+ },
+ [sourceData],
+ );
+ const metricSum = useMemo(() => {
+ let obj = { logTs: i18n.t('pages.GroupDetail.Audit.Total') };
+ sourceData.map(item => {
+ const sum = item.auditSet?.reduce((total, cur) => {
+ return total + cur.count;
+ }, 0);
+ obj = { ...obj, [item.auditId]: sum };
+ });
+ return obj;
+ }, [sourceData]);
+ const csvData = useMemo(() => {
+ const result = [...toTableData(sourceData, sourceDataMap),
metricSum].map(item => {
+ let obj = {};
+ Object.keys(item)
+ .reverse()
+ .forEach(key => {
+ obj = { ...obj, [numToName(key)]: item[key] };
+ });
+ return obj;
+ });
+ return result;
+ }, [sourceData, sourceDataMap, metricSum]);
+ const [fileName, setFileName] = useState('audit.csv');
+ useEffect(() => {
+ setFileName(`audit_${inlongGroupId}_${inlongStreamID}.csv`);
+ }, [inlongGroupId, inlongStreamID]);
return (
<>
<div style={{ marginBottom: 40 }}>
<FormGenerator
form={form}
layout="inline"
- content={getFormContent(inlongGroupId, query, onSearch,
onDataStreamSuccess)}
- style={{ marginBottom: 30 }}
+ content={getFormContent(
+ inlongGroupId,
+ query,
+ onSearch,
+ onDataStreamSuccess,
+ sourceData,
+ csvData,
+ fileName,
+ setInlongStreamID,
+ )}
+ style={{ marginBottom: 30, gap: 10 }}
onFilter={allValues =>
setQuery({
...allValues,
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx
b/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx
index 02961f38b4..93a119634a 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx
@@ -64,7 +64,6 @@ const Comp: React.FC = () => {
ready: !!id,
refreshDeps: [id],
onSuccess: result => {
- console.log('res', result, getLocalStorage('tenant')?.['name']);
if (getLocalStorage('tenant')?.['name'] !== result) {
setLocalStorage({ name: result });
message.success(t('components.Layout.Tenant.Success'));
diff --git a/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/config.tsx
b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/config.tsx
index e3821a15ec..423c096774 100644
--- a/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/config.tsx
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/config.tsx
@@ -22,8 +22,7 @@ import { Button } from 'antd';
import dayjs from 'dayjs';
import i18n from '@/i18n';
import { sinks } from '@/plugins/sinks';
-import request from '@/core/utils/request';
-
+import { CSVLink } from 'react-csv';
export const timeStaticsDimList = [
{
label: i18n.t('pages.GroupDetail.Audit.Min'),
@@ -69,7 +68,6 @@ function getAuditLabel(auditId: number, nodeType?: string) {
}
export const toChartData = (source, sourceDataMap) => {
- console.log(source, sourceDataMap);
const xAxisData = Object.keys(sourceDataMap ? sourceDataMap : '12345');
return {
legend: {
@@ -158,7 +156,17 @@ export const getSourceDataWithCommas = sourceData => {
let endTimeVisible = true;
-export const getFormContent = (initialValues, onSearch, onDataStreamSuccess,
auditData) => [
+export const getFormContent = (
+ initialValues,
+ onSearch,
+ onDataStreamSuccess,
+ auditData,
+ sourceData,
+ csvData,
+ setInlongGroupId,
+ setInlongStreamID,
+ fileName,
+) => [
{
type: 'select',
label: i18n.t('pages.ModuleAudit.config.InlongGroupId'),
@@ -167,6 +175,9 @@ export const getFormContent = (initialValues, onSearch,
onDataStreamSuccess, aud
dropdownMatchSelectWidth: false,
showSearch: true,
allowClear: true,
+ onChange: (value, option) => {
+ setInlongGroupId(value);
+ },
options: {
requestAuto: true,
requestTrigger: ['onOpen', 'onSearch'],
@@ -200,6 +211,9 @@ export const getFormContent = (initialValues, onSearch,
onDataStreamSuccess, aud
showSearch: true,
allowClear: true,
disabled: !Boolean(values.inlongGroupId),
+ onChange: (value, option) => {
+ setInlongStreamID(value);
+ },
options: {
requestAuto: true,
requestTrigger: ['onOpen', 'onSearch'],
@@ -249,7 +263,6 @@ export const getFormContent = (initialValues, onSearch,
onDataStreamSuccess, aud
return Promise.resolve();
}
const timeDiff = value - getFieldValue('startDate');
- console.log('timeDiff', value, getFieldValue('startDate'), timeDiff);
if (timeDiff >= 0) {
const isHourDiff = dim === 'HOUR' && timeDiff < 1000 * 60 * 60 *
24 * 3;
const isDayDiff = dim === 'DAY' && timeDiff < 1000 * 60 * 60 * 24
* 7;
@@ -325,6 +338,15 @@ export const getFormContent = (initialValues, onSearch,
onDataStreamSuccess, aud
</Button>
),
},
+ {
+ type: (
+ <Button type="primary" disabled={!(sourceData.length > 0)}>
+ <CSVLink data={csvData} filename={fileName}>
+ {i18n.t('pages.GroupDetail.Audit.ExportCSV')}
+ </CSVLink>
+ </Button>
+ ),
+ },
];
export const getTableColumns = (source, dim) => {
diff --git a/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/index.tsx
b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/index.tsx
index 8a4491b5e2..311d8be2d3 100644
--- a/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/index.tsx
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/index.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
-import React, { useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import FormGenerator, { useForm } from '@/ui/components/FormGenerator';
import HighTable from '@/ui/components/HighTable';
import { useRequest } from '@/ui/hooks';
@@ -45,7 +45,8 @@ const Comp: React.FC<AuditProps> = ({ auditData }) => {
endDate: +new Date(),
timeStaticsDim: timeStaticsDimList[0].value,
});
-
+ const [inlongStreamID, setInlongStreamID] = useState('');
+ const [inlongGroupId, setInlongGroupId] = useState('');
const { data: sourceData = [], run } = useRequest(
{
url: '/audit/list',
@@ -61,7 +62,17 @@ const Comp: React.FC<AuditProps> = ({ auditData }) => {
formatResult: result => result.sort((a, b) => (a.auditId - b.auditId > 0
? 1 : -1)),
},
);
-
+ const numToName = useCallback(
+ num => {
+ let obj = {};
+ sourceData.forEach(item => {
+ obj = { ...obj, [item.auditId]: item.auditName };
+ });
+ obj = { ...obj, logTs: i18n.t('pages.GroupDetail.Audit.Time') };
+ return obj[num];
+ },
+ [sourceData],
+ );
const sourceDataMap = useMemo(() => {
const flatArr = sourceData
.reduce(
@@ -99,7 +110,29 @@ const Comp: React.FC<AuditProps> = ({ auditData }) => {
}
run();
};
+ const metricSum = useMemo(() => {
+ let obj = { logTs: i18n.t('pages.GroupDetail.Audit.Total') };
+ sourceData.map(item => {
+ const sum = item.auditSet?.reduce((total, cur) => {
+ return total + cur.count;
+ }, 0);
+ obj = { ...obj, [item.auditId]: sum };
+ });
+ return obj;
+ }, [sourceData]);
+ const csvData = useMemo(() => {
+ const result = [...toTableData(sourceData, sourceDataMap),
metricSum].map(item => {
+ let obj = {};
+ Object.keys(item)
+ .reverse()
+ .forEach(key => {
+ obj = { ...obj, [numToName(key)]: item[key] };
+ });
+ return obj;
+ });
+ return result;
+ }, [sourceData, sourceDataMap, metricSum]);
const onDataStreamSuccess = data => {
const defaultDataStream = data[0]?.value;
if (defaultDataStream) {
@@ -108,15 +141,28 @@ const Comp: React.FC<AuditProps> = ({ auditData }) => {
run();
}
};
-
+ const [fileName, setFileName] = useState('metrics.csv');
+ useEffect(() => {
+ setFileName(`metrics_${inlongGroupId}_${inlongStreamID}.csv`);
+ }, [inlongGroupId, inlongStreamID]);
return (
<>
<div style={{ marginBottom: 40 }}>
<FormGenerator
form={form}
layout="inline"
- content={getFormContent(query, onSearch, onDataStreamSuccess,
auditData)}
- style={{ marginBottom: 30 }}
+ content={getFormContent(
+ query,
+ onSearch,
+ onDataStreamSuccess,
+ auditData,
+ sourceData,
+ csvData,
+ setInlongGroupId,
+ setInlongStreamID,
+ fileName,
+ )}
+ style={{ marginBottom: 30, gap: 10 }}
onFilter={allValues =>
setQuery({
...allValues,
diff --git a/inlong-dashboard/src/ui/pages/ModuleAudit/IdModule/config.tsx
b/inlong-dashboard/src/ui/pages/ModuleAudit/IdModule/config.tsx
index cd868b6797..b3803906f5 100644
--- a/inlong-dashboard/src/ui/pages/ModuleAudit/IdModule/config.tsx
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/IdModule/config.tsx
@@ -22,7 +22,9 @@ import i18n from '@/i18n';
import request from '@/core/utils/request';
import { Button } from 'antd';
import React from 'react';
-
+import { SortOrder } from 'antd/es/table/interface';
+import { range } from 'lodash';
+import { CSVLink } from 'react-csv';
export const timeStaticsDimList = [
{
label: i18n.t('pages.GroupDetail.Audit.Min'),
@@ -38,7 +40,20 @@ export const timeStaticsDimList = [
},
];
+export const sumSubValue = sourceDataMap => {
+ if (sourceDataMap === null || sourceDataMap === undefined) {
+ return 0;
+ }
+ return Object.keys(sourceDataMap).reduce((acc, cur) => {
+ const element = sourceDataMap[cur];
+ acc += element.subValue;
+ return acc;
+ }, 0);
+};
export const toTableData = (source, sourceDataMap) => {
+ if (sourceDataMap === null || sourceDataMap === undefined) {
+ return [];
+ }
return Object.keys(sourceDataMap)
.reverse()
.map(logTs => ({
@@ -47,7 +62,16 @@ export const toTableData = (source, sourceDataMap) => {
}));
};
-export const getFormContent = (initialValues, onSearch, auditData) => [
+export const getFormContent = (
+ initialValues,
+ onSearch,
+ auditData,
+ sourceData,
+ csvData,
+ setInlongGroupId,
+ setInlongStreamID,
+ fileName,
+) => [
{
type: 'select',
label: i18n.t('pages.ModuleAudit.config.InlongGroupId'),
@@ -55,6 +79,9 @@ export const getFormContent = (initialValues, onSearch,
auditData) => [
props: {
dropdownMatchSelectWidth: false,
showSearch: true,
+ onChange: (value, option) => {
+ setInlongGroupId(value);
+ },
options: {
requestAuto: true,
requestTrigger: ['onOpen', 'onSearch'],
@@ -85,6 +112,9 @@ export const getFormContent = (initialValues, onSearch,
auditData) => [
props: values => ({
dropdownMatchSelectWidth: false,
showSearch: true,
+ onChange: (value, option) => {
+ setInlongStreamID(value);
+ },
options: {
requestAuto: true,
requestTrigger: ['onOpen', 'onSearch'],
@@ -116,7 +146,12 @@ export const getFormContent = (initialValues, onSearch,
auditData) => [
props: {
allowClear: false,
showTime: true,
- format: 'YYYY-MM-DD HH:mm:ss',
+ format: 'YYYY-MM-DD HH:mm',
+ disabledTime: (date: dayjs.Dayjs, type, info: { from?: dayjs.Dayjs }) =>
{
+ return {
+ disabledSeconds: () => range(0, 60),
+ };
+ },
},
},
{
@@ -127,7 +162,12 @@ export const getFormContent = (initialValues, onSearch,
auditData) => [
props: {
allowClear: false,
showTime: true,
- format: 'YYYY-MM-DD HH:mm:ss',
+ format: 'YYYY-MM-DD HH:mm',
+ disabledTime: (date: dayjs.Dayjs, type, info: { from?: dayjs.Dayjs }) =>
{
+ return {
+ disabledSeconds: () => range(0, 60),
+ };
+ },
},
},
{
@@ -181,18 +221,56 @@ export const getFormContent = (initialValues, onSearch,
auditData) => [
</Button>
),
},
+ {
+ type: (
+ <Button type="primary" disabled={!(sourceData.length > 0)}>
+ <CSVLink data={csvData} filename={fileName}>
+ {i18n.t('pages.GroupDetail.Audit.ExportCSV')}
+ </CSVLink>
+ </Button>
+ ),
+ },
];
-export const getTableColumns = source => {
+const strSorter = (a, b) => {
+ return a?.ip.localeCompare(b?.ip);
+};
+const sortOrder: SortOrder = 'descend';
+
+const baseSorter = (a, b) => {
+ return a.base - b.base;
+};
+const comparedSorter = (a, b) => {
+ return a.compared - b.compared;
+};
+const subValueSorter = (a, b) => {
+ return a.subValue - b.subValue;
+};
+
+export const getTableColumns = (source: any) => {
const data = source.map(item => ({
title: item.auditName,
- dataIndex: item.auditId,
+ dataIndex: source[0].auditId === item.auditId ? 'base' : 'compared',
+ key: source[0].auditId === item.auditId ? 'base' : 'compared',
+ sorter: {
+ compare: source[0].auditId === item.auditId ? baseSorter :
comparedSorter,
+ multiple: source[0].auditId === item.auditId ? 3 : 4,
+ },
render: text => text || 0,
}));
return [
{
title: i18n.t('pages.ModuleAudit.config.Ip'),
dataIndex: 'ip',
+ defaultSortOrder: sortOrder,
+ sorter: strSorter,
},
- ].concat(data);
+ ]
+ .concat(data)
+ .concat({
+ title: i18n.t('pages.ModuleAudit.config.SubValue'),
+ dataIndex: 'subValue',
+ defaultSortOrder: null,
+ sorter: subValueSorter,
+ });
};
diff --git a/inlong-dashboard/src/ui/pages/ModuleAudit/IdModule/index.tsx
b/inlong-dashboard/src/ui/pages/ModuleAudit/IdModule/index.tsx
index 65fb7bd053..20f9d64fd6 100644
--- a/inlong-dashboard/src/ui/pages/ModuleAudit/IdModule/index.tsx
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/IdModule/index.tsx
@@ -17,25 +17,29 @@
* under the License.
*/
-import React, { useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import HighTable, { useForm } from '@/ui/components/HighTable';
import { useRequest } from '@/ui/hooks';
import { timestampFormat } from '@/core/utils';
-import { getFormContent, toTableData, getTableColumns } from './config';
+import { getFormContent, toTableData, getTableColumns, sumSubValue } from
'./config';
+import i18n from '@/i18n';
import { AuditProps } from '@/ui/pages/ModuleAudit';
+import { Table } from 'antd';
+import dayjs from 'dayjs';
export const idModule = 'id';
const Comp: React.FC<AuditProps> = ({ auditData }) => {
const [form] = useForm();
const [query, setQuery] = useState({
- startDate: +new Date(),
- endDate: +new Date(),
+ startDate: dayjs().startOf('hour').valueOf(),
+ endDate: dayjs().startOf('hour').valueOf(),
auditIds: ['3', '4'],
inlongGroupId: '',
inlongStreamId: '',
});
-
+ const [inlongStreamID, setInlongStreamID] = useState('');
+ const [inlongGroupId, setInlongGroupId] = useState('');
const { data: sourceData = [], run } = useRequest(
{
url: '/audit/listAll',
@@ -48,32 +52,49 @@ const Comp: React.FC<AuditProps> = ({ auditData }) => {
},
{
refreshDeps: [query],
- formatResult: result => result.sort((a, b) => (a.auditId - b.auditId > 0
? 1 : -1)),
+ formatResult: result => {
+ const base = result.find(item2 => item2.auditId ===
query.auditIds[0].toString());
+ const compared = result.find(item2 => item2.auditId ===
query.auditIds[1].toString());
+ return [base, compared];
+ },
},
);
const sourceDataMap = useMemo(() => {
- const flatArr = sourceData.reduce(
- (acc, cur) =>
- acc.concat(
- cur.auditSet.map(item => ({
- ...item,
- auditId: cur.auditId,
- })),
- ),
- [],
- );
- const output = flatArr.reduce((acc, cur) => {
- if (!acc[cur.ip]) {
- acc[cur.ip] = {};
- }
+ if (!sourceData) {
+ return {};
+ }
+ let baseData =
+ sourceData[0]?.auditSet?.length > sourceData[1]?.auditSet?.length
+ ? sourceData[0]
+ : sourceData[1];
+ const output = baseData?.auditSet?.reduce((acc, cur) => {
acc[cur.ip] = {
- ...acc[cur.ip],
- [cur.auditId]: cur.count,
ip: cur.ip,
+ base:
+ sourceData[0].auditId === baseData.auditId
+ ? cur.count
+ : sourceData[0].auditSet.find(item => (item.ip = cur.ip))
+ ? sourceData[0].auditSet.find(item => (item.ip = cur.ip)).count
+ : 0,
+ compared:
+ sourceData[1].auditId === baseData.auditId
+ ? cur.count
+ : sourceData[1].auditSet.find(item => (item.ip = cur.ip))
+ ? sourceData[1].auditSet.find(item => (item.ip = cur.ip)).count
+ : 0,
};
return acc;
}, {});
+ if (output === undefined || output === null) {
+ return {};
+ }
+ Object.keys(output).forEach(key => {
+ output[key] = {
+ ...output[key],
+ subValue: output[key].compared - output[key].base,
+ };
+ });
return output;
}, [sourceData]);
@@ -86,28 +107,84 @@ const Comp: React.FC<AuditProps> = ({ auditData }) => {
setQuery({
...query,
...keyword,
- auditIds:
- keyword.benchmark !== undefined && keyword.compared !== undefined
- ? [keyword.benchmark, keyword.compared]
- : ['3', '4'],
+ auditIds: [
+ keyword.benchmark !== undefined ? keyword.benchmark :
query.auditIds[0],
+ keyword.compared !== undefined ? keyword.compared : query.auditIds[1],
+ ],
inlongGroupId: keyword.inlongGroupId,
inlongStreamId: keyword.inlongStreamId,
startDate: +keyword.startDate.$d,
endDate: keyword.endDate === undefined ? +keyword.startDate.$d :
+keyword.endDate.$d,
});
};
+ const numToName = useCallback(
+ num => {
+ let obj = {};
+ obj = {
+ base: sourceData[0].auditName,
+ compared: sourceData[1].auditName,
+ ip: i18n.t('pages.ModuleAudit.config.Ip'),
+ subValue: i18n.t('pages.ModuleAudit.config.SubValue'),
+ };
+ return obj[num];
+ },
+ [sourceData],
+ );
+ const csvData = useMemo(() => {
+ const result = toTableData(sourceData, sourceDataMap).map(item => {
+ let obj = {};
+ Object.keys(item)
+ .filter(key => key !== 'logTs')
+ .forEach(key => {
+ obj = { ...obj, [numToName(key)]: item[key] };
+ });
+ return obj;
+ });
+ return result;
+ }, [sourceData, sourceDataMap]);
+ const [fileName, setFileName] = useState('metrics.csv');
+ useEffect(() => {
+ setFileName(`id_${inlongGroupId}_${inlongStreamID}.csv`);
+ }, [inlongGroupId, inlongStreamID]);
return (
<>
<HighTable
filterForm={{
- content: getFormContent(query, onSearch, auditData),
+ style: { gap: '10px' },
+ content: getFormContent(
+ query,
+ onSearch,
+ auditData,
+ sourceData,
+ csvData,
+ setInlongGroupId,
+ setInlongStreamID,
+ fileName,
+ ),
onFilter,
}}
table={{
columns: getTableColumns(sourceData),
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.Cell key={sourceData.length}
index={sourceData.length + 1}>
+ {sumSubValue(sourceDataMap).toLocaleString()}
+ </Table.Summary.Cell>
+ </Table.Summary.Row>
+ </Table.Summary>
+ ),
}}
/>
</>
diff --git a/inlong-dashboard/src/ui/pages/ModuleAudit/IpModule/config.tsx
b/inlong-dashboard/src/ui/pages/ModuleAudit/IpModule/config.tsx
index 1b6dc36e88..9edc4a251e 100644
--- a/inlong-dashboard/src/ui/pages/ModuleAudit/IpModule/config.tsx
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/IpModule/config.tsx
@@ -22,7 +22,9 @@ import i18n from '@/i18n';
import request from '@/core/utils/request';
import { Button } from 'antd';
import React from 'react';
-
+import { CSVLink } from 'react-csv';
+import { range } from 'lodash';
+import { SortOrder } from 'antd/es/table/interface';
export const toChartData = (source, sourceDataMap) => {
const xAxisData = Object.keys(sourceDataMap);
return {
@@ -48,6 +50,9 @@ export const toChartData = (source, sourceDataMap) => {
};
export const toTableData = (source, sourceDataMap) => {
+ if (sourceDataMap === null || sourceDataMap === undefined) {
+ return [];
+ }
return Object.keys(sourceDataMap)
.reverse()
.map(logTs => ({
@@ -56,10 +61,23 @@ export const toTableData = (source, sourceDataMap) => {
}));
};
-export const getFormContent = (initialValues, onSearch, auditData) => [
+export const getFormContent = (
+ initialValues,
+ onSearch,
+ auditData,
+ sourceData,
+ csvData,
+ setIp,
+ fileName,
+) => [
{
type: 'input',
label: i18n.t('pages.ModuleAudit.config.Ip'),
+ props: {
+ onChange: (e: any) => {
+ setIp(e.target.value);
+ },
+ },
name: 'ip',
},
{
@@ -70,18 +88,28 @@ export const getFormContent = (initialValues, onSearch,
auditData) => [
props: {
allowClear: false,
showTime: true,
- format: 'YYYY-MM-DD HH:mm:ss',
+ format: 'YYYY-MM-DD HH:mm',
+ disabledTime: (date: dayjs.Dayjs, type, info: { from?: dayjs.Dayjs }) =>
{
+ return {
+ disabledSeconds: () => range(0, 60),
+ };
+ },
},
},
{
type: 'datepicker',
label: i18n.t('pages.GroupDetail.Audit.EndDate'),
name: 'endDate',
- initialValues: dayjs(initialValues.endDate),
+ initialValues: dayjs().startOf('hour').valueOf(),
props: {
allowClear: false,
showTime: true,
- format: 'YYYY-MM-DD HH:mm:ss',
+ format: 'YYYY-MM-DD HH:mm',
+ disabledTime: (date: dayjs.Dayjs, type, info: { from?: dayjs.Dayjs }) =>
{
+ return {
+ disabledSeconds: () => range(0, 60),
+ };
+ },
},
},
{
@@ -135,22 +163,74 @@ export const getFormContent = (initialValues, onSearch,
auditData) => [
</Button>
),
},
+ {
+ type: (
+ <Button type="primary" disabled={!(sourceData.length > 0)}>
+ <CSVLink data={csvData} filename={fileName}>
+ {i18n.t('pages.GroupDetail.Audit.ExportCSV')}
+ </CSVLink>
+ </Button>
+ ),
+ },
];
-
+const baseSorter = (a, b) => {
+ return a.base - b.base;
+};
+const comparedSorter = (a, b) => {
+ return a.compared - b.compared;
+};
+const subValueSorter = (a, b) => {
+ return a.subValue - b.subValue;
+};
+const groupIdStrSorter = (a, b) => {
+ return a?.inlongGroupId.localeCompare(b.inlongGroupId);
+};
+const streamIdStrSorter = (a, b) => {
+ return a?.inlongStreamId.localeCompare(b?.inlongStreamId);
+};
+const sortOrder: SortOrder = 'descend';
export const getTableColumns = source => {
const data = source.map(item => ({
title: item.auditName,
- dataIndex: item.auditId,
+ dataIndex: source[0].auditId === item.auditId ? 'base' : 'compared',
+ key: source[0].auditId === item.auditId ? 'base' : 'compared',
+ sorter: {
+ compare: source[0].auditId === item.auditId ? baseSorter :
comparedSorter,
+ multiple: source[0].auditId === item.auditId ? 3 : 4,
+ },
render: text => text || 0,
}));
return [
{
title: i18n.t('pages.ModuleAudit.config.InlongGroupId'),
dataIndex: 'inlongGroupId',
+ key: 'inlongGroupId',
+ sorter: {
+ compare: groupIdStrSorter,
+ multiple: 1,
+ },
},
{
title: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
dataIndex: 'inlongStreamId',
+ key: 'inlongStreamId',
+ defaultSortOrder: sortOrder,
+ sorter: {
+ compare: streamIdStrSorter,
+ multiple: 2,
+ },
},
- ].concat(data);
+ ]
+ .concat(data)
+ .concat([
+ {
+ title: i18n.t('pages.ModuleAudit.config.SubValue'),
+ dataIndex: 'subValue',
+ key: 'subValue',
+ sorter: {
+ compare: subValueSorter,
+ multiple: 5,
+ },
+ },
+ ]);
};
diff --git a/inlong-dashboard/src/ui/pages/ModuleAudit/IpModule/index.tsx
b/inlong-dashboard/src/ui/pages/ModuleAudit/IpModule/index.tsx
index ef24a3e7dd..9342298464 100644
--- a/inlong-dashboard/src/ui/pages/ModuleAudit/IpModule/index.tsx
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/IpModule/index.tsx
@@ -17,21 +17,25 @@
* under the License.
*/
-import React, { useMemo, useState } from 'react';
+import React, { useCallback, useEffect, 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 } from './config';
+import i18n from '@/i18n';
import { AuditProps } from '@/ui/pages/ModuleAudit';
+import { Table } from 'antd';
+import { sumSubValue } from '@/ui/pages/ModuleAudit/IdModule/config';
+import dayjs from 'dayjs';
export const ipModule = 'ip';
const Comp: React.FC<AuditProps> = ({ auditData }) => {
const [form] = useForm();
const [query, setQuery] = useState({
- startDate: +new Date(),
- endDate: +new Date(),
+ startDate: dayjs().startOf('hour').valueOf(),
+ endDate: dayjs().startOf('hour').valueOf(),
auditIds: ['3', '4'],
});
@@ -47,33 +51,68 @@ const Comp: React.FC<AuditProps> = ({ auditData }) => {
},
{
refreshDeps: [query],
- formatResult: result => result.sort((a, b) => (a.auditId - b.auditId > 0
? 1 : -1)),
+ formatResult: result => {
+ const base = result.find(item2 => item2.auditId ===
query.auditIds[0].toString());
+ const compared = result.find(item2 => item2.auditId ===
query.auditIds[1].toString());
+ return [base, compared];
+ },
},
);
const sourceDataMap = useMemo(() => {
- const flatArr = sourceData.reduce(
- (acc, cur) =>
- acc.concat(
- cur.auditSet.map(item => ({
- ...item,
- auditId: cur.auditId,
- })),
- ),
- [],
- );
- const output = flatArr.reduce((acc, cur) => {
- if (!acc[cur.inlongStreamId]) {
- acc[cur.inlongStreamId] = {};
- }
- acc[cur.inlongStreamId] = {
- ...acc[cur.inlongStreamId],
- [cur.auditId]: cur.count,
+ if (!sourceData) {
+ return {};
+ }
+ let baseData =
+ sourceData[0]?.auditSet?.length > sourceData[1]?.auditSet?.length
+ ? sourceData[0]
+ : sourceData[1];
+ const output = baseData?.auditSet?.reduce((acc, cur) => {
+ console.log('cur', cur, sourceData[0].auditId, baseData.auditId);
+ acc[cur.inlongGroupId + cur.inlongStreamId] = {
inlongGroupId: cur.inlongGroupId,
inlongStreamId: cur.inlongStreamId,
+ base:
+ sourceData[0].auditId === baseData.auditId
+ ? cur.count
+ : sourceData[0].auditSet.find(item => {
+ return (
+ item.inlongGroupId + item.inlongStreamId ===
+ cur.inlongGroupId + cur.inlongStreamId
+ );
+ })
+ ? sourceData[0].auditSet.find(
+ item =>
+ item.inlongGroupId + item.inlongStreamId ===
+ cur.inlongGroupId + cur.inlongStreamId,
+ ).count
+ : 0,
+ compared:
+ sourceData[1].auditId === baseData.auditId
+ ? cur.count
+ : sourceData[1].auditSet.find(
+ item =>
+ item.inlongGroupId + item.inlongStreamId ===
+ cur.inlongGroupId + cur.inlongStreamId,
+ )
+ ? sourceData[1].auditSet.find(
+ item =>
+ item.inlongGroupId + item.inlongStreamId ===
+ cur.inlongGroupId + cur.inlongStreamId,
+ ).count
+ : 0,
};
return acc;
}, {});
+ if (output === undefined || output === null) {
+ return {};
+ }
+ Object.keys(output).forEach(key => {
+ output[key] = {
+ ...output[key],
+ subValue: output[key].compared - output[key].base,
+ };
+ });
return output;
}, [sourceData]);
@@ -87,26 +126,78 @@ const Comp: React.FC<AuditProps> = ({ auditData }) => {
...query,
...keyword,
ip: keyword.ip,
- auditIds:
- keyword.benchmark !== undefined && keyword.compared !== undefined
- ? [keyword.benchmark, keyword.compared]
- : ['3', '4'],
+ auditIds: [
+ keyword.benchmark !== undefined ? keyword.benchmark :
query.auditIds[0],
+ keyword.compared !== undefined ? keyword.compared : query.auditIds[1],
+ ],
startDate: +keyword.startDate.$d,
endDate: keyword.endDate === undefined ? +keyword.startDate.$d :
+keyword.endDate.$d,
});
};
+ const numToName = useCallback(
+ num => {
+ let obj = {
+ inlongGroupId: i18n.t('pages.ModuleAudit.config.InlongGroupId'),
+ inlongStreamId: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
+ subValue: i18n.t('pages.ModuleAudit.config.SubValue'),
+ base: sourceData[0].auditName,
+ compared: sourceData[1].auditName,
+ };
+ return obj[num];
+ },
+ [sourceData],
+ );
+ const csvData = useMemo(() => {
+ if (!sourceData) {
+ return {};
+ }
+ const result = toTableData(sourceData, sourceDataMap).map(item => {
+ let obj = {};
+ Object.keys(item)
+ .filter(key => key !== 'logTs')
+ .forEach(key => {
+ obj = { ...obj, [numToName(key)]: item[key] };
+ });
+ return obj;
+ });
+ return result;
+ }, [sourceData, sourceDataMap]);
+ const [ip, setIp] = useState('');
+ const [fileName, setFileName] = useState('ip.csv');
+ useEffect(() => {
+ setFileName('ip_' + ip + '.csv');
+ }, [ip]);
return (
<>
<HighTable
filterForm={{
- content: getFormContent(query, onSearch, auditData),
+ style: { gap: '10px' },
+ content: getFormContent(query, onSearch, auditData, sourceData,
csvData, setIp, fileName),
onFilter,
}}
table={{
columns: getTableColumns(sourceData),
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>
+ <Table.Summary.Cell index={1}></Table.Summary.Cell>
+ {sourceData.map((row, index) => (
+ <Table.Summary.Cell key={index} index={index + 2}>
+ {row.auditSet.reduce((total, item) => total + item.count,
0).toLocaleString()}
+ </Table.Summary.Cell>
+ ))}
+ <Table.Summary.Cell key={sourceData.length}
index={sourceData.length - 1}>
+ {sumSubValue(sourceDataMap).toLocaleString()}
+ </Table.Summary.Cell>
+ </Table.Summary.Row>
+ </Table.Summary>
+ ),
}}
/>
</>