Browse Source

Merge branch 'dev' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev

dev-active_learn
somunslotus 10 months ago
parent
commit
454f37718d
48 changed files with 387 additions and 316 deletions
  1. +1
    -1
      react-ui/.nvmrc
  2. +16
    -8
      react-ui/src/components/CodeSelect/index.tsx
  3. +12
    -6
      react-ui/src/components/ParameterInput/index.tsx
  4. +4
    -22
      react-ui/src/components/ParameterSelect/config.tsx
  5. +8
    -4
      react-ui/src/components/ParameterSelect/index.tsx
  6. +39
    -43
      react-ui/src/components/ResourceSelect/index.tsx
  7. +21
    -14
      react-ui/src/hooks/resource.ts
  8. +1
    -1
      react-ui/src/pages/AutoML/Instance/index.tsx
  9. +0
    -1
      react-ui/src/pages/AutoML/components/CreateForm/DatasetConfig.tsx
  10. +6
    -1
      react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfig.tsx
  11. +5
    -18
      react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx
  12. +1
    -11
      react-ui/src/pages/Experiment/components/ExperimentParameter/index.tsx
  13. +12
    -12
      react-ui/src/pages/Experiment/components/LogGroup/index.tsx
  14. +0
    -1
      react-ui/src/pages/Experiment/index.jsx
  15. +1
    -1
      react-ui/src/pages/HyperParameter/Instance/index.tsx
  16. +3
    -16
      react-ui/src/pages/HyperParameter/components/CreateForm/ExecuteConfig.tsx
  17. +2
    -2
      react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx
  18. +1
    -1
      react-ui/src/pages/HyperParameter/types.ts
  19. +4
    -15
      react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx
  20. +1
    -1
      react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx
  21. +1
    -1
      react-ui/src/pages/ModelDeployment/components/VersionBasicInfo/index.tsx
  22. +1
    -1
      react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx
  23. +2
    -2
      react-ui/src/pages/Pipeline/Info/index.jsx
  24. +12
    -21
      react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx
  25. +1
    -0
      react-ui/src/stories/CodeSelect.stories.tsx
  26. +27
    -43
      react-ui/src/stories/ParameterInput.stories.tsx
  27. +1
    -3
      react-ui/src/stories/ResourceSelect.stories.tsx
  28. +1
    -0
      ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java
  29. +0
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/constant/Constant.java
  30. +7
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/resources/ComputingResourceController.java
  31. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ComputingResource.java
  32. +10
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ResourceOccupy.java
  33. +8
    -4
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/ResourceOccupyDao.java
  34. +27
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ExperimentInstanceStatusTask.java
  35. +8
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/RayInsStatusTask.java
  36. +3
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ResourceOccupyTask.java
  37. +8
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ResourceOccupyService.java
  38. +2
    -2
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/DevEnvironmentServiceImpl.java
  39. +7
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentInsServiceImpl.java
  40. +16
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentServiceImpl.java
  41. +3
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java
  42. +1
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/RayInsServiceImpl.java
  43. +2
    -1
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/RayServiceImpl.java
  44. +51
    -22
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ResourceOccupyServiceImpl.java
  45. +5
    -3
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ServiceServiceImpl.java
  46. +3
    -6
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java
  47. +37
    -6
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ResourceOccupy.xml
  48. +4
    -4
      ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

+ 1
- 1
react-ui/.nvmrc View File

@@ -1 +1 @@
v18.16.0
v18.20.7

+ 16
- 8
react-ui/src/components/CodeSelect/index.tsx View File

