Skip to content

Commit 53e1ebe

Browse files
JustaCattthLinx
authored andcommitted
feat(frontend): sqlserver数据迁移同步接口改动 #9802
# Reviewed, transaction id: 41445
1 parent a6e2353 commit 53e1ebe

File tree

24 files changed

+2247
-31
lines changed

24 files changed

+2247
-31
lines changed

dbm-ui/frontend/src/locales/zh-cn.json

+15
Original file line numberDiff line numberDiff line change
@@ -4492,5 +4492,20 @@
44924492
"标签值重复": "标签值重复",
44934493
"请选择标签": "请选择标签",
44944494
"执行常用管理命令": "执行常用管理命令",
4495+
"数据迁移记录": "数据迁移记录",
4496+
"手动修改迁移的 DB 名": "手动修改迁移的 DB 名",
4497+
"点击查看详情": "点击查看详情",
4498+
"迁移 DB 名称": "迁移 DB 名称",
4499+
"迁移后 DB 名称": "迁移后 DB 名称",
4500+
"SQLServer数据迁移手动修改迁移DB名": "SQLServer数据迁移手动修改迁移DB名",
4501+
"迁移后 DB 名称(自动生成,可修改)": "迁移后 DB 名称(自动生成,可修改)",
4502+
"集群x已存在DB名y": "集群 {x} 已存在 DB 名 {y} ",
4503+
"集群xx是已被选中的源集群": "集群 {0} 是已被选中的源集群",
4504+
"请修改冲突的 DB 名": "请修改冲突的 DB 名",
4505+
"请输入集群域名,多个请用分隔符分隔": "请输入集群域名,多个请用分隔符分隔",
4506+
"不允许选择源集群": "不允许选择源集群",
4507+
"集群是已被选中的源集群": "集群是已被选中的源集群",
4508+
"集群已被其他行选择": "集群已被其他行选择",
4509+
"n项已修改": "{0} 项已修改",
44954510
"这行勿动!新增翻译请在上一行添加!": ""
44964511
}

dbm-ui/frontend/src/services/model/ticket/details/sqlserver/dataMigrate.ts

