| @@ -15,7 +15,7 @@ export default { | |||||
| // localhost:8000/api/** -> https://preview.pro.ant.design/api/** | // localhost:8000/api/** -> https://preview.pro.ant.design/api/** | ||||
| '/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.98:8082', | ||||
| target: 'http://172.20.32.150:8082', | target: 'http://172.20.32.150:8082', | ||||
| // 配置了这个可以从 http 代理到 https | // 配置了这个可以从 http 代理到 https | ||||
| @@ -112,6 +112,10 @@ | |||||
| <groupId>io.swagger</groupId> | <groupId>io.swagger</groupId> | ||||
| <artifactId>swagger-annotations</artifactId> | <artifactId>swagger-annotations</artifactId> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>org.projectlombok</groupId> | |||||
| <artifactId>lombok</artifactId> | |||||
| </dependency> | |||||
| </dependencies> | </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; | 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.github.pagehelper.PageInfo; | ||||
| import com.ruoyi.common.core.constant.Constants; | |||||
| import com.ruoyi.common.core.constant.HttpStatus; | 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.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.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层通用数据处理 | * web层通用数据处理 | ||||
| * | |||||
| * | |||||
| * @author ruoyi | * @author ruoyi | ||||
| */ | */ | ||||
| public class BaseController | |||||
| { | |||||
| public class BaseController { | |||||
| protected final Logger logger = LoggerFactory.getLogger(this.getClass()); | protected final Logger logger = LoggerFactory.getLogger(this.getClass()); | ||||
| /** | /** | ||||
| * 将前台传递过来的日期格式的字符串,自动转化为Date类型 | * 将前台传递过来的日期格式的字符串,自动转化为Date类型 | ||||
| */ | */ | ||||
| @InitBinder | @InitBinder | ||||
| public void initBinder(WebDataBinder binder) | |||||
| { | |||||
| public void initBinder(WebDataBinder binder) { | |||||
| // Date 类型转换 | // Date 类型转换 | ||||
| binder.registerCustomEditor(Date.class, new PropertyEditorSupport() | |||||
| { | |||||
| binder.registerCustomEditor(Date.class, new PropertyEditorSupport() { | |||||
| @Override | @Override | ||||
| public void setAsText(String text) | |||||
| { | |||||
| public void setAsText(String text) { | |||||
| setValue(DateUtils.parseDate(text)); | setValue(DateUtils.parseDate(text)); | ||||
| } | } | ||||
| }); | }); | ||||
| @@ -43,25 +44,22 @@ public class BaseController | |||||
| /** | /** | ||||
| * 设置请求分页数据 | * 设置请求分页数据 | ||||
| */ | */ | ||||
| protected void startPage() | |||||
| { | |||||
| protected void startPage() { | |||||
| PageUtils.startPage(); | PageUtils.startPage(); | ||||
| } | } | ||||
| /** | /** | ||||
| * 清理分页的线程变量 | * 清理分页的线程变量 | ||||
| */ | */ | ||||
| protected void clearPage() | |||||
| { | |||||
| protected void clearPage() { | |||||
| PageUtils.clearPage(); | PageUtils.clearPage(); | ||||
| } | } | ||||
| /** | /** | ||||
| * 响应请求分页数据 | * 响应请求分页数据 | ||||
| */ | */ | ||||
| @SuppressWarnings({ "rawtypes", "unchecked" }) | |||||
| protected TableDataInfo getDataTable(List<?> list) | |||||
| { | |||||
| @SuppressWarnings({"rawtypes", "unchecked"}) | |||||
| protected TableDataInfo getDataTable(List<?> list) { | |||||
| TableDataInfo rspData = new TableDataInfo(); | TableDataInfo rspData = new TableDataInfo(); | ||||
| rspData.setCode(HttpStatus.SUCCESS); | rspData.setCode(HttpStatus.SUCCESS); | ||||
| rspData.setRows(list); | rspData.setRows(list); | ||||
| @@ -70,73 +68,188 @@ public class BaseController | |||||
| return rspData; | 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(); | return AjaxResult.success(); | ||||
| } | } | ||||
| /** | /** | ||||
| * 返回成功消息 | * 返回成功消息 | ||||
| */ | */ | ||||
| public AjaxResult success(String message) | |||||
| { | |||||
| public AjaxResult success(String message) { | |||||
| return AjaxResult.success(message); | return AjaxResult.success(message); | ||||
| } | } | ||||
| /** | /** | ||||
| * 返回成功消息 | * 返回成功消息 | ||||
| */ | */ | ||||
| public AjaxResult success(Object data) | |||||
| { | |||||
| public AjaxResult success(Object data) { | |||||
| return AjaxResult.success(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(); | return AjaxResult.error(); | ||||
| } | } | ||||
| /** | /** | ||||
| * 返回失败消息 | * 返回失败消息 | ||||
| */ | */ | ||||
| public AjaxResult error(String message) | |||||
| { | |||||
| public AjaxResult error(String message) { | |||||
| return AjaxResult.error(message); | return AjaxResult.error(message); | ||||
| } | } | ||||
| /** | /** | ||||
| * 返回警告消息 | * 返回警告消息 | ||||
| */ | */ | ||||
| public AjaxResult warn(String message) | |||||
| { | |||||
| public AjaxResult warn(String message) { | |||||
| return AjaxResult.warn(message); | return AjaxResult.warn(message); | ||||
| } | } | ||||
| /** | /** | ||||
| * 响应返回结果 | * 响应返回结果 | ||||
| * | |||||
| * | |||||
| * @param rows 影响行数 | * @param rows 影响行数 | ||||
| * @return 操作结果 | * @return 操作结果 | ||||
| */ | */ | ||||
| protected AjaxResult toAjax(int rows) | |||||
| { | |||||
| protected AjaxResult toAjax(int rows) { | |||||
| return rows > 0 ? AjaxResult.success() : AjaxResult.error(); | 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 结果 | * @param result 结果 | ||||
| * @return 操作结果 | * @return 操作结果 | ||||
| */ | */ | ||||
| protected AjaxResult toAjax(boolean result) | |||||
| { | |||||
| protected AjaxResult toAjax(boolean result) { | |||||
| return result ? success() : error(); | 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; | 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.AjaxResult; | ||||
| import com.ruoyi.common.core.web.domain.GenericsAjaxResult; | |||||
| import com.ruoyi.platform.domain.Experiment; | import com.ruoyi.platform.domain.Experiment; | ||||
| import com.ruoyi.platform.service.ExperimentService; | import com.ruoyi.platform.service.ExperimentService; | ||||
| import io.swagger.annotations.Api; | import io.swagger.annotations.Api; | ||||
| import io.swagger.annotations.ApiOperation; | import io.swagger.annotations.ApiOperation; | ||||
| import org.springframework.data.domain.PageRequest; | import org.springframework.data.domain.PageRequest; | ||||
| import org.springframework.http.ResponseEntity; | |||||
| import org.springframework.web.bind.annotation.*; | import org.springframework.web.bind.annotation.*; | ||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||
| @@ -21,7 +24,7 @@ import java.io.IOException; | |||||
| @RestController | @RestController | ||||
| @RequestMapping("experiment") | @RequestMapping("experiment") | ||||
| @Api("实验管理") | @Api("实验管理") | ||||
| public class ExperimentController { | |||||
| public class ExperimentController extends BaseController { | |||||
| /** | /** | ||||
| * 服务对象 | * 服务对象 | ||||
| */ | */ | ||||
| @@ -50,8 +53,8 @@ public class ExperimentController { | |||||
| @GetMapping(("/configuration")) | @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}") | @GetMapping("{id}") | ||||
| @ApiOperation("通过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 com.ruoyi.platform.vo.LogRequestVo; | ||||
| import io.swagger.annotations.Api; | import io.swagger.annotations.Api; | ||||
| import io.swagger.annotations.ApiOperation; | import io.swagger.annotations.ApiOperation; | ||||
| import io.swagger.annotations.ApiResponse; | |||||
| import org.springframework.data.domain.PageRequest; | import org.springframework.data.domain.PageRequest; | ||||
| import org.springframework.web.bind.annotation.*; | 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.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 io.swagger.annotations.ApiModel; | |||||
| import io.swagger.annotations.ApiModelProperty; | import io.swagger.annotations.ApiModelProperty; | ||||
| import java.io.Serializable; | import java.io.Serializable; | ||||
| @@ -20,6 +21,7 @@ import java.util.List; | |||||
| * @since 2023-11-07 15:08:22 | * @since 2023-11-07 15:08:22 | ||||
| */ | */ | ||||
| @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | ||||
| @ApiModel("实验对象") | |||||
| public class Experiment implements Serializable { | public class Experiment implements Serializable { | ||||
| private static final long serialVersionUID = 409135817108439880L; | private static final long serialVersionUID = 409135817108439880L; | ||||
| // @ApiModelProperty(name = "id") | // @ApiModelProperty(name = "id") | ||||
| @@ -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 io.swagger.annotations.ApiModel; | |||||
| import io.swagger.annotations.ApiModelProperty; | import io.swagger.annotations.ApiModelProperty; | ||||
| import java.io.Serializable; | 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 | @Override | ||||
| public String getJupyterServiceUrl() { | public String getJupyterServiceUrl() { | ||||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | 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); | V1PersistentVolumeClaim pvc = K8sClientUtil.createPvc(namespace, pvcName, storage,storageClassName); | ||||
| Integer podPort = K8sClientUtil.createPod(podName, namespace, port, mountPath, pvc, image); | Integer podPort = K8sClientUtil.createPod(podName, namespace, port, mountPath, pvc, image); | ||||
| return masterIp + ":" + podPort; | 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 { | try { | ||||
| pod = api.createNamespacedPod(namespace, pod, null, null, null); | pod = api.createNamespacedPod(namespace, pod, null, null, null); | ||||
| } catch (ApiException e) { | } catch (ApiException e) { | ||||
| log.error("创建pvc异常:" + e.getResponseBody(), e); | |||||
| log.error("创建pod异常:" + e.getResponseBody(), e); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| log.error("创建pvc系统异常:", e); | |||||
| log.error("创建pod系统异常:", e); | |||||
| } | } | ||||
| V1Service service = createService(namespace, podName + "-svc", port, selector); | 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 | * 根据获取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; | |||||
| } | |||||
| } | |||||