From 6673ab4a3a3b2f583e8bd60e9de982fcb3bcee19 Mon Sep 17 00:00:00 2001 From: zixi0825 Date: Sun, 24 Sep 2023 22:38:17 +0800 Subject: [PATCH 1/5] [Feature][UI] Add dataquality dashboard --- .../controller/JobExecutionController.java | 27 +- .../api/dto/bo/job/JobExecutionPageParam.java | 40 ++ .../repository/entity/JobExecution.java | 12 + .../repository/mapper/JobExecutionMapper.java | 9 +- .../service/JobExecutionService.java | 9 +- .../CatalogEntityInstanceServiceImpl.java | 8 +- .../service/impl/JobExecutionServiceImpl.java | 8 +- .../src/main/resources/application.yaml | 4 +- .../resources/mapper/JobExecutionMapper.xml | 32 +- .../components/Database/Detail/dashBoard.tsx | 3 +- .../components/Database/Detail/index.tsx | 2 +- datavines-ui/src/global.less | 6 +- datavines-ui/src/locale/en_US.ts | 567 +++++++++--------- datavines-ui/src/locale/zh_CN.ts | 563 ++++++++--------- datavines-ui/src/router/detailRouter.tsx | 58 +- datavines-ui/src/view/Main/Config/index.tsx | 3 +- .../src/view/Main/ErrorDataManage/index.tsx | 2 +- .../view/Main/HomeDetail/Dashboard/index.tsx | 517 ++++++++++++++++ .../Main/HomeDetail/Jobs/JobsInstance.tsx | 2 +- .../view/Main/HomeDetail/Jobs/JobsList.tsx | 3 +- .../src/view/Main/HomeDetail/index.tsx | 278 ++++----- datavines-ui/src/view/Main/Label/index.tsx | 2 +- .../src/view/Main/UserManage/index.tsx | 2 +- .../view/Main/Warning/SLAsMetric/index.tsx | 2 +- .../Main/Warning/SLAsSetting/Notification.tsx | 2 +- datavines-ui/src/view/Main/Warning/index.tsx | 2 +- scripts/sql/datavines-mysql.sql | 4 + 27 files changed, 1411 insertions(+), 756 deletions(-) create mode 100644 datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionPageParam.java mode change 100644 => 100755 datavines-ui/src/locale/en_US.ts mode change 100644 => 100755 datavines-ui/src/locale/zh_CN.ts mode change 100644 => 100755 datavines-ui/src/router/detailRouter.tsx create mode 100755 datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx mode change 100644 => 100755 datavines-ui/src/view/Main/HomeDetail/index.tsx diff --git a/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java b/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java index c7ecf3ced..d178669e0 100644 --- a/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java +++ b/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java @@ -20,6 +20,7 @@ import io.datavines.core.constant.DataVinesConstants; import io.datavines.core.exception.DataVinesServerException; import io.datavines.common.entity.job.SubmitJob; +import io.datavines.server.api.dto.bo.job.JobExecutionPageParam; import io.datavines.server.api.dto.vo.JobExecutionResultVO; import io.datavines.server.repository.entity.JobExecution; import io.datavines.server.repository.service.JobExecutionErrorDataService; @@ -93,13 +94,27 @@ public Object getJobExecutionResultInfoList(@PathVariable("executionId") Long ex return jobExecutionResultService.getResultVOListByJobExecutionId(executionId); } +// @ApiOperation(value = "get job execution page", response = JobExecutionResultVO.class, responseContainer = "page") +// @GetMapping(value = "/page") +// public Object page(@RequestParam(value = "searchVal", required = false) String searchVal, +// @RequestParam(value ="jobId",required = false) Long jobId, +// @RequestParam(value ="metricType",required = false) String metricType, +// @RequestParam(value ="schemaName",required = false) String schemaName, +// @RequestParam(value ="tableName",required = false) String tableName, +// @RequestParam(value ="columnName",required = false) String columnName, +// @RequestParam(value ="status",required = false) String status, +// @RequestParam(value ="startTime",required = false) String startTime, +// @RequestParam(value ="endTime",required = false) String endTime, +// @RequestParam("pageNumber") Integer pageNumber, +// @RequestParam("pageSize") Integer pageSize) { +// return jobExecutionService.getJobExecutionPage(searchVal, jobId, metricType, schemaName, tableName, columnName, startTime, endTime, pageNumber, pageSize); +// } + @ApiOperation(value = "get job execution page", response = JobExecutionResultVO.class, responseContainer = "page") - @GetMapping(value = "/page") - public Object page(@RequestParam(value = "searchVal", required = false) String searchVal, - @RequestParam("jobId") Long jobId, - @RequestParam("pageNumber") Integer pageNumber, - @RequestParam("pageSize") Integer pageSize) { - return jobExecutionService.getJobExecutionPage(searchVal, jobId, pageNumber, pageSize); + @PostMapping(value = "/page") + public Object page(@Valid @RequestBody JobExecutionPageParam jobExecutionPageParam) { + log.info("pageParam : {}", jobExecutionPageParam); + return jobExecutionService.getJobExecutionPage(jobExecutionPageParam); } @ApiOperation(value = "get job execution error data page", response = Object.class, responseContainer = "page") diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionPageParam.java b/datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionPageParam.java new file mode 100644 index 000000000..900871084 --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionPageParam.java @@ -0,0 +1,40 @@ +/* + * 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. + */ +package io.datavines.server.api.dto.bo.job; + +import lombok.Data; +import javax.validation.constraints.NotNull; + +@Data +@NotNull(message = "JobExecutionPageParam cannot be null") +public class JobExecutionPageParam { + + Long datasourceId; + + Integer status; + String searchVal; + Long jobId; + String metricType; + String schemaName; + String tableName; + String columnName; + String startTime; + String endTime; + Integer pageNumber; + Integer pageSize; + +} diff --git a/datavines-server/src/main/java/io/datavines/server/repository/entity/JobExecution.java b/datavines-server/src/main/java/io/datavines/server/repository/entity/JobExecution.java index 1776e1c89..b1e9da92c 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/entity/JobExecution.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/entity/JobExecution.java @@ -48,6 +48,18 @@ public class JobExecution implements Serializable { @TableField(value = "job_type") private JobType jobType; + @TableField(value = "schema_name") + private String schemaName; + + @TableField(value = "table_name") + private String tableName; + + @TableField(value = "column_name") + private String columnName; + + @TableField(value = "metric_type") + private String metricType; + @TableField(value = "datasource_id") private Long dataSourceId; diff --git a/datavines-server/src/main/java/io/datavines/server/repository/mapper/JobExecutionMapper.java b/datavines-server/src/main/java/io/datavines/server/repository/mapper/JobExecutionMapper.java index 88443dfc3..cbf34b318 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/mapper/JobExecutionMapper.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/mapper/JobExecutionMapper.java @@ -34,6 +34,11 @@ public interface JobExecutionMapper extends BaseMapper { List listByJobId(long jobId); IPage getJobExecutionPage(Page page, - @Param("searchVal") String searchVal, - @Param("jobId") Long jobId); + @Param("searchVal") String searchVal, + @Param("jobId") Long jobId, + @Param("datasourceId") Long datasourceId, + @Param("status") int status, + @Param("metricType") String metricType, @Param("schemaName") String schemaName, + @Param("tableName") String tableName, @Param("columnName") String columnName, + @Param("startTime") String startTime, @Param("endTime") String endTime); } diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java b/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java index 4524bc978..31a0a38ee 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java @@ -21,6 +21,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import io.datavines.common.entity.job.SubmitJob; +import io.datavines.server.api.dto.bo.job.JobExecutionPageParam; import io.datavines.server.api.dto.vo.JobExecutionVO; import io.datavines.server.api.dto.vo.MetricExecutionDashBoard; import io.datavines.server.repository.entity.JobExecution; @@ -38,7 +39,13 @@ public interface JobExecutionService extends IService { int deleteByJobId(long jobId); - IPage getJobExecutionPage(String searchVal, Long jobId, Integer pageNumber, Integer pageSize); +// IPage getJobExecutionPage(String searchVal, Long jobId, +// String metricType, String schemaName, +// String tableName, String columnName, +// String startTime, String endTime, +// Integer pageNumber, Integer pageSize); + + IPage getJobExecutionPage(JobExecutionPageParam pageParam); Long submitJob(SubmitJob submitJob) throws DataVinesServerException; diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/CatalogEntityInstanceServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/CatalogEntityInstanceServiceImpl.java index 8e5b514fd..3d4ac6a38 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/CatalogEntityInstanceServiceImpl.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/CatalogEntityInstanceServiceImpl.java @@ -34,6 +34,7 @@ import io.datavines.server.api.dto.bo.catalog.profile.RunProfileRequest; import io.datavines.server.api.dto.bo.job.DataProfileJobCreateOrUpdate; import io.datavines.server.api.dto.bo.job.JobCreateWithEntityUuid; +import io.datavines.server.api.dto.bo.job.JobExecutionPageParam; import io.datavines.server.api.dto.vo.DataTime2ValueItem; import io.datavines.server.api.dto.vo.JobExecutionVO; import io.datavines.server.api.dto.vo.catalog.*; @@ -990,6 +991,11 @@ public IPage profileJobExecutionPage(String uuid, Integer pageNu CatalogEntityMetricJobRel rel = list.get(0); Long jobId = rel.getMetricJobId(); - return jobExecutionService.getJobExecutionPage("", jobId, pageNumber, pageSize); + JobExecutionPageParam pageParam = new JobExecutionPageParam(); + pageParam.setPageNumber(pageNumber); + pageParam.setPageSize(pageSize); + pageParam.setJobId(jobId); + + return jobExecutionService.getJobExecutionPage(pageParam); } } diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java index b92d165ea..a42a68121 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java @@ -29,6 +29,7 @@ import io.datavines.core.enums.Status; import io.datavines.metric.api.ResultFormula; import io.datavines.common.entity.job.SubmitJob; +import io.datavines.server.api.dto.bo.job.JobExecutionPageParam; import io.datavines.server.api.dto.vo.JobExecutionVO; import io.datavines.core.exception.DataVinesServerException; import io.datavines.server.api.dto.vo.MetricExecutionDashBoard; @@ -107,10 +108,9 @@ public int deleteByJobId(long jobId) { } @Override - public IPage getJobExecutionPage(String searchVal, Long jobId, Integer pageNumber, Integer pageSize) { - Page page = new Page<>(pageNumber, pageSize); - IPage jobs = baseMapper.getJobExecutionPage(page, searchVal, jobId); - return jobs; + public IPage getJobExecutionPage(JobExecutionPageParam pageParam) { + Page page = new Page<>(pageParam.getPageNumber(), pageParam.getPageSize()); + return baseMapper.getJobExecutionPage(page, pageParam.getSearchVal(), pageParam.getJobId() , pageParam.getDatasourceId(), pageParam.getStatus(), pageParam.getMetricType(), pageParam.getSchemaName(), pageParam.getTableName(),pageParam.getColumnName(), pageParam.getStartTime(), pageParam.getEndTime()); } @Override diff --git a/datavines-server/src/main/resources/application.yaml b/datavines-server/src/main/resources/application.yaml index 383521ff5..0575399bd 100644 --- a/datavines-server/src/main/resources/application.yaml +++ b/datavines-server/src/main/resources/application.yaml @@ -87,9 +87,9 @@ spring: on-profile: mysql datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://127.0.0.1:3306/datavines?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai + url: jdbc:mysql://116.205.236.108:3306/datavines?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai username: root - password: 123456 + password: vines@0825 quartz: properties: org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate \ No newline at end of file diff --git a/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml b/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml index b06b8331c..270db91c9 100644 --- a/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml +++ b/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml @@ -21,7 +21,37 @@ - select * from dv_job_execution where job_id = #{jobId} + select * from dv_job_execution + + + and job_id = #{jobId} + + + and metric_type = #{metricType} + + + and schema_name = #{schemaName} + + + and table_name = #{tableName} + + + and column_name = #{columnName} + + + and update_time >= #{startTime} + + + and update_time <= #{endTime} + + + and datasource_id = #{datasourceId} + + + and status = #{status} + + + + 开始时间 + 结束时间 + + + + +
+ + + + {intl.formatMessage({ id: 'config_title' })} + + + + + + {intl.formatMessage({ id: 'config_title' })} + + + + + + + + {intl.formatMessage({ id: 'config_title' })} + + + size="middle" + loading={loading} + rowKey="id" + columns={columns} + dataSource={tableData.list || []} + onChange={onPageChange} + pagination={{ + size: 'small', + total: tableData.total, + showSizeChanger: true, + current: pageParam.pageNumber, + pageSize: pageParam.pageSize, + }} + /> + + + + + +
+ + ); +}; + +export default Dashboard; diff --git a/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx b/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx index 7510008c7..94dc98d70 100644 --- a/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx +++ b/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx @@ -131,7 +131,7 @@ const JobsInstance = () => { ]; return (
{ + console.log("datasourceId:" ,datasourceId); const intl = useIntl(); const form = Form.useForm()[0]; const [loading, setLoading] = useState(false); @@ -248,7 +249,7 @@ const Jobs = ({ datasourceId }: TJobs) => { }, ]; return ( -
+
{/* {intl.formatMessage({ id: 'jobs_list' })} */}
diff --git a/datavines-ui/src/view/Main/HomeDetail/index.tsx b/datavines-ui/src/view/Main/HomeDetail/index.tsx old mode 100644 new mode 100755 index f7ed8238b..b4bab2d8c --- a/datavines-ui/src/view/Main/HomeDetail/index.tsx +++ b/datavines-ui/src/view/Main/HomeDetail/index.tsx @@ -1,135 +1,143 @@ -import React, { useEffect, useState } from 'react'; -import useRoute from 'src/router/useRoute'; -import { useIntl } from 'react-intl'; -import { - Route, Switch, useHistory, useLocation, useParams, useRouteMatch, -} from 'react-router-dom'; -import { ArrowLeftOutlined } from '@ant-design/icons'; -import { Button } from 'antd'; -import { CustomSelect } from '@Editor/common'; -import { useSelector } from 'react-redux'; -import { MenuItem } from '@/component/Menu/MenuAside'; -import MenuLayout from '@/component/Menu/Layout'; -import { $http } from '@/http'; -import store from '@/store'; -import EditorData from '@/view/Main/HomeDetail/EditorData'; -import Jobs from '@/view/Main/HomeDetail/Jobs'; - -type DataSource = { - id:number, - name:'hellow' -} -const DetailMain = () => { - const { isDetailPage } = useSelector((r:any) => r.commonReducer); - const { workspaceId } = useSelector((r:any) => r.workSpaceReducer); - const { editType } = useSelector((r:any) => r.datasourceReducer); - const location = useLocation(); - const [dataSourceList, setDataSourceList] = useState([]); - const params = useParams<{ id: string}>(); - useEffect(() => { - if (isDetailPage) { - try { - $http.get('/datasource/page', { - workSpaceId: workspaceId, - pageNumber: 1, - pageSize: 9999, - }).then((res) => { - setDataSourceList(res?.records || []); - }); - } catch (error) { - console.log('error', error); - } - } - }, [isDetailPage]); - const match = useRouteMatch(); - const intl = useIntl(); - const { detailRoutes, visible } = useRoute(); - if (!visible || !detailRoutes.length) { - return null; - } - const detailMenus = detailRoutes.map((item) => ({ - ...item, - key: item.path.replace(/:id/, (match.params as any).id || ''), - label: intl.formatMessage({ id: item.path as any }), - })) as MenuItem[]; - const generateRoute = (menusArray: MenuItem[]) => menusArray.map((route) => ( - - )); - const history = useHistory(); - const goBack = () => { - // eslint-disable-next-line no-unused-expressions - location.pathname.includes('jobs/instance') ? history.goBack() : history.push('/main/home'); - }; - const onChangeDataSource = (id: string) => { - const url = `${match.path}`.replace(/:id/, id); - history.push(`${url}/editor`); - }; - const changeType = () => { - store.dispatch({ - type: 'save_datasource_editType', - payload: !editType, - }); - }; - const renderDataSourcSelect = () => ( - option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} - source={dataSourceList} - value={params?.id ? +params.id : undefined} - onChange={onChangeDataSource} - sourceLabelMap="name" - sourceValueMap="id" - /> - ); - const renderTopContent = () => ( -
-
- -
- {renderDataSourcSelect()} - {!location.pathname.includes('jobs') ? ( - - ) : ''} - -
- ); - return ( - - - {renderTopContent()} -
- -
-
- -
-
- - ); -}; - -// export default App; -export default DetailMain; +import React, { useEffect, useState } from 'react'; +import useRoute from 'src/router/useRoute'; +import { useIntl } from 'react-intl'; +import { + Route, Switch, useHistory, useLocation, useParams, useRouteMatch, +} from 'react-router-dom'; +import { ArrowLeftOutlined } from '@ant-design/icons'; +import { Button } from 'antd'; +import { CustomSelect } from '@Editor/common'; +import { useSelector } from 'react-redux'; +import { MenuItem } from '@/component/Menu/MenuAside'; +import MenuLayout from '@/component/Menu/Layout'; +import { $http } from '@/http'; +import store from '@/store'; +import EditorData from '@/view/Main/HomeDetail/EditorData'; +import Jobs from '@/view/Main/HomeDetail/Jobs'; +import Dashboard from "view/Main/HomeDetail/Dashboard"; + +type DataSource = { + id:number, + name:'hellow' +} +const DetailMain = () => { + const { isDetailPage } = useSelector((r:any) => r.commonReducer); + const { workspaceId } = useSelector((r:any) => r.workSpaceReducer); + const { editType } = useSelector((r:any) => r.datasourceReducer); + const location = useLocation(); + const [dataSourceList, setDataSourceList] = useState([]); + const params = useParams<{ id: string}>(); + useEffect(() => { + if (isDetailPage) { + try { + $http.get('/datasource/page', { + workSpaceId: workspaceId, + pageNumber: 1, + pageSize: 9999, + }).then((res) => { + setDataSourceList(res?.records || []); + }); + } catch (error) { + console.log('error', error); + } + } + }, [isDetailPage]); + const match = useRouteMatch(); + const intl = useIntl(); + const { detailRoutes, visible } = useRoute(); + if (!visible || !detailRoutes.length) { + return null; + } + const detailMenus = detailRoutes.map((item) => ({ + ...item, + key: item.path.replace(/:id/, (match.params as any).id || ''), + label: intl.formatMessage({ id: item.path as any }), + })) as MenuItem[]; + const generateRoute = (menusArray: MenuItem[]) => menusArray.map((route) => ( + + )); + const history = useHistory(); + const goBack = () => { + // eslint-disable-next-line no-unused-expressions + location.pathname.includes('jobs/instance') ? history.goBack() : history.push('/main/home'); + }; + const onChangeDataSource = (id: string) => { + const url = `${match.path}`.replace(/:id/, id); + history.push(`${url}/editor`); + }; + const changeType = () => { + store.dispatch({ + type: 'save_datasource_editType', + payload: !editType, + }); + }; + const renderDataSourcSelect = () => ( + option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} + source={dataSourceList} + value={params?.id ? +params.id : undefined} + onChange={onChangeDataSource} + sourceLabelMap="name" + sourceValueMap="id" + /> + ); + const renderTopContent = () => ( +
+
+ +
+ {renderDataSourcSelect()} + {!location.pathname.includes('jobs') ? ( + + ) : ''} + +
+ ); + return ( + + + {renderTopContent()} +
+ +
+ +
+ +
+
+ +
+
+ + ); +}; + +// export default App; +export default DetailMain; diff --git a/datavines-ui/src/view/Main/Label/index.tsx b/datavines-ui/src/view/Main/Label/index.tsx index 7f022c393..07eabb902 100644 --- a/datavines-ui/src/view/Main/Label/index.tsx +++ b/datavines-ui/src/view/Main/Label/index.tsx @@ -92,7 +92,7 @@ const Index = () => { getTagList(tagCategoryList[index].uuid); }; return ( -
+

