From 0853e29a0437838352566cd5b14c9563faa4fad4 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Wed, 11 Oct 2023 17:20:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20MML=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/mmlManage/neOperate.ts | 52 ++ src/api/mmlManage/omcOperate.ts | 46 + src/api/mmlManage/udmOperate.ts | 2 +- src/views/configManage/configParam/index.vue | 3 + src/views/mmlManage/neOperate/index.vue | 911 +++++++++---------- src/views/mmlManage/omcOperate/index.vue | 909 +++++++++--------- src/views/mmlManage/udmOperate/index.vue | 39 +- 7 files changed, 982 insertions(+), 980 deletions(-) create mode 100644 src/api/mmlManage/neOperate.ts create mode 100644 src/api/mmlManage/omcOperate.ts diff --git a/src/api/mmlManage/neOperate.ts b/src/api/mmlManage/neOperate.ts new file mode 100644 index 00000000..fd6e8477 --- /dev/null +++ b/src/api/mmlManage/neOperate.ts @@ -0,0 +1,52 @@ +import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; +import { request } from '@/plugins/http-fetch'; +import { parseObjLineToHump } from '@/utils/parse-utils'; + +/** + * 查询网元可用cmd命令 + * @param neType 网元类型 + * @returns object + */ +export async function getMMLByNE(neType: string) { + // 发起请求 + const result = await request({ + url: `/databaseManagement/v1/elementType/omc_db/objectType/mml_system`, + method: 'get', + params: { + SQL: `select * from mml_system where ne_type = '${neType}'`, + }, + }); + // 解析数据 + if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) { + let data = result.data.data[0]; + return Object.assign(result, { + data: parseObjLineToHump(data['mml_system']), + }); + } + return result; +} + +/** + * 发送网元的mml命令 + * @param neType 网元类型 + * @param neId 网元ID + * @param cmdStr 命令串 + * @returns + */ +export async function sendMMlByNE( + neType: string, + neId: string, + cmdStr: string +) { + // 发起请求 + const result = await request({ + url: `/operationManagement/v1/elementType/${neType}/objectType/mml?ne_id=${neId}`, + method: 'post', + data: { mml: [cmdStr] }, + }); + // 解析数据 + if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) { + result.data = result.data.data[0]; + } + return result; +} diff --git a/src/api/mmlManage/omcOperate.ts b/src/api/mmlManage/omcOperate.ts new file mode 100644 index 00000000..539e9cc9 --- /dev/null +++ b/src/api/mmlManage/omcOperate.ts @@ -0,0 +1,46 @@ +import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; +import { request } from '@/plugins/http-fetch'; +import { parseObjLineToHump } from '@/utils/parse-utils'; + +/** + * 查询OMC可用cmd命令 + * @returns object + */ +export async function getMMLByOMC() { + // 发起请求 + const result = await request({ + url: `/databaseManagement/v1/elementType/omc_db/objectType/mml_command`, + method: 'get', + params: { + SQL: `select * from mml_command where ne_type = 'OMC'`, + }, + }); + // 解析数据 + if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) { + let data = result.data.data[0]; + return Object.assign(result, { + data: parseObjLineToHump(data['mml_command']), + }); + } + return result; +} + +/** + * 发送OMC的mml命令 + * @param neId 网元ID + * @param cmdStr 命令串 + * @returns + */ +export async function sendMMlByOMC(neId: string, cmdStr: string) { + // 发起请求 + const result = await request({ + url: `/operationManagement/v1/elementType/OMC/objectType/mml?ne_id=${neId}`, + method: 'post', + data: { mml: [cmdStr] }, + }); + // 解析数据 + if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) { + result.data = result.data.data[0]; + } + return result; +} diff --git a/src/api/mmlManage/udmOperate.ts b/src/api/mmlManage/udmOperate.ts index 6733d9e7..8f8456d9 100644 --- a/src/api/mmlManage/udmOperate.ts +++ b/src/api/mmlManage/udmOperate.ts @@ -6,7 +6,7 @@ import { parseObjLineToHump } from '@/utils/parse-utils'; * 查询UDM可用cmd命令 * @returns object */ -export async function getSubscriberByUDM() { +export async function getMMLByUDM() { // 发起请求 const result = await request({ url: `/databaseManagement/v1/elementType/omc_db/objectType/mml_subscriber`, diff --git a/src/views/configManage/configParam/index.vue b/src/views/configManage/configParam/index.vue index 5002a978..b8e528f7 100644 --- a/src/views/configManage/configParam/index.vue +++ b/src/views/configManage/configParam/index.vue @@ -1163,6 +1163,7 @@ onMounted(() => { v-else-if="record['type'] === 'enum'" v-model:value="tableState.editRecord['value']" :placeholder="record['filter']" + :allow-clear="true" > { tableState.editRecord[text.name]['value'] " :placeholder="text['filter']" + :allow-clear="true" > { ] " :placeholder="text['filter']" + :allow-clear="true" > import { useRoute } from 'vue-router'; -import { reactive, ref, onMounted, toRaw, nextTick } from 'vue'; +import { reactive, ref, onMounted, toRaw } from 'vue'; import { PageContainer } from '@ant-design-vue/pro-layout'; -import { message, Modal } from 'ant-design-vue/lib'; -import { SizeType } from 'ant-design-vue/lib/config-provider'; -import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface'; -import { ColumnsType } from 'ant-design-vue/lib/table'; -import { parseDateToStr } from '@/utils/date-utils'; +import { message } from 'ant-design-vue/lib'; +import CodemirrorEdite from '@/components/CodemirrorEdite/index.vue'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; -import { saveAs } from 'file-saver'; +import useNeInfoStore from '@/store/modules/neinfo'; +import { regExpIPv4, regExpIPv6 } from '@/utils/regular-utils'; import useI18n from '@/hooks/useI18n'; -import { getTraceRawInfo, listTraceData } from '@/api/traceManage/analysis'; +import { getMMLByNE, sendMMlByNE } from '@/api/mmlManage/neOperate'; const { t } = useI18n(); const route = useRoute(); /**路由标题 */ let title = ref((route.meta.title as string) ?? '标题'); -/**查询参数 */ -let queryParams = reactive({ - /**移动号 */ - imsi: '', - /**移动号 */ - msisdn: '', - /**当前页数 */ - pageNum: 1, - /**每页条数 */ - pageSize: 20, -}); +/**网元参数 */ +let neCascaderOtions = ref[]>([]); -/**查询参数重置 */ -function fnQueryReset() { - queryParams = Object.assign(queryParams, { - imsi: '', - pageNum: 1, - pageSize: 20, - }); - tablePagination.current = 1; - tablePagination.pageSize = 20; - fnGetList(); -} - -/**表格状态类型 */ -type TabeStateType = { - /**加载等待 */ - loading: boolean; - /**紧凑型 */ - size: SizeType; - /**搜索栏 */ - seached: boolean; - /**记录数据 */ - data: object[]; -}; - -/**表格状态 */ -let tableState: TabeStateType = reactive({ - loading: false, - size: 'middle', - seached: true, - data: [], -}); - -/**表格字段列 */ -let tableColumns: ColumnsType = [ - { - title: t('views.traceManage.analysis.trackTaskId'), - dataIndex: 'taskId', - align: 'center', - }, - { - title: t('views.traceManage.analysis.imsi'), - dataIndex: 'imsi', - align: 'center', - }, - { - title: t('views.traceManage.analysis.msisdn'), - dataIndex: 'msisdn', - align: 'center', - }, - { - title: t('views.traceManage.analysis.srcIp'), - dataIndex: 'srcAddr', - align: 'center', - }, - { - title: t('views.traceManage.analysis.dstIp'), - dataIndex: 'dstAddr', - align: 'center', - }, - { - title: t('views.traceManage.analysis.signalType'), - dataIndex: 'ifType', - align: 'center', - }, - { - title: t('views.traceManage.analysis.msgType'), - dataIndex: 'msgType', - align: 'center', - }, - { - title: t('views.traceManage.analysis.msgDirect'), - dataIndex: 'msgDirect', - align: 'center', - }, - { - title: t('views.traceManage.analysis.rowTime'), - dataIndex: 'timestamp', - align: 'center', - customRender(opt) { - if (!opt.value) return ''; - return parseDateToStr(opt.value); - }, - }, - { - title: t('common.operate'), - key: 'id', - align: 'center', - }, -]; - -/**表格分页器参数 */ -let tablePagination = reactive({ - /**当前页数 */ - current: 1, - /**每页条数 */ - pageSize: 20, - /**默认的每页条数 */ - defaultPageSize: 20, - /**指定每页可以显示多少条 */ - pageSizeOptions: ['10', '20', '50', '100'], - /**只有一页时是否隐藏分页器 */ - hideOnSinglePage: false, - /**是否可以快速跳转至某页 */ - showQuickJumper: true, - /**是否可以改变 pageSize */ - showSizeChanger: true, - /**数据总数 */ - total: 0, - showTotal: (total: number) => t('common.tablePaginationTotal', { total }), - onChange: (page: number, pageSize: number) => { - tablePagination.current = page; - tablePagination.pageSize = pageSize; - queryParams.pageNum = page; - queryParams.pageSize = pageSize; - fnGetList(); - }, -}); - -/**表格紧凑型变更操作 */ -function fnTableSize({ key }: MenuInfo) { - tableState.size = key as SizeType; -} - -/**查询备份信息列表 */ -function fnGetList() { - if (tableState.loading) return; - tableState.loading = true; - listTraceData(toRaw(queryParams)).then(res => { - if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { - tablePagination.total = res.total; - tableState.data = res.rows; - } - tableState.loading = false; - }); -} - -/**抽屉对象信息状态类型 */ -type ModalStateType = { - /**抽屉框是否显示 */ - visible: boolean; - /**标题 */ - title: string; +/**对象信息状态类型 */ +type StateType = { + /**网元类型 */ + neType: string[]; + /**命令网元类型 */ + mmlNeType: string; + /**命令数据 tree */ + mmlTreeData: any[]; + /**命令选中 */ + mmlSelect: Record; /**表单数据 */ from: Record; + /**命令发送日志 */ + mmlCmdLog: string; }; -/**抽屉对象信息状态 */ -let modalState: ModalStateType = reactive({ - visible: false, - title: '', +/**对象信息状态 */ +let state: StateType = reactive({ + neType: [], + mmlNeType: '', + mmlTreeData: [], + mmlSelect: {}, from: { - rawData: '', - rawDataHTML: '', - downBtn: false, + sendLoading: false, }, + mmlCmdLog: '', }); -/** - * 对话框弹出显示 - * @param row 记录信息 - */ -function fnModalVisible(row: Record) { - // 进制转数据 - const hexString = parseBase64Data(row.rawMsg); - const rawData = convertToReadableFormat(hexString); - modalState.from.rawData = rawData; - // RAW解析HTML - getTraceRawInfo(row.id).then(res => { +/**查询可选命令列表 */ +function fnTreeSelect(_: any, info: any) { + state.mmlSelect = info.node.dataRef; + state.from = {}; + // state.mmlCmdLog = ''; +} + +/**清空控制台日志 */ +function fnCleanCmdLog() { + state.mmlCmdLog = ''; +} + +/**清空表单 */ +function fnCleanFrom() { + state.from = {}; +} + +/**命令发送 */ +function fnSendMML() { + if (state.from.sendLoading) { + return; + } + const operation = state.mmlSelect.operation; + const object = state.mmlSelect.object; + let cmdStr = ''; + // 根据参数取值 + let argsArr: string[] = []; + const param = toRaw(state.mmlSelect.param) || []; + const from = toRaw(state.from); + for (const item of param) { + const value = from[item.name]; + + // 是否必填项且有效值 + const notV = value === null || value === undefined || value === ''; + if (item.optional === 'false' && notV) { + message.warning(`必填参数:${item.display}`, 2); + return; + } + + // 检查是否存在值 + if (!Reflect.has(from, item.name) || notV) { + continue; + } + + // 检查规则 + const [ok, msg] = ruleVerification(item, from[item.name]); + if (!ok) { + message.warning({ + content: `${msg}`, + duration: 3, + }); + return; + } + + argsArr.push(`${item.name}=${from[item.name]}`); + } + + // 拼装命令 + const argsStr = argsArr.join(','); + if (object && argsStr) { + cmdStr = `${operation} ${object}:${argsStr}`; + } else if (object) { + cmdStr = `${operation} ${object}`; + } else { + cmdStr = `${operation} ${argsStr}`; + } + cmdStr = cmdStr.trim(); + + // 发送 + state.mmlCmdLog += `$> ${cmdStr}\n`; + state.from.sendLoading = true; + const [neType, neId] = state.neType; + sendMMlByNE(neType, neId, cmdStr).then(res => { + state.from.sendLoading = false; if (res.code === RESULT_CODE_SUCCESS) { - const htmlString = rawDataHTMLScript(res.msg); - modalState.from.rawDataHTML = htmlString; - modalState.from.downBtn = true; + let resultStr = res.data; + resultStr = resultStr.replace(/(\r\n|\n)/g, '\n$> '); + state.mmlCmdLog += `$> ${resultStr}\n`; } else { - modalState.from.rawDataHTML = t('views.traceManage.analysis.noData'); + state.mmlCmdLog += `$> ${res.msg}\n`; } }); - modalState.title = t('views.traceManage.analysis.taskTitle', { - num: row.imsi, - }); - modalState.visible = true; } -/** - * 对话框弹出关闭 - */ -function fnModalVisibleClose() { - modalState.visible = false; - modalState.from.downBtn = false; - modalState.from.rawDataHTML = ''; - modalState.from.rawData = ''; -} +/**规则校验 */ +function ruleVerification( + row: Record, + value: any +): (string | boolean)[] { + let result = [true, '']; + const type = row.type; + const filter = row.filter; + const display = row.display; -// 将Base64编码解码为字节数组 -function parseBase64Data(hexData: string) { - // 将Base64编码解码为字节数组 - const byteString = atob(hexData); - const byteArray = new Uint8Array(byteString.length); - for (let i = 0; i < byteString.length; i++) { - byteArray[i] = byteString.charCodeAt(i); + switch (type) { + case 'int': + if (filter && filter.indexOf('~') !== -1) { + const filterArr = filter.split('~'); + const minInt = parseInt(filterArr[0]); + const maxInt = parseInt(filterArr[1]); + const valueInt = parseInt(value); + if (valueInt < minInt || valueInt > maxInt) { + return [false, `${display} 参数值不在合理范围 ${filter}`]; + } + } + break; + case 'ipv4': + if (!regExpIPv4.test(value)) { + return [false, `${display} 不是合法的IPV4地址`]; + } + break; + case 'ipv6': + if (!regExpIPv6.test(value)) { + return [false, `${display} 不是合法的IPV6地址`]; + } + break; + case 'enum': + if (filter && filter.indexOf('{') === 1) { + let filterJson: Record = {}; + try { + filterJson = JSON.parse(filter); //string---json + } catch (error) { + console.error(error); + } + + if (!Object.keys(filterJson).includes(`${value}`)) { + return [false, `${display} 不是合理的枚举值`]; + } + } + break; + case 'bool': + if (filter && filter.indexOf('{') === 1) { + let filterJson: Record = {}; + try { + filterJson = JSON.parse(filter); //string---json + } catch (error) { + console.error(error); + } + + if (!Object.values(filterJson).includes(`${value}`)) { + return [false, `${display} 不是合理的布尔类型的值`]; + } + } + break; + case 'string': + if (filter && filter.indexOf('~') !== -1) { + try { + const filterArr = filter.split('~'); + let rule = new RegExp( + '^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$' + ); + if (!rule.test(value)) { + return [false, `${display} 参数值不合理`]; + } + } catch (error) { + console.error(error); + } + } + + break; + case 'regex': + if (filter) { + try { + let regex = new RegExp(filter); + if (!regex.test(value)) { + return [false, `${display} 参数值不合理`]; + } + } catch (error) { + console.error(error); + } + } + break; + + default: + console.warn('未知类型', type); + return [false, `${display} 输入值是未知类型`]; } - - // 将每一个字节转换为2位16进制数表示,并拼接起来 - let hexString = ''; - for (let i = 0; i < byteArray.length; i++) { - const hex = byteArray[i].toString(16); - hexString += hex.length === 1 ? '0' + hex : hex; - } - return hexString; + return result; } -// 转换十六进制字节流为可读格式和ASCII码表示 -function convertToReadableFormat(hexString: string) { - let result = ''; - let asciiResult = ''; - let arr = []; - let row = 100; - for (let i = 0; i < hexString.length; i += 2) { - const hexChars = hexString.substring(i, i + 2); - const decimal = parseInt(hexChars, 16); - const asciiChar = - decimal >= 32 && decimal <= 126 ? String.fromCharCode(decimal) : '.'; +/**网元类型选择对应修改 */ +function fnNeChange(keys: any, _: any) { + // 不是同类型时需要重新加载 + if (state.mmlNeType !== keys[0]) { + state.mmlTreeData = []; + state.mmlSelect = {}; + fnGetList(); + } +} - result += hexChars + ' '; - asciiResult += asciiChar; +/**查询可选命令列表 */ +function fnGetList() { + const neType = state.neType[0]; + state.mmlNeType = neType; + getMMLByNE(neType).then(res => { + if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { + // 构建树结构 + const treeArr: Record[] = []; + for (const item of res.data) { + const id = item['id']; + const object = item['object']; + const operation = item['operation']; + const mmlDisplay = item['mmlDisplay']; + // 可选属性参数 + let param = []; + try { + param = JSON.parse(item['paramJson']); + } catch (error) { + console.error(error); + } - if ((i + 2) % 32 === 0) { - arr.push({ - row: row, - code: result, - asciiText: asciiResult, + // 遍历检查大类 + const treeItem = treeArr.find(i => i.key == item['category']); + if (!treeItem) { + treeArr.push({ + title: item['catDisplay'], + key: item['category'], + selectable: false, + children: [ + { key: id, title: mmlDisplay, object, operation, param }, + ], + }); + } else { + treeItem.children.push({ + key: id, + title: mmlDisplay, + object, + operation, + param, + }); + } + } + state.mmlTreeData = treeArr; + } else { + message.warning({ + content: `${neType} 无可选命令操作`, + duration: 2, }); - result = ''; - asciiResult = ''; - row += 10; } - if (2 + i == hexString.length) { - arr.push({ - row: row, - code: result, - asciiText: asciiResult, - }); - result = ''; - asciiResult = ''; - row += 10; - } - } - return arr; -} - -// 信息详情HTMl内容处理 -function rawDataHTMLScript(htmlString: string) { - // 删除所有 标签 - // const withoutATags = htmlString.replace(/]*>(.*?)<\/a>/gi, ''); - // 删除所有