diff --git a/react-ui/src/assets/img/clock.png b/react-ui/src/assets/img/clock.png new file mode 100644 index 00000000..a2068364 Binary files /dev/null and b/react-ui/src/assets/img/clock.png differ diff --git a/react-ui/src/assets/img/creatBy.png b/react-ui/src/assets/img/creatBy.png new file mode 100644 index 00000000..de3341cb Binary files /dev/null and b/react-ui/src/assets/img/creatBy.png differ diff --git a/react-ui/src/global.less b/react-ui/src/global.less index d5eb8e2c..43894072 100644 --- a/react-ui/src/global.less +++ b/react-ui/src/global.less @@ -1,12 +1,9 @@ -@import '@/styles/theme.less'; - html, body, #root { height: 100%; margin: 0; padding: 0; - overflow-y: hidden; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; @@ -35,24 +32,28 @@ body { -moz-osx-font-smoothing: grayscale; } .ant-pro-layout .ant-pro-layout-content { - padding: 0 10px 10px; - overflow-y: hidden; - background-color: @background-color; + padding: 10px; } .ant-pro-layout .ant-pro-layout-bg-list { - background: @background-color; + background: #f9fafb; } .ant-menu-light .ant-menu-item-selected { background: rgba(197, 232, 255, 0.8) !important; } +.ant-menu-light .ant-menu-item-selected .ant-pro-base-menu-inline-item-text { + // color: #1664ff; +} .ant-pro-layout .ant-pro-sider .ant-layout-sider-children { background: #f2f5f7; } .ant-pro-base-menu-inline-item-title .ant-pro-base-menu-inline-item-text { - color: #1d1d20; + // color: #1d1d20; font-size: 16px; } +// .ant-menu-light .ant-menu-item-selected{ +// color:#1664ff; +// } .ant-pro-layout .ant-pro-sider-menu { padding-top: 40px; } @@ -74,7 +75,9 @@ body { background: #f2f5f7; border-radius: 0px 20px 20px 0px; } - +.ant-pro-layout .ant-pro-layout-content { + background-color: transparent; +} .ant-drawer .ant-drawer-body { padding: 0; } @@ -84,6 +87,9 @@ body { .ant-drawer .ant-drawer-body .ant-form-item { margin-bottom: 20px; } +.ant-menu .ant-menu-submenu-title .anticon { + font-size: 16px; +} .ant-table-wrapper .ant-table-pagination.ant-pagination { margin: 0; padding: 21px 16px; @@ -99,8 +105,7 @@ body { height: 94vh; } .ant-pro-layout .ant-pro-layout-container { - height: 100vh; - overflow-y: hidden; + height: 98vh; } .ant-modal-confirm .ant-modal-confirm-paragraph { margin: 54px 0 auto; @@ -117,7 +122,7 @@ body { width: 110px; height: 40px; margin-right: 10px; - color: #1d1d20; + // color: #1d1d20; font-size: 18px; background: rgba(22, 100, 255, 0.06); border-color: transparent; @@ -132,6 +137,7 @@ body { height: 40px; font-size: 18px; border-radius: 10px; + border-radius: 10px; } .ant-modal .ant-input-affix-wrapper { height: 46px; @@ -163,7 +169,6 @@ body { background-repeat: no-repeat; background-position: top center; background-size: 100%; - border-radius: 21px; border-radius: 0; } .ant-modal .ant-modal-content { @@ -190,6 +195,17 @@ body { border-radius: 6px; } +.ant-tabs { + .ant-tabs-nav::before, + div > .ant-tabs-nav::before { + border: none; + } + + .ant-tabs-nav { + margin-bottom: 0; + } +} + // ::-webkit-scrollbar-button { // background: #97a1bd; // } diff --git a/react-ui/src/icons/dataset-select-button.svg b/react-ui/src/icons/dataset-select-button.svg deleted file mode 100644 index 2a376d71..00000000 --- a/react-ui/src/icons/dataset-select-button.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/react-ui/src/icons/mirror-select-button.svg b/react-ui/src/icons/mirror-select-button.svg deleted file mode 100644 index 59e5a215..00000000 --- a/react-ui/src/icons/mirror-select-button.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/react-ui/src/icons/model-select-button.svg b/react-ui/src/icons/model-select-button.svg deleted file mode 100644 index 525e061b..00000000 --- a/react-ui/src/icons/model-select-button.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/react-ui/src/pages/Dataset/index.less b/react-ui/src/pages/Dataset/index.less index 8be59a9d..d22c8dbe 100644 --- a/react-ui/src/pages/Dataset/index.less +++ b/react-ui/src/pages/Dataset/index.less @@ -7,6 +7,7 @@ padding-right: 30px; background-image: url(/assets/images/pipeline-back.png); background-size: 100% 100%; + font-family: 'Alibaba'; } .datasetIntroTopBox { display: flex; @@ -38,6 +39,7 @@ background: #ffffff; border-radius: 10px; box-shadow: 0px 2px 12px rgba(180, 182, 191, 0.09); + font-family: alibaba; .dataButtonList { display: flex; align-items: center; @@ -68,6 +70,7 @@ .datasetBox { font-family: 'Alibaba'; background: #f9fafb; + :global { .ant-tabs-top > .ant-tabs-nav { margin: 0; @@ -117,6 +120,7 @@ margin-right: 10px; padding-top: 15px; background: #ffffff; + font-family: 'Alibaba'; box-shadow: 0px 3px 6px rgba(146, 146, 146, 0.09); .custTab { display: flex; @@ -136,6 +140,7 @@ padding: 15px 20px; overflow-x: hidden; overflow-y: auto; + font-family: 'Alibaba'; .itemTitle { margin-bottom: 15px; color: #1d1d20; @@ -205,6 +210,7 @@ display: flex; flex: 1; flex-direction: column; + font-family: 'Alibaba'; height: 100%; padding: 22px 30px 26px 30px; background: #ffffff; @@ -223,38 +229,69 @@ flex: 1; flex-wrap: wrap; align-content: flex-start; - width: 100%; + font-family: 'Alibaba'; + width: 103%; .dataItem { position: relative; - width: 32%; - height: 66px; - margin: 0 15px 18px 0; - background: rgba(128, 128, 128, 0.05); - border-radius: 8px; - box-shadow: 0px 0px 12px rgba(75, 84, 137, 0.05); + width: 23%; + height:164px; + background:#ffffff; + border:1px solid; + border-color:#eaeaea; + border-radius:4px; + margin: 0 20px 25px 0; cursor: pointer; .itemText { position: absolute; - top: 10px; + top: 20px; left: 20px; - color: #1d1d20; - font-size: 15px; + background: linear-gradient(to right ,rgba(22, 100, 255,0.6) 0,rgba(22, 100, 255,0) 100%); + height: 6px; + line-height: 0px; + color:#1d1d20; + font-size:16px; + + } + .itemDescripition{ + position: absolute; + top: 57px; + left: 20px; + padding-right: 28px; + color:#575757; + font-size:14px; + word-break: break-all; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; } .itemTime { position: absolute; - bottom: 10px; + display: flex; + align-items: center; + bottom: 22px; left: 20px; color: #808080; - font-size: 14px; + font-size: 13px; } .itemIcon { position: absolute; + display: flex; + align-items: center; right: 20px; - bottom: 10px; + bottom: 22px; color: #808080; - font-size: 14px; + font-size: 13px; } } + .dataItem:hover{ + border-color: #1664FF; + box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2) + + } + .dataItem:hover .itemText{ + color: #1664FF; + } } } } diff --git a/react-ui/src/pages/Dataset/personalData.jsx b/react-ui/src/pages/Dataset/personalData.jsx index cbebbd0a..3d183016 100644 --- a/react-ui/src/pages/Dataset/personalData.jsx +++ b/react-ui/src/pages/Dataset/personalData.jsx @@ -1,4 +1,6 @@ import { getAccessToken } from '@/access'; +import clock from '@/assets/img/clock.png'; +import creatByImg from '@/assets/img/creatBy.png'; import { addDatesetAndVesion, getAssetIcon, getDatasetList } from '@/services/dataset/index.js'; import { getDictSelectOption } from '@/services/system/dict'; import { PlusCircleOutlined, UploadOutlined } from '@ant-design/icons'; @@ -40,7 +42,7 @@ const PublicData = (React.FC = () => { }; const [queryFlow, setQueryFlow] = useState({ page: 0, - size: 10, + size: 20, name: null, available_range: 0, }); @@ -149,6 +151,11 @@ const PublicData = (React.FC = () => { const onFinishFailed = (errorInfo) => { console.log('Failed:', errorInfo); }; + const onPageChange = (pageNum, pageSize) => { + console.log(pageNum, pageSize); + setQueryFlow({ ...queryFlow, page: pageNum - 1, size: pageSize }); + getDatasetlist({ ...queryFlow, page: pageNum - 1, size: pageSize }); + }; useEffect(() => { getDictSelectOption('available_cluster').then((data) => { setClusterOptions(data); @@ -268,17 +275,19 @@ const PublicData = (React.FC = () => { ? datasetList.map((item) => { return (
routeToIntro(e, item)}> -
{item.name}
+ {item.name} +
{item.description}
- 最近更新: {moment(item.update_time).format('YYYY-MM-DD')} -
-
- 1582 + {item.create_by} +
+
+ + 最近更新: {moment(item.update_time).format('YYYY-MM-DD')}
); @@ -286,7 +295,14 @@ const PublicData = (React.FC = () => { : ''} {/* Demo */} - + { const onFinishFailed = (errorInfo) => { console.log('Failed:', errorInfo); }; + const onPageChange = (pageNum, pageSize) => { + console.log(pageNum, pageSize); + setQueryFlow({ ...queryFlow, page: pageNum - 1, size: pageSize }); + getDatasetlist({ ...queryFlow, page: pageNum - 1, size: pageSize }); + }; useEffect(() => { getAssetIconList(iconParams); getDatasetlist(queryFlow); @@ -217,17 +224,19 @@ const PublicData = (React.FC = () => { ? datasetList.map((item) => { return (
routeToIntro(e, item)}> -
{item.name}
+ {item.name} +
{item.description}
- 最近更新: {moment(item.update_time).format('YYYY-MM-DD')} -
-
- 1582 + {item.create_by} +
+
+ + 最近更新: {moment(item.update_time).format('YYYY-MM-DD')}
); @@ -235,7 +244,14 @@ const PublicData = (React.FC = () => { : ''} {/* Demo */} - + diff --git a/react-ui/src/pages/Model/index.less b/react-ui/src/pages/Model/index.less index 81a18b11..5bf1cc1b 100644 --- a/react-ui/src/pages/Model/index.less +++ b/react-ui/src/pages/Model/index.less @@ -222,35 +222,65 @@ width: 100%; .dataItem { position: relative; - width: 32%; - height: 66px; - margin: 0 15px 18px 0; - background: rgba(128, 128, 128, 0.05); - border-radius: 8px; - box-shadow: 0px 0px 12px rgba(75, 84, 137, 0.05); + width: 23%; + height:164px; + background:#ffffff; + border:1px solid; + border-color:#eaeaea; + border-radius:4px; + margin: 0 20px 25px 0; cursor: pointer; .itemText { position: absolute; - top: 10px; + top: 20px; left: 20px; - color: #1d1d20; - font-size: 15px; + background: linear-gradient(to right ,rgba(22, 100, 255,0.6) 0,rgba(22, 100, 255,0) 100%); + height: 6px; + line-height: 0px; + color:#1d1d20; + font-size:16px; + + } + .itemDescripition{ + position: absolute; + top: 57px; + left: 20px; + padding-right: 28px; + color:#575757; + font-size:14px; + word-break: break-all; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; } .itemTime { position: absolute; - bottom: 10px; + display: flex; + align-items: center; + bottom: 22px; left: 20px; color: #808080; - font-size: 14px; + font-size: 13px; } .itemIcon { position: absolute; + display: flex; + align-items: center; right: 20px; - bottom: 10px; + bottom: 22px; color: #808080; - font-size: 14px; + font-size: 13px; } } + .dataItem:hover{ + border-color: #1664FF; + box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.2) + + } + .dataItem:hover .itemText{ + color: #1664FF; + } } } } diff --git a/react-ui/src/pages/Model/personalData.jsx b/react-ui/src/pages/Model/personalData.jsx index a52420c8..152b3450 100644 --- a/react-ui/src/pages/Model/personalData.jsx +++ b/react-ui/src/pages/Model/personalData.jsx @@ -1,4 +1,6 @@ import { getAccessToken } from '@/access'; +import clock from '@/assets/img/clock.png'; +import creatByImg from '@/assets/img/creatBy.png'; import { addModel, getAssetIcon, getModelList } from '@/services/dataset/index.js'; import { PlusCircleOutlined, UploadOutlined } from '@ant-design/icons'; import { Button, Form, Input, Modal, Pagination, Radio, Select, Upload } from 'antd'; @@ -148,6 +150,11 @@ const PublicData = () => { const onFinishFailed = (errorInfo) => { console.log('Failed:', errorInfo); }; + const onPageChange = (pageNum, pageSize) => { + console.log(pageNum, pageSize); + setQueryFlow({ ...queryFlow, page: pageNum - 1, size: pageSize }); + getModelLists({ ...queryFlow, page: pageNum - 1, size: pageSize }); + }; useEffect(() => { getAssetIconList(iconParams); getModelLists(queryFlow); @@ -271,17 +278,19 @@ const PublicData = () => { ? datasetList.map((item) => { return (
routeToIntro(e, item)}> -
{item.name}
+ {item.name} +
{item.description}
- 最近更新: {moment(item.update_time).format('YYYY-MM-DD')} -
-
- 1582 + {item.create_by} +
+
+ + 最近更新: {moment(item.update_time).format('YYYY-MM-DD')}
); @@ -289,7 +298,14 @@ const PublicData = () => { : ''} {/* Demo */} - + { const onFinishFailed = (errorInfo) => { console.log('Failed:', errorInfo); }; + const onPageChange = (pageNum, pageSize) => { + console.log(pageNum, pageSize); + setQueryFlow({ ...queryFlow, page: pageNum - 1, size: pageSize }); + getModelLists({ ...queryFlow, page: pageNum - 1, size: pageSize }); + }; useEffect(() => { getAssetIconList(iconParams); getModelLists(queryFlow); @@ -218,17 +225,19 @@ const PublicData = () => { ? datasetList.map((item) => { return (
routeToIntro(e, item)}> -
{item.name}
+ {item.name} +
{item.description}
- 最近更新: {moment(item.update_time).format('YYYY-MM-DD')} -
-
- 1582 + {item.create_by} +
+
+ + 最近更新: {moment(item.update_time).format('YYYY-MM-DD')}
); @@ -236,7 +245,14 @@ const PublicData = () => { : ''} {/* Demo */} - + { openParamsDrawer(); return; } - const data = graph.save(); - console.log(data); - const params = { - ...locationParams, - dag: JSON.stringify(data), - global_param: JSON.stringify(res.global_param), - }; - saveWorkflow(params).then((ret) => { - message.success('保存成功'); - closeParamsDrawer(); - setTimeout(() => { - if (val) { - navgite({ pathname: `/pipeline` }); - } - }, 500); - }); + const [propsRes, propsError] = await to(propsRef.current.getFieldsValue()); + console.log(await to(propsRef.current.getFieldsValue())); + if (propsError) { + message.error('基本信息必填项需配置'); + // handlerClick(); + return; + } + propsRef.current.propClose(); + setTimeout(() => { + const data = graph.save(); + console.log(data); + const params = { + ...locationParams, + dag: JSON.stringify(data), + global_param: JSON.stringify(res.global_param), + }; + saveWorkflow(params).then((ret) => { + message.success('保存成功'); + closeParamsDrawer(); + setTimeout(() => { + if (val) { + navgite({ pathname: `/pipeline` }); + } + }, 500); + }); + }, 500); }; const handlerClick = (e) => { e.stopPropagation(); diff --git a/react-ui/src/pages/Pipeline/editPipeline/props.jsx b/react-ui/src/pages/Pipeline/editPipeline/props.jsx index 8d2f36f6..1756855d 100644 --- a/react-ui/src/pages/Pipeline/editPipeline/props.jsx +++ b/react-ui/src/pages/Pipeline/editPipeline/props.jsx @@ -10,6 +10,7 @@ import Styles from './editPipeline.less'; const { TextArea } = Input; const Props = forwardRef(({ onParentChange }, ref) => { const [form] = Form.useForm(); + const [stagingItem, setStagingItem] = useState({}); const [open, setOpen] = useState(false); const [selectedModel, setSelectedModel] = useState(undefined); @@ -78,6 +79,15 @@ const Props = forwardRef(({ onParentChange }, ref) => { console.log('Failed:', errorInfo); }; useImperativeHandle(ref, () => ({ + getFieldsValue: async () => { + const [propsRes, propsError] = await to(form.validateFields()); + if (propsRes && !propsError) { + const values = form.getFieldsValue(); + return values; + } else { + return Promise.reject(propsError); + } + }, showDrawer(e) { if (e.item && e.item.getModel()) { // console.log(e.item.getModel().in_parameters); @@ -104,6 +114,15 @@ const Props = forwardRef(({ onParentChange }, ref) => { setOpen(true); } }, + propClose: async () => { + setOpen(false); + const [openRes, propsError] = await to(setOpen(false)); + console.log(setOpen(false)); + }, + // propClose() { + + // setOpen(false); + // }, })); // 选择数据集、模型 diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/DatasetController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/DatasetController.java index a5fd5c53..0f8c6700 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/DatasetController.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/DatasetController.java @@ -1,5 +1,6 @@ package com.ruoyi.platform.controller.dataset; +import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.security.utils.SecurityUtils; import com.ruoyi.platform.domain.Dataset; @@ -54,11 +55,15 @@ public class DatasetController { public AjaxResult queryByPage(Dataset dataset, @RequestParam("page") int page, @RequestParam("size") int size, @RequestParam(value = "available_range") int availableRange , - @RequestParam(value = "data_type", required = false) String dataType) { + @RequestParam(value = "data_type", required = false) String dataType, + @RequestParam(value = "data_tag", required = false) String dataTag) { if (dataType != null) { // 仅当dataType有值时设置 dataset.setDataType(dataType); } + if (dataTag != null) { + dataset.setDataTag(dataTag); + } dataset.setAvailableRange(availableRange); PageRequest pageRequest = PageRequest.of(page, size); return AjaxResult.success(this.datasetService.queryByPage(dataset, pageRequest)); @@ -79,6 +84,9 @@ public class DatasetController { if (dataType != null) { // 仅当dataType有值时设置 dataset.setDataType(dataType); } + if (dataTag != null) { + dataset.setDataTag(dataTag); + } PageRequest pageRequest = PageRequest.of(page, size); return AjaxResult.success(this.datasetService.queryByPage(dataset, pageRequest)); } diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageController.java index ab839406..64b569d8 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageController.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageController.java @@ -122,7 +122,7 @@ public class ImageController extends BaseController { @PostMapping("/net") @ApiOperation("从网络上传构建镜像") - public GenericsAjaxResult createImageFromNet(@RequestParam("name") String imageName, + public GenericsAjaxResult> createImageFromNet(@RequestParam("name") String imageName, @RequestParam("tag") String imageTag, @RequestParam("path") String path) throws Exception { return genericsSuccess(this.imageService.createImageFromNet(imageName,imageTag,path)); @@ -130,7 +130,7 @@ public class ImageController extends BaseController { @PostMapping("/local") @ApiOperation("从本地上传构建镜像") - public GenericsAjaxResult createImageFromLocal(@RequestParam("name") String imageName, + public GenericsAjaxResult> createImageFromLocal(@RequestParam("name") String imageName, @RequestParam("tag") String imageTag, @RequestParam("path") String path) throws Exception { return genericsSuccess(this.imageService.createImageFromLocal(imageName,imageTag,path)); diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageVersionController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageVersionController.java index c6937b1a..a95460e7 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageVersionController.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageVersionController.java @@ -8,8 +8,10 @@ import io.swagger.annotations.ApiOperation; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import java.util.Map; /** * (ImageVersion)表控制层 @@ -36,11 +38,17 @@ public class ImageVersionController extends BaseController { */ @GetMapping @ApiOperation("分页查询") - public GenericsAjaxResult> queryByPage(ImageVersion imageVersion, int page, int size) { + public GenericsAjaxResult> queryByPage(ImageVersion imageVersion, @RequestParam("page") int page, + @RequestParam("size") int size, + @RequestParam(value = "image_id") int imageId) { + imageVersion.setImageId(imageId); PageRequest pageRequest = PageRequest.of(page,size); return genericsSuccess(this.imageVersionService.queryByPage(imageVersion, pageRequest)); + } + + /** * 通过主键查询单条数据 * diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ImageService.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ImageService.java index 4fb293b7..4e6290dd 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ImageService.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ImageService.java @@ -1,13 +1,11 @@ package com.ruoyi.platform.service; import com.ruoyi.platform.domain.Image; -import com.ruoyi.platform.domain.Workflow; import com.ruoyi.platform.vo.ImageVo; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.web.multipart.MultipartFile; -import java.util.Collection; import java.util.Map; /** @@ -69,13 +67,14 @@ public interface ImageService { /** * 本地上传构建镜像 + * * @param imageName * @param imageTag * @param path * @return */ - String createImageFromLocal(String imageName, String imageTag, String path) throws Exception; - String createImageFromNet(String imageName, String imageTag, String NetPath) throws Exception; + Map createImageFromLocal(String imageName, String imageTag, String path) throws Exception; + Map createImageFromNet(String imageName, String imageTag, String NetPath) throws Exception; Map uploadImageFiles(MultipartFile file) throws Exception; } diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java index ef447ea9..831c242f 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java @@ -8,6 +8,7 @@ import com.ruoyi.platform.mapper.ImageVersionDao; import com.ruoyi.platform.service.ImageService; import com.ruoyi.platform.service.ImageVersionService; import com.ruoyi.platform.service.MinioService; +import com.ruoyi.platform.utils.FileUtil; import com.ruoyi.platform.utils.K8sClientUtil; import com.ruoyi.platform.vo.ImageVo; import com.ruoyi.system.api.model.LoginUser; @@ -23,7 +24,9 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.util.Date; +import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CompletableFuture; /** * (Image)表服务实现类 @@ -171,7 +174,6 @@ public class ImageServiceImpl implements ImageService { image.setName(imageVo.getName()); image.setDescription(imageVo.getDescription()); image.setImageType(imageVo.getImageType()); - Image imageInsert = this.insert(image); if (imageInsert == null){ throw new Exception("新增镜像失败"); @@ -181,17 +183,50 @@ public class ImageServiceImpl implements ImageService { imageVersion.setVersion(imageVo.getVersion()); imageVersion.setUrl(imageVo.getUrl()); imageVersion.setTagName(imageVo.getTagName()); - //imageVersion.setFileSize(datasetVo.getFileSize()); - + imageVersion.setFileSize(imageVo.getFileSize()); + imageVersion.setStatus("building"); ImageVersion imageVersionInsert = this.imageVersionService.insert(imageVersion); if (imageVersionInsert == null) { throw new Exception("新增镜像失败"); } - return "新增镜像成功"; + // 使用CompletableFuture异步执行不同的镜像构建逻辑 + CompletableFuture.supplyAsync(() -> { + Map resultMap = new HashMap<>(); + try { + if(imageVo.getUploadType()==0){ + resultMap = createImageFromNet(imageVo.getName(), imageVo.getTagName(), imageVo.getPath()); + }else{ + resultMap = createImageFromLocal(imageVo.getName(), imageVo.getTagName(), imageVo.getPath()); + } + } catch (Exception e) { + imageVersion.setStatus("failed"); + imageVersionService.update(imageVersion); + throw new RuntimeException("镜像构建失败: " + e.getMessage(), e); + } + return resultMap; + }).thenAccept(resultMap ->{ + try { + String imageUrl = resultMap.get("url"); + String fileSize = resultMap.get("filesize"); + imageVersion.setUrl(imageUrl); + imageVersion.setFileSize(fileSize); + imageVersion.setStatus("available"); + imageVersionService.update(imageVersion); + } catch (Exception e) { + System.err.println("更新数据库失败: " + e.getMessage()); + } + }).exceptionally(ex -> { + System.err.println("异步操作发生错误: " + ex.getMessage()); + return null; + }); + + + return "新增镜像成功,镜像构建中..."; } @Override - public String createImageFromNet(String imageName, String imageTag, String netPath) throws Exception { + public Map createImageFromNet(String imageName, String imageTag, String netPath) throws Exception { + Map resultMap = new HashMap<>(); // 得到容器 V1Pod pod = k8sClientUtil.getNSPodList(serviceNS, deploymentName); if (pod == null) { @@ -203,7 +238,7 @@ public class ImageServiceImpl implements ImageService { // 在这个容器的/data/admin 目录下执行命令 docker load -i fileName 得到返回的镜像名字name:tag String username = SecurityUtils.getLoginUser().getUsername(); // - String logs2 = k8sClientUtil.executeCommand(pod,"docker pull "+netPath); + String logs2 = k8sClientUtil.executeCommand(pod,"docker pull "+ netPath); // 在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag if (StringUtils.isNoneBlank(logs2)){ String substring = logs2.substring(logs2.indexOf(harborUrl), logs2.length()); @@ -211,19 +246,28 @@ public class ImageServiceImpl implements ImageService { String cmd1 = "docker tag " + cleanedString+ " " + harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; String imageUrl = harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; String cmd2 = "docker push " + imageUrl; + String cmd3 = "docker inspect --format='{{.Size}}' " + imageUrl; + String s = k8sClientUtil.executeCommand(pod, cmd1); if (StringUtils.isNotEmpty(k8sClientUtil.executeCommand(pod, cmd2))){ - return imageUrl; + resultMap.put("url", imageUrl); + //得到镜像文件大小 + long sizeInBytes = Long.parseLong(k8sClientUtil.executeCommand(pod, cmd3)); + String formattedImageSize = FileUtil.formatFileSize(sizeInBytes); // 格式化镜像文件大小 + resultMap.put("fileSize", formattedImageSize); + return resultMap; + }else { - throw new Exception("拉取公网镜像失败,请检查网络或者镜像地址"); + throw new Exception("拉取公网镜像失败,请检查网络或者镜像地址"); } }else { - throw new Exception("拉取公网镜像失败,请检查网络或者镜像地址"); + throw new Exception("拉取公网镜像失败,请检查网络或者镜像地址"); } } @Override - public String createImageFromLocal(String imageName, String imageTag, String path) throws Exception { + public Map createImageFromLocal(String imageName, String imageTag, String path) throws Exception { + Map resultMap = new HashMap<>(); // 得到容器 V1Pod pod = k8sClientUtil.getNSPodList(serviceNS, deploymentName); if (pod == null) { @@ -244,11 +288,17 @@ public class ImageServiceImpl implements ImageService { String cmd1 = "docker tag " + cleanedString+ " " + harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; String imageUrl = harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; String cmd2 = "docker push " + imageUrl; + String cmd3 = "docker inspect --format='{{.Size}}' " + imageUrl; String s = k8sClientUtil.executeCommand(pod, cmd1); if (StringUtils.isNotEmpty(k8sClientUtil.executeCommand(pod, cmd2))){ - return imageUrl; + resultMap.put("url", imageUrl); + //得到镜像文件大小 + long sizeInBytes = Long.parseLong(k8sClientUtil.executeCommand(pod, cmd3)); + String formattedImageSize = FileUtil.formatFileSize(sizeInBytes); // 格式化镜像文件大小 + resultMap.put("fileSize", formattedImageSize); + return resultMap; }else { - throw new Exception("解析镜像压缩包失败,请检查镜像文件"); + throw new Exception("解析镜像压缩包失败,请检查镜像文件"); } }else { throw new Exception("解析镜像压缩包失败,请检查镜像文件"); @@ -259,7 +309,6 @@ public class ImageServiceImpl implements ImageService { public Map uploadImageFiles(MultipartFile file) throws Exception { LoginUser loginUser = SecurityUtils.getLoginUser(); String path = loginUser.getUsername()+"/"+file.getOriginalFilename(); - Map stringMap = minioService.uploadFile(bucketName, path, file); - return stringMap; + return minioService.uploadFile(bucketName, path, file); } } 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 654014f4..58bdabfe 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 @@ -382,9 +382,10 @@ public class K8sClientUtil { // invokes the CoreV1Api client for (V1Pod item : v1PodList.getItems()) { - if (item.getMetadata().getGenerateName().startsWith(deploymentName)) { + String generateName = item.getMetadata().getGenerateName(); + if (StringUtils.isNotEmpty(generateName) && generateName.startsWith(deploymentName)) { // 找到匹配的Pod,获取其名称 - return item; + return item; } } return null; diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java index 321a6953..1f6319cf 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java @@ -9,10 +9,7 @@ import java.io.Serializable; @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class ImageVo implements Serializable { - /** - * 主键 - */ - private Integer id; + /** * 镜像名称 */ @@ -58,13 +55,20 @@ public class ImageVo implements Serializable { @ApiModelProperty(name = "status") private String status; - public Integer getId() { - return id; - } + @ApiModelProperty(value = "上传方式, 基于公网上传0,基于本地上传1") + private Integer uploadType; - public void setId(Integer id) { - this.id = id; - } + @ApiModelProperty(value = "镜像上传路径") + private String path; + + +// public Integer getId() { +// return id; +// } +// +// public void setId(Integer id) { +// this.id = id; +// } public String getName() { return name; @@ -129,4 +133,19 @@ public class ImageVo implements Serializable { public void setStatus(String status) { this.status = status; } + + public Integer getUploadType() { + return uploadType; + } + + public void setUploadType(Integer uploadType) { + this.uploadType = uploadType; + } + + public String getPath() { + return path; + } + public void setPath(String path) { + this.path = path; + } } diff --git a/ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ImageDaoMapper.xml b/ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ImageDaoMapper.xml index 4c9b4bd0..c9e80b5f 100644 --- a/ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ImageDaoMapper.xml +++ b/ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ImageDaoMapper.xml @@ -19,7 +19,7 @@ select id,name,description,image_type,create_by,create_time,update_by,update_time,state from image - where id = #{id} + where id = #{id} and state = 1