| @@ -19,7 +19,7 @@ const Settings: ProLayoutProps & { | |||||
| title: '智能软件开发平台', | title: '智能软件开发平台', | ||||
| pwa: true, | pwa: true, | ||||
| logo: '/assets/images/left-top-logo.png', | logo: '/assets/images/left-top-logo.png', | ||||
| iconfontUrl: '//at.alicdn.com/t/c/font_4511326_2511riex401.js', | |||||
| iconfontUrl: '//at.alicdn.com/t/c/font_4511326_1cmi0j3dj1x.js', | |||||
| token: { | token: { | ||||
| // 参见ts声明,demo 见文档,通过token 修改样式 | // 参见ts声明,demo 见文档,通过token 修改样式 | ||||
| //https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F | //https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F | ||||
| @@ -68,32 +68,33 @@ export default [ | |||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| name: '流水线', | name: '流水线', | ||||
| path: '', | |||||
| path: '/pipeline/pipelineText', | |||||
| component: './Pipeline/index', | component: './Pipeline/index', | ||||
| }, | }, | ||||
| { | { | ||||
| name: '训练', | name: '训练', | ||||
| path: 'pytorchtext/:id/:name', | |||||
| path: '/pipeline/pytorchtext/:id/:name', | |||||
| component: './Pipeline/editPipeline/index', | component: './Pipeline/editPipeline/index', | ||||
| }, | }, | ||||
| ], | |||||
| }, | |||||
| { | |||||
| name: 'experiment', | |||||
| path: '/experiment', | |||||
| routes: [ | |||||
| { | { | ||||
| name: '实验', | name: '实验', | ||||
| path: '', | |||||
| path: '/pipeline/experimentText', | |||||
| component: './Experiment/index', | component: './Experiment/index', | ||||
| }, | }, | ||||
| { | { | ||||
| name: '实验训练', | name: '实验训练', | ||||
| path: 'pytorchtext/:workflowId/:id', | |||||
| path: '/pipeline/experimentPytorchtext/:workflowId/:id', | |||||
| component: './Experiment/experimentText/index', | component: './Experiment/experimentText/index', | ||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | |||||
| name: 'experiment', | |||||
| path: '/experiment', | |||||
| routes: [ | |||||
| ], | |||||
| }, | |||||
| { | { | ||||
| name: 'developmentEnvironment', | name: 'developmentEnvironment', | ||||
| path: '/developmentEnvironment', | path: '/developmentEnvironment', | ||||
| @@ -168,6 +169,52 @@ export default [ | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | |||||
| name: 'workspace', | |||||
| path: '/workspace', | |||||
| routes: [ | |||||
| { | |||||
| name: '工作空间', | |||||
| path: '', | |||||
| component: './missingPage.jsx', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| name: 'modelDseployment', | |||||
| path: '/modelDseployment', | |||||
| routes: [ | |||||
| { | |||||
| name: '模型部署', | |||||
| path: '', | |||||
| component: './missingPage.jsx', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| name: 'appsDeployment', | |||||
| path: '/appsDeployment', | |||||
| routes: [ | |||||
| { | |||||
| name: '应用开发', | |||||
| path: '', | |||||
| component: './missingPage.jsx', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| name: 'see', | |||||
| path: '/see', | |||||
| routes: [ | |||||
| { | |||||
| name: '监控运维', | |||||
| path: '', | |||||
| component: './missingPage.jsx', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | { | ||||
| name: 'monitor', | name: 'monitor', | ||||
| path: '/monitor', | path: '/monitor', | ||||
| @@ -59,10 +59,11 @@ body { | |||||
| padding-top: 40px; | padding-top: 40px; | ||||
| } | } | ||||
| .ant-table-wrapper .ant-table-container table > thead > tr:first-child > *:first-child, | .ant-table-wrapper .ant-table-container table > thead > tr:first-child > *:first-child, | ||||
| .ant-table-wrapper .ant-table-container table > tbody > tr:first-child { | |||||
| .ant-table-wrapper .ant-table-tbody>tr>td:first-child { | |||||
| padding: 0 30px; | padding: 0 30px; | ||||
| } | } | ||||
| .ant-pro-global-header-logo-mix { | .ant-pro-global-header-logo-mix { | ||||
| width: 257px; | width: 257px; | ||||
| height: 75px; | height: 75px; | ||||
| @@ -256,7 +256,7 @@ function Experiment() { | |||||
| }; | }; | ||||
| const routerToText = (e, item, record) => { | const routerToText = (e, item, record) => { | ||||
| e.stopPropagation(); | e.stopPropagation(); | ||||
| navgite({ pathname: `/experiment/pytorchtext/${record.workflow_id}/${item.id}` }); | |||||
| navgite({ pathname: `/pipeline/experimentPytorchtext/${record.workflow_id}/${item.id}` }); | |||||
| }; | }; | ||||
| const handleTensorboard = async (experimentIn) => { | const handleTensorboard = async (experimentIn) => { | ||||
| @@ -0,0 +1,10 @@ | |||||
| import missingPage from '@/assets/img/missing-back.png'; | |||||
| const MissingPage = () => ( | |||||
| <div style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center' }}> | |||||
| <img src={missingPage} style={{ width: '575px', margin: '278px 0 44px 0' }} alt="" /> | |||||
| <span style={{ color: '#575757', fontSize: '16px' }}>页面开发中,敬请期待......</span> | |||||
| </div> | |||||
| ); | |||||
| export default MissingPage; | |||||
| @@ -79,7 +79,7 @@ public class ExperimentController extends BaseController { | |||||
| */ | */ | ||||
| @PostMapping | @PostMapping | ||||
| @ApiOperation("新增实验") | @ApiOperation("新增实验") | ||||
| public GenericsAjaxResult<Experiment> add(@RequestBody Experiment experiment) { | |||||
| public GenericsAjaxResult<Experiment> add(@RequestBody Experiment experiment) throws Exception { | |||||
| return genericsSuccess(this.experimentService.insert(experiment)); | return genericsSuccess(this.experimentService.insert(experiment)); | ||||
| } | } | ||||
| @@ -131,7 +131,7 @@ public class ExperimentController extends BaseController { | |||||
| */ | */ | ||||
| @PostMapping("/addAndRunExperiment") | @PostMapping("/addAndRunExperiment") | ||||
| @ApiOperation("实验创建页面确定并运行") | @ApiOperation("实验创建页面确定并运行") | ||||
| public GenericsAjaxResult<Experiment> addAndRunExperiment(@RequestBody Experiment experiment) { | |||||
| public GenericsAjaxResult<Experiment> addAndRunExperiment(@RequestBody Experiment experiment) throws Exception { | |||||
| return genericsSuccess(this.experimentService.addAndRunExperiment(experiment)); | return genericsSuccess(this.experimentService.addAndRunExperiment(experiment)); | ||||
| } | } | ||||
| } | } | ||||
| @@ -79,7 +79,7 @@ public class WorkflowController extends BaseController { | |||||
| */ | */ | ||||
| @PostMapping | @PostMapping | ||||
| @ApiOperation("新增流水线") | @ApiOperation("新增流水线") | ||||
| public GenericsAjaxResult<Workflow> add(@RequestBody Workflow workflow) { | |||||
| public GenericsAjaxResult<Workflow> add(@RequestBody Workflow workflow) throws Exception { | |||||
| return genericsSuccess(this.workflowService.insert(workflow)); | return genericsSuccess(this.workflowService.insert(workflow)); | ||||
| } | } | ||||
| @@ -0,0 +1,36 @@ | |||||
| package com.ruoyi.platform.controller.workspace; | |||||
| import com.ruoyi.common.core.web.controller.BaseController; | |||||
| import com.ruoyi.common.core.web.domain.AjaxResult; | |||||
| import com.ruoyi.common.core.web.domain.GenericsAjaxResult; | |||||
| import com.ruoyi.platform.service.JupyterService; | |||||
| import com.ruoyi.platform.service.WorkspaceService; | |||||
| import io.swagger.annotations.Api; | |||||
| import io.swagger.annotations.ApiOperation; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| import javax.annotation.Resource; | |||||
| import java.util.Map; | |||||
| @RestController | |||||
| @RequestMapping("workspace") | |||||
| @Api("工作空间管理") | |||||
| public class WorkspaceController extends BaseController { | |||||
| @Resource | |||||
| private WorkspaceService workspaceService; | |||||
| @GetMapping("/overview") | |||||
| @ApiOperation("得到运行概览") | |||||
| public GenericsAjaxResult<Map<String, Object>> getOverview() { | |||||
| return genericsSuccess(this.workspaceService.getOverview()); | |||||
| } | |||||
| @GetMapping("/assetCount") | |||||
| @ApiOperation("得到AI资产计数") | |||||
| public GenericsAjaxResult<Map<String, Integer>> getAssetCount(@RequestParam(value = "isPublic", | |||||
| defaultValue = "true") Boolean isPublic) { | |||||
| return genericsSuccess(this.workspaceService.getAssetCount(isPublic)); | |||||
| } | |||||
| } | |||||
| @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonRawValue; | |||||
| import com.fasterxml.jackson.databind.PropertyNamingStrategy; | import com.fasterxml.jackson.databind.PropertyNamingStrategy; | ||||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||||
| import com.ruoyi.common.core.web.domain.BaseEntity; | import com.ruoyi.common.core.web.domain.BaseEntity; | ||||
| import com.ruoyi.platform.annotations.CheckDuplicate; | |||||
| import io.swagger.annotations.ApiModel; | import io.swagger.annotations.ApiModel; | ||||
| import io.swagger.annotations.ApiModelProperty; | import io.swagger.annotations.ApiModelProperty; | ||||
| @@ -28,6 +29,7 @@ public class Experiment implements Serializable { | |||||
| @ApiModelProperty(value = "主键") | @ApiModelProperty(value = "主键") | ||||
| private Integer id; | private Integer id; | ||||
| @ApiModelProperty(value = "名称") | @ApiModelProperty(value = "名称") | ||||
| @CheckDuplicate | |||||
| private String name; | private String name; | ||||
| @ApiModelProperty(value = "工作流ID") | @ApiModelProperty(value = "工作流ID") | ||||
| @@ -71,6 +71,11 @@ public class ExperimentIns implements Serializable { | |||||
| private Integer state; | private Integer state; | ||||
| @ApiModelProperty(value = "实验实例对应的流水线ID") | |||||
| private Long workflowId; | |||||
| public ExperimentIns() { | public ExperimentIns() { | ||||
| } | } | ||||
| @@ -201,5 +206,9 @@ public class ExperimentIns implements Serializable { | |||||
| this.state = state; | this.state = state; | ||||
| } | } | ||||
| public Long getWorkflowId() {return workflowId;} | |||||
| public void setWorkflowId(Long workflowId) {this.workflowId = workflowId;} | |||||
| } | } | ||||
| @@ -2,6 +2,7 @@ package com.ruoyi.platform.domain; | |||||
| import com.fasterxml.jackson.databind.PropertyNamingStrategy; | import com.fasterxml.jackson.databind.PropertyNamingStrategy; | ||||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||||
| import com.ruoyi.platform.annotations.CheckDuplicate; | |||||
| import io.swagger.annotations.ApiModelProperty; | import io.swagger.annotations.ApiModelProperty; | ||||
| import java.util.Date; | import java.util.Date; | ||||
| @@ -25,6 +26,7 @@ public class Image implements Serializable { | |||||
| * 镜像名称 | * 镜像名称 | ||||
| */ | */ | ||||
| @ApiModelProperty(value = "名称") | @ApiModelProperty(value = "名称") | ||||
| @CheckDuplicate | |||||
| private String name; | private String name; | ||||
| @ApiModelProperty(value = "镜像描述") | @ApiModelProperty(value = "镜像描述") | ||||
| @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; | |||||
| import com.fasterxml.jackson.annotation.JsonRawValue; | import com.fasterxml.jackson.annotation.JsonRawValue; | ||||
| import com.fasterxml.jackson.databind.PropertyNamingStrategy; | import com.fasterxml.jackson.databind.PropertyNamingStrategy; | ||||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||||
| import com.ruoyi.platform.annotations.CheckDuplicate; | |||||
| import com.ruoyi.platform.handler.BaseMetaObjectHandler; | import com.ruoyi.platform.handler.BaseMetaObjectHandler; | ||||
| import io.swagger.annotations.ApiModel; | import io.swagger.annotations.ApiModel; | ||||
| import io.swagger.annotations.ApiModelProperty; | import io.swagger.annotations.ApiModelProperty; | ||||
| @@ -33,6 +34,7 @@ public class Workflow extends BaseMetaObjectHandler implements Serializable { | |||||
| private Long id; | private Long id; | ||||
| @ApiModelProperty(value = "工作流名称") | @ApiModelProperty(value = "工作流名称") | ||||
| @CheckDuplicate | |||||
| private String name; | private String name; | ||||
| @ApiModelProperty(value = "流水线描述") | @ApiModelProperty(value = "流水线描述") | ||||
| @@ -1,5 +1,6 @@ | |||||
| package com.ruoyi.platform.mapper; | package com.ruoyi.platform.mapper; | ||||
| import com.ruoyi.platform.domain.Dataset; | |||||
| import com.ruoyi.platform.domain.Experiment; | import com.ruoyi.platform.domain.Experiment; | ||||
| import org.apache.ibatis.annotations.Param; | import org.apache.ibatis.annotations.Param; | ||||
| import org.springframework.data.domain.Pageable; | import org.springframework.data.domain.Pageable; | ||||
| @@ -80,6 +81,9 @@ public interface ExperimentDao { | |||||
| */ | */ | ||||
| int deleteById(Integer id); | int deleteById(Integer id); | ||||
| List<Experiment> queryByExperiment(@Param("experiment")Experiment experiment); | |||||
| List<Experiment> queryByExperiment(@Param("experiment") Experiment experiment); | |||||
| Experiment findByName(@Param("name") String name); | |||||
| } | } | ||||
| @@ -39,6 +39,14 @@ public interface ExperimentInsDao { | |||||
| */ | */ | ||||
| long count(@Param("experimentIns") ExperimentIns experimentIns); | long count(@Param("experimentIns") ExperimentIns experimentIns); | ||||
| /* | |||||
| 统计实验实例总数 | |||||
| @return 总行数 | |||||
| */ | |||||
| // long experimentInsCount(String status); | |||||
| /** | /** | ||||
| * 新增数据 | * 新增数据 | ||||
| * | * | ||||
| @@ -89,6 +97,10 @@ public interface ExperimentInsDao { | |||||
| */ | */ | ||||
| List<ExperimentIns> getByExperimentId(Integer experimentId); | List<ExperimentIns> getByExperimentId(Integer experimentId); | ||||
| List<ExperimentIns> getLatestInsList(); | |||||
| List<ExperimentIns> queryByExperiment(@Param("experimentIns") ExperimentIns experimentIns); | List<ExperimentIns> queryByExperiment(@Param("experimentIns") ExperimentIns experimentIns); | ||||
| List<ExperimentIns> queryByExperimentId(Integer id); | List<ExperimentIns> queryByExperimentId(Integer id); | ||||
| @@ -1,5 +1,6 @@ | |||||
| package com.ruoyi.platform.mapper; | package com.ruoyi.platform.mapper; | ||||
| import com.ruoyi.platform.domain.Experiment; | |||||
| import com.ruoyi.platform.domain.Workflow; | import com.ruoyi.platform.domain.Workflow; | ||||
| import org.apache.ibatis.annotations.Param; | import org.apache.ibatis.annotations.Param; | ||||
| import org.springframework.data.domain.Pageable; | import org.springframework.data.domain.Pageable; | ||||
| @@ -32,13 +33,25 @@ public interface WorkflowDao { | |||||
| List<Workflow> queryAllByLimit(@Param("workflow") Workflow workflow, @Param("pageable") Pageable pageable); | List<Workflow> queryAllByLimit(@Param("workflow") Workflow workflow, @Param("pageable") Pageable pageable); | ||||
| /** | /** | ||||
| * 统计总行数 | |||||
| * 统计总行数 实体作为筛选条件 | |||||
| * | * | ||||
| * @param workflow 查询条件 | * @param workflow 查询条件 | ||||
| * @return 总行数 | * @return 总行数 | ||||
| */ | */ | ||||
| long count(@Param("workflow") Workflow workflow); | long count(@Param("workflow") Workflow workflow); | ||||
| /** | |||||
| * 统计流水线总数 | |||||
| * | |||||
| * | |||||
| * @return 总行数 | |||||
| */ | |||||
| long workflowCount(); | |||||
| /** | /** | ||||
| * 新增数据 | * 新增数据 | ||||
| * | * | ||||
| @@ -87,5 +100,8 @@ public interface WorkflowDao { | |||||
| * @return 对象列表 | * @return 对象列表 | ||||
| */ | */ | ||||
| List<Workflow> queryByName(@Param("name") String name); | List<Workflow> queryByName(@Param("name") String name); | ||||
| Workflow findByName(@Param("name") String name); | |||||
| } | } | ||||
| @@ -30,6 +30,7 @@ public class ExperimentInstanceStatusTask { | |||||
| private ExperimentInsDao experimentInsDao; | private ExperimentInsDao experimentInsDao; | ||||
| private List<Integer> experimentIds = new ArrayList<>(); | private List<Integer> experimentIds = new ArrayList<>(); | ||||
| @Scheduled(cron = "0/30 * * * * ?") // 每30S执行一次 | @Scheduled(cron = "0/30 * * * * ?") // 每30S执行一次 | ||||
| public void executeExperimentInsStatus() throws IOException { | public void executeExperimentInsStatus() throws IOException { | ||||
| // 首先查到所有非终止态的实验实例 | // 首先查到所有非终止态的实验实例 | ||||
| @@ -47,15 +48,16 @@ public class ExperimentInstanceStatusTask { | |||||
| } | } | ||||
| if (!StringUtils.equals(oldStatus,experimentIns.getStatus())){ | if (!StringUtils.equals(oldStatus,experimentIns.getStatus())){ | ||||
| experimentIns.setUpdateTime(new Date()); | experimentIns.setUpdateTime(new Date()); | ||||
| // 线程安全的添加操作 | |||||
| synchronized (experimentIds) { | synchronized (experimentIds) { | ||||
| experimentIds.add(experimentIns.getExperimentId()); // 线程安全的添加操作 | |||||
| experimentIds.add(experimentIns.getExperimentId()); | |||||
| } | } | ||||
| updateList.add(experimentIns); | updateList.add(experimentIns); | ||||
| } | } | ||||
| experimentInsDao.update(experimentIns); | experimentInsDao.update(experimentIns); | ||||
| } | } | ||||
| } | } | ||||
| if (updateList.size() > 0){ | if (updateList.size() > 0){ | ||||
| experimentInsDao.insertOrUpdateBatch(updateList); | experimentInsDao.insertOrUpdateBatch(updateList); | ||||
| @@ -85,11 +87,13 @@ public class ExperimentInstanceStatusTask { | |||||
| updateExperiments.add(experiment); | updateExperiments.add(experiment); | ||||
| } | } | ||||
| } | } | ||||
| if (!updateExperiments.isEmpty()) { | if (!updateExperiments.isEmpty()) { | ||||
| experimentDao.insertOrUpdateBatch(updateExperiments); | experimentDao.insertOrUpdateBatch(updateExperiments); | ||||
| for (int index = 0; index < updateExperiments.size(); index++) { | for (int index = 0; index < updateExperiments.size(); index++) { | ||||
| // 线程安全的删除操作 | |||||
| synchronized (experimentIds) { | synchronized (experimentIds) { | ||||
| experimentIds.remove(index); // 线程安全的删除操作 | |||||
| experimentIds.remove(index); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -79,7 +79,7 @@ DatasetService { | |||||
| List<String> getDatasetVersions(Integer datasetId) throws Exception; | List<String> getDatasetVersions(Integer datasetId) throws Exception; | ||||
| String insertDatasetAndVersion(DatasetVo datasetVo) throws Exception; | String insertDatasetAndVersion(DatasetVo datasetVo) throws Exception; | ||||
| public void checkDeclaredName(Dataset insert) throws Exception; | |||||
| void checkDeclaredName(Dataset insert) throws Exception; | |||||
| ResponseEntity<InputStreamResource> downloadAllDatasetFiles(Integer datasetId, String version) throws Exception; | ResponseEntity<InputStreamResource> downloadAllDatasetFiles(Integer datasetId, String version) throws Exception; | ||||
| @@ -103,4 +103,6 @@ public interface ExperimentInsService { | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| List<ExperimentIns> queryByExperimentIsNotTerminated(); | List<ExperimentIns> queryByExperimentIsNotTerminated(); | ||||
| Map<String, Long> countByStatus(); | |||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| package com.ruoyi.platform.service; | package com.ruoyi.platform.service; | ||||
| import com.ruoyi.platform.domain.Experiment; | import com.ruoyi.platform.domain.Experiment; | ||||
| import com.ruoyi.platform.domain.Workflow; | |||||
| import org.springframework.data.domain.Page; | import org.springframework.data.domain.Page; | ||||
| import org.springframework.data.domain.PageRequest; | import org.springframework.data.domain.PageRequest; | ||||
| import org.springframework.http.ResponseEntity; | import org.springframework.http.ResponseEntity; | ||||
| @@ -39,7 +40,7 @@ public interface ExperimentService { | |||||
| * @param experiment 实例对象 | * @param experiment 实例对象 | ||||
| * @return 实例对象 | * @return 实例对象 | ||||
| */ | */ | ||||
| Experiment insert(Experiment experiment); | |||||
| Experiment insert(Experiment experiment) throws Exception; | |||||
| /** | /** | ||||
| * 修改数据 | * 修改数据 | ||||
| @@ -59,7 +60,7 @@ public interface ExperimentService { | |||||
| String removeById(Integer id) throws Exception; | String removeById(Integer id) throws Exception; | ||||
| Experiment runExperiment(Integer id) throws Exception; | Experiment runExperiment(Integer id) throws Exception; | ||||
| Experiment addAndRunExperiment(Experiment experiment); | |||||
| Experiment addAndRunExperiment(Experiment experiment) throws Exception; | |||||
| /** | /** | ||||
| * 分页查询实验状态 | * 分页查询实验状态 | ||||
| @@ -75,4 +76,6 @@ public interface ExperimentService { | |||||
| ResponseEntity<Experiment> showExperimentConfig(Experiment experiment); | ResponseEntity<Experiment> showExperimentConfig(Experiment experiment); | ||||
| List<Experiment> queryByWorkflowId(Long id); | List<Experiment> queryByWorkflowId(Long id); | ||||
| void checkDeclaredName(Experiment insert) throws Exception; | |||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| package com.ruoyi.platform.service; | package com.ruoyi.platform.service; | ||||
| import com.fasterxml.jackson.core.JsonProcessingException; | import com.fasterxml.jackson.core.JsonProcessingException; | ||||
| import com.ruoyi.platform.domain.Dataset; | |||||
| import com.ruoyi.platform.domain.Workflow; | import com.ruoyi.platform.domain.Workflow; | ||||
| import org.springframework.data.domain.Page; | import org.springframework.data.domain.Page; | ||||
| import org.springframework.data.domain.PageRequest; | import org.springframework.data.domain.PageRequest; | ||||
| @@ -36,7 +37,7 @@ public interface WorkflowService { | |||||
| * @param workflow 实例对象 | * @param workflow 实例对象 | ||||
| * @return 实例对象 | * @return 实例对象 | ||||
| */ | */ | ||||
| Workflow insert(Workflow workflow); | |||||
| Workflow insert(Workflow workflow) throws Exception; | |||||
| /** | /** | ||||
| * 修改数据 | * 修改数据 | ||||
| @@ -64,6 +65,8 @@ public interface WorkflowService { | |||||
| Workflow duplicateWorkflow(Long id); | Workflow duplicateWorkflow(Long id); | ||||
| void checkDeclaredName(Workflow insert) throws Exception; | |||||
| /** | /** | ||||
| * 按流水线id保存 | * 按流水线id保存 | ||||
| * | * | ||||
| @@ -0,0 +1,24 @@ | |||||
| package com.ruoyi.platform.service; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * (workspace)服务接口 | |||||
| * | |||||
| * @author Xidaray | |||||
| * @since 2024-5-06 14:38:07 | |||||
| */ | |||||
| public interface WorkspaceService { | |||||
| /** | |||||
| * 得到工作空间数据概览 | |||||
| * | |||||
| * @return 概览数据 | |||||
| */ | |||||
| Map<String, Object> getOverview(); | |||||
| Map<String, Integer> getAssetCount(Boolean isPublic); | |||||
| } | |||||
| @@ -1,7 +1,6 @@ | |||||
| package com.ruoyi.platform.service.impl; | package com.ruoyi.platform.service.impl; | ||||
| import com.ruoyi.common.security.utils.SecurityUtils; | import com.ruoyi.common.security.utils.SecurityUtils; | ||||
| import com.ruoyi.platform.domain.Experiment; | |||||
| import com.ruoyi.platform.domain.ExperimentIns; | import com.ruoyi.platform.domain.ExperimentIns; | ||||
| import com.ruoyi.platform.mapper.ExperimentDao; | import com.ruoyi.platform.mapper.ExperimentDao; | ||||
| import com.ruoyi.platform.mapper.ExperimentInsDao; | import com.ruoyi.platform.mapper.ExperimentInsDao; | ||||
| @@ -62,6 +61,15 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||||
| this.minioUtil = minioUtil; | this.minioUtil = minioUtil; | ||||
| } | } | ||||
| public enum ExperimentInsStatus { | |||||
| Running, | |||||
| Succeeded, | |||||
| Pending, | |||||
| Failed, | |||||
| Terminated | |||||
| } | |||||
| /** | /** | ||||
| * 通过ID查询单条数据 | * 通过ID查询单条数据 | ||||
| * | * | ||||
| @@ -533,6 +541,26 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||||
| return experimentInsDao.queryByExperimentIsNotTerminated(); | return experimentInsDao.queryByExperimentIsNotTerminated(); | ||||
| } | } | ||||
| @Override | |||||
| public Map<String, Long> countByStatus() { | |||||
| // 创建一个用于存储状态计数的map | |||||
| Map<String, Long> statusCountMap = new HashMap<>(); | |||||
| // 遍历枚举中的所有状态 | |||||
| for (ExperimentInsStatus status : ExperimentInsStatus.values()) { | |||||
| ExperimentIns experimentIns = new ExperimentIns(); | |||||
| experimentIns.setStatus(status.toString()); // 设置实例的状态为枚举中的状态 | |||||
| // 查询具有相同状态的实例数量 | |||||
| Long count = experimentInsDao.count(experimentIns); | |||||
| // 将状态及其对应的实例数量放入映射中 | |||||
| statusCountMap.put(status.toString(), count); | |||||
| } | |||||
| return statusCountMap; | |||||
| } | |||||
| private boolean isTerminatedState(ExperimentIns ins) throws IOException { | private boolean isTerminatedState(ExperimentIns ins) throws IOException { | ||||
| // 定义终止态的列表,例如 "Succeeded", "Failed" 等 | // 定义终止态的列表,例如 "Succeeded", "Failed" 等 | ||||
| String status = ins.getStatus(); | String status = ins.getStatus(); | ||||
| @@ -543,8 +571,6 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||||
| //如果跟node_status里面不一样,就要去更新node_status的信息 | //如果跟node_status里面不一样,就要去更新node_status的信息 | ||||
| String nodesStatus = ins.getNodesStatus(); | String nodesStatus = ins.getNodesStatus(); | ||||
| Map<String, Object> nodeMap = JsonUtils.jsonToMap(nodesStatus); | Map<String, Object> nodeMap = JsonUtils.jsonToMap(nodesStatus); | ||||
| String keyStartsWithWorkflow = nodeMap.keySet().stream() | String keyStartsWithWorkflow = nodeMap.keySet().stream() | ||||
| .filter(key -> key.startsWith("workflow-")) | .filter(key -> key.startsWith("workflow-")) | ||||
| .findFirst() | .findFirst() | ||||
| @@ -1,6 +1,7 @@ | |||||
| package com.ruoyi.platform.service.impl; | package com.ruoyi.platform.service.impl; | ||||
| import com.ruoyi.common.security.utils.SecurityUtils; | import com.ruoyi.common.security.utils.SecurityUtils; | ||||
| import com.ruoyi.platform.annotations.CheckDuplicate; | |||||
| import com.ruoyi.platform.domain.Experiment; | import com.ruoyi.platform.domain.Experiment; | ||||
| import com.ruoyi.platform.domain.ExperimentIns; | import com.ruoyi.platform.domain.ExperimentIns; | ||||
| import com.ruoyi.platform.domain.Workflow; | import com.ruoyi.platform.domain.Workflow; | ||||
| @@ -25,6 +26,7 @@ import org.springframework.stereotype.Service; | |||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.lang.reflect.Field; | |||||
| import java.util.*; | import java.util.*; | ||||
| /** | /** | ||||
| @@ -130,8 +132,9 @@ public class ExperimentServiceImpl implements ExperimentService { | |||||
| * @return 实例对象 | * @return 实例对象 | ||||
| */ | */ | ||||
| @Override | @Override | ||||
| public Experiment insert(Experiment experiment) { | |||||
| public Experiment insert(Experiment experiment) throws Exception { | |||||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | LoginUser loginUser = SecurityUtils.getLoginUser(); | ||||
| checkDeclaredName(experiment); | |||||
| experiment.setCreateBy(loginUser.getUsername()); | experiment.setCreateBy(loginUser.getUsername()); | ||||
| experiment.setUpdateBy(loginUser.getUsername()); | experiment.setUpdateBy(loginUser.getUsername()); | ||||
| experiment.setUpdateTime(new Date()); | experiment.setUpdateTime(new Date()); | ||||
| @@ -229,7 +232,7 @@ public class ExperimentServiceImpl implements ExperimentService { | |||||
| //这里全局参数是一个json数组,需要转换成一个list<Map> | //这里全局参数是一个json数组,需要转换成一个list<Map> | ||||
| List<Map<String, Object>> params = JacksonUtil.parseJSONStr2MapList(StringUtils.isEmpty(experiment.getGlobalParam()) ? "[]" : experiment.getGlobalParam()); | List<Map<String, Object>> params = JacksonUtil.parseJSONStr2MapList(StringUtils.isEmpty(experiment.getGlobalParam()) ? "[]" : experiment.getGlobalParam()); | ||||
| runReqMap.put("params", params); | runReqMap.put("params", params); | ||||
| //// 实验字段的Map,不要写成一行! | |||||
| //// 实验字段的Map,不要写成一行!否则会返回null | |||||
| Map<String, Object> experimentMap = new HashMap<>(); | Map<String, Object> experimentMap = new HashMap<>(); | ||||
| experimentMap.put("name", "experiment-"+experiment.getId()); | experimentMap.put("name", "experiment-"+experiment.getId()); | ||||
| runReqMap.put("experiment", experimentMap); | runReqMap.put("experiment", experimentMap); | ||||
| @@ -279,7 +282,7 @@ public class ExperimentServiceImpl implements ExperimentService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public Experiment addAndRunExperiment(Experiment experiment) { | |||||
| public Experiment addAndRunExperiment(Experiment experiment) throws Exception { | |||||
| // 第一步: 调用add方法插入实验记录到数据库 | // 第一步: 调用add方法插入实验记录到数据库 | ||||
| Experiment newExperiment = this.insert(experiment); | Experiment newExperiment = this.insert(experiment); | ||||
| @@ -317,6 +320,28 @@ public class ExperimentServiceImpl implements ExperimentService { | |||||
| return experimentDao.queryByExperiment(experiment); | return experimentDao.queryByExperiment(experiment); | ||||
| } | } | ||||
| public void checkDeclaredName(Experiment insert) throws Exception { | |||||
| Experiment existingExperiment = experimentDao.findByName(insert.getName()); | |||||
| if (existingExperiment != null) { | |||||
| // 检查找到的流水线是否与要插入的流水线相同 | |||||
| if (insert.getId() != null && insert.getId().equals(existingExperiment.getId())) { | |||||
| // 这是相同的流水线,更新操作中没有重复名称问题 | |||||
| return; | |||||
| } | |||||
| // 现在我们知道还有另一个具有相同名称的流水线 | |||||
| Field[] fields = Experiment.class.getDeclaredFields(); | |||||
| for (Field field : fields) { | |||||
| field.setAccessible(true); // 使私有字段可访问 | |||||
| if ("name".equals(field.getName()) && field.isAnnotationPresent(CheckDuplicate.class)) { | |||||
| // 如果字段是“name”并且标记了CheckDuplicate注解 | |||||
| CheckDuplicate annotation = field.getAnnotation(CheckDuplicate.class); | |||||
| throw new Exception("重复的实验名称: " + insert.getName() + ". " + annotation.message()); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,6 +1,8 @@ | |||||
| package com.ruoyi.platform.service.impl; | package com.ruoyi.platform.service.impl; | ||||
| import com.ruoyi.common.security.utils.SecurityUtils; | import com.ruoyi.common.security.utils.SecurityUtils; | ||||
| import com.ruoyi.platform.annotations.CheckDuplicate; | |||||
| import com.ruoyi.platform.domain.Dataset; | |||||
| import com.ruoyi.platform.domain.Experiment; | import com.ruoyi.platform.domain.Experiment; | ||||
| import com.ruoyi.platform.domain.Workflow; | import com.ruoyi.platform.domain.Workflow; | ||||
| import com.ruoyi.platform.mapper.WorkflowDao; | import com.ruoyi.platform.mapper.WorkflowDao; | ||||
| @@ -18,6 +20,7 @@ import org.springframework.data.domain.PageRequest; | |||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||
| import java.lang.reflect.Field; | |||||
| import java.util.Date; | import java.util.Date; | ||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -88,8 +91,9 @@ public class WorkflowServiceImpl implements WorkflowService { | |||||
| * @return 实例对象 | * @return 实例对象 | ||||
| */ | */ | ||||
| @Override | @Override | ||||
| public Workflow insert(Workflow workflow) { | |||||
| public Workflow insert(Workflow workflow) throws Exception { | |||||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | LoginUser loginUser = SecurityUtils.getLoginUser(); | ||||
| checkDeclaredName(workflow); | |||||
| workflow.setCreateBy(loginUser.getUsername()); | workflow.setCreateBy(loginUser.getUsername()); | ||||
| workflow.setUpdateBy(loginUser.getUsername()); | workflow.setUpdateBy(loginUser.getUsername()); | ||||
| workflow.setUpdateTime(new Date()); | workflow.setUpdateTime(new Date()); | ||||
| @@ -191,7 +195,32 @@ public class WorkflowServiceImpl implements WorkflowService { | |||||
| } | } | ||||
| return null; | return null; | ||||
| } | |||||
| public void checkDeclaredName(Workflow insert) throws Exception { | |||||
| Workflow existingWorkflow = workflowDao.findByName(insert.getName()); | |||||
| if (existingWorkflow != null) { | |||||
| // 检查找到的流水线是否与要插入的流水线相同 | |||||
| if (insert.getId() != null && insert.getId().equals(existingWorkflow.getId())) { | |||||
| // 这是相同的流水线,更新操作中没有重复名称问题 | |||||
| return; | |||||
| } | |||||
| // 现在我们知道还有另一个具有相同名称的流水线 | |||||
| Field[] fields = Workflow.class.getDeclaredFields(); | |||||
| for (Field field : fields) { | |||||
| field.setAccessible(true); // 使私有字段可访问 | |||||
| if ("name".equals(field.getName()) && field.isAnnotationPresent(CheckDuplicate.class)) { | |||||
| // 如果字段是“name”并且标记了CheckDuplicate注解 | |||||
| CheckDuplicate annotation = field.getAnnotation(CheckDuplicate.class); | |||||
| throw new Exception("重复的流水线名称: " + insert.getName() + ". " + annotation.message()); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,117 @@ | |||||
| package com.ruoyi.platform.service.impl; | |||||
| import com.ruoyi.platform.domain.*; | |||||
| import com.ruoyi.platform.mapper.*; | |||||
| import com.ruoyi.platform.service.ExperimentInsService; | |||||
| import com.ruoyi.platform.service.ModelsService; | |||||
| import com.ruoyi.platform.service.WorkflowService; | |||||
| import com.ruoyi.platform.service.WorkspaceService; | |||||
| import lombok.val; | |||||
| import org.checkerframework.checker.units.qual.C; | |||||
| import org.springframework.stereotype.Service; | |||||
| import javax.annotation.Resource; | |||||
| import java.util.HashMap; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| @Service("WorkspaceService") | |||||
| public class WorkspaceServiceImpl implements WorkspaceService { | |||||
| @Resource | |||||
| private WorkflowService workflowService; | |||||
| @Resource | |||||
| private WorkflowDao workflowDao; | |||||
| @Resource | |||||
| private ExperimentDao experimentDao; | |||||
| @Resource | |||||
| private ExperimentInsDao experimentInsDao; | |||||
| @Resource | |||||
| private ExperimentInsService experimentInsService; | |||||
| @Resource | |||||
| private ModelsService modelsService; | |||||
| @Resource | |||||
| private ModelsDao modelsDao; | |||||
| @Resource | |||||
| private DatasetDao datasetDao; | |||||
| @Resource | |||||
| private ImageDao imageDao; | |||||
| @Resource | |||||
| private ComponentDao componentDao; | |||||
| @Override | |||||
| public Map<String, Object> getOverview() { | |||||
| Map<String, Object> resMap = new HashMap<String, Object>(); | |||||
| // 获取流水线数量 | |||||
| Long workflowCount = workflowDao.workflowCount(); | |||||
| resMap.put("workflowCount", workflowCount); | |||||
| // 获取运行中实验实例数量 | |||||
| List<ExperimentIns> experimentInsList = experimentInsService.queryByExperimentIsNotTerminated(); | |||||
| if (experimentInsList!=null && !experimentInsList.isEmpty()) { | |||||
| Integer experimentInsCount = experimentInsList.size(); | |||||
| resMap.put("runningExperimentInsCount", experimentInsCount); | |||||
| }else{ | |||||
| resMap.put("runningExperimentInsCount", 0); | |||||
| } | |||||
| //得到最近的三条实例记录,放进返回map | |||||
| List<ExperimentIns> latestExperimentInsList = experimentInsDao.getLatestInsList(); | |||||
| if (latestExperimentInsList != null && !latestExperimentInsList.isEmpty()) { | |||||
| for (ExperimentIns experimentIns : latestExperimentInsList){ | |||||
| Integer experimentId = experimentIns.getExperimentId(); | |||||
| Experiment experiment = this.experimentDao.queryById(experimentId); | |||||
| //拿到对应的流水线id | |||||
| Long correspondingWorkflowId = experiment.getWorkflowId(); | |||||
| experimentIns.setWorkflowId(correspondingWorkflowId); | |||||
| } | |||||
| resMap.put("latestExperimentInsList", latestExperimentInsList); | |||||
| } | |||||
| Map<String, Long> statusMap = experimentInsService.countByStatus(); | |||||
| resMap.put("experimentInsStatus", statusMap); | |||||
| return resMap; | |||||
| } | |||||
| @Override | |||||
| public Map<String, Integer> getAssetCount(Boolean isPublic) { | |||||
| Map<String,Integer> assetCountMap = new HashMap<String, Integer>(); | |||||
| int availableRange = isPublic ? 1 : 0; | |||||
| //统计数据集数量 | |||||
| Dataset dataset = new Dataset(); | |||||
| dataset.setAvailableRange(availableRange); | |||||
| Integer datasetCount = (int) this.datasetDao.count(dataset); | |||||
| assetCountMap.put("dataset", datasetCount); | |||||
| //统计模型数量 | |||||
| Models models = new Models(); | |||||
| models.setAvailableRange(availableRange); | |||||
| Integer modelCount = (int) this.modelsDao.count(models); | |||||
| assetCountMap.put("model", modelCount); | |||||
| //统计镜像数量 | |||||
| Image image = new Image(); | |||||
| image.setImageType(availableRange); | |||||
| Integer imageCount = (int) this.imageDao.count(image); | |||||
| assetCountMap.put("image", imageCount); | |||||
| //统计组件数量 | |||||
| Component component = new Component(); | |||||
| Integer componentCount = (int) this.componentDao.count(component); | |||||
| assetCountMap.put("component", componentCount); | |||||
| //统计流水线数量 | |||||
| Workflow workflow = new Workflow(); | |||||
| Integer workflowCount = (int) this.workflowDao.count(workflow); | |||||
| assetCountMap.put("workflow", workflowCount); | |||||
| return assetCountMap; | |||||
| } | |||||
| } | |||||
| @@ -340,6 +340,19 @@ public class K8sClientUtil { | |||||
| .withImage(image) | .withImage(image) | ||||
| .withPorts(new V1ContainerPort().containerPort(port).protocol("TCP")) | .withPorts(new V1ContainerPort().containerPort(port).protocol("TCP")) | ||||
| .withVolumeMounts(new V1VolumeMount().name("workspace").mountPath(mountPath).subPath(subPath)) | .withVolumeMounts(new V1VolumeMount().name("workspace").mountPath(mountPath).subPath(subPath)) | ||||
| .withNewSecurityContext().withNewPrivileged(true).endSecurityContext() | |||||
| .addNewEnv() | |||||
| .withName("HTTP_PROXY") | |||||
| .withValue("http://172.20.32.253:3128") | |||||
| .endEnv() | |||||
| .addNewEnv() | |||||
| .withName("HTTPS_PROXY") | |||||
| .withValue("http://172.20.32.253:3128") | |||||
| .endEnv() | |||||
| .addNewEnv() | |||||
| .withName("NO_PROXY") | |||||
| .withValue("localhost,kubernetes.default.svc") | |||||
| .endEnv() | |||||
| .endContainer() | .endContainer() | ||||
| .addNewVolume() | .addNewVolume() | ||||
| .withName("workspace").withPersistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvcName)) | .withName("workspace").withPersistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvcName)) | ||||
| @@ -24,6 +24,14 @@ | |||||
| where id = #{id} and state = 1 | where id = #{id} and state = 1 | ||||
| </select> | </select> | ||||
| <!--查询单个--> | |||||
| <select id="findByName" resultMap="ExperimentMap"> | |||||
| select | |||||
| id,name, workflow_id, global_param, status_list, description, create_by, create_time, update_by, update_time, state | |||||
| from experiment | |||||
| where name = #{name} and state = 1 limit 1 | |||||
| </select> | |||||
| <!--根据experiment查询--> | <!--根据experiment查询--> | ||||
| <select id="queryByExperiment" resultMap="ExperimentMap"> | <select id="queryByExperiment" resultMap="ExperimentMap"> | ||||
| select | select | ||||
| @@ -145,6 +153,7 @@ | |||||
| </where> | </where> | ||||
| </select> | </select> | ||||
| <!--新增所有列--> | <!--新增所有列--> | ||||
| <insert id="insert" keyProperty="id" useGeneratedKeys="true"> | <insert id="insert" keyProperty="id" useGeneratedKeys="true"> | ||||
| insert into experiment(name,workflow_id, global_param, status_list, 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) | ||||
| @@ -45,6 +45,16 @@ | |||||
| limit 5 | limit 5 | ||||
| </select> | </select> | ||||
| <!--查询最近的3个实例列表--> | |||||
| <select id="getLatestInsList" resultMap="ExperimentInsMap"> | |||||
| select id, experiment_id, argo_ins_name, argo_ins_ns, status, nodes_status,nodes_result, nodes_logs, global_param, start_time, finish_time, create_by, create_time, update_by, update_time, state | |||||
| from experiment_ins | |||||
| where state = 1 | |||||
| order by create_time DESC | |||||
| limit 3 | |||||
| </select> | |||||
| <select id="queryByExperiment" resultMap="ExperimentInsMap"> | <select id="queryByExperiment" resultMap="ExperimentInsMap"> | ||||
| select | select | ||||
| @@ -98,6 +98,13 @@ | |||||
| </where> | </where> | ||||
| </select> | </select> | ||||
| <!--统计总行数--> | |||||
| <select id="workflowCount" resultType="java.lang.Long"> | |||||
| select count(1) | |||||
| from workflow | |||||
| where state = 1 | |||||
| </select> | |||||
| <!--新增所有列--> | <!--新增所有列--> | ||||
| <insert id="insert" keyProperty="id" useGeneratedKeys="true"> | <insert id="insert" keyProperty="id" useGeneratedKeys="true"> | ||||
| insert into workflow(name, description, dag, global_param, create_by, create_time, update_by, update_time,state) | insert into workflow(name, description, dag, global_param, create_by, create_time, update_by, update_time,state) | ||||
| @@ -185,5 +192,13 @@ | |||||
| </if> | </if> | ||||
| </where> | </where> | ||||
| </select> | </select> | ||||
| <!--通过流水线名字进行精准查询--> | |||||
| <select id="findByName" resultMap="WorkflowMap"> | |||||
| select | |||||
| id, name, description, dag, global_param, create_by, create_time, update_by, update_time, state | |||||
| from workflow | |||||
| where name = #{name} and state = 1 limit 1 | |||||
| </select> | |||||
| </mapper> | </mapper> | ||||