From 6e582afeac9fec9e00a7be4b6f9a395a1c8a471e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=95=85=E6=99=9A?= Date: Tue, 29 Oct 2024 10:21:41 +0800 Subject: [PATCH 1/3] PullRequest: 556 fix/202407 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge branch 'fix/202407 of git@code.alipay.com:oceanbase/oceanbase-developer-center.git into dev-4.3.2 https://code.alipay.com/oceanbase/oceanbase-developer-center/pull_requests/556 Signed-off-by: 晓康 * fix: fix back to home url whrn haveocp * fix: hidden osc swap when task is failed --- .../CommonDetailModal/TaskProgress/colums.tsx | 8 ++++++-- .../CommonDetailModal/TaskProgress/index.tsx | 14 +++++++++----- src/page/Workspace/ActivityBar/Logo.tsx | 6 +++++- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/component/Task/component/CommonDetailModal/TaskProgress/colums.tsx b/src/component/Task/component/CommonDetailModal/TaskProgress/colums.tsx index 8e0365cd9..b10e3e126 100644 --- a/src/component/Task/component/CommonDetailModal/TaskProgress/colums.tsx +++ b/src/component/Task/component/CommonDetailModal/TaskProgress/colums.tsx @@ -2,7 +2,7 @@ import { getDataSourceStyleByConnectType } from '@/common/datasource'; import Action from '@/component/Action'; import RiskLevelLabel from '@/component/RiskLevelLabel'; import StatusLabel from '@/component/Task/component/Status'; -import { TaskType } from '@/d.ts'; +import { TaskStatus, TaskType } from '@/d.ts'; import { formatMessage } from '@/util/intl'; import { getLocalFormatDateTime } from '@/util/utils'; import Icon from '@ant-design/icons'; @@ -13,6 +13,7 @@ const { Link } = Typography; const getColumns = (params: { handleDetailVisible: (id: number) => void; onSwapTable: (id: number) => void; + taskStatus: TaskStatus; }) => { return [ { @@ -50,6 +51,7 @@ const getColumns = (params: { width: 120, render: (_, record) => { const resultJson = JSON.parse(record?.resultJson); + const isTaskFailed = [TaskStatus.EXECUTING]?.includes(params.taskStatus); return ( <> - {resultJson?.manualSwapTableEnabled && ( + {resultJson?.manualSwapTableEnabled && !isTaskFailed && ( { params?.onSwapTable(record?.id); @@ -234,6 +236,7 @@ export const getColumnsByTaskType = ( handleSwapTable; handleMultipleAsyncOpen; }, + status: TaskStatus, ) => { switch (type) { case TaskType.MULTIPLE_ASYNC: { @@ -245,6 +248,7 @@ export const getColumnsByTaskType = ( return getColumns({ handleDetailVisible: params?.handleDetailVisible, onSwapTable: params?.handleSwapTable, + taskStatus: status, }); } } diff --git a/src/component/Task/component/CommonDetailModal/TaskProgress/index.tsx b/src/component/Task/component/CommonDetailModal/TaskProgress/index.tsx index b31ee90dc..6455c654d 100644 --- a/src/component/Task/component/CommonDetailModal/TaskProgress/index.tsx +++ b/src/component/Task/component/CommonDetailModal/TaskProgress/index.tsx @@ -139,11 +139,15 @@ const TaskProgress: React.FC = (props) => { }, []); // #endregion - const columns = getColumnsByTaskType(task?.type, { - handleDetailVisible, - handleMultipleAsyncOpen, - handleSwapTable, - }); + const columns = getColumnsByTaskType( + task?.type, + { + handleDetailVisible, + handleMultipleAsyncOpen, + handleSwapTable, + }, + task.status, + ); return ( <> diff --git a/src/page/Workspace/ActivityBar/Logo.tsx b/src/page/Workspace/ActivityBar/Logo.tsx index ccec5ca68..948de4c39 100644 --- a/src/page/Workspace/ActivityBar/Logo.tsx +++ b/src/page/Workspace/ActivityBar/Logo.tsx @@ -16,7 +16,7 @@ import { formatMessage } from '@/util/intl'; */ import { ReactComponent as ODCBlackSvg } from '@/svgr/odc_logo_color.svg'; -import { isClient } from '@/util/env'; +import { haveOCP, isClient } from '@/util/env'; import Icon, { HomeOutlined } from '@ant-design/icons'; import { Tooltip } from 'antd'; import { useState } from 'react'; @@ -29,6 +29,10 @@ export default function Logo() { const backToHome = () => { if (isClient()) return; if (login.isPrivateSpace()) return; + if (haveOCP) { + window.open(location.origin + location.pathname); + return; + } window.open(location.origin + '/#/project'); }; From c5abd36de6ea3157fca42d4f18ea6d92cc4dfabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E5=AE=BD?= Date: Tue, 29 Oct 2024 14:11:43 +0800 Subject: [PATCH 2/3] =?UTF-8?q?PullRequest:=20557=20feat/=E6=97=A0?= =?UTF-8?q?=E9=94=81=E7=BB=93=E6=9E=84=E5=8F=98=E6=9B=B4433=E6=8C=AA?= =?UTF-8?q?=E5=8A=A8=E5=88=B0432?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge branch 'feat/no_lock of git@code.alipay.com:oceanbase/oceanbase-developer-center.git into dev-4.3.2 https://code.alipay.com/oceanbase/oceanbase-developer-center/pull_requests/557 Signed-off-by: 晓康 * feat/无锁结构变更433挪动到432 * feat/恢复ActionBar部分代码 * feat/国际化文案恢复 --- src/common/network/task.ts | 33 ++++- src/component/Action/Item.tsx | 4 +- .../Task/AlterDdlTask/CreateModal/index.tsx | 100 +++++++++++++-- .../Task/component/ActionBar/index.tsx | 38 +++++- .../TaskProgress/ProgressDetailsModal.tsx | 115 ++++++++++++++++++ .../CommonDetailModal/TaskProgress/colums.tsx | 57 +++++++-- .../CommonDetailModal/TaskProgress/index.tsx | 19 ++- src/component/Task/component/Status/index.tsx | 30 +++-- .../Task/component/ThrottleFormItem/index.tsx | 38 ------ src/d.ts/index.ts | 22 +++- 10 files changed, 374 insertions(+), 82 deletions(-) create mode 100644 src/component/Task/component/CommonDetailModal/TaskProgress/ProgressDetailsModal.tsx diff --git a/src/common/network/task.ts b/src/common/network/task.ts index 034d931bf..5ef7b6418 100644 --- a/src/common/network/task.ts +++ b/src/common/network/task.ts @@ -16,6 +16,7 @@ import { IShadowSyncAnalysisResult } from '@/component/Task/ShadowSyncTask/CreateModal/interface'; import { + AgainTaskRecord, CommonTaskLogType, CreateStructureComparisonTaskRecord, CreateTaskRecord, @@ -563,7 +564,9 @@ export async function getDataArchiveSubTask( size?: number; }, ): Promise> { - const res = await request.get(`/api/v2/schedule/schedules/${taskId}/tasks`, { params }); + const res = await request.get(`/api/v2/schedule/schedules/${taskId}/tasks`, { + params, + }); return res?.data; } @@ -715,3 +718,31 @@ export async function getStructrueComparisonDetail( ); return res?.data; } + +/** + * 重试 + * 无锁结构变更 执行异常时发起重试 + */ +export async function againTask(data: Partial): Promise { + const res = await request.post(`/api/v2/osc/${data.id}/resume`); + + return res?.successful; +} + +/** + * 无锁结构变更阿里云检查用户OMS资源 + */ +export async function queryOmsWorkerInstance(): Promise<{ + successful: boolean; + data: { hasUnconfiguredProject?: boolean }; +}> { + const res = await request.get(`/api/v2/aliyun/osc/queryOmsWorkerInstance`); + + if (res.successful) { + return { + successful: res.successful, + data: JSON.parse(res.data || {}), + }; + } + return res; +} diff --git a/src/component/Action/Item.tsx b/src/component/Action/Item.tsx index 88b43a8f7..ee0434675 100644 --- a/src/component/Action/Item.tsx +++ b/src/component/Action/Item.tsx @@ -17,7 +17,7 @@ import { LoadingOutlined } from '@ant-design/icons'; import { Button, Tooltip, Typography } from 'antd'; import { TooltipPlacement } from 'antd/lib/tooltip'; -import React from 'react'; +import React, { ReactNode } from 'react'; export interface BaseProps { /** 是否显示 */ @@ -28,7 +28,7 @@ export interface BaseProps { type?: 'default' | 'primary'; className?: string; enableLoading?: boolean; - tooltip?: string; + tooltip?: string | React.ReactElement; placement?: TooltipPlacement; loading?: boolean; /** loading的时候覆盖children,用于icon的场景 */ diff --git a/src/component/Task/AlterDdlTask/CreateModal/index.tsx b/src/component/Task/AlterDdlTask/CreateModal/index.tsx index 62ad00862..781710f9c 100644 --- a/src/component/Task/AlterDdlTask/CreateModal/index.tsx +++ b/src/component/Task/AlterDdlTask/CreateModal/index.tsx @@ -15,7 +15,12 @@ */ import { getDataSourceModeConfig } from '@/common/datasource'; -import { createTask, getDatasourceUsers, getLockDatabaseUserRequired } from '@/common/network/task'; +import { + createTask, + getDatasourceUsers, + getLockDatabaseUserRequired, + queryOmsWorkerInstance, +} from '@/common/network/task'; import CommonIDE from '@/component/CommonIDE'; import FormItemPanel from '@/component/FormItemPanel'; import HelpDoc from '@/component/helpDoc'; @@ -40,6 +45,7 @@ import { Row, Select, Space, + Tooltip, } from 'antd'; import { inject, observer } from 'mobx-react'; import React, { useEffect, useState } from 'react'; @@ -48,6 +54,7 @@ import ThrottleFormItem from '../../component/ThrottleFormItem'; import { OscMaxDataSizeLimit, OscMaxRowLimit } from '../../const'; import styles from './index.less'; +import { isBoolean } from 'lodash'; interface IProps { modalStore?: ModalStore; @@ -78,6 +85,7 @@ const CreateDDLTaskModal: React.FC = (props) => { const [hasEdit, setHasEdit] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false); const [datasourceUser, setDatasourceUser] = useState([]); + const [canCreateTask, setCanCreateTask] = useState(true); // 判断是否可以进行创建任务 const [lockDatabaseUserRequired, setLockDatabaseUserRequired] = useState(false); const databaseId = Form.useWatch('databaseId', form); const { database } = useDBSession(databaseId); @@ -90,6 +98,12 @@ const CreateDDLTaskModal: React.FC = (props) => { label: name, value: name, })); + + // 当前路径处理 为了处理跳转到其他页面 + const { origin, pathname } = new URL(window.location.href); + // /截取出来后第一个为空值,进行剔除 + const [regionId] = pathname.split('/').slice(1); + const hadleReset = () => { form.resetFields(null); setHasEdit(false); @@ -217,6 +231,22 @@ const CreateDDLTaskModal: React.FC = (props) => { } }, [ddlAlterData?.databaseId]); + useEffect(() => { + if (!modalStore.createDDLAlterVisible) return; + settingStore.enableOSCLimiting && + queryOmsWorkerInstance().then((res) => { + //接口返回正常 && hasUnconfiguredProject有值且为布尔类型的 false 时 禁用新建 + if ( + isBoolean(res?.data?.hasUnconfiguredProject) && + !res?.data?.hasUnconfiguredProject && + res.successful + ) { + setCanCreateTask(false); + return; + } + }); + }, [modalStore.createDDLAlterVisible]); + return ( = (props) => { }) /*取消*/ } - + + + + } open={modalStore.createDDLAlterVisible} @@ -273,7 +311,7 @@ const CreateDDLTaskModal: React.FC = (props) => { formatMessage({ id: 'odc.src.component.Task.AlterDdlTask.CreateModal.1BeforePerformingThe', defaultMessage: '1. 执行无锁结构变更前请确保数据库服务器磁盘空间充足;', - }) /* + }) /* 1、执行无锁结构变更前请确保数据库服务器磁盘空间充足; */ } @@ -283,7 +321,7 @@ const CreateDDLTaskModal: React.FC = (props) => { formatMessage({ id: 'odc.src.component.Task.AlterDdlTask.CreateModal.2WhenCreatingA', defaultMessage: '2. 创建工单选择源表清理策略时建议选择保留源表;', - }) /* + }) /* 2、创建工单选择源表清理策略时建议选择保留源表; */ } @@ -296,7 +334,7 @@ const CreateDDLTaskModal: React.FC = (props) => { id: 'odc.src.component.Task.AlterDdlTask.CreateModal.3IfTheOB', defaultMessage: '3. 若 OceanBase Oracle 模式版本小于 4.0.0 或 OceanBase MySQL 模式版本小于 4.3.0,表名切换之前会锁定您指定的数据库账号,并关闭该账号对应的会话。表名切换期间,锁定账号涉及应用将无法访问数据库,请勿在业务高峰期执行;', - }) /* + }) /* 3、若 OB Oracle 模式版本小于 4.0 或 OB MySQL 模式版本小于 4.3,表名切换之前会锁定您指定的数据库账号,并 kill 该账号对应的 session。表名切换期间,锁定账号涉及应用将无法访问数据库,请勿在业务高峰期执行; @@ -307,6 +345,42 @@ const CreateDDLTaskModal: React.FC = (props) => { } /> + {!canCreateTask && ( + + 依赖 + + 数据迁移服务 + + 完成数据拷贝,检查到您没有空闲的数据迁移资源。 +
+ 请进行 + + 购买 + + ,重新配置任务。 + + } + /> + )}
= (props) => { formatMessage({ id: 'odc.src.component.Task.AlterDdlTask.CreateModal.LockUsers.1', defaultMessage: '锁定用户', - }) /* + }) /* 锁定用户 */ } diff --git a/src/component/Task/component/ActionBar/index.tsx b/src/component/Task/component/ActionBar/index.tsx index 0d0d5431d..ca83ce22d 100644 --- a/src/component/Task/component/ActionBar/index.tsx +++ b/src/component/Task/component/ActionBar/index.tsx @@ -15,6 +15,7 @@ */ import { + againTask, createTask, downloadTaskFlow, executeTask, @@ -61,7 +62,6 @@ import React, { useEffect, useState } from 'react'; import { isCycleTask, isLogicalDbChangeTask } from '../../helper'; import RollBackModal from '../RollbackModal'; import { useRequest } from 'ahooks'; - interface IProps { userStore?: UserStore; taskStore?: TaskStore; @@ -104,19 +104,20 @@ const ActionBar: React.FC = inject( const [openRollback, setOpenRollback] = useState(false); const [taskList, setTaskList] = useState([]); + const disabledApproval = + task?.status === TaskStatus.WAIT_FOR_CONFIRM && !isDetailModal ? true : disabledSubmit; + useEffect(() => { if (task?.id && isLogicalDbChangeTask(task?.type) && isDetailModal) { loadtaskList(); } return cancel(); }, [task?.id]); - const getScheduleTask = async () => { const taskList = await getDataArchiveSubTask(task?.id); setTaskList(taskList?.contents); return taskList?.contents; }; - const { run: loadtaskList, cancel } = useRequest(getScheduleTask, { pollingInterval: 3000, manual: true, @@ -131,9 +132,6 @@ const ActionBar: React.FC = inject( }, }); - const disabledApproval = - task?.status === TaskStatus.WAIT_FOR_CONFIRM && !isDetailModal ? true : disabledSubmit; - const openTaskDetail = async () => { props.onDetailVisible(task as TaskRecord, true); }; @@ -275,6 +273,17 @@ const ActionBar: React.FC = inject( } }; + const handleAgain = async () => { + const { id } = task; + + const res = await againTask({ id: id }); + if (res) { + message.success('发起重试成功'); + props?.onReloadList?.(); + props?.onReload?.(); + } + }; + const editCycleTask = async () => { props?.onClose?.(); switch (task?.type) { @@ -620,6 +629,14 @@ const ActionBar: React.FC = inject( action: handleReTry, }; + const againBtn = { + key: 'again', + text: '重试', + //再次发起 + type: 'button', + action: handleAgain, + }; + const downloadBtn = { key: 'download', text: formatMessage({ @@ -828,6 +845,15 @@ const ActionBar: React.FC = inject( tools = setProjectOwnerStopBtn(tools, stopBtn); break; } + case TaskStatus.EXECUTION_ABNORMAL: { + if (isOwner) { + tools = [reTryBtn, againBtn, stopBtn]; + } + if (isApprover) { + tools = []; + } + break; + } default: } diff --git a/src/component/Task/component/CommonDetailModal/TaskProgress/ProgressDetailsModal.tsx b/src/component/Task/component/CommonDetailModal/TaskProgress/ProgressDetailsModal.tsx new file mode 100644 index 000000000..3da0cea4c --- /dev/null +++ b/src/component/Task/component/CommonDetailModal/TaskProgress/ProgressDetailsModal.tsx @@ -0,0 +1,115 @@ +import { ProgressOfLocklessStructureChangeTaskStatusMap } from '@/d.ts'; +import { Button, Modal, Steps } from 'antd'; +import React, { useEffect, useState } from 'react'; + +export default ({ modalOpen, handleClose, parametersJson, resultJson }) => { + const { state } = parametersJson || {}; + const { manualSwapTableEnabled, fullTransferProgressPercentage, currentStep } = resultJson || {}; + const [stepsItems, setStepsItems] = useState([]); + const [stepsCurrent, setStepsCurrent] = useState(0); + + const statusList = [ + { + title: '创建影子表', + value: ProgressOfLocklessStructureChangeTaskStatusMap.CREATE_GHOST_TABLES, + }, + { + title: '创建数据迁移任务', + value: ProgressOfLocklessStructureChangeTaskStatusMap.CREATE_DATA_TASK, + }, + { + title: '数据迁移任务预检查', + value: ProgressOfLocklessStructureChangeTaskStatusMap.MONITOR_DATA_TASK_TRANSFER_PRECHECK, + }, + { + title: '数据迁移任务迁移全量数据', + value: ProgressOfLocklessStructureChangeTaskStatusMap.MONITOR_DATA_TASK_FULL_TRANSFER, + }, + { + title: '数据迁移任务迁移增量数据', + value: + ProgressOfLocklessStructureChangeTaskStatusMap.MONITOR_DATA_TASK_TRANSFER_APP_SWITCH_FALSE, + }, + { title: '切换中', value: ProgressOfLocklessStructureChangeTaskStatusMap.SWAP_TABLE }, + { + title: '释放迁移任务资源', + value: ProgressOfLocklessStructureChangeTaskStatusMap.CLEAR_RESOURCE, + }, + ]; + + useEffect(() => { + const tempStepsItems = statusList.map((item) => { + // manualSwapTableEnabled 为true 表示可以进行手动切换 + if (manualSwapTableEnabled && item.value === 'MONITOR_DATA_TASK_TRANSFER_APP_SWITCH_FALSE') { + return { + ...item, + title: '数据迁移任务迁移增量数据(切换就绪)', + }; + } + // manualSwapTableEnabled 为false 表示在迁移中 + if ( + manualSwapTableEnabled !== undefined && + !manualSwapTableEnabled && + state === 'MONITOR_DATA_TASK' && + currentStep === 'TRANSFER_APP_SWITCH' && + item.value === 'MONITOR_DATA_TASK_TRANSFER_APP_SWITCH_FALSE' + ) { + return { + ...item, + title: '数据迁移任务迁移增量数据(进行中)', + }; + } + + if (item.value === 'MONITOR_DATA_TASK_FULL_TRANSFER' && fullTransferProgressPercentage) { + return { + ...item, + title: `数据迁移任务迁移全量数据${ + fullTransferProgressPercentage < 100 + ? `(进度${parseInt(fullTransferProgressPercentage)}%)` + : '' + }`, + }; + } + return item; + }); + setStepsItems(tempStepsItems); + }, [state, manualSwapTableEnabled, fullTransferProgressPercentage]); + + useEffect(() => { + // COMPLETE 表示完成 + if (state === 'COMPLETE') { + setStepsCurrent(8); + return; + } + + let tempKey = `${state}`; + // MONITOR_DATA_TASK 数据迁移中的不同状态处理 + + if (state === 'MONITOR_DATA_TASK') { + // TRANSFER_INCR_LOG_PULL 和 TRANSFER_PRECHECK 表示 预检查中 + if (currentStep === 'TRANSFER_INCR_LOG_PULL') { + // TRANSFER_INCR_LOG_PULL 也表示预检查中 + tempKey += `_TRANSFER_PRECHECK`; + } else { + tempKey += `_${currentStep}`; + } + + // TRANSFER_APP_SWITCH 增量迁移 + if (manualSwapTableEnabled !== undefined && currentStep === 'TRANSFER_APP_SWITCH') { + tempKey += `_FALSE`; + } + } + + const keyIndex = stepsItems.findIndex((item) => item.value === tempKey); + + setStepsCurrent(keyIndex); + }, [state, currentStep, manualSwapTableEnabled]); + + return ( + <> + + + + + ); +}; diff --git a/src/component/Task/component/CommonDetailModal/TaskProgress/colums.tsx b/src/component/Task/component/CommonDetailModal/TaskProgress/colums.tsx index b10e3e126..4e3444eba 100644 --- a/src/component/Task/component/CommonDetailModal/TaskProgress/colums.tsx +++ b/src/component/Task/component/CommonDetailModal/TaskProgress/colums.tsx @@ -2,19 +2,40 @@ import { getDataSourceStyleByConnectType } from '@/common/datasource'; import Action from '@/component/Action'; import RiskLevelLabel from '@/component/RiskLevelLabel'; import StatusLabel from '@/component/Task/component/Status'; -import { TaskStatus, TaskType } from '@/d.ts'; +import { SubTaskStatus, TaskType, TaskStatus } from '@/d.ts'; import { formatMessage } from '@/util/intl'; import { getLocalFormatDateTime } from '@/util/utils'; -import Icon from '@ant-design/icons'; -import { Popover, Space, Typography } from 'antd'; +import Icon, { QuestionCircleOutlined } from '@ant-design/icons'; +import { Popover, Space, Tooltip, Typography } from 'antd'; const { Link } = Typography; const getColumns = (params: { handleDetailVisible: (id: number) => void; onSwapTable: (id: number) => void; + handleProgressDetailVisible: (id: number) => void; taskStatus: TaskStatus; }) => { + // 查看进度 提示文本 + const viewProgressTooltip = ( +
+
ODC无锁结构变更功能包含如下步骤,步骤按照先后执行
+
+ 1.创建影子表.该阶段为创建命名规则为 _${'{'}原始表名{'}'}_osc_new_ 的影子表 +
+
+ 2.创建数据迁移任务.该阶段为创建数据迁移服务.无锁结构变更依赖数据迁移服务进行原表到影子表的数据复制 +
+
3.数据迁移任务预检查.该阶段为数据迁移服务检查用户数据库是否满足迁移条件
+
4.数据迁移任务迁移全量数据. 该阶段为数据迁移服务复制静态数据到影子表
+
5.数据迁移服务补齐增量数据.该阶段为数据迁移服务应用增量变更数据到影子表
+
+ 6.切换表结构.该阶段为表切换阶段,在保证数据一致的前提下,原表重命名到 _$ {'{'}原始表名{'}'} + _osc_old_, 影子表重命名为原表 +
+
7.释放数据迁移任务资源.该阶段为释放数据迁移服务的相关资源
+
+ ); return [ { dataIndex: 'resultJson', @@ -50,20 +71,17 @@ const getColumns = (params: { ellipsis: true, width: 120, render: (_, record) => { + const { status } = record; const resultJson = JSON.parse(record?.resultJson); const isTaskFailed = [TaskStatus.EXECUTING]?.includes(params.taskStatus); return ( - <> + { params?.handleDetailVisible(record?.id); }} > - { - formatMessage({ - id: 'odc.component.CommonDetailModal.TaskProgress.View', - }) /*查看*/ - } + 查看结构 {resultJson?.manualSwapTableEnabled && !isTaskFailed && ( )} - + {/* 进行中和异常状态可查看进度 */} + {[SubTaskStatus.RUNNING, SubTaskStatus.ABNORMAL].includes(status) && ( + + { + params?.handleProgressDetailVisible(record?.id); + }} + > + 查看进度 + + + + + + )} + ); }, }, @@ -235,6 +268,7 @@ export const getColumnsByTaskType = ( handleDetailVisible; handleSwapTable; handleMultipleAsyncOpen; + handleProgressDetailVisible; }, status: TaskStatus, ) => { @@ -248,6 +282,7 @@ export const getColumnsByTaskType = ( return getColumns({ handleDetailVisible: params?.handleDetailVisible, onSwapTable: params?.handleSwapTable, + handleProgressDetailVisible: params?.handleProgressDetailVisible, taskStatus: status, }); } diff --git a/src/component/Task/component/CommonDetailModal/TaskProgress/index.tsx b/src/component/Task/component/CommonDetailModal/TaskProgress/index.tsx index 6455c654d..eefeeef92 100644 --- a/src/component/Task/component/CommonDetailModal/TaskProgress/index.tsx +++ b/src/component/Task/component/CommonDetailModal/TaskProgress/index.tsx @@ -36,6 +36,7 @@ import { getColumnsByTaskType } from './colums'; import styles from './index.less'; import TaskProgressDrawer from './TaskProgressDrawer'; import TaskProgressHeader from './TaskProgressHeader'; +import ProgressDetailsModal from './ProgressDetailsModal'; interface IProps { taskStore?: TaskStore; @@ -51,7 +52,7 @@ const TaskProgress: React.FC = (props) => { const [databases, setDatabases] = useState([]); const [detailId, setDetailId] = useState(null); const [drawerOpen, setDrawerOpen] = useState(false); - const [modalOpen, setModalOpen] = useState(false); + const [progressModalOpen, setProgressModalOpen] = useState(false); const { run: loadData } = useRequest( async () => { const res = await getSubTask(task.id); @@ -98,6 +99,7 @@ const TaskProgress: React.FC = (props) => { ); const subTask = subTasks?.find((item) => item.id === detailId); const resultJson = JSON.parse(subTask?.resultJson ?? '{}'); + const parametersJson = JSON.parse(subTask?.parametersJson ?? '{}'); const pendingExectionDatabases = databases?.filter((item) => !item?.status)?.length; // #endregion @@ -131,6 +133,14 @@ const TaskProgress: React.FC = (props) => { const handleClose = () => { setDrawerOpen(false); }; + + const handleProgressDetailVisible = (id: number) => { + setDetailId(id); + setProgressModalOpen(true); + }; + const handleProgressModalClose = () => { + setProgressModalOpen(false); + }; // #endregion // #region ------------------------- hooks ------------------------- @@ -145,6 +155,7 @@ const TaskProgress: React.FC = (props) => { handleDetailVisible, handleMultipleAsyncOpen, handleSwapTable, + handleProgressDetailVisible, }, task.status, ); @@ -171,6 +182,12 @@ const TaskProgress: React.FC = (props) => { resultJson={resultJson} handleClose={handleClose} /> + ); }; diff --git a/src/component/Task/component/Status/index.tsx b/src/component/Task/component/Status/index.tsx index 69af3f7cf..d782051cd 100644 --- a/src/component/Task/component/Status/index.tsx +++ b/src/component/Task/component/Status/index.tsx @@ -398,6 +398,17 @@ export const status = { defaultMessage: '已完成', }), //已完成 }, + [TaskStatus.EXECUTION_ABNORMAL]: { + icon: ( + + ), + + text: '执行异常', //执行异常 + }, }; // 周期任务状态 @@ -530,7 +541,6 @@ export const cycleStatus = { }} /> ), - text: formatMessage({ id: 'src.component.Task.component.Status.3DCF46EC', defaultMessage: '创建中', @@ -614,6 +624,17 @@ export const subTaskStatus = { defaultMessage: '执行中', }), //执行中 }, + [SubTaskStatus.ABNORMAL]: { + icon: ( + + ), + + text: '执行异常', + }, }; // 逻辑库-任务状态 @@ -626,7 +647,6 @@ export const logicDBChangeTaskStatus = { }} /> ), - text: formatMessage({ id: 'src.component.Task.component.Status.64D3C12B', defaultMessage: '执行失败', @@ -644,7 +664,6 @@ export const logicDBChangeTaskStatus = { }} /> ), - text: formatMessage({ id: 'src.component.Task.component.Status.1B9301A5', defaultMessage: '待执行', @@ -658,7 +677,6 @@ export const logicDBChangeTaskStatus = { }} /> ), - text: formatMessage({ id: 'src.component.Task.component.Status.2497FE39', defaultMessage: '执行中', @@ -672,7 +690,6 @@ export const logicDBChangeTaskStatus = { }} /> ), - text: formatMessage({ id: 'src.component.Task.component.Status.8FA22B8A', defaultMessage: '执行成功', @@ -693,7 +710,6 @@ export const logicDBChangeTaskStatus = { }} /> ), - text: formatMessage({ id: 'src.component.Task.component.Status.CA4AFAD3', defaultMessage: '跳过中', @@ -714,7 +730,6 @@ export const logicDBChangeTaskStatus = { }} /> ), - text: formatMessage({ id: 'src.component.Task.component.Status.726F85D7', defaultMessage: '终止中', @@ -728,7 +743,6 @@ export const logicDBChangeTaskStatus = { }} /> ), - text: formatMessage({ id: 'src.component.Task.component.Status.BB5E3650', defaultMessage: '终止失败', diff --git a/src/component/Task/component/ThrottleFormItem/index.tsx b/src/component/Task/component/ThrottleFormItem/index.tsx index fbbb532f0..825f9b6a1 100644 --- a/src/component/Task/component/ThrottleFormItem/index.tsx +++ b/src/component/Task/component/ThrottleFormItem/index.tsx @@ -82,44 +82,6 @@ const ThrottleFormItem: React.FC = (props) => { Rows/s - - - { - formatMessage({ - id: 'odc.src.component.Task.component.ThrottleFormItem.DataSizeLimit', - defaultMessage: '数据大小限流', - }) /* 数据大小限流 */ - } - - - - } - required - > - - - - - MB/s - - ); diff --git a/src/d.ts/index.ts b/src/d.ts/index.ts index 41ee7d1fc..d2846b548 100644 --- a/src/d.ts/index.ts +++ b/src/d.ts/index.ts @@ -137,6 +137,7 @@ export enum IManagerDetailTabs { ROLE = 'ROLE', TASK_FLOW = 'TASK_FLOW', } + /** * ODC_CONNECTION ODC_PROJECT @@ -3042,6 +3043,7 @@ export enum TaskStatus { TIMEOUT = 'TIMEOUT', PRE_CHECK_FAILED = 'PRE_CHECK_FAILED', CREATING = 'CREATING', + EXECUTION_ABNORMAL = 'EXECUTION_ABNORMAL', // 执行异常 } export enum SubTaskStatus { @@ -3049,8 +3051,9 @@ export enum SubTaskStatus { RUNNING = 'RUNNING', // 运行中 DONE = 'DONE', // 执行完成 FAILED = 'FAILED', // 执行失败 - CANCELED = 'CANCELED', -} // 执行取消 + CANCELED = 'CANCELED', // 执行取消 + ABNORMAL = 'ABNORMAL', // 执行异常 +} export enum StatusNodeType { FLOW_TASK = 'FLOW_TASK', @@ -3903,3 +3906,18 @@ export enum EStatus { APPROVAL = 'approval', DISABLED = 'disabled', } + +export interface AgainTaskRecord { + id: string | number; +} + +// 无锁结构变更任务进度状态 +export enum ProgressOfLocklessStructureChangeTaskStatusMap { + CREATE_GHOST_TABLES = 'CREATE_GHOST_TABLES', //'创建影子表' + CREATE_DATA_TASK = 'CREATE_DATA_TASK', //'创建数据迁移任务' + MONITOR_DATA_TASK_TRANSFER_PRECHECK = 'MONITOR_DATA_TASK_TRANSFER_PRECHECK', //'数据迁移任务预检查' + MONITOR_DATA_TASK_FULL_TRANSFER = 'MONITOR_DATA_TASK_FULL_TRANSFER', //数据迁移任务迁移全量数据' + MONITOR_DATA_TASK_TRANSFER_APP_SWITCH_FALSE = 'MONITOR_DATA_TASK_TRANSFER_APP_SWITCH_FALSE', //'数据迁移任务迁移增量数据' + SWAP_TABLE = 'SWAP_TABLE', // '切换中' + CLEAR_RESOURCE = 'CLEAR_RESOURCE', // '释放迁移任务资源' +} From dba42e3ab0e4fe9edbc7de3dc0d3495bfbfb6a0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=89=BA=E6=B3=BD?= Date: Wed, 30 Oct 2024 13:50:04 +0800 Subject: [PATCH 3/3] =?UTF-8?q?PullRequest:=20559=20=E5=9C=A8=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=BA=93=E5=8F=98=E6=9B=B4=E4=B8=AD=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=85=A8=E9=87=8F=E6=97=A5=E5=BF=97=E4=B8=8B=E8=BD=BD=E5=92=8C?= =?UTF-8?q?=E5=91=8A=E8=AD=A6=E6=97=A5=E5=BF=97=E5=B1=8F=E8=94=BD=E5=85=A8?= =?UTF-8?q?=E9=87=8F=E6=97=A5=E5=BF=97=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge branch 'feat/taskLogAddFullQuantityTaskDownload of git@code.alipay.com:oceanbase/oceanbase-developer-center.git into dev-4.3.2 https://code.alipay.com/oceanbase/oceanbase-developer-center/pull_requests/559 Signed-off-by: 晓康 * feat: 计划任务增加全量日志下载 * feat: 去除currentOrganizationId参数 * feat: 下载全量日志地址删除参数 * feat: 逻辑库变更添加全量日志下载,告警日志屏蔽全量日志下载 --- src/component/Task/DetailModal.tsx | 6 +++++- .../component/CommonDetailModal/LogModal.tsx | 6 +++--- .../Task/component/CommonDetailModal/index.tsx | 17 ++++++++++++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/component/Task/DetailModal.tsx b/src/component/Task/DetailModal.tsx index 37ccbfff0..7427bdfa4 100644 --- a/src/component/Task/DetailModal.tsx +++ b/src/component/Task/DetailModal.tsx @@ -23,6 +23,7 @@ import { getTaskList, getTaskLog, getTaskResult, + getDownloadUrl, } from '@/common/network/task'; import type { ITableLoadOptions } from '@/component/CommonTable/interface'; import CommonDetailModal from '@/component/Task/component/CommonDetailModal'; @@ -119,7 +120,7 @@ const DetailModal: React.FC = React.memo((props) => { const [approvalVisible, setApprovalVisible] = useState(false); const [approvalStatus, setApprovalStatus] = useState(false); const [isTaskProjectOwner, setIsTaskProjectOwner] = useState(false); - + const [downloadUrl, setDownloadUrl] = useState(undefined); const hasFlow = !!task?.nodeList?.find( (node) => @@ -187,6 +188,8 @@ const DetailModal: React.FC = React.memo((props) => { ...log, [logType]: res, }); + const url = await getDownloadUrl(task?.id, flowId); + setDownloadUrl(url); return; } return; @@ -516,6 +519,7 @@ const DetailModal: React.FC = React.memo((props) => { onReload={loadData} taskContent={taskContent} getItems={getItems} + downloadUrl={downloadUrl} /> = function (props) { }, ); - const { run: getLogDownLoadUrl } = useRequest(async (scheduleId, recordId, logType) => { + const { run: getLogDownLoadUrl } = useRequest(async (scheduleId, recordId) => { if (scheduleId && recordId) { const res = await getDownloadUrl(scheduleId, recordId); if (!!res) { @@ -72,7 +72,7 @@ const LogModal: React.FC = function (props) { useEffect(() => { if (visible) { getLog(scheduleId, recordId, logType); - getLogDownLoadUrl(scheduleId, recordId, logType); + getLogDownLoadUrl(scheduleId, recordId); } }, [scheduleId, recordId, visible, logType]); @@ -105,7 +105,7 @@ const LogModal: React.FC = function (props) { log={log} logType={logType} isLoading={loading} - downloadUrl={downloadUrl} + downloadUrl={logType === CommonTaskLogType.ALL ? downloadUrl : undefined} onLogTypeChange={handleLogTypeChange} /> diff --git a/src/component/Task/component/CommonDetailModal/index.tsx b/src/component/Task/component/CommonDetailModal/index.tsx index 526d5e998..fa2d75a56 100644 --- a/src/component/Task/component/CommonDetailModal/index.tsx +++ b/src/component/Task/component/CommonDetailModal/index.tsx @@ -18,7 +18,7 @@ import TaskLog from '@/component/Task/component/Log'; import StatusLabel from '@/component/Task/component/Status'; import type { ITaskDetailModalProps } from '@/component/Task/interface'; import { TaskDetailType } from '@/component/Task/interface'; -import { ITaskResult, TaskDetail, TaskRecordParameters, TaskType } from '@/d.ts'; +import { ITaskResult, TaskDetail, TaskRecordParameters, TaskType, CommonTaskLogType } from '@/d.ts'; import login from '@/store/login'; import { formatMessage } from '@/util/intl'; import { ShareAltOutlined } from '@ant-design/icons'; @@ -51,8 +51,19 @@ const TaskContent: React.FC = (props) => { hasFlow, onLogTypeChange, onReload, + downloadUrl, } = props; let content = null; + const getDownloadUrl = () => { + if (isLogicalDbChangeTask(task?.type) && logType === CommonTaskLogType.ALL) { + return downloadUrl; + } else if (!isLogicalDbChangeTask(task?.type)) { + return result?.fullLogDownloadUrl; + } else { + return undefined; + } + }; + switch (detailType) { case TaskDetailType.INFO: content = taskContent ? ( @@ -64,7 +75,6 @@ const TaskContent: React.FC = (props) => { isSplit={isSplit} /> ); - break; case TaskDetailType.LOG: content = ( @@ -72,7 +82,7 @@ const TaskContent: React.FC = (props) => { log={log} logType={logType} isLoading={isLoading} - downloadUrl={result?.fullLogDownloadUrl} + downloadUrl={getDownloadUrl()} onLogTypeChange={onLogTypeChange} /> ); @@ -109,6 +119,7 @@ interface ICommonTaskDetailModalProps extends ITaskDetailModalProps { width?: number; isSplit?: boolean; theme?: string; + downloadUrl?: string; getItems?: ( task: TaskDetail, result: ITaskResult,