From d7fd6c040c57e91af6c2401c2932746e9cc84135 Mon Sep 17 00:00:00 2001 From: xlsea Date: Tue, 8 Apr 2025 17:57:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(1.5.0)-beta1):=20=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E6=8E=A5=E5=85=A5ws?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/log-drawer-ws.vue | 447 ------------------ src/components/common/log-drawer.vue | 119 ++++- .../modules/detail/callback-detail.vue | 5 +- .../workflow/modules/detail/task-detail.vue | 2 +- .../modules/drawer/callback-drawer.vue | 4 +- .../workflow/modules/drawer/task-drawer.vue | 2 +- src/typings/api.d.ts | 1 + src/utils/common.ts | 16 + src/utils/websocket.ts | 36 +- src/views/_builtin/log/index.vue | 2 +- .../batch/modules/job-batch-detail-drawer.vue | 2 +- 11 files changed, 148 insertions(+), 488 deletions(-) delete mode 100644 src/components/common/log-drawer-ws.vue diff --git a/src/components/common/log-drawer-ws.vue b/src/components/common/log-drawer-ws.vue deleted file mode 100644 index a9737d3..0000000 --- a/src/components/common/log-drawer-ws.vue +++ /dev/null @@ -1,447 +0,0 @@ - - - - - - - diff --git a/src/components/common/log-drawer.vue b/src/components/common/log-drawer.vue index d79b713..d09176f 100644 --- a/src/components/common/log-drawer.vue +++ b/src/components/common/log-drawer.vue @@ -14,8 +14,12 @@ import { } from 'naive-ui'; import { defineComponent, nextTick, onBeforeUnmount, ref, watch } from 'vue'; import { useRouter } from 'vue-router'; +import type { UseWebSocketReturn } from '@vueuse/core'; +import { useWebSocket } from '@vueuse/core'; import { fetchJobLogList, fetchRetryLogList } from '@/service/api/log'; import ButtonIcon from '@/components/custom/button-icon.vue'; +import { initWebSocketUrl } from '@/utils/websocket'; +import { generateRandomString } from '@/utils/common'; defineOptions({ name: 'LogDrawer' @@ -25,6 +29,7 @@ interface Props { title?: string; drawer?: boolean; type?: 'job' | 'retry'; + fetchType?: 'ws' | 'http'; taskData?: Api.Job.JobTask | Api.RetryTask.RetryTask; modelValue?: Api.JobLog.JobMessage[]; } @@ -33,6 +38,7 @@ const props = withDefaults(defineProps(), { title: undefined, drawer: true, type: 'job', + fetchType: 'ws', taskData: undefined, modelValue: () => [] }); @@ -47,6 +53,7 @@ const expandedNames = ref([]); const virtualListInst = ref(); const syncTime = ref(1); const logList = ref([]); +const websocket = ref>(); const interval = ref(); let controller = new AbortController(); const finished = ref(true); @@ -59,7 +66,11 @@ const pauseLog = () => { interval.value = undefined; }; -const stopLog = () => { +const stopLogByWs = () => { + websocket.value?.close(); +}; + +const stopLogByHttp = async () => { if (!finished.value) controller.abort(); pauseLog(); startId = '0'; @@ -67,7 +78,46 @@ const stopLog = () => { logList.value = []; }; +const stopLog = () => { + if (props.fetchType === 'http') { + stopLogByHttp(); + return; + } + stopLogByWs(); +}; + async function getLogList() { + if (props.fetchType === 'http') { + await getLogListByHttp(); + return; + } + getLogListByWs(); +} + +function getLogListByWs() { + finished.value = false; + logList.value = []; + websocket.value?.open(); + if (props.type === 'job') { + const taskData = props.taskData! as Api.Job.JobTask; + const msg = { + taskBatchId: taskData.taskBatchId, + taskId: taskData.id + }; + websocket.value?.send(JSON.stringify(msg)); + } + + if (props.type === 'retry') { + const taskData = props.taskData! as Api.RetryTask.RetryTask; + const msg = { + groupName: taskData.groupName, + retryTaskId: taskData.id + }; + websocket.value?.send(JSON.stringify(msg)); + } +} + +async function getLogListByHttp() { clearTimeout(interval.value); let logData = null; let logError; @@ -110,7 +160,12 @@ async function getLogList() { logList.value.push(...logData.message); logList.value .sort((a, b) => Number.parseInt(a.time_stamp, 10) - Number.parseInt(b.time_stamp, 10)) - .forEach((item, index) => (item.index = index)); + .forEach((item, index) => { + item.index = index; + if (!item.key) { + item.key = `${item.time_stamp}-${generateRandomString(16)}`; + } + }); } nextTick(() => { if (isAutoScroll.value) virtualListInst.value?.scrollTo({ position: 'bottom', debounce: true }); @@ -162,22 +217,58 @@ watch( () => visible.value, async val => { if (val) { + logList.value = []; if (props.modelValue) { logList.value = [...props.modelValue]; } } - if ((val || !props.drawer) && props.type && props.taskData) { - finished.value = false; - controller = new AbortController(); - await getLogList(); - } - if (!val && props.drawer) { stopLog(); + return; } - }, - { immediate: true } + + if (((val && props.drawer) || !props.drawer) && props.type && props.taskData) { + finished.value = false; + controller = new AbortController(); + if (props.fetchType === 'ws') { + const url = initWebSocketUrl('JOB_LOG_SCENE', props.taskData.id); + if (!url) { + window.$message?.error('Token 失效'); + visible.value = false; + return; + } + websocket.value = useWebSocket(url, { + immediate: false, + autoConnect: false, + autoReconnect: { + // 重连最大次数 + retries: 3, + // 重连间隔 + delay: 1000, + onFailed() { + window.$message?.error('websocket 连接失败'); + visible.value = false; + } + }, + onMessage: (_, e) => { + if (e.data !== 'END') { + const data = JSON.parse(e.data) as Api.JobLog.JobMessage; + data.key = `${data.time_stamp}-${generateRandomString(16)}`; + logList.value.push(data); + } else { + finished.value = true; + stopLogByWs(); + } + } + }); + getLogListByWs(); + return; + } + + await getLogList(); + } + } ); function timestampToDate(timestamp: string): string { @@ -284,7 +375,7 @@ const SnailLogComponent = defineComponent({ } const restOfText = throwable.replace(/^.+(\n|$)/m, ''); return ( - + {`${restOfText}`} ); @@ -302,7 +393,7 @@ const SnailLogComponent = defineComponent({ const restOfText = msg.replace(/^.+(\n|$)/m, '').replaceAll('\n', '\n - '); if (restOfText) { return ( - + {` - ${restOfText}`} ); @@ -336,8 +427,8 @@ const SnailLogComponent = defineComponent({ onResize={handleResize} > {{ - default: ({ item: message }: { item: Api.JobLog.JobMessage }) => ( -
+              default: ({ item: message }: { item: Api.JobLog.JobMessage; index: number }) => (
+                
                   
{timestampToDate(message.time_stamp)} import { ref, watch } from 'vue'; -import { contentTypeRecord } from '@/constants/business'; +import { contentTypeRecord, workFlowNodeStatusRecord } from '@/constants/business'; defineOptions({ name: 'CallbackDetail' @@ -48,6 +48,9 @@ const onClose = () => { {{ modelValue.callback?.secret }} + + {{ $t(workFlowNodeStatusRecord[modelValue.workflowNodeStatus!]) }} + diff --git a/src/components/workflow/modules/detail/task-detail.vue b/src/components/workflow/modules/detail/task-detail.vue index 212bbe1..bf954df 100644 --- a/src/components/workflow/modules/detail/task-detail.vue +++ b/src/components/workflow/modules/detail/task-detail.vue @@ -54,7 +54,7 @@ const getTaskName = (id: string) => { {{ $t(failStrategyRecord[modelValue.failStrategy!]) }} - + {{ $t(workFlowNodeStatusRecord[modelValue.workflowNodeStatus!]) }} diff --git a/src/components/workflow/modules/drawer/callback-drawer.vue b/src/components/workflow/modules/drawer/callback-drawer.vue index 2aba6ac..52368aa 100644 --- a/src/components/workflow/modules/drawer/callback-drawer.vue +++ b/src/components/workflow/modules/drawer/callback-drawer.vue @@ -98,8 +98,8 @@ const rules: FormRules = { diff --git a/src/components/workflow/modules/drawer/task-drawer.vue b/src/components/workflow/modules/drawer/task-drawer.vue index e47251b..343e187 100644 --- a/src/components/workflow/modules/drawer/task-drawer.vue +++ b/src/components/workflow/modules/drawer/task-drawer.vue @@ -141,7 +141,7 @@ const jobTaskChange = (_: string, option: { label: string; value: number }) => { - + any, wait: number, immediate?: boolean) { } }; } + +/** + * 生成 随机数 + * + * @param length - 长度 + * @returns 随机数 + */ +export function generateRandomString(length: number) { + const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + let randomString = ''; + for (let i = 0; i < length; i += 1) { + const randomNumber = Math.floor(Math.random() * chars.length); + randomString += chars.substring(randomNumber, randomNumber + 1); + } + return randomString; +} diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts index cb2c719..c3ee656 100644 --- a/src/utils/websocket.ts +++ b/src/utils/websocket.ts @@ -1,28 +1,24 @@ /** socket 通信 */ import { getServiceBaseURL } from '@/utils/service'; import { localStg } from './storage'; -const { baseURL } = getServiceBaseURL(import.meta.env, false); -const url = baseURL.replace('http://', 'ws://'); +import { generateRandomString } from './common'; -/** 生成 token */ -function generateToken(length: number) { - const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; - let token = 'SID_'; - for (let i = 0; i < length; i += 1) { - const randomNumber = Math.floor(Math.random() * chars.length); - token += chars.substring(randomNumber, randomNumber + 1); - } - return token; -} - -// 初始化socket -export function initWebSocket(scene: string) { +/** + * 初始化 websocket + * + * @param scene - 场景 + * @param sid - 会话 id + * @returns + */ +export function initWebSocketUrl(scene: string, sid?: string) { + const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; + const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y'; + const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy); + const url = + import.meta.env.MODE === 'test' ? import.meta.env.VITE_SERVICE_BASE_URL : protocol + window.location.host + baseURL; const token = localStg.get('token'); - if (import.meta.env.VITE_APP_WEBSOCKET === 'N' || !token) { + if (!token) { return null; } - - const sid = generateToken(32); - // 初始化 websocket - return new WebSocket(`${url}/websocket?Snail-Job-Auth=${token}&sid=${sid}&scene=${scene}`); + return `${url}/websocket?Snail-Job-Auth=${token}&sid=${sid ?? generateRandomString(32)}&scene=${scene}`; } diff --git a/src/views/_builtin/log/index.vue b/src/views/_builtin/log/index.vue index f5ee0f9..4373d38 100644 --- a/src/views/_builtin/log/index.vue +++ b/src/views/_builtin/log/index.vue @@ -42,7 +42,7 @@ const title = computed(() => { diff --git a/src/views/job/batch/modules/job-batch-detail-drawer.vue b/src/views/job/batch/modules/job-batch-detail-drawer.vue index d17c973..3ab0688 100644 --- a/src/views/job/batch/modules/job-batch-detail-drawer.vue +++ b/src/views/job/batch/modules/job-batch-detail-drawer.vue @@ -75,7 +75,7 @@ async function retry() { - +