| @@ -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 | |||
| @@ -112,6 +112,10 @@ | |||
| <groupId>io.swagger</groupId> | |||
| <artifactId>swagger-annotations</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.projectlombok</groupId> | |||
| <artifactId>lombok</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -1,40 +1,41 @@ | |||
| package com.ruoyi.common.core.web.controller; | |||
| import java.beans.PropertyEditorSupport; | |||
| import java.util.Date; | |||
| import java.util.List; | |||
| 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.Constants; | |||
| 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.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 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 <T> GenericsTableDataInfo<T> getGenericsDataTable(List<T> list) { | |||
| GenericsTableDataInfo<T> rspData = new GenericsTableDataInfo<>(); | |||
| rspData.setCode(HttpStatus.SUCCESS); | |||
| rspData.setRows(list); | |||
| rspData.setMsg("查询成功"); | |||
| rspData.setTotal(new PageInfo(list).getTotal()); | |||
| return rspData; | |||
| } | |||
| /** | |||
| * 响应请求分页数据 | |||
| */ | |||
| protected <T> GenericsTableDataInfo<T> getGenericsDataTable(List<T> list, Integer count) { | |||
| GenericsTableDataInfo<T> rspData = new GenericsTableDataInfo<>(); | |||
| rspData.setCode(HttpStatus.SUCCESS); | |||
| rspData.setRows(list); | |||
| rspData.setMsg("查询成功"); | |||
| rspData.setTotal(count); | |||
| return rspData; | |||
| } | |||
| /** | |||
| * 响应请求分页数据 | |||
| */ | |||
| protected <T> GenericsTableDataInfo<T> getAllGenericsDataTableToPage(List<T> list) { | |||
| GenericsTableDataInfo<T> 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 <T> GenericsAjaxResult<T> 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 <T> GenericsAjaxResult<T> toGenericsAjax(int rows) { | |||
| return rows > 0 ? GenericsAjaxResult.success() : GenericsAjaxResult.error(); | |||
| } | |||
| /** | |||
| * 响应返回结果 | |||
| * | |||
| * @param data 数据 | |||
| * @return 操作结果 | |||
| */ | |||
| protected <T> GenericsAjaxResult<T> successToGenericsAjax(T data) { | |||
| return GenericsAjaxResult.success(data); | |||
| } | |||
| /** | |||
| * 响应返回结果 | |||
| * | |||
| * @param result 结果 | |||
| * @return 操作结果 | |||
| */ | |||
| protected AjaxResult toAjax(boolean result) | |||
| { | |||
| protected AjaxResult toAjax(boolean result) { | |||
| return result ? success() : error(); | |||
| } | |||
| } | |||
| @@ -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<T> 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 <T> GenericsAjaxResult<T> success() { | |||
| return GenericsAjaxResult.success("操作成功"); | |||
| } | |||
| /** | |||
| * 返回成功数据 | |||
| * | |||
| * @return 成功消息 | |||
| */ | |||
| public static <T> GenericsAjaxResult<T> success(T data) { | |||
| return GenericsAjaxResult.success("操作成功", data); | |||
| } | |||
| /** | |||
| * 返回成功消息 | |||
| * | |||
| * @param msg 返回内容 | |||
| * @return 成功消息 | |||
| */ | |||
| public static <T> GenericsAjaxResult<T> success(String msg) { | |||
| return GenericsAjaxResult.success(msg, null); | |||
| } | |||
| /** | |||
| * 返回成功消息 | |||
| * | |||
| * @param msg 返回内容 | |||
| * @param data 数据对象 | |||
| * @return 成功消息 | |||
| */ | |||
| public static <T> GenericsAjaxResult<T> success(String msg, T data) { | |||
| return new GenericsAjaxResult<>(HttpStatus.SUCCESS, msg, data); | |||
| } | |||
| /** | |||
| * 返回警告消息 | |||
| * | |||
| * @param msg 返回内容 | |||
| * @return 警告消息 | |||
| */ | |||
| public static <T> GenericsAjaxResult<T> warn(String msg) { | |||
| return GenericsAjaxResult.warn(msg, null); | |||
| } | |||
| /** | |||
| * 返回警告消息 | |||
| * | |||
| * @param msg 返回内容 | |||
| * @param data 数据对象 | |||
| * @return 警告消息 | |||
| */ | |||
| public static <T> GenericsAjaxResult<T> warn(String msg, T data) { | |||
| return new GenericsAjaxResult<>(HttpStatus.WARN, msg, data); | |||
| } | |||
| /** | |||
| * 返回错误消息 | |||
| * | |||
| * @return 错误消息 | |||
| */ | |||
| public static <T> GenericsAjaxResult<T> error() { | |||
| return GenericsAjaxResult.error("操作失败"); | |||
| } | |||
| /** | |||
| * 返回错误消息 | |||
| * | |||
| * @param msg 返回内容 | |||
| * @return 错误消息 | |||
| */ | |||
| public static <T> GenericsAjaxResult<T> error(String msg) { | |||
| return GenericsAjaxResult.error(msg, null); | |||
| } | |||
| /** | |||
| * 返回错误消息 | |||
| * | |||
| * @param msg 返回内容 | |||
| * @param data 数据对象 | |||
| * @return 错误消息 | |||
| */ | |||
| public static <T> GenericsAjaxResult<T> error(String msg, T data) { | |||
| return new GenericsAjaxResult<>(HttpStatus.ERROR, msg, data); | |||
| } | |||
| /** | |||
| * 返回错误消息 | |||
| * | |||
| * @param code 状态码 | |||
| * @param msg 返回内容 | |||
| * @return 错误消息 | |||
| */ | |||
| public static <T> GenericsAjaxResult<T> error(int code, String msg) { | |||
| return new GenericsAjaxResult<>(code, msg, null); | |||
| } | |||
| } | |||
| @@ -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<T> implements Serializable { | |||
| private static final long serialVersionUID = 1L; | |||
| /** | |||
| * 总记录数 | |||
| */ | |||
| @ApiModelProperty("总记录数") | |||
| private long total; | |||
| /** | |||
| * 列表数据 | |||
| */ | |||
| @ApiModelProperty("列表数据") | |||
| private List<T> 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 <E> 原始列表列席 | |||
| */ | |||
| @SuppressWarnings({"rawtypes", "unchecked"}) | |||
| public <E> GenericsTableDataInfo(List<E> rows, Page.Function<List<E>, List<T>> 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<T> rows) { | |||
| this.total = new PageInfo(rows).getTotal(); | |||
| this.rows = rows; | |||
| this.code = HttpStatus.SUCCESS; | |||
| this.msg = "查询成功"; | |||
| } | |||
| /** | |||
| * 分页 | |||
| * | |||
| * @param list 列表数据 | |||
| * @param total 总记录数 | |||
| */ | |||
| public GenericsTableDataInfo(List<T> list, Long total) { | |||
| this.rows = list; | |||
| this.total = total; | |||
| this.code = HttpStatus.SUCCESS; | |||
| this.msg = "查询成功"; | |||
| } | |||
| } | |||
| @@ -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<ResponseEntity<Experiment>> 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<Experiment> queryById(@PathVariable("id") Integer id) throws IOException { | |||
| return genericsSuccess(this.experimentService.queryById(id)); | |||
| } | |||
| /** | |||
| @@ -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.*; | |||
| @@ -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)); | |||
| } | |||
| } | |||
| @@ -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接口 | |||
| } | |||
| @@ -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") | |||
| @@ -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; | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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<String, String> selector = new LinkedHashMap<String, String>(); | |||
| 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 | |||
| * | |||
| @@ -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; | |||
| } | |||
| } | |||