# Conflicts: # ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentInsServiceImpl.javapull/7/head
| @@ -27,4 +27,18 @@ | |||
| height:45px; | |||
| background:#ffffff; | |||
| box-shadow:0px 3px 6px rgba(146, 146, 146, 0.09); | |||
| } | |||
| .detailBox{ | |||
| color:#1d1d20; | |||
| font-size:15px; | |||
| margin-bottom: 15px; | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| .allMessageItem{ | |||
| display: flex; | |||
| align-items: center; | |||
| color:rgba(29, 29, 32, 0.8); | |||
| font-size:15px; | |||
| margin-right: 30px; | |||
| } | |||
| @@ -8,14 +8,45 @@ import { s8 } from '../../../utils'; | |||
| import { Button, message} from 'antd'; | |||
| import {SaveOutlined} from '@ant-design/icons'; | |||
| import {saveWorkflow,getWorkflowById,} from '@/services/pipeline/index.js' | |||
| import {getExperimentIns} from '@/services/experiment/index.js' | |||
| import {getExperimentIns,} from '@/services/experiment/index.js' | |||
| import { useNavigate} from 'react-router-dom'; | |||
| import momnet from 'moment' | |||
| const ExperimentText = React.FC = () => { | |||
| const propsRef=useRef() | |||
| const navgite=useNavigate(); | |||
| const locationParams =useParams () //新版本获取路由参数接口 | |||
| let graph=null | |||
| const [experimentStatusObj,setExperimentStatusObj]=useState({}) | |||
| const [experimentAllMessage,setExperimentAllMessage]=useState({}) | |||
| const statusObj={ | |||
| "Running":'运行中', | |||
| "Succeeded":'成功', | |||
| "Pending":'等待中', | |||
| "Failed":'失败', | |||
| "Error":'错误', | |||
| "Terminated":'终止', | |||
| "Skipped":'未执行', | |||
| "Omitted":'未执行', | |||
| } | |||
| const statusColorObj={ | |||
| "Running":'#165bff', | |||
| "Succeeded":'#63a728', | |||
| "Pending":'#f981eb', | |||
| "Failed":'#c73131', | |||
| "Error":'#c73131', | |||
| "Terminated":'#8a8a8a', | |||
| "Skipped":'#8a8a8a', | |||
| "Omitted":'#8a8a8ae', | |||
| } | |||
| const timers=(time)=>{ | |||
| let timer=new Date(time) | |||
| let hours = timer.getHours(); //转换成时 | |||
| let minutes = timer.getMinutes(); //转换成分 | |||
| let secend = timer.getSeconds(); //转换成秒 | |||
| let str = `${minutes}分${secend}秒`; | |||
| return str; | |||
| } | |||
| const pipelineContainer = useEmotionCss(() => { | |||
| return { | |||
| display: 'flex', | |||
| @@ -88,11 +119,13 @@ const ExperimentText = React.FC = () => { | |||
| console.log(JSON.parse(ret.data.dag)); | |||
| getExperimentIns(locationParams.id).then(res=>{ | |||
| if(res.code==200){ | |||
| console.log(ret.data,'data'); | |||
| const experimentStatusObjs=JSON.parse(res.data.nodes_status) | |||
| const newNodeList= JSON.parse(ret.data.dag).nodes.map(item=>{console.log(experimentStatusObjs); return {...item,component_id:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].id,img:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].phase?item.img.slice(0,item.img.length-4)+'-'+experimentStatusObjs[item.id].phase+'.png':item.img}}) | |||
| const newData={...JSON.parse(ret.data.dag),nodes:newNodeList} | |||
| console.log(newData); | |||
| getGraphData(newData) | |||
| const newNodeList= JSON.parse(ret.data.dag).nodes.map(item=>{console.log(experimentStatusObjs); return {...item,experimentEndTime:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].finishedAt,experimentStartTime:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].startedAt,experimentStatus:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].phase,component_id:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].id,img:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].phase?item.img.slice(0,item.img.length-4)+'-'+experimentStatusObjs[item.id].phase+'.png':item.img}}) | |||
| const newData={...JSON.parse(ret.data.dag),nodes:newNodeList} | |||
| console.log(newData); | |||
| setExperimentAllMessage(res.data) | |||
| getGraphData(newData) | |||
| // setExperimentStatusObj(JSON.parse(ret.data.nodes_status)) | |||
| } | |||
| @@ -327,6 +360,16 @@ const ExperimentText = React.FC = () => { | |||
| return (<div className={pipelineContainer}> | |||
| <div className={Styles.centerContainer}> | |||
| <div className={Styles.buttonList}> | |||
| <div className={Styles.allMessageItem}>启动时间:{momnet(experimentAllMessage.create_time).format('YYYY-MM-DD HH:mm:ss')}</div> | |||
| <div className={Styles.allMessageItem}>执行时长:{experimentAllMessage.finish_time?timers(new Date(experimentAllMessage.finish_time).getTime()-new Date(experimentAllMessage.create_time).getTime()):timers(new Date().getTime()-new Date(experimentAllMessage.create_time).getTime())}</div> | |||
| <div className={Styles.allMessageItem}>状态: | |||
| <div style={{width:'8px', | |||
| height:'8px', | |||
| borderRadius:'50%', | |||
| marginRight:'6px', | |||
| backgroundColor:statusColorObj[experimentAllMessage.status] | |||
| }}></div> | |||
| <span style={{color:statusColorObj[experimentAllMessage.status]}}>{statusObj[experimentAllMessage.status]}</span></div> | |||
| </div> | |||
| <div className={graphStyle} ref={graphRef} id={Styles.graphStyle}></div> | |||
| </div> | |||
| @@ -3,19 +3,48 @@ import { Button, Drawer,Form, Input ,Tabs } from 'antd'; | |||
| import Styles from './editPipeline.less' | |||
| import{getQueryByExperimentLog}from '@/services/experiment/index.js' | |||
| import { ProfileOutlined, DatabaseOutlined} from '@ant-design/icons'; | |||
| import momnet from 'moment' | |||
| const { TextArea } = Input; | |||
| const Props = forwardRef(({onParentChange}, ref) =>{ | |||
| const [form] = Form.useForm(); | |||
| const [stagingItem,setStagingItem]=useState({}) | |||
| const [messageItem,setMessageItem]=useState('') | |||
| const statusObj={ | |||
| "Running":'运行中', | |||
| "Succeeded":'成功', | |||
| "Pending":'等待中', | |||
| "Failed":'失败', | |||
| "Error":'错误', | |||
| "Terminated":'终止', | |||
| "Skipped":'未执行', | |||
| "Omitted":'未执行', | |||
| } | |||
| const statusColorObj={ | |||
| "Running":'#165bff', | |||
| "Succeeded":'#63a728', | |||
| "Pending":'#f981eb', | |||
| "Failed":'#c73131', | |||
| "Error":'#c73131', | |||
| "Terminated":'#8a8a8a', | |||
| "Skipped":'#8a8a8a', | |||
| "Omitted":'#8a8a8ae', | |||
| } | |||
| const timers=(time)=>{ | |||
| let timer=new Date(time) | |||
| let hours = timer.getHours(); //转换成时 | |||
| let minutes = timer.getMinutes(); //转换成分 | |||
| let secend = timer.getSeconds(); //转换成秒 | |||
| let str = `${minutes}分${secend}秒`; | |||
| return str; | |||
| } | |||
| const items = [ | |||
| { | |||
| key: '1', | |||
| label: '日志详情', | |||
| children: <div style={{height:'740px', | |||
| background:'rgba(234, 234, 234, 0.5)', | |||
| color:'rgba(29, 29, 32, 0.8)', | |||
| background:'#19253b', | |||
| color:'#fff', | |||
| fontSize:'14px' | |||
| }} dangerouslySetInnerHTML={{ __html: messageItem }}></div>, | |||
| icon:<ProfileOutlined/> | |||
| @@ -226,11 +255,6 @@ const Props = forwardRef(({onParentChange}, ref) =>{ | |||
| form.resetFields(); | |||
| form.setFieldsValue({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters),control_strategy:JSON.parse(e.item.getModel().control_strategy)}) | |||
| setStagingItem({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters),control_strategy:JSON.parse(e.item.getModel().control_strategy)}) | |||
| // form.setFieldsValue({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters)}) | |||
| // setStagingItem({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters)}) | |||
| // setTimeout(() => { | |||
| // console.log(stagingItem); | |||
| // }, (500)); | |||
| setOpen(true); | |||
| }) | |||
| } | |||
| @@ -238,11 +262,6 @@ const Props = forwardRef(({onParentChange}, ref) =>{ | |||
| form.resetFields(); | |||
| form.setFieldsValue({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters),control_strategy:JSON.parse(e.item.getModel().control_strategy)}) | |||
| setStagingItem({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters),control_strategy:JSON.parse(e.item.getModel().control_strategy)}) | |||
| // form.setFieldsValue({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters)}) | |||
| // setStagingItem({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters)}) | |||
| // setTimeout(() => { | |||
| // console.log(stagingItem); | |||
| // }, (500)); | |||
| setOpen(true); | |||
| } | |||
| // console.log(e.item.getModel().in_parameters); | |||
| @@ -252,7 +271,18 @@ const Props = forwardRef(({onParentChange}, ref) =>{ | |||
| })); | |||
| return ( | |||
| <> | |||
| <Drawer title="编辑任务" placement="right" closeIcon={false} onClose={onClose} afterOpenChange={afterOpenChange} open={open}> | |||
| <Drawer title="任务执行详情" placement="right" closeIcon={false} onClose={onClose} afterOpenChange={afterOpenChange} open={open}> | |||
| <div className={Styles.detailBox}>任务名称:{stagingItem.label}</div> | |||
| <div className={Styles.detailBox} >执行状态: | |||
| <div style={{width:'8px', | |||
| height:'8px', | |||
| borderRadius:'50%', | |||
| marginRight:'6px', | |||
| backgroundColor:statusColorObj[stagingItem.experimentStatus] | |||
| }}></div> | |||
| <span style={{color:statusColorObj[stagingItem.experimentStatus]}}>{statusObj[stagingItem.experimentStatus]}</span></div> | |||
| <div className={Styles.detailBox}>启动时间:{momnet(stagingItem.experimentStartTime).format('YYYY-MM-DD HH:mm:ss')}</div> | |||
| <div className={Styles.detailBox}>耗时:{stagingItem.experimentEndTime?timers(new Date(stagingItem.experimentEndTime).getTime()-new Date(stagingItem.experimentStartTime).getTime()):timers(new Date().getTime()-new Date(stagingItem.experimentStartTime).getTime())}</div> | |||
| <Tabs | |||
| defaultActiveKey="1" | |||
| items={items}/> | |||
| @@ -1,8 +1,8 @@ | |||
| import React ,{ useState,useEffect,useRef }from 'react'; | |||
| import { Space, Table, Tag,Button,Modal, Form, Input ,message, Select,} from 'antd'; | |||
| import { PlusOutlined, EditOutlined ,PlayCircleOutlined} from '@ant-design/icons'; | |||
| import {getWorkflow,addWorkflow,removeWorkflow,cloneWorkflow} from '@/services/pipeline/index.js' | |||
| import {getExperiment,runExperiments,getExperimentById,postExperiment,putExperiment,getQueryByExperimentId} from '@/services/experiment/index.js' | |||
| import { PlusOutlined, EditOutlined ,PlayCircleOutlined,DeleteOutlined,FieldTimeOutlined} from '@ant-design/icons'; | |||
| import {getWorkflow,} from '@/services/pipeline/index.js' | |||
| import {getExperiment,runExperiments,getExperimentById,postExperiment,putExperiment,getQueryByExperimentId,deleteExperimentById,deleteQueryByExperimentInsId,putQueryByExperimentInsId} from '@/services/experiment/index.js' | |||
| import Styles from './index.less' | |||
| import { useNavigate} from 'react-router-dom'; | |||
| import momnet from 'moment' | |||
| @@ -14,9 +14,31 @@ const Experiment = React.FC = () => { | |||
| const statusObj={ | |||
| "Running":'运行中', | |||
| "Succeeded":'成功', | |||
| "Pending":'等待中', | |||
| "Failed":'失败', | |||
| "Error":'错误', | |||
| "Teminated":'终止' | |||
| "Terminated":'终止', | |||
| "Skipped":'未执行', | |||
| "Omitted":'未执行', | |||
| } | |||
| const statusColorObj={ | |||
| "Running":'#165bff', | |||
| "Succeeded":'#63a728', | |||
| "Pending":'#f981eb', | |||
| "Failed":'#c73131', | |||
| "Error":'#c73131', | |||
| "Terminated":'#8a8a8a', | |||
| "Skipped":'#8a8a8a', | |||
| "Omitted":'#8a8a8ae', | |||
| } | |||
| const statusImgObj={ | |||
| 'Running':'/assets/images/running-icon.png', | |||
| 'Succeeded':'/assets/images/success-icon.png', | |||
| 'Pending':'/assets/images/pending-icon.png', | |||
| 'Failed':'/assets/images/fail-icon.png', | |||
| 'Terminated':'/assets/images/omitted-icon.png', | |||
| 'Skipped':'/assets/images/omitted-icon.png', | |||
| 'Omitted':'/assets/images/omitted-icon.png', | |||
| } | |||
| const [experimentList, setExperimentList] = useState([]); | |||
| @@ -56,10 +78,11 @@ const Experiment = React.FC = () => { | |||
| setExpandedRowKeys(val) | |||
| if(ret.code==200&&ret.data&&ret.data.length>0){ | |||
| setExperimentInList(ret.data) | |||
| getList() | |||
| } | |||
| else{ | |||
| setExperimentInList([]) | |||
| getList() | |||
| } | |||
| }) | |||
| } | |||
| @@ -157,7 +180,6 @@ const Experiment = React.FC = () => { | |||
| if(ret.code==200){ | |||
| message.success('运行成功') | |||
| getQueryByExperiment(id) | |||
| getList() | |||
| } | |||
| else{ | |||
| message.error('运行失败') | |||
| @@ -227,8 +249,14 @@ const Experiment = React.FC = () => { | |||
| }, | |||
| { | |||
| title: '最近五次运行状态', | |||
| dataIndex: 'state', | |||
| key: 'state', | |||
| dataIndex: 'status_list', | |||
| key: 'status_list', | |||
| render: (text) => { | |||
| let newText=text&&text.replace(/\s+/g,'').split(',') | |||
| console.log(newText); | |||
| return <>{ newText&&newText.length>0?newText.map((item,index)=>{console.log(item,statusImgObj[item]); return <img style={{width:'17px',marginRight:'6px'}} key={index} src={statusImgObj[item]} />}):null}</> | |||
| } | |||
| }, | |||
| { | |||
| @@ -258,7 +286,41 @@ const Experiment = React.FC = () => { | |||
| > | |||
| 编辑 | |||
| </Button> | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| danger | |||
| key="batchRemove" | |||
| icon = {< DeleteOutlined />} | |||
| onClick={async () => { | |||
| Modal.confirm({ | |||
| title: '删除', | |||
| content: '确定删除该条实验吗?', | |||
| okText: '确认', | |||
| cancelText: '取消', | |||
| onOk: () => { | |||
| console.log(record); | |||
| deleteExperimentById(record.id).then(ret=>{ | |||
| if(ret.code==200){ | |||
| message.success('删除成功') | |||
| getList() | |||
| } | |||
| else{ | |||
| message.error(ret.msg) | |||
| } | |||
| }); | |||
| // if (success) { | |||
| // if (actionRef.current) { | |||
| // actionRef.current.reload(); | |||
| // } | |||
| // } | |||
| }, | |||
| }); | |||
| }} | |||
| > | |||
| 删除 | |||
| </Button> | |||
| </Space> | |||
| ), | |||
| }, | |||
| @@ -278,14 +340,66 @@ const Experiment = React.FC = () => { | |||
| <div style={{width:'200px'}}>状态</div> | |||
| <div style={{width:'300px'}}>运行时长</div> | |||
| <div style={{width:'300px'}}>开始时间</div> | |||
| <div style={{width:'300px'}}>操作</div> | |||
| </div>:''} | |||
| {experimentInList&&experimentInList.length>0?experimentInList.map((item,index)=>( | |||
| <div className={Styles.tableExpandBox} style={{border:'1px solid #eaeaea',backgroundColor:'#fff',height:'45px'}}> | |||
| <a style={{width:'50px'}} onClick={(e)=>routerToText(e,item,record)}>{index+1}</a> | |||
| <div style={{width:'200px'}}>{statusObj[item.status]}</div> | |||
| <div style={{width:'300px'}}>{item.finish_time?timers(new Date(item.finish_time).getTime()-new Date(item.start_time).getTime()):timers(new Date().getTime()-new Date(item.start_time).getTime())}</div> | |||
| <div style={{width:'300px'}}>{momnet(item.start_time).format('YYYY-MM-DD HH:mm:ss')}</div> | |||
| <div className={Styles.statusBox} style={{width:'200px'}}><img style={{width:'17px',marginRight:'7px'}} src={statusImgObj[item.status]}/> <span style={{color:statusColorObj[item.status]}} className={Styles.statusIcon}>{statusObj[item.status]}</span></div> | |||
| <div style={{width:'300px'}}>{item.finish_time?timers(new Date(item.finish_time).getTime()-new Date(item.create_time).getTime()):timers(new Date().getTime()-new Date(item.create_time).getTime())}</div> | |||
| <div style={{width:'300px'}}>{momnet(item.create_time).format('YYYY-MM-DD HH:mm:ss')}</div> | |||
| <div style={{width:'300px'}}> | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="batchRemove" | |||
| disabled={item.status=='Succeeded'||item.status=='Failed'||item.status=='Terminated'} | |||
| icon = {<FieldTimeOutlined />} | |||
| onClick={async () => { | |||
| putQueryByExperimentInsId(item.id).then(ret=>{ | |||
| if(ret.code==200){ | |||
| message.success('终止成功') | |||
| getQueryByExperiment(record.id) | |||
| } | |||
| else{ | |||
| message.error(ret.msg) | |||
| } | |||
| }) | |||
| }} | |||
| > | |||
| 终止 | |||
| </Button> | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| danger | |||
| key="batchRemove" | |||
| disabled={item.status=='Running'||item.status=='Pending'} | |||
| icon = {< DeleteOutlined />} | |||
| onClick={async () => { | |||
| Modal.confirm({ | |||
| title: '删除', | |||
| content: '确定删除该条实例吗?', | |||
| okText: '确认', | |||
| cancelText: '取消', | |||
| onOk: () => { | |||
| deleteQueryByExperimentInsId(item.id).then(ret=>{ | |||
| if(ret.code==200){ | |||
| message.success('删除成功') | |||
| getQueryByExperiment(record.id) | |||
| } | |||
| else{ | |||
| message.error(ret.msg) | |||
| } | |||
| }); | |||
| }, | |||
| }); | |||
| }} | |||
| > | |||
| 删除 | |||
| </Button> | |||
| </div> | |||
| </div> | |||
| )):''} | |||
| </div> | |||
| @@ -32,4 +32,16 @@ | |||
| font-size:15px; | |||
| padding: 0 65px 0 40px; | |||
| } | |||
| .statusBox{ | |||
| display: flex; | |||
| align-items: center; | |||
| .statusIcon{ | |||
| visibility: hidden; | |||
| transition: all 0.2s; | |||
| } | |||
| } | |||
| .statusBox:hover .statusIcon{ | |||
| visibility: visible; | |||
| } | |||
| @@ -13,6 +13,7 @@ import { useNavigate} from 'react-router-dom'; | |||
| const editPipeline = React.FC = () => { | |||
| const propsRef=useRef() | |||
| const navgite=useNavigate(); | |||
| const [contextMenu,setContextMenu]=useState({}) | |||
| const locationParams =useParams () //新版本获取路由参数接口 | |||
| let graph=null | |||
| const pipelineContainer = useEmotionCss(() => { | |||
| @@ -30,6 +31,7 @@ const editPipeline = React.FC = () => { | |||
| }; | |||
| }); | |||
| const graphRef=useRef() | |||
| const onDragEnd=(val)=>{ | |||
| console.log(val,'eee'); | |||
| const _x = val.x | |||
| @@ -117,9 +119,49 @@ const editPipeline = React.FC = () => { | |||
| // graph.render() | |||
| }) | |||
| } | |||
| const handlerContextMenu=(e)=> { | |||
| e.stopPropagation(); | |||
| // this.menuType = e.item._cfg.type; | |||
| } | |||
| const initMenu=()=> { | |||
| // const selectedNodes = this.selectedNodes; | |||
| setContextMenu(new G6.Menu({ | |||
| getContent(evt) { | |||
| console.log(11111, evt); | |||
| let ul = `<ul> | |||
| <li class="rightmenu-item" code="undo" disabled>撤销</li> | |||
| <li class="rightmenu-item" code="redo" >恢复</li> | |||
| <li class="rightmenu-item" code="delete" >删除</li> | |||
| `; | |||
| }, | |||
| handleMenuClick:(target, item) => { | |||
| switch (target.getAttribute('code')) { | |||
| // case 'undo': | |||
| // this.$emit('handleMenuCall', { code: 'undo' }); | |||
| // break; | |||
| // case 'redo': | |||
| // this.$emit('handleMenuCall', { code: 'redo' }); | |||
| // break; | |||
| case 'delete': | |||
| graph.removeItem(item); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| }, | |||
| offsetX: 16 + 20, | |||
| offsetY: 0, | |||
| itemTypes: ['node', 'canvas', 'edge'], | |||
| })); | |||
| }; | |||
| useEffect(()=>{ | |||
| getFirstWorkflow(locationParams.id) | |||
| initGraph() | |||
| initMenu() | |||
| },[]) | |||
| const initGraph=()=>{ | |||
| G6.registerNode( | |||
| @@ -224,7 +266,7 @@ const editPipeline = React.FC = () => { | |||
| animate: false, | |||
| groupByTypes: false, | |||
| fitView:true, | |||
| plugins: [], | |||
| plugins: [contextMenu], | |||
| enabledStack: true, | |||
| modes: { | |||
| default: [ | |||
| @@ -345,6 +387,7 @@ const editPipeline = React.FC = () => { | |||
| }); | |||
| }); | |||
| }); | |||
| graph.on('contextmenu', handlerContextMenu); | |||
| window.onresize = () => { | |||
| if (!graph || graph.get('destroyed')) return; | |||
| if (!graphRef.current || !graphRef.current.scrollWidth || !graphRef.current.scrollHeight) return; | |||
| @@ -49,8 +49,8 @@ const Props = forwardRef(({onParentChange}, ref) =>{ | |||
| }; | |||
| useImperativeHandle(ref, () => ({ | |||
| showDrawer (e) { | |||
| console.log(e.item.getModel()); | |||
| // console.log(e.item.getModel().in_parameters); | |||
| if(e.item&&e.item.getModel()){ | |||
| // console.log(e.item.getModel().in_parameters); | |||
| form.resetFields(); | |||
| form.setFieldsValue({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters),control_strategy:JSON.parse(e.item.getModel().control_strategy)}) | |||
| setStagingItem({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters),control_strategy:JSON.parse(e.item.getModel().control_strategy)}) | |||
| @@ -60,6 +60,8 @@ const Props = forwardRef(({onParentChange}, ref) =>{ | |||
| // console.log(stagingItem); | |||
| // }, (500)); | |||
| setOpen(true); | |||
| } | |||
| }, | |||
| })); | |||
| @@ -21,12 +21,30 @@ export function getExperimentById(id) { | |||
| method: 'GET', | |||
| }); | |||
| } | |||
| // 根据id删除实验 | |||
| export function deleteExperimentById(id) { | |||
| return request(`/api/mmp/experiment/${id}`, { | |||
| method: 'DELETE', | |||
| }); | |||
| } | |||
| // 根据id查询实验实例 | |||
| export function getQueryByExperimentId(id) { | |||
| return request(`/api/mmp/experimentIns/queryByExperimentId/${id}`, { | |||
| method: 'GET', | |||
| }); | |||
| } | |||
| // 根据id删除实验实例 | |||
| export function deleteQueryByExperimentInsId(id) { | |||
| return request(`/api/mmp/experimentIns/${id}`, { | |||
| method: 'DELETE', | |||
| }); | |||
| } | |||
| // 根据id终止实验实例 | |||
| export function putQueryByExperimentInsId(id) { | |||
| return request(`/api/mmp/experimentIns/${id}`, { | |||
| method: 'PUT', | |||
| }); | |||
| } | |||
| // 根据id查询查询日志 | |||
| export function getQueryByExperimentLog(params) { | |||
| return request(`/api/mmp/experimentIns/log/`, { | |||
| @@ -33,6 +33,8 @@ public class Experiment implements Serializable { | |||
| */ | |||
| @ApiModelProperty(name = "global_param") | |||
| private String globalParam; | |||
| private String statusList; | |||
| /** | |||
| * 简介 | |||
| */ | |||
| @@ -100,7 +102,13 @@ public class Experiment implements Serializable { | |||
| this.globalParam = globalParam; | |||
| } | |||
| public String getStatusList() { | |||
| return statusList; | |||
| } | |||
| public void setStatusList(String statusList) { | |||
| this.statusList = statusList; | |||
| } | |||
| public String getCreateBy() { | |||
| return createBy; | |||
| @@ -1,6 +1,7 @@ | |||
| package com.ruoyi.platform.service.impl; | |||
| import com.ruoyi.common.security.utils.SecurityUtils; | |||
| import com.ruoyi.platform.domain.Experiment; | |||
| import com.ruoyi.platform.domain.ExperimentIns; | |||
| import com.ruoyi.platform.mapper.ExperimentDao; | |||
| import com.ruoyi.platform.mapper.ExperimentInsDao; | |||
| @@ -56,12 +57,12 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| * @return 实例对象 | |||
| */ | |||
| @Override | |||
| public ExperimentIns queryById(Integer id) { | |||
| public ExperimentIns queryById(Integer id) throws IOException { | |||
| ExperimentIns experimentIns = this.experimentInsDao.queryById(id); | |||
| if (experimentIns != null && (StringUtils.isEmpty(experimentIns.getStatus())) || !isTerminatedState(experimentIns.getStatus())) { | |||
| if (experimentIns != null && (StringUtils.isEmpty(experimentIns.getStatus())) || !isTerminatedState(experimentIns)) { | |||
| experimentIns = this.queryStatusFromArgo(experimentIns); | |||
| //只有当新状态是终止态时才更新数据库 | |||
| if (isTerminatedState(experimentIns.getStatus())) { | |||
| if (isTerminatedState(experimentIns)) { | |||
| //同时更新各个节点 | |||
| this.update(experimentIns); | |||
| } | |||
| @@ -78,17 +79,21 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| * @return 实验列表 | |||
| */ | |||
| @Override | |||
| public List<ExperimentIns> getByExperimentId(Integer experimentId) { | |||
| public List<ExperimentIns> getByExperimentId(Integer experimentId) throws IOException { | |||
| List<ExperimentIns> experimentInsList = experimentInsDao.getByExperimentId(experimentId); | |||
| //搞个标记,当状态改变才去改表 | |||
| boolean flag = false; | |||
| List<ExperimentIns> result = new ArrayList<ExperimentIns>(); | |||
| if (experimentInsList!=null&&experimentInsList.size()>0) { | |||
| for (ExperimentIns experimentIns : experimentInsList) { | |||
| //当原本状态为null或非终止态时才调用argo接口 | |||
| if (experimentIns != null && (StringUtils.isEmpty(experimentIns.getStatus())) || !isTerminatedState(experimentIns.getStatus())) { | |||
| if (experimentIns != null && (StringUtils.isEmpty(experimentIns.getStatus())) || !isTerminatedState(experimentIns)) { | |||
| experimentIns = this.queryStatusFromArgo(experimentIns); | |||
| if (!flag){ | |||
| flag = true; | |||
| } | |||
| //只有当新状态是终止态时才更新数据库 | |||
| if (isTerminatedState(experimentIns.getStatus())) { | |||
| if (isTerminatedState(experimentIns)) { | |||
| //同时更新各个节点 | |||
| this.update(experimentIns); | |||
| } | |||
| @@ -97,6 +102,17 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| } | |||
| } | |||
| if (flag) { | |||
| List<String> statusList = new ArrayList<String>(); | |||
| // 更新实验状态列表 | |||
| for (int i=0;i<result.size();i++){ | |||
| statusList.add(result.get(i).getStatus()); | |||
| } | |||
| Experiment experiment = experimentDao.queryById(experimentId); | |||
| experiment.setStatusList(statusList.toString().substring(1, statusList.toString().length()-1)); | |||
| experimentDao.update(experiment); | |||
| } | |||
| return result; | |||
| } | |||
| @@ -109,7 +125,7 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| * @return 查询结果 | |||
| */ | |||
| @Override | |||
| public Page<ExperimentIns> queryByPage(ExperimentIns experimentIns, PageRequest pageRequest) { | |||
| public Page<ExperimentIns> queryByPage(ExperimentIns experimentIns, PageRequest pageRequest) throws IOException { | |||
| long total = this.experimentInsDao.count(experimentIns); | |||
| List<ExperimentIns> experimentInsList = this.experimentInsDao.queryAllByLimit(experimentIns, pageRequest); | |||
| if (experimentInsList!=null && experimentInsList.size()>0) { | |||
| @@ -152,7 +168,7 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| * @return 实例对象 | |||
| */ | |||
| @Override | |||
| public ExperimentIns update(ExperimentIns experimentIns) { | |||
| public ExperimentIns update(ExperimentIns experimentIns) throws IOException { | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| experimentIns.setUpdateBy(loginUser.getUsername()); | |||
| experimentIns.setUpdateTime(new Date()); | |||
| @@ -212,8 +228,8 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| String namespace = ins.getArgoInsNs(); | |||
| String name = ins.getArgoInsName(); | |||
| Integer id = ins.getId(); | |||
| ExperimentIns experimentIns = this.experimentInsDao.queryById(id); | |||
| // 创建请求数据map | |||
| ExperimentIns experimentIns = this.experimentInsDao.queryById(id); | |||
| Map<String,Object> requestData = new HashMap<>(); | |||
| requestData.put("namespace", namespace); | |||
| requestData.put("name", name); | |||
| @@ -241,10 +257,6 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| if (status == null || status.isEmpty()) { | |||
| throw new RuntimeException("工作流状态为空。"); | |||
| } | |||
| //解析流水线开始时间,开始时间一定存在,所以不需要判断 | |||
| Date startTime = DateUtils.convertUTCtoShanghaiDate((String) status.get("startedAt")); | |||
| experimentIns.setStartTime(startTime); | |||
| //解析流水线结束时间 | |||
| String finishedAtString = (String) status.get("finishedAt"); | |||
| @@ -255,21 +267,25 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| // 解析nodes字段,提取节点状态并转换为JSON字符串 | |||
| Map<String, Object> nodes = (Map<String, Object>) status.get("nodes"); | |||
| if (nodes == null || nodes.isEmpty()) { | |||
| throw new RuntimeException("工作流的节点数据为空。"); | |||
| } | |||
| Map<String, Object> modifiedNodes = new LinkedHashMap<>(); | |||
| for (Map.Entry<String, Object> nodeEntry : nodes.entrySet()) { | |||
| Map<String,Object> nodeDetails = (Map<String, Object>) nodeEntry.getValue(); | |||
| String templateName = (String) nodeDetails.get("displayName"); | |||
| modifiedNodes.put(templateName, nodeDetails); | |||
| if (nodes != null ) { | |||
| for (Map.Entry<String, Object> nodeEntry : nodes.entrySet()) { | |||
| Map<String,Object> nodeDetails = (Map<String, Object>) nodeEntry.getValue(); | |||
| String templateName = (String) nodeDetails.get("displayName"); | |||
| modifiedNodes.put(templateName, nodeDetails); | |||
| } | |||
| } | |||
| String nodeStatusJson = JsonUtils.mapToJson(modifiedNodes); | |||
| experimentIns.setNodesStatus(nodeStatusJson); | |||
| experimentIns.setStatus((String) status.get("phase")); | |||
| //终止态为终止不改 | |||
| if (!StringUtils.equals(experimentIns.getStatus(),"Terminated")) { | |||
| experimentIns.setStatus(StringUtils.isNotEmpty((String) status.get("phase"))?(String) status.get("phase"):"Pending"); | |||
| } | |||
| if (StringUtils.equals(experimentIns.getStatus(),"Error")) { | |||
| experimentIns.setStatus("Failed"); | |||
| } | |||
| return experimentIns; | |||
| @@ -278,7 +294,6 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| } | |||
| } | |||
| /** | |||
| @@ -329,9 +344,11 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| // 从响应Map中直接获取"errCode"的值 | |||
| Integer errCode = (Integer) runResMap.get("errCode"); | |||
| if (errCode != null && errCode == 0) { | |||
| experimentIns.setStatus("Terminated"); | |||
| //更新experimentIns,确保状态更新被保存到数据库 | |||
| this.experimentInsDao.update(experimentIns); | |||
| ExperimentIns ins = queryStatusFromArgo(experimentIns); | |||
| ins.setStatus("Terminated"); | |||
| ins.setFinishTime(new Date()); | |||
| this.experimentInsDao.update(ins); | |||
| return true; | |||
| } else { | |||
| return false; | |||
| @@ -395,10 +412,27 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| } | |||
| } | |||
| private boolean isTerminatedState(String state) { | |||
| private boolean isTerminatedState(ExperimentIns ins) throws IOException { | |||
| // 定义终止态的列表,例如 "Succeeded", "Failed" 等 | |||
| List<String> terminatedStates = Arrays.asList("Succeeded", "Failed", "Terminated"); | |||
| return terminatedStates.contains(state); | |||
| String status = ins.getStatus(); | |||
| boolean flag = true; | |||
| List<String> terminatedStates = Arrays.asList("Succeeded", "Failed"); | |||
| flag = terminatedStates.contains(status); | |||
| if (StringUtils.equals(status, "Terminated")){ | |||
| //如果跟node_status里面不一样,就要去更新node_status的信息 | |||
| String nodesStatus = ins.getNodesStatus(); | |||
| Map<String, Object> nodeMap = JsonUtils.jsonToMap(nodesStatus); | |||
| String keyStartsWithWorkflow = nodeMap.keySet().stream() | |||
| .filter(key -> key.startsWith("workflow-")) | |||
| .findFirst() | |||
| .orElse(null); | |||
| Map workflowMap = (Map) nodeMap.get(keyStartsWithWorkflow); | |||
| if (workflowMap != null){ | |||
| flag = StringUtils.equals("Terminated", (String) workflowMap.get("phase")); | |||
| } | |||
| } | |||
| return flag; | |||
| } | |||
| } | |||
| @@ -7,6 +7,7 @@ | |||
| <result property="name" column="name" jdbcType="VARCHAR"/> | |||
| <result property="workflowId" column="workflow_id" jdbcType="INTEGER"/> | |||
| <result property="globalParam" column="global_param" jdbcType="VARCHAR"/> | |||
| <result property="statusList" column="status_list" jdbcType="VARCHAR"/> | |||
| <result property="description" column="description" jdbcType="VARCHAR"/> | |||
| <result property="createBy" column="create_by" jdbcType="VARCHAR"/> | |||
| <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/> | |||
| @@ -18,7 +19,7 @@ | |||
| <!--查询单个--> | |||
| <select id="queryById" resultMap="ExperimentMap"> | |||
| select | |||
| id, name,workflow_id, global_param, description, create_by, create_time, update_by, update_time, state | |||
| id, name,workflow_id, global_param, status_list,status_list, description, create_by, create_time, update_by, update_time, state | |||
| from experiment | |||
| where id = #{id} and state = 1 | |||
| </select> | |||
| @@ -26,7 +27,7 @@ | |||
| <!--根据experiment查询--> | |||
| <select id="queryByExperiment" resultMap="ExperimentMap"> | |||
| select | |||
| id,name, workflow_id, global_param, description, create_by, create_time, update_by, update_time, state | |||
| id,name, workflow_id, global_param, status_list, description, create_by, create_time, update_by, update_time, state | |||
| from experiment | |||
| <where> | |||
| state = 1 | |||
| @@ -42,6 +43,9 @@ | |||
| <if test="experiment.globalParam != null and experiment.globalParam != ''"> | |||
| and global_param = #{experiment.globalParam} | |||
| </if> | |||
| <if test="experiment.statusList != null and experiment.statusList != ''"> | |||
| status_list = #{experiment.statusList}, | |||
| </if> | |||
| <if test="experiment.description != null and experiment.description != ''"> | |||
| and description = #{experiment.description} | |||
| </if> | |||
| @@ -63,7 +67,7 @@ | |||
| <!--查询指定行数据--> | |||
| <select id="queryAllByLimit" resultMap="ExperimentMap"> | |||
| select | |||
| id,name, workflow_id, global_param, description, create_by, create_time, update_by, update_time, state | |||
| id,name, workflow_id, global_param, status_list, description, create_by, create_time, update_by, update_time, state | |||
| from experiment | |||
| <where> | |||
| state = 1 | |||
| @@ -79,6 +83,9 @@ | |||
| <if test="experiment.globalParam != null and experiment.globalParam != ''"> | |||
| and global_param = #{experiment.globalParam} | |||
| </if> | |||
| <if test="experiment.statusList != null and experiment.statusList != ''"> | |||
| status_list = #{experiment.statusList}, | |||
| </if> | |||
| <if test="experiment.description != null and experiment.description != ''"> | |||
| and description = #{experiment.description} | |||
| </if> | |||
| @@ -117,6 +124,9 @@ | |||
| <if test="experiment.globalParam != null and experiment.globalParam != ''"> | |||
| and global_param = #{experiment.globalParam} | |||
| </if> | |||
| <if test="experiment.statusList != null and experiment.statusList != ''"> | |||
| status_list = #{experiment.statusList}, | |||
| </if> | |||
| <if test="experiment.description != null and experiment.description != ''"> | |||
| and description = #{experiment.description} | |||
| </if> | |||
| @@ -137,24 +147,24 @@ | |||
| <!--新增所有列--> | |||
| <insert id="insert" keyProperty="id" useGeneratedKeys="true"> | |||
| insert into experiment(name,workflow_id, global_param, description, create_by, create_time, update_by, update_time, state) | |||
| values (#{name},#{workflowId}, #{globalParam}, #{description}, #{createBy}, #{createTime}, #{updateBy}, #{updateTime}, #{state}) | |||
| insert into experiment(name,workflow_id, global_param, status_list, description, create_by, create_time, update_by, update_time, state) | |||
| values (#{name},#{workflowId}, #{globalParam},#{statusList}, #{description}, #{createBy}, #{createTime}, #{updateBy}, #{updateTime}, #{state}) | |||
| </insert> | |||
| <insert id="insertBatch" keyProperty="id" useGeneratedKeys="true"> | |||
| insert into experiment(name,workflow_id, global_param, description, create_by, create_time, update_by, update_time, state) | |||
| insert into experiment(name,workflow_id, global_param, status_list, description, create_by, create_time, update_by, update_time, state) | |||
| values | |||
| <foreach collection="entities" item="entity" separator=","> | |||
| (#{entity.name},#{entity.workflowId}, #{entity.globalParam}, #{entity.description}, #{entity.createBy}, #{entity.createTime}, | |||
| (#{entity.name},#{entity.workflowId}, #{entity.globalParam},#{entity.statusList}, #{entity.description}, #{entity.createBy}, #{entity.createTime}, | |||
| #{entity.updateBy}, #{entity.updateTime}, #{entity.state}) | |||
| </foreach> | |||
| </insert> | |||
| <insert id="insertOrUpdateBatch" keyProperty="id" useGeneratedKeys="true"> | |||
| insert into experiment(name,workflow_id, global_param, description, create_by, create_time, update_by, update_time, state) | |||
| insert into experiment(name,workflow_id, global_param, status_list, description, create_by, create_time, update_by, update_time, state) | |||
| values | |||
| <foreach collection="entities" item="entity" separator=","> | |||
| (#{entity.name},#{entity.workflowId}, #{entity.globalParam}, #{entity.description}, #{entity.createBy}, #{entity.createTime}, | |||
| (#{entity.name},#{entity.workflowId}, #{entity.globalParam}, #{entity.statusList}, #{entity.description}, #{entity.createBy}, #{entity.createTime}, | |||
| #{entity.updateBy}, #{entity.updateTime}, #{entity.state}) | |||
| </foreach> | |||
| on duplicate key update | |||
| @@ -182,6 +192,9 @@ | |||
| <if test="globalParam != null and globalParam != ''"> | |||
| global_param = #{globalParam}, | |||
| </if> | |||
| <if test="statusList != null and statusList != ''"> | |||
| status_list = #{statusList}, | |||
| </if> | |||
| <if test="description != null and description != ''"> | |||
| description = #{description}, | |||
| </if> | |||