Commit 92ba0fdf authored by wenmo's avatar wenmo

运维首页轮询

parent 4e1fcdff
...@@ -39,20 +39,20 @@ public class JobInstanceController { ...@@ -39,20 +39,20 @@ public class JobInstanceController {
*/ */
@DeleteMapping @DeleteMapping
public Result deleteMul(@RequestBody JsonNode para) { public Result deleteMul(@RequestBody JsonNode para) {
if (para.size()>0){ if (para.size() > 0) {
List<Integer> error = new ArrayList<>(); List<Integer> error = new ArrayList<>();
for (final JsonNode item : para){ for (final JsonNode item : para) {
Integer id = item.asInt(); Integer id = item.asInt();
if(!jobInstanceService.removeById(id)){ if (!jobInstanceService.removeById(id)) {
error.add(id); error.add(id);
} }
} }
if(error.size()==0) { if (error.size() == 0) {
return Result.succeed("删除成功"); return Result.succeed("删除成功");
}else { } else {
return Result.succeed("删除部分成功,但"+error.toString()+"删除失败,共"+error.size()+"次失败。"); return Result.succeed("删除部分成功,但" + error.toString() + "删除失败,共" + error.size() + "次失败。");
} }
}else{ } else {
return Result.failed("请选择要删除的记录"); return Result.failed("请选择要删除的记录");
} }
} }
...@@ -63,7 +63,7 @@ public class JobInstanceController { ...@@ -63,7 +63,7 @@ public class JobInstanceController {
@PostMapping("/getOneById") @PostMapping("/getOneById")
public Result getOneById(@RequestBody JobInstance JobInstance) throws Exception { public Result getOneById(@RequestBody JobInstance JobInstance) throws Exception {
JobInstance = jobInstanceService.getById(JobInstance.getId()); JobInstance = jobInstanceService.getById(JobInstance.getId());
return Result.succeed(JobInstance,"获取成功"); return Result.succeed(JobInstance, "获取成功");
} }
/** /**
...@@ -71,6 +71,14 @@ public class JobInstanceController { ...@@ -71,6 +71,14 @@ public class JobInstanceController {
*/ */
@GetMapping("/getStatusCount") @GetMapping("/getStatusCount")
public Result getStatusCount() { public Result getStatusCount() {
return Result.succeed(jobInstanceService.getStatusCount(),"获取成功"); return Result.succeed(jobInstanceService.getStatusCount(), "获取成功");
}
/**
* 获取Job实例的所有信息
*/
@GetMapping("/getJobInfoDetail")
public Result getJobInfoDetail(@RequestParam Integer id) {
return Result.succeed(jobInstanceService.getJobInfoDetail(id), "获取成功");
} }
} }
package com.dlink.model;
/**
* JobInfoDetail
*
* @author wenmo
* @since 2022/3/1 19:31
**/
public class JobInfoDetail {
private Integer id;
private JobInstance instance;
private Cluster cluster;
private ClusterConfiguration clusterConfiguration;
private Task task;
private History history;
public JobInfoDetail(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public JobInstance getInstance() {
return instance;
}
public void setInstance(JobInstance instance) {
this.instance = instance;
}
public Cluster getCluster() {
return cluster;
}
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
public ClusterConfiguration getClusterConfiguration() {
return clusterConfiguration;
}
public void setClusterConfiguration(ClusterConfiguration clusterConfiguration) {
this.clusterConfiguration = clusterConfiguration;
}
public Task getTask() {
return task;
}
public void setTask(Task task) {
this.task = task;
}
public History getHistory() {
return history;
}
public void setHistory(History history) {
this.history = history;
}
}
package com.dlink.service; package com.dlink.service;
import com.dlink.db.service.ISuperService; import com.dlink.db.service.ISuperService;
import com.dlink.model.JobInfoDetail;
import com.dlink.model.JobInstance; import com.dlink.model.JobInstance;
import com.dlink.model.JobInstanceStatus; import com.dlink.model.JobInstanceStatus;
...@@ -13,4 +14,6 @@ import com.dlink.model.JobInstanceStatus; ...@@ -13,4 +14,6 @@ import com.dlink.model.JobInstanceStatus;
public interface JobInstanceService extends ISuperService<JobInstance> { public interface JobInstanceService extends ISuperService<JobInstance> {
JobInstanceStatus getStatusCount(); JobInstanceStatus getStatusCount();
JobInfoDetail getJobInfoDetail(Integer id);
} }
...@@ -3,11 +3,18 @@ package com.dlink.service.impl; ...@@ -3,11 +3,18 @@ package com.dlink.service.impl;
import com.dlink.assertion.Asserts; import com.dlink.assertion.Asserts;
import com.dlink.db.service.impl.SuperServiceImpl; import com.dlink.db.service.impl.SuperServiceImpl;
import com.dlink.mapper.JobInstanceMapper; import com.dlink.mapper.JobInstanceMapper;
import com.dlink.model.History;
import com.dlink.model.JobInfoDetail;
import com.dlink.model.JobInstance; import com.dlink.model.JobInstance;
import com.dlink.model.JobInstanceCount; import com.dlink.model.JobInstanceCount;
import com.dlink.model.JobInstanceStatus; import com.dlink.model.JobInstanceStatus;
import com.dlink.model.JobStatus; import com.dlink.model.JobStatus;
import com.dlink.service.ClusterConfigurationService;
import com.dlink.service.ClusterService;
import com.dlink.service.HistoryService;
import com.dlink.service.JobInstanceService; import com.dlink.service.JobInstanceService;
import com.dlink.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
...@@ -20,13 +27,23 @@ import java.util.List; ...@@ -20,13 +27,23 @@ import java.util.List;
*/ */
@Service @Service
public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper, JobInstance> implements JobInstanceService { public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper, JobInstance> implements JobInstanceService {
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
@Autowired
private ClusterService clusterService;
@Autowired
private ClusterConfigurationService clusterConfigurationService;
@Override @Override
public JobInstanceStatus getStatusCount() { public JobInstanceStatus getStatusCount() {
List<JobInstanceCount> jobInstanceCounts = baseMapper.countStatus(); List<JobInstanceCount> jobInstanceCounts = baseMapper.countStatus();
JobInstanceStatus jobInstanceStatus = new JobInstanceStatus(); JobInstanceStatus jobInstanceStatus = new JobInstanceStatus();
Integer total = 0; Integer total = 0;
for (JobInstanceCount item : jobInstanceCounts) { for (JobInstanceCount item : jobInstanceCounts) {
Integer counts = Asserts.isNull(item.getCounts())?0:item.getCounts(); Integer counts = Asserts.isNull(item.getCounts()) ? 0 : item.getCounts();
total += counts; total += counts;
switch (JobStatus.get(item.getStatus())) { switch (JobStatus.get(item.getStatus())) {
case INITIALIZING: case INITIALIZING:
...@@ -48,4 +65,20 @@ public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper, ...@@ -48,4 +65,20 @@ public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper,
jobInstanceStatus.setAll(total); jobInstanceStatus.setAll(total);
return jobInstanceStatus; return jobInstanceStatus;
} }
@Override
public JobInfoDetail getJobInfoDetail(Integer id) {
JobInfoDetail jobInfoDetail = new JobInfoDetail(id);
JobInstance jobInstance = getById(id);
Asserts.checkNull(jobInstance, "该任务实例不存在");
jobInfoDetail.setInstance(jobInstance);
jobInfoDetail.setTask(taskService.getTaskInfoById(jobInstance.getTaskId()));
jobInfoDetail.setCluster(clusterService.getById(jobInstance.getClusterId()));
History history = historyService.getById(jobInstance.getHistoryId());
jobInfoDetail.setHistory(history);
if (Asserts.isNotNull(history) && Asserts.isNotNull(history.getClusterConfigurationId())) {
jobInfoDetail.setClusterConfiguration(clusterConfigurationService.getClusterConfigById(history.getClusterConfigurationId()));
}
return jobInfoDetail;
}
} }
...@@ -28,6 +28,12 @@ export default [ ...@@ -28,6 +28,12 @@ export default [
icon: 'control', icon: 'control',
component: './DevOps', component: './DevOps',
}, },
{
path: '/job',
name: 'job',
component: './DevOps/JobInfo',
hideInMenu: true,
},
{ {
path: '/registration', path: '/registration',
name: 'registration', name: 'registration',
......
export type HistoryItem = {
id: number;
clusterId: number;
clusterAlias: string;
session: string;
jobId: string;
jobName: string;
jobManagerAddress: string;
statusText: string;
status: number;
statement: string;
error: string;
result: string;
config: string;
type: string;
startTime: string;
endTime: string;
taskId: number;
taskAlias: string;
};
...@@ -13,31 +13,11 @@ import styles from "./index.less"; ...@@ -13,31 +13,11 @@ import styles from "./index.less";
import {Scrollbars} from 'react-custom-scrollbars'; import {Scrollbars} from 'react-custom-scrollbars';
import StudioPreview from "../StudioPreview"; import StudioPreview from "../StudioPreview";
import {getJobData} from "@/pages/FlinkSqlStudio/service"; import {getJobData} from "@/pages/FlinkSqlStudio/service";
import {HistoryItem} from "@/components/Studio/StudioConsole/StudioHistory/data";
const { Title, Paragraph, Text, Link } = Typography; const { Title, Paragraph, Text, Link } = Typography;
type HistoryItem = {
id: number;
clusterId: number;
clusterAlias: string;
session: string;
jobId: string;
jobName: string;
jobManagerAddress: string;
statusText: string;
status: number;
statement: string;
error: string;
result: string;
config: string;
type: string;
startTime: string;
endTime: string;
taskId: number;
taskAlias: string;
};
type HistoryConfig={ type HistoryConfig={
useSession: boolean; useSession: boolean;
session: string; session: string;
...@@ -101,297 +81,297 @@ const StudioHistory = (props: any) => { ...@@ -101,297 +81,297 @@ const StudioHistory = (props: any) => {
<> <>
<ProList<HistoryItem> <ProList<HistoryItem>
actionRef={refs.history} actionRef={refs.history}
toolBarRender={() => { toolBarRender={() => {
return [ return [
// <Button key="3" type="primary" icon={<ReloadOutlined />}/>, // <Button key="3" type="primary" icon={<ReloadOutlined />}/>,
]; ];
}} }}
search={{ search={{
filterType: 'light', filterType: 'light',
}} }}
rowKey="id" rowKey="id"
headerTitle="执行历史" headerTitle="执行历史"
request={(params, sorter, filter) => queryData(url,{...params, sorter:{id:'descend'}, filter})} request={(params, sorter, filter) => queryData(url,{...params, sorter:{id:'descend'}, filter})}
pagination={{ pagination={{
pageSize: 5, pageSize: 5,
}} }}
showActions="hover" showActions="hover"
metas={{ metas={{
title: { title: {
dataIndex: 'jobId', dataIndex: 'jobId',
title: 'JobId', title: 'JobId',
render: (_, row) => { render: (_, row) => {
return ( return (
<Space size={0}> <Space size={0}>
<Tag color="blue" key={row.jobId}> <Tag color="blue" key={row.jobId}>
<FireOutlined /> {row.jobId} <FireOutlined /> {row.jobId}
</Tag> </Tag>
</Space> </Space>
); );
}, },
},
description: {
search: false,
render:(_, row)=>{
return (<Paragraph>
<blockquote>
<Link href={`http://${row.jobManagerAddress}`} target="_blank">
[{row.jobManagerAddress}]
</Link>
<Divider type="vertical"/>开始于:{row.startTime}
<Divider type="vertical"/>完成于:{row.endTime}
</blockquote>
</Paragraph>)
}
},
subTitle: {
render: (_, row) => {
return (
<Space size={0}>
{row.jobName?(
<Tag color="gray" key={row.jobName}>
{row.jobName}
</Tag>
):''}
{row.session?(
<Tag color="orange" key={row.session}>
<MessageOutlined /> {row.session}
</Tag>
):''}
{row.clusterAlias?(
<Tag color="green" key={row.clusterAlias}>
<ClusterOutlined /> {row.clusterAlias}
</Tag>
):(<Tag color="green" key={row.clusterAlias}>
<ClusterOutlined /> 本地环境
</Tag>)}
{row.type?(
<Tag color="blue" key={row.type}>
<RocketOutlined /> {row.type}
</Tag>
):''}
{(row.status==2) ?
(<><Badge status="success"/><Text type="success">SUCCESS</Text></>):
(row.status==1) ?
<><Badge status="success"/><Text type="secondary">RUNNING</Text></> :
(row.status==3) ?
<><Badge status="error"/><Text type="danger">FAILED</Text></> :
(row.status==4) ?
<><Badge status="error"/><Text type="warning">CANCEL</Text></> :
(row.status==0) ?
<><Badge status="error"/><Text type="warning">INITIALIZE</Text></> :
<><Badge status="success"/><Text type="danger">UNKNOWEN</Text></>}
</Space>
);
},
search: false,
},
actions: {
render: (text, row) => [
<a key="config" onClick={()=>{showDetail(row,1)}}>
执行配置
</a>,
<a key="statement" onClick={()=>{showDetail(row,2)}}>
FlinkSql语句
</a>,
<a key="result" onClick={()=>{showDetail(row,3)}}>
预览数据
</a>,
<a key="error" onClick={()=>{showDetail(row,4)}}>
异常信息
</a>,
<a key="delete" onClick={()=>{removeHistory(row)}}>
删除
</a>,
],
search: false,
},
jobName: {
dataIndex: 'jobName',
title: '作业名',
},
clusterId: {
dataIndex: 'clusterId',
title: '运行集群',
},
session: {
dataIndex: 'session',
title: '共享会话',
},
status: {
// 自己扩展的字段,主要用于筛选,不在列表中显示
title: '状态',
valueType: 'select',
valueEnum: {
'': {text: '全部', status: 'ALL'},
0: {
text: '初始化',
status: 'INITIALIZE',
}, },
1: { description: {
text: '运行中', search: false,
status: 'RUNNING', render:(_, row)=>{
return (<Paragraph>
<blockquote>
<Link href={`http://${row.jobManagerAddress}`} target="_blank">
[{row.jobManagerAddress}]
</Link>
<Divider type="vertical"/>开始于:{row.startTime}
<Divider type="vertical"/>完成于:{row.endTime}
</blockquote>
</Paragraph>)
}
}, },
2: { subTitle: {
text: '成功', render: (_, row) => {
status: 'SUCCESS', return (
<Space size={0}>
{row.jobName?(
<Tag color="gray" key={row.jobName}>
{row.jobName}
</Tag>
):''}
{row.session?(
<Tag color="orange" key={row.session}>
<MessageOutlined /> {row.session}
</Tag>
):''}
{row.clusterAlias?(
<Tag color="green" key={row.clusterAlias}>
<ClusterOutlined /> {row.clusterAlias}
</Tag>
):(<Tag color="green" key={row.clusterAlias}>
<ClusterOutlined /> 本地环境
</Tag>)}
{row.type?(
<Tag color="blue" key={row.type}>
<RocketOutlined /> {row.type}
</Tag>
):''}
{(row.status==2) ?
(<><Badge status="success"/><Text type="success">SUCCESS</Text></>):
(row.status==1) ?
<><Badge status="success"/><Text type="secondary">RUNNING</Text></> :
(row.status==3) ?
<><Badge status="error"/><Text type="danger">FAILED</Text></> :
(row.status==4) ?
<><Badge status="error"/><Text type="warning">CANCEL</Text></> :
(row.status==0) ?
<><Badge status="error"/><Text type="warning">INITIALIZE</Text></> :
<><Badge status="success"/><Text type="danger">UNKNOWEN</Text></>}
</Space>
);
},
search: false,
}, },
3: { actions: {
text: '失败', render: (text, row) => [
status: 'FAILED', <a key="config" onClick={()=>{showDetail(row,1)}}>
执行配置
</a>,
<a key="statement" onClick={()=>{showDetail(row,2)}}>
FlinkSql语句
</a>,
<a key="result" onClick={()=>{showDetail(row,3)}}>
预览数据
</a>,
<a key="error" onClick={()=>{showDetail(row,4)}}>
异常信息
</a>,
<a key="delete" onClick={()=>{removeHistory(row)}}>
删除
</a>,
],
search: false,
}, },
4: { jobName: {
text: '停止', dataIndex: 'jobName',
status: 'CANCEL', title: '作业名',
}, },
}, clusterId: {
}, dataIndex: 'clusterId',
startTime: { title: '运行集群',
dataIndex: 'startTime', },
title: '开始时间', session: {
valueType: 'dateTimeRange', dataIndex: 'session',
}, title: '共享会话',
endTime: { },
dataIndex: 'endTime', status: {
title: '完成时间', // 自己扩展的字段,主要用于筛选,不在列表中显示
valueType: 'dateTimeRange', title: '状态',
}, valueType: 'select',
}} valueEnum: {
options={{ '': {text: '全部', status: 'ALL'},
search: false, 0: {
setting:false text: '初始化',
}} status: 'INITIALIZE',
/>
<ModalForm
visible={modalVisit}
onFinish={async () => {
}}
onVisibleChange={setModalVisit}
submitter={{
submitButtonProps: {
style: {
display: 'none',
}, },
1: {
text: '运行中',
status: 'RUNNING',
},
2: {
text: '成功',
status: 'SUCCESS',
},
3: {
text: '失败',
status: 'FAILED',
},
4: {
text: '停止',
status: 'CANCEL',
},
},
},
startTime: {
dataIndex: 'startTime',
title: '开始时间',
valueType: 'dateTimeRange',
},
endTime: {
dataIndex: 'endTime',
title: '完成时间',
valueType: 'dateTimeRange',
},
}}
options={{
search: false,
setting:false
}}
/>
<ModalForm
visible={modalVisit}
onFinish={async () => {
}}
onVisibleChange={setModalVisit}
submitter={{
submitButtonProps: {
style: {
display: 'none',
}, },
}} },
}}
>
{type==1 && (
<ProDescriptions
column={2}
title='执行配置'
>
<ProDescriptions.Item span={2} label="JobId" >
<Tag color="blue" key={row.jobId}>
<FireOutlined /> {row.jobId}
</Tag>
</ProDescriptions.Item>
<ProDescriptions.Item label="共享会话" >
{config.useSession?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item label="会话 Key">
{config.session}
</ProDescriptions.Item>
<ProDescriptions.Item label="执行方式" >
{config.useRemote?'远程':'本地'}
</ProDescriptions.Item>
<ProDescriptions.Item label="任务类型">
{config.type}
</ProDescriptions.Item>
<ProDescriptions.Item label="集群ID">
{config.clusterId}
</ProDescriptions.Item>
<ProDescriptions.Item label="集群配置ID">
{config.clusterConfigurationId}
</ProDescriptions.Item>
<ProDescriptions.Item label="预览结果" >
{config.useResult?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item label="打印流" >
{config.useChangeLog?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item label="最大行数">
{config.maxRowNum}
</ProDescriptions.Item>
<ProDescriptions.Item label="自动停止" >
{config.useAutoCancel?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item span={2} label="JobManagerAddress">
{row.jobManagerAddress}
</ProDescriptions.Item>
<ProDescriptions.Item label="作业ID">
{config.taskId}
</ProDescriptions.Item>
<ProDescriptions.Item label="作业名">
{config.jobName}
</ProDescriptions.Item>
<ProDescriptions.Item label="片段机制">
{config.useSqlFragment?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item label="语句集">
{config.useStatementSet?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item label="并行度">
{config.parallelism}
</ProDescriptions.Item>
<ProDescriptions.Item label="CheckPoint">
{config.checkpoint}
</ProDescriptions.Item>
<ProDescriptions.Item label="savePoint 机制">
{config.savePointStrategy}
</ProDescriptions.Item>
<ProDescriptions.Item label="SavePointPath">
{config.savePointPath}
</ProDescriptions.Item>
</ProDescriptions>
)}
{type==2 && (
<ProDescriptions
column={1}
title='FlinkSql 语句'
>
<ProDescriptions.Item label="JobId" >
<Tag color="blue" key={row.jobId}>
<FireOutlined /> {row.jobId}
</Tag>
</ProDescriptions.Item>
<ProDescriptions.Item>
<pre className={styles.code}>{row.statement}</pre>
</ProDescriptions.Item>
</ProDescriptions>
)}
{type==3 && (
<ProDescriptions
column={2}
title='数据预览'
>
<ProDescriptions.Item span={2} label="JobId" >
<Tag color="blue" key={row.jobId}>
<FireOutlined /> {row.jobId}
</Tag>
</ProDescriptions.Item>
<ProDescriptions.Item span={2} >
<StudioPreview result={result} style={{width: '100%'}}/>
</ProDescriptions.Item>
</ProDescriptions>
)}
{type==4 && (
<ProDescriptions
column={1}
title='异常信息'
> >
{type==1 && ( <ProDescriptions.Item label="JobId" >
<ProDescriptions <Tag color="blue" key={row.jobId}>
column={2} <FireOutlined /> {row.jobId}
title='执行配置' </Tag>
> </ProDescriptions.Item>
<ProDescriptions.Item span={2} label="JobId" > <ProDescriptions.Item>
<Tag color="blue" key={row.jobId}>
<FireOutlined /> {row.jobId}
</Tag>
</ProDescriptions.Item>
<ProDescriptions.Item label="共享会话" >
{config.useSession?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item label="会话 Key">
{config.session}
</ProDescriptions.Item>
<ProDescriptions.Item label="执行方式" >
{config.useRemote?'远程':'本地'}
</ProDescriptions.Item>
<ProDescriptions.Item label="任务类型">
{config.type}
</ProDescriptions.Item>
<ProDescriptions.Item label="集群ID">
{config.clusterId}
</ProDescriptions.Item>
<ProDescriptions.Item label="集群配置ID">
{config.clusterConfigurationId}
</ProDescriptions.Item>
<ProDescriptions.Item label="预览结果" >
{config.useResult?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item label="打印流" >
{config.useChangeLog?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item label="最大行数">
{config.maxRowNum}
</ProDescriptions.Item>
<ProDescriptions.Item label="自动停止" >
{config.useAutoCancel?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item span={2} label="JobManagerAddress">
{row.jobManagerAddress}
</ProDescriptions.Item>
<ProDescriptions.Item label="作业ID">
{config.taskId}
</ProDescriptions.Item>
<ProDescriptions.Item label="作业名">
{config.jobName}
</ProDescriptions.Item>
<ProDescriptions.Item label="片段机制">
{config.useSqlFragment?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item label="语句集">
{config.useStatementSet?'启用':'禁用'}
</ProDescriptions.Item>
<ProDescriptions.Item label="并行度">
{config.parallelism}
</ProDescriptions.Item>
<ProDescriptions.Item label="CheckPoint">
{config.checkpoint}
</ProDescriptions.Item>
<ProDescriptions.Item label="savePoint 机制">
{config.savePointStrategy}
</ProDescriptions.Item>
<ProDescriptions.Item label="SavePointPath">
{config.savePointPath}
</ProDescriptions.Item>
</ProDescriptions>
)}
{type==2 && (
<ProDescriptions
column={1}
title='FlinkSql 语句'
>
<ProDescriptions.Item label="JobId" >
<Tag color="blue" key={row.jobId}>
<FireOutlined /> {row.jobId}
</Tag>
</ProDescriptions.Item>
<ProDescriptions.Item>
<pre className={styles.code}>{row.statement}</pre>
</ProDescriptions.Item>
</ProDescriptions>
)}
{type==3 && (
<ProDescriptions
column={2}
title='数据预览'
>
<ProDescriptions.Item span={2} label="JobId" >
<Tag color="blue" key={row.jobId}>
<FireOutlined /> {row.jobId}
</Tag>
</ProDescriptions.Item>
<ProDescriptions.Item span={2} >
<StudioPreview result={result} style={{width: '100%'}}/>
</ProDescriptions.Item>
</ProDescriptions>
)}
{type==4 && (
<ProDescriptions
column={1}
title='异常信息'
>
<ProDescriptions.Item label="JobId" >
<Tag color="blue" key={row.jobId}>
<FireOutlined /> {row.jobId}
</Tag>
</ProDescriptions.Item>
<ProDescriptions.Item>
<Scrollbars style={{height: '400px',width:'100%'}}> <Scrollbars style={{height: '400px',width:'100%'}}>
<pre className={styles.code}>{row.error}</pre> <pre className={styles.code}>{row.error}</pre>
</Scrollbars> </Scrollbars>
</ProDescriptions.Item> </ProDescriptions.Item>
</ProDescriptions> </ProDescriptions>
)} )}
</ModalForm> </ModalForm>
</> </>
); );
}; };
......
...@@ -61,6 +61,7 @@ export default { ...@@ -61,6 +61,7 @@ export default {
'menu.studio': 'FlinkSql IDE', 'menu.studio': 'FlinkSql IDE',
'menu.flinksqlstudio': 'FlinkSQL Studio', 'menu.flinksqlstudio': 'FlinkSQL Studio',
'menu.devops': '运维中心', 'menu.devops': '运维中心',
'menu.job': '作业实例',
'menu.registration.jar': 'Jar 管理', 'menu.registration.jar': 'Jar 管理',
'menu.registration.document': '文档管理', 'menu.registration.document': '文档管理',
'menu.settings': '系统设置', 'menu.settings': '系统设置',
......
import { Descriptions, Badge } from 'antd';
const BaseInfo = (props: any) => {
const {job} = props;
return (<Descriptions bordered>
<Descriptions.Item label="实例状态">{job?.instance.status}</Descriptions.Item>
<Descriptions.Item label="重启次数">{job?.instance.schema}</Descriptions.Item>
<Descriptions.Item label="耗时">{job?.instance.duration}</Descriptions.Item>
<Descriptions.Item label="启动时间">{job?.instance.createTime}</Descriptions.Item>
<Descriptions.Item label="更新时间">{job?.instance.updateTime}</Descriptions.Item>
<Descriptions.Item label="完成时间">{job?.instance.finishTime}</Descriptions.Item>
<Descriptions.Item label="Task" span={3}>
{}
</Descriptions.Item>
<Descriptions.Item >{}</Descriptions.Item>
</Descriptions>)
};
export default BaseInfo;
import React, {useEffect, useState} from 'react';
import { history, useLocation } from 'umi';
import { EllipsisOutlined, CheckCircleOutlined,SyncOutlined,CloseCircleOutlined,MinusCircleOutlined,ClockCircleOutlined,
FireOutlined,ClusterOutlined,RocketOutlined} from '@ant-design/icons';
import { Button, Dropdown, Menu, Tag, Space } from 'antd';
import { PageContainer } from '@ant-design/pro-layout';
import ProCard from '@ant-design/pro-card';
import {JobInfoDetail, StatusCount} from "@/pages/DevOps/data";
import {getJobInfoDetail, getStatusCount} from "@/pages/DevOps/service";
import moment from "moment";
const JobInfo = (props:any) => {
const params = useLocation();
const { } = props;
const id = params.query.id;
const [job, setJob] = useState<JobInfoDetail>();
const [time, setTime] = useState(() => Date.now());
const refreshJobInfoDetail = () => {
const res = getJobInfoDetail(id);
res.then((result)=>{
setJob(result.datas);
setTime(Date.now());
});
};
useEffect(() => {
refreshJobInfoDetail();
let dataPolling = setInterval(refreshJobInfoDetail,3000);
return () => {
clearInterval(dataPolling);
};
}, []);
const handleBack = () => {
history.goBack();
};
return (
<PageContainer
header={{
title: `${job?.instance.name}`,
ghost: true,
extra: [
<Button key="back" type="dashed" onClick={handleBack}>返回</Button>,
<Button key="flinkwebui">FlinkWebUI</Button>,
<Button key="autorestart" type="primary">智能重启</Button>,
<Button key="autostop" type="primary" danger>智能停止</Button>,
<Dropdown
key="dropdown"
trigger={['click']}
overlay={
<Menu>
<Menu.Item key="1">普通停止</Menu.Item>
<Menu.Item key="2">SavePoint停止</Menu.Item>
<Menu.Item key="3">SavePoint暂停</Menu.Item>
</Menu>
}
>
<Button key="4" style={{ padding: '0 8px' }}>
<EllipsisOutlined />
</Button>
</Dropdown>,
],
}}
content={<>
<Space size={0}>
{job?.instance.jid?(
<Tag color="blue" key={job?.instance.jid}>
<FireOutlined /> {job?.instance.jid}
</Tag>
):undefined}
{(job?.instance.status == 'FINISHED') ?
(<Tag icon={<CheckCircleOutlined />} color="success">
FINISHED
</Tag>) :
(job?.instance.status == 'RUNNING') ?
(<Tag icon={<SyncOutlined spin />} color="processing">
RUNNING
</Tag>) :
(job?.instance.status == 'FAILED') ?
(<Tag icon={<CloseCircleOutlined />} color="error">
FAILED
</Tag>) :
(job?.instance.status == 'CANCELED') ?
(<Tag icon={<MinusCircleOutlined />} color="default">
CANCELED
</Tag>) :
(job?.instance.status == 'INITIALIZING') ?
(<Tag icon={<ClockCircleOutlined />} color="default">
INITIALIZING
</Tag>) :(job?.instance.status == 'RESTARTING') ?
(<Tag icon={<ClockCircleOutlined />} color="default">
RESTARTING
</Tag>) :
(<Tag color="default">
UNKNOWEN
</Tag>)
}
{job?.history.type?(
<Tag color="blue" key={job?.history.type}>
<RocketOutlined /> {job?.history.type}
</Tag>
):undefined}
{job?.cluster.alias?(
<Tag color="green" key={job?.cluster.alias}>
<ClusterOutlined /> {job?.cluster.alias}
</Tag>
):(<Tag color="green" key='local'>
<ClusterOutlined /> 本地环境
</Tag>)}
</Space>
</>}
tabBarExtraContent={`上次更新时间:${moment(time).format('HH:mm:ss')}`}
tabList={[
{
tab: '作业总览',
key: 'base',
closable: false,
},
{
tab: '配置信息',
key: 'config',
closable: false,
},
{
tab: 'FlinkSQL',
key: 'flinksql',
closable: false,
},
{
tab: '集群信息',
key: 'cluster',
closable: false,
},
{
tab: '作业快照',
key: 'snapshot',
closable: false,
},
{
tab: '告警记录',
key: 'alert',
closable: false,
},
]}
>
<ProCard direction="column" ghost gutter={[0, 16]}>
<ProCard style={{ height: 200 }} />
<ProCard gutter={16} ghost style={{ height: 200 }}>
<ProCard colSpan={16} />
<ProCard colSpan={8} />
</ProCard>
</ProCard>
</PageContainer>
);
};
export default JobInfo;
import {Tag} from 'antd'; import {Tag} from 'antd';
import { history } from 'umi';
import { import {
CheckCircleOutlined, CheckCircleOutlined,
SyncOutlined, CloseCircleOutlined, MinusCircleOutlined, ClockCircleOutlined, DownOutlined SyncOutlined, CloseCircleOutlined, MinusCircleOutlined, ClockCircleOutlined, DownOutlined
...@@ -8,11 +9,14 @@ import React, {useState} from "react"; ...@@ -8,11 +9,14 @@ import React, {useState} from "react";
import type { ProColumns } from '@ant-design/pro-table'; import type { ProColumns } from '@ant-design/pro-table';
import ProTable from "@ant-design/pro-table"; import ProTable from "@ant-design/pro-table";
import {JobInstanceTableListItem} from "@/pages/DevOps/data"; import {JobInstanceTableListItem} from "@/pages/DevOps/data";
import moment from 'moment';
import {RUN_MODE} from "@/components/Studio/conf";
const url = '/api/jobInstance'; const url = '/api/jobInstance';
const JobInstanceTable = (props: any) => { const JobInstanceTable = (props: any) => {
const {status, dispatch} = props; const {status, activeKey, dispatch} = props;
const [time, setTime] = useState(() => Date.now());
const getColumns = () => { const getColumns = () => {
const columns: ProColumns<JobInstanceTableListItem>[] = [{ const columns: ProColumns<JobInstanceTableListItem>[] = [{
...@@ -23,6 +27,38 @@ const JobInstanceTable = (props: any) => { ...@@ -23,6 +27,38 @@ const JobInstanceTable = (props: any) => {
title: "运行模式", title: "运行模式",
dataIndex: "type", dataIndex: "type",
sorter: true, sorter: true,
valueType: 'radio',
valueEnum: {
'': {text: '全部', status: 'ALL'},
'local': {
text: RUN_MODE.LOCAL,
status: RUN_MODE.LOCAL,
},
'standalone': {
text: RUN_MODE.STANDALONE,
status: RUN_MODE.STANDALONE,
},
'yarn-session': {
text: RUN_MODE.YARN_SESSION,
status: RUN_MODE.YARN_SESSION,
},
'yarn-per-job': {
text: RUN_MODE.YARN_PER_JOB,
status: RUN_MODE.YARN_PER_JOB,
},
'yarn-application': {
text: RUN_MODE.YARN_APPLICATION,
status: RUN_MODE.YARN_APPLICATION,
},
'kubernetes-session': {
text: RUN_MODE.KUBERNETES_SESSION,
status: RUN_MODE.KUBERNETES_SESSION,
},
'kubernetes-application': {
text: RUN_MODE.KUBERNETES_APPLICATION,
status: RUN_MODE.KUBERNETES_APPLICATION,
},
},
},{ },{
title: "集群实例", title: "集群实例",
dataIndex: "clusterAlias", dataIndex: "clusterAlias",
...@@ -35,6 +71,7 @@ const JobInstanceTable = (props: any) => { ...@@ -35,6 +71,7 @@ const JobInstanceTable = (props: any) => {
title: "状态", title: "状态",
dataIndex: "status", dataIndex: "status",
sorter: true, sorter: true,
hideInSearch: true,
render: (_, row) => { render: (_, row) => {
return ( return (
<> <>
...@@ -72,37 +109,59 @@ const JobInstanceTable = (props: any) => { ...@@ -72,37 +109,59 @@ const JobInstanceTable = (props: any) => {
dataIndex: "createTime", dataIndex: "createTime",
sorter: true, sorter: true,
valueType: 'dateTime', valueType: 'dateTime',
hideInSearch: true,
}, { }, {
title: "更新时间", title: "更新时间",
dataIndex: "updateTime", dataIndex: "updateTime",
sorter: true, sorter: true,
valueType: 'dateTime', valueType: 'dateTime',
hideInTable: true hideInTable: true,
hideInSearch: true,
}, { }, {
title: "结束时间", title: "结束时间",
dataIndex: "finishTime", dataIndex: "finishTime",
sorter: true, sorter: true,
valueType: 'dateTime', valueType: 'dateTime',
hideInTable: true hideInTable: true,
hideInSearch: true,
}, { }, {
title: "耗时", title: "耗时",
dataIndex: "duration", dataIndex: "duration",
sorter: true, sorter: true,
valueType: 'second', valueType: 'second',
hideInSearch: true,
},]; },];
return columns; return columns;
}; };
return ( return (
<><ProTable <><ProTable
request={(params, sorter, filter) => queryData(url, {...params,status, sorter: {id: 'descend'}, filter})} request={(params, sorter, filter) => {
setTime(Date.now());
return queryData(url, {...params,status, sorter: {id: 'descend'}, filter});
}}
columns={getColumns()} columns={getColumns()}
size="small" size="small"
search={false} search={{
toolBarRender={false} filterType: 'light',
}}
headerTitle={`上次更新时间:${moment(time).format('HH:mm:ss')}`}
polling={status==activeKey?3000:undefined}
pagination={{ pagination={{
pageSize: 5, pageSize: 5,
}} }}
onRow={ record => {
return {
onClick: event => {
history.push({
pathname: '/job',
query: {
id: record.id,
},
});
},
};
}}
/> />
</> </>
); );
......
import {ClusterTableListItem} from "@/pages/Cluster/data";
import {ClusterConfigurationTableListItem} from "@/pages/ClusterConfiguration/data";
import {HistoryItem} from "@/components/Studio/StudioConsole/StudioHistory/data";
export type JobInstanceTableListItem = { export type JobInstanceTableListItem = {
id: number, id: number,
name: string, name: string,
...@@ -25,3 +29,12 @@ export type StatusCount = { ...@@ -25,3 +29,12 @@ export type StatusCount = {
failed: number, failed: number,
canceled: number, canceled: number,
} }
export type JobInfoDetail = {
id: number,
instance: JobInstanceTableListItem,
cluster: ClusterTableListItem,
clusterConfiguration: ClusterConfigurationTableListItem,
task: TaskTableListItem,
history: HistoryItem
}
...@@ -20,6 +20,7 @@ const DevOps = (props:any) => { ...@@ -20,6 +20,7 @@ const DevOps = (props:any) => {
{ key: 'CANCELED', status: 'warning', title: '停止', value: 0 }, { key: 'CANCELED', status: 'warning', title: '停止', value: 0 },
]; ];
const [statusCount, setStatusCount] = useState<any[]>(statusCountDefault); const [statusCount, setStatusCount] = useState<any[]>(statusCountDefault);
const [activeKey, setActiveKey] = useState<string>('');
const refreshStatusCount = () => { const refreshStatusCount = () => {
const res = getStatusCount(); const res = getStatusCount();
...@@ -35,16 +36,21 @@ const DevOps = (props:any) => { ...@@ -35,16 +36,21 @@ const DevOps = (props:any) => {
]; ];
setStatusCount(items); setStatusCount(items);
}); });
} };
useEffect(() => { useEffect(() => {
refreshStatusCount(); refreshStatusCount();
let dataPolling = setInterval(refreshStatusCount,3000);
return () => {
clearInterval(dataPolling);
};
}, []); }, []);
return ( return (
<ProCard <ProCard
tabs={{ tabs={{
onChange: (key) => { onChange: (key) => {
setActiveKey(key);
}, },
}} }}
> >
...@@ -69,7 +75,7 @@ const DevOps = (props:any) => { ...@@ -69,7 +75,7 @@ const DevOps = (props:any) => {
backgroundColor: '#fafafa', backgroundColor: '#fafafa',
}} }}
> >
<JobInstanceTable status={item.key}/> <JobInstanceTable status={item.key} activeKey={activeKey}/>
</div> </div>
</ProCard.TabPane> </ProCard.TabPane>
))} ))}
......
...@@ -3,3 +3,7 @@ import {getData} from "@/components/Common/crud"; ...@@ -3,3 +3,7 @@ import {getData} from "@/components/Common/crud";
export function getStatusCount() { export function getStatusCount() {
return getData("api/jobInstance/getStatusCount"); return getData("api/jobInstance/getStatusCount");
} }
export function getJobInfoDetail(id:number) {
return getData("api/jobInstance/getJobInfoDetail",{id});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment