From 2f8d383a066dd8e0711c56aa91f0ba579a9147b7 Mon Sep 17 00:00:00 2001 From: fanshuai <1141904845@qq.com> Date: Tue, 26 Mar 2024 16:45:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=BF=94=E5=9B=9E=E6=A1=86?= =?UTF-8?q?=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- react-ui/config/proxy.ts | 2 +- ruoyi-common/ruoyi-common-core/pom.xml | 4 + .../ruoyi/common/core/utils/SortUtils.java | 63 ++++++ .../core/web/controller/BaseController.java | 199 ++++++++++++++---- .../core/web/domain/GenericsAjaxResult.java | 171 +++++++++++++++ .../core/web/page/GenericsTableDataInfo.java | 98 +++++++++ .../experiment/ExperimentController.java | 13 +- .../experiment/ExperimentInsController.java | 1 + .../tensorBoard/TensorBoardController.java | 39 ++++ .../controller/tensorBoard/tensorBoard.java | 15 -- .../com/ruoyi/platform/domain/Experiment.java | 2 + .../ruoyi/platform/domain/ExperimentIns.java | 1 + .../platform/service/TensorBoardService.java | 13 ++ .../service/impl/JupyterServiceImpl.java | 4 +- .../service/impl/TensorBoardServiceImpl.java | 35 +++ .../ruoyi/platform/utils/K8sClientUtil.java | 73 ++++++- .../com/ruoyi/platform/vo/FrameLogPathVo.java | 37 ++++ 17 files changed, 702 insertions(+), 68 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SortUtils.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/GenericsAjaxResult.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/GenericsTableDataInfo.java create mode 100644 ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/tensorBoard/TensorBoardController.java delete mode 100644 ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/tensorBoard/tensorBoard.java create mode 100644 ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/TensorBoardService.java create mode 100644 ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/TensorBoardServiceImpl.java create mode 100644 ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/FrameLogPathVo.java diff --git a/react-ui/config/proxy.ts b/react-ui/config/proxy.ts index 911db70..fc98495 100644 --- a/react-ui/config/proxy.ts +++ b/react-ui/config/proxy.ts @@ -15,7 +15,7 @@ export default { // localhost:8000/api/** -> https://preview.pro.ant.design/api/** '/api/': { // 要代理的地址 - // target: 'http://172.20.32.181:31205', + // target: 'ci4s-gateway-service.ci4s-test.svc:8082', // target: 'http://172.20.32.98:8082', target: 'http://172.20.32.150:8082', // 配置了这个可以从 http 代理到 https diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml index b394ea9..67735a9 100644 --- a/ruoyi-common/ruoyi-common-core/pom.xml +++ b/ruoyi-common/ruoyi-common-core/pom.xml @@ -112,6 +112,10 @@ io.swagger swagger-annotations + + org.projectlombok + lombok + diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SortUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SortUtils.java new file mode 100644 index 0000000..7d1e233 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SortUtils.java @@ -0,0 +1,63 @@ +package com.ruoyi.common.core.utils; + +import com.ruoyi.common.core.exception.ServiceException; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +/** + * @author otto + */ +public class SortUtils { + public static List sort(List list, final String param, boolean isAsc) { + //自定义Comparator对象,自定义排序 + Comparator c = (o1, o2) -> { + try { + Field field1 = o1.getClass().getDeclaredField(param); + Field field2 = o2.getClass().getDeclaredField(param); + field1.setAccessible(true); + field2.setAccessible(true); + if (Integer.class.equals(field1.getType())) { + if ((Integer) field1.get(o1) < (Integer) field2.get(o2)) { + return isAsc ? -1 : 1; + } else { + //注意!!返回值必须是一对相反数,否则无效。jdk1.7以后就是这样。 + return isAsc ? 1 : -1; + } + } + if (Long.class.equals(field1.getType())) { + Long long1 = (Long) field1.get(o1); + Long long2 = (Long) field2.get(o2); + if (long1 < long2) { + return isAsc ? -1 : 1; + } else { + //注意!!返回值必须是一对相反数,否则无效。jdk1.7以后就是这样。 + return isAsc ? 1 : -1; + } + } + if (Date.class.equals(field1.getType())) { + Date date1 = (Date) field1.get(o1); + Date date2 = (Date) field2.get(o2); + if (date1.before(date2)) { + return isAsc ? -1 : 1; + } else { + //注意!!返回值必须是一对相反数,否则无效。jdk1.7以后就是这样。 + return isAsc ? 1 : -1; + } + } + + } catch (NoSuchFieldException e) { + throw new ServiceException(String.format("指定的排序字段不存在[%s]", param)); + } catch (IllegalAccessException e) { + throw new ServiceException(String.format("访问排序字段非法,请检查排序字段[%s]权限", param)); + } + return 1; + }; + Collections.sort(list, c); + return list; + + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java index 2bfd8cb..b567af5 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java @@ -1,40 +1,41 @@ package com.ruoyi.common.core.web.controller; -import java.beans.PropertyEditorSupport; -import java.util.Date; -import java.util.List; +import com.github.pagehelper.PageInfo; +import com.ruoyi.common.core.constant.Constants; +import com.ruoyi.common.core.constant.HttpStatus; +import com.ruoyi.common.core.utils.*; +import com.ruoyi.common.core.web.domain.AjaxResult; +import com.ruoyi.common.core.web.domain.GenericsAjaxResult; +import com.ruoyi.common.core.web.page.GenericsTableDataInfo; +import com.ruoyi.common.core.web.page.PageDomain; +import com.ruoyi.common.core.web.page.TableDataInfo; +import com.ruoyi.common.core.web.page.TableSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; -import com.github.pagehelper.PageInfo; -import com.ruoyi.common.core.constant.HttpStatus; -import com.ruoyi.common.core.utils.DateUtils; -import com.ruoyi.common.core.utils.PageUtils; -import com.ruoyi.common.core.web.domain.AjaxResult; -import com.ruoyi.common.core.web.page.TableDataInfo; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; /** * web层通用数据处理 - * + * * @author ruoyi */ -public class BaseController -{ +public class BaseController { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 将前台传递过来的日期格式的字符串,自动转化为Date类型 */ @InitBinder - public void initBinder(WebDataBinder binder) - { + public void initBinder(WebDataBinder binder) { // Date 类型转换 - binder.registerCustomEditor(Date.class, new PropertyEditorSupport() - { + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() { @Override - public void setAsText(String text) - { + public void setAsText(String text) { setValue(DateUtils.parseDate(text)); } }); @@ -43,25 +44,22 @@ public class BaseController /** * 设置请求分页数据 */ - protected void startPage() - { + protected void startPage() { PageUtils.startPage(); } /** * 清理分页的线程变量 */ - protected void clearPage() - { + protected void clearPage() { PageUtils.clearPage(); } /** * 响应请求分页数据 */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - protected TableDataInfo getDataTable(List list) - { + @SuppressWarnings({"rawtypes", "unchecked"}) + protected TableDataInfo getDataTable(List list) { TableDataInfo rspData = new TableDataInfo(); rspData.setCode(HttpStatus.SUCCESS); rspData.setRows(list); @@ -70,73 +68,188 @@ public class BaseController return rspData; } + /** + * 响应请求分页数据 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected GenericsTableDataInfo getGenericsDataTable(List list) { + GenericsTableDataInfo rspData = new GenericsTableDataInfo<>(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setRows(list); + rspData.setMsg("查询成功"); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 响应请求分页数据 + */ + + protected GenericsTableDataInfo getGenericsDataTable(List list, Integer count) { + GenericsTableDataInfo rspData = new GenericsTableDataInfo<>(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setRows(list); + rspData.setMsg("查询成功"); + rspData.setTotal(count); + return rspData; + } + + /** + * 响应请求分页数据 + */ + protected GenericsTableDataInfo getAllGenericsDataTableToPage(List list) { + GenericsTableDataInfo rspData = new GenericsTableDataInfo<>(); + rspData.setCode(HttpStatus.SUCCESS); + int total = list.size(); + rspData.setTotal(total); + String orderByStr = ServletUtils.getParameter(Constants.ORDER_BY_COLUMN); + if (StringUtils.isNotEmpty(orderByStr)) { + String isAscStr = ServletUtils.getParameter(Constants.IS_ASC); + if (StringUtils.isNotEmpty(orderByStr)) { + String ascString = "asc"; + boolean isAsc = true; + if (isAscStr != null && !ascString.equals(isAscStr.toLowerCase())) { + isAsc = false; + } + SortUtils.sort(list, orderByStr, isAsc); + } + } + if (StringUtils.isNotEmpty(ServletUtils.getParameter(Constants.PAGE_NUM)) + && StringUtils.isNotEmpty(ServletUtils.getParameter(Constants.PAGE_SIZE))) { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + + if (pageNum != null && pageSize != null) { + if (pageNum > 0) { + pageNum = pageNum - 1; + } else { + pageNum = 0; + } + int startIndex = pageNum * pageSize; + int endIndex = (pageNum + 1) * pageSize; + + int currentSize = total - startIndex; + if (currentSize < pageSize && currentSize > 0) { + endIndex = startIndex + currentSize; + } else if (currentSize <= 0) { + if (total > pageSize) { + startIndex = total - pageSize - 1; + } else { + startIndex = 0; + endIndex = total; + } + + } + list = list.subList(startIndex, endIndex); + } + //todo:暂不考虑 Boolean reasonable = pageDomain.getReasonable(); + } + + rspData.setRows(list); + rspData.setMsg("查询成功"); + return rspData; + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected TableDataInfo getDataTable(List list, Integer size) { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setRows(list); + rspData.setMsg("查询成功"); + rspData.setTotal(size); + return rspData; + } + /** * 返回成功 */ - public AjaxResult success() - { + public AjaxResult success() { return AjaxResult.success(); } /** * 返回成功消息 */ - public AjaxResult success(String message) - { + public AjaxResult success(String message) { return AjaxResult.success(message); } /** * 返回成功消息 */ - public AjaxResult success(Object data) - { + public AjaxResult success(Object data) { return AjaxResult.success(data); } + /** + * 返回成功消息 + */ + public GenericsAjaxResult genericsSuccess(T data) { + return GenericsAjaxResult.success(data); + } + /** * 返回失败消息 */ - public AjaxResult error() - { + public AjaxResult error() { return AjaxResult.error(); } /** * 返回失败消息 */ - public AjaxResult error(String message) - { + public AjaxResult error(String message) { return AjaxResult.error(message); } /** * 返回警告消息 */ - public AjaxResult warn(String message) - { + public AjaxResult warn(String message) { return AjaxResult.warn(message); } /** * 响应返回结果 - * + * * @param rows 影响行数 * @return 操作结果 */ - protected AjaxResult toAjax(int rows) - { + protected AjaxResult toAjax(int rows) { return rows > 0 ? AjaxResult.success() : AjaxResult.error(); } /** * 响应返回结果 - * + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected GenericsAjaxResult toGenericsAjax(int rows) { + return rows > 0 ? GenericsAjaxResult.success() : GenericsAjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param data 数据 + * @return 操作结果 + */ + protected GenericsAjaxResult successToGenericsAjax(T data) { + return GenericsAjaxResult.success(data); + } + + /** + * 响应返回结果 + * * @param result 结果 * @return 操作结果 */ - protected AjaxResult toAjax(boolean result) - { + protected AjaxResult toAjax(boolean result) { return result ? success() : error(); } } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/GenericsAjaxResult.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/GenericsAjaxResult.java new file mode 100644 index 0000000..9185011 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/GenericsAjaxResult.java @@ -0,0 +1,171 @@ +package com.ruoyi.common.core.web.domain; + +import com.ruoyi.common.core.constant.HttpStatus; +import com.ruoyi.common.core.utils.StringUtils; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 操作消息提醒 + * + * @author ruoyi + */ +@Data +@ApiModel("返回对象") +public class GenericsAjaxResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 状态码 + */ + @ApiModelProperty("状态码") + private Integer code; + + /** + * 返回内容 + */ + @ApiModelProperty("返回内容") + private String msg; + + /** + * 数据对象 + */ + @ApiModelProperty("数据对象") + public T data; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public GenericsAjaxResult() { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public GenericsAjaxResult(int code, String msg) { + this.code = code; + this.msg = msg; + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public GenericsAjaxResult(int code, String msg, T data) { + this.code = code; + this.msg = msg; + if (StringUtils.isNotNull(data)) { + this.data = data; + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static GenericsAjaxResult success() { + return GenericsAjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static GenericsAjaxResult success(T data) { + return GenericsAjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static GenericsAjaxResult success(String msg) { + return GenericsAjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static GenericsAjaxResult success(String msg, T data) { + return new GenericsAjaxResult<>(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static GenericsAjaxResult warn(String msg) { + return GenericsAjaxResult.warn(msg, null); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static GenericsAjaxResult warn(String msg, T data) { + return new GenericsAjaxResult<>(HttpStatus.WARN, msg, data); + } + + /** + * 返回错误消息 + * + * @return 错误消息 + */ + public static GenericsAjaxResult error() { + return GenericsAjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 错误消息 + */ + public static GenericsAjaxResult error(String msg) { + return GenericsAjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 错误消息 + */ + public static GenericsAjaxResult error(String msg, T data) { + return new GenericsAjaxResult<>(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 错误消息 + */ + public static GenericsAjaxResult error(int code, String msg) { + return new GenericsAjaxResult<>(code, msg, null); + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/GenericsTableDataInfo.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/GenericsTableDataInfo.java new file mode 100644 index 0000000..9a75e79 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/GenericsTableDataInfo.java @@ -0,0 +1,98 @@ +package com.ruoyi.common.core.web.page; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageInfo; +import com.ruoyi.common.core.constant.HttpStatus; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * 表格分页数据对象 + * + * @author ruoyi + */ +@Data +@ApiModel("表格分页数据对象") +public class GenericsTableDataInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 总记录数 + */ + @ApiModelProperty("总记录数") + private long total; + + /** + * 列表数据 + */ + @ApiModelProperty("列表数据") + private List rows; + + /** + * 消息状态码 + */ + @ApiModelProperty("消息状态码") + private int code; + + /** + * 消息内容 + */ + @ApiModelProperty("消息内容") + private String msg; + + + /** + * 表格数据对象 + */ + public GenericsTableDataInfo() { + this.total = 0; + this.rows = new ArrayList<>(); + this.code = HttpStatus.SUCCESS; + this.msg = "查询成功"; + } + + /** + * 将原始列表通过传入方法转换为对应类型,同时根据原始列表分页数据构建分页对象 + * + * @param rows 原始列表(带分页) + * @param toList Lambda表达式,对原始列表进行转换 + * @param 原始列表列席 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public GenericsTableDataInfo(List rows, Page.Function, List> toList) { + rows = Optional.ofNullable(rows).orElse(Collections.emptyList()); + this.total = new PageInfo(rows).getTotal(); + this.rows = toList.apply(rows); + this.code = HttpStatus.SUCCESS; + this.msg = "查询成功"; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public GenericsTableDataInfo(List rows) { + this.total = new PageInfo(rows).getTotal(); + this.rows = rows; + this.code = HttpStatus.SUCCESS; + this.msg = "查询成功"; + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public GenericsTableDataInfo(List list, Long total) { + this.rows = list; + this.total = total; + this.code = HttpStatus.SUCCESS; + this.msg = "查询成功"; + } +} \ No newline at end of file diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentController.java index 0dc9a52..c6a9636 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentController.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentController.java @@ -1,12 +1,15 @@ package com.ruoyi.platform.controller.experiment; +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.domain.Experiment; import com.ruoyi.platform.service.ExperimentService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.data.domain.PageRequest; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @@ -21,7 +24,7 @@ import java.io.IOException; @RestController @RequestMapping("experiment") @Api("实验管理") -public class ExperimentController { +public class ExperimentController extends BaseController { /** * 服务对象 */ @@ -50,8 +53,8 @@ public class ExperimentController { @GetMapping(("/configuration")) - public AjaxResult showExperimentConfig(@RequestBody Experiment experiment){ - return AjaxResult.success(this.experimentService.showExperimentConfig(experiment)); + public GenericsAjaxResult> showExperimentConfig(@RequestBody Experiment experiment){ + return genericsSuccess(this.experimentService.showExperimentConfig(experiment)); } /** @@ -62,8 +65,8 @@ public class ExperimentController { */ @GetMapping("{id}") @ApiOperation("通过id查询实验") - public AjaxResult queryById(@PathVariable("id") Integer id) throws IOException { - return AjaxResult.success(this.experimentService.queryById(id)); + public GenericsAjaxResult queryById(@PathVariable("id") Integer id) throws IOException { + return genericsSuccess(this.experimentService.queryById(id)); } /** diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentInsController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentInsController.java index c3ab713..0a43196 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentInsController.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentInsController.java @@ -6,6 +6,7 @@ import com.ruoyi.platform.service.ExperimentInsService; import com.ruoyi.platform.vo.LogRequestVo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; import org.springframework.data.domain.PageRequest; import org.springframework.web.bind.annotation.*; diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/tensorBoard/TensorBoardController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/tensorBoard/TensorBoardController.java new file mode 100644 index 0000000..23b99a4 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/tensorBoard/TensorBoardController.java @@ -0,0 +1,39 @@ +package com.ruoyi.platform.controller.tensorBoard; + +import com.ruoyi.common.core.web.domain.AjaxResult; +import com.ruoyi.platform.service.TensorBoardService; +import com.ruoyi.platform.vo.FrameLogPathVo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@RestController +@RequestMapping("tensorBoard") +@Api("TensorBoard管理") +public class TensorBoardController { + + //状态查询接口 + + @Resource + private TensorBoardService tensorBoardService; + + /** + * 启动tensorBoard接口 + * + * @param frameLogPathVo 存储路径 + * @return url + */ + @PostMapping("/run") + @ApiOperation("启动tensorBoard") + @ApiResponse + public AjaxResult runTensorBoard(@RequestBody FrameLogPathVo frameLogPathVo) throws Exception { + return AjaxResult.success(tensorBoardService.runTensorBoard(frameLogPathVo)); + } + +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/tensorBoard/tensorBoard.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/tensorBoard/tensorBoard.java deleted file mode 100644 index 5fd78fa..0000000 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/tensorBoard/tensorBoard.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ruoyi.platform.controller.tensorBoard; - -import io.swagger.annotations.Api; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("tensorBoard") -@Api("流水线管理") -public class tensorBoard { - - //状态查询接口 - - //启动tensorBoard接口 -} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/Experiment.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/Experiment.java index 222e8fa..2a775f0 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/Experiment.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/Experiment.java @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableLogic; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.ruoyi.common.core.web.domain.BaseEntity; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; @@ -20,6 +21,7 @@ import java.util.List; * @since 2023-11-07 15:08:22 */ @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +@ApiModel("实验对象") public class Experiment implements Serializable { private static final long serialVersionUID = 409135817108439880L; // @ApiModelProperty(name = "id") diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ExperimentIns.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ExperimentIns.java index 1573938..1ec0923 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ExperimentIns.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ExperimentIns.java @@ -2,6 +2,7 @@ package com.ruoyi.platform.domain; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/TensorBoardService.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/TensorBoardService.java new file mode 100644 index 0000000..f54ad7d --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/TensorBoardService.java @@ -0,0 +1,13 @@ +package com.ruoyi.platform.service; + +import com.ruoyi.platform.vo.FrameLogPathVo; + +public interface TensorBoardService { + /** + * 在集群中启动TensorBoard容器,并且返回地址,4小时后销毁 + * @param frameLogPathVo + * @return + * @throws Exception + */ + String runTensorBoard(FrameLogPathVo frameLogPathVo) throws Exception; +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java index 066b7e4..b95688d 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java @@ -44,8 +44,8 @@ public class JupyterServiceImpl implements JupyterService { @Override public String getJupyterServiceUrl() { LoginUser loginUser = SecurityUtils.getLoginUser(); - String podName = loginUser.getUsername() + "-editor-pod"; - String pvcName = loginUser.getUsername() + "-editor-pvc"; + String podName = loginUser.getUsername().toLowerCase() + "-editor-pod"; + String pvcName = loginUser.getUsername().toLowerCase() + "-editor-pvc"; V1PersistentVolumeClaim pvc = K8sClientUtil.createPvc(namespace, pvcName, storage,storageClassName); Integer podPort = K8sClientUtil.createPod(podName, namespace, port, mountPath, pvc, image); return masterIp + ":" + podPort; diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/TensorBoardServiceImpl.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/TensorBoardServiceImpl.java new file mode 100644 index 0000000..1fb9323 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/TensorBoardServiceImpl.java @@ -0,0 +1,35 @@ +package com.ruoyi.platform.service.impl; + +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.common.security.utils.SecurityUtils; +import com.ruoyi.platform.service.TensorBoardService; +import com.ruoyi.platform.utils.K8sClientUtil; +import com.ruoyi.platform.vo.FrameLogPathVo; +import com.ruoyi.system.api.model.LoginUser; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.util.Date; + +@Service +public class TensorBoardServiceImpl implements TensorBoardService { + @Value("${tensorBoard.image}") + private String image; + @Value("${tensorBoard.port}") + private Integer port; + @Value("${tensorBoard.mountPath}") + private String mountPath; + @Value("${tensorBoard.masterIp}") + private String masterIp; + @Override + public String runTensorBoard(FrameLogPathVo frameLogPathVo) throws Exception { + if (StringUtils.isEmpty(frameLogPathVo.getPath())||StringUtils.isEmpty(frameLogPathVo.getPvcName())){ + throw new Exception("存储路径或存储为空"); + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + String podName = loginUser.getUsername().toLowerCase()+"-"+frameLogPathVo.getPath().split("/")[2]+ "-tensorboard-pod"; + Integer podPort = K8sClientUtil.createPodWithSubPath(podName, StringUtils.isEmpty(frameLogPathVo.getNamespace())?"default":frameLogPathVo.getNamespace(), port, mountPath,frameLogPathVo.getPath(), frameLogPathVo.getPvcName(), image); + return masterIp + ":" + podPort; + } +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java index fab2b2c..19beb71 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java @@ -265,9 +265,9 @@ public class K8sClientUtil { try { pod = api.createNamespacedPod(namespace, pod, null, null, null); } catch (ApiException e) { - log.error("创建pvc异常:" + e.getResponseBody(), e); + log.error("创建pod异常:" + e.getResponseBody(), e); } catch (Exception e) { - log.error("创建pvc系统异常:", e); + log.error("创建pod系统异常:", e); } V1Service service = createService(namespace, podName + "-svc", port, selector); @@ -275,6 +275,75 @@ public class K8sClientUtil { } + /** + * 创建k8s 临时POD + * @param podName pod name + * @param namespace 命名空间 + * @param port port + * @param mountPath 映射路径 + * @param subPath pvc子路径 + * @param pvcName 存储名 + * @param image 镜像 + * @return 创建成功的pod,的nodePort端口 + */ + + public static Integer createPodWithSubPath(String podName, String namespace, Integer port ,String mountPath,String subPath,String pvcName, String image){ + + Map selector = new LinkedHashMap(); + selector.put("k8s-jupyter", podName); + + CoreV1Api api = new CoreV1Api(apiClient); + V1PodList v1PodList = null; + try { + v1PodList = api.listNamespacedPod(namespace, null, null, null, null, null, null, null, null, null, null); + } catch (ApiException e) { + log.error("获取 POD 异常:", e); + } + if (v1PodList!=null) { + for (V1Pod pod1 : v1PodList.getItems()) { + if (StringUtils.equals(pod1.getMetadata().getName(), podName)) { + // PVC 已存在 + + V1Service service = createService(namespace, podName + "-svc", port, selector); + if (service != null) { + return service.getSpec().getPorts().get(0).getNodePort(); + } + } + } + } + + V1Pod pod = new V1PodBuilder() + .withNewMetadata() + .withName(podName) + .withLabels(selector) + .endMetadata() + .withNewSpec() + .addNewContainer() + .withName(podName) + .withImage(image) + .withPorts(new V1ContainerPort().containerPort(port).protocol("TCP")) + .withVolumeMounts(new V1VolumeMount().name("workspace").mountPath(mountPath).subPath(subPath)) + .endContainer() + .addNewVolume() + .withName("workspace").withPersistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvcName)) + .endVolume() + .withTerminationGracePeriodSeconds(14400L) //默认不被操作4小时后删除 + .endSpec() + .build(); + + + try { + pod = api.createNamespacedPod(namespace, pod, null, null, null); + } catch (ApiException e) { + log.error("创建pod异常:" + e.getResponseBody(), e); + } catch (Exception e) { + log.error("创建pod系统异常:", e); + } + + V1Service service = createService(namespace, podName + "-svc", port, selector); + return service.getSpec().getPorts().get(0).getNodePort(); + } + /** * 根据获取namespace,deploymentName的Pod Name * diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/FrameLogPathVo.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/FrameLogPathVo.java new file mode 100644 index 0000000..99a0301 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/FrameLogPathVo.java @@ -0,0 +1,37 @@ +package com.ruoyi.platform.vo; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import java.io.Serializable; + +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +public class FrameLogPathVo implements Serializable { + String path; + String namespace; + String pvcName; + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getPvcName() { + return pvcName; + } + + public void setPvcName(String pvcName) { + this.pvcName = pvcName; + } +}