+3
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ export interface DataMigrate extends DetailBase {
66
infos: {
77
db_list: string[];
88
dst_cluster: number;
9+
dst_cluster_list: number[];
910
dts_id: number;
1011
ignore_db_list: string[];
1112
rename_infos: {
1213
db_name: string;
1314
old_db_name: string;
15+
rename_cluster_list: number[];
16+
rename_db_name: string;
1417
target_db_name: string;
1518
}[];
1619
src_cluster: number;

dbm-ui/frontend/src/services/source/dbbase.ts

+9
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,15 @@ export function checkClusterDatabase(params: { bk_biz_id: number; cluster_id: nu
103103
return http.post<Record<string, boolean>>(`${path}/check_cluster_databases/`, params);
104104
}
105105

106+
// 批量查询集群的库是否存在
107+
export function batchCheckClusterDatabase(params: { bk_biz_id: number; cluster_ids: number[]; db_list: string[] }) {
108+
return http.post<{
109+
[clusterId: string]: {
110+
[dbName: string]: boolean;
111+
};
112+
}>(`${path}/batch_check_cluster_databases/`, params);
113+
}
114+
106115
// 根据用户手动输入的ip[:port]查询真实的实例
107116
export function checkInstance<T extends InstanceInfos>(params: { bk_biz_id?: number; instance_addresses: string[] }) {
108117
return http.post<T[]>(`${path}/check_instances/`, params);

dbm-ui/frontend/src/types/auto-imports.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,6 @@ declare global {
7171
// for type re-export
7272
declare global {
7373
// @ts-ignore
74-
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
74+
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
7575
import('vue')
7676
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
<!--
2+
* TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available.
3+
*
4+
* Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved.
5+
*
6+
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License athttps://opensource.org/licenses/MIT
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
10+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for
11+
* the specific language governing permissions and limitations under the License.
12+
-->
13+
14+
<template>
15+
<SmartAction>
16+
<BkAlert
17+
class="mb-20"
18+
closable
19+
:title="t('数据迁移:数据同步复制到新集群,迁移后将会对原库进行')" />
20+
<BkForm
21+
class="mb-20"
22+
form-type="vertical"
23+
:model="formData">
24+
<BkFormItem
25+
:label="t('迁移类型')"
26+
required>
27+
<BkRadioGroup
28+
v-model="formData.ticketType"
29+
style="width: 400px"
30+
type="card"
31+
@change="handleMigrateTypeChange">
32+
<BkRadioButton :label="TicketTypes.SQLSERVER_FULL_MIGRATE">
33+
{{ t('一次性全备迁移') }}
34+
</BkRadioButton>
35+
<BkRadioButton :label="TicketTypes.SQLSERVER_INCR_MIGRATE">
36+
{{ t('持续增量迁移') }}
37+
</BkRadioButton>
38+
</BkRadioGroup>
39+
</BkFormItem>
40+
<EditableTable
41+
ref="table"
42+
class="mb-20"
43+
:model="formData.tableData">
44+
<EditableRow
45+
v-for="(item, index) in formData.tableData"
46+
:key="index">
47+
<SrcClusterColumn
48+
v-model="item.srcCluster"
49+
:selected="selected"
50+
@batch-edit="handleBatchEditCluster" />
51+
<DstClusterColumn
52+
v-model="item.dstCluster"
53+
:selected-map="selectedMap"
54+
:src-cluster="item.srcCluster"
55+
@batch-edit="handleBatchEdit" />
56+
<DbNameColumn
57+
v-model="item.dbName"
58+
allow-asterisk
59+
check-not-exist
60+
:cluster-id="item.srcCluster.id"
61+
field="dbName"
62+
:label="t('迁移 DB 名')"
63+
required
64+
@batch-edit="handleBatchEdit" />
65+
<DbNameColumn
66+
v-model="item.dbIgnoreName"
67+
check-not-exist
68+
:cluster-id="item.srcCluster.id"
69+
field="dbIgnoreName"
70+
:label="t('忽略 DB 名')"
71+
:required="false"
72+
@batch-edit="handleBatchEdit" />
73+
<RenameColumn
74+
v-model="item.renameInfoList"
75+
v-model:db-ignore-name="item.dbIgnoreName"
76+
v-model:db-name="item.dbName"
77+
:data="item" />
78+
<OperationColumn
79+
v-model:table-data="formData.tableData"
80+
:create-row-method="createTableRow" />
81+
</EditableRow>
82+
</EditableTable>
83+
<BkFormItem
84+
class="mt-24"
85+
:label="t('DB名处理')">
86+
<BkRadioGroup v-model="formData.need_auto_rename">
87+
<BkRadio :label="false">
88+
{{ t('迁移后源DB继续使用,DB名不变') }}
89+
</BkRadio>
90+
<BkRadio label>
91+
{{ t('迁移后源DB不再使用,自动重命名') }}
92+
</BkRadio>
93+
</BkRadioGroup>
94+
</BkFormItem>
95+
<TicketPayload v-model="formData.payload" />
96+
</BkForm>
97+
<template #action>
98+
<BkButton
99+
class="mr-8 w-88"
100+
:loading="isSubmitting"
101+
theme="primary"
102+
@click="handleSubmit">
103+
{{ t('提交') }}
104+
</BkButton>
105+
<DbPopconfirm
106+
:confirm-handler="handleReset"
107+
:content="t('重置将会情况当前填写的所有内容_请谨慎操作')"
108+
:title="t('确认重置页面')">
109+
<BkButton
110+
class="ml8 w-88"
111+
:disabled="isSubmitting">
112+
{{ t('重置') }}
113+
</BkButton>
114+
</DbPopconfirm>
115+
</template>
116+
</SmartAction>
117+
</template>
118+
<script lang="ts" setup>
119+
import { reactive, useTemplateRef } from 'vue';
120+
import { useI18n } from 'vue-i18n';
121+
122+
import SqlServerHaModel from '@services/model/sqlserver/sqlserver-ha';
123+
import type { Sqlserver } from '@services/model/ticket/ticket';
124+
125+
import { useCreateTicket, useTicketDetail } from '@hooks';
126+
127+
import { ClusterTypes, TicketTypes } from '@common/const';
128+
129+
import TicketPayload, {
130+
createTickePayload,
131+
} from '@views/db-manage/common/toolbox-field/form-item/ticket-payload/Index.vue';
132+
import DbNameColumn from '@views/db-manage/sqlserver/common/toolbox-field/db-name-column/Index.vue';
133+
134+
import DstClusterColumn from './components/DstClusterColumn.vue';
135+
import RenameColumn from './components/RenameColumn.vue';
136+
import SrcClusterColumn from './components/SrcClusterColumn.vue';
137+
138+
interface RowData {
139+
dbIgnoreName: string[];
140+
dbName: string[];
141+
dstCluster: {
142+
cluster_type: ClusterTypes;
143+
id: number;
144+
major_version: string;
145+
master_domain: string;
146+
}[];
147+
renameInfoList: {
148+
db_name: string;
149+
rename_cluster_list: number[];
150+
rename_db_name: string;
151+
target_db_name: string;
152+
}[];
153+
srcCluster: {
154+
cluster_type: ClusterTypes;
155+
id: number;
156+
major_version: string;
157+
master_domain: string;
158+
};
159+
}
160+
161+
const { t } = useI18n();
162+
const tableRef = useTemplateRef('table');
163+
const router = useRouter();
164+
165+
const createTableRow = (data = {} as Partial<RowData>) => ({
166+
dbIgnoreName: data.dbIgnoreName || [],
167+
dbName: data.dbName || ['*'],
168+
dstCluster: data.dstCluster || [],
169+
renameInfoList: data.renameInfoList || [],
170+
srcCluster: data.srcCluster || {
171+
cluster_type: ClusterTypes.SQLSERVER_HA,
172+
id: 0,
173+
major_version: '',
174+
master_domain: '',
175+
},
176+
});
177+
178+
const defaultData = () => ({
179+
need_auto_rename: false,
180+
payload: createTickePayload(),
181+
tableData: [createTableRow()],
182+
ticketType: TicketTypes.SQLSERVER_FULL_MIGRATE,
183+
});
184+
185+
const formData = reactive(defaultData());
186+
const selected = computed(() =>
187+
formData.tableData.filter((item) => item.srcCluster.id).map((item) => item.srcCluster),
188+
);
189+
const selectedMap = computed(() =>
190+
Object.fromEntries(formData.tableData.map((cur) => [cur.srcCluster.master_domain, true])),
191+
);
192+
193+
useTicketDetail<Sqlserver.DataMigrate>(TicketTypes.SQLSERVER_FULL_MIGRATE, {
194+
onSuccess(ticketDetail) {
195+
const { details } = ticketDetail;
196+
const { clusters, infos } = details;
197+
Object.assign(formData, {
198+
need_auto_rename: details.need_auto_rename,
199+
ticketType: TicketTypes.SQLSERVER_FULL_MIGRATE,
200+
...createTickePayload(ticketDetail),
201+
tableData: infos.map((item) => {
202+
const srcCluster = clusters[item.src_cluster];
203+
return createTableRow({
204+
dbIgnoreName: item.ignore_db_list,
205+
dbName: item.db_list,
206+
dstCluster: item.dst_cluster_list.map((id) => {
207+
const cluster = clusters[id];
208+
return {
209+
cluster_type: cluster.cluster_type as ClusterTypes,
210+
id: cluster.id,
211+
major_version: cluster.major_version,
212+
master_domain: cluster.immute_domain,
213+
};
214+
}),
215+
renameInfoList: item.rename_infos.map((cur) => ({
216+
db_name: cur.old_db_name,
217+
rename_cluster_list: [],
218+
rename_db_name: cur.db_name,
219+
target_db_name: cur.target_db_name,
220+
})),
221+
srcCluster: {
222+
cluster_type: srcCluster.cluster_type as ClusterTypes,
223+
id: srcCluster.id,
224+
major_version: srcCluster.major_version,
225+
master_domain: srcCluster.immute_domain,
226+
},
227+
});
228+
}),
229+
});
230+
},
231+
});
232+
233+
const { loading: isSubmitting, run: createTicketRun } = useCreateTicket<{
234+
infos: {
235+
db_list: string[];
236+
dst_cluster_list: number[];
237+
ignore_db_list: string[];
238+
rename_infos: {
239+
db_name: string;
240+
rename_cluster_list: number[];
241+
rename_db_name: string;
242+
target_db_name: string;
243+
}[];
244+
src_cluster: number;
245+
}[];
246+
need_auto_rename: boolean;
247+
}>(TicketTypes.SQLSERVER_FULL_MIGRATE);
248+
249+
const handleSubmit = async () => {
250+
const result = await tableRef.value!.validate();
251+
if (!result) {
252+
return;
253+
}
254+
createTicketRun({
255+
details: {
256+
infos: formData.tableData.map((item) => ({
257+
db_list: item.dbName,
258+
dst_cluster_list: item.dstCluster.map((cur) => cur.id),
259+
ignore_db_list: item.dbIgnoreName,
260+
rename_infos: item.renameInfoList,
261+
src_cluster: item.srcCluster.id,
262+
})),
263+
need_auto_rename: formData.need_auto_rename,
264+
},
265+
...formData.payload,
266+
});
267+
};
268+
269+
const handleMigrateTypeChange = (ticketType: TicketTypes) => {
270+
router.push({
271+
name: ticketType,
272+
});
273+
};
274+
275+
const handleReset = () => {
276+
Object.assign(formData, defaultData());
277+
};
278+
279+
const handleBatchEditCluster = (list: SqlServerHaModel[]) => {
280+
const dataList = list.reduce<RowData[]>((acc, item) => {
281+
if (!selectedMap.value[item.master_domain]) {
282+
acc.push(
283+
createTableRow({
284+
dbName: ['*'],
285+
srcCluster: {
286+
cluster_type: item.cluster_type,
287+
id: item.id,
288+
major_version: item.major_version,
289+
master_domain: item.master_domain,
290+
},
291+
}),
292+
);
293+
}
294+
return acc;
295+
}, []);
296+
formData.tableData = [...(formData.tableData[0].srcCluster.id ? formData.tableData : []), ...dataList];
297+
};
298+
299+
const handleBatchEdit = (value: any, field: string) => {
300+
formData.tableData.forEach((item) => {
301+
Object.assign(item, {
302+
[field as keyof RowData]: value,
303+
});
304+
});
305+
};
306+
</script>
307+
308+
<style lang="less" scoped>
309+
:deep(.bk-form-label) {
310+
font-size: 12px;
311+
font-weight: 700;
312+
color: #313238;
313+
}
314+
</style>

0 commit comments

Comments
 (0)