| @@ -60,3 +60,5 @@ mvnw | |||
| **/node_modules | |||
| *storybook.log | |||
| /react-ui/docs | |||
| @@ -16,7 +16,7 @@ const config: StorybookConfig = { | |||
| name: '@storybook/react-webpack5', | |||
| options: {}, | |||
| }, | |||
| staticDirs: ['../public'], | |||
| staticDirs: ['../public', { from: '../docs', to: '/docs' }], | |||
| docs: { | |||
| defaultName: 'Documentation', | |||
| }, | |||
| @@ -1,133 +1 @@ | |||
| Language : 🇺🇸 | [🇨🇳](./README.zh-CN.md) | [🇷🇺](./README.ru-RU.md) | [🇹🇷](./README.tr-TR.md) | [🇯🇵](./README.ja-JP.md) | [🇫🇷](./README.fr-FR.md) | [🇵🇹](./README.pt-BR.md) | [🇸🇦](./README.ar-DZ.md) | |||
| <h1 align="center">Ant Design Pro</h1> | |||
| <div align="center"> | |||
| An out-of-box UI solution for enterprise applications as a React boilerplate. | |||
| [](https://dev.azure.com/ant-design/ant-design-pro/_build/latest?definitionId=1?branchName=master)   | |||
| [](https://gitter.im/ant-design/pro-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](http://umijs.org/)  | |||
|  | |||
| </div> | |||
| - Preview: http://preview.pro.ant.design | |||
| - Home Page: http://pro.ant.design | |||
| - Documentation: http://pro.ant.design/docs/getting-started | |||
| - ChangeLog: http://pro.ant.design/docs/changelog | |||
| - FAQ: http://pro.ant.design/docs/faq | |||
| - Mirror Site in China: http://ant-design-pro.gitee.io | |||
| ## 5.0 is out! 🎉🎉🎉 | |||
| [Ant Design Pro 5.0.0](https://github.com/ant-design/ant-design-pro/issues/8656) | |||
| ## Translation Recruitment :loudspeaker: | |||
| We need your help: https://github.com/ant-design/ant-design-pro/issues/120 | |||
| ## Features | |||
| - :bulb: **TypeScript**: A language for application-scale JavaScript | |||
| - :scroll: **Blocks**: Build page with block template | |||
| - :gem: **Neat Design**: Follow [Ant Design specification](http://ant.design/) | |||
| - :triangular_ruler: **Common Templates**: Typical templates for enterprise applications | |||
| - :rocket: **State of The Art Development**: Newest development stack of React/umi/dva/antd | |||
| - :iphone: **Responsive**: Designed for variable screen sizes | |||
| - :art: **Theming**: Customizable theme with simple config | |||
| - :globe_with_meridians: **International**: Built-in i18n solution | |||
| - :gear: **Best Practices**: Solid workflow to make your code healthy | |||
| - :1234: **Mock development**: Easy to use mock development solution | |||
| - :white_check_mark: **UI Test**: Fly safely with unit and e2e tests | |||
| ## Templates | |||
| ``` | |||
| - Dashboard | |||
| - Analytic | |||
| - Monitor | |||
| - Workspace | |||
| - Form | |||
| - Basic Form | |||
| - Step Form | |||
| - Advanced From | |||
| - List | |||
| - Standard Table | |||
| - Standard List | |||
| - Card List | |||
| - Search List (Project/Applications/Article) | |||
| - Profile | |||
| - Simple Profile | |||
| - Advanced Profile | |||
| - Account | |||
| - Account Center | |||
| - Account Settings | |||
| - Result | |||
| - Success | |||
| - Failed | |||
| - Exception | |||
| - 403 | |||
| - 404 | |||
| - 500 | |||
| - User | |||
| - Login | |||
| - Register | |||
| - Register Result | |||
| ``` | |||
| ## Usage | |||
| ### Use bash | |||
| We provide pro-cli to quickly initialize scaffolding. | |||
| ```bash | |||
| # use npm | |||
| npm i @ant-design/pro-cli -g | |||
| pro create myapp | |||
| ``` | |||
| select umi version | |||
| ```shell | |||
| 🐂 Use umi@4 or umi@3 ? (Use arrow keys) | |||
| ❯ umi@4 | |||
| umi@3 | |||
| ``` | |||
| > If the umi@4 version is selected, full blocks are not yet supported. | |||
| If you choose umi@3, you can also choose the pro template. Pro is the basic template, which only provides the basic content of the framework operation. Complete contains all blocks, which is not suitable for secondary development as a basic template. | |||
| ```shell | |||
| ? 🚀 Full or a simple scaffold? (Use arrow keys) | |||
| ❯ simple | |||
| complete | |||
| ``` | |||
| Install dependencies: | |||
| ```shell | |||
| $ cd myapp && tyarn | |||
| // or | |||
| $ cd myapp && npm install | |||
| ``` | |||
| ## Browsers support | |||
| Modern browsers. | |||
| | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | | |||
| | --- | --- | --- | --- | --- | | |||
| | Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | | |||
| ## Contributing | |||
| Any type of contribution is welcome, here are some examples of how you may contribute to this project: | |||
| - Use Ant Design Pro in your daily work. | |||
| - Submit [issues](http://github.com/ant-design/ant-design-pro/issues) to report bugs or ask questions. | |||
| - Propose [pull requests](http://github.com/ant-design/ant-design-pro/pulls) to improve our code. | |||
| # Documentation | |||
| @@ -345,7 +345,6 @@ export default [ | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| name: '应用开发', | |||
| path: '/appsDeployment', | |||
| @@ -498,6 +497,18 @@ export default [ | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| name: '算力积分', | |||
| path: '/points', | |||
| routes: [ | |||
| { | |||
| name: '算力积分', | |||
| path: '', | |||
| key: 'points', | |||
| component: './Points/index', | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| path: '*', | |||
| layout: false, | |||
| @@ -1,8 +1,8 @@ | |||
| { | |||
| "name": "ant-design-pro", | |||
| "version": "6.0.0", | |||
| "name": "cl-model", | |||
| "version": "1.0.0", | |||
| "private": true, | |||
| "description": "An out-of-box UI solution for enterprise applications", | |||
| "description": "", | |||
| "scripts": { | |||
| "analyze": "cross-env ANALYZE=1 max build", | |||
| "build": "max build", | |||
| @@ -16,6 +16,7 @@ | |||
| "docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up", | |||
| "docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro", | |||
| "docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro", | |||
| "docs": "typedoc --entryPointStrategy expand --entryPoints 'src/utils' --skipErrorChecking --out docs", | |||
| "gh-pages": "gh-pages -d dist", | |||
| "i18n-remove": "pro i18n-remove --locale=zh-CN --write", | |||
| "postinstall": "max setup", | |||
| @@ -132,6 +133,7 @@ | |||
| "swagger-ui-dist": "^4.18.2", | |||
| "ts-loader": "~9.5.2", | |||
| "ts-node": "^10.9.1", | |||
| "typedoc": "~0.28.1", | |||
| "typescript": "^5.0.4", | |||
| "umi-presets-pro": "^2.0.0" | |||
| }, | |||
| @@ -1,5 +1,23 @@ | |||
| @font-face { | |||
| font-family: Alibaba; | |||
| src: url('./ALIBABA-PUHUITI-MEDIUM.TTF'); | |||
| font-display: swap; | |||
| } | |||
| font-family: Alibaba; | |||
| src: url('./ALIBABA-PUHUITI-MEDIUM.TTF'); | |||
| font-display: swap; | |||
| } | |||
| @font-face { | |||
| font-family: 'TaoBaoMaiCaiTi'; | |||
| src: url('./TaoBaoMaiCaiTi-Regular.woff2') format('woff2'), /* 最优先使用 woff2 */ | |||
| url('./TaoBaoMaiCaiTi-Regular.woff') format('woff'), /* 兼容性较好的 woff */ | |||
| url('./TaoBaoMaiCaiTi-Regular.ttf') format('truetype'), /* ttf 作为备选 */ | |||
| url('./TaoBaoMaiCaiTi-Regular.otf') format('opentype'); /* otf 作为最后选项 */ | |||
| font-display: swap; /* 优化页面加载时的字体显示 */ | |||
| } | |||
| @font-face { | |||
| font-family: 'DingTalk-JinBuTi'; | |||
| src: url('./DingTalk-JinBuTi.woff2') format('woff2'), /* 最优先使用 woff2 */ | |||
| url('./DingTalk-JinBuTi.woff') format('woff'), /* 兼容性较好的 woff */ | |||
| url('./DingTalk-JinBuTi.ttf') format('truetype'); /* ttf 作为备选 */ | |||
| font-display: swap; /* 优化页面加载时的字体显示 */ | |||
| } | |||
| @@ -137,7 +137,7 @@ function EditorCreate() { | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="资源规格" | |||
| name="standard" | |||
| name="computing_resource_id" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| @@ -347,7 +347,7 @@ function CreateServiceVersion() { | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="资源规格" | |||
| name="resource" | |||
| name="computing_resource_id" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| @@ -309,8 +309,8 @@ function ServiceInfo() { | |||
| }, | |||
| { | |||
| title: '资源规格', | |||
| dataIndex: 'resource', | |||
| key: 'resource', | |||
| dataIndex: 'computing_resource_id', | |||
| key: 'computing_resource_id', | |||
| width: '20%', | |||
| render: tableCellRender(true, TableCellValueType.Custom, { | |||
| format: getResourceDescription, | |||
| @@ -68,7 +68,7 @@ function VersionBasicInfo({ info }: BasicInfoProps) { | |||
| }, | |||
| { | |||
| label: '资源规格', | |||
| value: info?.resource, | |||
| value: info?.computing_resource_id, | |||
| format: getResourceDescription, | |||
| }, | |||
| { | |||
| @@ -24,7 +24,7 @@ export type ServiceVersionData = { | |||
| run_state: ServiceRunStatus; // 运行状态 | |||
| image: string; // 镜像 | |||
| replicas: number; // 副本数 | |||
| resource: string; // 资源 | |||
| computing_resource_id: number; // 资源 | |||
| mount_path: string; // 挂载路径 | |||
| model: { | |||
| // 模型 | |||
| @@ -0,0 +1,31 @@ | |||
| .statistics { | |||
| display: flex; | |||
| align-items: center; | |||
| width: 100%; | |||
| padding: 20px 0; | |||
| background-color: @background-color; | |||
| border-radius: 8px; | |||
| &__item { | |||
| display: flex; | |||
| flex: 1; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| &--border { | |||
| border-right: 1px solid @border-color; | |||
| } | |||
| &__value { | |||
| margin-bottom: 8px; | |||
| color: @text-color; | |||
| font-weight: bold; | |||
| font-size: 30px; | |||
| } | |||
| &__title { | |||
| color: #999999; | |||
| font-size: @font-size-input; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| import classNames from 'classnames'; | |||
| import styles from './index.less'; | |||
| type StatisticsProps = { | |||
| remaining?: number; | |||
| consuming?: number; | |||
| }; | |||
| function Statistics({ remaining, consuming }: StatisticsProps) { | |||
| const items = [ | |||
| { | |||
| title: '当前可用算力积分(分)', | |||
| value: remaining ?? '-', | |||
| }, | |||
| { | |||
| title: '总消耗算力积分(分)', | |||
| value: consuming ?? '-', | |||
| }, | |||
| ]; | |||
| return ( | |||
| <div className={styles['statistics']}> | |||
| {items.map((item, index) => ( | |||
| <div | |||
| key={item.title} | |||
| className={classNames(styles['statistics__item'], { | |||
| [styles['statistics__item--border']]: index === 0, | |||
| })} | |||
| > | |||
| <span className={styles['statistics__item__value']}>{item.value}</span> | |||
| <span className={styles['statistics__item__title']}>{item.title}</span> | |||
| </div> | |||
| ))} | |||
| </div> | |||
| ); | |||
| } | |||
| export default Statistics; | |||
| @@ -0,0 +1,19 @@ | |||
| .points-detail { | |||
| height: 100%; | |||
| &__content { | |||
| height: calc(100% - 60px); | |||
| margin-top: 10px; | |||
| padding: 20px 30px 0; | |||
| background-color: white; | |||
| border-radius: 10px; | |||
| &__top { | |||
| width: 100%; | |||
| } | |||
| &__table { | |||
| height: calc(100% - 117px - 28px); | |||
| margin-top: 28px; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,256 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 13:58:08 | |||
| * @Description: 模型部署服务列表 | |||
| */ | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import { getPointsConsumptionReq, getPointsStatisticsReq } from '@/services/points'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { to } from '@/utils/promise'; | |||
| import statusTableCell from '@/utils/statusTableCell'; | |||
| import tableCellRender, { TableCellValueType } from '@/utils/table'; | |||
| import { Link } from '@umijs/max'; | |||
| import { Table, type TablePaginationConfig, type TableProps } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import Statistics from './components/Statistics'; | |||
| import styles from './index.less'; | |||
| enum TaskType { | |||
| DevEnvironment = 'dev_environment', | |||
| Workflow = 'workflow', | |||
| Ray = 'ray', | |||
| Service = 'service', | |||
| } | |||
| const taskTypeOptions = [ | |||
| { | |||
| value: 'dev_environment', | |||
| label: '开发环境', | |||
| }, | |||
| { | |||
| value: 'workflow', | |||
| label: '实验', | |||
| }, | |||
| { | |||
| value: 'ray', | |||
| label: '超参数自动寻优', | |||
| }, | |||
| { | |||
| value: 'service', | |||
| label: '服务', | |||
| }, | |||
| ]; | |||
| export type PointsData = { | |||
| computing_resource_id: number; | |||
| credit_per_hour: number; // 每小时消耗的积分 | |||
| deduce_credit: number; | |||
| deduce_last_time: string; | |||
| description: string; | |||
| id: number; | |||
| node_id: string; | |||
| start_time: string; | |||
| state: number; | |||
| workflow_id?: number; // 流水线id | |||
| task_id: number; // 实验id | |||
| task_ins_id: number; // 实例id | |||
| task_type: string; | |||
| user_id: number; | |||
| }; | |||
| export type PointsStatistics = { | |||
| deduceCredit: number; | |||
| userCredit: number; | |||
| }; | |||
| // 格式化任务 | |||
| const formatTask = (value: string, record: PointsData) => { | |||
| let url; | |||
| switch (record.task_type) { | |||
| case TaskType.DevEnvironment: | |||
| url = `/developmentEnvironment`; | |||
| break; | |||
| case TaskType.Workflow: | |||
| if (record.workflow_id && record.task_ins_id) { | |||
| url = `/pipeline/experiment/instance/${record.workflow_id}/${record.task_ins_id}`; | |||
| } | |||
| break; | |||
| case TaskType.Ray: | |||
| if (record.task_id && record.task_ins_id) { | |||
| url = `/pipeline/hyperparameter/instance/${record.task_id}/${record.task_ins_id}`; | |||
| } | |||
| break; | |||
| case TaskType.Service: | |||
| if (record.task_id && record.task_ins_id) { | |||
| url = `/dataset/modelDeployment/serviceInfo/${record.task_id}/versionInfo/${record.task_ins_id}`; | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| if (url) { | |||
| return <Link to={url}>{value}</Link>; | |||
| } else { | |||
| return <span>{value}</span>; | |||
| } | |||
| }; | |||
| function PointsDetail() { | |||
| const [tableData, setTableData] = useState<PointsData[]>([]); | |||
| const [total, setTotal] = useState(0); | |||
| const [statistics, setStatistics] = useState<PointsStatistics>(); | |||
| const [pagination, setPagination] = useState<TablePaginationConfig>({ | |||
| current: 1, | |||
| pageSize: 10, | |||
| }); | |||
| useEffect(() => { | |||
| // 获取积分统计 | |||
| const getPointsStatistics = async () => { | |||
| const [res] = await to(getPointsStatisticsReq()); | |||
| if (res && res.data) { | |||
| setStatistics(res.data); | |||
| } | |||
| }; | |||
| getPointsStatistics(); | |||
| }, []); | |||
| useEffect(() => { | |||
| // 获取积分消费明细 | |||
| const getPointsConsumption = async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| }; | |||
| const [res] = await to(getPointsConsumptionReq(params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| getPointsConsumption(); | |||
| }, [pagination]); | |||
| // 分页切换 | |||
| const handleTableChange: TableProps<PointsData>['onChange'] = ( | |||
| pagination, | |||
| _filters, | |||
| _sorter, | |||
| { action }, | |||
| ) => { | |||
| if (action === 'paginate') { | |||
| setPagination(pagination); | |||
| } | |||
| }; | |||
| const columns: TableProps<PointsData>['columns'] = [ | |||
| { | |||
| title: '序号', | |||
| dataIndex: 'index', | |||
| key: 'index', | |||
| width: 80, | |||
| render: tableCellRender(false, TableCellValueType.Index, { | |||
| page: pagination.current! - 1, | |||
| pageSize: pagination.pageSize!, | |||
| }), | |||
| }, | |||
| { | |||
| title: '任务', | |||
| dataIndex: 'task_name', | |||
| key: 'task_name', | |||
| width: '15%', | |||
| render: formatTask, | |||
| }, | |||
| { | |||
| title: '任务类型', | |||
| dataIndex: 'task_type', | |||
| key: 'task_type', | |||
| width: '10%', | |||
| render: statusTableCell(taskTypeOptions), | |||
| }, | |||
| { | |||
| title: '积分/小时', | |||
| dataIndex: 'credit_per_hour', | |||
| key: 'credit_per_hour', | |||
| width: '10%', | |||
| render: tableCellRender(), | |||
| }, | |||
| { | |||
| title: '消耗的积分(分)', | |||
| dataIndex: 'deduce_credit', | |||
| key: 'deduce_credit', | |||
| width: '12%', | |||
| render: tableCellRender(), | |||
| }, | |||
| { | |||
| title: '描述', | |||
| dataIndex: 'description', | |||
| key: 'description', | |||
| render: tableCellRender(true), | |||
| }, | |||
| { | |||
| title: '开始时间', | |||
| dataIndex: 'start_time', | |||
| key: 'start_time', | |||
| width: '15%', | |||
| render: tableCellRender(false, TableCellValueType.Date), | |||
| }, | |||
| { | |||
| title: '状态', | |||
| dataIndex: 'state', | |||
| key: 'state', | |||
| width: '8%', | |||
| render: statusTableCell([ | |||
| { | |||
| value: 0, | |||
| label: '已结束', | |||
| color: themes['textColor'], | |||
| }, | |||
| { | |||
| value: 1, | |||
| label: '进行中', | |||
| color: themes['primaryColor'], | |||
| }, | |||
| ]), | |||
| }, | |||
| ]; | |||
| return ( | |||
| <div className={styles['points-detail']}> | |||
| <PageTitle title="算力积分明细"></PageTitle> | |||
| <div className={styles['points-detail__content']}> | |||
| <div className={styles['points-detail__content__top']}> | |||
| <Statistics | |||
| remaining={statistics?.userCredit} | |||
| consuming={statistics?.deduceCredit} | |||
| ></Statistics> | |||
| </div> | |||
| <div | |||
| className={classNames('vertical-scroll-table', styles['points-detail__content__table'])} | |||
| > | |||
| <Table | |||
| dataSource={tableData} | |||
| columns={columns} | |||
| scroll={{ y: 'calc(100% - 55px)' }} | |||
| pagination={{ | |||
| ...pagination, | |||
| total: total, | |||
| showSizeChanger: true, | |||
| showQuickJumper: true, | |||
| showTotal: () => `共${total}条`, | |||
| }} | |||
| onChange={handleTableChange} | |||
| rowKey="id" | |||
| /> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default PointsDetail; | |||
| @@ -2,6 +2,7 @@ import { DictValueEnumObj } from '@/components/DictTag'; | |||
| import KFModal from '@/components/KFModal'; | |||
| import { | |||
| ProForm, | |||
| ProFormDigit, | |||
| ProFormRadio, | |||
| ProFormSelect, | |||
| ProFormText, | |||
| @@ -64,6 +65,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||
| remark: props.values.remark, | |||
| gitLinkUsername: props.values.gitLinkUsername, | |||
| gitLinkPassword: props.values.gitLinkPassword, | |||
| credit: props.values.credit, | |||
| }); | |||
| }, [form, props, statusOptions]); | |||
| @@ -219,12 +221,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||
| autoComplete: 'new-password', | |||
| }} | |||
| allowClear | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: <FormattedMessage id="请输入密码!" defaultMessage="请输入密码!" />, | |||
| }, | |||
| ]} | |||
| rules={props.values.userId ? [] : [{ required: true, message: '请输入密码!' }]} | |||
| /> | |||
| <ProFormSelect | |||
| valueEnum={sexOptions} | |||
| @@ -304,6 +301,18 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||
| }} | |||
| rules={props.values.userId ? [] : [{ required: true, message: '请输入 Git 密码!' }]} | |||
| /> | |||
| <ProFormDigit | |||
| name="credit" | |||
| label="算力积分" | |||
| placeholder="请输入算力积分" | |||
| colProps={{ xs: 24, md: 12, xl: 12 }} | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入算力积分', | |||
| }, | |||
| ]} | |||
| /> | |||
| <ProFormTextArea | |||
| name="remark" | |||
| label={intl.formatMessage({ | |||
| @@ -259,6 +259,11 @@ const UserTableList: React.FC = () => { | |||
| dataIndex: 'phonenumber', | |||
| valueType: 'text', | |||
| }, | |||
| { | |||
| title: <FormattedMessage id="system.user.credit" defaultMessage="算力积分" />, | |||
| dataIndex: 'credit', | |||
| valueType: 'text', | |||
| }, | |||
| { | |||
| title: <FormattedMessage id="system.user.status" defaultMessage="帐号状态" />, | |||
| dataIndex: 'status', | |||
| @@ -1,4 +1,5 @@ | |||
| .experiment-chart { | |||
| flex: none; | |||
| width: 295px; | |||
| min-width: 295px; | |||
| height: 140px; | |||
| @@ -1,8 +1,8 @@ | |||
| .experiment-table { | |||
| flex: 1; | |||
| min-width: 500px; | |||
| min-width: 378px; | |||
| height: 140px; | |||
| padding: 12px 24px; | |||
| padding: 12px; | |||
| background: @workspace-background; | |||
| border-radius: 4px; | |||
| @@ -32,15 +32,15 @@ | |||
| } | |||
| &__status { | |||
| width: 20%; | |||
| width: 15%; | |||
| } | |||
| &__duration { | |||
| width: 30%; | |||
| width: 25%; | |||
| } | |||
| &__date { | |||
| width: calc(50% - 60px); | |||
| width: calc(60% - 60px); | |||
| } | |||
| &__operation { | |||
| @@ -1,16 +1,12 @@ | |||
| .total-statistics { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| width: 400px; | |||
| height: 140px; | |||
| background: @workspace-background; | |||
| border-radius: 4px; | |||
| padding: 0 16px; | |||
| &__icon { | |||
| width: 85px; | |||
| height: 80px; | |||
| margin-right: 40px; | |||
| width: 63px; | |||
| margin-right: 15px; | |||
| } | |||
| &__title { | |||
| @@ -20,18 +16,10 @@ | |||
| font-size: @font-size-content; | |||
| } | |||
| &__title-shadow { | |||
| position: absolute; | |||
| bottom: 6px; | |||
| left: 0; | |||
| width: 79px; | |||
| height: 6px; | |||
| background: linear-gradient(87.07deg, rgba(22, 100, 255, 0.6) 0%, rgba(22, 100, 255, 0) 100%); | |||
| } | |||
| &__count { | |||
| color: @text-color; | |||
| font-weight: 700; | |||
| font-size: 25px; | |||
| font-family: DingTalk-JinBuTi; | |||
| } | |||
| } | |||
| @@ -1,3 +1,4 @@ | |||
| import { Flex } from 'antd'; | |||
| import styles from './index.less'; | |||
| type TotalStatisticsProps = { | |||
| @@ -11,13 +12,12 @@ function TotalStatistics({ icon = '', title = '', count = 0, style }: TotalStati | |||
| return ( | |||
| <div className={styles['total-statistics']} style={style}> | |||
| <img className={styles['total-statistics__icon']} src={icon} draggable={false} alt="" /> | |||
| <div> | |||
| <Flex vertical align="center"> | |||
| <div className={styles['total-statistics__title']}> | |||
| <span>{title}</span> | |||
| <div className={styles['total-statistics__title-shadow']}></div> | |||
| </div> | |||
| <div className={styles['total-statistics__count']}>{count ?? '--'}</div> | |||
| </div> | |||
| </Flex> | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| .user-points { | |||
| display: flex; | |||
| flex: none; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| width: 326px; | |||
| height: 228px; | |||
| .backgroundFullImage(url(@/assets/img/user-points-bg.png)); | |||
| &__label { | |||
| margin-top: 60px; | |||
| color: @text-color; | |||
| font-size: @font-size-title; | |||
| font-family: DingTalk-JinBuTi; | |||
| } | |||
| &__value { | |||
| margin-top: 8px; | |||
| margin-bottom: 12px; | |||
| color: @primary-color; | |||
| font-size: 36px; | |||
| font-family: DingTalk-JinBuTi; | |||
| line-height: 43px; | |||
| } | |||
| &__button { | |||
| padding: 8px 20px; | |||
| color: @primary-color; | |||
| font-size: @font-size-content; | |||
| background: rgba(255, 255, 255, 0.3); | |||
| border: 1px solid #ffffff; | |||
| border-radius: 8px; | |||
| cursor: pointer; | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| import { PointsStatistics } from '@/pages/Points/index'; | |||
| import { getPointsStatisticsReq } from '@/services/points'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { useEffect, useState } from 'react'; | |||
| import styles from './index.less'; | |||
| function UserPoints() { | |||
| const [statistics, setStatistics] = useState<PointsStatistics>(); | |||
| const navigate = useNavigate(); | |||
| useEffect(() => { | |||
| // 获取积分统计 | |||
| const getPointsStatistics = async () => { | |||
| const [res] = await to(getPointsStatisticsReq()); | |||
| if (res && res.data) { | |||
| setStatistics(res.data); | |||
| } | |||
| }; | |||
| getPointsStatistics(); | |||
| }, []); | |||
| return ( | |||
| <div className={styles['user-points']}> | |||
| <span className={styles['user-points__label']}>当前可用算力积分</span> | |||
| <span className={styles['user-points__value']}>{statistics?.userCredit ?? '--'}</span> | |||
| <div | |||
| className={styles['user-points__button']} | |||
| onClick={() => { | |||
| navigate('/points'); | |||
| }} | |||
| > | |||
| 查看详情 | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default UserPoints; | |||
| @@ -6,6 +6,7 @@ | |||
| background: linear-gradient(#ecf2fe, #f9fafb); | |||
| &__overview { | |||
| flex: 1; | |||
| gap: 15px; | |||
| margin-bottom: 16px; | |||
| padding: 20px 30px; | |||
| @@ -21,11 +22,19 @@ | |||
| &__content { | |||
| display: flex; | |||
| flex-wrap: wrap; | |||
| gap: 15px; | |||
| align-items: center; | |||
| @media screen and (max-width: 1800px) { | |||
| flex-wrap: wrap; | |||
| &__statistics { | |||
| flex: none; | |||
| background: linear-gradient( | |||
| 123.08deg, | |||
| rgba(138, 138, 138, 0.06) 1.32%, | |||
| rgba(22, 100, 255, 0.02) 58.35% | |||
| ); | |||
| border-radius: 4px; | |||
| } | |||
| } | |||
| } | |||
| @@ -2,6 +2,7 @@ import { useDraggable } from '@/hooks/draggable'; | |||
| import { getWorkspaceOverviewReq } from '@/services/workspace'; | |||
| import { ExperimentInstance } from '@/types'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Divider, Flex } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import Draggable from 'react-draggable'; | |||
| import AssetsManagement from './components/AssetsManagement'; | |||
| @@ -10,6 +11,7 @@ import ExperitableTable from './components/ExperimentTable'; | |||
| import QuickStart from './components/QuickStart'; | |||
| import RobotFrame from './components/RobotFrame'; | |||
| import TotalStatistics from './components/TotalStatistics'; | |||
| import UserPoints from './components/UserPoints'; | |||
| import UserSpace from './components/UserSpace'; | |||
| import WorkspaceIntro from './components/WorkspaceIntro'; | |||
| import styles from './index.less'; | |||
| @@ -28,6 +30,7 @@ function Workspace() { | |||
| setRobotFrameVisible((prev) => !prev), | |||
| ); | |||
| const users: number[] = new Array(8).fill(0); | |||
| useEffect(() => { | |||
| getWorkspaceOverview(); | |||
| }, []); | |||
| @@ -43,27 +46,38 @@ function Workspace() { | |||
| return ( | |||
| <div className={styles.workspace}> | |||
| <WorkspaceIntro></WorkspaceIntro> | |||
| <div className={styles['workspace__overview']}> | |||
| <div className={styles['workspace__overview__title']}>运行概览</div> | |||
| <div className={styles['workspace__overview__content']}> | |||
| <TotalStatistics | |||
| icon={require('@/assets/img/workspace-pipeline.png')} | |||
| title="流水线总数" | |||
| count={overviewData?.workflowCount} | |||
| /> | |||
| <TotalStatistics | |||
| icon={require('@/assets/img/workspace-experiment.png')} | |||
| title="正在运行实例总数" | |||
| count={overviewData?.runningExperimentInsCount} | |||
| /> | |||
| <ExperitableTable | |||
| tableData={overviewData?.latestExperimentInsList || []} | |||
| ></ExperitableTable> | |||
| {overviewData?.experimentInsStatus && ( | |||
| <ExperimentChart chartData={overviewData?.experimentInsStatus}></ExperimentChart> | |||
| )} | |||
| <Flex gap={'0 15px'} wrap> | |||
| <div className={styles['workspace__overview']}> | |||
| <div className={styles['workspace__overview__title']}>运行概览</div> | |||
| <div className={styles['workspace__overview__content']}> | |||
| <Flex align="center" className={styles['workspace__overview__content__statistics']}> | |||
| <TotalStatistics | |||
| icon={require('@/assets/img/workspace-pipeline.png')} | |||
| title="流水线总数" | |||
| count={overviewData?.workflowCount} | |||
| /> | |||
| <Divider | |||
| type="vertical" | |||
| dashed | |||
| style={{ color: '1px dashed rgba(96, 107, 122, 0.19)', height: 68 }} | |||
| /> | |||
| <TotalStatistics | |||
| icon={require('@/assets/img/workspace-experiment.png')} | |||
| title="正在运行实例总数" | |||
| count={overviewData?.runningExperimentInsCount} | |||
| /> | |||
| </Flex> | |||
| <ExperitableTable | |||
| tableData={overviewData?.latestExperimentInsList || []} | |||
| ></ExperitableTable> | |||
| {overviewData?.experimentInsStatus && ( | |||
| <ExperimentChart chartData={overviewData?.experimentInsStatus}></ExperimentChart> | |||
| )} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <UserPoints /> | |||
| </Flex> | |||
| <div className={styles['workspace__quick-start']}> | |||
| <QuickStart></QuickStart> | |||
| <div className={styles['workspace__user']}> | |||
| @@ -0,0 +1,22 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2025-03-20 13:48:53 | |||
| * @Description: 积分 | |||
| */ | |||
| import { request } from '@umijs/max'; | |||
| // 分页查询积分消费明细 | |||
| export function getPointsConsumptionReq(params: any) { | |||
| return request(`/api/mmp/computingResource/resouceOccupy`, { | |||
| method: 'GET', | |||
| params, | |||
| }); | |||
| } | |||
| // 分页查询积分消费明细 | |||
| export function getPointsStatisticsReq() { | |||
| return request(`/api/mmp/computingResource/credit`, { | |||
| method: 'GET', | |||
| }); | |||
| } | |||
| @@ -0,0 +1,5 @@ | |||
| import { Meta } from '@storybook/blocks'; | |||
| <Meta title="Documentation/Utils" /> | |||
| <a href="/docs/index.html" target='_blank'>工具栏文档</a> | |||
| @@ -69,6 +69,14 @@ | |||
| -webkit-line-clamp: @line; | |||
| } | |||
| // 背景 | |||
| .backgroundFullImage(@url) { | |||
| background-image: @url; | |||
| background-repeat: no-repeat; | |||
| background-position: top center; | |||
| background-size: 100% 100%; | |||
| } | |||
| // 导出变量 | |||
| :export { | |||
| primaryColor: @primary-color; | |||
| @@ -21,6 +21,7 @@ declare namespace API.System { | |||
| remark: string; | |||
| gitLinkUsername?: string; | |||
| gitLinkPassword?: string; | |||
| credit?: number; | |||
| } | |||
| export interface UserListParams { | |||
| @@ -7,7 +7,10 @@ | |||
| import { PageEnum } from '@/enums/pagesEnums'; | |||
| import G6 from '@antv/g6'; | |||
| // 生成 8 位随机数 | |||
| /** | |||
| * 生成 8 位随机数 | |||
| * @returns 8 位随机数 | |||
| */ | |||
| export function s8() { | |||
| return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1); | |||
| } | |||
| @@ -20,7 +23,12 @@ export function getNameByCode(list: any[], code: any) { | |||
| return name; | |||
| } | |||
| // 解析 json 字符串 | |||
| /** | |||
| * 完全解析 json 字符串,不会抛异常 | |||
| * | |||
| * @param text - the string to be parsed | |||
| * @returns the parsed JSON object if the string is a valid JSON text, null otherwise | |||
| */ | |||
| export function parseJsonText(text?: string | null): any | null { | |||
| if (text === undefined || text === null || text === '') { | |||
| return null; | |||
| @@ -32,8 +40,17 @@ export function parseJsonText(text?: string | null): any | null { | |||
| } | |||
| } | |||
| // 判断是否为一般对象 | |||
| function isPlainObject(value: any) { | |||
| /** | |||
| * Checks if a given value is a plain object. | |||
| * | |||
| * A plain object is an object that is created using the Object constructor. | |||
| * It does not have any special properties or methods that are not part of the | |||
| * standard Object prototype. | |||
| * | |||
| * @param {any} value - The value to be checked. | |||
| * @returns {boolean} true if the value is a plain object, false otherwise. | |||
| */ | |||
| function isPlainObject(value: any): boolean { | |||
| if (value === null || typeof value !== 'object') return false; | |||
| let proto = Object.getPrototypeOf(value); | |||
| while (proto !== null) { | |||
| @@ -43,7 +60,14 @@ function isPlainObject(value: any) { | |||
| return true; | |||
| } | |||
| // underscore to camelCase | |||
| /** | |||
| * Converts all property names in an object from underscore notation to camelCase. | |||
| * | |||
| * If the object is not a plain object, it is returned as is. | |||
| * | |||
| * @param obj - The object whose property names need to be converted. | |||
| * @returns The object with all property names converted to camelCase. | |||
| */ | |||
| export function underscoreToCamelCase(obj: Record<string, any>) { | |||
| if (!isPlainObject(obj)) { | |||
| return obj; | |||
| @@ -66,7 +90,14 @@ export function underscoreToCamelCase(obj: Record<string, any>) { | |||
| return newObj; | |||
| } | |||
| // camelCase to underscore | |||
| /** | |||
| * Converts all property names in an object from camelCase to underscore notation. | |||
| * | |||
| * If the object is not a plain object, it is returned as is. | |||
| * | |||
| * @param obj - The object whose property names need to be converted. | |||
| * @returns The object with all property names converted to underscore notation. | |||
| */ | |||
| export function camelCaseToUnderscore(obj: Record<string, any>) { | |||
| if (!isPlainObject(obj)) { | |||
| return obj; | |||
| @@ -87,7 +118,14 @@ export function camelCaseToUnderscore(obj: Record<string, any>) { | |||
| return newObj; | |||
| } | |||
| // null to undefined | |||
| /** | |||
| * Recursively converts all null values in an object to undefined. | |||
| * | |||
| * If the given value is not an object, it is returned as is. | |||
| * | |||
| * @param obj - The object whose null values need to be converted. | |||
| * @returns The object with all null values converted to undefined. | |||
| */ | |||
| export function nullToUndefined(obj: Record<string, any> | null) { | |||
| if (obj === null) { | |||
| return undefined; | |||
| @@ -113,7 +151,15 @@ export function nullToUndefined(obj: Record<string, any> | null) { | |||
| return newObj; | |||
| } | |||
| // undefined to null | |||
| /** | |||
| * Recursively converts all undefined values in an object to null. | |||
| * | |||
| * If the given value is not an object, it is returned as is. | |||
| * | |||
| * @param obj - The object whose undefined values need to be converted. | |||
| * @returns The object with all undefined values converted to null. | |||
| */ | |||
| export function undefinedToNull(obj?: Record<string, any>) { | |||
| if (obj === undefined) { | |||
| return null; | |||
| @@ -204,10 +250,20 @@ export const fittingString = (str: string, maxWidth: number, fontSize: number): | |||
| * @param {any} str - the string to be checked | |||
| * @return {boolean} true if the string is empty, undefined, or null, false otherwise | |||
| */ | |||
| export const isEmpty = (str: any): boolean => { | |||
| export const isEmpty = (str?: any | null): boolean => { | |||
| return str === '' || str === undefined || str === null; | |||
| }; | |||
| /** | |||
| * Checks if a given value is undefined or null. | |||
| * | |||
| * @param {any} value - the value to be checked | |||
| * @return {boolean} true if the value is undefined or null, false otherwise | |||
| */ | |||
| export const hasNoValue = (value?: any | null): boolean => { | |||
| return value === undefined || value === null; | |||
| }; | |||
| /** | |||
| * 获取 git 仓库的 url | |||
| * | |||
| @@ -226,12 +282,30 @@ export const getGitUrl = (url: string, branch: string): string => { | |||
| return branch ? `${gitUrl}/tree/${branch}` : gitUrl; | |||
| }; | |||
| // 判断是否需要登录 | |||
| export const needAuth = (pathname: string) => { | |||
| /** | |||
| * 判断是否需要登录 | |||
| * | |||
| * @param {string} pathname - the pathname to be checked | |||
| * @return {boolean} true if the pathname needs to be authorized, false otherwise | |||
| */ | |||
| export const needAuth = (pathname: string): boolean => { | |||
| return pathname !== PageEnum.LOGIN && pathname !== PageEnum.Authorize; | |||
| }; | |||
| // 表格排序 | |||
| /** | |||
| * 表格排序 | |||
| * | |||
| * @param {any} a - the first value to be compared | |||
| * @param {any} b - the second value to be compared | |||
| * @returns {number} -1 if a is less than b, 1 if a is greater than b, 0 if a is equal to b | |||
| * | |||
| * The sorter function compares two values and returns a number indicating their relative order. | |||
| * The function first checks if either value is null or undefined. If so, it returns -1 if b is null or undefined, | |||
| * and 1 if a is null or undefined. | |||
| * If both values are numbers, it returns the difference between the two numbers. | |||
| * If both values are strings, it returns the result of the localeCompare() method. | |||
| * Otherwise, it returns 0. | |||
| */ | |||
| export const tableSorter = (a: any, b: any) => { | |||
| if (b === null || b === undefined) { | |||
| return -1; | |||
| @@ -0,0 +1,23 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2025-03-20 14:33:01 | |||
| * @Description: 通用的 Table 状态单元格 | |||
| */ | |||
| export type StatusInfo = { | |||
| value: number | string; | |||
| label: string; | |||
| color?: string; | |||
| }; | |||
| function statusTableCell(infos: StatusInfo[]) { | |||
| return function (status?: string | number | null) { | |||
| const info = infos.find((item) => item.value === status); | |||
| if (status === null || status === undefined || !info) { | |||
| return <span>--</span>; | |||
| } | |||
| return <span style={{ color: info.color }}>{info.label}</span>; | |||
| }; | |||
| } | |||
| export default statusTableCell; | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-06-26 10:05:52 | |||
| * @Description: Table cell 自定义 render | |||
| * @Description: Table Cell 自定义 render | |||
| */ | |||
| import { isEmpty } from '@/utils'; | |||
| @@ -0,0 +1,11 @@ | |||
| { | |||
| "entryPoints": ["./src/utils"], | |||
| "entryPointStrategy": "expand", | |||
| "out": "docs", | |||
| "excludePrivate": true, | |||
| "excludeProtected": false, | |||
| "excludeExternals": true, | |||
| "includeVersion": true, | |||
| "categorizeByGroup": true, | |||
| "name": "工具类文档" | |||
| } | |||
| @@ -7,12 +7,15 @@ import com.ruoyi.platform.mapper.DevEnvironmentDao; | |||
| import com.ruoyi.platform.mapper.ServiceDao; | |||
| import com.ruoyi.platform.service.JupyterService; | |||
| import com.ruoyi.platform.service.ResourceOccupyService; | |||
| import com.ruoyi.platform.service.ServiceService; | |||
| import com.ruoyi.platform.vo.PodStatusVo; | |||
| import org.springframework.scheduling.annotation.Scheduled; | |||
| import org.springframework.stereotype.Component; | |||
| import javax.annotation.Resource; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import java.util.stream.Collectors; | |||
| @Component() | |||
| public class ResourceOccupyTask { | |||
| @@ -26,6 +29,9 @@ public class ResourceOccupyTask { | |||
| @Resource | |||
| private ServiceDao serviceDao; | |||
| @Resource | |||
| private ServiceService serviceService; | |||
| @Resource | |||
| private JupyterService jupyterService; | |||
| @@ -50,8 +56,13 @@ public class ResourceOccupyTask { | |||
| @Scheduled(cron = "0 0/1 * * * ?") // 每1分钟执行一次 | |||
| public void serviceDeduceCredit() { | |||
| List<ServiceVersion> serviceVersions = serviceDao.getRunning(); | |||
| List<String> deploymentNames = serviceVersions.stream().map(ServiceVersion::getDeploymentName).collect(Collectors.toList()); | |||
| Map<String, String> runStates = serviceService.getRunState(deploymentNames); | |||
| serviceService.updateRunState(runStates, serviceVersions); | |||
| for (ServiceVersion serviceVersion : serviceVersions) { | |||
| resourceOccupyService.deducing(Constant.TaskType_Service, null, serviceVersion.getId(), null, null); | |||
| if (Constant.Running.equals(serviceVersion.getRunState())) { | |||
| resourceOccupyService.deducing(Constant.TaskType_Service, null, serviceVersion.getId(), null, null); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -45,4 +45,8 @@ public interface ServiceService { | |||
| Map<String, Object> getServiceVersionDocs(Long id); | |||
| List<ServiceVersion> serviceVersionList(Long id); | |||
| Map<String, String> getRunState(List<String> deploymentNames); | |||
| void updateRunState(Map<String, String> runStates, List<ServiceVersion> serviceVersionList); | |||
| } | |||
| @@ -97,9 +97,8 @@ public class JupyterServiceImpl implements JupyterService { | |||
| // String modelPath = "argo-workflow" + "/" + model.get("path"); | |||
| String modelPath = (String) model.get("path"); | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| //构造pod名称 | |||
| String podName = loginUser.getUsername().toLowerCase() + "-editor-pod" + "-" + id; | |||
| String podName = devEnvironment.getCreateBy() + "-editor-pod" + "-" + id; | |||
| //新建编辑器的pvc | |||
| // String pvcName = loginUser.getUsername().toLowerCase() + "-editor-pvc"; | |||
| // V1PersistentVolumeClaim pvc = k8sClientUtil.createPvc(namespace, pvcName, storage, storageClassName); | |||
| @@ -123,10 +122,9 @@ public class JupyterServiceImpl implements JupyterService { | |||
| if (devEnvironment == null) { | |||
| throw new Exception("开发环境配置不存在"); | |||
| } | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| //构造pod和svc名称 | |||
| String podName = loginUser.getUsername().toLowerCase() + "-editor-pod" + "-" + id; | |||
| String svcName = loginUser.getUsername().toLowerCase() + "-editor-pod" + "-" + id + "-svc"; | |||
| String podName = devEnvironment.getCreateBy() + "-editor-pod" + "-" + id; | |||
| String svcName = devEnvironment.getCreateBy() + "-editor-pod" + "-" + id + "-svc"; | |||
| //得到pod | |||
| V1Pod pod = k8sClientUtil.getNSPodList(namespace, podName); | |||
| if (pod == null) { | |||
| @@ -155,8 +153,7 @@ public class JupyterServiceImpl implements JupyterService { | |||
| if (devEnvironment == null) { | |||
| return JupyterStatusVo; | |||
| } | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String podName = loginUser.getUsername().toLowerCase() + "-editor-pod" + "-" + devEnvironment.getId(); | |||
| String podName = devEnvironment.getCreateBy() + "-editor-pod" + "-" + devEnvironment.getId(); | |||
| try { | |||
| // 查询相应pod状态 | |||
| @@ -401,7 +401,8 @@ public class ServiceServiceImpl implements ServiceService { | |||
| return serviceVersionVo; | |||
| } | |||
| Map<String, String> getRunState(List<String> deploymentNames) { | |||
| @Override | |||
| public Map<String, String> getRunState(List<String> deploymentNames) { | |||
| HashMap<String, Object> paramMap = new HashMap<>(); | |||
| paramMap.put("deployment_names", deploymentNames); | |||
| String req = HttpUtils.sendPost(argoUrl + modelService + "/getStatus", JSON.toJSONString(paramMap)); | |||
| @@ -417,7 +418,8 @@ public class ServiceServiceImpl implements ServiceService { | |||
| } | |||
| } | |||
| void updateRunState(Map<String, String> runStates, List<ServiceVersion> serviceVersionList) { | |||
| @Override | |||
| public void updateRunState(Map<String, String> runStates, List<ServiceVersion> serviceVersionList) { | |||
| for (ServiceVersion sv : serviceVersionList) { | |||
| String runState = runStates.get(sv.getDeploymentName()); | |||
| sv.setRunState(runState); | |||
| @@ -56,9 +56,9 @@ | |||
| </sql> | |||
| <select id="getServiceById" resultType="com.ruoyi.platform.domain.Service"> | |||
| select a.*,count(b.id) as version_count | |||
| select a.*, count(b.id) as version_count | |||
| from service a | |||
| left join (select * from service_version where state = 1) b on a.id = b.service_id | |||
| left join (select * from service_version where state = 1) b on a.id = b.service_id | |||
| where a.id = #{id} | |||
| </select> | |||
| @@ -75,31 +75,43 @@ | |||
| </select> | |||
| <select id="getServiceByName" resultType="com.ruoyi.platform.domain.Service"> | |||
| select * from service where service_name = #{serviceName} and state = 1 | |||
| select * | |||
| from service | |||
| where service_name = #{serviceName} | |||
| and state = 1 | |||
| </select> | |||
| <select id="getSvByVersion" resultType="com.ruoyi.platform.domain.ServiceVersion"> | |||
| select * | |||
| from service_version | |||
| where service_id = #{serviceId} and version = #{version} and state = 1 | |||
| where service_id = #{serviceId} | |||
| and version = #{version} | |||
| and state = 1 | |||
| </select> | |||
| <select id="getRunning" resultType="com.ruoyi.platform.domain.ServiceVersion"> | |||
| select * | |||
| from service_version where state = 1 and run_state = 'Running' | |||
| from service_version | |||
| where state = 1 | |||
| and (run_state = 'Running' or run_state = 'Pending' or run_state = 'Init') | |||
| </select> | |||
| <insert id="insertService" keyProperty="id" useGeneratedKeys="true"> | |||
| insert into service(service_name, service_type, description, create_by, update_by) | |||
| values (#{service.serviceName}, #{service.serviceType}, #{service.description}, #{service.createBy}, #{service.updateBy}) | |||
| values (#{service.serviceName}, #{service.serviceType}, #{service.description}, #{service.createBy}, | |||
| #{service.updateBy}) | |||
| </insert> | |||
| <insert id="insertServiceVersion" keyProperty="id" useGeneratedKeys="true"> | |||
| insert into service_version(service_id, version, model, description, image, resource, computing_resource_id, replicas, mount_path, env_variables, | |||
| insert into service_version(service_id, version, model, description, image, resource, computing_resource_id, | |||
| replicas, mount_path, env_variables, | |||
| code_config, command, create_by, update_by, deploy_type) | |||
| values (#{serviceVersion.serviceId}, #{serviceVersion.version}, #{serviceVersion.model}, #{serviceVersion.description}, #{serviceVersion.image}, | |||
| #{serviceVersion.resource}, #{serviceVersion.computingResourceId}, #{serviceVersion.replicas}, #{serviceVersion.mountPath}, #{serviceVersion.envVariables}, | |||
| #{serviceVersion.codeConfig}, #{serviceVersion.command}, #{serviceVersion.createBy}, #{serviceVersion.updateBy}, #{serviceVersion.deployType}) | |||
| values (#{serviceVersion.serviceId}, #{serviceVersion.version}, #{serviceVersion.model}, | |||
| #{serviceVersion.description}, #{serviceVersion.image}, | |||
| #{serviceVersion.resource}, #{serviceVersion.computingResourceId}, #{serviceVersion.replicas}, | |||
| #{serviceVersion.mountPath}, #{serviceVersion.envVariables}, | |||
| #{serviceVersion.codeConfig}, #{serviceVersion.command}, #{serviceVersion.createBy}, | |||
| #{serviceVersion.updateBy}, #{serviceVersion.deployType}) | |||
| </insert> | |||
| <update id="updateService"> | |||