| @@ -1 +1 @@ | |||
| v18.16.0 | |||
| v18.20.7 | |||
| @@ -30,35 +30,42 @@ function CodeSelect({ | |||
| onChange, | |||
| ...rest | |||
| }: CodeSelectProps) { | |||
| // 选择代码配置 | |||
| const selectResource = () => { | |||
| const { close } = openAntdModal(CodeSelectorModal, { | |||
| onOk: (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 = { | |||
| id, | |||
| name: code_repo_name, | |||
| code_path: git_url, | |||
| branch: git_branch, | |||
| username: git_user_name, | |||
| password: git_password, | |||
| ssh_private_key: ssh_key, | |||
| }; | |||
| const jsonObjStr = JSON.stringify(jsonObj); | |||
| const showValue = code_repo_name; | |||
| onChange?.({ | |||
| value: jsonObjStr, | |||
| showValue, | |||
| showValue: code_repo_name, | |||
| fromSelect: true, | |||
| ...jsonObj, | |||
| }); | |||
| } else { | |||
| onChange?.({ | |||
| value: undefined, | |||
| showValue: undefined, | |||
| fromSelect: false, | |||
| }); | |||
| onChange?.(undefined); | |||
| } | |||
| close(); | |||
| }, | |||
| }); | |||
| }; | |||
| // 删除 | |||
| const handleRemove = () => { | |||
| onChange?.(undefined); | |||
| }; | |||
| return ( | |||
| <div className={classNames('kf-code-select', className)} style={style}> | |||
| <ParameterInput | |||
| @@ -68,6 +75,7 @@ function CodeSelect({ | |||
| value={value} | |||
| onChange={onChange} | |||
| onClick={selectResource} | |||
| onRemove={handleRemove} | |||
| ></ParameterInput> | |||
| <Button | |||
| className="kf-code-select__button" | |||
| @@ -6,7 +6,7 @@ | |||
| import { CommonTabKeys } from '@/enums'; | |||
| import { CloseOutlined } from '@ant-design/icons'; | |||
| import { Form, Input } from 'antd'; | |||
| import { ConfigProvider, Form, Input } from 'antd'; | |||
| import { RuleObject } from 'antd/es/form'; | |||
| import classNames from 'classnames'; | |||
| import './index.less'; | |||
| @@ -67,7 +67,7 @@ function ParameterInput({ | |||
| allowClear, | |||
| className, | |||
| style, | |||
| size = 'middle', | |||
| size, | |||
| disabled = false, | |||
| id, | |||
| ...rest | |||
| @@ -81,10 +81,17 @@ function ParameterInput({ | |||
| const placeholder = valueObj?.placeholder || rest?.placeholder; | |||
| const InputComponent = textArea ? Input.TextArea : Input; | |||
| const { status } = Form.Item.useStatus(); | |||
| const { componentSize } = ConfigProvider.useConfig(); | |||
| const mySize = size || componentSize; | |||
| // 删除 | |||
| const handleRemove = (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => { | |||
| e.stopPropagation(); | |||
| if (onRemove) { | |||
| onRemove(); | |||
| return; | |||
| } | |||
| onChange?.({ | |||
| ...valueObj, | |||
| value: undefined, | |||
| @@ -94,7 +101,6 @@ function ParameterInput({ | |||
| expandedKeys: [], | |||
| checkedKeys: [], | |||
| }); | |||
| onRemove?.(); | |||
| }; | |||
| return ( | |||
| @@ -104,8 +110,8 @@ function ParameterInput({ | |||
| id={id} | |||
| className={classNames( | |||
| '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 }, | |||
| className, | |||
| )} | |||
| @@ -128,7 +134,7 @@ function ParameterInput({ | |||
| <InputComponent | |||
| {...rest} | |||
| id={id} | |||
| size={size} | |||
| size={mySize} | |||
| className={className} | |||
| style={style} | |||
| placeholder={placeholder} | |||
| @@ -1,21 +1,10 @@ | |||
| import { filterResourceStandard, resourceFieldNames } from '@/hooks/resource'; | |||
| import { ServiceData } from '@/pages/ModelDeployment/types'; | |||
| import { getDatasetList, getModelList } from '@/services/dataset/index.js'; | |||
| import { getServiceListReq } from '@/services/modelDeployment'; | |||
| import { getComputingResourceReq } from '@/services/pipeline'; | |||
| import { ComputingResource } from '@/types'; | |||
| import { type SelectProps } from 'antd'; | |||
| 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 | |||
| const convertId = (item: any) => ({ | |||
| ...item, | |||
| @@ -86,17 +75,10 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = { | |||
| }, | |||
| resource: { | |||
| 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'], | |||
| }, | |||
| }; | |||
| @@ -4,6 +4,7 @@ | |||
| * @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务 | |||
| */ | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Select, type SelectProps } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| @@ -20,7 +21,7 @@ export interface ParameterSelectProps extends SelectProps { | |||
| dataType: 'dataset' | 'model' | 'service' | 'resource'; | |||
| /** 是否只是展示信息 */ | |||
| display?: boolean; | |||
| /** 值 */ | |||
| /** 值,支持对象,对象必须包含 value */ | |||
| value?: string | ParameterSelectObject; | |||
| /** 修改后回调 */ | |||
| onChange?: (value: string | ParameterSelectObject) => void; | |||
| @@ -34,9 +35,10 @@ function ParameterSelect({ | |||
| onChange, | |||
| ...rest | |||
| }: ParameterSelectProps) { | |||
| const [options, setOptions] = useState([]); | |||
| const [options, setOptions] = useState<SelectProps['options']>([]); | |||
| const propsConfig = paramSelectConfig[dataType]; | |||
| const valueText = typeof value === 'object' && value !== null ? value.value : value; | |||
| const [resourceStandardList] = useComputingResource(); | |||
| useEffect(() => { | |||
| // 获取下拉数据 | |||
| @@ -54,6 +56,8 @@ function ParameterSelect({ | |||
| getSelectOptions(); | |||
| }, [propsConfig]); | |||
| const selectOptions = dataType === 'resource' ? resourceStandardList : options; | |||
| const handleChange = (text: string) => { | |||
| if (typeof value === 'object' && value !== null) { | |||
| onChange?.({ | |||
| @@ -71,7 +75,7 @@ function ParameterSelect({ | |||
| <FormInfo | |||
| select | |||
| value={valueText} | |||
| options={options} | |||
| options={selectOptions} | |||
| fieldNames={propsConfig?.fieldNames} | |||
| ></FormInfo> | |||
| ); | |||
| @@ -81,7 +85,7 @@ function ParameterSelect({ | |||
| <Select | |||
| {...rest} | |||
| filterOption={propsConfig?.filterOption} | |||
| options={options} | |||
| options={selectOptions} | |||
| fieldNames={propsConfig?.fieldNames} | |||
| optionFilterProp={propsConfig?.optionFilterProp} | |||
| value={valueText} | |||
| @@ -11,10 +11,9 @@ import ResourceSelectorModal, { | |||
| selectorTypeConfig, | |||
| } from '@/components/ResourceSelectorModal'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { Button } from 'antd'; | |||
| import { Button, ConfigProvider } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import ParameterInput, { type ParameterInputProps } from '../ParameterInput'; | |||
| import './index.less'; | |||
| @@ -46,43 +45,40 @@ function ResourceSelect({ | |||
| onChange, | |||
| ...rest | |||
| }: 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 resource = selectedResource; | |||
| const { close } = openAntdModal(ResourceSelectorModal, { | |||
| 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) => { | |||
| setSelectedResource(res); | |||
| if (res) { | |||
| const { activeTab, id, name, version, path, identifier, owner } = res; | |||
| if (type === ResourceSelectorType.Mirror) { | |||
| @@ -116,32 +112,32 @@ function ResourceSelect({ | |||
| }); | |||
| } | |||
| } else { | |||
| onChange?.({ | |||
| value: undefined, | |||
| showValue: undefined, | |||
| fromSelect: false, | |||
| activeTab: undefined, | |||
| }); | |||
| onChange?.(undefined); | |||
| } | |||
| close(); | |||
| }, | |||
| }); | |||
| }; | |||
| // 删除 | |||
| const handleRemove = () => { | |||
| onChange?.(undefined); | |||
| }; | |||
| return ( | |||
| <div className={classNames('kf-resource-select', className)} style={style}> | |||
| <ParameterInput | |||
| {...rest} | |||
| disabled={disabled} | |||
| value={value} | |||
| size={size} | |||
| size={mySize} | |||
| onChange={onChange} | |||
| onRemove={() => setSelectedResource(undefined)} | |||
| onRemove={handleRemove} | |||
| onClick={selectResource} | |||
| ></ParameterInput> | |||
| <Button | |||
| className="kf-resource-select__button" | |||
| size={size} | |||
| size={mySize} | |||
| type="link" | |||
| icon={getSelectBtnIcon(type)} | |||
| disabled={disabled} | |||
| @@ -12,6 +12,22 @@ import { useCallback, useEffect, useState } from 'react'; | |||
| 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() { | |||
| const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]); | |||
| @@ -25,7 +41,7 @@ export function useComputingResource() { | |||
| resource_type: '', | |||
| }; | |||
| 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); | |||
| 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 | |||
| const getDescription = useCallback( | |||
| (standard?: string) => { | |||
| if (!standard) { | |||
| (id?: string | number) => { | |||
| if (!id) { | |||
| return undefined; | |||
| } | |||
| return resourceStandardList.find((item) => item.standard === standard)?.description; | |||
| return resourceStandardList.find((item) => Number(item.id) === Number(id))?.description; | |||
| }, | |||
| [resourceStandardList], | |||
| ); | |||
| return [resourceStandardList, filterResourceStandard, getDescription] as const; | |||
| return [resourceStandardList, getDescription] as const; | |||
| } | |||
| @@ -40,7 +40,7 @@ function AutoMLInstance() { | |||
| closeSSE(); | |||
| }; | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, []); | |||
| }, [instanceId]); | |||
| // 获取实验实例详情 | |||
| const getExperimentInsInfo = async (isStatusDetermined: boolean) => { | |||
| @@ -30,7 +30,6 @@ function DatasetConfig() { | |||
| type={ResourceSelectorType.Dataset} | |||
| placeholder="请选择数据集" | |||
| canInput={false} | |||
| size="large" | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| @@ -431,7 +431,12 @@ function ExecuteConfig() { | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item label="是否打乱" name="shuffle" tooltip="拆分数据前是否打乱顺序"> | |||
| <Form.Item | |||
| label="是否打乱" | |||
| name="shuffle" | |||
| tooltip="拆分数据前是否打乱顺序" | |||
| valuePropName="checked" | |||
| > | |||
| <Switch /> | |||
| </Form.Item> | |||
| </Col> | |||
| @@ -6,17 +6,17 @@ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import KFRadio, { type KFRadioItem } from '@/components/KFRadio'; | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import ParameterSelect from '@/components/ParameterSelect'; | |||
| import ResourceSelect, { | |||
| requiredValidator, | |||
| ResourceSelectorType, | |||
| type ParameterInputObject, | |||
| } from '@/components/ResourceSelect'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { createEditorReq } from '@/services/developmentEnvironment'; | |||
| import { to } from '@/utils/promise'; | |||
| 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 styles from './index.less'; | |||
| @@ -51,7 +51,6 @@ function EditorCreate() { | |||
| const navigate = useNavigate(); | |||
| const [form] = Form.useForm(); | |||
| const { message } = App.useApp(); | |||
| const [resourceStandardList, filterResourceStandard] = useComputingResource(); | |||
| // 创建编辑器 | |||
| const createEditor = async (formData: FormData) => { | |||
| @@ -62,8 +61,8 @@ function EditorCreate() { | |||
| const params = { | |||
| ...omit(formData, ['image', 'model', 'dataset']), | |||
| 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)); | |||
| 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> | |||
| </Col> | |||
| </Row> | |||
| @@ -181,7 +171,6 @@ function EditorCreate() { | |||
| type={ResourceSelectorType.Mirror} | |||
| placeholder="请选择镜像" | |||
| canInput={false} | |||
| size="large" | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| @@ -193,7 +182,6 @@ function EditorCreate() { | |||
| type={ResourceSelectorType.Model} | |||
| placeholder="请选择模型" | |||
| canInput={false} | |||
| size="large" | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| @@ -205,7 +193,6 @@ function EditorCreate() { | |||
| type={ResourceSelectorType.Dataset} | |||
| placeholder="请选择数据集" | |||
| canInput={false} | |||
| size="large" | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| @@ -1,7 +1,6 @@ | |||
| import FormInfo from '@/components/FormInfo'; | |||
| import ParameterSelect from '@/components/ParameterSelect'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { PipelineNodeModelSerialize } from '@/types'; | |||
| import { Form } from 'antd'; | |||
| import styles from './index.less'; | |||
| @@ -11,8 +10,6 @@ type ExperimentParameterProps = { | |||
| }; | |||
| function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||
| const [resourceStandardList] = useComputingResource(); // 资源规模 | |||
| // 控制策略 | |||
| const controlStrategyList = Object.entries(nodeData.control_strategy ?? {}).map( | |||
| ([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 label="挂载路径" name="mount_path"> | |||
| <FormInfo /> | |||
| @@ -33,7 +33,7 @@ function LogGroup({ | |||
| status, | |||
| }: LogGroupProps) { | |||
| const [collapse, setCollapse] = useState(true); | |||
| const [logList, setLogList, logListRef] = useStateRef<Log[]>([]); | |||
| const [logList, setLogList] = useState<Log[]>([]); | |||
| const [completed, setCompleted] = useState(false); | |||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | |||
| const [_isMouseDown, setIsMouseDown, isMouseDownRef] = useStateRef(false); | |||
| @@ -126,6 +126,14 @@ function LogGroup({ | |||
| socketRef.current = socket; | |||
| }; | |||
| // 关闭 socket | |||
| const closeSocket = () => { | |||
| if (socketRef.current) { | |||
| socketRef.current.close(1000, 'completed'); | |||
| socketRef.current = undefined; | |||
| } | |||
| }; | |||
| if (status === ExperimentStatus.Running) { | |||
| setupSockect(); | |||
| } | |||
| @@ -133,7 +141,7 @@ function LogGroup({ | |||
| return () => { | |||
| closeSocket(); | |||
| }; | |||
| }, [status, start_time, pod_name, isMouseDownRef, setLogList]); | |||
| }, [status, start_time, pod_name, isMouseDownRef]); | |||
| // 鼠标拖到中不滚动到底部 | |||
| useEffect(() => { | |||
| @@ -153,8 +161,8 @@ function LogGroup({ | |||
| // 请求日志 | |||
| 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 = { | |||
| pod_name, | |||
| start_time: startTime, | |||
| @@ -201,14 +209,6 @@ function LogGroup({ | |||
| requestExperimentPodsLog(); | |||
| }; | |||
| // 关闭 socket | |||
| const closeSocket = () => { | |||
| if (socketRef.current) { | |||
| socketRef.current.close(1000, 'completed'); | |||
| socketRef.current = undefined; | |||
| } | |||
| }; | |||
| // 滚动到底部 | |||
| const scrollToBottom = (smooth: boolean = true) => { | |||
| // const element = document.getElementById(listId); | |||
| @@ -277,7 +277,6 @@ function Experiment() { | |||
| current, | |||
| pageSize, | |||
| }); | |||
| getExperimentList(); | |||
| }; | |||
| // 运行实验 | |||
| const runExperiment = async (id) => { | |||
| @@ -44,7 +44,7 @@ function HyperParameterInstance() { | |||
| closeSSE(); | |||
| }; | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, []); | |||
| }, [instanceId]); | |||
| // 获取实验实例详情 | |||
| const getExperimentInsInfo = async (isStatusDetermined: boolean) => { | |||
| @@ -1,12 +1,12 @@ | |||
| import CodeSelect from '@/components/CodeSelect'; | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import ParameterSelect from '@/components/ParameterSelect'; | |||
| import ResourceSelect, { | |||
| ResourceSelectorType, | |||
| requiredValidator, | |||
| } from '@/components/ResourceSelect'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { hyperParameterOptimizedModeOptions } from '@/enums'; | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { isEmpty } from '@/utils'; | |||
| import { modalConfirm, removeFormListItem } from '@/utils/ui'; | |||
| import { MinusCircleOutlined, PlusCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons'; | |||
| @@ -86,7 +86,6 @@ function ExecuteConfig() { | |||
| const searchAlgorithm = Form.useWatch('search_alg', form); | |||
| const paramsTypeOptions = searchAlgorithm === 'Ax' ? axParameterOptions : parameterOptions; | |||
| const paramsTypeTooltip = searchAlgorithm === 'Ax' ? axParameterTooltip : parameterTooltip; | |||
| const [resourceStandardList, filterResourceStandard] = useComputingResource(); | |||
| const handleSearchAlgorithmChange = (value: string) => { | |||
| if ( | |||
| @@ -157,7 +156,6 @@ function ExecuteConfig() { | |||
| type={ResourceSelectorType.Mirror} | |||
| placeholder="请选择镜像" | |||
| canInput={false} | |||
| size="large" | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| @@ -180,7 +178,6 @@ function ExecuteConfig() { | |||
| type={ResourceSelectorType.Dataset} | |||
| placeholder="请选择数据集" | |||
| canInput={false} | |||
| size="large" | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| @@ -193,7 +190,6 @@ function ExecuteConfig() { | |||
| type={ResourceSelectorType.Model} | |||
| placeholder="请选择模型" | |||
| canInput={false} | |||
| size="large" | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| @@ -596,7 +592,7 @@ function ExecuteConfig() { | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="资源规格" | |||
| name="resource" | |||
| name="computing_resource_id" | |||
| rules={[ | |||
| { | |||
| 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> | |||
| </Col> | |||
| </Row> | |||
| @@ -41,7 +41,7 @@ function HyperParameterBasic({ | |||
| runStatus, | |||
| isInstance = false, | |||
| }: HyperParameterBasicProps) { | |||
| const getResourceDescription = useComputingResource()[2]; | |||
| const getResourceDescription = useComputingResource()[1]; | |||
| const basicDatas: BasicInfoData[] = useMemo(() => { | |||
| if (!info) { | |||
| @@ -136,7 +136,7 @@ function HyperParameterBasic({ | |||
| }, | |||
| { | |||
| label: '资源规格', | |||
| value: info.resource, | |||
| value: info.computing_resource_id, | |||
| format: getResourceDescription, | |||
| }, | |||
| ]; | |||
| @@ -24,7 +24,7 @@ export type FormData = { | |||
| num_samples: number; // 总试验次数 | |||
| max_t: number; // 单次试验最大时间 | |||
| min_samples_required: number; // 计算中位数的最小试验数 | |||
| resource: string; // 资源规格 | |||
| computing_resource_id: number; // 资源规格 | |||
| parameters: FormParameter[]; | |||
| points_to_evaluate: { [key: string]: any }[]; | |||
| }; | |||
| @@ -5,13 +5,13 @@ | |||
| */ | |||
| import CodeSelect from '@/components/CodeSelect'; | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import ParameterSelect from '@/components/ParameterSelect'; | |||
| import ResourceSelect, { | |||
| ResourceSelectorType, | |||
| requiredValidator, | |||
| type ParameterInputObject, | |||
| } from '@/components/ResourceSelect'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { | |||
| createServiceVersionReq, | |||
| getServiceInfoReq, | |||
| @@ -23,7 +23,7 @@ import SessionStorage from '@/utils/sessionStorage'; | |||
| import { removeFormListItem } from '@/utils/ui'; | |||
| import { MinusCircleOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons'; | |||
| 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 { useEffect, useState } from 'react'; | |||
| import { CreateServiceVersionFrom, ServiceOperationType, ServiceVersionData } from '../types'; | |||
| @@ -51,7 +51,7 @@ type ServiceVersionCache = ServiceVersionData & { | |||
| function CreateServiceVersion() { | |||
| const navigate = useNavigate(); | |||
| const [form] = Form.useForm(); | |||
| const [resourceStandardList, filterResourceStandard] = useComputingResource(); | |||
| const [operationType, setOperationType] = useState(ServiceOperationType.Create); | |||
| const [lastPage, setLastPage] = useState(CreateServiceVersionFrom.ServiceInfo); | |||
| const { message } = App.useApp(); | |||
| @@ -305,7 +305,6 @@ function CreateServiceVersion() { | |||
| placeholder="请选择模型" | |||
| disabled={disabled} | |||
| canInput={false} | |||
| size="large" | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| @@ -327,7 +326,6 @@ function CreateServiceVersion() { | |||
| type={ResourceSelectorType.Mirror} | |||
| placeholder="请选择镜像" | |||
| canInput={false} | |||
| size="large" | |||
| disabled={disabled} | |||
| /> | |||
| </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> | |||
| </Col> | |||
| </Row> | |||
| @@ -87,7 +87,7 @@ function ServiceInfo() { | |||
| format: formatDate, | |||
| }, | |||
| ]; | |||
| const getResourceDescription = useComputingResource()[2]; | |||
| const getResourceDescription = useComputingResource()[1]; | |||
| // 获取服务详情 | |||
| const getServiceInfo = useCallback(async () => { | |||
| @@ -36,7 +36,7 @@ const formatEnvText = (env?: Record<string, string>) => { | |||
| }; | |||
| function VersionBasicInfo({ info }: BasicInfoProps) { | |||
| const getResourceDescription = useComputingResource()[2]; | |||
| const getResourceDescription = useComputingResource()[1]; | |||
| const datas: BasicInfoData[] = [ | |||
| { | |||
| @@ -42,7 +42,7 @@ const formatEnvText = (env: Record<string, string>) => { | |||
| function VersionCompareModal({ version1, version2, ...rest }: VersionCompareModalProps) { | |||
| const [compareData, setCompareData] = useState<CompareData | undefined>(undefined); | |||
| const getResourceDescription = useComputingResource()[2]; | |||
| const getResourceDescription = useComputingResource()[1]; | |||
| const fields: FiledType[] = useMemo( | |||
| () => [ | |||
| @@ -7,7 +7,7 @@ import { to } from '@/utils/promise'; | |||
| import G6 from '@antv/g6'; | |||
| import { useNavigate, useParams } from '@umijs/max'; | |||
| import { App, Button } from 'antd'; | |||
| import { useEffect, useRef } from 'react'; | |||
| import { useEffect, useRef, useState } from 'react'; | |||
| import GlobalParamsDrawer from '../components/GlobalParamsDrawer'; | |||
| import ModelMenu from '../components/ModelMenu'; | |||
| import Props from '../components/PipelineNodeDrawer'; | |||
| @@ -24,7 +24,7 @@ const EditPipeline = () => { | |||
| const propsRef = useRef(); | |||
| const [paramsDrawerOpen, openParamsDrawer, closeParamsDrawer] = useVisible(false); | |||
| const [globalParam, setGlobalParam, globalParamRef] = useStateRef([]); | |||
| const [workflowInfo, setWorkflowInfo] = useStateRef(undefined); | |||
| const [workflowInfo, setWorkflowInfo] = useState(undefined); | |||
| const { message } = App.useApp(); | |||
| let sourceAnchorIdx, targetAnchorIdx, dropAnchorIdx; | |||
| let dragSourceNode; | |||
| @@ -8,7 +8,6 @@ import ResourceSelectorModal, { | |||
| } from '@/components/ResourceSelectorModal'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { CommonTabKeys } from '@/enums'; | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { canInput, createMenuItems } from '@/pages/Pipeline/Info/utils'; | |||
| import { | |||
| PipelineGlobalParam, | |||
| @@ -19,7 +18,7 @@ import { | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { to } from '@/utils/promise'; | |||
| 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 { forwardRef, useImperativeHandle, useState } from 'react'; | |||
| import PropsLabel from '../PropsLabel'; | |||
| @@ -36,7 +35,6 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| {} as PipelineNodeModelSerialize, | |||
| ); | |||
| const [open, setOpen] = useState(false); | |||
| const [resourceStandardList, filterResourceStandard] = useComputingResource(); // 资源规模 | |||
| const [menuItems, setMenuItems] = useState<MenuProps['items']>([]); | |||
| const afterOpenChange = async () => { | |||
| @@ -152,19 +150,21 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| const { close } = openAntdModal(CodeSelectorModal, { | |||
| onOk: (res) => { | |||
| if (res) { | |||
| const { id, code_repo_name, git_url, git_branch, git_user_name, git_password, ssh_key } = | |||
| res; | |||
| 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, { | |||
| ...item, | |||
| value, | |||
| showValue: res.code_repo_name, | |||
| showValue: code_repo_name, | |||
| fromSelect: true, | |||
| }); | |||
| 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 | |||
| name="mount_path" | |||
| @@ -41,6 +41,7 @@ export const Primary: Story = { | |||
| canInput: false, | |||
| textArea: false, | |||
| size: 'large', | |||
| placeholder: '请选择代码配置', | |||
| style: { width: 400 }, | |||
| }, | |||
| render: function Render(args) { | |||
| @@ -1,5 +1,7 @@ | |||
| import ParameterInput, { ParameterInputValue } from '@/components/ParameterInput'; | |||
| import { action } from '@storybook/addon-actions'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| import { fn } from '@storybook/test'; | |||
| import { Button } from 'antd'; | |||
| import { useState } from 'react'; | |||
| @@ -18,7 +20,7 @@ const meta = { | |||
| // 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 | |||
| // args: { onClick: fn() }, | |||
| args: { onChange: fn() }, | |||
| } satisfies Meta<typeof ParameterInput>; | |||
| export default meta; | |||
| @@ -37,45 +39,6 @@ export const Input: 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: { | |||
| placeholder: '请输入工作目录', | |||
| style: { width: 300 }, | |||
| @@ -86,18 +49,24 @@ export const Application: Story = { | |||
| const [value, setValue] = useState<ParameterInputValue | undefined>(''); | |||
| const onClick = () => { | |||
| setValue({ | |||
| const value = { | |||
| value: 'storybook', | |||
| showValue: 'storybook', | |||
| fromSelect: true, | |||
| }); | |||
| otherValue: 'others', | |||
| }; | |||
| setValue(value); | |||
| action('onChange')(value); | |||
| }; | |||
| return ( | |||
| <> | |||
| <ParameterInput | |||
| {...args} | |||
| value={value} | |||
| onChange={(value) => setValue(value)} | |||
| onChange={(value) => { | |||
| setValue(value); | |||
| action('onChange')(value); | |||
| }} | |||
| ></ParameterInput> | |||
| <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, | |||
| }, | |||
| }; | |||
| @@ -76,6 +76,7 @@ export const Primary: Story = { | |||
| canInput: false, | |||
| textArea: false, | |||
| size: 'large', | |||
| placeholder: '请选择数据集', | |||
| style: { width: 400 }, | |||
| }, | |||
| render: function Render(args) { | |||
| @@ -120,7 +121,6 @@ export const InForm: Story = { | |||
| type={ResourceSelectorType.Dataset} | |||
| placeholder="请选择" | |||
| canInput={false} | |||
| size="large" | |||
| onChange={onChange} | |||
| /> | |||
| </Form.Item> | |||
| @@ -133,7 +133,6 @@ export const InForm: Story = { | |||
| type={ResourceSelectorType.Model} | |||
| placeholder="请选择" | |||
| canInput={false} | |||
| size="large" | |||
| onChange={onChange} | |||
| /> | |||
| </Form.Item> | |||
| @@ -146,7 +145,6 @@ export const InForm: Story = { | |||
| type={ResourceSelectorType.Mirror} | |||
| placeholder="请选择" | |||
| canInput={false} | |||
| size="large" | |||
| onChange={onChange} | |||
| /> | |||
| </Form.Item> | |||
| @@ -349,6 +349,7 @@ public class SysUser extends BaseEntity { | |||
| .append("dept", getDept()) | |||
| .append("gitLinkUsername", getGitLinkUsername()) | |||
| .append("gitLinkPassword", getGitLinkPassword()) | |||
| .append("credit", getCredit()) | |||
| .toString(); | |||
| } | |||
| } | |||
| @@ -53,7 +53,6 @@ public class Constant { | |||
| // 任务类型 | |||
| public final static String TaskType_Dev = "dev_environment"; | |||
| 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_ActiveLearn = "active_learn"; | |||
| public final static String TaskType_Service = "service"; | |||
| @@ -13,6 +13,7 @@ import org.springframework.data.domain.PageRequest; | |||
| import org.springframework.web.bind.annotation.*; | |||
| import javax.annotation.Resource; | |||
| import java.util.Map; | |||
| /** | |||
| * (ComputingResource)表控制层 | |||
| @@ -104,5 +105,11 @@ public class ComputingResourceController extends BaseController { | |||
| PageRequest pageRequest = PageRequest.of(page, size); | |||
| return genericsSuccess(resourceOccupyService.queryByPage(pageRequest)); | |||
| } | |||
| @GetMapping("/credit") | |||
| @ApiOperation("查询用户积分使用情况") | |||
| public GenericsAjaxResult<Map<String, Double>> queryCredit() { | |||
| return genericsSuccess(resourceOccupyService.queryCredit()); | |||
| } | |||
| } | |||
| @@ -49,7 +49,7 @@ public class ComputingResource implements Serializable { | |||
| private Integer gpuNums; | |||
| @ApiModelProperty("积分/小时") | |||
| private Float creditPerHour; | |||
| private Double creditPerHour; | |||
| @ApiModelProperty("标签") | |||
| private String labels; | |||
| @@ -19,8 +19,14 @@ public class ResourceOccupy { | |||
| @ApiModelProperty("计算资源") | |||
| private Integer computingResourceId; | |||
| @ApiModelProperty("描述") | |||
| private String description; | |||
| @ApiModelProperty("积分/小时") | |||
| private Float creditPerHour; | |||
| private Double creditPerHour; | |||
| @ApiModelProperty("扣除的积分") | |||
| private Double deduceCredit; | |||
| @ApiModelProperty("上一次扣分时间") | |||
| private Date deduceLastTime; | |||
| @@ -36,4 +42,7 @@ public class ResourceOccupy { | |||
| @ApiModelProperty("类型id") | |||
| private Long taskId; | |||
| @ApiModelProperty("流水线节点id") | |||
| private String nodeId; | |||
| } | |||
| @@ -14,15 +14,19 @@ public interface ResourceOccupyDao { | |||
| 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 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); | |||
| } | |||
| @@ -1,11 +1,13 @@ | |||
| package com.ruoyi.platform.scheduling; | |||
| import com.ruoyi.platform.constant.Constant; | |||
| import com.ruoyi.platform.domain.Experiment; | |||
| import com.ruoyi.platform.domain.ExperimentIns; | |||
| import com.ruoyi.platform.mapper.ExperimentDao; | |||
| import com.ruoyi.platform.mapper.ExperimentInsDao; | |||
| import com.ruoyi.platform.service.AimService; | |||
| import com.ruoyi.platform.service.ExperimentInsService; | |||
| import com.ruoyi.platform.service.ResourceOccupyService; | |||
| import com.ruoyi.platform.utils.JacksonUtil; | |||
| import com.ruoyi.platform.utils.JsonUtils; | |||
| import com.ruoyi.platform.vo.InsMetricInfoVo; | |||
| @@ -16,6 +18,8 @@ import org.springframework.stereotype.Component; | |||
| import javax.annotation.Resource; | |||
| import java.io.IOException; | |||
| import java.text.SimpleDateFormat; | |||
| import java.time.Instant; | |||
| import java.util.*; | |||
| @Component() | |||
| @@ -28,6 +32,8 @@ public class ExperimentInstanceStatusTask { | |||
| private ExperimentInsDao experimentInsDao; | |||
| @Resource | |||
| private AimService aimService; | |||
| @Resource | |||
| private ResourceOccupyService resourceOccupyService; | |||
| private List<Integer> experimentIds = new ArrayList<>(); | |||
| @@ -43,7 +49,27 @@ public class ExperimentInstanceStatusTask { | |||
| try { | |||
| experimentIns = experimentInsService.queryStatusFromArgo(experimentIns); | |||
| } 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()); | |||
| @@ -42,13 +42,18 @@ public class RayInsStatusTask { | |||
| //当原本状态为null或非终止态时才调用argo接口 | |||
| try { | |||
| rayIns = rayInsService.queryStatusFromArgo(rayIns); | |||
| // 扣除积分 | |||
| 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) { | |||
| rayIns.setStatus(Constant.Failed); | |||
| // 结束扣除积分 | |||
| resourceOccupyService.endDeduce(Constant.TaskType_Ray, rayIns.getId(), null, null); | |||
| } | |||
| // 线程安全的添加操作 | |||
| synchronized (rayIds) { | |||
| @@ -25,11 +25,11 @@ public class ResourceOccupyTask { | |||
| private ServiceDao serviceDao; | |||
| // 开发环境功能扣除积分 | |||
| @Scheduled(cron = "0 0/10 * * * ?") // 每10分钟执行一次 | |||
| @Scheduled(cron = "0 0/1 * * * ?") // 每10分钟执行一次 | |||
| public void devDeduceCredit() { | |||
| List<DevEnvironment> devEnvironments = devEnvironmentDao.getRunning(); | |||
| 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() { | |||
| List<ServiceVersion> serviceVersions = serviceDao.getRunning(); | |||
| for (ServiceVersion serviceVersion : serviceVersions) { | |||
| resourceOccupyService.deducing(Constant.TaskType_Service, serviceVersion.getId()); | |||
| resourceOccupyService.deducing(Constant.TaskType_Service, serviceVersion.getId(), null, null); | |||
| } | |||
| } | |||
| } | |||
| @@ -4,15 +4,20 @@ import com.ruoyi.platform.domain.ResourceOccupy; | |||
| import org.springframework.data.domain.Page; | |||
| import org.springframework.data.domain.PageRequest; | |||
| import java.util.Date; | |||
| import java.util.Map; | |||
| public interface ResourceOccupyService { | |||
| 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); | |||
| Map<String, Double> queryCredit(); | |||
| } | |||
| @@ -64,11 +64,11 @@ public class DevEnvironmentServiceImpl implements DevEnvironmentService { | |||
| if (!devEnv.getStatus().equals(PodStatus.Terminated.getName()) && | |||
| !devEnv.getStatus().equals(PodStatus.Failed.getName())) { | |||
| PodStatusVo podStatusVo = this.jupyterService.getJupyterStatus(devEnv); | |||
| devEnv.setStatus(podStatusVo.getStatus()); | |||
| devEnv.setUrl(podStatusVo.getUrl()); | |||
| if(!devEnv.getStatus().equals(podStatusVo.getStatus())){ | |||
| this.devEnvironmentDao.update(devEnv); | |||
| } | |||
| devEnv.setStatus(podStatusVo.getStatus()); | |||
| devEnv.setUrl(podStatusVo.getUrl()); | |||
| } | |||
| } catch (Exception e) { | |||
| devEnv.setStatus(PodStatus.Unknown.getName()); | |||
| @@ -3,6 +3,7 @@ package com.ruoyi.platform.service.impl; | |||
| import cn.hutool.json.JSONUtil; | |||
| import com.alibaba.fastjson2.JSON; | |||
| import com.ruoyi.common.security.utils.SecurityUtils; | |||
| import com.ruoyi.platform.constant.Constant; | |||
| import com.ruoyi.platform.domain.DatasetTempStorage; | |||
| import com.ruoyi.platform.domain.Experiment; | |||
| 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.ModelDependency1Dao; | |||
| import com.ruoyi.platform.service.ExperimentInsService; | |||
| import com.ruoyi.platform.service.ResourceOccupyService; | |||
| import com.ruoyi.platform.utils.*; | |||
| import com.ruoyi.platform.vo.LogRequestVo; | |||
| import com.ruoyi.platform.vo.PodLogVo; | |||
| @@ -66,6 +68,9 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| @Resource | |||
| private DatasetTempStorageDao datasetTempStorageDao; | |||
| @Resource | |||
| private ResourceOccupyService resourceOccupyService; | |||
| private final MinioUtil minioUtil; | |||
| public ExperimentInsServiceImpl(MinioUtil minioUtil) { | |||
| @@ -410,6 +415,8 @@ public class ExperimentInsServiceImpl implements ExperimentInsService { | |||
| //修改实验状态 | |||
| updateExperimentStatus(experimentIns.getExperimentId()); | |||
| // 结束扣除积分 | |||
| resourceOccupyService.endDeduce(Constant.TaskType_Workflow, Long.valueOf(experimentIns.getId()), null, null); | |||
| return true; | |||
| } else { | |||
| throw new Exception("终止错误"); | |||
| @@ -10,7 +10,6 @@ import com.ruoyi.platform.domain.*; | |||
| import com.ruoyi.platform.domain.dependencydomain.ProjectDepency; | |||
| import com.ruoyi.platform.domain.dependencydomain.TrainTaskDepency; | |||
| import com.ruoyi.platform.mapper.ExperimentDao; | |||
| import com.ruoyi.platform.mapper.ExperimentInsDao; | |||
| import com.ruoyi.platform.mapper.ModelDependency1Dao; | |||
| import com.ruoyi.platform.service.*; | |||
| import com.ruoyi.platform.utils.HttpUtils; | |||
| @@ -62,6 +61,9 @@ public class ExperimentServiceImpl implements ExperimentService { | |||
| @Lazy | |||
| private ExperimentInsService experimentInsService; | |||
| @Resource | |||
| private ResourceOccupyService resourceOccupyService; | |||
| @Resource | |||
| private ModelDependency1Dao modelDependency1Dao; | |||
| @@ -233,6 +235,14 @@ public class ExperimentServiceImpl implements ExperimentService { | |||
| } | |||
| 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 | |||
| Map<String, Object> runReqMap = new HashMap<>(); | |||
| 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()); | |||
| } | |||
| // 记录开始扣积分 | |||
| 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) { | |||
| throw new RuntimeException(e); | |||
| @@ -133,9 +133,6 @@ public class JupyterServiceImpl implements JupyterService { | |||
| return "pod不存在!"; | |||
| } | |||
| // 结束扣积分 | |||
| resourceOccupyService.endDeduce(Constant.TaskType_Dev, Long.valueOf(id)); | |||
| // 使用 Kubernetes API 删除 Pod | |||
| String deleteResult = k8sClientUtil.deletePod(podName, namespace); | |||
| // 删除service | |||
| @@ -143,6 +140,9 @@ public class JupyterServiceImpl implements JupyterService { | |||
| devEnvironment.setStatus(Constant.Terminated); | |||
| this.devEnvironmentService.update(devEnvironment); | |||
| // 结束扣积分 | |||
| resourceOccupyService.endDeduce(Constant.TaskType_Dev, Long.valueOf(id), null, null); | |||
| return deleteResult + ",编辑器已停止"; | |||
| } | |||
| @@ -168,7 +168,7 @@ public class RayInsServiceImpl implements RayInsService { | |||
| rayInsDao.update(ins); | |||
| updateRayStatus(rayIns.getRayId()); | |||
| // 结束扣积分 | |||
| resourceOccupyService.endDeduce(Constant.TaskType_Ray, id); | |||
| resourceOccupyService.endDeduce(Constant.TaskType_Ray, id, null, null); | |||
| return true; | |||
| } else { | |||
| return false; | |||
| @@ -205,7 +205,8 @@ public class RayServiceImpl implements RayService { | |||
| rayIns.setResultPath(outputPath); | |||
| rayInsDao.insert(rayIns); | |||
| 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) { | |||
| throw new RuntimeException(e); | |||
| } | |||
| @@ -12,9 +12,13 @@ import org.springframework.data.domain.Page; | |||
| import org.springframework.data.domain.PageImpl; | |||
| import org.springframework.data.domain.PageRequest; | |||
| import org.springframework.stereotype.Service; | |||
| import org.springframework.transaction.annotation.Transactional; | |||
| import javax.annotation.Resource; | |||
| import java.util.Date; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| @Service("resourceOccupyService") | |||
| public class ResourceOccupyServiceImpl implements ResourceOccupyService { | |||
| @@ -48,15 +52,18 @@ public class ResourceOccupyServiceImpl implements ResourceOccupyService { | |||
| } | |||
| @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(); | |||
| ComputingResource computingResource = computingResourceDao.queryById(computingResourceId); | |||
| resourceOccupy.setComputingResourceId(computingResourceId); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| resourceOccupy.setUserId(loginUser.getUserid()); | |||
| resourceOccupy.setCreditPerHour(computingResource.getCreditPerHour()); | |||
| resourceOccupy.setDescription(computingResource.getDescription()); | |||
| resourceOccupy.setTaskType(taskType); | |||
| resourceOccupy.setTaskId(taskId); | |||
| resourceOccupy.setNodeId(nodeId); | |||
| resourceOccupyDao.save(resourceOccupy); | |||
| if (Constant.Computing_Resource_GPU.equals(computingResource.getComputingResource())) { | |||
| @@ -67,36 +74,58 @@ public class ResourceOccupyServiceImpl implements ResourceOccupyService { | |||
| } | |||
| @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 | |||
| 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 | |||
| 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; | |||
| } | |||
| } | |||
| @@ -252,19 +252,21 @@ public class ServiceServiceImpl implements ServiceService { | |||
| paramMap.put("service_type", service.getServiceType()); | |||
| paramMap.put("deploy_type", serviceVersion.getDeployType()); | |||
| // 记录开始扣积分 | |||
| // 判断是否有资源 | |||
| if (resourceOccupyService.haveResource(serviceVersion.getComputingResourceId())) { | |||
| String req = HttpUtils.sendPost(argoUrl + modelService + "/create", JSON.toJSONString(paramMap)); | |||
| if (StringUtils.isNotEmpty(req)) { | |||
| Map<String, Object> reqMap = JacksonUtil.parseJSONStr2Map(req); | |||
| 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"); | |||
| serviceVersion.setUrl(data.get("url")); | |||
| serviceVersion.setDeploymentName(data.get("deployment_name")); | |||
| serviceVersion.setSvcName(data.get("svc_name")); | |||
| serviceVersion.setRunState(Constant.Pending); | |||
| serviceDao.updateServiceVersion(serviceVersion); | |||
| // 记录开始扣积分 | |||
| resourceOccupyService.startDeduce(serviceVersion.getComputingResourceId(), Constant.TaskType_Service, serviceVersion.getId(), null); | |||
| return "启动成功"; | |||
| } else { | |||
| throw new RuntimeException("启动失败"); | |||
| @@ -286,7 +288,7 @@ public class ServiceServiceImpl implements ServiceService { | |||
| serviceVersion.setRunState(Constant.Stopped); | |||
| serviceDao.updateServiceVersion(serviceVersion); | |||
| // 结束扣积分 | |||
| resourceOccupyService.endDeduce(Constant.TaskType_Service, id); | |||
| resourceOccupyService.endDeduce(Constant.TaskType_Service, id, null, null); | |||
| return "停止成功"; | |||
| } else { | |||
| throw new RuntimeException("停止失败"); | |||
| @@ -1,6 +1,5 @@ | |||
| package com.ruoyi.platform.utils; | |||
| import com.alibaba.fastjson2.JSON; | |||
| import com.ruoyi.platform.constant.Constant; | |||
| import com.ruoyi.platform.domain.ComputingResource; | |||
| 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.openapi.ApiClient; | |||
| 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.models.*; | |||
| import io.kubernetes.client.util.ClientBuilder; | |||
| @@ -19,7 +17,6 @@ import io.kubernetes.client.util.Config; | |||
| import io.kubernetes.client.util.credentials.AccessTokenAuthentication; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.apache.commons.lang.StringUtils; | |||
| import org.json.JSONObject; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.stereotype.Component; | |||
| @@ -519,7 +516,8 @@ public class K8sClientUtil { | |||
| pod = api.createNamespacedPod(namespace, pod, null, null, null); | |||
| 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) { | |||
| throw new RuntimeException("创建pod异常:" + e.getResponseBody()); | |||
| @@ -699,8 +697,7 @@ public class K8sClientUtil { | |||
| ComputingResource computingResource = computingResourceDao.queryById(computingResourceId); | |||
| //配置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<>(); | |||
| if (computingResource.getGpuNums() != null && computingResource.getGpuNums() != 0) { | |||
| limitMap.put("nvidia.com/gpu", new Quantity(String.valueOf(computingResource.getGpuNums()))); | |||
| @@ -2,8 +2,11 @@ | |||
| <!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"> | |||
| <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> | |||
| <update id="edit"> | |||
| @@ -15,6 +18,9 @@ | |||
| <if test="resourceOccupy.deduceLastTime != null"> | |||
| deduce_last_time = #{resourceOccupy.deduceLastTime}, | |||
| </if> | |||
| <if test="resourceOccupy.deduceCredit != null"> | |||
| deduce_credit = #{resourceOccupy.deduceCredit}, | |||
| </if> | |||
| </set> | |||
| where id = #{resourceOccupy.id} | |||
| </update> | |||
| @@ -46,17 +52,42 @@ | |||
| <select id="getResourceOccupyByTask" resultType="com.ruoyi.platform.domain.ResourceOccupy"> | |||
| select * | |||
| 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 id="count" resultType="java.lang.Long"> | |||
| select count(1) resource_occupy | |||
| select count(1) | |||
| from resource_occupy | |||
| where user_id = #{userId} | |||
| </select> | |||
| <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 | |||
| where user_id = #{userId} | |||
| order by start_time desc limit #{pageable.offset}, #{pageable.pageSize} | |||
| </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> | |||
| @@ -60,7 +60,7 @@ | |||
| u.password, | |||
| u.git_link_username, | |||
| u.git_link_password, | |||
| u.credit, | |||
| TRUNCATE(u.credit, 1) as credit, | |||
| u.sex, | |||
| u.status, | |||
| u.del_flag, | |||
| @@ -90,7 +90,7 @@ | |||
| <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, | |||
| 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 | |||
| from sys_user u | |||
| left join sys_dept d on u.dept_id = d.dept_id | |||
| @@ -125,7 +125,7 @@ | |||
| </select> | |||
| <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 | |||
| 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 | |||
| @@ -142,7 +142,7 @@ | |||
| </select> | |||
| <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 | |||
| 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 | |||