@@ -30,35 +30,42 @@ function CodeSelect({
onChange, onChange,
...rest ...rest
}: CodeSelectProps) { }: CodeSelectProps) {
// 选择代码配置
const selectResource = () => { const selectResource = () => {
const { close } = openAntdModal(CodeSelectorModal, { const { close } = openAntdModal(CodeSelectorModal, {
onOk: (res) => { onOk: (res) => {
if (res) { if (res) {
const { git_url, git_branch, code_repo_name } = res;
const { id, code_repo_name, git_url, git_branch, git_user_name, git_password, ssh_key } =
res;
const jsonObj = { const jsonObj = {
id,
name: code_repo_name,
code_path: git_url, code_path: git_url,
branch: git_branch, branch: git_branch,
username: git_user_name,
password: git_password,
ssh_private_key: ssh_key,
}; };
const jsonObjStr = JSON.stringify(jsonObj); const jsonObjStr = JSON.stringify(jsonObj);
const showValue = code_repo_name;
onChange?.({ onChange?.({
value: jsonObjStr, value: jsonObjStr,
showValue,
showValue: code_repo_name,
fromSelect: true, fromSelect: true,
...jsonObj, ...jsonObj,
}); });
} else { } else {
onChange?.({
value: undefined,
showValue: undefined,
fromSelect: false,
});
onChange?.(undefined);
} }
close(); close();
}, },
}); });
}; };


// 删除
const handleRemove = () => {
onChange?.(undefined);
};

return ( return (
<div className={classNames('kf-code-select', className)} style={style}> <div className={classNames('kf-code-select', className)} style={style}>
<ParameterInput <ParameterInput
@@ -68,6 +75,7 @@ function CodeSelect({
value={value} value={value}
onChange={onChange} onChange={onChange}
onClick={selectResource} onClick={selectResource}
onRemove={handleRemove}
></ParameterInput> ></ParameterInput>
<Button <Button
className="kf-code-select__button" className="kf-code-select__button"


+ 12
- 6
react-ui/src/components/ParameterInput/index.tsx View File

@@ -6,7 +6,7 @@


import { CommonTabKeys } from '@/enums'; import { CommonTabKeys } from '@/enums';
import { CloseOutlined } from '@ant-design/icons'; import { CloseOutlined } from '@ant-design/icons';
import { Form, Input } from 'antd';
import { ConfigProvider, Form, Input } from 'antd';
import { RuleObject } from 'antd/es/form'; import { RuleObject } from 'antd/es/form';
import classNames from 'classnames'; import classNames from 'classnames';
import './index.less'; import './index.less';
@@ -67,7 +67,7 @@ function ParameterInput({
allowClear, allowClear,
className, className,
style, style,
size = 'middle',
size,
disabled = false, disabled = false,
id, id,
...rest ...rest
@@ -81,10 +81,17 @@ function ParameterInput({
const placeholder = valueObj?.placeholder || rest?.placeholder; const placeholder = valueObj?.placeholder || rest?.placeholder;
const InputComponent = textArea ? Input.TextArea : Input; const InputComponent = textArea ? Input.TextArea : Input;
const { status } = Form.Item.useStatus(); const { status } = Form.Item.useStatus();
const { componentSize } = ConfigProvider.useConfig();
const mySize = size || componentSize;


// 删除 // 删除
const handleRemove = (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => { const handleRemove = (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
e.stopPropagation(); e.stopPropagation();
if (onRemove) {
onRemove();
return;
}

onChange?.({ onChange?.({
...valueObj, ...valueObj,
value: undefined, value: undefined,
@@ -94,7 +101,6 @@ function ParameterInput({
expandedKeys: [], expandedKeys: [],
checkedKeys: [], checkedKeys: [],
}); });
onRemove?.();
}; };


return ( return (
@@ -104,8 +110,8 @@ function ParameterInput({
id={id} id={id}
className={classNames( className={classNames(
'parameter-input', 'parameter-input',
{ 'parameter-input--large': size === 'large' },
{ 'parameter-input--small': size === 'small' },
{ 'parameter-input--large': mySize === 'large' },
{ 'parameter-input--small': mySize === 'small' },
{ [`parameter-input--${status}`]: status }, { [`parameter-input--${status}`]: status },
className, className,
)} )}
@@ -128,7 +134,7 @@ function ParameterInput({
<InputComponent <InputComponent
{...rest} {...rest}
id={id} id={id}
size={size}
size={mySize}
className={className} className={className}
style={style} style={style}
placeholder={placeholder} placeholder={placeholder}


+ 4
- 22
react-ui/src/components/ParameterSelect/config.tsx View File

@@ -1,21 +1,10 @@
import { filterResourceStandard, resourceFieldNames } from '@/hooks/resource';
import { ServiceData } from '@/pages/ModelDeployment/types'; import { ServiceData } from '@/pages/ModelDeployment/types';
import { getDatasetList, getModelList } from '@/services/dataset/index.js'; import { getDatasetList, getModelList } from '@/services/dataset/index.js';
import { getServiceListReq } from '@/services/modelDeployment'; import { getServiceListReq } from '@/services/modelDeployment';
import { getComputingResourceReq } from '@/services/pipeline';
import { ComputingResource } from '@/types';
import { type SelectProps } from 'antd'; import { type SelectProps } from 'antd';
import { pick } from 'lodash'; import { pick } from 'lodash';


// 过滤资源规格
const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] = (
input: string,
option?: ComputingResource,
) => {
return (
option?.computing_resource?.toLocaleLowerCase()?.includes(input.toLocaleLowerCase()) ?? false
);
};

// id 从 number 转换为 string // id 从 number 转换为 string
const convertId = (item: any) => ({ const convertId = (item: any) => ({
...item, ...item,
@@ -86,17 +75,10 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = {
}, },
resource: { resource: {
getOptions: async () => { getOptions: async () => {
const res = await getComputingResourceReq({
page: 0,
size: 1000,
resource_type: '',
});
return res?.data?.content ?? [];
},
fieldNames: {
label: 'description',
value: 'standard',
// 不需要这个函数
return [];
}, },
fieldNames: resourceFieldNames,
filterOption: filterResourceStandard as SelectProps['filterOption'], filterOption: filterResourceStandard as SelectProps['filterOption'],
}, },
}; };

+ 8
- 4
react-ui/src/components/ParameterSelect/index.tsx View File

@@ -4,6 +4,7 @@
* @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务 * @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务
*/ */


import { useComputingResource } from '@/hooks/resource';
import { to } from '@/utils/promise'; import { to } from '@/utils/promise';
import { Select, type SelectProps } from 'antd'; import { Select, type SelectProps } from 'antd';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@@ -20,7 +21,7 @@ export interface ParameterSelectProps extends SelectProps {
dataType: 'dataset' | 'model' | 'service' | 'resource'; dataType: 'dataset' | 'model' | 'service' | 'resource';
/** 是否只是展示信息 */ /** 是否只是展示信息 */
display?: boolean; display?: boolean;
/** 值 */
/** 值,支持对象,对象必须包含 value */
value?: string | ParameterSelectObject; value?: string | ParameterSelectObject;
/** 修改后回调 */ /** 修改后回调 */
onChange?: (value: string | ParameterSelectObject) => void; onChange?: (value: string | ParameterSelectObject) => void;
@@ -34,9 +35,10 @@ function ParameterSelect({
onChange, onChange,
...rest ...rest
}: ParameterSelectProps) { }: ParameterSelectProps) {
const [options, setOptions] = useState([]);
const [options, setOptions] = useState<SelectProps['options']>([]);
const propsConfig = paramSelectConfig[dataType]; const propsConfig = paramSelectConfig[dataType];
const valueText = typeof value === 'object' && value !== null ? value.value : value; const valueText = typeof value === 'object' && value !== null ? value.value : value;
const [resourceStandardList] = useComputingResource();


useEffect(() => { useEffect(() => {
// 获取下拉数据 // 获取下拉数据
@@ -54,6 +56,8 @@ function ParameterSelect({
getSelectOptions(); getSelectOptions();
}, [propsConfig]); }, [propsConfig]);


const selectOptions = dataType === 'resource' ? resourceStandardList : options;

const handleChange = (text: string) => { const handleChange = (text: string) => {
if (typeof value === 'object' && value !== null) { if (typeof value === 'object' && value !== null) {
onChange?.({ onChange?.({
@@ -71,7 +75,7 @@ function ParameterSelect({
<FormInfo <FormInfo
select select
value={valueText} value={valueText}
options={options}
options={selectOptions}
fieldNames={propsConfig?.fieldNames} fieldNames={propsConfig?.fieldNames}
></FormInfo> ></FormInfo>
); );
@@ -81,7 +85,7 @@ function ParameterSelect({
<Select <Select
{...rest} {...rest}
filterOption={propsConfig?.filterOption} filterOption={propsConfig?.filterOption}
options={options}
options={selectOptions}
fieldNames={propsConfig?.fieldNames} fieldNames={propsConfig?.fieldNames}
optionFilterProp={propsConfig?.optionFilterProp} optionFilterProp={propsConfig?.optionFilterProp}
value={valueText} value={valueText}


+ 39
- 43
react-ui/src/components/ResourceSelect/index.tsx View File

@@ -11,10 +11,9 @@ import ResourceSelectorModal, {
selectorTypeConfig, selectorTypeConfig,
} from '@/components/ResourceSelectorModal'; } from '@/components/ResourceSelectorModal';
import { openAntdModal } from '@/utils/modal'; import { openAntdModal } from '@/utils/modal';
import { Button } from 'antd';
import { Button, ConfigProvider } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import { pick } from 'lodash'; import { pick } from 'lodash';
import { useEffect, useState } from 'react';
import ParameterInput, { type ParameterInputProps } from '../ParameterInput'; import ParameterInput, { type ParameterInputProps } from '../ParameterInput';
import './index.less'; import './index.less';


@@ -46,43 +45,40 @@ function ResourceSelect({
onChange, onChange,
...rest ...rest
}: ResourceSelectProps) { }: ResourceSelectProps) {
const [selectedResource, setSelectedResource] = useState<ResourceSelectorResponse | undefined>(
undefined,
);

useEffect(() => {
if (
value &&
typeof value === 'object' &&
value.activeTab &&
value.id &&
value.name &&
value.version &&
value.path &&
(type === ResourceSelectorType.Mirror || (value.identifier && value.owner))
) {
const originResource = pick(value, [
'activeTab',
'id',
'identifier',
'name',
'owner',
'version',
'path',
]) as ResourceSelectorResponse;
setSelectedResource(originResource);
}
}, [value, type]);
const { componentSize } = ConfigProvider.useConfig();
const mySize = size || componentSize;
let selectedResource: ResourceSelectorResponse | undefined = undefined;
if (
value &&
typeof value === 'object' &&
value.activeTab &&
value.id &&
value.name &&
value.version &&
value.path &&
(type === ResourceSelectorType.Mirror || (value.identifier && value.owner))
) {
selectedResource = pick(value, [
'activeTab',
'id',
'identifier',
'name',
'owner',
'version',
'path',
]) as ResourceSelectorResponse;
}


// 选择数据集、模型、镜像
const selectResource = () => { const selectResource = () => {
const resource = selectedResource;
const { close } = openAntdModal(ResourceSelectorModal, { const { close } = openAntdModal(ResourceSelectorModal, {
type, type,
defaultExpandedKeys: resource ? [resource.id] : [],
defaultCheckedKeys: resource ? [`${resource.id}-${resource.version}`] : [],
defaultActiveTab: resource?.activeTab,
defaultExpandedKeys: selectedResource ? [selectedResource.id] : [],
defaultCheckedKeys: selectedResource
? [`${selectedResource.id}-${selectedResource.version}`]
: [],
defaultActiveTab: selectedResource?.activeTab,
onOk: (res) => { onOk: (res) => {
setSelectedResource(res);
if (res) { if (res) {
const { activeTab, id, name, version, path, identifier, owner } = res; const { activeTab, id, name, version, path, identifier, owner } = res;
if (type === ResourceSelectorType.Mirror) { if (type === ResourceSelectorType.Mirror) {
@@ -116,32 +112,32 @@ function ResourceSelect({
}); });
} }
} else { } else {
onChange?.({
value: undefined,
showValue: undefined,
fromSelect: false,
activeTab: undefined,
});
onChange?.(undefined);
} }
close(); close();
}, },
}); });
}; };


// 删除
const handleRemove = () => {
onChange?.(undefined);
};

return ( return (
<div className={classNames('kf-resource-select', className)} style={style}> <div className={classNames('kf-resource-select', className)} style={style}>
<ParameterInput <ParameterInput
{...rest} {...rest}
disabled={disabled} disabled={disabled}
value={value} value={value}
size={size}
size={mySize}
onChange={onChange} onChange={onChange}
onRemove={() => setSelectedResource(undefined)}
onRemove={handleRemove}
onClick={selectResource} onClick={selectResource}
></ParameterInput> ></ParameterInput>
<Button <Button
className="kf-resource-select__button" className="kf-resource-select__button"
size={size}
size={mySize}
type="link" type="link"
icon={getSelectBtnIcon(type)} icon={getSelectBtnIcon(type)}
disabled={disabled} disabled={disabled}


+ 21
- 14
react-ui/src/hooks/resource.ts View File

@@ -12,6 +12,22 @@ import { useCallback, useEffect, useState } from 'react';


const computingResource: ComputingResource[] = []; const computingResource: ComputingResource[] = [];


// 过滤资源规格
export const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] = (
input: string,
option?: ComputingResource,
) => {
return (
option?.computing_resource?.toLocaleLowerCase()?.includes(input.toLocaleLowerCase()) ?? false
);
};

// 资源规格字段
export const resourceFieldNames = {
label: 'description',
value: 'id',
};

// 获取资源规格 // 获取资源规格
export function useComputingResource() { export function useComputingResource() {
const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]); const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]);
@@ -25,7 +41,7 @@ export function useComputingResource() {
resource_type: '', resource_type: '',
}; };
const [res] = await to(getComputingResourceReq(params)); const [res] = await to(getComputingResourceReq(params));
if (res && res.data && res.data.content) {
if (res && res.data && Array.isArray(res.data.content)) {
setResourceStandardList(res.data.content); setResourceStandardList(res.data.content);
computingResource.splice(0, computingResource.length, ...res.data.content); computingResource.splice(0, computingResource.length, ...res.data.content);
} }
@@ -38,25 +54,16 @@ export function useComputingResource() {
} }
}, []); }, []);


// 过滤资源规格
const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] =
useCallback((input: string, option?: ComputingResource) => {
return (
option?.computing_resource?.toLocaleLowerCase()?.includes(input.toLocaleLowerCase()) ??
false
);
}, []);

// 根据 standard 获取 description // 根据 standard 获取 description
const getDescription = useCallback( const getDescription = useCallback(
(standard?: string) => {
if (!standard) {
(id?: string | number) => {
if (!id) {
return undefined; return undefined;
} }
return resourceStandardList.find((item) => item.standard === standard)?.description;
return resourceStandardList.find((item) => Number(item.id) === Number(id))?.description;
}, },
[resourceStandardList], [resourceStandardList],
); );


return [resourceStandardList, filterResourceStandard, getDescription] as const;
return [resourceStandardList, getDescription] as const;
} }

+ 1
- 1
react-ui/src/pages/AutoML/Instance/index.tsx View File

@@ -40,7 +40,7 @@ function AutoMLInstance() {
closeSSE(); closeSSE();
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [instanceId]);


// 获取实验实例详情 // 获取实验实例详情
const getExperimentInsInfo = async (isStatusDetermined: boolean) => { const getExperimentInsInfo = async (isStatusDetermined: boolean) => {


+ 0
- 1
react-ui/src/pages/AutoML/components/CreateForm/DatasetConfig.tsx View File

@@ -30,7 +30,6 @@ function DatasetConfig() {
type={ResourceSelectorType.Dataset} type={ResourceSelectorType.Dataset}
placeholder="请选择数据集" placeholder="请选择数据集"
canInput={false} canInput={false}
size="large"
/> />
</Form.Item> </Form.Item>
</Col> </Col>


+ 6
- 1
react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfig.tsx View File

@@ -431,7 +431,12 @@ function ExecuteConfig() {


<Row gutter={8}> <Row gutter={8}>
<Col span={10}> <Col span={10}>
<Form.Item label="是否打乱" name="shuffle" tooltip="拆分数据前是否打乱顺序">
<Form.Item
label="是否打乱"
name="shuffle"
tooltip="拆分数据前是否打乱顺序"
valuePropName="checked"
>
<Switch /> <Switch />
</Form.Item> </Form.Item>
</Col> </Col>


+ 5
- 18
react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx View File

@@ -6,17 +6,17 @@
import KFIcon from '@/components/KFIcon'; import KFIcon from '@/components/KFIcon';
import KFRadio, { type KFRadioItem } from '@/components/KFRadio'; import KFRadio, { type KFRadioItem } from '@/components/KFRadio';
import PageTitle from '@/components/PageTitle'; import PageTitle from '@/components/PageTitle';
import ParameterSelect from '@/components/ParameterSelect';
import ResourceSelect, { import ResourceSelect, {
requiredValidator, requiredValidator,
ResourceSelectorType, ResourceSelectorType,
type ParameterInputObject, type ParameterInputObject,
} from '@/components/ResourceSelect'; } from '@/components/ResourceSelect';
import SubAreaTitle from '@/components/SubAreaTitle'; import SubAreaTitle from '@/components/SubAreaTitle';
import { useComputingResource } from '@/hooks/resource';
import { createEditorReq } from '@/services/developmentEnvironment'; import { createEditorReq } from '@/services/developmentEnvironment';
import { to } from '@/utils/promise'; import { to } from '@/utils/promise';
import { useNavigate } from '@umijs/max'; import { useNavigate } from '@umijs/max';
import { App, Button, Col, Form, Input, Row, Select } from 'antd';
import { App, Button, Col, Form, Input, Row } from 'antd';
import { omit, pick } from 'lodash'; import { omit, pick } from 'lodash';
import styles from './index.less'; import styles from './index.less';


@@ -51,7 +51,6 @@ function EditorCreate() {
const navigate = useNavigate(); const navigate = useNavigate();
const [form] = Form.useForm(); const [form] = Form.useForm();
const { message } = App.useApp(); const { message } = App.useApp();
const [resourceStandardList, filterResourceStandard] = useComputingResource();


// 创建编辑器 // 创建编辑器
const createEditor = async (formData: FormData) => { const createEditor = async (formData: FormData) => {
@@ -62,8 +61,8 @@ function EditorCreate() {
const params = { const params = {
...omit(formData, ['image', 'model', 'dataset']), ...omit(formData, ['image', 'model', 'dataset']),
image: image.value, image: image.value,
model: pick(model, ['id', 'version', 'path', 'showValue']),
dataset: pick(dataset, ['id', 'version', 'path', 'showValue']),
model: model && pick(model, ['id', 'version', 'path', 'showValue']),
dataset: dataset && pick(dataset, ['id', 'version', 'path', 'showValue']),
}; };
const [res] = await to(createEditorReq(params)); const [res] = await to(createEditorReq(params));
if (res) { if (res) {
@@ -146,16 +145,7 @@ function EditorCreate() {
}, },
]} ]}
> >
<Select
showSearch
placeholder="请选择资源规格"
filterOption={filterResourceStandard}
options={resourceStandardList}
fieldNames={{
label: 'description',
value: 'standard',
}}
/>
<ParameterSelect dataType="resource" placeholder="请选择资源规格" />
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
@@ -181,7 +171,6 @@ function EditorCreate() {
type={ResourceSelectorType.Mirror} type={ResourceSelectorType.Mirror}
placeholder="请选择镜像" placeholder="请选择镜像"
canInput={false} canInput={false}
size="large"
/> />
</Form.Item> </Form.Item>
</Col> </Col>
@@ -193,7 +182,6 @@ function EditorCreate() {
type={ResourceSelectorType.Model} type={ResourceSelectorType.Model}
placeholder="请选择模型" placeholder="请选择模型"
canInput={false} canInput={false}
size="large"
/> />
</Form.Item> </Form.Item>
</Col> </Col>
@@ -205,7 +193,6 @@ function EditorCreate() {
type={ResourceSelectorType.Dataset} type={ResourceSelectorType.Dataset}
placeholder="请选择数据集" placeholder="请选择数据集"
canInput={false} canInput={false}
size="large"
/> />
</Form.Item> </Form.Item>
</Col> </Col>


+ 1
- 11
react-ui/src/pages/Experiment/components/ExperimentParameter/index.tsx View File

@@ -1,7 +1,6 @@
import FormInfo from '@/components/FormInfo'; import FormInfo from '@/components/FormInfo';
import ParameterSelect from '@/components/ParameterSelect'; import ParameterSelect from '@/components/ParameterSelect';
import SubAreaTitle from '@/components/SubAreaTitle'; import SubAreaTitle from '@/components/SubAreaTitle';
import { useComputingResource } from '@/hooks/resource';
import { PipelineNodeModelSerialize } from '@/types'; import { PipelineNodeModelSerialize } from '@/types';
import { Form } from 'antd'; import { Form } from 'antd';
import styles from './index.less'; import styles from './index.less';
@@ -11,8 +10,6 @@ type ExperimentParameterProps = {
}; };


function ExperimentParameter({ nodeData }: ExperimentParameterProps) { function ExperimentParameter({ nodeData }: ExperimentParameterProps) {
const [resourceStandardList] = useComputingResource(); // 资源规模

// 控制策略 // 控制策略
const controlStrategyList = Object.entries(nodeData.control_strategy ?? {}).map( const controlStrategyList = Object.entries(nodeData.control_strategy ?? {}).map(
([key, value]) => ({ key, value }), ([key, value]) => ({ key, value }),
@@ -112,14 +109,7 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) {
}, },
]} ]}
> >
<FormInfo
select
options={resourceStandardList}
fieldNames={{
label: 'description',
value: 'standard',
}}
/>
<ParameterSelect dataType="resource" placeholder="请选择资源规格" display />
</Form.Item> </Form.Item>
<Form.Item label="挂载路径" name="mount_path"> <Form.Item label="挂载路径" name="mount_path">
<FormInfo /> <FormInfo />


+ 12
- 12
react-ui/src/pages/Experiment/components/LogGroup/index.tsx View File

@@ -33,7 +33,7 @@ function LogGroup({
status, status,
}: LogGroupProps) { }: LogGroupProps) {
const [collapse, setCollapse] = useState(true); const [collapse, setCollapse] = useState(true);
const [logList, setLogList, logListRef] = useStateRef<Log[]>([]);
const [logList, setLogList] = useState<Log[]>([]);
const [completed, setCompleted] = useState(false); const [completed, setCompleted] = useState(false);
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_isMouseDown, setIsMouseDown, isMouseDownRef] = useStateRef(false); const [_isMouseDown, setIsMouseDown, isMouseDownRef] = useStateRef(false);
@@ -126,6 +126,14 @@ function LogGroup({
socketRef.current = socket; socketRef.current = socket;
}; };


// 关闭 socket
const closeSocket = () => {
if (socketRef.current) {
socketRef.current.close(1000, 'completed');
socketRef.current = undefined;
}
};

if (status === ExperimentStatus.Running) { if (status === ExperimentStatus.Running) {
setupSockect(); setupSockect();
} }
@@ -133,7 +141,7 @@ function LogGroup({
return () => { return () => {
closeSocket(); closeSocket();
}; };
}, [status, start_time, pod_name, isMouseDownRef, setLogList]);
}, [status, start_time, pod_name, isMouseDownRef]);


// 鼠标拖到中不滚动到底部 // 鼠标拖到中不滚动到底部
useEffect(() => { useEffect(() => {
@@ -153,8 +161,8 @@ function LogGroup({


// 请求日志 // 请求日志
const requestExperimentPodsLog = async () => { const requestExperimentPodsLog = async () => {
const list = logListRef.current;
const startTime = list.length > 0 ? list[list.length - 1].start_time : start_time;
const last = logList[logList.length - 1];
const startTime = last ? last.start_time : start_time;
const params = { const params = {
pod_name, pod_name,
start_time: startTime, start_time: startTime,
@@ -201,14 +209,6 @@ function LogGroup({
requestExperimentPodsLog(); requestExperimentPodsLog();
}; };


// 关闭 socket
const closeSocket = () => {
if (socketRef.current) {
socketRef.current.close(1000, 'completed');
socketRef.current = undefined;
}
};

// 滚动到底部 // 滚动到底部
const scrollToBottom = (smooth: boolean = true) => { const scrollToBottom = (smooth: boolean = true) => {
// const element = document.getElementById(listId); // const element = document.getElementById(listId);


+ 0
- 1
react-ui/src/pages/Experiment/index.jsx View File

@@ -277,7 +277,6 @@ function Experiment() {
current, current,
pageSize, pageSize,
}); });
getExperimentList();
}; };
// 运行实验 // 运行实验
const runExperiment = async (id) => { const runExperiment = async (id) => {


+ 1
- 1
react-ui/src/pages/HyperParameter/Instance/index.tsx View File

@@ -44,7 +44,7 @@ function HyperParameterInstance() {
closeSSE(); closeSSE();
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [instanceId]);


// 获取实验实例详情 // 获取实验实例详情
const getExperimentInsInfo = async (isStatusDetermined: boolean) => { const getExperimentInsInfo = async (isStatusDetermined: boolean) => {


+ 3
- 16
react-ui/src/pages/HyperParameter/components/CreateForm/ExecuteConfig.tsx View File

@@ -1,12 +1,12 @@
import CodeSelect from '@/components/CodeSelect'; import CodeSelect from '@/components/CodeSelect';
import KFIcon from '@/components/KFIcon'; import KFIcon from '@/components/KFIcon';
import ParameterSelect from '@/components/ParameterSelect';
import ResourceSelect, { import ResourceSelect, {
ResourceSelectorType, ResourceSelectorType,
requiredValidator, requiredValidator,
} from '@/components/ResourceSelect'; } from '@/components/ResourceSelect';
import SubAreaTitle from '@/components/SubAreaTitle'; import SubAreaTitle from '@/components/SubAreaTitle';
import { hyperParameterOptimizedModeOptions } from '@/enums'; import { hyperParameterOptimizedModeOptions } from '@/enums';
import { useComputingResource } from '@/hooks/resource';
import { isEmpty } from '@/utils'; import { isEmpty } from '@/utils';
import { modalConfirm, removeFormListItem } from '@/utils/ui'; import { modalConfirm, removeFormListItem } from '@/utils/ui';
import { MinusCircleOutlined, PlusCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { MinusCircleOutlined, PlusCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons';
@@ -86,7 +86,6 @@ function ExecuteConfig() {
const searchAlgorithm = Form.useWatch('search_alg', form); const searchAlgorithm = Form.useWatch('search_alg', form);
const paramsTypeOptions = searchAlgorithm === 'Ax' ? axParameterOptions : parameterOptions; const paramsTypeOptions = searchAlgorithm === 'Ax' ? axParameterOptions : parameterOptions;
const paramsTypeTooltip = searchAlgorithm === 'Ax' ? axParameterTooltip : parameterTooltip; const paramsTypeTooltip = searchAlgorithm === 'Ax' ? axParameterTooltip : parameterTooltip;
const [resourceStandardList, filterResourceStandard] = useComputingResource();


const handleSearchAlgorithmChange = (value: string) => { const handleSearchAlgorithmChange = (value: string) => {
if ( if (
@@ -157,7 +156,6 @@ function ExecuteConfig() {
type={ResourceSelectorType.Mirror} type={ResourceSelectorType.Mirror}
placeholder="请选择镜像" placeholder="请选择镜像"
canInput={false} canInput={false}
size="large"
/> />
</Form.Item> </Form.Item>
</Col> </Col>
@@ -180,7 +178,6 @@ function ExecuteConfig() {
type={ResourceSelectorType.Dataset} type={ResourceSelectorType.Dataset}
placeholder="请选择数据集" placeholder="请选择数据集"
canInput={false} canInput={false}
size="large"
/> />
</Form.Item> </Form.Item>
</Col> </Col>
@@ -193,7 +190,6 @@ function ExecuteConfig() {
type={ResourceSelectorType.Model} type={ResourceSelectorType.Model}
placeholder="请选择模型" placeholder="请选择模型"
canInput={false} canInput={false}
size="large"
/> />
</Form.Item> </Form.Item>
</Col> </Col>
@@ -596,7 +592,7 @@ function ExecuteConfig() {
<Col span={10}> <Col span={10}>
<Form.Item <Form.Item
label="资源规格" label="资源规格"
name="resource"
name="computing_resource_id"
rules={[ rules={[
{ {
required: true, required: true,
@@ -604,16 +600,7 @@ function ExecuteConfig() {
}, },
]} ]}
> >
<Select
showSearch
placeholder="请选择资源规格"
filterOption={filterResourceStandard}
options={resourceStandardList}
fieldNames={{
label: 'description',
value: 'standard',
}}
/>
<ParameterSelect dataType="resource" placeholder="请选择资源规格" />
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>


+ 2
- 2
react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx View File

@@ -41,7 +41,7 @@ function HyperParameterBasic({
runStatus, runStatus,
isInstance = false, isInstance = false,
}: HyperParameterBasicProps) { }: HyperParameterBasicProps) {
const getResourceDescription = useComputingResource()[2];
const getResourceDescription = useComputingResource()[1];


const basicDatas: BasicInfoData[] = useMemo(() => { const basicDatas: BasicInfoData[] = useMemo(() => {
if (!info) { if (!info) {
@@ -136,7 +136,7 @@ function HyperParameterBasic({
}, },
{ {
label: '资源规格', label: '资源规格',
value: info.resource,
value: info.computing_resource_id,
format: getResourceDescription, format: getResourceDescription,
}, },
]; ];


+ 1
- 1
react-ui/src/pages/HyperParameter/types.ts View File

@@ -24,7 +24,7 @@ export type FormData = {
num_samples: number; // 总试验次数 num_samples: number; // 总试验次数
max_t: number; // 单次试验最大时间 max_t: number; // 单次试验最大时间
min_samples_required: number; // 计算中位数的最小试验数 min_samples_required: number; // 计算中位数的最小试验数
resource: string; // 资源规格
computing_resource_id: number; // 资源规格
parameters: FormParameter[]; parameters: FormParameter[];
points_to_evaluate: { [key: string]: any }[]; points_to_evaluate: { [key: string]: any }[];
}; };


+ 4
- 15
react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx View File

@@ -5,13 +5,13 @@
*/ */
import CodeSelect from '@/components/CodeSelect'; import CodeSelect from '@/components/CodeSelect';
import PageTitle from '@/components/PageTitle'; import PageTitle from '@/components/PageTitle';
import ParameterSelect from '@/components/ParameterSelect';
import ResourceSelect, { import ResourceSelect, {
ResourceSelectorType, ResourceSelectorType,
requiredValidator, requiredValidator,
type ParameterInputObject, type ParameterInputObject,
} from '@/components/ResourceSelect'; } from '@/components/ResourceSelect';
import SubAreaTitle from '@/components/SubAreaTitle'; import SubAreaTitle from '@/components/SubAreaTitle';
import { useComputingResource } from '@/hooks/resource';
import { import {
createServiceVersionReq, createServiceVersionReq,
getServiceInfoReq, getServiceInfoReq,
@@ -23,7 +23,7 @@ import SessionStorage from '@/utils/sessionStorage';
import { removeFormListItem } from '@/utils/ui'; import { removeFormListItem } from '@/utils/ui';
import { MinusCircleOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons'; import { MinusCircleOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { useNavigate, useParams } from '@umijs/max'; import { useNavigate, useParams } from '@umijs/max';
import { App, Button, Col, Flex, Form, Input, InputNumber, Row, Select } from 'antd';
import { App, Button, Col, Flex, Form, Input, InputNumber, Row } from 'antd';
import { omit, pick } from 'lodash'; import { omit, pick } from 'lodash';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { CreateServiceVersionFrom, ServiceOperationType, ServiceVersionData } from '../types'; import { CreateServiceVersionFrom, ServiceOperationType, ServiceVersionData } from '../types';
@@ -51,7 +51,7 @@ type ServiceVersionCache = ServiceVersionData & {
function CreateServiceVersion() { function CreateServiceVersion() {
const navigate = useNavigate(); const navigate = useNavigate();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [resourceStandardList, filterResourceStandard] = useComputingResource();
const [operationType, setOperationType] = useState(ServiceOperationType.Create); const [operationType, setOperationType] = useState(ServiceOperationType.Create);
const [lastPage, setLastPage] = useState(CreateServiceVersionFrom.ServiceInfo); const [lastPage, setLastPage] = useState(CreateServiceVersionFrom.ServiceInfo);
const { message } = App.useApp(); const { message } = App.useApp();
@@ -305,7 +305,6 @@ function CreateServiceVersion() {
placeholder="请选择模型" placeholder="请选择模型"
disabled={disabled} disabled={disabled}
canInput={false} canInput={false}
size="large"
/> />
</Form.Item> </Form.Item>
</Col> </Col>
@@ -327,7 +326,6 @@ function CreateServiceVersion() {
type={ResourceSelectorType.Mirror} type={ResourceSelectorType.Mirror}
placeholder="请选择镜像" placeholder="请选择镜像"
canInput={false} canInput={false}
size="large"
disabled={disabled} disabled={disabled}
/> />
</Form.Item> </Form.Item>
@@ -357,16 +355,7 @@ function CreateServiceVersion() {
}, },
]} ]}
> >
<Select
showSearch
placeholder="请选择资源规格"
filterOption={filterResourceStandard}
options={resourceStandardList}
fieldNames={{
label: 'description',
value: 'standard',
}}
/>
<ParameterSelect dataType="resource" placeholder="请选择资源规格" />
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>


+ 1
- 1
react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx View File

@@ -87,7 +87,7 @@ function ServiceInfo() {
format: formatDate, format: formatDate,
}, },
]; ];
const getResourceDescription = useComputingResource()[2];
const getResourceDescription = useComputingResource()[1];


// 获取服务详情 // 获取服务详情
const getServiceInfo = useCallback(async () => { const getServiceInfo = useCallback(async () => {


+ 1
- 1
react-ui/src/pages/ModelDeployment/components/VersionBasicInfo/index.tsx View File

@@ -36,7 +36,7 @@ const formatEnvText = (env?: Record<string, string>) => {
}; };


function VersionBasicInfo({ info }: BasicInfoProps) { function VersionBasicInfo({ info }: BasicInfoProps) {
const getResourceDescription = useComputingResource()[2];
const getResourceDescription = useComputingResource()[1];


const datas: BasicInfoData[] = [ const datas: BasicInfoData[] = [
{ {


+ 1
- 1
react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx View File

@@ -42,7 +42,7 @@ const formatEnvText = (env: Record<string, string>) => {


function VersionCompareModal({ version1, version2, ...rest }: VersionCompareModalProps) { function VersionCompareModal({ version1, version2, ...rest }: VersionCompareModalProps) {
const [compareData, setCompareData] = useState<CompareData | undefined>(undefined); const [compareData, setCompareData] = useState<CompareData | undefined>(undefined);
const getResourceDescription = useComputingResource()[2];
const getResourceDescription = useComputingResource()[1];


const fields: FiledType[] = useMemo( const fields: FiledType[] = useMemo(
() => [ () => [


+ 2
- 2
react-ui/src/pages/Pipeline/Info/index.jsx View File

@@ -7,7 +7,7 @@ import { to } from '@/utils/promise';
import G6 from '@antv/g6'; import G6 from '@antv/g6';
import { useNavigate, useParams } from '@umijs/max'; import { useNavigate, useParams } from '@umijs/max';
import { App, Button } from 'antd'; import { App, Button } from 'antd';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState } from 'react';
import GlobalParamsDrawer from '../components/GlobalParamsDrawer'; import GlobalParamsDrawer from '../components/GlobalParamsDrawer';
import ModelMenu from '../components/ModelMenu'; import ModelMenu from '../components/ModelMenu';
import Props from '../components/PipelineNodeDrawer'; import Props from '../components/PipelineNodeDrawer';
@@ -24,7 +24,7 @@ const EditPipeline = () => {
const propsRef = useRef(); const propsRef = useRef();
const [paramsDrawerOpen, openParamsDrawer, closeParamsDrawer] = useVisible(false); const [paramsDrawerOpen, openParamsDrawer, closeParamsDrawer] = useVisible(false);
const [globalParam, setGlobalParam, globalParamRef] = useStateRef([]); const [globalParam, setGlobalParam, globalParamRef] = useStateRef([]);
const [workflowInfo, setWorkflowInfo] = useStateRef(undefined);
const [workflowInfo, setWorkflowInfo] = useState(undefined);
const { message } = App.useApp(); const { message } = App.useApp();
let sourceAnchorIdx, targetAnchorIdx, dropAnchorIdx; let sourceAnchorIdx, targetAnchorIdx, dropAnchorIdx;
let dragSourceNode; let dragSourceNode;


+ 12
- 21
react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx View File

@@ -8,7 +8,6 @@ import ResourceSelectorModal, {
} from '@/components/ResourceSelectorModal'; } from '@/components/ResourceSelectorModal';
import SubAreaTitle from '@/components/SubAreaTitle'; import SubAreaTitle from '@/components/SubAreaTitle';
import { CommonTabKeys } from '@/enums'; import { CommonTabKeys } from '@/enums';
import { useComputingResource } from '@/hooks/resource';
import { canInput, createMenuItems } from '@/pages/Pipeline/Info/utils'; import { canInput, createMenuItems } from '@/pages/Pipeline/Info/utils';
import { import {
PipelineGlobalParam, PipelineGlobalParam,
@@ -19,7 +18,7 @@ import {
import { openAntdModal } from '@/utils/modal'; import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise'; import { to } from '@/utils/promise';
import { INode } from '@antv/g6'; import { INode } from '@antv/g6';
import { Button, Drawer, Form, Input, MenuProps, Select } from 'antd';
import { Button, Drawer, Form, Input, MenuProps } from 'antd';
import { NamePath } from 'antd/es/form/interface'; import { NamePath } from 'antd/es/form/interface';
import { forwardRef, useImperativeHandle, useState } from 'react'; import { forwardRef, useImperativeHandle, useState } from 'react';
import PropsLabel from '../PropsLabel'; import PropsLabel from '../PropsLabel';
@@ -36,7 +35,6 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
{} as PipelineNodeModelSerialize, {} as PipelineNodeModelSerialize,
); );
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [resourceStandardList, filterResourceStandard] = useComputingResource(); // 资源规模
const [menuItems, setMenuItems] = useState<MenuProps['items']>([]); const [menuItems, setMenuItems] = useState<MenuProps['items']>([]);


const afterOpenChange = async () => { const afterOpenChange = async () => {
@@ -152,19 +150,21 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
const { close } = openAntdModal(CodeSelectorModal, { const { close } = openAntdModal(CodeSelectorModal, {
onOk: (res) => { onOk: (res) => {
if (res) { if (res) {
const { id, code_repo_name, git_url, git_branch, git_user_name, git_password, ssh_key } =
res;
const value = JSON.stringify({ const value = JSON.stringify({
id: res.id,
name: res.code_repo_name,
code_path: res.git_url,
branch: res.git_branch,
username: res.git_user_name,
password: res.git_password,
ssh_private_key: res.ssh_key,
id,
name: code_repo_name,
code_path: git_url,
branch: git_branch,
username: git_user_name,
password: git_password,
ssh_private_key: ssh_key,
}); });
form.setFieldValue(formItemName, { form.setFieldValue(formItemName, {
...item, ...item,
value, value,
showValue: res.code_repo_name,
showValue: code_repo_name,
fromSelect: true, fromSelect: true,
}); });
form.validateFields([formItemName]); form.validateFields([formItemName]);
@@ -458,16 +458,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
}, },
]} ]}
> >
<Select
placeholder="请选择资源规格"
filterOption={filterResourceStandard}
options={resourceStandardList}
fieldNames={{
label: 'description',
value: 'standard',
}}
showSearch
/>
<ParameterSelect dataType="resource" placeholder="请选择资源规格" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="mount_path" name="mount_path"


+ 1
- 0
react-ui/src/stories/CodeSelect.stories.tsx View File

@@ -41,6 +41,7 @@ export const Primary: Story = {
canInput: false, canInput: false,
textArea: false, textArea: false,
size: 'large', size: 'large',
placeholder: '请选择代码配置',
style: { width: 400 }, style: { width: 400 },
}, },
render: function Render(args) { render: function Render(args) {


+ 27
- 43
react-ui/src/stories/ParameterInput.stories.tsx View File

@@ -1,5 +1,7 @@
import ParameterInput, { ParameterInputValue } from '@/components/ParameterInput'; import ParameterInput, { ParameterInputValue } from '@/components/ParameterInput';
import { action } from '@storybook/addon-actions';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
import { Button } from 'antd'; import { Button } from 'antd';
import { useState } from 'react'; import { useState } from 'react';


@@ -18,7 +20,7 @@ const meta = {
// backgroundColor: { control: 'color' }, // backgroundColor: { control: 'color' },
}, },
// Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
// args: { onClick: fn() },
args: { onChange: fn() },
} satisfies Meta<typeof ParameterInput>; } satisfies Meta<typeof ParameterInput>;


export default meta; export default meta;
@@ -37,45 +39,6 @@ export const Input: Story = {
}; };


export const Select: Story = { export const Select: Story = {
args: {
placeholder: '请输入工作目录',
style: { width: 300 },
value: 'storybook',
canInput: false,
size: 'large',
},
};

export const SelectWithObjctValue: Story = {
args: {
placeholder: '请输入工作目录',
style: { width: 300 },
value: {
value: 'storybook',
showValue: 'storybook',
fromSelect: true,
},
canInput: true,
size: 'large',
},
};

export const Disabled: Story = {
args: {
placeholder: '请输入工作目录',
style: { width: 300 },
value: {
value: 'storybook',
showValue: 'storybook',
fromSelect: true,
},
canInput: true,
size: 'large',
disabled: true,
},
};

export const Application: Story = {
args: { args: {
placeholder: '请输入工作目录', placeholder: '请输入工作目录',
style: { width: 300 }, style: { width: 300 },
@@ -86,18 +49,24 @@ export const Application: Story = {
const [value, setValue] = useState<ParameterInputValue | undefined>(''); const [value, setValue] = useState<ParameterInputValue | undefined>('');


const onClick = () => { const onClick = () => {
setValue({
const value = {
value: 'storybook', value: 'storybook',
showValue: 'storybook', showValue: 'storybook',
fromSelect: true, fromSelect: true,
});
otherValue: 'others',
};
setValue(value);
action('onChange')(value);
}; };
return ( return (
<> <>
<ParameterInput <ParameterInput
{...args} {...args}
value={value} value={value}
onChange={(value) => setValue(value)}
onChange={(value) => {
setValue(value);
action('onChange')(value);
}}
></ParameterInput> ></ParameterInput>
<Button type="primary" style={{ display: 'block', marginTop: 10 }} onClick={onClick}> <Button type="primary" style={{ display: 'block', marginTop: 10 }} onClick={onClick}>
模拟从全局参数选择 模拟从全局参数选择
@@ -106,3 +75,18 @@ export const Application: Story = {
); );
}, },
}; };

export const Disabled: Story = {
args: {
placeholder: '请输入工作目录',
style: { width: 300 },
value: {
value: 'storybook',
showValue: 'storybook',
fromSelect: true,
},
canInput: true,
size: 'large',
disabled: true,
},
};

+ 1
- 3
react-ui/src/stories/ResourceSelect.stories.tsx View File

@@ -76,6 +76,7 @@ export const Primary: Story = {
canInput: false, canInput: false,
textArea: false, textArea: false,
size: 'large', size: 'large',
placeholder: '请选择数据集',
style: { width: 400 }, style: { width: 400 },
}, },
render: function Render(args) { render: function Render(args) {
@@ -120,7 +121,6 @@ export const InForm: Story = {
type={ResourceSelectorType.Dataset} type={ResourceSelectorType.Dataset}
placeholder="请选择" placeholder="请选择"
canInput={false} canInput={false}
size="large"
onChange={onChange} onChange={onChange}
/> />
</Form.Item> </Form.Item>
@@ -133,7 +133,6 @@ export const InForm: Story = {
type={ResourceSelectorType.Model} type={ResourceSelectorType.Model}
placeholder="请选择" placeholder="请选择"
canInput={false} canInput={false}
size="large"
onChange={onChange} onChange={onChange}
/> />
</Form.Item> </Form.Item>
@@ -146,7 +145,6 @@ export const InForm: Story = {
type={ResourceSelectorType.Mirror} type={ResourceSelectorType.Mirror}
placeholder="请选择" placeholder="请选择"
canInput={false} canInput={false}
size="large"
onChange={onChange} onChange={onChange}
/> />
</Form.Item> </Form.Item>


+ 1
- 0
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java View File

@@ -349,6 +349,7 @@ public class SysUser extends BaseEntity {
.append("dept", getDept()) .append("dept", getDept())
.append("gitLinkUsername", getGitLinkUsername()) .append("gitLinkUsername", getGitLinkUsername())
.append("gitLinkPassword", getGitLinkPassword()) .append("gitLinkPassword", getGitLinkPassword())
.append("credit", getCredit())
.toString(); .toString();
} }
} }

+ 0
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/constant/Constant.java View File

@@ -53,7 +53,6 @@ public class Constant {
// 任务类型 // 任务类型
public final static String TaskType_Dev = "dev_environment"; public final static String TaskType_Dev = "dev_environment";
public final static String TaskType_Workflow = "workflow"; public final static String TaskType_Workflow = "workflow";
public final static String TaskType_AutoMl = "auto_ml";
public final static String TaskType_Ray = "ray"; public final static String TaskType_Ray = "ray";
public final static String TaskType_ActiveLearn = "active_learn"; public final static String TaskType_ActiveLearn = "active_learn";
public final static String TaskType_Service = "service"; public final static String TaskType_Service = "service";


+ 7
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/resources/ComputingResourceController.java View File

@@ -13,6 +13,7 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;


import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Map;


/** /**
* (ComputingResource)表控制层 * (ComputingResource)表控制层
@@ -104,5 +105,11 @@ public class ComputingResourceController extends BaseController {
PageRequest pageRequest = PageRequest.of(page, size); PageRequest pageRequest = PageRequest.of(page, size);
return genericsSuccess(resourceOccupyService.queryByPage(pageRequest)); return genericsSuccess(resourceOccupyService.queryByPage(pageRequest));
} }

@GetMapping("/credit")
@ApiOperation("查询用户积分使用情况")
public GenericsAjaxResult<Map<String, Double>> queryCredit() {
return genericsSuccess(resourceOccupyService.queryCredit());
}
} }



+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ComputingResource.java View File

@@ -49,7 +49,7 @@ public class ComputingResource implements Serializable {
private Integer gpuNums; private Integer gpuNums;


@ApiModelProperty("积分/小时") @ApiModelProperty("积分/小时")
private Float creditPerHour;
private Double creditPerHour;


@ApiModelProperty("标签") @ApiModelProperty("标签")
private String labels; private String labels;


+ 10
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/ResourceOccupy.java View File

@@ -19,8 +19,14 @@ public class ResourceOccupy {
@ApiModelProperty("计算资源") @ApiModelProperty("计算资源")
private Integer computingResourceId; private Integer computingResourceId;


@ApiModelProperty("描述")
private String description;

@ApiModelProperty("积分/小时") @ApiModelProperty("积分/小时")
private Float creditPerHour;
private Double creditPerHour;

@ApiModelProperty("扣除的积分")
private Double deduceCredit;


@ApiModelProperty("上一次扣分时间") @ApiModelProperty("上一次扣分时间")
private Date deduceLastTime; private Date deduceLastTime;
@@ -36,4 +42,7 @@ public class ResourceOccupy {


@ApiModelProperty("类型id") @ApiModelProperty("类型id")
private Long taskId; private Long taskId;

@ApiModelProperty("流水线节点id")
private String nodeId;
} }

+ 8
- 4
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/ResourceOccupyDao.java View File

@@ -14,15 +14,19 @@ public interface ResourceOccupyDao {


int edit(@Param("resourceOccupy") ResourceOccupy resourceOccupy); int edit(@Param("resourceOccupy") ResourceOccupy resourceOccupy);


ResourceOccupy getResourceOccupyByTask(@Param("taskType") String taskType, @Param("taskId") Long taskId);
List<ResourceOccupy> getResourceOccupyByTask(@Param("taskType") String taskType, @Param("taskId") Long taskId, @Param("nodeId") String nodeId);


int deduceCredit(@Param("credit") Float credit, @Param("userId") Long userId);
int deduceCredit(@Param("credit") Double credit, @Param("userId") Long userId);


int updateUsed(@Param("id") Integer id, @Param("used") Integer used); int updateUsed(@Param("id") Integer id, @Param("used") Integer used);


int updateUnUsed(@Param("id") Integer id, @Param("used") Integer used); int updateUnUsed(@Param("id") Integer id, @Param("used") Integer used);


long count();
long count(@Param("userId") Long userId);


List<ResourceOccupy> queryByPage(@Param("pageable") Pageable pageable);
List<ResourceOccupy> queryByPage(@Param("userId") Long userId, @Param("pageable") Pageable pageable);

Double getUserCredit(@Param("userId") Long userId);

Double getDeduceCredit(@Param("userId") Long userId);
} }

+ 27
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ExperimentInstanceStatusTask.java View File

@@ -1,11 +1,13 @@
package com.ruoyi.platform.scheduling; package com.ruoyi.platform.scheduling;


import com.ruoyi.platform.constant.Constant;
import com.ruoyi.platform.domain.Experiment; import com.ruoyi.platform.domain.Experiment;
import com.ruoyi.platform.domain.ExperimentIns; import com.ruoyi.platform.domain.ExperimentIns;
import com.ruoyi.platform.mapper.ExperimentDao; import com.ruoyi.platform.mapper.ExperimentDao;
import com.ruoyi.platform.mapper.ExperimentInsDao; import com.ruoyi.platform.mapper.ExperimentInsDao;
import com.ruoyi.platform.service.AimService; import com.ruoyi.platform.service.AimService;
import com.ruoyi.platform.service.ExperimentInsService; import com.ruoyi.platform.service.ExperimentInsService;
import com.ruoyi.platform.service.ResourceOccupyService;
import com.ruoyi.platform.utils.JacksonUtil; import com.ruoyi.platform.utils.JacksonUtil;
import com.ruoyi.platform.utils.JsonUtils; import com.ruoyi.platform.utils.JsonUtils;
import com.ruoyi.platform.vo.InsMetricInfoVo; import com.ruoyi.platform.vo.InsMetricInfoVo;
@@ -16,6 +18,8 @@ import org.springframework.stereotype.Component;


import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.*; import java.util.*;


@Component() @Component()
@@ -28,6 +32,8 @@ public class ExperimentInstanceStatusTask {
private ExperimentInsDao experimentInsDao; private ExperimentInsDao experimentInsDao;
@Resource @Resource
private AimService aimService; private AimService aimService;
@Resource
private ResourceOccupyService resourceOccupyService;


private List<Integer> experimentIds = new ArrayList<>(); private List<Integer> experimentIds = new ArrayList<>();


@@ -43,7 +49,27 @@ public class ExperimentInstanceStatusTask {
try { try {
experimentIns = experimentInsService.queryStatusFromArgo(experimentIns); experimentIns = experimentInsService.queryStatusFromArgo(experimentIns);
} catch (Exception e) { } catch (Exception e) {
experimentIns.setStatus("Failed");
experimentIns.setStatus(Constant.Failed);
// 结束扣除积分
resourceOccupyService.endDeduce(Constant.TaskType_Workflow, Long.valueOf(experimentIns.getId()), null, null);
}

// 扣除积分
if (StringUtils.isNotEmpty(experimentIns.getNodesStatus())) {
Map<String, Object> nodesStatusMap = JsonUtils.jsonToMap(experimentIns.getNodesStatus());
for (String key : nodesStatusMap.keySet()) {
Map<String, Object> value = (Map<String, Object>) nodesStatusMap.get(key);
String startedAt = (String) value.get("startedAt");
Instant instant = Instant.parse(startedAt);
Date startTime = Date.from(instant);

String finishedAt = (String) value.get("finishedAt");
if (StringUtils.isEmpty(finishedAt)) {
resourceOccupyService.deducing(Constant.TaskType_Workflow, Long.valueOf(experimentIns.getId()), key, startTime);
} else {
resourceOccupyService.endDeduce(Constant.TaskType_Workflow, Long.valueOf(experimentIns.getId()), key, startTime);
}
}
} }
//运行成功的实验实例记录指标数值 //运行成功的实验实例记录指标数值
Map<String, Object> metricRecord = JacksonUtil.parseJSONStr2Map(experimentIns.getMetricRecord()); Map<String, Object> metricRecord = JacksonUtil.parseJSONStr2Map(experimentIns.getMetricRecord());


+ 8
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/RayInsStatusTask.java View File

@@ -42,13 +42,18 @@ public class RayInsStatusTask {
//当原本状态为null或非终止态时才调用argo接口 //当原本状态为null或非终止态时才调用argo接口
try { try {
rayIns = rayInsService.queryStatusFromArgo(rayIns); rayIns = rayInsService.queryStatusFromArgo(rayIns);

// 扣除积分
if (Constant.Running.equals(rayIns.getStatus())) { if (Constant.Running.equals(rayIns.getStatus())) {
resourceOccupyService.deducing(Constant.TaskType_Ray, rayIns.getId());
} else {
resourceOccupyService.endDeduce(Constant.TaskType_Ray, rayIns.getId());
resourceOccupyService.deducing(Constant.TaskType_Ray, rayIns.getId(), null,null);
} else if (Constant.Failed.equals(rayIns.getStatus()) || Constant.Terminated.equals(rayIns.getStatus())
|| Constant.Succeeded.equals(rayIns.getStatus())) {
resourceOccupyService.endDeduce(Constant.TaskType_Ray, rayIns.getId(), null,null);
} }
} catch (Exception e) { } catch (Exception e) {
rayIns.setStatus(Constant.Failed); rayIns.setStatus(Constant.Failed);
// 结束扣除积分
resourceOccupyService.endDeduce(Constant.TaskType_Ray, rayIns.getId(), null, null);
} }
// 线程安全的添加操作 // 线程安全的添加操作
synchronized (rayIds) { synchronized (rayIds) {


+ 3
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/scheduling/ResourceOccupyTask.java View File

@@ -25,11 +25,11 @@ public class ResourceOccupyTask {
private ServiceDao serviceDao; private ServiceDao serviceDao;


// 开发环境功能扣除积分 // 开发环境功能扣除积分
@Scheduled(cron = "0 0/10 * * * ?") // 每10分钟执行一次
@Scheduled(cron = "0 0/1 * * * ?") // 每10分钟执行一次
public void devDeduceCredit() { public void devDeduceCredit() {
List<DevEnvironment> devEnvironments = devEnvironmentDao.getRunning(); List<DevEnvironment> devEnvironments = devEnvironmentDao.getRunning();
for (DevEnvironment devEnvironment : devEnvironments) { for (DevEnvironment devEnvironment : devEnvironments) {
resourceOccupyService.deducing(Constant.TaskType_Dev, Long.valueOf(devEnvironment.getId()));
resourceOccupyService.deducing(Constant.TaskType_Dev, Long.valueOf(devEnvironment.getId()), null, null);
} }
} }


@@ -38,7 +38,7 @@ public class ResourceOccupyTask {
public void serviceDeduceCredit() { public void serviceDeduceCredit() {
List<ServiceVersion> serviceVersions = serviceDao.getRunning(); List<ServiceVersion> serviceVersions = serviceDao.getRunning();
for (ServiceVersion serviceVersion : serviceVersions) { for (ServiceVersion serviceVersion : serviceVersions) {
resourceOccupyService.deducing(Constant.TaskType_Service, serviceVersion.getId());
resourceOccupyService.deducing(Constant.TaskType_Service, serviceVersion.getId(), null, null);
} }
} }
} }

+ 8
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ResourceOccupyService.java View File

@@ -4,15 +4,20 @@ import com.ruoyi.platform.domain.ResourceOccupy;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;


import java.util.Date;
import java.util.Map;

public interface ResourceOccupyService { public interface ResourceOccupyService {


Boolean haveResource(Integer computingResourceId) throws Exception; Boolean haveResource(Integer computingResourceId) throws Exception;


void startDeduce(Integer computingResourceId, String taskType, Long taskId);
void startDeduce(Integer computingResourceId, String taskType, Long taskId, String nodeId);


void endDeduce(String taskType, Long taskId);
void endDeduce(String taskType, Long taskId, String nodeId, Date nodeStartTime);


void deducing(String taskType, Long taskId);
void deducing(String taskType, Long taskId, String nodeId, Date nodeStartTime);


Page<ResourceOccupy> queryByPage(PageRequest pageRequest); Page<ResourceOccupy> queryByPage(PageRequest pageRequest);

Map<String, Double> queryCredit();
} }

+ 2
- 2
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/DevEnvironmentServiceImpl.java View File

@@ -64,11 +64,11 @@ public class DevEnvironmentServiceImpl implements DevEnvironmentService {
if (!devEnv.getStatus().equals(PodStatus.Terminated.getName()) && if (!devEnv.getStatus().equals(PodStatus.Terminated.getName()) &&
!devEnv.getStatus().equals(PodStatus.Failed.getName())) { !devEnv.getStatus().equals(PodStatus.Failed.getName())) {
PodStatusVo podStatusVo = this.jupyterService.getJupyterStatus(devEnv); PodStatusVo podStatusVo = this.jupyterService.getJupyterStatus(devEnv);
devEnv.setStatus(podStatusVo.getStatus());
devEnv.setUrl(podStatusVo.getUrl());
if(!devEnv.getStatus().equals(podStatusVo.getStatus())){ if(!devEnv.getStatus().equals(podStatusVo.getStatus())){
this.devEnvironmentDao.update(devEnv); this.devEnvironmentDao.update(devEnv);
} }
devEnv.setStatus(podStatusVo.getStatus());
devEnv.setUrl(podStatusVo.getUrl());
} }
} catch (Exception e) { } catch (Exception e) {
devEnv.setStatus(PodStatus.Unknown.getName()); devEnv.setStatus(PodStatus.Unknown.getName());


+ 7
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentInsServiceImpl.java View File

@@ -3,6 +3,7 @@ package com.ruoyi.platform.service.impl;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.security.utils.SecurityUtils; import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.constant.Constant;
import com.ruoyi.platform.domain.DatasetTempStorage; import com.ruoyi.platform.domain.DatasetTempStorage;
import com.ruoyi.platform.domain.Experiment; import com.ruoyi.platform.domain.Experiment;
import com.ruoyi.platform.domain.ExperimentIns; import com.ruoyi.platform.domain.ExperimentIns;
@@ -12,6 +13,7 @@ import com.ruoyi.platform.mapper.ExperimentDao;
import com.ruoyi.platform.mapper.ExperimentInsDao; import com.ruoyi.platform.mapper.ExperimentInsDao;
import com.ruoyi.platform.mapper.ModelDependency1Dao; import com.ruoyi.platform.mapper.ModelDependency1Dao;
import com.ruoyi.platform.service.ExperimentInsService; import com.ruoyi.platform.service.ExperimentInsService;
import com.ruoyi.platform.service.ResourceOccupyService;
import com.ruoyi.platform.utils.*; import com.ruoyi.platform.utils.*;
import com.ruoyi.platform.vo.LogRequestVo; import com.ruoyi.platform.vo.LogRequestVo;
import com.ruoyi.platform.vo.PodLogVo; import com.ruoyi.platform.vo.PodLogVo;
@@ -66,6 +68,9 @@ public class ExperimentInsServiceImpl implements ExperimentInsService {
@Resource @Resource
private DatasetTempStorageDao datasetTempStorageDao; private DatasetTempStorageDao datasetTempStorageDao;


@Resource
private ResourceOccupyService resourceOccupyService;

private final MinioUtil minioUtil; private final MinioUtil minioUtil;


public ExperimentInsServiceImpl(MinioUtil minioUtil) { public ExperimentInsServiceImpl(MinioUtil minioUtil) {
@@ -410,6 +415,8 @@ public class ExperimentInsServiceImpl implements ExperimentInsService {
//修改实验状态 //修改实验状态
updateExperimentStatus(experimentIns.getExperimentId()); updateExperimentStatus(experimentIns.getExperimentId());


// 结束扣除积分
resourceOccupyService.endDeduce(Constant.TaskType_Workflow, Long.valueOf(experimentIns.getId()), null, null);
return true; return true;
} else { } else {
throw new Exception("终止错误"); throw new Exception("终止错误");


+ 16
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ExperimentServiceImpl.java View File

@@ -10,7 +10,6 @@ import com.ruoyi.platform.domain.*;
import com.ruoyi.platform.domain.dependencydomain.ProjectDepency; import com.ruoyi.platform.domain.dependencydomain.ProjectDepency;
import com.ruoyi.platform.domain.dependencydomain.TrainTaskDepency; import com.ruoyi.platform.domain.dependencydomain.TrainTaskDepency;
import com.ruoyi.platform.mapper.ExperimentDao; import com.ruoyi.platform.mapper.ExperimentDao;
import com.ruoyi.platform.mapper.ExperimentInsDao;
import com.ruoyi.platform.mapper.ModelDependency1Dao; import com.ruoyi.platform.mapper.ModelDependency1Dao;
import com.ruoyi.platform.service.*; import com.ruoyi.platform.service.*;
import com.ruoyi.platform.utils.HttpUtils; import com.ruoyi.platform.utils.HttpUtils;
@@ -62,6 +61,9 @@ public class ExperimentServiceImpl implements ExperimentService {
@Lazy @Lazy
private ExperimentInsService experimentInsService; private ExperimentInsService experimentInsService;


@Resource
private ResourceOccupyService resourceOccupyService;

@Resource @Resource
private ModelDependency1Dao modelDependency1Dao; private ModelDependency1Dao modelDependency1Dao;


@@ -233,6 +235,14 @@ public class ExperimentServiceImpl implements ExperimentService {
} }
Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes); Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes);


// 判断积分和资源是否足够
Map<String, Map<String, Object>> resourceInfo = (Map<String, Map<String, Object>>) converMap.get("resource_info");
for (Map.Entry<String, Map<String, Object>> entry : resourceInfo.entrySet()) {
Map<String, Object> node = entry.getValue();
resourceOccupyService.haveResource((Integer) node.get("computing_resource_id"));
}


// 组装运行接口json // 组装运行接口json
Map<String, Object> runReqMap = new HashMap<>(); Map<String, Object> runReqMap = new HashMap<>();
runReqMap.put("data", converMap.get("data")); runReqMap.put("data", converMap.get("data"));
@@ -300,6 +310,11 @@ public class ExperimentServiceImpl implements ExperimentService {
insertDatasetTempStorage(datasetDependendcy, trainInfo, experiment.getId(), insert.getId(), experiment.getName(), experiment.getWorkflowId()); insertDatasetTempStorage(datasetDependendcy, trainInfo, experiment.getId(), insert.getId(), experiment.getName(), experiment.getWorkflowId());
} }


// 记录开始扣积分
for (Map.Entry<String, Map<String, Object>> entry : resourceInfo.entrySet()) {
Map<String, Object> node = entry.getValue();
resourceOccupyService.startDeduce((Integer) node.get("computing_resource_id"), Constant.TaskType_Workflow, Long.valueOf(insert.getId()), entry.getKey());
}


} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);


+ 3
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java View File

@@ -133,9 +133,6 @@ public class JupyterServiceImpl implements JupyterService {
return "pod不存在!"; return "pod不存在!";
} }


// 结束扣积分
resourceOccupyService.endDeduce(Constant.TaskType_Dev, Long.valueOf(id));

// 使用 Kubernetes API 删除 Pod // 使用 Kubernetes API 删除 Pod
String deleteResult = k8sClientUtil.deletePod(podName, namespace); String deleteResult = k8sClientUtil.deletePod(podName, namespace);
// 删除service // 删除service
@@ -143,6 +140,9 @@ public class JupyterServiceImpl implements JupyterService {


devEnvironment.setStatus(Constant.Terminated); devEnvironment.setStatus(Constant.Terminated);
this.devEnvironmentService.update(devEnvironment); this.devEnvironmentService.update(devEnvironment);

// 结束扣积分
resourceOccupyService.endDeduce(Constant.TaskType_Dev, Long.valueOf(id), null, null);
return deleteResult + ",编辑器已停止"; return deleteResult + ",编辑器已停止";


} }


+ 1
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/RayInsServiceImpl.java View File

@@ -168,7 +168,7 @@ public class RayInsServiceImpl implements RayInsService {
rayInsDao.update(ins); rayInsDao.update(ins);
updateRayStatus(rayIns.getRayId()); updateRayStatus(rayIns.getRayId());
// 结束扣积分 // 结束扣积分
resourceOccupyService.endDeduce(Constant.TaskType_Ray, id);
resourceOccupyService.endDeduce(Constant.TaskType_Ray, id, null, null);
return true; return true;
} else { } else {
return false; return false;


+ 2
- 1
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/RayServiceImpl.java View File

@@ -205,7 +205,8 @@ public class RayServiceImpl implements RayService {
rayIns.setResultPath(outputPath); rayIns.setResultPath(outputPath);
rayInsDao.insert(rayIns); rayInsDao.insert(rayIns);
rayInsService.updateRayStatus(id); rayInsService.updateRayStatus(id);
resourceOccupyService.startDeduce(ray.getComputingResourceId(), Constant.TaskType_Ray, rayIns.getId());
// 记录开始扣除积分
resourceOccupyService.startDeduce(ray.getComputingResourceId(), Constant.TaskType_Ray, rayIns.getId(), null);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }


+ 51
- 22
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ResourceOccupyServiceImpl.java View File

@@ -12,9 +12,13 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service("resourceOccupyService") @Service("resourceOccupyService")
public class ResourceOccupyServiceImpl implements ResourceOccupyService { public class ResourceOccupyServiceImpl implements ResourceOccupyService {
@@ -48,15 +52,18 @@ public class ResourceOccupyServiceImpl implements ResourceOccupyService {
} }


@Override @Override
public void startDeduce(Integer computingResourceId, String taskType, Long taskId) {
@Transactional
public void startDeduce(Integer computingResourceId, String taskType, Long taskId, String nodeId) {
ResourceOccupy resourceOccupy = new ResourceOccupy(); ResourceOccupy resourceOccupy = new ResourceOccupy();
ComputingResource computingResource = computingResourceDao.queryById(computingResourceId); ComputingResource computingResource = computingResourceDao.queryById(computingResourceId);
resourceOccupy.setComputingResourceId(computingResourceId); resourceOccupy.setComputingResourceId(computingResourceId);
LoginUser loginUser = SecurityUtils.getLoginUser(); LoginUser loginUser = SecurityUtils.getLoginUser();
resourceOccupy.setUserId(loginUser.getUserid()); resourceOccupy.setUserId(loginUser.getUserid());
resourceOccupy.setCreditPerHour(computingResource.getCreditPerHour()); resourceOccupy.setCreditPerHour(computingResource.getCreditPerHour());
resourceOccupy.setDescription(computingResource.getDescription());
resourceOccupy.setTaskType(taskType); resourceOccupy.setTaskType(taskType);
resourceOccupy.setTaskId(taskId); resourceOccupy.setTaskId(taskId);
resourceOccupy.setNodeId(nodeId);
resourceOccupyDao.save(resourceOccupy); resourceOccupyDao.save(resourceOccupy);


if (Constant.Computing_Resource_GPU.equals(computingResource.getComputingResource())) { if (Constant.Computing_Resource_GPU.equals(computingResource.getComputingResource())) {
@@ -67,36 +74,58 @@ public class ResourceOccupyServiceImpl implements ResourceOccupyService {
} }


@Override @Override
public void endDeduce(String taskType, Long taskId) {
ResourceOccupy resourceOccupy = resourceOccupyDao.getResourceOccupyByTask(taskType, taskId);
deducing(taskType, taskId);
resourceOccupy.setState(Constant.State_invalid);
resourceOccupyDao.edit(resourceOccupy);
@Transactional
public void endDeduce(String taskType, Long taskId, String nodeId, Date nodeStartTime) {
List<ResourceOccupy> resourceOccupys = resourceOccupyDao.getResourceOccupyByTask(taskType, taskId, nodeId);
for (ResourceOccupy resourceOccupy : resourceOccupys) {
deducing(taskType, taskId, nodeId, nodeStartTime);
resourceOccupy.setState(Constant.State_invalid);
resourceOccupyDao.edit(resourceOccupy);


ComputingResource computingResource = computingResourceDao.queryById(resourceOccupy.getComputingResourceId());
if (Constant.Computing_Resource_GPU.equals(computingResource.getComputingResource())) {
resourceOccupyDao.updateUnUsed(computingResource.getResourceId(), computingResource.getGpuNums());
} else {
resourceOccupyDao.updateUnUsed(computingResource.getResourceId(), computingResource.getCpuCores());
ComputingResource computingResource = computingResourceDao.queryById(resourceOccupy.getComputingResourceId());
if (Constant.Computing_Resource_GPU.equals(computingResource.getComputingResource())) {
resourceOccupyDao.updateUnUsed(computingResource.getResourceId(), computingResource.getGpuNums());
} else {
resourceOccupyDao.updateUnUsed(computingResource.getResourceId(), computingResource.getCpuCores());
}
} }
} }


@Override @Override
public void deducing(String taskType, Long taskId) {
ResourceOccupy resourceOccupy = resourceOccupyDao.getResourceOccupyByTask(taskType, taskId);
long timeDifferenceMillis = new Date().getTime() - resourceOccupy.getDeduceLastTime().getTime();
Float hours = (float) (timeDifferenceMillis / (1000 * 60 * 60));

float deduceCredit = resourceOccupy.getCreditPerHour() * hours;
resourceOccupyDao.deduceCredit(deduceCredit, resourceOccupy.getUserId());
@Transactional
public void deducing(String taskType, Long taskId, String nodeId, Date nodeStartTime) {
List<ResourceOccupy> resourceOccupys = resourceOccupyDao.getResourceOccupyByTask(taskType, taskId, nodeId);
for (ResourceOccupy resourceOccupy : resourceOccupys) {
Date now = new Date();
long timeDifferenceMillis;
if (nodeStartTime != null && resourceOccupy.getDeduceLastTime().before(nodeStartTime)) {
timeDifferenceMillis = now.getTime() - nodeStartTime.getTime();
} else {
timeDifferenceMillis = now.getTime() - resourceOccupy.getDeduceLastTime().getTime();
}
Double hours = (double) timeDifferenceMillis / (1000 * 60 * 60);
Double deduceCredit = resourceOccupy.getCreditPerHour() * hours;
resourceOccupyDao.deduceCredit(deduceCredit, resourceOccupy.getUserId());


resourceOccupy.setDeduceLastTime(new Date());
resourceOccupyDao.edit(resourceOccupy);
resourceOccupy.setDeduceCredit(resourceOccupy.getDeduceCredit() + deduceCredit);
resourceOccupy.setDeduceLastTime(now);
resourceOccupyDao.edit(resourceOccupy);
}
} }


@Override @Override
public Page<ResourceOccupy> queryByPage(PageRequest pageRequest) { public Page<ResourceOccupy> queryByPage(PageRequest pageRequest) {
long total = resourceOccupyDao.count();
return new PageImpl<>(resourceOccupyDao.queryByPage(pageRequest), pageRequest, total);
long total = resourceOccupyDao.count(SecurityUtils.getLoginUser().getUserid());
return new PageImpl<>(resourceOccupyDao.queryByPage(SecurityUtils.getLoginUser().getUserid(), pageRequest), pageRequest, total);
}

@Override
public Map<String, Double> queryCredit() {
Double userCredit = resourceOccupyDao.getUserCredit(SecurityUtils.getLoginUser().getUserid());
Double deduceCredit = resourceOccupyDao.getDeduceCredit(SecurityUtils.getLoginUser().getUserid());
HashMap<String, Double> result = new HashMap<>();
result.put("userCredit", userCredit);
result.put("deduceCredit", deduceCredit);
return result;
} }
} }

+ 5
- 3
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ServiceServiceImpl.java View File

@@ -252,19 +252,21 @@ public class ServiceServiceImpl implements ServiceService {
paramMap.put("service_type", service.getServiceType()); paramMap.put("service_type", service.getServiceType());
paramMap.put("deploy_type", serviceVersion.getDeployType()); paramMap.put("deploy_type", serviceVersion.getDeployType());


// 记录开始扣积分
// 判断是否有资源
if (resourceOccupyService.haveResource(serviceVersion.getComputingResourceId())) { if (resourceOccupyService.haveResource(serviceVersion.getComputingResourceId())) {
String req = HttpUtils.sendPost(argoUrl + modelService + "/create", JSON.toJSONString(paramMap)); String req = HttpUtils.sendPost(argoUrl + modelService + "/create", JSON.toJSONString(paramMap));
if (StringUtils.isNotEmpty(req)) { if (StringUtils.isNotEmpty(req)) {
Map<String, Object> reqMap = JacksonUtil.parseJSONStr2Map(req); Map<String, Object> reqMap = JacksonUtil.parseJSONStr2Map(req);
if ((Integer) reqMap.get("code") == 200) { if ((Integer) reqMap.get("code") == 200) {
resourceOccupyService.startDeduce(serviceVersion.getComputingResourceId(), Constant.TaskType_Service, serviceVersion.getId());
Map<String, String> data = (Map<String, String>) reqMap.get("data"); Map<String, String> data = (Map<String, String>) reqMap.get("data");
serviceVersion.setUrl(data.get("url")); serviceVersion.setUrl(data.get("url"));
serviceVersion.setDeploymentName(data.get("deployment_name")); serviceVersion.setDeploymentName(data.get("deployment_name"));
serviceVersion.setSvcName(data.get("svc_name")); serviceVersion.setSvcName(data.get("svc_name"));
serviceVersion.setRunState(Constant.Pending); serviceVersion.setRunState(Constant.Pending);
serviceDao.updateServiceVersion(serviceVersion); serviceDao.updateServiceVersion(serviceVersion);

// 记录开始扣积分
resourceOccupyService.startDeduce(serviceVersion.getComputingResourceId(), Constant.TaskType_Service, serviceVersion.getId(), null);
return "启动成功"; return "启动成功";
} else { } else {
throw new RuntimeException("启动失败"); throw new RuntimeException("启动失败");
@@ -286,7 +288,7 @@ public class ServiceServiceImpl implements ServiceService {
serviceVersion.setRunState(Constant.Stopped); serviceVersion.setRunState(Constant.Stopped);
serviceDao.updateServiceVersion(serviceVersion); serviceDao.updateServiceVersion(serviceVersion);
// 结束扣积分 // 结束扣积分
resourceOccupyService.endDeduce(Constant.TaskType_Service, id);
resourceOccupyService.endDeduce(Constant.TaskType_Service, id, null, null);
return "停止成功"; return "停止成功";
} else { } else {
throw new RuntimeException("停止失败"); throw new RuntimeException("停止失败");


+ 3
- 6
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java View File

@@ -1,6 +1,5 @@
package com.ruoyi.platform.utils; package com.ruoyi.platform.utils;


import com.alibaba.fastjson2.JSON;
import com.ruoyi.platform.constant.Constant; import com.ruoyi.platform.constant.Constant;
import com.ruoyi.platform.domain.ComputingResource; import com.ruoyi.platform.domain.ComputingResource;
import com.ruoyi.platform.domain.DevEnvironment; import com.ruoyi.platform.domain.DevEnvironment;
@@ -11,7 +10,6 @@ import io.kubernetes.client.custom.IntOrString;
import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.custom.Quantity;
import io.kubernetes.client.openapi.ApiClient; import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api; import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.*; import io.kubernetes.client.openapi.models.*;
import io.kubernetes.client.util.ClientBuilder; import io.kubernetes.client.util.ClientBuilder;
@@ -19,7 +17,6 @@ import io.kubernetes.client.util.Config;
import io.kubernetes.client.util.credentials.AccessTokenAuthentication; import io.kubernetes.client.util.credentials.AccessTokenAuthentication;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -519,7 +516,8 @@ public class K8sClientUtil {
pod = api.createNamespacedPod(namespace, pod, null, null, null); pod = api.createNamespacedPod(namespace, pod, null, null, null);
String nodeName = getNodeName(podName, namespace); String nodeName = getNodeName(podName, namespace);


resourceOccupyService.startDeduce(devEnvironment.getComputingResourceId(), Constant.TaskType_Dev, Long.valueOf(devEnvironment.getId()));
// 记录开始扣除积分
resourceOccupyService.startDeduce(devEnvironment.getComputingResourceId(), Constant.TaskType_Dev, Long.valueOf(devEnvironment.getId()), null);
} }
} catch (ApiException e) { } catch (ApiException e) {
throw new RuntimeException("创建pod异常:" + e.getResponseBody()); throw new RuntimeException("创建pod异常:" + e.getResponseBody());
@@ -699,8 +697,7 @@ public class K8sClientUtil {
ComputingResource computingResource = computingResourceDao.queryById(computingResourceId); ComputingResource computingResource = computingResourceDao.queryById(computingResourceId);


//配置pod资源 //配置pod资源
String memory = computingResource.getMemoryGb().toString();
memory = memory.substring(0, memory.length() - 1).concat("i");
String memory = computingResource.getMemoryGb().toString().concat("Gi");
HashMap<String, Quantity> limitMap = new HashMap<>(); HashMap<String, Quantity> limitMap = new HashMap<>();
if (computingResource.getGpuNums() != null && computingResource.getGpuNums() != 0) { if (computingResource.getGpuNums() != null && computingResource.getGpuNums() != 0) {
limitMap.put("nvidia.com/gpu", new Quantity(String.valueOf(computingResource.getGpuNums()))); limitMap.put("nvidia.com/gpu", new Quantity(String.valueOf(computingResource.getGpuNums())));


+ 37
- 6
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/ResourceOccupy.xml View File

@@ -2,8 +2,11 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.platform.mapper.ResourceOccupyDao"> <mapper namespace="com.ruoyi.platform.mapper.ResourceOccupyDao">
<insert id="save"> <insert id="save">
insert into resource_occupy (user_id, computing_resource_id, credit_per_hour)
values (#{resourceOccupy.userId}, #{resourceOccupy.computingResourceId}, #{resourceOccupy.creditPerHour})
insert into resource_occupy (user_id, computing_resource_id, credit_per_hour, description, task_type, task_id,
node_id)
values (#{resourceOccupy.userId}, #{resourceOccupy.computingResourceId}, #{resourceOccupy.creditPerHour},
#{resourceOccupy.description}, #{resourceOccupy.taskType}, #{resourceOccupy.taskId},
#{resourceOccupy.nodeId})
</insert> </insert>


<update id="edit"> <update id="edit">
@@ -15,6 +18,9 @@
<if test="resourceOccupy.deduceLastTime != null"> <if test="resourceOccupy.deduceLastTime != null">
deduce_last_time = #{resourceOccupy.deduceLastTime}, deduce_last_time = #{resourceOccupy.deduceLastTime},
</if> </if>
<if test="resourceOccupy.deduceCredit != null">
deduce_credit = #{resourceOccupy.deduceCredit},
</if>
</set> </set>
where id = #{resourceOccupy.id} where id = #{resourceOccupy.id}
</update> </update>
@@ -46,17 +52,42 @@
<select id="getResourceOccupyByTask" resultType="com.ruoyi.platform.domain.ResourceOccupy"> <select id="getResourceOccupyByTask" resultType="com.ruoyi.platform.domain.ResourceOccupy">
select * select *
from resource_occupy from resource_occupy
where task_type = #{task_type}
and task_id = #{task_id}
where task_type = #{taskType}
and task_id = #{taskId}
<if test="nodeId != null and nodeId !=''">
and node_id = #{nodeId}
</if>
and state = 1
</select> </select>


<select id="count" resultType="java.lang.Long"> <select id="count" resultType="java.lang.Long">
select count(1) resource_occupy
select count(1)
from resource_occupy
where user_id = #{userId}
</select> </select>


<select id="queryByPage" resultType="com.ruoyi.platform.domain.ResourceOccupy"> <select id="queryByPage" resultType="com.ruoyi.platform.domain.ResourceOccupy">
select *
select id,
user_id,
description,
credit_per_hour,
TRUNCATE(deduce_credit, 1) as deduce_credit,
start_time,
state
from resource_occupy from resource_occupy
where user_id = #{userId}
order by start_time desc limit #{pageable.offset}, #{pageable.pageSize} order by start_time desc limit #{pageable.offset}, #{pageable.pageSize}
</select> </select>

<select id="getUserCredit" resultType="java.lang.Double">
select TRUNCATE(credit, 1) as credit
from sys_user
where user_id = #{userId}
</select>

<select id="getDeduceCredit" resultType="java.lang.Double">
select TRUNCATE(sum(deduce_credit), 1) as deduce_credit
from resource_occupy
where user_id = #{userId}
</select>
</mapper> </mapper>

+ 4
- 4
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml View File

@@ -60,7 +60,7 @@
u.password, u.password,
u.git_link_username, u.git_link_username,
u.git_link_password, u.git_link_password,
u.credit,
TRUNCATE(u.credit, 1) as credit,
u.sex, u.sex,
u.status, u.status,
u.del_flag, u.del_flag,
@@ -90,7 +90,7 @@


<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult"> <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status,
u.git_link_username, u.credit,
u.git_link_username, TRUNCATE(u.credit, 1) as credit,
u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader
from sys_user u from sys_user u
left join sys_dept d on u.dept_id = d.dept_id left join sys_dept d on u.dept_id = d.dept_id
@@ -125,7 +125,7 @@
</select> </select>


<select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult"> <select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult">
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time, u.credit, u.git_link_username
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time, TRUNCATE(u.credit, 1) as credit, u.git_link_username
from sys_user u from sys_user u
left join sys_dept d on u.dept_id = d.dept_id left join sys_dept d on u.dept_id = d.dept_id
left join sys_user_role ur on u.user_id = ur.user_id left join sys_user_role ur on u.user_id = ur.user_id
@@ -142,7 +142,7 @@
</select> </select>


<select id="selectUnallocatedList" parameterType="SysUser" resultMap="SysUserResult"> <select id="selectUnallocatedList" parameterType="SysUser" resultMap="SysUserResult">
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time , u.credit, u.git_link_username
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time , TRUNCATE(u.credit, 1) as credit, u.git_link_username
from sys_user u from sys_user u
left join sys_dept d on u.dept_id = d.dept_id left join sys_dept d on u.dept_id = d.dept_id
left join sys_user_role ur on u.user_id = ur.user_id left join sys_user_role ur on u.user_id = ur.user_id


Loading…
Cancel
Save