{intl.formatMessage({ id: 'label_title' })} diff --git a/datavines-ui/src/view/Main/UserManage/index.tsx b/datavines-ui/src/view/Main/UserManage/index.tsx index 900f611d5..973ce746b 100644 --- a/datavines-ui/src/view/Main/UserManage/index.tsx +++ b/datavines-ui/src/view/Main/UserManage/index.tsx @@ -106,7 +106,7 @@ const Index = () => { ]; return (

{ ]; return (
{ }, ]; return ( -
+
diff --git a/datavines-ui/src/view/Main/Warning/index.tsx b/datavines-ui/src/view/Main/Warning/index.tsx index e74fdc9fe..42b75e699 100644 --- a/datavines-ui/src/view/Main/Warning/index.tsx +++ b/datavines-ui/src/view/Main/Warning/index.tsx @@ -8,7 +8,7 @@ import SLAsSetting from './SLAsSetting'; const Index = () => { const match = useRouteMatch(); return ( -
+
diff --git a/scripts/sql/datavines-mysql.sql b/scripts/sql/datavines-mysql.sql index 663269451..2806b6567 100644 --- a/scripts/sql/datavines-mysql.sql +++ b/scripts/sql/datavines-mysql.sql @@ -525,6 +525,10 @@ CREATE TABLE `dv_job_execution` ( `name` varchar(255) NOT NULL COMMENT '作业运行实例名称', `job_id` bigint(20) NOT NULL DEFAULT '-1' COMMENT '作业ID', `job_type` int(11) NOT NULL DEFAULT '0' COMMENT '作业类型', + `schema_name` varchar(128) DEFAULT NULL COMMENT '数据库名', + `table_name` varchar(128) DEFAULT NULL COMMENT '表名', + `column_name` varchar(128) DEFAULT NULL COMMENT '列名', + `metric_type` varchar(255) DEFAULT NULL COMMENT '规则类型', `datasource_id` bigint(20) NOT NULL DEFAULT '-1' COMMENT '数据源ID', `execute_platform_type` varchar(128) DEFAULT NULL COMMENT '运行平台类型', `execute_platform_parameter` text COMMENT '运行平台参数', From 5e6b82cd0e10a11fdec9520a2ef35853d429de07 Mon Sep 17 00:00:00 2001 From: zixi0825 Date: Wed, 27 Sep 2023 22:13:07 +0800 Subject: [PATCH 2/5] [Feature][UI] Add dataquality dashboard (1) --- .../metric/expected/plugin/Last30DayAvg.java | 132 +++---- .../metric/expected/plugin/WeeklyAvg.java | 132 +++---- .../metric/plugin/CustomAggregateSql.java | 217 +++++------ .../controller/JobExecutionController.java | 251 +++++++------ .../bo/job/JobExecutionDashboardParam.java | 40 +++ .../api/dto/bo/job/JobExecutionPageParam.java | 90 ++--- .../api/dto/vo/JobExecutionAggItem.java | 11 + .../api/dto/vo/JobExecutionTrendBar.java | 17 + .../api/dto/vo/JobExecutionTrendBarItem.java | 12 + .../repository/mapper/JobExecutionMapper.java | 13 +- .../service/JobExecutionService.java | 14 +- .../service/impl/JobExecutionServiceImpl.java | 158 +++++++- .../src/main/resources/application.yaml | 4 +- .../resources/mapper/JobExecutionMapper.xml | 62 ++++ .../components/Database/Detail/dashBoard.tsx | 6 +- .../view/Main/HomeDetail/Dashboard/index.tsx | 303 +++++++++------- .../Main/HomeDetail/Jobs/JobsInstance.tsx | 336 +++++++++--------- 17 files changed, 1076 insertions(+), 722 deletions(-) mode change 100644 => 100755 datavines-metric/datavines-metric-expected-plugins/datavines-metric-expected-last30day-avg/src/main/java/io/datavines/metric/expected/plugin/Last30DayAvg.java mode change 100644 => 100755 datavines-metric/datavines-metric-expected-plugins/datavines-metric-expected-weekly-avg/src/main/java/io/datavines/metric/expected/plugin/WeeklyAvg.java mode change 100644 => 100755 datavines-metric/datavines-metric-plugins/datavines-metric-custom-aggregate-sql/src/main/java/io/datavines/metric/plugin/CustomAggregateSql.java mode change 100644 => 100755 datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java create mode 100755 datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionDashboardParam.java mode change 100644 => 100755 datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionPageParam.java create mode 100644 datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionAggItem.java create mode 100644 datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBar.java create mode 100644 datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBarItem.java mode change 100644 => 100755 datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx diff --git a/datavines-metric/datavines-metric-expected-plugins/datavines-metric-expected-last30day-avg/src/main/java/io/datavines/metric/expected/plugin/Last30DayAvg.java b/datavines-metric/datavines-metric-expected-plugins/datavines-metric-expected-last30day-avg/src/main/java/io/datavines/metric/expected/plugin/Last30DayAvg.java old mode 100644 new mode 100755 index 1f969ccae..33ad121bd --- a/datavines-metric/datavines-metric-expected-plugins/datavines-metric-expected-last30day-avg/src/main/java/io/datavines/metric/expected/plugin/Last30DayAvg.java +++ b/datavines-metric/datavines-metric-expected-plugins/datavines-metric-expected-last30day-avg/src/main/java/io/datavines/metric/expected/plugin/Last30DayAvg.java @@ -1,66 +1,66 @@ -/* - * 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. - */ -package io.datavines.metric.expected.plugin; - -import io.datavines.metric.api.ExpectedValue; - -import java.util.Map; - -import static io.datavines.common.ConfigConstants.METRIC_UNIQUE_KEY; - -public class Last30DayAvg implements ExpectedValue { - - @Override - public String getName() { - return "last_30d_avg"; - } - - @Override - public String getZhName() { - return "最近30天均值"; - } - - @Override - public String getKey(Map inputParameter) { - String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); - return "expected_value_" + uniqueKey; - } - - @Override - public String getExecuteSql(Map inputParameter) { - String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); - return "select round(avg(actual_value),2) as last_30d_avg" + "expected_value_" + uniqueKey + - " from dv_actual_values where data_time >= date_sub(date_format(${data_time},'%Y-%m-%d'),interval 30 DAY)" + - " and data_time < date_add(date_format(${data_time},'%Y-%m-%d'),interval 1 DAY) and unique_code = ${unique_code}"; - } - - @Override - public String getOutputTable(Map inputParameter) { - String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); - return "last_30d_" + uniqueKey; - } - - @Override - public boolean isNeedDefaultDatasource() { - return true; - } - - @Override - public void prepare(Map config) { - - } -} +/* + * 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. + */ +package io.datavines.metric.expected.plugin; + +import io.datavines.metric.api.ExpectedValue; + +import java.util.Map; + +import static io.datavines.common.ConfigConstants.METRIC_UNIQUE_KEY; + +public class Last30DayAvg implements ExpectedValue { + + @Override + public String getName() { + return "last_30d_avg"; + } + + @Override + public String getZhName() { + return "最近30天均值"; + } + + @Override + public String getKey(Map inputParameter) { + String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); + return "expected_value_" + uniqueKey; + } + + @Override + public String getExecuteSql(Map inputParameter) { + String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); + return "select round(avg(actual_value),2) as expected_value_" + uniqueKey + + " from dv_actual_values where data_time >= date_sub(date_format(${data_time},'%Y-%m-%d'),interval 30 DAY)" + + " and data_time < date_add(date_format(${data_time},'%Y-%m-%d'),interval 1 DAY) and unique_code = ${unique_code}"; + } + + @Override + public String getOutputTable(Map inputParameter) { + String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); + return "last_30d_" + uniqueKey; + } + + @Override + public boolean isNeedDefaultDatasource() { + return true; + } + + @Override + public void prepare(Map config) { + + } +} diff --git a/datavines-metric/datavines-metric-expected-plugins/datavines-metric-expected-weekly-avg/src/main/java/io/datavines/metric/expected/plugin/WeeklyAvg.java b/datavines-metric/datavines-metric-expected-plugins/datavines-metric-expected-weekly-avg/src/main/java/io/datavines/metric/expected/plugin/WeeklyAvg.java old mode 100644 new mode 100755 index 468f608d2..08843d370 --- a/datavines-metric/datavines-metric-expected-plugins/datavines-metric-expected-weekly-avg/src/main/java/io/datavines/metric/expected/plugin/WeeklyAvg.java +++ b/datavines-metric/datavines-metric-expected-plugins/datavines-metric-expected-weekly-avg/src/main/java/io/datavines/metric/expected/plugin/WeeklyAvg.java @@ -1,66 +1,66 @@ -/* - * 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. - */ -package io.datavines.metric.expected.plugin; - -import io.datavines.metric.api.ExpectedValue; - -import java.util.Map; - -import static io.datavines.common.ConfigConstants.METRIC_UNIQUE_KEY; - -public class WeeklyAvg implements ExpectedValue { - - @Override - public String getName() { - return "weekly_avg"; - } - - @Override - public String getKey(Map inputParameter) { - String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); - return "expected_value_" + uniqueKey; - } - - @Override - public String getZhName() { - return "周均值"; - } - - @Override - public String getExecuteSql(Map inputParameter) { - String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); - return "select round(avg(actual_value),2) as weekly_avg" + "expected_value_" + uniqueKey + - " from dv_actual_values where data_time >= date_sub(${data_time},interval weekday(${data_time}) + 0 day)" + - " and data_time < date_add(date_format(${data_time},'%Y-%m-%d'),interval 1 DAY) and unique_code = ${unique_code}"; - } - - @Override - public String getOutputTable(Map inputParameter) { - String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); - return "weekly_range_" + uniqueKey; - } - - @Override - public boolean isNeedDefaultDatasource() { - return true; - } - - @Override - public void prepare(Map config) { - - } -} +/* + * 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. + */ +package io.datavines.metric.expected.plugin; + +import io.datavines.metric.api.ExpectedValue; + +import java.util.Map; + +import static io.datavines.common.ConfigConstants.METRIC_UNIQUE_KEY; + +public class WeeklyAvg implements ExpectedValue { + + @Override + public String getName() { + return "weekly_avg"; + } + + @Override + public String getKey(Map inputParameter) { + String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); + return "expected_value_" + uniqueKey; + } + + @Override + public String getZhName() { + return "周均值"; + } + + @Override + public String getExecuteSql(Map inputParameter) { + String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); + return "select round(avg(actual_value),2) as expected_value_" + uniqueKey + + " from dv_actual_values where data_time >= date_sub(${data_time},interval weekday(${data_time}) + 0 day)" + + " and data_time < date_add(date_format(${data_time},'%Y-%m-%d'),interval 1 DAY) and unique_code = ${unique_code}"; + } + + @Override + public String getOutputTable(Map inputParameter) { + String uniqueKey = inputParameter.get(METRIC_UNIQUE_KEY); + return "weekly_range_" + uniqueKey; + } + + @Override + public boolean isNeedDefaultDatasource() { + return true; + } + + @Override + public void prepare(Map config) { + + } +} diff --git a/datavines-metric/datavines-metric-plugins/datavines-metric-custom-aggregate-sql/src/main/java/io/datavines/metric/plugin/CustomAggregateSql.java b/datavines-metric/datavines-metric-plugins/datavines-metric-custom-aggregate-sql/src/main/java/io/datavines/metric/plugin/CustomAggregateSql.java old mode 100644 new mode 100755 index bd58307c3..ff46f7e8d --- a/datavines-metric/datavines-metric-plugins/datavines-metric-custom-aggregate-sql/src/main/java/io/datavines/metric/plugin/CustomAggregateSql.java +++ b/datavines-metric/datavines-metric-plugins/datavines-metric-custom-aggregate-sql/src/main/java/io/datavines/metric/plugin/CustomAggregateSql.java @@ -1,108 +1,109 @@ -/* - * 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. - */ -package io.datavines.metric.plugin; - -import java.util.*; - -import io.datavines.common.config.CheckResult; -import io.datavines.common.config.ConfigChecker; -import io.datavines.common.entity.ExecuteSql; -import io.datavines.common.enums.DataVinesDataType; -import io.datavines.common.utils.StringUtils; -import io.datavines.metric.api.ConfigItem; -import io.datavines.metric.api.MetricDimension; -import io.datavines.metric.api.MetricType; -import io.datavines.metric.api.SqlMetric; - -import static io.datavines.common.CommonConstants.TABLE; -import static io.datavines.common.ConfigConstants.*; -import static io.datavines.common.ConfigConstants.METRIC_UNIQUE_KEY; - -public class CustomAggregateSql implements SqlMetric { - - private final Set requiredOptions = new HashSet<>(); - - private final HashMap configMap = new HashMap<>(); - - public CustomAggregateSql() { - configMap.put("table",new ConfigItem("table", "表名", "table")); - configMap.put("actual_aggregate_sql", new ConfigItem("actual_aggregate_sql","自定义聚合SQL","actual_aggregate_sql")); - - requiredOptions.add("actual_aggregate_sql"); - requiredOptions.add("table"); - } - - @Override - public String getName() { - return "custom_aggregate_sql"; - } - - @Override - public String getZhName() { - return "自定义聚合SQL"; - } - - @Override - public MetricDimension getDimension() { - return MetricDimension.ACCURACY; - } - - @Override - public MetricType getType() { - return MetricType.SINGLE_TABLE; - } - - @Override - public boolean isInvalidateItemsCanOutput() { - return false; - } - - @Override - public CheckResult validateConfig(Map config) { - return ConfigChecker.checkConfig(config, requiredOptions); - } - - @Override - public void prepare(Map config) { - - } - - @Override - public Map getConfigMap() { - return configMap; - } - - @Override - public ExecuteSql getInvalidateItems(Map inputParameter) { - return null; - } - - @Override - public ExecuteSql getActualValue(Map inputParameter) { - inputParameter.put(ACTUAL_TABLE, inputParameter.get(TABLE)); - String actualAggregateSql = inputParameter.get(ACTUAL_AGGREGATE_SQL); - if (StringUtils.isNotEmpty(actualAggregateSql)) { - actualAggregateSql = actualAggregateSql.replace("as actual_value", "as actual_value_" + inputParameter.get(METRIC_UNIQUE_KEY)); - } - return new ExecuteSql(actualAggregateSql, inputParameter.get(TABLE)); - } - - @Override - public List suitableType() { - return Collections.emptyList(); - } -} +/* + * 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. + */ +package io.datavines.metric.plugin; + +import java.util.*; + +import io.datavines.common.config.CheckResult; +import io.datavines.common.config.ConfigChecker; +import io.datavines.common.entity.ExecuteSql; +import io.datavines.common.enums.DataVinesDataType; +import io.datavines.common.utils.StringUtils; +import io.datavines.metric.api.ConfigItem; +import io.datavines.metric.api.MetricDimension; +import io.datavines.metric.api.MetricType; +import io.datavines.metric.api.SqlMetric; + +import static io.datavines.common.CommonConstants.TABLE; +import static io.datavines.common.ConfigConstants.*; +import static io.datavines.common.ConfigConstants.METRIC_UNIQUE_KEY; + +public class CustomAggregateSql implements SqlMetric { + + private final Set requiredOptions = new HashSet<>(); + + private final HashMap configMap = new HashMap<>(); + + public CustomAggregateSql() { + configMap.put("table",new ConfigItem("table", "表名", "table")); + configMap.put("actual_aggregate_sql", new ConfigItem("actual_aggregate_sql","自定义聚合SQL","actual_aggregate_sql")); + configMap.put("filter",new ConfigItem("filter", "过滤条件", "filter")); + + requiredOptions.add("actual_aggregate_sql"); + requiredOptions.add("table"); + } + + @Override + public String getName() { + return "custom_aggregate_sql"; + } + + @Override + public String getZhName() { + return "自定义聚合SQL"; + } + + @Override + public MetricDimension getDimension() { + return MetricDimension.ACCURACY; + } + + @Override + public MetricType getType() { + return MetricType.SINGLE_TABLE; + } + + @Override + public boolean isInvalidateItemsCanOutput() { + return false; + } + + @Override + public CheckResult validateConfig(Map config) { + return ConfigChecker.checkConfig(config, requiredOptions); + } + + @Override + public void prepare(Map config) { + + } + + @Override + public Map getConfigMap() { + return configMap; + } + + @Override + public ExecuteSql getInvalidateItems(Map inputParameter) { + return null; + } + + @Override + public ExecuteSql getActualValue(Map inputParameter) { + inputParameter.put(ACTUAL_TABLE, inputParameter.get(TABLE)); + String actualAggregateSql = inputParameter.get(ACTUAL_AGGREGATE_SQL); + if (StringUtils.isNotEmpty(actualAggregateSql)) { + actualAggregateSql = actualAggregateSql.replace("as actual_value", "as actual_value_" + inputParameter.get(METRIC_UNIQUE_KEY)); + } + return new ExecuteSql(actualAggregateSql, inputParameter.get(TABLE)); + } + + @Override + public List suitableType() { + return Collections.emptyList(); + } +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java b/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java old mode 100644 new mode 100755 index d178669e0..0666a5184 --- a/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java +++ b/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java @@ -1,127 +1,124 @@ -/* - * 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. - */ -package io.datavines.server.api.controller; - -import io.datavines.core.aop.RefreshToken; -import io.datavines.core.constant.DataVinesConstants; -import io.datavines.core.exception.DataVinesServerException; -import io.datavines.common.entity.job.SubmitJob; -import io.datavines.server.api.dto.bo.job.JobExecutionPageParam; -import io.datavines.server.api.dto.vo.JobExecutionResultVO; -import io.datavines.server.repository.entity.JobExecution; -import io.datavines.server.repository.service.JobExecutionErrorDataService; -import io.datavines.server.repository.service.JobExecutionResultService; -import io.datavines.server.repository.service.JobExecutionService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; - -@Slf4j -@Api(value = "job", tags = "job", produces = MediaType.APPLICATION_JSON_VALUE) -@RestController -@RequestMapping(value = DataVinesConstants.BASE_API_PATH + "/job/execution", produces = MediaType.APPLICATION_JSON_VALUE) -@RefreshToken -public class JobExecutionController { - - @Autowired - private JobExecutionService jobExecutionService; - - @Autowired - private JobExecutionResultService jobExecutionResultService; - - @Autowired - private JobExecutionErrorDataService jobExecutionErrorDataService; - - @ApiOperation(value = "submit external data quality job", response = Long.class) - @PostMapping(value = "/submit/data-quality", consumes = MediaType.APPLICATION_JSON_VALUE) - public Object submitDataQualityJob(@Valid @RequestBody SubmitJob submitJob) throws DataVinesServerException { - return jobExecutionService.submitJob(submitJob); - } - - @ApiOperation(value = "submit external data reconciliation job", response = Long.class) - @PostMapping(value = "/submit/data-reconciliation", consumes = MediaType.APPLICATION_JSON_VALUE) - public Object submitDataReconJob(@Valid @RequestBody SubmitJob submitJob) throws DataVinesServerException { - return jobExecutionService.submitJob(submitJob); - } - - @ApiOperation(value = "kill job", response = Long.class) - @DeleteMapping(value = "/kill/{executionId}") - public Object killTask(@PathVariable("executionId") Long executionId) { - return jobExecutionService.killJob(executionId); - } - - @ApiOperation(value = "get job execution status", response = String.class) - @GetMapping(value = "/status/{executionId}") - public Object getTaskStatus(@PathVariable("executionId") Long executionId) { - return jobExecutionService.getById(executionId).getStatus().getDescription(); - } - - @ApiOperation(value = "get job execution list by job id", response = JobExecution.class, responseContainer = "list") - @GetMapping(value = "/list/{jobId}") - public Object getJobExecutionListByJobId(@PathVariable("jobId") Long jobId) { - return jobExecutionService.listByJobId(jobId); - } - - @Deprecated - @ApiOperation(value = "get job execution result", response = JobExecutionResultVO.class) - @GetMapping(value = "/result/{executionId}") - public Object getJobExecutionResultInfo(@PathVariable("executionId") Long executionId) { - return jobExecutionResultService.getResultVOByJobExecutionId(executionId); - } - - @ApiOperation(value = "get job execution result", response = JobExecutionResultVO.class) - @GetMapping(value = "/list/result/{executionId}") - public Object getJobExecutionResultInfoList(@PathVariable("executionId") Long executionId) { - return jobExecutionResultService.getResultVOListByJobExecutionId(executionId); - } - -// @ApiOperation(value = "get job execution page", response = JobExecutionResultVO.class, responseContainer = "page") -// @GetMapping(value = "/page") -// public Object page(@RequestParam(value = "searchVal", required = false) String searchVal, -// @RequestParam(value ="jobId",required = false) Long jobId, -// @RequestParam(value ="metricType",required = false) String metricType, -// @RequestParam(value ="schemaName",required = false) String schemaName, -// @RequestParam(value ="tableName",required = false) String tableName, -// @RequestParam(value ="columnName",required = false) String columnName, -// @RequestParam(value ="status",required = false) String status, -// @RequestParam(value ="startTime",required = false) String startTime, -// @RequestParam(value ="endTime",required = false) String endTime, -// @RequestParam("pageNumber") Integer pageNumber, -// @RequestParam("pageSize") Integer pageSize) { -// return jobExecutionService.getJobExecutionPage(searchVal, jobId, metricType, schemaName, tableName, columnName, startTime, endTime, pageNumber, pageSize); -// } - - @ApiOperation(value = "get job execution page", response = JobExecutionResultVO.class, responseContainer = "page") - @PostMapping(value = "/page") - public Object page(@Valid @RequestBody JobExecutionPageParam jobExecutionPageParam) { - log.info("pageParam : {}", jobExecutionPageParam); - return jobExecutionService.getJobExecutionPage(jobExecutionPageParam); - } - - @ApiOperation(value = "get job execution error data page", response = Object.class, responseContainer = "page") - @GetMapping(value = "/errorDataPage") - public Object readErrorDataPage(@RequestParam("taskId") Long taskId, - @RequestParam("pageNumber") Integer pageNumber, - @RequestParam("pageSize") Integer pageSize){ - return jobExecutionErrorDataService.readErrorDataPage(taskId, pageNumber, pageSize); - } -} +/* + * 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. + */ +package io.datavines.server.api.controller; + +import io.datavines.core.aop.RefreshToken; +import io.datavines.core.constant.DataVinesConstants; +import io.datavines.core.exception.DataVinesServerException; +import io.datavines.common.entity.job.SubmitJob; +import io.datavines.server.api.dto.bo.job.JobExecutionDashboardParam; +import io.datavines.server.api.dto.bo.job.JobExecutionPageParam; +import io.datavines.server.api.dto.vo.JobExecutionResultVO; +import io.datavines.server.repository.entity.JobExecution; +import io.datavines.server.repository.service.JobExecutionErrorDataService; +import io.datavines.server.repository.service.JobExecutionResultService; +import io.datavines.server.repository.service.JobExecutionService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@Slf4j +@Api(value = "job", tags = "job", produces = MediaType.APPLICATION_JSON_VALUE) +@RestController +@RequestMapping(value = DataVinesConstants.BASE_API_PATH + "/job/execution", produces = MediaType.APPLICATION_JSON_VALUE) +@RefreshToken +public class JobExecutionController { + + @Autowired + private JobExecutionService jobExecutionService; + + @Autowired + private JobExecutionResultService jobExecutionResultService; + + @Autowired + private JobExecutionErrorDataService jobExecutionErrorDataService; + + @ApiOperation(value = "submit external data quality job", response = Long.class) + @PostMapping(value = "/submit/data-quality", consumes = MediaType.APPLICATION_JSON_VALUE) + public Object submitDataQualityJob(@Valid @RequestBody SubmitJob submitJob) throws DataVinesServerException { + return jobExecutionService.submitJob(submitJob); + } + + @ApiOperation(value = "submit external data reconciliation job", response = Long.class) + @PostMapping(value = "/submit/data-reconciliation", consumes = MediaType.APPLICATION_JSON_VALUE) + public Object submitDataReconJob(@Valid @RequestBody SubmitJob submitJob) throws DataVinesServerException { + return jobExecutionService.submitJob(submitJob); + } + + @ApiOperation(value = "kill job", response = Long.class) + @DeleteMapping(value = "/kill/{executionId}") + public Object killTask(@PathVariable("executionId") Long executionId) { + return jobExecutionService.killJob(executionId); + } + + @ApiOperation(value = "get job execution status", response = String.class) + @GetMapping(value = "/status/{executionId}") + public Object getTaskStatus(@PathVariable("executionId") Long executionId) { + return jobExecutionService.getById(executionId).getStatus().getDescription(); + } + + @ApiOperation(value = "get job execution list by job id", response = JobExecution.class, responseContainer = "list") + @GetMapping(value = "/list/{jobId}") + public Object getJobExecutionListByJobId(@PathVariable("jobId") Long jobId) { + return jobExecutionService.listByJobId(jobId); + } + + @Deprecated + @ApiOperation(value = "get job execution result", response = JobExecutionResultVO.class) + @GetMapping(value = "/result/{executionId}") + public Object getJobExecutionResultInfo(@PathVariable("executionId") Long executionId) { + return jobExecutionResultService.getResultVOByJobExecutionId(executionId); + } + + @ApiOperation(value = "get job execution result", response = JobExecutionResultVO.class) + @GetMapping(value = "/list/result/{executionId}") + public Object getJobExecutionResultInfoList(@PathVariable("executionId") Long executionId) { + return jobExecutionResultService.getResultVOListByJobExecutionId(executionId); + } + + @ApiOperation(value = "get job execution page", response = JobExecutionResultVO.class, responseContainer = "page") + @PostMapping(value = "/page") + public Object page(@Valid @RequestBody JobExecutionPageParam jobExecutionPageParam) { + log.info("param : {}" , jobExecutionPageParam); + return jobExecutionService.getJobExecutionPage(jobExecutionPageParam); + } + + @ApiOperation(value = "get job execution error data page", response = Object.class, responseContainer = "page") + @GetMapping(value = "/errorDataPage") + public Object readErrorDataPage(@RequestParam("taskId") Long taskId, + @RequestParam("pageNumber") Integer pageNumber, + @RequestParam("pageSize") Integer pageSize){ + return jobExecutionErrorDataService.readErrorDataPage(taskId, pageNumber, pageSize); + } + + @ApiOperation(value = "get job execution agg pie", response = JobExecutionResultVO.class) + @PostMapping(value = "/agg-pie") + public Object getExecutionAggPie(@Valid @RequestBody JobExecutionDashboardParam dashboardParam) { + return jobExecutionService.getJobExecutionAggPie(dashboardParam); + } + + @ApiOperation(value = "get job execution trend bar", response = JobExecutionResultVO.class) + @PostMapping(value = "/trend-bar") + public Object getExecutionTrendBar(@Valid @RequestBody JobExecutionDashboardParam dashboardParam) { + return jobExecutionService.getJobExecutionTrendBar(dashboardParam); + } +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionDashboardParam.java b/datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionDashboardParam.java new file mode 100755 index 000000000..2d582fc87 --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionDashboardParam.java @@ -0,0 +1,40 @@ +/* + * 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. + */ +package io.datavines.server.api.dto.bo.job; + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +@NotNull(message = "JobExecutionPageParam cannot be null") +public class JobExecutionDashboardParam { + + private Long datasourceId; + + private String metricType; + + private String schemaName; + + private String tableName; + + private String columnName; + + private String startTime; + + private String endTime; +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionPageParam.java b/datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionPageParam.java old mode 100644 new mode 100755 index 900871084..ee863e2f8 --- a/datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionPageParam.java +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/bo/job/JobExecutionPageParam.java @@ -1,40 +1,50 @@ -/* - * 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. - */ -package io.datavines.server.api.dto.bo.job; - -import lombok.Data; -import javax.validation.constraints.NotNull; - -@Data -@NotNull(message = "JobExecutionPageParam cannot be null") -public class JobExecutionPageParam { - - Long datasourceId; - - Integer status; - String searchVal; - Long jobId; - String metricType; - String schemaName; - String tableName; - String columnName; - String startTime; - String endTime; - Integer pageNumber; - Integer pageSize; - -} +/* + * 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. + */ +package io.datavines.server.api.dto.bo.job; + +import lombok.Data; +import javax.validation.constraints.NotNull; + +@Data +@NotNull(message = "JobExecutionPageParam cannot be null") +public class JobExecutionPageParam { + + private Long datasourceId; + + private Integer status; + + private String searchVal; + + private Long jobId; + + private String metricType; + + private String schemaName; + + private String tableName; + + private String columnName; + + private String startTime; + + private String endTime; + + private Integer pageNumber; + + private Integer pageSize; + +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionAggItem.java b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionAggItem.java new file mode 100644 index 000000000..a813fc6d2 --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionAggItem.java @@ -0,0 +1,11 @@ +package io.datavines.server.api.dto.vo; + +import lombok.Data; + +@Data +public class JobExecutionAggItem { + + private String name; + + private int value; +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBar.java b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBar.java new file mode 100644 index 000000000..6210b0ce0 --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBar.java @@ -0,0 +1,17 @@ +package io.datavines.server.api.dto.vo; + +import lombok.Data; + +import java.util.List; + +@Data +public class JobExecutionTrendBar { + + private List dateList; + + private List allList; + + private List successList; + + private List failureList; +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBarItem.java b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBarItem.java new file mode 100644 index 000000000..c09b0d731 --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBarItem.java @@ -0,0 +1,12 @@ +package io.datavines.server.api.dto.vo; + +import lombok.Data; + +@Data +public class JobExecutionTrendBarItem { + + private String createDate; + + private int status; + private int num; +} diff --git a/datavines-server/src/main/java/io/datavines/server/repository/mapper/JobExecutionMapper.java b/datavines-server/src/main/java/io/datavines/server/repository/mapper/JobExecutionMapper.java index cbf34b318..95d5ce95f 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/mapper/JobExecutionMapper.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/mapper/JobExecutionMapper.java @@ -18,6 +18,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.datavines.server.api.dto.vo.JobExecutionAggItem; +import io.datavines.server.api.dto.vo.JobExecutionTrendBarItem; import io.datavines.server.api.dto.vo.JobExecutionVO; import org.apache.ibatis.annotations.*; @@ -37,8 +39,17 @@ IPage getJobExecutionPage(Page page, @Param("searchVal") String searchVal, @Param("jobId") Long jobId, @Param("datasourceId") Long datasourceId, - @Param("status") int status, + @Param("status") Integer status, @Param("metricType") String metricType, @Param("schemaName") String schemaName, @Param("tableName") String tableName, @Param("columnName") String columnName, @Param("startTime") String startTime, @Param("endTime") String endTime); + + List getJobExecutionAggPie(@Param("datasourceId") Long datasourceId, + @Param("metricType") String metricType, @Param("schemaName") String schemaName, + @Param("tableName") String tableName, @Param("columnName") String columnName, + @Param("startTime") String startTime, @Param("endTime") String endTime); + List getJobExecutionTrendBar(@Param("datasourceId") Long datasourceId, + @Param("metricType") String metricType, @Param("schemaName") String schemaName, + @Param("tableName") String tableName, @Param("columnName") String columnName, + @Param("startTime") String startTime, @Param("endTime") String endTime); } diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java b/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java index 31a0a38ee..16d5f5e1a 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java @@ -21,11 +21,15 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import io.datavines.common.entity.job.SubmitJob; +import io.datavines.server.api.dto.bo.job.JobExecutionDashboardParam; import io.datavines.server.api.dto.bo.job.JobExecutionPageParam; +import io.datavines.server.api.dto.vo.JobExecutionAggItem; +import io.datavines.server.api.dto.vo.JobExecutionTrendBar; import io.datavines.server.api.dto.vo.JobExecutionVO; import io.datavines.server.api.dto.vo.MetricExecutionDashBoard; import io.datavines.server.repository.entity.JobExecution; import io.datavines.core.exception.DataVinesServerException; +import org.apache.ibatis.annotations.Param; public interface JobExecutionService extends IService { @@ -39,12 +43,6 @@ public interface JobExecutionService extends IService { int deleteByJobId(long jobId); -// IPage getJobExecutionPage(String searchVal, Long jobId, -// String metricType, String schemaName, -// String tableName, String columnName, -// String startTime, String endTime, -// Integer pageNumber, Integer pageSize); - IPage getJobExecutionPage(JobExecutionPageParam pageParam); Long submitJob(SubmitJob submitJob) throws DataVinesServerException; @@ -60,4 +58,8 @@ public interface JobExecutionService extends IService { String getJobExecutionHost(Long jobExecutionId); List getMetricExecutionDashBoard(Long jobId, String startTime, String endTime); + + List getJobExecutionAggPie(JobExecutionDashboardParam dashboardParam); + + JobExecutionTrendBar getJobExecutionTrendBar(JobExecutionDashboardParam dashboardParam); } diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java index a42a68121..33725f2b3 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java @@ -1,6 +1,6 @@ /* * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with + * 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 @@ -16,23 +16,29 @@ */ package io.datavines.server.repository.service.impl; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.stream.Collectors; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import io.datavines.common.entity.JobExecutionParameter; import io.datavines.common.param.ExecuteRequestParam; +import io.datavines.common.utils.DateUtils; import io.datavines.connector.api.ConnectorFactory; import io.datavines.core.enums.Status; +import io.datavines.core.utils.LanguageUtils; import io.datavines.metric.api.ResultFormula; import io.datavines.common.entity.job.SubmitJob; +import io.datavines.server.api.dto.bo.job.JobExecutionDashboardParam; import io.datavines.server.api.dto.bo.job.JobExecutionPageParam; -import io.datavines.server.api.dto.vo.JobExecutionVO; +import io.datavines.server.api.dto.vo.*; import io.datavines.core.exception.DataVinesServerException; -import io.datavines.server.api.dto.vo.MetricExecutionDashBoard; import io.datavines.server.repository.entity.DataSource; import io.datavines.server.repository.entity.JobExecution; import io.datavines.server.repository.entity.JobExecutionResult; @@ -55,7 +61,9 @@ import org.springframework.transaction.annotation.Transactional; import static io.datavines.common.ConfigConstants.ERROR_DATA_OUTPUT_TO_DATASOURCE_DATABASE; +import static io.datavines.core.constant.DataVinesConstants.EMPTY; import static io.datavines.core.constant.DataVinesConstants.SPARK; +import static java.util.Collections.EMPTY_LIST; @Service("jobExecutionService") public class JobExecutionServiceImpl extends ServiceImpl implements JobExecutionService { @@ -293,4 +301,148 @@ public List getMetricExecutionDashBoard(Long jobId, St return resultList; } + + @Override + public List getJobExecutionAggPie(JobExecutionDashboardParam dashboardParam) { + List statusList = new ArrayList<>(Arrays.asList("6","7")); + + List items = + baseMapper.getJobExecutionAggPie(dashboardParam.getDatasourceId(), dashboardParam.getMetricType(), + dashboardParam.getSchemaName(), dashboardParam.getTableName(), dashboardParam.getColumnName(), + dashboardParam.getStartTime(), dashboardParam.getEndTime()); + if (CollectionUtils.isEmpty(items)) { + return new ArrayList<>(); + } + items = items.stream().filter(it -> statusList.contains(it.getName())).collect(Collectors.toList()); + + boolean isZh = LanguageUtils.isZhContext(); + for (JobExecutionAggItem jobExecutionAggItem : items) { + switch (jobExecutionAggItem.getName()) { + case "1": + if (isZh) { + jobExecutionAggItem.setName("执行中"); + } else { + jobExecutionAggItem.setName("Running"); + } + + break; + case "6": + if (isZh) { + jobExecutionAggItem.setName("执行失败"); + } else { + jobExecutionAggItem.setName("Failure"); + } + + break; + case "7": + if (isZh) { + jobExecutionAggItem.setName("执行成功"); + } else { + jobExecutionAggItem.setName("Success"); + } + + break; + case "9": + if (isZh) { + jobExecutionAggItem.setName("停止"); + } else { + jobExecutionAggItem.setName("Kill"); + } + + break; + default: + break; + } + } + + return items; + } + + @Override + public JobExecutionTrendBar getJobExecutionTrendBar(JobExecutionDashboardParam dashboardParam) { + + JobExecutionTrendBar trendBar = new JobExecutionTrendBar(); + + String startDateStr = ""; + String endDateStr = ""; + if (StringUtils.isEmpty(dashboardParam.getStartTime()) && StringUtils.isEmpty(dashboardParam.getEndTime())) { + startDateStr = DateUtils.format(DateUtils.addDays(new Date(), -5),"yyyy-MM-dd"); + endDateStr = DateUtils.format(DateUtils.addDays(new Date(), +1),"yyyy-MM-dd"); + } else { + if (StringUtils.isEmpty(dashboardParam.getEndTime())) { + startDateStr = dashboardParam.getStartTime().substring(0,10); + endDateStr = DateUtils.format(new Date(),"yyyy-MM-dd"); + } else if (StringUtils.isEmpty(dashboardParam.getStartTime())) { + Date endDate = DateUtils.parse(dashboardParam.getEndTime(), "yyyy-MM-dd"); + startDateStr = DateUtils.format(DateUtils.addDays(endDate,-6),"yyyy-MM-dd"); + endDateStr = dashboardParam.getEndTime().substring(0,10); + } + } + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + LocalDate startDate = LocalDate.parse(startDateStr, formatter); + LocalDate endDate = LocalDate.parse(endDateStr, formatter); + + List dateList = new ArrayList<>(); + LocalDate currentDate = startDate; + + while (!currentDate.isAfter(endDate)) { + dateList.add(currentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + currentDate = currentDate.plusDays(1); + } + + List trendBars = baseMapper.getJobExecutionTrendBar(dashboardParam.getDatasourceId(), + dashboardParam.getMetricType(), dashboardParam.getSchemaName(), dashboardParam.getTableName(), dashboardParam.getColumnName(), + startDateStr, endDateStr); + + if (CollectionUtils.isEmpty(trendBars)) { + return trendBar; + } + + Map> trendBarListMap = new HashMap<>(); + trendBars.forEach(it -> { + if (trendBarListMap.get(it.getCreateDate()) == null) { + List list = new ArrayList<>(); + list.add(it); + trendBarListMap.put(it.getCreateDate(), list); + } else { + trendBarListMap.get(it.getCreateDate()).add(it); + } + }); + + List allList = new ArrayList<>(); + List successList = new ArrayList<>(); + List failureList = new ArrayList<>(); + + dateList.forEach(date -> { + List list = trendBarListMap.get(date); + if (CollectionUtils.isEmpty(list)) { + allList.add(0); + successList.add(0); + failureList.add(0); + } else { + int all = 0; + int success = 0; + int failure = 0; + for (JobExecutionTrendBarItem item :list) { + if (item.getStatus() == 6) { + failure++; + } else if (item.getStatus() == 7) { + success++; + } + } + allList.add(failure+success); + failureList.add(failure); + successList.add(success); + } + }); + + + trendBar.setDateList(dateList); + trendBar.setAllList(allList); + trendBar.setSuccessList(successList); + trendBar.setFailureList(failureList); + + return trendBar; + } } diff --git a/datavines-server/src/main/resources/application.yaml b/datavines-server/src/main/resources/application.yaml index 0575399bd..ae85c0707 100644 --- a/datavines-server/src/main/resources/application.yaml +++ b/datavines-server/src/main/resources/application.yaml @@ -63,8 +63,8 @@ spring: mybatis-plus: type-enums-package: io.datavines.*.enums -# configuration: -# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl server: port: 5600 diff --git a/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml b/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml index 270db91c9..4d5e8d44c 100644 --- a/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml +++ b/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml @@ -54,6 +54,60 @@ + + select * from dv_job_execution + + + and metric_type = #{metricType} + + + and schema_name = #{schemaName} + + + and table_name = #{tableName} + + + and column_name = #{columnName} + + + and update_time >= #{startTime} + + + and update_time <= #{endTime} + + + and datasource_id = #{datasourceId} + + + + + + select DATE_FORMAT(create_time, '%Y-%m-%d') AS create_date, status from dv_job_execution + + + and metric_type = #{metricType} + + + and schema_name = #{schemaName} + + + and table_name = #{tableName} + + + and column_name = #{columnName} + + + and create_time >= #{startTime} + + + and create_time <= #{endTime} + + + and datasource_id = #{datasourceId} + + + + + + + + \ No newline at end of file diff --git a/datavines-ui/Editor/components/Database/Detail/dashBoard.tsx b/datavines-ui/Editor/components/Database/Detail/dashBoard.tsx index 775a91224..991cb4203 100644 --- a/datavines-ui/Editor/components/Database/Detail/dashBoard.tsx +++ b/datavines-ui/Editor/components/Database/Detail/dashBoard.tsx @@ -40,12 +40,14 @@ const Index = ({ id, option, style = {} }:{id:string, option:any, style?:any}) = if (document.getElementById(id) && document.getElementById(id)?.innerHTML) { // eslint-disable-next-line no-unused-expressions myChart?.dispose && myChart.current?.dispose(); + myChart.clear(); setMyChart(null); - const dom = document.getElementById(id) as HTMLElement; - dom.innerHTML = ''; + // const dom = document.getElementById(id) as HTMLElement; + // dom.innerHTML = ""; } return; } + if (!myChart) { const charts = echarts.init(document.getElementById(id) as HTMLElement); setMyChart(charts); diff --git a/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx b/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx index a5971a997..f5af0c223 100755 --- a/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx +++ b/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx @@ -39,46 +39,7 @@ interface Option { isLeaf?: boolean; } -const pieOption = { - tooltip: { - trigger: 'item' - }, - legend: { - top: '5%', - left: 'center' - }, - series: [ - { - name: 'Access From', - type: 'pie', - radius: ['40%', '70%'], - avoidLabelOverlap: false, - itemStyle: { - borderRadius: 10, - borderColor: '#fff', - borderWidth: 2 - }, - label: { - show: false, - position: 'center' - }, - emphasis: { - label: { - show: true, - fontSize: 40, - fontWeight: 'bold' - } - }, - labelLine: { - show: false - }, - data: [ - { value: 1048, name: 'Success' }, - { value: 735, name: 'Failure' }, - ] - } - ] -}; + const app: any = {}; const posList = [ @@ -162,87 +123,9 @@ const labelOption: BarLabelOption = { } }; -const barOption = { - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow' - } - }, - legend: { - data: ['Forest', 'Steppe', 'Desert', 'Wetland'] - }, - toolbox: { - show: true, - orient: 'vertical', - left: 'right', - top: 'center', - feature: { - mark: { show: true }, - dataView: { show: true, readOnly: false }, - magicType: { show: true, type: ['line', 'bar', 'stack'] }, - restore: { show: true }, - saveAsImage: { show: true } - } - }, - xAxis: [ - { - type: 'category', - axisTick: { show: false }, - data: ['2012', '2013', '2014', '2015', '2016'] - } - ], - yAxis: [ - { - type: 'value' - } - ], - series: [ - { - name: 'Forest', - type: 'bar', - barGap: 0, - label: labelOption, - emphasis: { - focus: 'series' - }, - data: [320, 332, 301, 334, 390] - }, - { - name: 'Steppe', - type: 'bar', - label: labelOption, - emphasis: { - focus: 'series' - }, - data: [220, 182, 191, 234, 290] - }, - { - name: 'Desert', - type: 'bar', - label: labelOption, - emphasis: { - focus: 'series' - }, - data: [150, 232, 201, 154, 190] - }, - { - name: 'Wetland', - type: 'bar', - label: labelOption, - emphasis: { - focus: 'series' - }, - data: [98, 77, 101, 99, 40] - } - ] -}; - - - const Dashboard = ({ datasourceId }: TJobs) => { - const [dqBarOption, setDqBarOption] = useState(barOption); - const [dqPieOption, setDqPieOption] = useState(pieOption); + const [dqBarOption, setDqBarOption] = useState(); + const [dqPieOption, setDqPieOption] = useState(); const intl = useIntl(); const match = useRouteMatch(); @@ -317,12 +200,11 @@ const Dashboard = ({ datasourceId }: TJobs) => { const { Render: RenderErrorDataModal, show: showErrorDataModal } = useInstanceErrorDataModal({}); const { Render: RenderResultModal, show: showResultModal } = useInstanceResult({}); const { Render: RenderLoggerModal, show: showLoggerModal } = useLogger({}); - // const optionLists: Option[] = []; - // const [options, setOptions] = useState(optionLists); + const [databases, setDataBases] = useState([]); const [metricList, setMetricList] = useState([]); - const getJobExecutionData = async () => { + const getJobExecutionData = async (pageParam1 :any) => { try { setLoading(true); const res = (await $http.post('/job/execution/page', { @@ -331,9 +213,9 @@ const Dashboard = ({ datasourceId }: TJobs) => { columnName : entityParam.columnName, metricType : metricType, datasourceId : datasourceId || (match.params as any).id, - pageNumber : pageParam.pageNumber, + pageNumber : pageParam1.pageNumber, pageSize : 5, - status: 2, + status: 6, startTime : startTime, endTime : endTime }, @@ -348,6 +230,153 @@ const Dashboard = ({ datasourceId }: TJobs) => { } }; + const getJobExecutionAggPie = async () => { + try { + setLoading(true); + const res = (await $http.post('/job/execution/agg-pie', { + schemaName : entityParam.schemaName, + tableName : entityParam.tableName, + columnName : entityParam.columnName, + metricType : metricType, + datasourceId : datasourceId || (match.params as any).id, + startTime : startTime, + endTime : endTime + }, + )) || []; + console.log("agg pie res : ", res) + + const pieOption = { + tooltip: { + trigger: 'item' + }, + legend: { + top: '5%', + left: 'center' + }, + color: ['#ef6567', '#91cd77'], + series: [ + { + name: 'Execution Agg Pie', + type: 'pie', + radius: ['40%', '70%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 10, + borderColor: '#fff', + borderWidth: 2 + }, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true, + fontSize: 40, + fontWeight: 'bold' + } + }, + labelLine: { + show: false + }, + data: res + } + ] + }; + setDqPieOption(pieOption) + + } catch (error) { + } finally { + setLoading(false); + } + }; + + const getJobExecutionTrendBar = async () => { + try { + setLoading(true); + const res = (await $http.post('/job/execution/trend-bar', { + schemaName : entityParam.schemaName, + tableName : entityParam.tableName, + columnName : entityParam.columnName, + metricType : metricType, + datasourceId : datasourceId || (match.params as any).id, + startTime : startTime, + endTime : endTime + }, + )) || []; + console.log("agg bar res : ", res) + + const barOption = { + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } + }, + toolbox: { + show: true, + orient: 'vertical', + left: 'right', + top: 'center', + feature: { + mark: { show: true }, + dataView: { show: true, readOnly: false }, + magicType: { show: true, type: ['line', 'bar', 'stack'] }, + restore: { show: true }, + saveAsImage: { show: true } + } + }, + xAxis: [ + { + type: 'category', + axisTick: { show: false }, + data: res.dateList + } + ], + yAxis: [ + { + type: 'value' + } + ], + series: [ + { + name: 'All', + type: 'bar', + barGap: 0, + label: labelOption, + emphasis: { + focus: 'series' + }, + data: res.allList + }, + { + name: 'Success', + type: 'bar', + label: labelOption, + emphasis: { + focus: 'series' + }, + data: res.successList + }, + { + name: 'Failure', + type: 'bar', + label: labelOption, + emphasis: { + focus: 'series' + }, + data: res.failureList + } + ] + }; + setDqBarOption(barOption) + + } catch (error) { + } finally { + setLoading(false); + } + }; + useMount(async () => { const $metricList = await $http.get('metric/list/DATA_QUALITY'); @@ -368,16 +397,22 @@ const Dashboard = ({ datasourceId }: TJobs) => { }) // @ts-ignore setDataBases($reDatabases1); - getJobExecutionData(); + getJobExecutionData(pageParam); + getJobExecutionAggPie(); + getJobExecutionTrendBar(); }); - const onPageChange = ({ current, pageSize }: any) => { + const onPageChange = ({ current, pageSize }: any) => { + console.log("current page :" , current) setPageParam({ pageNumber : current, pageSize : pageSize }) - getJobExecutionData(); + getJobExecutionData({ + pageNumber : current, + pageSize : pageSize + }); }; const onLog = (record: TJobsInstanceTableItem) => { @@ -404,7 +439,9 @@ const Dashboard = ({ datasourceId }: TJobs) => { }; const onQueryClick = () => { - getJobExecutionData(); + getJobExecutionData(pageParam); + getJobExecutionAggPie(); + getJobExecutionTrendBar(); } const columns: ColumnsType = [ @@ -452,7 +489,7 @@ const Dashboard = ({ datasourceId }: TJobs) => { }, ]; return ( -
+
@@ -469,19 +506,19 @@ const Dashboard = ({ datasourceId }: TJobs) => {
-
+
{intl.formatMessage({ id: 'config_title' })} - + {intl.formatMessage({ id: 'config_title' })} - + diff --git a/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx b/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx old mode 100644 new mode 100755 index 94dc98d70..8a9b023b3 --- a/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx +++ b/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx @@ -1,168 +1,168 @@ -/* eslint-disable react/no-children-prop */ -import React, { useState } from 'react'; -import { Table, Form } from 'antd'; -import { ColumnsType } from 'antd/lib/table'; -import { useIntl } from 'react-intl'; -import querystring from 'querystring'; -import { TJobsInstanceTableData, TJobsInstanceTableItem } from '@/type/JobsInstance'; -import { Title, SearchForm } from '@/component'; -import { useMount, IF } from '@/common'; -import { $http } from '@/http'; -import { defaultRender } from '@/utils/helper'; -import { useLogger } from './useLogger'; -import { useInstanceErrorDataModal } from './useInstanceErrorDataModal'; -import { useInstanceResult } from './useInstanceResult'; - -const JobsInstance = () => { - const intl = useIntl(); - const form = Form.useForm()[0]; - const [loading, setLoading] = useState(false); - const { Render: RenderErrorDataModal, show: showErrorDataModal } = useInstanceErrorDataModal({}); - const { Render: RenderResultModal, show: showResultModal } = useInstanceResult({}); - const { Render: RenderLoggerModal, show: showLoggerModal } = useLogger({}); - const [tableData, setTableData] = useState({ list: [], total: 0 }); - const [pageParams, setPageParams] = useState({ - pageNumber: 1, - pageSize: 10, - }); - const [qs] = useState(querystring.parse(window.location.href.split('?')[1] || '')); - const getData = async (values?: any, $pageParams?: any) => { - try { - setLoading(true); - const res = (await $http.get('/job/execution/page', { - jobId: qs.jobId, - ...($pageParams || pageParams), - ...(values || form.getFieldsValue()), - })) || []; - setTableData({ - list: res?.records || [], - total: res.total || 0, - }); - } catch (error) { - } finally { - setLoading(false); - } - }; - useMount(() => { - getData(); - }); - const onSearch = (_values: any) => { - setPageParams({ ...pageParams, pageNumber: 1 }); - getData({ - ..._values, - pageNumber: 1, - }); - }; - const onChange = ({ current, pageSize }: any) => { - setPageParams({ - pageNumber: current, - pageSize, - }); - getData(null, { - pageNumber: current, - pageSize, - }); - }; - const onStop = async (record: TJobsInstanceTableItem) => { - try { - setLoading(true); - await $http.delete(`job/execution/kill/${record.id}`); - getData(); - } catch (error) { - } finally { - setLoading(false); - } - }; - const onLog = (record: TJobsInstanceTableItem) => { - showLoggerModal(record); - }; - const onResult = (record: TJobsInstanceTableItem) => { - showResultModal(record); - }; - const onErrorData = (record: TJobsInstanceTableItem) => { - showErrorDataModal(record); - }; - const columns: ColumnsType = [ - { - title: intl.formatMessage({ id: 'jobs_task_name' }), - dataIndex: 'name', - key: 'name', - width: 300, - render: (text) => defaultRender(text, 300), - }, - { - title: intl.formatMessage({ id: 'jobs_task_type' }), - dataIndex: 'jobType', - key: 'jobType', - width: 140, - render: (text: string) =>
{text}
, - }, - { - title: intl.formatMessage({ id: 'jobs_task_status' }), - dataIndex: 'status', - key: 'status', - width: 140, - render: (text: string) =>
{text}
, - }, - { - title: intl.formatMessage({ id: 'jobs_update_time' }), - dataIndex: 'updateTime', - key: 'updateTime', - width: 160, - render: (text: string) =>
{text || '--'}
, - }, - { - title: intl.formatMessage({ id: 'common_action' }), - fixed: 'right', - key: 'right', - dataIndex: 'right', - width: 240, - render: (text: string, record: TJobsInstanceTableItem) => ( - <> - - { onStop(record); }}>{intl.formatMessage({ id: 'jobs_task_stop_btn' })} - - { onLog(record); }}>{intl.formatMessage({ id: 'jobs_task_log_btn' })} - { onResult(record); }}>{intl.formatMessage({ id: 'jobs_task_result' })} - { onErrorData(record); }}>{intl.formatMessage({ id: 'jobs_task_error_data' })} - - ), - }, - ]; - return ( -
- {/* */} - <div> - <div className="dv-flex-between"> - <SearchForm form={form} onSearch={onSearch} placeholder={intl.formatMessage({ id: 'common_search' })} /> - </div> - </div> - <Table<TJobsInstanceTableItem> - size="middle" - loading={loading} - rowKey="id" - columns={columns} - dataSource={tableData.list || []} - onChange={onChange} - pagination={{ - size: 'small', - total: tableData.total, - showSizeChanger: true, - current: pageParams.pageNumber, - pageSize: pageParams.pageSize, - }} - /> - <RenderLoggerModal /> - <RenderErrorDataModal /> - <RenderResultModal /> - </div> - ); -}; - -export default JobsInstance; +/* eslint-disable react/no-children-prop */ +import React, { useState } from 'react'; +import { Table, Form } from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import { useIntl } from 'react-intl'; +import querystring from 'querystring'; +import { TJobsInstanceTableData, TJobsInstanceTableItem } from '@/type/JobsInstance'; +import { Title, SearchForm } from '@/component'; +import { useMount, IF } from '@/common'; +import { $http } from '@/http'; +import { defaultRender } from '@/utils/helper'; +import { useLogger } from './useLogger'; +import { useInstanceErrorDataModal } from './useInstanceErrorDataModal'; +import { useInstanceResult } from './useInstanceResult'; + +const JobsInstance = () => { + const intl = useIntl(); + const form = Form.useForm()[0]; + const [loading, setLoading] = useState(false); + const { Render: RenderErrorDataModal, show: showErrorDataModal } = useInstanceErrorDataModal({}); + const { Render: RenderResultModal, show: showResultModal } = useInstanceResult({}); + const { Render: RenderLoggerModal, show: showLoggerModal } = useLogger({}); + const [tableData, setTableData] = useState<TJobsInstanceTableData>({ list: [], total: 0 }); + const [pageParams, setPageParams] = useState({ + pageNumber: 1, + pageSize: 10, + }); + const [qs] = useState(querystring.parse(window.location.href.split('?')[1] || '')); + const getData = async (values?: any, $pageParams?: any) => { + try { + setLoading(true); + const res = (await $http.post('/job/execution/page', { + jobId: qs.jobId, + ...($pageParams || pageParams), + ...(values || form.getFieldsValue()), + })) || []; + setTableData({ + list: res?.records || [], + total: res.total || 0, + }); + } catch (error) { + } finally { + setLoading(false); + } + }; + useMount(() => { + getData(); + }); + const onSearch = (_values: any) => { + setPageParams({ ...pageParams, pageNumber: 1 }); + getData({ + ..._values, + pageNumber: 1, + }); + }; + const onChange = ({ current, pageSize }: any) => { + setPageParams({ + pageNumber: current, + pageSize, + }); + getData(null, { + pageNumber: current, + pageSize, + }); + }; + const onStop = async (record: TJobsInstanceTableItem) => { + try { + setLoading(true); + await $http.delete(`job/execution/kill/${record.id}`); + getData(); + } catch (error) { + } finally { + setLoading(false); + } + }; + const onLog = (record: TJobsInstanceTableItem) => { + showLoggerModal(record); + }; + const onResult = (record: TJobsInstanceTableItem) => { + showResultModal(record); + }; + const onErrorData = (record: TJobsInstanceTableItem) => { + showErrorDataModal(record); + }; + const columns: ColumnsType<TJobsInstanceTableItem> = [ + { + title: intl.formatMessage({ id: 'jobs_task_name' }), + dataIndex: 'name', + key: 'name', + width: 300, + render: (text) => defaultRender(text, 300), + }, + { + title: intl.formatMessage({ id: 'jobs_task_type' }), + dataIndex: 'jobType', + key: 'jobType', + width: 140, + render: (text: string) => <div>{text}</div>, + }, + { + title: intl.formatMessage({ id: 'jobs_task_status' }), + dataIndex: 'status', + key: 'status', + width: 140, + render: (text: string) => <div>{text}</div>, + }, + { + title: intl.formatMessage({ id: 'jobs_update_time' }), + dataIndex: 'updateTime', + key: 'updateTime', + width: 160, + render: (text: string) => <div>{text || '--'}</div>, + }, + { + title: intl.formatMessage({ id: 'common_action' }), + fixed: 'right', + key: 'right', + dataIndex: 'right', + width: 240, + render: (text: string, record: TJobsInstanceTableItem) => ( + <> + <IF visible={record.status === 'submitted' || record.status === 'running'}> + <a style={{ marginRight: 5 }} onClick={() => { onStop(record); }}>{intl.formatMessage({ id: 'jobs_task_stop_btn' })}</a> + </IF> + <a style={{ marginRight: 5 }} onClick={() => { onLog(record); }}>{intl.formatMessage({ id: 'jobs_task_log_btn' })}</a> + <a style={{ marginRight: 5 }} onClick={() => { onResult(record); }}>{intl.formatMessage({ id: 'jobs_task_result' })}</a> + <a style={{ marginRight: 5 }} onClick={() => { onErrorData(record); }}>{intl.formatMessage({ id: 'jobs_task_error_data' })}</a> + </> + ), + }, + ]; + return ( + <div + className="dv-page-padding" + style={{ + padding: '0px 20px 0px 0px', + height: 'auto', + }} + > + {/* <Title isBack children={undefined} /> */} + <div> + <div className="dv-flex-between"> + <SearchForm form={form} onSearch={onSearch} placeholder={intl.formatMessage({ id: 'common_search' })} /> + </div> + </div> + <Table<TJobsInstanceTableItem> + size="middle" + loading={loading} + rowKey="id" + columns={columns} + dataSource={tableData.list || []} + onChange={onChange} + pagination={{ + size: 'small', + total: tableData.total, + showSizeChanger: true, + current: pageParams.pageNumber, + pageSize: pageParams.pageSize, + }} + /> + <RenderLoggerModal /> + <RenderErrorDataModal /> + <RenderResultModal /> + </div> + ); +}; + +export default JobsInstance; From 7643d542123dd4ff5922b9c84405f07931a256f6 Mon Sep 17 00:00:00 2001 From: zixi0825 <sunzhaohe0825@gmail.com> Date: Wed, 27 Sep 2023 22:14:49 +0800 Subject: [PATCH 3/5] [Feature][UI] Add dataquality dashboard (1) --- datavines-server/src/main/resources/application.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datavines-server/src/main/resources/application.yaml b/datavines-server/src/main/resources/application.yaml index ae85c0707..df8005376 100644 --- a/datavines-server/src/main/resources/application.yaml +++ b/datavines-server/src/main/resources/application.yaml @@ -87,9 +87,9 @@ spring: on-profile: mysql datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://116.205.236.108:3306/datavines?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai + url: jdbc:mysql://127.0.0.1:3306/datavines?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai username: root - password: vines@0825 + password: 123456 quartz: properties: org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate \ No newline at end of file From a680e241d0372bb7f908615ebe31646effcf2966 Mon Sep 17 00:00:00 2001 From: zixi0825 <sunzhaohe0825@gmail.com> Date: Thu, 28 Sep 2023 07:47:15 +0800 Subject: [PATCH 4/5] [Feature][UI] Add quality dashboard (2) --- .../io/datavines/common/utils/DateUtils.java | 4 + .../service/impl/JobExecutionServiceImpl.java | 12 +- .../src/main/resources/application.yaml | 4 +- datavines-ui/src/locale/en_US.ts | 572 +++++++++--------- datavines-ui/src/locale/zh_CN.ts | 568 ++++++++--------- datavines-ui/src/router/detailRouter.tsx | 66 +- .../view/Main/HomeDetail/Dashboard/index.tsx | 6 +- 7 files changed, 627 insertions(+), 605 deletions(-) diff --git a/datavines-common/src/main/java/io/datavines/common/utils/DateUtils.java b/datavines-common/src/main/java/io/datavines/common/utils/DateUtils.java index 46520eeb2..d8f37b49a 100644 --- a/datavines-common/src/main/java/io/datavines/common/utils/DateUtils.java +++ b/datavines-common/src/main/java/io/datavines/common/utils/DateUtils.java @@ -182,6 +182,10 @@ public static long diffHours(Date d1, Date d2) { return (long) Math.ceil(diffMin(d1, d2) / 60.0); } + public static long diffDays(Date d1, Date d2) { + return (long) Math.ceil(diffMin(d1, d2) / 60.0/24); + } + /** * get minutes between two dates * diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java index 33725f2b3..71740f0aa 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java @@ -370,12 +370,22 @@ public JobExecutionTrendBar getJobExecutionTrendBar(JobExecutionDashboardParam d endDateStr = DateUtils.format(DateUtils.addDays(new Date(), +1),"yyyy-MM-dd"); } else { if (StringUtils.isEmpty(dashboardParam.getEndTime())) { + Date startDate = DateUtils.parse(dashboardParam.getStartTime(), "yyyy-MM-dd"); startDateStr = dashboardParam.getStartTime().substring(0,10); - endDateStr = DateUtils.format(new Date(),"yyyy-MM-dd"); + endDateStr = DateUtils.format(DateUtils.addDays(startDate,7),"yyyy-MM-dd"); } else if (StringUtils.isEmpty(dashboardParam.getStartTime())) { Date endDate = DateUtils.parse(dashboardParam.getEndTime(), "yyyy-MM-dd"); startDateStr = DateUtils.format(DateUtils.addDays(endDate,-6),"yyyy-MM-dd"); endDateStr = dashboardParam.getEndTime().substring(0,10); + } else { + Date endDate = DateUtils.parse(dashboardParam.getEndTime(), "yyyy-MM-dd HH:mm:dd"); + Date startDate = DateUtils.parse(dashboardParam.getStartTime(), "yyyy-MM-dd HH:mm:dd"); + long days = DateUtils.diffDays(endDate,startDate); + if (days > 7) { + endDate = DateUtils.addDays(startDate, 7); + } + startDateStr = DateUtils.format(startDate,"yyyy-MM-dd"); + endDateStr = DateUtils.format(endDate,"yyyy-MM-dd"); } } diff --git a/datavines-server/src/main/resources/application.yaml b/datavines-server/src/main/resources/application.yaml index df8005376..ae85c0707 100644 --- a/datavines-server/src/main/resources/application.yaml +++ b/datavines-server/src/main/resources/application.yaml @@ -87,9 +87,9 @@ spring: on-profile: mysql datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://127.0.0.1:3306/datavines?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai + url: jdbc:mysql://116.205.236.108:3306/datavines?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai username: root - password: 123456 + password: vines@0825 quartz: properties: org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate \ No newline at end of file diff --git a/datavines-ui/src/locale/en_US.ts b/datavines-ui/src/locale/en_US.ts index bf0505545..27ed7ffff 100755 --- a/datavines-ui/src/locale/en_US.ts +++ b/datavines-ui/src/locale/en_US.ts @@ -1,284 +1,288 @@ -export default { - common_input_tip: 'Please Enter ', - common_required_top: ' is required!', - common_input_pattern_tip: 'Please enter the correct ', - common_action: 'Actions', - common_add: 'Add ', - common_save: 'Save', - common_edit: 'Edit', - common_view: 'View', - common_search: 'Search', - common_delete: 'Delete', - common_type: 'Type', - common_dataSource: 'DataSource', - common_settings: 'Settings', - common_notice: 'Notification', - common_desc: 'Description', - common_required_tip: 'Cannot be empty', - common_Ok: 'Yes', - common_Cancel: 'No', - common_delete_tip: 'Are you sure to delete?', - common_update: 'Update', - common_create_btn: 'Create', - common_switch_language: 'Switch Language', - common_required: 'Cannot be empty', - common_more: 'More', - common_success: 'Success', - common_fail: 'Fail', - common_updater: 'Updater', - common_back: 'Back', - common_Logout: 'Exit the system', - common_error_tips: 'User not logged in', - common_update_time: 'Update Time', - - '/main/home': 'DataSource', - '/main/warning': 'Warning', - '/main/userManage': 'User', - '/main/errorDataManage': 'ErrorData', - '/main/detail/:id/dashboard': 'Dashboard', - '/main/detail/:id/editor': 'Catalog', - '/main/detail/:id/jobs': 'Jobs', - '/main/detail/:id/tasks': 'Results', - '/main/detail/:id': 'Detail', - '/main/label': 'Label', - '/main/config': 'Config', - - confirm_text: 'OK', - test_link: 'Test Connect', - test_link_fail: 'Test Connect Failed', - - login_btn_text: 'Login', - login_username_msg: 'Please input your username!', - login_password_msg: 'Please input your password!', - - create_space: 'Create Workspace', - create_space_name: 'Workspace Name', - - register: 'Sign Up', - register_title: 'User Registration', - register_btn: 'Registration', - register_success: 'Registration succeeded, go to login', - forget_success: 'Reset successfully, go to login', - forget_password: 'Forget Password', - password_text: 'Password', - old_password_text: 'Old Password', - new_password_text: 'New Password', - confirm_password_text: 'Confirm Password', - login_password_text: ' Login Password', - password_tip: 'The password must contain uppercase letters, lowercase letters, numbers, and special characters', - userName_text: 'Username', - email_text: 'Email ', - verification_code_text: 'Verification Code', - get_verification_code_text: 'Get Verification Code', - forget_pwd_title: 'Email Forget Password', - forget_pwd_btn: 'Reset Password', - phone_text: 'Phone', - - header_top_search_msg: 'Search to Select', - header_top_add_workspace: 'Add Workspace', - header_top_workspace: 'Workspace', - - home_search_placeholder: 'Search to DataSource', - home_create_datasource: 'Create DataSource', - home_metadata_fetch: 'Metadata Fetcher', - home_metadata_fetch_schedule: 'Metadata Fetcher Schedule', - - // jobs - jobs_list: 'Jobs', - jobs_add: 'Add', - jobs_add_tip: 'Please save the job first', - jobs_name: 'Jobs Name', - jobs_type: 'Jobs Type', - jobs_updater: 'Updater', - jobs_update_time: 'Update time', - jobs_run: 'Run', - jobs_view: 'View JobExecution', - jobs_task_title: 'JobExecution List', - jobs_task_name: 'JobExecution Name', - jobs_task_type: 'Job Type', - jobs_task_status: 'JobExecution Status', - jobs_task_stop_btn: 'Stop', - jobs_task_result: 'ExecutionResult', - jobs_task_error_data: 'Error', - jobs_task_error_data_view: 'ErrorData', - jobs_task_log_btn: 'Log', - jobs_task_result_btn: 'Result', - - jobs_task_check_result: 'Check Result', - jobs_task_check_subject: 'Check Subject', - jobs_task_check_rule: 'Check Rule', - jobs_task_check_expectVal_type: 'ExpectedValue Type', - jobs_task_check_params: 'Check Params', - jobs_task_check_formula: 'Result Formula', - jobs_task_check_explain: 'Explain', - jobs_task_check_explain_text: 'When the result of the check formula is true, the check result is failed, and vice versa', - - jobs_tabs_title: 'Data Quality Metric Job', - jobs_tabs_comparison_title: 'Data Reconciliation Job', - jobs_tabs_config: 'Job Configuration', - jobs_save_run: 'Save and Run', - jobs_tabs_schedule: 'Schedule Configuration', - jobs_tabs_SLA: 'SLA Configuration', - jobs_add_metric: 'Add Metric', - jobs_run_profile: 'Run Profile', - jobs_tabs_config_file: 'Configuration File', - jobs_tabs_config_file_copy: 'Copy', - jobs_tabs_config_file_download: 'Download', - - jobs_schedule_type: 'Schedule Type', - jobs_schedule_cycle: 'Schedule Cycle', - jobs_schedule_time: 'Schedule Time', - jobs_schedule_express: 'Crontab Expression', - jobs_schedule_obtain_time: 'Effective Time', - jobs_schedule_custom: 'Custom Schedule', - jobs_schedule_crontab: 'Crontab Schedule', - jobs_schedule_offline: 'Offline', - - jobs_schedule_cycle_week: 'Week', - jobs_week_before: 'Every', - jobs_week_after: 'execute', - jobs_schedule_cycle_month: 'Month', - jobs_every_month: 'Every Month ', - jobs_every_month_day: ' day', - jobs_schedule_cycle_day: 'Day', - jobs_day_before: 'Every Day', - jobs_day_after: 'Execute', - jobs_schedule_cycle_hour: 'Hour', - jobs_hour_before: 'Every Dour', - jobs_hour_after: 'minute execute', - jobs_schedule_cycle_nhour: 'N Hours', - jobs_nhour_before: 'Every day from', - jobs_nhour_middle: 'start, every', - jobs_nhour_after: 'hour execute', - jobs_schedule_cycle_minute: 'Minutes', - jobs_schedule_cycle_nminute: 'N minutes', - jobs_nminute_before: 'After', - jobs_nminute_middle: 'minutes, every', - jobs_nminute_after: 'minute execute', - jobs_schedule_time_to: 'To', - jobs_schedule_auto_generate: 'Auto generated', - jobs_monday: 'Monday', - jobs_tuesday: 'Tuesday', - jobs_wednesday: 'Wednesday', - jobs_thursday: 'Thursday', - jobs_friday: 'Friday', - jobs_saturday: 'Saturday', - jobs_sunday: 'Sunday', - jobs_editor: 'SQL Editor', - jobs_directory: 'Catalog Details', - jobs_last_scan_time: 'Last Scan Time', - jobs_number_of_labels: 'Labels', - jobs_number_of_rules: 'Rules', - jobs_use_heat: 'Use heat', - - job_quantity: 'quantity', - job_table_quantity: 'Tables', - job_column_quantity: 'Columns', - job_alter_quantity: 'Alters', - job_table_name: 'Table Name', - job_column: 'Column', - job_column_Name: 'Column Name', - job_alter: 'Alter', - job_table: 'Table', - job_database_name: 'Database Name', - job_database: 'Database', - job_last_refresh_time: 'Last Refresh Time', - job_metrics: 'Metrics', - job_Create_time: 'Create Time', - job_title: 'Title', - job_last_update_time: 'Last Updated Time', - job_null: 'Null', - job_notNull: 'NotNull', - job_unique: 'Unique', - job_distinct: 'Distinct', - job_comment: 'Comment', - job_status: 'Status', - job_trend: 'Trend', - job_action: 'Action', - job_before: 'Before', - job_after: 'After', - job_schema_changes: 'Schema Changes', - job_profile: 'Profile', - job_issue: 'Issues', - job_issue_detail: 'Issues Detail', - job_good: 'good', - job_bad: 'bad', - job_maximum: 'Maximum', - job_minimum: 'Minimum', - job_max_length: 'Max Length', - job_min_length: 'Min Length', - job_avg_length: 'Avg Length', - job_blank: 'Blank', - job_sum: 'Sum', - job_avg: 'Avg', - job_stdDev: 'StdDev', - job_variance: 'Variance', - - job_choose_col: 'Choose Col', - job_profile_schedule: 'Schedule', - job_profile_execution_history: 'Execution History', - jobs_status: 'Execution Status', - jobs_execution_time: 'Execution Time', - warn_sLAs_name: 'SLA Name', - warn_sLAs_type: 'Type', - warn_update_time: 'Update Time', - warn_sLAs_tip_one: 'For Monitoring ', - warn_sLAs_tip_two: ' related data', - warn_create_sLAs: 'Create SLA', - - warn_create_widget: 'Create Channel', - warn_widget_name: 'Channel Name', - warn_setting_basic_info: 'Basic Data', - warn_setting_notice_instance_name: 'Notification instance name', - warn_setting_notice_add: 'Create Notification', - warn_setting_notice_edit: 'Edit Notification', - warn_setting_notice_sender: 'Notification Channel', - warn_monitor_tip: 'SLAs for monitoring {{data}} related data', - warn_association_rule_jobs: 'Related Metric Job', - warn_notice: 'Notification', - - workspace_name: 'Name', - workspace_exit: 'Exit Workspace', - workspace_exit_tip: 'Are you sure to exit the workspace?', - workspace_add: 'Create Workspace', - workspace_edit: 'Edit Workspace', - workspace_delete: 'Delete Workspace', - workspace_delete_tip: 'Are you sure to delete the current workspace?', - workspace_user_invite: 'Invite User', - - datasource: 'DataSource', - datasource_modal_add_title: 'Create DataSource', - datasource_modal_edit_title: 'Edit DataSource', - datasource_modal_name: 'Name', - datasource_modal_source_type: 'Source Type', - datasource_updater: 'Updater', - datasource_updateTime: 'UpdateTime', - datasource_list_title: 'DataSources', - - sla_name: 'SLA Name', - sla_select: 'Select SLA', - sla_title: 'SLA Management', - sla_rule_job_name: 'Rule job name', - - job_log_view_log: 'View Log', - job_log_refresh: 'Refresh', - job_log_download: 'Download', - job_log_fullScreen: 'FullScreen', - - error_create_btn: 'Create error data store', - error_table_store_name: 'Storage Name', - error_table_store_type: 'Storage Type', - error_title: 'Storage Management', - - user_title: 'User Management', - label_title: 'Tag Category', - label_list: 'Tags', - label_add_category: 'Add Tag Category', - label_add: 'Add Tag', - label_name: 'Tag Name', - - config_title: 'Config Management', - config_var_key: 'Config Key', - config_var_value: 'Config Value', - create_config: 'Create Config' -}; +export default { + common_input_tip: 'Please Enter ', + common_required_top: ' is required!', + common_input_pattern_tip: 'Please enter the correct ', + common_action: 'Actions', + common_add: 'Add ', + common_save: 'Save', + common_edit: 'Edit', + common_view: 'View', + common_search: 'Search', + common_delete: 'Delete', + common_type: 'Type', + common_dataSource: 'DataSource', + common_settings: 'Settings', + common_notice: 'Notification', + common_desc: 'Description', + common_required_tip: 'Cannot be empty', + common_Ok: 'Yes', + common_Cancel: 'No', + common_delete_tip: 'Are you sure to delete?', + common_update: 'Update', + common_create_btn: 'Create', + common_switch_language: 'Switch Language', + common_required: 'Cannot be empty', + common_more: 'More', + common_success: 'Success', + common_fail: 'Fail', + common_updater: 'Updater', + common_back: 'Back', + common_Logout: 'Exit the system', + common_error_tips: 'User not logged in', + common_update_time: 'Update Time', + + '/main/home': 'DataSource', + '/main/warning': 'Warning', + '/main/userManage': 'User', + '/main/errorDataManage': 'ErrorData', + '/main/detail/:id/dashboard': 'Dashboard', + '/main/detail/:id/editor': 'Catalog', + '/main/detail/:id/jobs': 'Jobs', + '/main/detail/:id/tasks': 'Results', + '/main/detail/:id': 'Detail', + '/main/label': 'Label', + '/main/config': 'Config', + + confirm_text: 'OK', + test_link: 'Test Connect', + test_link_fail: 'Test Connect Failed', + + login_btn_text: 'Login', + login_username_msg: 'Please input your username!', + login_password_msg: 'Please input your password!', + + create_space: 'Create Workspace', + create_space_name: 'Workspace Name', + + register: 'Sign Up', + register_title: 'User Registration', + register_btn: 'Registration', + register_success: 'Registration succeeded, go to login', + forget_success: 'Reset successfully, go to login', + forget_password: 'Forget Password', + password_text: 'Password', + old_password_text: 'Old Password', + new_password_text: 'New Password', + confirm_password_text: 'Confirm Password', + login_password_text: ' Login Password', + password_tip: 'The password must contain uppercase letters, lowercase letters, numbers, and special characters', + userName_text: 'Username', + email_text: 'Email ', + verification_code_text: 'Verification Code', + get_verification_code_text: 'Get Verification Code', + forget_pwd_title: 'Email Forget Password', + forget_pwd_btn: 'Reset Password', + phone_text: 'Phone', + + header_top_search_msg: 'Search to Select', + header_top_add_workspace: 'Add Workspace', + header_top_workspace: 'Workspace', + + home_search_placeholder: 'Search to DataSource', + home_create_datasource: 'Create DataSource', + home_metadata_fetch: 'Metadata Fetcher', + home_metadata_fetch_schedule: 'Metadata Fetcher Schedule', + + // jobs + jobs_list: 'Jobs', + jobs_add: 'Add', + jobs_add_tip: 'Please save the job first', + jobs_name: 'Jobs Name', + jobs_type: 'Jobs Type', + jobs_updater: 'Updater', + jobs_update_time: 'Update time', + jobs_run: 'Run', + jobs_view: 'View JobExecution', + jobs_task_title: 'JobExecution List', + jobs_task_name: 'JobExecution Name', + jobs_task_type: 'Job Type', + jobs_task_status: 'JobExecution Status', + jobs_task_stop_btn: 'Stop', + jobs_task_result: 'ExecutionResult', + jobs_task_error_data: 'Error', + jobs_task_error_data_view: 'ErrorData', + jobs_task_log_btn: 'Log', + jobs_task_result_btn: 'Result', + + jobs_task_check_result: 'Check Result', + jobs_task_check_subject: 'Check Subject', + jobs_task_check_rule: 'Check Rule', + jobs_task_check_expectVal_type: 'ExpectedValue Type', + jobs_task_check_params: 'Check Params', + jobs_task_check_formula: 'Result Formula', + jobs_task_check_explain: 'Explain', + jobs_task_check_explain_text: 'When the result of the check formula is true, the check result is failed, and vice versa', + + jobs_tabs_title: 'Data Quality Metric Job', + jobs_tabs_comparison_title: 'Data Reconciliation Job', + jobs_tabs_config: 'Job Configuration', + jobs_save_run: 'Save and Run', + jobs_tabs_schedule: 'Schedule Configuration', + jobs_tabs_SLA: 'SLA Configuration', + jobs_add_metric: 'Add Metric', + jobs_run_profile: 'Run Profile', + jobs_tabs_config_file: 'Configuration File', + jobs_tabs_config_file_copy: 'Copy', + jobs_tabs_config_file_download: 'Download', + + jobs_schedule_type: 'Schedule Type', + jobs_schedule_cycle: 'Schedule Cycle', + jobs_schedule_time: 'Schedule Time', + jobs_schedule_express: 'Crontab Expression', + jobs_schedule_obtain_time: 'Effective Time', + jobs_schedule_custom: 'Custom Schedule', + jobs_schedule_crontab: 'Crontab Schedule', + jobs_schedule_offline: 'Offline', + + jobs_schedule_cycle_week: 'Week', + jobs_week_before: 'Every', + jobs_week_after: 'execute', + jobs_schedule_cycle_month: 'Month', + jobs_every_month: 'Every Month ', + jobs_every_month_day: ' day', + jobs_schedule_cycle_day: 'Day', + jobs_day_before: 'Every Day', + jobs_day_after: 'Execute', + jobs_schedule_cycle_hour: 'Hour', + jobs_hour_before: 'Every Dour', + jobs_hour_after: 'minute execute', + jobs_schedule_cycle_nhour: 'N Hours', + jobs_nhour_before: 'Every day from', + jobs_nhour_middle: 'start, every', + jobs_nhour_after: 'hour execute', + jobs_schedule_cycle_minute: 'Minutes', + jobs_schedule_cycle_nminute: 'N minutes', + jobs_nminute_before: 'After', + jobs_nminute_middle: 'minutes, every', + jobs_nminute_after: 'minute execute', + jobs_schedule_time_to: 'To', + jobs_schedule_auto_generate: 'Auto generated', + jobs_monday: 'Monday', + jobs_tuesday: 'Tuesday', + jobs_wednesday: 'Wednesday', + jobs_thursday: 'Thursday', + jobs_friday: 'Friday', + jobs_saturday: 'Saturday', + jobs_sunday: 'Sunday', + jobs_editor: 'SQL Editor', + jobs_directory: 'Catalog Details', + jobs_last_scan_time: 'Last Scan Time', + jobs_number_of_labels: 'Labels', + jobs_number_of_rules: 'Rules', + jobs_use_heat: 'Use heat', + + job_quantity: 'quantity', + job_table_quantity: 'Tables', + job_column_quantity: 'Columns', + job_alter_quantity: 'Alters', + job_table_name: 'Table Name', + job_column: 'Column', + job_column_Name: 'Column Name', + job_alter: 'Alter', + job_table: 'Table', + job_database_name: 'Database Name', + job_database: 'Database', + job_last_refresh_time: 'Last Refresh Time', + job_metrics: 'Metrics', + job_Create_time: 'Create Time', + job_title: 'Title', + job_last_update_time: 'Last Updated Time', + job_null: 'Null', + job_notNull: 'NotNull', + job_unique: 'Unique', + job_distinct: 'Distinct', + job_comment: 'Comment', + job_status: 'Status', + job_trend: 'Trend', + job_action: 'Action', + job_before: 'Before', + job_after: 'After', + job_schema_changes: 'Schema Changes', + job_profile: 'Profile', + job_issue: 'Issues', + job_issue_detail: 'Issues Detail', + job_good: 'good', + job_bad: 'bad', + job_maximum: 'Maximum', + job_minimum: 'Minimum', + job_max_length: 'Max Length', + job_min_length: 'Min Length', + job_avg_length: 'Avg Length', + job_blank: 'Blank', + job_sum: 'Sum', + job_avg: 'Avg', + job_stdDev: 'StdDev', + job_variance: 'Variance', + + job_choose_col: 'Choose Col', + job_profile_schedule: 'Schedule', + job_profile_execution_history: 'Execution History', + jobs_status: 'Execution Status', + jobs_execution_time: 'Execution Time', + warn_sLAs_name: 'SLA Name', + warn_sLAs_type: 'Type', + warn_update_time: 'Update Time', + warn_sLAs_tip_one: 'For Monitoring ', + warn_sLAs_tip_two: ' related data', + warn_create_sLAs: 'Create SLA', + + warn_create_widget: 'Create Channel', + warn_widget_name: 'Channel Name', + warn_setting_basic_info: 'Basic Data', + warn_setting_notice_instance_name: 'Notification instance name', + warn_setting_notice_add: 'Create Notification', + warn_setting_notice_edit: 'Edit Notification', + warn_setting_notice_sender: 'Notification Channel', + warn_monitor_tip: 'SLAs for monitoring {{data}} related data', + warn_association_rule_jobs: 'Related Metric Job', + warn_notice: 'Notification', + + workspace_name: 'Name', + workspace_exit: 'Exit Workspace', + workspace_exit_tip: 'Are you sure to exit the workspace?', + workspace_add: 'Create Workspace', + workspace_edit: 'Edit Workspace', + workspace_delete: 'Delete Workspace', + workspace_delete_tip: 'Are you sure to delete the current workspace?', + workspace_user_invite: 'Invite User', + + datasource: 'DataSource', + datasource_modal_add_title: 'Create DataSource', + datasource_modal_edit_title: 'Edit DataSource', + datasource_modal_name: 'Name', + datasource_modal_source_type: 'Source Type', + datasource_updater: 'Updater', + datasource_updateTime: 'UpdateTime', + datasource_list_title: 'DataSources', + + sla_name: 'SLA Name', + sla_select: 'Select SLA', + sla_title: 'SLA Management', + sla_rule_job_name: 'Rule job name', + + job_log_view_log: 'View Log', + job_log_refresh: 'Refresh', + job_log_download: 'Download', + job_log_fullScreen: 'FullScreen', + + error_create_btn: 'Create error data store', + error_table_store_name: 'Storage Name', + error_table_store_type: 'Storage Type', + error_title: 'Storage Management', + + user_title: 'User Management', + label_title: 'Tag Category', + label_list: 'Tags', + label_add_category: 'Add Tag Category', + label_add: 'Add Tag', + label_name: 'Tag Name', + + config_title: 'Config Management', + config_var_key: 'Config Key', + config_var_value: 'Config Value', + create_config: 'Create Config', + + quality_dashboard_profile: 'Quality Profile', + quality_dashboard_trend: 'Quality Trend', + quality_dashboard_failure_execution: 'Fail Check', +}; diff --git a/datavines-ui/src/locale/zh_CN.ts b/datavines-ui/src/locale/zh_CN.ts index 89293d1c3..4c833e5a7 100755 --- a/datavines-ui/src/locale/zh_CN.ts +++ b/datavines-ui/src/locale/zh_CN.ts @@ -1,282 +1,286 @@ -export default { - common_input_tip: '请输入', - common_input_pattern_tip: '请输入正确的', - common_required_top: '必填', - common_action: '操作', - common_add: '新增', - common_save: '保存', - common_edit: '编辑', - common_view: '查看', - common_search: '搜索', - common_delete: '删除', - common_type: '类型', - common_dataSource: '数据源', - common_settings: '设置', - common_notice: '通知', - common_desc: '描述', - common_required_tip: '此项必填', - common_Ok: '确定', - common_Cancel: '取消', - common_delete_tip: '确定删除吗?', - common_update: '更新', - common_create_btn: '创建', - common_switch_language: '切换语言', - common_required: '不可为空', - common_more: '更多', - common_success: '操作成功', - common_fail: '操作失败', - common_updater: '更新人', - common_back: '返回', - common_Logout: '退出系统', - common_error_tips: '用户未登录', - common_update_time: '更新时间', - - '/main/home': '数据源管理', - '/main/warning': '告警管理', - '/main/userManage': '用户管理', - '/main/errorDataManage': '错误数据管理', - '/main/detail/:id/dashboard': '质量大盘', - '/main/detail/:id/editor': '数据目录', - '/main/detail/:id/jobs': '作业管理', - '/main/detail/:id/tasks': '结果管理', - '/main/detail/:id': '详情', - '/main/label': '标签管理', - '/main/config': '参数管理', - - confirm_text: '确认', - test_link: '测试链接', - test_link_fail: '测试链接失败', - - login_btn_text: '登录', - login_username_msg: '请输入用户名!', - login_password_msg: '请输入密码!', - - create_space: '创建空间', - create_space_name: '空间名称', - - register: '注册', - register_title: '用户注册', - register_btn: '完成注册', - register_success: '注册成功,前往登录', - forget_success: '重置成功,前往登录', - forget_password: '忘记密码', - password_text: '密码', - old_password_text: '旧密码', - new_password_text: '新密码', - confirm_password_text: '确认密码', - login_password_text: '登录密码', - password_tip: '密码必须包含大写字母、小写字母、数字、 特殊字符', - userName_text: '用户名', - email_text: '邮箱', - verification_code_text: '图形验证码', - get_verification_code_text: '获取图形验证码', - forget_pwd_title: '邮箱重置密码', - forget_pwd_btn: '重置密码', - phone_text: '手机号', - - header_top_search_msg: '请选择', - header_top_add_workspace: '新增工作空间', - header_top_workspace: '工作空间', - - home_search_placeholder: '搜索数据源', - home_create_datasource: '创建数据源', - home_metadata_fetch: '元数据抓取', - home_metadata_fetch_schedule: '元数据抓取调度', - - // jobs - jobs_list: '规则作业列表', - jobs_add: '创建规则作业', - jobs_add_tip: '请先保存规则作业', - jobs_name: '规则作业名称', - jobs_type: '规则作业类型', - jobs_updater: '更新人', - jobs_update_time: '更新时间', - jobs_run: '运行', - jobs_view: '查看执行记录', - jobs_task_title: '执行记录列表', - jobs_task_name: '执行记录名称', - jobs_task_type: '执行记录类型', - jobs_task_status: '执行记录状态', - jobs_task_stop_btn: '停止', - jobs_task_result: '结果', - jobs_task_error_data: '错误数据', - jobs_task_error_data_view: '查看错误数据', - jobs_task_log_btn: '日志', - jobs_task_result_btn: '检查结果', - - jobs_task_check_result: '检查结果', - jobs_task_check_subject: '检查对象', - jobs_task_check_params: '检查参数', - jobs_task_check_rule: '检查规则', - jobs_task_check_expectVal_type: '期望值类型', - jobs_task_check_formula: '校验公式', - jobs_task_check_explain: '说明', - jobs_task_check_explain_text: '当校验公式的结果为真,那么检查结果为失败,反之亦然', - - jobs_tabs_title: '数据质量作业', - jobs_tabs_comparison_title: '数据比对作业', - jobs_tabs_config: '作业配置', - jobs_save_run: '保存并运行', - jobs_tabs_schedule: '定时任务配置', - jobs_tabs_SLA: 'SLA配置', - jobs_add_metric: '添加作业', - jobs_run_profile: '运行数据概览', - jobs_tabs_config_file: '配置文件', - jobs_tabs_config_file_copy: '复制', - jobs_tabs_config_file_download: '下载', - - jobs_schedule_type: '调度类型', - jobs_schedule_cycle: '调度周期', - jobs_schedule_time: '调度时间', - jobs_schedule_express: 'Crontab表达式', - jobs_schedule_obtain_time: '有效时间', - jobs_schedule_custom: '常规调度', - jobs_schedule_crontab: 'Crontab调度', - jobs_schedule_offline: '不调度', - - jobs_schedule_cycle_week: '周', - jobs_week_before: '每', - jobs_week_after: '执行', - jobs_schedule_cycle_month: '月', - jobs_every_month: '每月', - jobs_every_month_day: '号', - jobs_schedule_cycle_day: '日', - jobs_day_before: '每天', - jobs_day_after: '执行', - jobs_schedule_cycle_hour: '小时', - jobs_hour_before: '每小时', - jobs_hour_after: '分钟执行', - jobs_schedule_cycle_nhour: '每N小时', - jobs_nhour_before: '每天从', - jobs_nhour_middle: '开始,每', - jobs_nhour_after: '小时执行', - jobs_schedule_cycle_minute: '分钟', - jobs_schedule_cycle_nminute: '每N分钟', - jobs_nminute_before: '第', - jobs_nminute_middle: '分之后,每', - jobs_nminute_after: '分钟执行', - jobs_schedule_time_to: '至', - jobs_schedule_auto_generate: '自动生成的', - jobs_monday: '星期一', - jobs_tuesday: '星期二', - jobs_wednesday: '星期三', - jobs_thursday: '星期四', - jobs_friday: '星期五', - jobs_saturday: '星期六', - jobs_sunday: '星期日', - jobs_editor: 'SQL编辑器', - jobs_directory: '目录', - jobs_last_scan_time: '上次扫描时间', - jobs_number_of_labels: '标签数量', - jobs_number_of_rules: '规则数量', - jobs_use_heat: '使用热度', - job_quantity: '数量', - job_table_quantity: '表数量', - job_column_quantity: '列数量', - job_alter_quantity: '变更数量', - job_table_name: '表名', - job_column: '列', - job_column_Name: '列名', - job_alter: '变更', - job_table: '表', - job_database_name: '数据库名', - job_database: '数据库', - job_last_refresh_time: '最后刷新时间', - job_metrics: '作业', - job_Create_time: '创建时间', - job_title: '标题', - job_last_update_time: '最后更新时间', - job_null: '空值', - job_notNull: '非空值', - job_unique: '唯一值', - job_distinct: '不同值', - job_comment: '注释', - job_status: '状态', - job_trend: '趋势', - job_action: '运行', - job_before: '变更前', - job_after: '变更后', - job_schema_changes: '变更记录', - job_profile: '概要', - job_issue: '问题', - job_issue_detail: '问题详情', - job_good: '好的', - job_bad: '坏的', - job_maximum: '最大', - job_minimum: '最小', - job_max_length: '最大长度', - job_min_length: '最短长度', - job_avg_length: '平均长度', - job_blank: '空白', - job_sum: '合计', - job_avg: '平均', - job_stdDev: '标准差', - job_variance: '方差', - job_choose_col: '选择列', - job_profile_execution_history: '历史记录', - job_profile_schedule: '定时调度配置', - jobs_status: '执行状态', - jobs_execution_time: '执行时间', - - warn_sLAs_name: '名称', - warn_sLAs_type: '类型', - warn_update_time: '更新时间', - warn_sLAs_tip_one: '用于监控', - warn_sLAs_tip_two: '相关数据', - warn_create_sLAs: '创建SLA', - warn_create_widget: '创建通知通道', - warn_widget_name: '通道名称', - warn_setting_basic_info: '基础信息', - warn_setting_notice_instance_name: '通知实例名字', - warn_setting_notice_add: '添加通知', - warn_setting_notice_edit: '编辑通知', - warn_setting_notice_sender: '通知', - warn_monitor_tip: '用于监控{{data}}相关数据的SLAs', - warn_association_rule_jobs: '关联规则作业', - warn_notice: '通知管理', - - workspace_name: '空间名称', - workspace_exit: '退出空间', - workspace_exit_tip: '确定退出空间?', - workspace_add: '创建', - workspace_edit: '编辑空间', - workspace_delete: '删除空间', - workspace_delete_tip: '确定删除当前空间?', - workspace_user_invite: '邀请用户', - - datasource: '数据源', - datasource_modal_add_title: '新增数据源', - datasource_modal_edit_title: '编辑数据源', - datasource_modal_name: '数据源名称', - datasource_modal_source_type: '数据源类型', - datasource_updater: '更新人', - datasource_updateTime: '更新时间', - datasource_list_title: '数据源列表', - - sla_name: 'SLA名称', - sla_select: '选择SLA', - sla_title: 'SLA管理', - sla_rule_job_name: '规则作业名称', - - job_log_view_log: '日志', - job_log_refresh: '刷新', - job_log_download: '下载', - job_log_fullScreen: '全屏', - - error_create_btn: '创建错误数据存储', - error_table_store_name: '存储名称', - error_table_store_type: '存储类型', - error_title: '存储管理', - - user_title: '用户管理', - label_title: '标签分类', - label_list: '标签列表', - label_add_category: '新增标签分类', - label_add: '新增标签', - label_name: '标签名', - - config_title: '参数管理', - config_var_key: '参数名', - config_var_value: '参数值', - create_config: '创建参数' -}; +export default { + common_input_tip: '请输入', + common_input_pattern_tip: '请输入正确的', + common_required_top: '必填', + common_action: '操作', + common_add: '新增', + common_save: '保存', + common_edit: '编辑', + common_view: '查看', + common_search: '搜索', + common_delete: '删除', + common_type: '类型', + common_dataSource: '数据源', + common_settings: '设置', + common_notice: '通知', + common_desc: '描述', + common_required_tip: '此项必填', + common_Ok: '确定', + common_Cancel: '取消', + common_delete_tip: '确定删除吗?', + common_update: '更新', + common_create_btn: '创建', + common_switch_language: '切换语言', + common_required: '不可为空', + common_more: '更多', + common_success: '操作成功', + common_fail: '操作失败', + common_updater: '更新人', + common_back: '返回', + common_Logout: '退出系统', + common_error_tips: '用户未登录', + common_update_time: '更新时间', + + '/main/home': '数据源管理', + '/main/warning': '告警管理', + '/main/userManage': '用户管理', + '/main/errorDataManage': '错误数据管理', + '/main/detail/:id/dashboard': '质量大盘', + '/main/detail/:id/editor': '数据目录', + '/main/detail/:id/jobs': '作业管理', + '/main/detail/:id/tasks': '结果管理', + '/main/detail/:id': '详情', + '/main/label': '标签管理', + '/main/config': '参数管理', + + confirm_text: '确认', + test_link: '测试链接', + test_link_fail: '测试链接失败', + + login_btn_text: '登录', + login_username_msg: '请输入用户名!', + login_password_msg: '请输入密码!', + + create_space: '创建空间', + create_space_name: '空间名称', + + register: '注册', + register_title: '用户注册', + register_btn: '完成注册', + register_success: '注册成功,前往登录', + forget_success: '重置成功,前往登录', + forget_password: '忘记密码', + password_text: '密码', + old_password_text: '旧密码', + new_password_text: '新密码', + confirm_password_text: '确认密码', + login_password_text: '登录密码', + password_tip: '密码必须包含大写字母、小写字母、数字、 特殊字符', + userName_text: '用户名', + email_text: '邮箱', + verification_code_text: '图形验证码', + get_verification_code_text: '获取图形验证码', + forget_pwd_title: '邮箱重置密码', + forget_pwd_btn: '重置密码', + phone_text: '手机号', + + header_top_search_msg: '请选择', + header_top_add_workspace: '新增工作空间', + header_top_workspace: '工作空间', + + home_search_placeholder: '搜索数据源', + home_create_datasource: '创建数据源', + home_metadata_fetch: '元数据抓取', + home_metadata_fetch_schedule: '元数据抓取调度', + + // jobs + jobs_list: '规则作业列表', + jobs_add: '创建规则作业', + jobs_add_tip: '请先保存规则作业', + jobs_name: '规则作业名称', + jobs_type: '规则作业类型', + jobs_updater: '更新人', + jobs_update_time: '更新时间', + jobs_run: '运行', + jobs_view: '查看执行记录', + jobs_task_title: '执行记录列表', + jobs_task_name: '执行记录名称', + jobs_task_type: '执行记录类型', + jobs_task_status: '执行记录状态', + jobs_task_stop_btn: '停止', + jobs_task_result: '结果', + jobs_task_error_data: '错误数据', + jobs_task_error_data_view: '查看错误数据', + jobs_task_log_btn: '日志', + jobs_task_result_btn: '检查结果', + + jobs_task_check_result: '检查结果', + jobs_task_check_subject: '检查对象', + jobs_task_check_params: '检查参数', + jobs_task_check_rule: '检查规则', + jobs_task_check_expectVal_type: '期望值类型', + jobs_task_check_formula: '校验公式', + jobs_task_check_explain: '说明', + jobs_task_check_explain_text: '当校验公式的结果为真,那么检查结果为失败,反之亦然', + + jobs_tabs_title: '数据质量作业', + jobs_tabs_comparison_title: '数据比对作业', + jobs_tabs_config: '作业配置', + jobs_save_run: '保存并运行', + jobs_tabs_schedule: '定时任务配置', + jobs_tabs_SLA: 'SLA配置', + jobs_add_metric: '添加作业', + jobs_run_profile: '运行数据概览', + jobs_tabs_config_file: '配置文件', + jobs_tabs_config_file_copy: '复制', + jobs_tabs_config_file_download: '下载', + + jobs_schedule_type: '调度类型', + jobs_schedule_cycle: '调度周期', + jobs_schedule_time: '调度时间', + jobs_schedule_express: 'Crontab表达式', + jobs_schedule_obtain_time: '有效时间', + jobs_schedule_custom: '常规调度', + jobs_schedule_crontab: 'Crontab调度', + jobs_schedule_offline: '不调度', + + jobs_schedule_cycle_week: '周', + jobs_week_before: '每', + jobs_week_after: '执行', + jobs_schedule_cycle_month: '月', + jobs_every_month: '每月', + jobs_every_month_day: '号', + jobs_schedule_cycle_day: '日', + jobs_day_before: '每天', + jobs_day_after: '执行', + jobs_schedule_cycle_hour: '小时', + jobs_hour_before: '每小时', + jobs_hour_after: '分钟执行', + jobs_schedule_cycle_nhour: '每N小时', + jobs_nhour_before: '每天从', + jobs_nhour_middle: '开始,每', + jobs_nhour_after: '小时执行', + jobs_schedule_cycle_minute: '分钟', + jobs_schedule_cycle_nminute: '每N分钟', + jobs_nminute_before: '第', + jobs_nminute_middle: '分之后,每', + jobs_nminute_after: '分钟执行', + jobs_schedule_time_to: '至', + jobs_schedule_auto_generate: '自动生成的', + jobs_monday: '星期一', + jobs_tuesday: '星期二', + jobs_wednesday: '星期三', + jobs_thursday: '星期四', + jobs_friday: '星期五', + jobs_saturday: '星期六', + jobs_sunday: '星期日', + jobs_editor: 'SQL编辑器', + jobs_directory: '目录', + jobs_last_scan_time: '上次扫描时间', + jobs_number_of_labels: '标签数量', + jobs_number_of_rules: '规则数量', + jobs_use_heat: '使用热度', + job_quantity: '数量', + job_table_quantity: '表数量', + job_column_quantity: '列数量', + job_alter_quantity: '变更数量', + job_table_name: '表名', + job_column: '列', + job_column_Name: '列名', + job_alter: '变更', + job_table: '表', + job_database_name: '数据库名', + job_database: '数据库', + job_last_refresh_time: '最后刷新时间', + job_metrics: '作业', + job_Create_time: '创建时间', + job_title: '标题', + job_last_update_time: '最后更新时间', + job_null: '空值', + job_notNull: '非空值', + job_unique: '唯一值', + job_distinct: '不同值', + job_comment: '注释', + job_status: '状态', + job_trend: '趋势', + job_action: '运行', + job_before: '变更前', + job_after: '变更后', + job_schema_changes: '变更记录', + job_profile: '概要', + job_issue: '问题', + job_issue_detail: '问题详情', + job_good: '好的', + job_bad: '坏的', + job_maximum: '最大', + job_minimum: '最小', + job_max_length: '最大长度', + job_min_length: '最短长度', + job_avg_length: '平均长度', + job_blank: '空白', + job_sum: '合计', + job_avg: '平均', + job_stdDev: '标准差', + job_variance: '方差', + job_choose_col: '选择列', + job_profile_execution_history: '历史记录', + job_profile_schedule: '定时调度配置', + jobs_status: '执行状态', + jobs_execution_time: '执行时间', + + warn_sLAs_name: '名称', + warn_sLAs_type: '类型', + warn_update_time: '更新时间', + warn_sLAs_tip_one: '用于监控', + warn_sLAs_tip_two: '相关数据', + warn_create_sLAs: '创建SLA', + warn_create_widget: '创建通知通道', + warn_widget_name: '通道名称', + warn_setting_basic_info: '基础信息', + warn_setting_notice_instance_name: '通知实例名字', + warn_setting_notice_add: '添加通知', + warn_setting_notice_edit: '编辑通知', + warn_setting_notice_sender: '通知', + warn_monitor_tip: '用于监控{{data}}相关数据的SLAs', + warn_association_rule_jobs: '关联规则作业', + warn_notice: '通知管理', + + workspace_name: '空间名称', + workspace_exit: '退出空间', + workspace_exit_tip: '确定退出空间?', + workspace_add: '创建', + workspace_edit: '编辑空间', + workspace_delete: '删除空间', + workspace_delete_tip: '确定删除当前空间?', + workspace_user_invite: '邀请用户', + + datasource: '数据源', + datasource_modal_add_title: '新增数据源', + datasource_modal_edit_title: '编辑数据源', + datasource_modal_name: '数据源名称', + datasource_modal_source_type: '数据源类型', + datasource_updater: '更新人', + datasource_updateTime: '更新时间', + datasource_list_title: '数据源列表', + + sla_name: 'SLA名称', + sla_select: '选择SLA', + sla_title: 'SLA管理', + sla_rule_job_name: '规则作业名称', + + job_log_view_log: '日志', + job_log_refresh: '刷新', + job_log_download: '下载', + job_log_fullScreen: '全屏', + + error_create_btn: '创建错误数据存储', + error_table_store_name: '存储名称', + error_table_store_type: '存储类型', + error_title: '存储管理', + + user_title: '用户管理', + label_title: '标签分类', + label_list: '标签列表', + label_add_category: '新增标签分类', + label_add: '新增标签', + label_name: '标签名', + + config_title: '参数管理', + config_var_key: '参数名', + config_var_value: '参数值', + create_config: '创建参数', + + quality_dashboard_profile: '质量概览', + quality_dashboard_trend: '质量趋势', + quality_dashboard_failure_execution: '失败作业', +}; diff --git a/datavines-ui/src/router/detailRouter.tsx b/datavines-ui/src/router/detailRouter.tsx index c6bf2e37d..15bcd6370 100755 --- a/datavines-ui/src/router/detailRouter.tsx +++ b/datavines-ui/src/router/detailRouter.tsx @@ -1,33 +1,33 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import React, { lazy } from 'react'; -import { DatabaseOutlined, UnorderedListOutlined, HistoryOutlined } from '@ant-design/icons'; -import { TRouter } from './type'; - -const detailRouter: TRouter = { - 'dv-detail-dashboard': { - path: '/main/detail/:id/dashboard', - key: '/main/detail/:id/dashboard', - label: '', - exact: false, - icon: <UnorderedListOutlined />, - component: lazy(() => import(/* webpackChunkName: 'view-detail-jobs' */ '@/view/Main/HomeDetail/Dashboard')), - }, - 'dv-detail-editor': { - path: '/main/detail/:id/editor', - key: '/main/detail/:id/editor', - label: '', - exact: true, - icon: <DatabaseOutlined />, - component: lazy(() => import(/* webpackChunkName: 'view-detail-editor' */ '@/view/Main/HomeDetail/EditorData')), - }, - 'dv-detail-jobs': { - path: '/main/detail/:id/jobs', - key: '/main/detail/:id/jobs', - label: '', - exact: false, - icon: <UnorderedListOutlined />, - component: lazy(() => import(/* webpackChunkName: 'view-detail-jobs' */ '@/view/Main/HomeDetail/Jobs')), - } -}; - -export default detailRouter; +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { lazy } from 'react'; +import { DatabaseOutlined, UnorderedListOutlined, HistoryOutlined ,BarChartOutlined} from '@ant-design/icons'; +import { TRouter } from './type'; + +const detailRouter: TRouter = { + 'dv-detail-dashboard': { + path: '/main/detail/:id/dashboard', + key: '/main/detail/:id/dashboard', + label: '', + exact: true, + icon: <BarChartOutlined/>, + component: lazy(() => import(/* webpackChunkName: 'view-detail-jobs' */ '@/view/Main/HomeDetail/Dashboard')), + }, + 'dv-detail-editor': { + path: '/main/detail/:id/editor', + key: '/main/detail/:id/editor', + label: '', + exact: false, + icon: <DatabaseOutlined />, + component: lazy(() => import(/* webpackChunkName: 'view-detail-editor' */ '@/view/Main/HomeDetail/EditorData')), + }, + 'dv-detail-jobs': { + path: '/main/detail/:id/jobs', + key: '/main/detail/:id/jobs', + label: '', + exact: false, + icon: <UnorderedListOutlined />, + component: lazy(() => import(/* webpackChunkName: 'view-detail-jobs' */ '@/view/Main/HomeDetail/Jobs')), + } +}; + +export default detailRouter; diff --git a/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx b/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx index f5af0c223..ec2933763 100755 --- a/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx +++ b/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx @@ -510,13 +510,13 @@ const Dashboard = ({ datasourceId }: TJobs) => { <Row style = {{marginTop: '20px'}}> <Col span={12}> <Title> - {intl.formatMessage({ id: 'config_title' })} + {intl.formatMessage({ id: 'quality_dashboard_profile' })} - {intl.formatMessage({ id: 'config_title' })} + {intl.formatMessage({ id: 'quality_dashboard_trend' })} @@ -524,7 +524,7 @@ const Dashboard = ({ datasourceId }: TJobs) => { - {intl.formatMessage({ id: 'config_title' })} + {intl.formatMessage({ id: 'quality_dashboard_failure_execution' })} size="middle" From 15de97f359d59e43a9090d05d1ff6d89e0f77d8c Mon Sep 17 00:00:00 2001 From: zixi0825 Date: Fri, 6 Oct 2023 22:07:36 +0800 Subject: [PATCH 5/5] [Feature][UI] Finish quality dashboard --- .../io/datavines/common/enums/JobType.java | 15 +- .../controller/JobExecutionController.java | 1 - .../api/dto/vo/JobExecutionAggItem.java | 16 + .../api/dto/vo/JobExecutionTrendBar.java | 16 + .../api/dto/vo/JobExecutionTrendBarItem.java | 16 + .../server/api/dto/vo/JobExecutionVO.java | 17 +- .../service/JobExecutionService.java | 1 - .../service/impl/JobExecutionServiceImpl.java | 51 +-- .../service/impl/JobServiceImpl.java | 2 +- .../src/main/resources/application.yaml | 8 +- .../resources/mapper/JobExecutionMapper.xml | 2 +- .../components/Database/Detail/dashBoard.tsx | 15 +- datavines-ui/Editor/components/Tree/index.tsx | 4 +- datavines-ui/src/global.less | 1 - datavines-ui/src/locale/en_US.ts | 6 + datavines-ui/src/locale/zh_CN.ts | 6 + datavines-ui/src/router/detailRouter.tsx | 6 +- datavines-ui/src/view/Main/Home/index.tsx | 2 +- .../view/Main/HomeDetail/Dashboard/index.tsx | 188 ++++----- .../Main/HomeDetail/Jobs/JobsInstance.tsx | 371 ++++++++++-------- .../view/Main/HomeDetail/Jobs/JobsList.tsx | 2 - .../src/view/Main/HomeDetail/index.tsx | 278 +++++++------ 22 files changed, 564 insertions(+), 460 deletions(-) diff --git a/datavines-common/src/main/java/io/datavines/common/enums/JobType.java b/datavines-common/src/main/java/io/datavines/common/enums/JobType.java index 9d1de18d8..be02c1f40 100644 --- a/datavines-common/src/main/java/io/datavines/common/enums/JobType.java +++ b/datavines-common/src/main/java/io/datavines/common/enums/JobType.java @@ -29,19 +29,22 @@ public enum JobType { * 1 DATA_PROFILE * 2 DATA_RECONCILIATION */ - DATA_QUALITY(0, "DATA_QUALITY"), - DATA_PROFILE(1, "DATA_PROFILE"), - DATA_RECONCILIATION(2, "DATA_RECONCILIATION"); + DATA_QUALITY(0, "DATA_QUALITY", "数据质量检查"), + DATA_PROFILE(1, "DATA_PROFILE","数据概览检查"), + DATA_RECONCILIATION(2, "DATA_RECONCILIATION","数据比对检查"); - JobType(int code, String description) { + JobType(int code, String description, String zhDescription) { this.code = code; this.description = description; + this.zhDescription = zhDescription; } @EnumValue private final int code; private final String description; + private final String zhDescription; + public int getCode() { return code; } @@ -50,6 +53,10 @@ public String getDescription() { return description; } + public String getZhDescription() { + return zhDescription; + } + private static final HashMap JOB_TYPE_MAP = new HashMap<>(); static { diff --git a/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java b/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java index 0666a5184..79ad90002 100755 --- a/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java +++ b/datavines-server/src/main/java/io/datavines/server/api/controller/JobExecutionController.java @@ -98,7 +98,6 @@ public Object getJobExecutionResultInfoList(@PathVariable("executionId") Long ex @ApiOperation(value = "get job execution page", response = JobExecutionResultVO.class, responseContainer = "page") @PostMapping(value = "/page") public Object page(@Valid @RequestBody JobExecutionPageParam jobExecutionPageParam) { - log.info("param : {}" , jobExecutionPageParam); return jobExecutionService.getJobExecutionPage(jobExecutionPageParam); } diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionAggItem.java b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionAggItem.java index a813fc6d2..1489e83f1 100644 --- a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionAggItem.java +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionAggItem.java @@ -1,3 +1,19 @@ +/* + * 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. + */ package io.datavines.server.api.dto.vo; import lombok.Data; diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBar.java b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBar.java index 6210b0ce0..f94af377c 100644 --- a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBar.java +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBar.java @@ -1,3 +1,19 @@ +/* + * 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. + */ package io.datavines.server.api.dto.vo; import lombok.Data; diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBarItem.java b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBarItem.java index c09b0d731..88e04059c 100644 --- a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBarItem.java +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionTrendBarItem.java @@ -1,3 +1,19 @@ +/* + * 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. + */ package io.datavines.server.api.dto.vo; import lombok.Data; diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionVO.java b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionVO.java index 83edc348d..0c66a1829 100644 --- a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionVO.java +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionVO.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import io.datavines.common.enums.ExecutionStatus; import io.datavines.common.enums.JobType; +import io.datavines.core.utils.LanguageUtils; import lombok.Data; import java.io.Serializable; @@ -33,15 +34,29 @@ public class JobExecutionVO implements Serializable { private String name; + private String schemaName; + + private String tableName; + + private String columnName; + + private String metricType; + private JobType jobType; private ExecutionStatus status; + @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + private LocalDateTime startTime; + + @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + private LocalDateTime endTime; + @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private LocalDateTime updateTime; public String getJobType() { - return jobType.getDescription(); + return LanguageUtils.isZhContext()? jobType.getZhDescription() : jobType.getDescription(); } public String getStatus() { diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java b/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java index 16d5f5e1a..efeee6f26 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionService.java @@ -29,7 +29,6 @@ import io.datavines.server.api.dto.vo.MetricExecutionDashBoard; import io.datavines.server.repository.entity.JobExecution; import io.datavines.core.exception.DataVinesServerException; -import org.apache.ibatis.annotations.Param; public interface JobExecutionService extends IService { diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java index 71740f0aa..19212331c 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java @@ -1,6 +1,6 @@ /* * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements`.` See the NOTICE file distributed with + * 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 @@ -22,15 +22,12 @@ import java.util.*; import java.util.stream.Collectors; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import io.datavines.common.entity.JobExecutionParameter; -import io.datavines.common.param.ExecuteRequestParam; import io.datavines.common.utils.DateUtils; -import io.datavines.connector.api.ConnectorFactory; import io.datavines.core.enums.Status; import io.datavines.core.utils.LanguageUtils; import io.datavines.metric.api.ResultFormula; @@ -39,7 +36,6 @@ import io.datavines.server.api.dto.bo.job.JobExecutionPageParam; import io.datavines.server.api.dto.vo.*; import io.datavines.core.exception.DataVinesServerException; -import io.datavines.server.repository.entity.DataSource; import io.datavines.server.repository.entity.JobExecution; import io.datavines.server.repository.entity.JobExecutionResult; import io.datavines.server.repository.service.*; @@ -60,10 +56,7 @@ import io.datavines.server.enums.Priority; import org.springframework.transaction.annotation.Transactional; -import static io.datavines.common.ConfigConstants.ERROR_DATA_OUTPUT_TO_DATASOURCE_DATABASE; -import static io.datavines.core.constant.DataVinesConstants.EMPTY; import static io.datavines.core.constant.DataVinesConstants.SPARK; -import static java.util.Collections.EMPTY_LIST; @Service("jobExecutionService") public class JobExecutionServiceImpl extends ServiceImpl implements JobExecutionService { @@ -369,14 +362,14 @@ public JobExecutionTrendBar getJobExecutionTrendBar(JobExecutionDashboardParam d startDateStr = DateUtils.format(DateUtils.addDays(new Date(), -5),"yyyy-MM-dd"); endDateStr = DateUtils.format(DateUtils.addDays(new Date(), +1),"yyyy-MM-dd"); } else { - if (StringUtils.isEmpty(dashboardParam.getEndTime())) { - Date startDate = DateUtils.parse(dashboardParam.getStartTime(), "yyyy-MM-dd"); + if (StringUtils.isEmpty(dashboardParam.getEndTime()) && StringUtils.isNotEmpty(dashboardParam.getStartTime())) { startDateStr = dashboardParam.getStartTime().substring(0,10); + Date startDate = DateUtils.stringToDate(dashboardParam.getStartTime()); endDateStr = DateUtils.format(DateUtils.addDays(startDate,7),"yyyy-MM-dd"); - } else if (StringUtils.isEmpty(dashboardParam.getStartTime())) { - Date endDate = DateUtils.parse(dashboardParam.getEndTime(), "yyyy-MM-dd"); - startDateStr = DateUtils.format(DateUtils.addDays(endDate,-6),"yyyy-MM-dd"); + } else if (StringUtils.isEmpty(dashboardParam.getStartTime()) && StringUtils.isNotEmpty(dashboardParam.getEndTime())) { endDateStr = dashboardParam.getEndTime().substring(0,10); + Date endDate = DateUtils.stringToDate(dashboardParam.getEndTime()); + startDateStr = DateUtils.format(DateUtils.addDays(endDate,-6),"yyyy-MM-dd"); } else { Date endDate = DateUtils.parse(dashboardParam.getEndTime(), "yyyy-MM-dd HH:mm:dd"); Date startDate = DateUtils.parse(dashboardParam.getStartTime(), "yyyy-MM-dd HH:mm:dd"); @@ -405,20 +398,20 @@ public JobExecutionTrendBar getJobExecutionTrendBar(JobExecutionDashboardParam d dashboardParam.getMetricType(), dashboardParam.getSchemaName(), dashboardParam.getTableName(), dashboardParam.getColumnName(), startDateStr, endDateStr); - if (CollectionUtils.isEmpty(trendBars)) { - return trendBar; - } - Map> trendBarListMap = new HashMap<>(); - trendBars.forEach(it -> { - if (trendBarListMap.get(it.getCreateDate()) == null) { - List list = new ArrayList<>(); - list.add(it); - trendBarListMap.put(it.getCreateDate(), list); - } else { - trendBarListMap.get(it.getCreateDate()).add(it); - } - }); + if (CollectionUtils.isNotEmpty(trendBars)) { + trendBars.forEach(it -> { + if (trendBarListMap.get(it.getCreateDate()) == null) { + List list = new ArrayList<>(); + list.add(it); + trendBarListMap.put(it.getCreateDate(), list); + } else { + trendBarListMap.get(it.getCreateDate()).add(it); + } + }); + } else { + return null; + } List allList = new ArrayList<>(); List successList = new ArrayList<>(); @@ -431,14 +424,13 @@ public JobExecutionTrendBar getJobExecutionTrendBar(JobExecutionDashboardParam d successList.add(0); failureList.add(0); } else { - int all = 0; int success = 0; int failure = 0; for (JobExecutionTrendBarItem item :list) { if (item.getStatus() == 6) { - failure++; + failure += item.getNum(); } else if (item.getStatus() == 7) { - success++; + success += item.getNum(); } } allList.add(failure+success); @@ -447,7 +439,6 @@ public JobExecutionTrendBar getJobExecutionTrendBar(JobExecutionDashboardParam d } }); - trendBar.setDateList(dateList); trendBar.setAllList(allList); trendBar.setSuccessList(successList); diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobServiceImpl.java index 9e5419310..b8200a991 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobServiceImpl.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobServiceImpl.java @@ -519,7 +519,7 @@ private JobExecution getJobExecution(Job job, LocalDateTime scheduleTime) { jobExecution.setId(null); jobExecution.setJobId(jobId); jobExecution.setParameter(executionParameter); - jobExecution.setName(job.getName() + "_task_" + System.currentTimeMillis()); + jobExecution.setName(job.getName() + "_" + System.currentTimeMillis()); jobExecution.setJobType(job.getType()); jobExecution.setErrorDataStorageType(errorDataStorageType); jobExecution.setErrorDataStorageParameter(errorDataStorageParameter); diff --git a/datavines-server/src/main/resources/application.yaml b/datavines-server/src/main/resources/application.yaml index ae85c0707..383521ff5 100644 --- a/datavines-server/src/main/resources/application.yaml +++ b/datavines-server/src/main/resources/application.yaml @@ -63,8 +63,8 @@ spring: mybatis-plus: type-enums-package: io.datavines.*.enums - configuration: - log-impl: org.apache.ibatis.logging.stdout.StdOutImpl +# configuration: +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl server: port: 5600 @@ -87,9 +87,9 @@ spring: on-profile: mysql datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://116.205.236.108:3306/datavines?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai + url: jdbc:mysql://127.0.0.1:3306/datavines?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai username: root - password: vines@0825 + password: 123456 quartz: properties: org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate \ No newline at end of file diff --git a/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml b/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml index 4d5e8d44c..8e064e155 100644 --- a/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml +++ b/datavines-server/src/main/resources/mapper/JobExecutionMapper.xml @@ -109,7 +109,7 @@ - + { // refresh search input diff --git a/datavines-ui/src/global.less b/datavines-ui/src/global.less index 9faae6fa6..cf712741c 100644 --- a/datavines-ui/src/global.less +++ b/datavines-ui/src/global.less @@ -4,7 +4,6 @@ body, html { margin: 0; font-size: 13px; color: #333; - } // theme variable diff --git a/datavines-ui/src/locale/en_US.ts b/datavines-ui/src/locale/en_US.ts index 27ed7ffff..e62f876ed 100755 --- a/datavines-ui/src/locale/en_US.ts +++ b/datavines-ui/src/locale/en_US.ts @@ -97,6 +97,12 @@ export default { jobs_task_name: 'JobExecution Name', jobs_task_type: 'Job Type', jobs_task_status: 'JobExecution Status', + jobs_task_schema_name: 'Database', + jobs_task_table_name: 'Table', + jobs_task_column_name: 'Column', + jobs_task_metric_type: 'Metric Type', + jobs_task_start_time: 'Start Time', + jobs_task_end_time: 'End Time', jobs_task_stop_btn: 'Stop', jobs_task_result: 'ExecutionResult', jobs_task_error_data: 'Error', diff --git a/datavines-ui/src/locale/zh_CN.ts b/datavines-ui/src/locale/zh_CN.ts index 4c833e5a7..44fad5867 100755 --- a/datavines-ui/src/locale/zh_CN.ts +++ b/datavines-ui/src/locale/zh_CN.ts @@ -97,6 +97,12 @@ export default { jobs_task_name: '执行记录名称', jobs_task_type: '执行记录类型', jobs_task_status: '执行记录状态', + jobs_task_schema_name: '数据库名', + jobs_task_table_name: '表名', + jobs_task_column_name: '列名', + jobs_task_metric_type: '规则类型', + jobs_task_start_time: '开始时间', + jobs_task_end_time: '结束时间', jobs_task_stop_btn: '停止', jobs_task_result: '结果', jobs_task_error_data: '错误数据', diff --git a/datavines-ui/src/router/detailRouter.tsx b/datavines-ui/src/router/detailRouter.tsx index 15bcd6370..6e8100435 100755 --- a/datavines-ui/src/router/detailRouter.tsx +++ b/datavines-ui/src/router/detailRouter.tsx @@ -8,15 +8,15 @@ const detailRouter: TRouter = { path: '/main/detail/:id/dashboard', key: '/main/detail/:id/dashboard', label: '', - exact: true, + exact: false, icon: , - component: lazy(() => import(/* webpackChunkName: 'view-detail-jobs' */ '@/view/Main/HomeDetail/Dashboard')), + component: lazy(() => import(/* webpackChunkName: 'view-detail-dashboard' */ '@/view/Main/HomeDetail/Dashboard')), }, 'dv-detail-editor': { path: '/main/detail/:id/editor', key: '/main/detail/:id/editor', label: '', - exact: false, + exact: true, icon: , component: lazy(() => import(/* webpackChunkName: 'view-detail-editor' */ '@/view/Main/HomeDetail/EditorData')), }, diff --git a/datavines-ui/src/view/Main/Home/index.tsx b/datavines-ui/src/view/Main/Home/index.tsx index cf39bb28f..7ba329a86 100644 --- a/datavines-ui/src/view/Main/Home/index.tsx +++ b/datavines-ui/src/view/Main/Home/index.tsx @@ -51,7 +51,7 @@ function App() { getData(); }, { immediate: true }); const goDetail = usePersistFn((record: IDataSourceListItem) => { - history.push(`/main/detail/${record.id}/editor?name=${record.name}`); + history.push(`/main/detail/${record.id}/dashboard?name=${record.name}`); }); const onSearch = usePersistFn((values) => { getData(values); diff --git a/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx b/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx index ec2933763..bbf59e9ee 100755 --- a/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx +++ b/datavines-ui/src/view/Main/HomeDetail/Dashboard/index.tsx @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import { Button, Cascader, @@ -15,18 +15,19 @@ import { Table } from "antd"; import DashBoard from "@Editor/components/Database/Detail/dashBoard"; -import {SearchForm, Title} from "@/component"; +import {Title} from "@/component"; import {useIntl} from "react-intl"; import * as echarts from 'echarts'; -import {useHistory, useRouteMatch} from "react-router-dom"; +import {useRouteMatch} from "react-router-dom"; import {$http} from "@/http"; -import {IF, Popconfirm, useMount} from "@Editor/common"; +import {useMount} from "@Editor/common"; import {ColumnsType} from "antd/lib/table"; import {TJobsInstanceTableItem} from "@/type/JobsInstance"; import {defaultRender} from "utils/helper"; import {useInstanceErrorDataModal} from "view/Main/HomeDetail/Jobs/useInstanceErrorDataModal"; import {useInstanceResult} from "view/Main/HomeDetail/Jobs/useInstanceResult"; import {useLogger} from "view/Main/HomeDetail/Jobs/useLogger"; +import { useWatch } from '@/common'; type TJobs = { datasourceId?: any, @@ -39,8 +40,6 @@ interface Option { isLeaf?: boolean; } - - const app: any = {}; const posList = [ 'left', @@ -89,40 +88,6 @@ app.configParameters = { } }; -app.config = { - rotate: 90, - align: 'left', - verticalAlign: 'middle', - position: 'insideBottom', - distance: 15, - onChange: function () { - const labelOption: BarLabelOption = { - rotate: app.config.rotate as BarLabelOption['rotate'], - align: app.config.align as BarLabelOption['align'], - verticalAlign: app.config - .verticalAlign as BarLabelOption['verticalAlign'], - position: app.config.position as BarLabelOption['position'], - distance: app.config.distance as BarLabelOption['distance'] - }; - } -}; - -type BarLabelOption = NonNullable; - -const labelOption: BarLabelOption = { - show: true, - position: app.config.position as BarLabelOption['position'], - distance: app.config.distance as BarLabelOption['distance'], - align: app.config.align as BarLabelOption['align'], - verticalAlign: app.config.verticalAlign as BarLabelOption['verticalAlign'], - rotate: app.config.rotate as BarLabelOption['rotate'], - formatter: '{c} {name|{a}}', - fontSize: 16, - rich: { - name: {} - } -}; - const Dashboard = ({ datasourceId }: TJobs) => { const [dqBarOption, setDqBarOption] = useState(); const [dqPieOption, setDqPieOption] = useState(); @@ -163,6 +128,12 @@ const Dashboard = ({ datasourceId }: TJobs) => { columnName : value[2] }) } + } else { + setEntityParam({ + schemaName : null, + tableName : null, + columnName : null + }) } }; @@ -256,7 +227,6 @@ const Dashboard = ({ datasourceId }: TJobs) => { color: ['#ef6567', '#91cd77'], series: [ { - name: 'Execution Agg Pie', type: 'pie', radius: ['40%', '70%'], avoidLabelOverlap: false, @@ -308,63 +278,45 @@ const Dashboard = ({ datasourceId }: TJobs) => { const barOption = { tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow' - } + trigger: 'axis' + }, + legend: { + data: ['All', 'Success', 'Failure'] + }, + grid: { + left: '3%', + right: '4%', + bottom: '3%', + containLabel: true }, toolbox: { - show: true, - orient: 'vertical', - left: 'right', - top: 'center', feature: { - mark: { show: true }, - dataView: { show: true, readOnly: false }, - magicType: { show: true, type: ['line', 'bar', 'stack'] }, - restore: { show: true }, - saveAsImage: { show: true } + saveAsImage: {} } }, - xAxis: [ - { - type: 'category', - axisTick: { show: false }, - data: res.dateList - } - ], - yAxis: [ - { - type: 'value' - } - ], + xAxis: { + type: 'category', + boundaryGap: false, + data: res.dateList + }, + yAxis: { + type: 'value' + }, series: [ { name: 'All', - type: 'bar', - barGap: 0, - label: labelOption, - emphasis: { - focus: 'series' - }, + type: 'line', data: res.allList }, { name: 'Success', - type: 'bar', - label: labelOption, - emphasis: { - focus: 'series' - }, + type: 'line', data: res.successList }, { name: 'Failure', - type: 'bar', - label: labelOption, - emphasis: { - focus: 'series' - }, + type: 'line', + color: 'red', data: res.failureList } ] @@ -377,21 +329,42 @@ const Dashboard = ({ datasourceId }: TJobs) => { } }; + useEffect(() => { + + }, []); + + useWatch([(match.params as any).id], () =>{ + refreshData() + }) + useMount(async () => { + refreshData() + }); + + const refreshData = async () => { + setEntityParam({ + schemaName: null, + tableName: null, + columnName: null + }) + + setMetricType(null) + setStartTime(null) + setEndTime(null) const $metricList = await $http.get('metric/list/DATA_QUALITY'); let $reMetricList = $metricList ? JSON.parse(JSON.stringify($metricList)) : []; - const $reMetricList1 : ((prevState: never[]) => never[]) | { value: any; label: any;}[]=[]; + const $reMetricList1: ((prevState: never[]) => never[]) | { value: any; label: any; }[] = []; $reMetricList.forEach((item: { key: any; label: any; }) => { return $reMetricList1?.push({value: item.key, label: item.label}); }) // @ts-ignore setMetricList($reMetricList1); - console.log("metricList: ", $reMetricList ) + console.log("metricList: ", $reMetricList) const $datasourceId = datasourceId || (match.params as any).id const $databases = await $http.get(`/datasource/${$datasourceId}/databases`); let $reDatabases = $databases ? JSON.parse(JSON.stringify($databases)) : []; - const $reDatabases1: ((prevState: never[]) => never[]) | { value: any; label: any; isLeaf:any;}[]=[]; + const $reDatabases1: ((prevState: never[]) => never[]) | { value: any; label: any; isLeaf: any; }[] = []; $reDatabases.forEach((item: { name: any; }) => { return $reDatabases1.push({value: item.name, label: item.name, isLeaf: false}); }) @@ -400,10 +373,9 @@ const Dashboard = ({ datasourceId }: TJobs) => { getJobExecutionData(pageParam); getJobExecutionAggPie(); getJobExecutionTrendBar(); - }); + } const onPageChange = ({ current, pageSize }: any) => { - console.log("current page :" , current) setPageParam({ pageNumber : current, pageSize : pageSize @@ -426,7 +398,6 @@ const Dashboard = ({ datasourceId }: TJobs) => { }; const onMetricSelectChange = (value: string) => { - console.log(value) setMetricType(value) }; @@ -452,6 +423,34 @@ const Dashboard = ({ datasourceId }: TJobs) => { width: 300, render: (text) => defaultRender(text, 300), }, + { + title: intl.formatMessage({ id: 'jobs_task_schema_name' }), + dataIndex: 'schemaName', + key: 'schemaName', + width: 100, + render: (text) => defaultRender(text, 300), + }, + { + title: intl.formatMessage({ id: 'jobs_task_table_name' }), + dataIndex: 'tableName', + key: 'tableName', + width: 200, + render: (text) => defaultRender(text, 300), + }, + { + title: intl.formatMessage({ id: 'jobs_task_column_name' }), + dataIndex: 'columnName', + key: 'columnName', + width: 200, + render: (text) => defaultRender(text, 300), + }, + { + title: intl.formatMessage({ id: 'jobs_task_metric_type' }), + dataIndex: 'metricType', + key: 'metricType', + width: 300, + render: (text) => defaultRender(text, 300), + }, { title: intl.formatMessage({ id: 'jobs_task_type' }), dataIndex: 'jobType', @@ -467,9 +466,16 @@ const Dashboard = ({ datasourceId }: TJobs) => { render: (text: string) =>
{text}
, }, { - title: intl.formatMessage({ id: 'jobs_update_time' }), - dataIndex: 'updateTime', - key: 'updateTime', + title: intl.formatMessage({ id: 'jobs_task_start_time' }), + dataIndex: 'startTime', + key: 'startTime', + width: 160, + render: (text: string) =>
{text || '--'}
, + }, + { + title: intl.formatMessage({ id: 'jobs_task_end_time' }), + dataIndex: 'endTime', + key: 'endTime', width: 160, render: (text: string) =>
{text || '--'}
, }, diff --git a/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx b/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx index 8a9b023b3..e2b495c23 100755 --- a/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx +++ b/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsInstance.tsx @@ -1,168 +1,203 @@ -/* eslint-disable react/no-children-prop */ -import React, { useState } from 'react'; -import { Table, Form } from 'antd'; -import { ColumnsType } from 'antd/lib/table'; -import { useIntl } from 'react-intl'; -import querystring from 'querystring'; -import { TJobsInstanceTableData, TJobsInstanceTableItem } from '@/type/JobsInstance'; -import { Title, SearchForm } from '@/component'; -import { useMount, IF } from '@/common'; -import { $http } from '@/http'; -import { defaultRender } from '@/utils/helper'; -import { useLogger } from './useLogger'; -import { useInstanceErrorDataModal } from './useInstanceErrorDataModal'; -import { useInstanceResult } from './useInstanceResult'; - -const JobsInstance = () => { - const intl = useIntl(); - const form = Form.useForm()[0]; - const [loading, setLoading] = useState(false); - const { Render: RenderErrorDataModal, show: showErrorDataModal } = useInstanceErrorDataModal({}); - const { Render: RenderResultModal, show: showResultModal } = useInstanceResult({}); - const { Render: RenderLoggerModal, show: showLoggerModal } = useLogger({}); - const [tableData, setTableData] = useState({ list: [], total: 0 }); - const [pageParams, setPageParams] = useState({ - pageNumber: 1, - pageSize: 10, - }); - const [qs] = useState(querystring.parse(window.location.href.split('?')[1] || '')); - const getData = async (values?: any, $pageParams?: any) => { - try { - setLoading(true); - const res = (await $http.post('/job/execution/page', { - jobId: qs.jobId, - ...($pageParams || pageParams), - ...(values || form.getFieldsValue()), - })) || []; - setTableData({ - list: res?.records || [], - total: res.total || 0, - }); - } catch (error) { - } finally { - setLoading(false); - } - }; - useMount(() => { - getData(); - }); - const onSearch = (_values: any) => { - setPageParams({ ...pageParams, pageNumber: 1 }); - getData({ - ..._values, - pageNumber: 1, - }); - }; - const onChange = ({ current, pageSize }: any) => { - setPageParams({ - pageNumber: current, - pageSize, - }); - getData(null, { - pageNumber: current, - pageSize, - }); - }; - const onStop = async (record: TJobsInstanceTableItem) => { - try { - setLoading(true); - await $http.delete(`job/execution/kill/${record.id}`); - getData(); - } catch (error) { - } finally { - setLoading(false); - } - }; - const onLog = (record: TJobsInstanceTableItem) => { - showLoggerModal(record); - }; - const onResult = (record: TJobsInstanceTableItem) => { - showResultModal(record); - }; - const onErrorData = (record: TJobsInstanceTableItem) => { - showErrorDataModal(record); - }; - const columns: ColumnsType = [ - { - title: intl.formatMessage({ id: 'jobs_task_name' }), - dataIndex: 'name', - key: 'name', - width: 300, - render: (text) => defaultRender(text, 300), - }, - { - title: intl.formatMessage({ id: 'jobs_task_type' }), - dataIndex: 'jobType', - key: 'jobType', - width: 140, - render: (text: string) =>
{text}
, - }, - { - title: intl.formatMessage({ id: 'jobs_task_status' }), - dataIndex: 'status', - key: 'status', - width: 140, - render: (text: string) =>
{text}
, - }, - { - title: intl.formatMessage({ id: 'jobs_update_time' }), - dataIndex: 'updateTime', - key: 'updateTime', - width: 160, - render: (text: string) =>
{text || '--'}
, - }, - { - title: intl.formatMessage({ id: 'common_action' }), - fixed: 'right', - key: 'right', - dataIndex: 'right', - width: 240, - render: (text: string, record: TJobsInstanceTableItem) => ( - <> - - { onStop(record); }}>{intl.formatMessage({ id: 'jobs_task_stop_btn' })} - - { onLog(record); }}>{intl.formatMessage({ id: 'jobs_task_log_btn' })} - { onResult(record); }}>{intl.formatMessage({ id: 'jobs_task_result' })} - { onErrorData(record); }}>{intl.formatMessage({ id: 'jobs_task_error_data' })} - - ), - }, - ]; - return ( -
- {/* */} - <div> - <div className="dv-flex-between"> - <SearchForm form={form} onSearch={onSearch} placeholder={intl.formatMessage({ id: 'common_search' })} /> - </div> - </div> - <Table<TJobsInstanceTableItem> - size="middle" - loading={loading} - rowKey="id" - columns={columns} - dataSource={tableData.list || []} - onChange={onChange} - pagination={{ - size: 'small', - total: tableData.total, - showSizeChanger: true, - current: pageParams.pageNumber, - pageSize: pageParams.pageSize, - }} - /> - <RenderLoggerModal /> - <RenderErrorDataModal /> - <RenderResultModal /> - </div> - ); -}; - -export default JobsInstance; +/* eslint-disable react/no-children-prop */ +import React, { useState } from 'react'; +import { Table, Form } from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import { useIntl } from 'react-intl'; +import querystring from 'querystring'; +import { TJobsInstanceTableData, TJobsInstanceTableItem } from '@/type/JobsInstance'; +import { Title, SearchForm } from '@/component'; +import { useMount, IF } from '@/common'; +import { $http } from '@/http'; +import { defaultRender } from '@/utils/helper'; +import { useLogger } from './useLogger'; +import { useInstanceErrorDataModal } from './useInstanceErrorDataModal'; +import { useInstanceResult } from './useInstanceResult'; + +const JobsInstance = () => { + const intl = useIntl(); + const form = Form.useForm()[0]; + const [loading, setLoading] = useState(false); + const { Render: RenderErrorDataModal, show: showErrorDataModal } = useInstanceErrorDataModal({}); + const { Render: RenderResultModal, show: showResultModal } = useInstanceResult({}); + const { Render: RenderLoggerModal, show: showLoggerModal } = useLogger({}); + const [tableData, setTableData] = useState<TJobsInstanceTableData>({ list: [], total: 0 }); + const [pageParams, setPageParams] = useState({ + pageNumber: 1, + pageSize: 10, + }); + const [qs] = useState(querystring.parse(window.location.href.split('?')[1] || '')); + const getData = async (values?: any, $pageParams?: any) => { + try { + setLoading(true); + const res = (await $http.post('/job/execution/page', { + jobId: qs.jobId, + ...($pageParams || pageParams), + ...(values || form.getFieldsValue()), + })) || []; + setTableData({ + list: res?.records || [], + total: res.total || 0, + }); + } catch (error) { + } finally { + setLoading(false); + } + }; + useMount(() => { + getData(); + }); + const onSearch = (_values: any) => { + setPageParams({ ...pageParams, pageNumber: 1 }); + getData({ + ..._values, + pageNumber: 1, + }); + }; + const onChange = ({ current, pageSize }: any) => { + setPageParams({ + pageNumber: current, + pageSize, + }); + getData(null, { + pageNumber: current, + pageSize, + }); + }; + const onStop = async (record: TJobsInstanceTableItem) => { + try { + setLoading(true); + await $http.delete(`job/execution/kill/${record.id}`); + getData(); + } catch (error) { + } finally { + setLoading(false); + } + }; + const onLog = (record: TJobsInstanceTableItem) => { + showLoggerModal(record); + }; + const onResult = (record: TJobsInstanceTableItem) => { + showResultModal(record); + }; + const onErrorData = (record: TJobsInstanceTableItem) => { + showErrorDataModal(record); + }; + const columns: ColumnsType<TJobsInstanceTableItem> = [ + { + title: intl.formatMessage({ id: 'jobs_task_name' }), + dataIndex: 'name', + key: 'name', + width: 300, + render: (text) => defaultRender(text, 300), + }, + { + title: intl.formatMessage({ id: 'jobs_task_schema_name' }), + dataIndex: 'schemaName', + key: 'schemaName', + width: 100, + render: (text) => defaultRender(text, 300), + }, + { + title: intl.formatMessage({ id: 'jobs_task_table_name' }), + dataIndex: 'tableName', + key: 'tableName', + width: 200, + render: (text) => defaultRender(text, 300), + }, + { + title: intl.formatMessage({ id: 'jobs_task_column_name' }), + dataIndex: 'columnName', + key: 'columnName', + width: 200, + render: (text) => defaultRender(text, 300), + }, + { + title: intl.formatMessage({ id: 'jobs_task_metric_type' }), + dataIndex: 'metricType', + key: 'metricType', + width: 300, + render: (text) => defaultRender(text, 300), + }, + { + title: intl.formatMessage({ id: 'jobs_task_type' }), + dataIndex: 'jobType', + key: 'jobType', + width: 140, + render: (text: string) => <div>{text}</div>, + }, + { + title: intl.formatMessage({ id: 'jobs_task_status' }), + dataIndex: 'status', + key: 'status', + width: 140, + render: (text: string) => <div>{text}</div>, + }, + { + title: intl.formatMessage({ id: 'jobs_task_start_time' }), + dataIndex: 'startTime', + key: 'startTime', + width: 160, + render: (text: string) => <div>{text || '--'}</div>, + }, + { + title: intl.formatMessage({ id: 'jobs_task_end_time' }), + dataIndex: 'endTime', + key: 'endTime', + width: 160, + render: (text: string) => <div>{text || '--'}</div>, + }, + { + title: intl.formatMessage({ id: 'common_action' }), + fixed: 'right', + key: 'right', + dataIndex: 'right', + width: 240, + render: (text: string, record: TJobsInstanceTableItem) => ( + <> + <IF visible={record.status === 'submitted' || record.status === 'running'}> + <a style={{ marginRight: 5 }} onClick={() => { onStop(record); }}>{intl.formatMessage({ id: 'jobs_task_stop_btn' })}</a> + </IF> + <a style={{ marginRight: 5 }} onClick={() => { onLog(record); }}>{intl.formatMessage({ id: 'jobs_task_log_btn' })}</a> + <a style={{ marginRight: 5 }} onClick={() => { onResult(record); }}>{intl.formatMessage({ id: 'jobs_task_result' })}</a> + <a style={{ marginRight: 5 }} onClick={() => { onErrorData(record); }}>{intl.formatMessage({ id: 'jobs_task_error_data' })}</a> + </> + ), + }, + ]; + return ( + <div + className="dv-page-padding" + style={{ + padding: '0px 20px 0px 0px', + height: 'auto', + }} + > + {/* <Title isBack children={undefined} /> */} + <div> + <div className="dv-flex-between"> + <SearchForm form={form} onSearch={onSearch} placeholder={intl.formatMessage({ id: 'common_search' })} /> + </div> + </div> + <Table<TJobsInstanceTableItem> + size="middle" + loading={loading} + rowKey="id" + columns={columns} + dataSource={tableData.list || []} + onChange={onChange} + pagination={{ + size: 'small', + total: tableData.total, + showSizeChanger: true, + current: pageParams.pageNumber, + pageSize: pageParams.pageSize, + }} + /> + <RenderLoggerModal /> + <RenderErrorDataModal /> + <RenderResultModal /> + </div> + ); +}; + +export default JobsInstance; diff --git a/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsList.tsx b/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsList.tsx index cc661040c..6b595d6ca 100644 --- a/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsList.tsx +++ b/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsList.tsx @@ -20,7 +20,6 @@ type TJobs = { } const Jobs = ({ datasourceId }: TJobs) => { - console.log("datasourceId:" ,datasourceId); const intl = useIntl(); const form = Form.useForm()[0]; const [loading, setLoading] = useState(false); @@ -233,7 +232,6 @@ const Jobs = ({ datasourceId }: TJobs) => { pageNumber: 1, pageSize: 10, }); - // getData(undefined, +key); }; const items: TabsProps['items'] = [ diff --git a/datavines-ui/src/view/Main/HomeDetail/index.tsx b/datavines-ui/src/view/Main/HomeDetail/index.tsx index b4bab2d8c..4a5e31f3e 100755 --- a/datavines-ui/src/view/Main/HomeDetail/index.tsx +++ b/datavines-ui/src/view/Main/HomeDetail/index.tsx @@ -1,143 +1,135 @@ -import React, { useEffect, useState } from 'react'; -import useRoute from 'src/router/useRoute'; -import { useIntl } from 'react-intl'; -import { - Route, Switch, useHistory, useLocation, useParams, useRouteMatch, -} from 'react-router-dom'; -import { ArrowLeftOutlined } from '@ant-design/icons'; -import { Button } from 'antd'; -import { CustomSelect } from '@Editor/common'; -import { useSelector } from 'react-redux'; -import { MenuItem } from '@/component/Menu/MenuAside'; -import MenuLayout from '@/component/Menu/Layout'; -import { $http } from '@/http'; -import store from '@/store'; -import EditorData from '@/view/Main/HomeDetail/EditorData'; -import Jobs from '@/view/Main/HomeDetail/Jobs'; -import Dashboard from "view/Main/HomeDetail/Dashboard"; - -type DataSource = { - id:number, - name:'hellow' -} -const DetailMain = () => { - const { isDetailPage } = useSelector((r:any) => r.commonReducer); - const { workspaceId } = useSelector((r:any) => r.workSpaceReducer); - const { editType } = useSelector((r:any) => r.datasourceReducer); - const location = useLocation(); - const [dataSourceList, setDataSourceList] = useState<DataSource[]>([]); - const params = useParams<{ id: string}>(); - useEffect(() => { - if (isDetailPage) { - try { - $http.get('/datasource/page', { - workSpaceId: workspaceId, - pageNumber: 1, - pageSize: 9999, - }).then((res) => { - setDataSourceList(res?.records || []); - }); - } catch (error) { - console.log('error', error); - } - } - }, [isDetailPage]); - const match = useRouteMatch(); - const intl = useIntl(); - const { detailRoutes, visible } = useRoute(); - if (!visible || !detailRoutes.length) { - return null; - } - const detailMenus = detailRoutes.map((item) => ({ - ...item, - key: item.path.replace(/:id/, (match.params as any).id || ''), - label: intl.formatMessage({ id: item.path as any }), - })) as MenuItem[]; - const generateRoute = (menusArray: MenuItem[]) => menusArray.map((route) => ( - <Route - key={`${route.label}-${route.path}`} - path={route.path} - exact={route.exact ? true : undefined} - component={route.component} - /> - )); - const history = useHistory(); - const goBack = () => { - // eslint-disable-next-line no-unused-expressions - location.pathname.includes('jobs/instance') ? history.goBack() : history.push('/main/home'); - }; - const onChangeDataSource = (id: string) => { - const url = `${match.path}`.replace(/:id/, id); - history.push(`${url}/editor`); - }; - const changeType = () => { - store.dispatch({ - type: 'save_datasource_editType', - payload: !editType, - }); - }; - const renderDataSourcSelect = () => ( - <CustomSelect - showSearch - style={{ - width: 240, - }} - placeholder={intl.formatMessage({ id: 'header_top_search_msg' })} - optionFilterProp="children" - filterOption={(input, option: any) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} - source={dataSourceList} - value={params?.id ? +params.id : undefined} - onChange={onChangeDataSource} - sourceLabelMap="name" - sourceValueMap="id" - /> - ); - const renderTopContent = () => ( - <div - className="dv-title-edit" - style={{ - paddingTop: '20px', - }} - > - <div onClick={goBack} style={{ cursor: 'pointer' }}> - <ArrowLeftOutlined /> - </div> - {renderDataSourcSelect()} - {!location.pathname.includes('jobs') ? ( - <Button style={{ marginLeft: '10px' }} onClick={changeType}> - { editType ? intl.formatMessage({ id: 'jobs_directory' }) : intl.formatMessage({ id: 'jobs_editor' })} - </Button> - ) : ''} - - </div> - ); - return ( - - <MenuLayout menus={detailMenus}> - {renderTopContent()} - <div style={{ - display: location.pathname.includes('dashboard') ? 'block' : 'none', - }} - > - <Dashboard /> - </div> - - <div style={{ - display: location.pathname.includes('editor') ? 'block' : 'none', - }} - > - <EditorData /> - </div> - <div style={{ - display: location.pathname.includes('jobs') ? 'block' : 'none', - }} - > - <Jobs /> - </div> - </MenuLayout> - - ); -}; - -// export default App; -export default DetailMain; +import React, { useEffect, useState } from 'react'; +import useRoute from 'src/router/useRoute'; +import { useIntl } from 'react-intl'; +import { + Route, Switch, useHistory, useLocation, useParams, useRouteMatch, +} from 'react-router-dom'; +import { ArrowLeftOutlined } from '@ant-design/icons'; +import { Button } from 'antd'; +import { CustomSelect } from '@Editor/common'; +import { useSelector } from 'react-redux'; +import { MenuItem } from '@/component/Menu/MenuAside'; +import MenuLayout from '@/component/Menu/Layout'; +import { $http } from '@/http'; +import store from '@/store'; +import EditorData from '@/view/Main/HomeDetail/EditorData'; +import Jobs from '@/view/Main/HomeDetail/Jobs'; +import Dashboard from "view/Main/HomeDetail/Dashboard"; + +type DataSource = { + id:number, + name:string +} +const DetailMain = () => { + const { isDetailPage } = useSelector((r:any) => r.commonReducer); + const { workspaceId } = useSelector((r:any) => r.workSpaceReducer); + const { editType } = useSelector((r:any) => r.datasourceReducer); + const location = useLocation(); + const [dataSourceList, setDataSourceList] = useState<DataSource[]>([]); + const params = useParams<{ id: string}>(); + useEffect(() => { + if (isDetailPage) { + try { + $http.get('/datasource/page', { + workSpaceId: workspaceId, + pageNumber: 1, + pageSize: 9999, + }).then((res) => { + setDataSourceList(res?.records || []); + }); + } catch (error) { + console.log('error', error); + } + } + }, [isDetailPage]); + const match = useRouteMatch(); + const intl = useIntl(); + const { detailRoutes, visible } = useRoute(); + if (!visible || !detailRoutes.length) { + return null; + } + const detailMenus = detailRoutes.map((item) => ({ + ...item, + key: item.path.replace(/:id/, (match.params as any).id || ''), + label: intl.formatMessage({ id: item.path as any }), + })) as MenuItem[]; + + const history = useHistory(); + const goBack = () => { + // eslint-disable-next-line no-unused-expressions + location.pathname.includes('jobs/instance') ? history.goBack() : history.push('/main/home'); + }; + const onChangeDataSource = (id: string) => { + const url = `${match.path}`.replace(/:id/, id); + history.push(`${url}/dashboard`); + }; + const changeType = () => { + store.dispatch({ + type: 'save_datasource_editType', + payload: !editType, + }); + }; + const renderDataSourceSelect = () => ( + <CustomSelect + showSearch + style={{ + width: 240, + }} + placeholder={intl.formatMessage({ id: 'header_top_search_msg' })} + optionFilterProp="children" + filterOption={(input, option: any) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} + source={dataSourceList} + value={params?.id ? +params.id : undefined} + onChange={onChangeDataSource} + sourceLabelMap="name" + sourceValueMap="id" + /> + ); + const renderTopContent = () => ( + <div + className="dv-title-edit" + style={{ + paddingTop: '20px', + }} + > + <div onClick={goBack} style={{ cursor: 'pointer' }}> + <ArrowLeftOutlined /> + </div> + {renderDataSourceSelect()} + {!location.pathname.includes('jobs') && !location.pathname.includes('dashboard') ? ( + <Button style={{ marginLeft: '10px' }} onClick={changeType}> + { editType ? intl.formatMessage({ id: 'jobs_directory' }) : intl.formatMessage({ id: 'jobs_editor' })} + </Button> + ) : ''} + + </div> + ); + return ( + + <MenuLayout menus={detailMenus}> + {renderTopContent()} + <div style={{ + display: location.pathname.includes('dashboard') ? 'block' : 'none', + }} + > + <Dashboard /> + </div> + + <div style={{ + display: location.pathname.includes('editor') ? 'block' : 'none', + }} + > + <EditorData /> + </div> + <div style={{ + display: location.pathname.includes('jobs') ? 'block' : 'none', + }} + > + <Jobs /> + </div> + </MenuLayout> + + ); +}; + +export default DetailMain;