fix(websocket): 调整 websocket 连接建立后执行逻辑

--bug=1047437 --user=白奇 WebSocket 建立连接和相关接口执行问题 https://www.tapd.cn/55049933/s/1592004
This commit is contained in:
baiqi 2024-10-15 14:59:12 +08:00 committed by Craftsman
parent 61d58d8ce7
commit 78106dd94c
8 changed files with 245 additions and 189 deletions

View File

@ -0,0 +1,30 @@
import { getSocket } from '@/api/modules/project-management/commonScript';
export interface WebsocketParams {
reportId: string | number;
socketUrl?: string;
host?: string;
onMessage?: (event: MessageEvent) => void;
}
export default function useWebsocket(options: WebsocketParams) {
const websocket = ref<WebSocket>();
function createSocket() {
return new Promise((resolve) => {
websocket.value = getSocket(options.reportId, options.socketUrl, options.host);
websocket.value.addEventListener('message', (event) => {
if (options.onMessage) {
options.onMessage(event);
}
});
websocket.value.addEventListener('open', () => {
resolve(true);
});
});
}
return {
websocket,
createSocket,
};
}

View File

@ -510,10 +510,10 @@
import { getPluginScript, getProtocolList } from '@/api/modules/api-test/common';
import { addCase } from '@/api/modules/api-test/management';
import { getSocket } from '@/api/modules/project-management/commonScript';
import { getProjectOptions } from '@/api/modules/project-management/projectMember';
import { useI18n } from '@/hooks/useI18n';
import useShortcutSave from '@/hooks/useShortcutSave';
import useWebsocket from '@/hooks/useWebsocket';
import useRequestCompositionStore from '@/store/modules/api/requestComposition';
import useAppStore from '@/store/modules/app';
import useUserStore from '@/store/modules/user';
@ -1026,40 +1026,42 @@
/**
* 开启websocket监听接收执行结果
*/
function debugSocket(executeType?: 'localExec' | 'serverExec') {
websocket.value = getSocket(
reportId.value,
executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? localExecuteUrl.value : ''
);
websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (requestVModel.value.reportId === data.reportId) {
// tabtab
requestVModel.value.response = data.taskResult;
requestVModel.value.executeLoading = false;
requestVModel.value.isExecute = false;
} else {
// tab
temporaryResponseMap[data.reportId] = data.taskResult;
async function debugSocket(executeType?: 'localExec' | 'serverExec') {
const { createSocket, websocket: _websocket } = useWebsocket({
reportId: reportId.value,
socketUrl: executeType === 'localExec' ? '/ws/debug' : '',
host: executeType === 'localExec' ? localExecuteUrl.value : '',
onMessage: (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (requestVModel.value.reportId === data.reportId) {
// tabtab
requestVModel.value.response = data.taskResult;
requestVModel.value.executeLoading = false;
requestVModel.value.isExecute = false;
} else {
// tab
temporaryResponseMap[data.reportId] = data.taskResult;
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocket.value?.close();
if (requestVModel.value.reportId === data.reportId) {
requestVModel.value.executeLoading = false;
requestVModel.value.isExecute = false;
}
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocket.value?.close();
if (requestVModel.value.reportId === data.reportId) {
requestVModel.value.executeLoading = false;
requestVModel.value.isExecute = false;
}
}
},
});
await createSocket();
websocket.value = _websocket.value;
}
/**
* 生成请求参数
* @param executeType 执行类型执行时传入
*/
function makeRequestParams(executeType?: 'localExec' | 'serverExec') {
async function makeRequestParams(executeType?: 'localExec' | 'serverExec') {
const isExecute = executeType === 'localExec' || executeType === 'serverExec';
const { formDataBody, wwwFormBody, jsonBody } = requestVModel.value.body;
const polymorphicName = protocolOptions.value.find(
@ -1120,7 +1122,7 @@
reportId.value = getGenerateId();
requestVModel.value.reportId = reportId.value; // ID
if (isExecute && !props.isCase) {
debugSocket(executeType); // websocket
await debugSocket(executeType); // websocket
}
let requestName = '';
let requestModuleId = '';
@ -1199,7 +1201,7 @@
await nextTick();
requestVModel.value.executeLoading = true;
requestVModel.value.response = cloneDeep(defaultResponse);
const res = await props.executeApi(makeRequestParams(executeType) as ExecuteRequestParams);
const res = await props.executeApi((await makeRequestParams(executeType)) as ExecuteRequestParams);
if (executeType === 'localExec' && props.localExecuteApi && localExecuteUrl.value) {
await props.localExecuteApi(localExecuteUrl.value, res);
}
@ -1217,7 +1219,7 @@
if (!props.executeApi) return;
requestVModel.value.executeLoading = true;
requestVModel.value.response = cloneDeep(defaultResponse);
const res = await props.executeApi(makeRequestParams(executeType) as ExecuteRequestParams);
const res = await props.executeApi((await makeRequestParams(executeType)) as ExecuteRequestParams);
if (executeType === 'localExec' && props.localExecuteApi && localExecuteUrl.value) {
await props.localExecuteApi(localExecuteUrl.value, res);
}
@ -1347,7 +1349,7 @@
saveLoading.value = true;
}
let params;
const requestParams = makeRequestParams();
const requestParams = await makeRequestParams();
if (props.isDefinition) {
params = {
...(fullParams || requestParams),
@ -1551,7 +1553,7 @@
if (!errors) {
try {
saveCaseLoading.value = true;
const definitionParams = makeRequestParams();
const definitionParams = await makeRequestParams();
if (requestVModel.value.isNew) {
//
await realSave(definitionParams, true);
@ -1614,7 +1616,7 @@
const tempApiDetail = ref<RequestParam>();
const saveNewApiModalVisible = ref(false);
function handleSelect(value: string | number | Record<string, any> | undefined) {
async function handleSelect(value: string | number | Record<string, any> | undefined) {
if (requestVModel.value.url === '' && requestVModel.value.protocol === 'HTTP') {
isUrlError.value = true;
return;
@ -1626,7 +1628,7 @@
isUrlError.value = false;
isNameError.value = false;
if (value === 'saveAsApi') {
const params = makeRequestParams();
const params = await makeRequestParams();
tempApiDetail.value = {
...params,
...params.request,

View File

@ -322,10 +322,10 @@
stopApiExport,
updateDefinition,
} from '@/api/modules/api-test/management';
import { getSocket } from '@/api/modules/project-management/commonScript';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useTableStore from '@/hooks/useTableStore';
import useWebsocket from '@/hooks/useWebsocket';
import useAppStore from '@/store/modules/app';
import useCacheStore from '@/store/modules/cache/cache';
import { characterLimit, downloadByteFile, getGenerateId, operationWidth } from '@/utils';
@ -1136,26 +1136,31 @@
const exportingMessage = ref();
// websocket
function startWebsocketGetExportResult() {
websocket.value = getSocket(reportId.value, '/ws/export');
websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
exportingMessage.value.close();
reportId.value = data.fileId;
// taskId.value = data.taskId;
if (data.isSuccessful) {
showExportSuccessfulMessage(reportId.value, data.count);
} else {
Message.error({
content: t('common.exportFailed'),
duration: 999999999, //
closable: true,
});
async function startWebsocketGetExportResult() {
const { createSocket, websocket: _websocket } = useWebsocket({
reportId: reportId.value,
socketUrl: '/ws/export',
onMessage: (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
exportingMessage.value.close();
reportId.value = data.fileId;
// taskId.value = data.taskId;
if (data.isSuccessful) {
showExportSuccessfulMessage(reportId.value, data.count);
} else {
Message.error({
content: t('common.exportFailed'),
duration: 999999999, //
closable: true,
});
}
websocket.value?.close();
}
websocket.value?.close();
}
},
});
await createSocket();
websocket.value = _websocket.value;
}
//
@ -1203,7 +1208,7 @@
try {
exportLoading.value = true;
reportId.value = getGenerateId();
startWebsocketGetExportResult();
await startWebsocketGetExportResult();
const batchConditionParams = await getBatchConditionParams();
const res = await exportApiDefinition(
{

View File

@ -140,8 +140,8 @@
import { localExecuteApiDebug, stopExecute, stopLocalExecute } from '@/api/modules/api-test/common';
import { debugCase, deleteCase, runCase, toggleFollowCase } from '@/api/modules/api-test/management';
import { getSocket } from '@/api/modules/project-management/commonScript';
import useModal from '@/hooks/useModal';
import useWebsocket from '@/hooks/useWebsocket';
import useAppStore from '@/store/modules/app';
import { getGenerateId } from '@/utils';
@ -276,31 +276,33 @@
const websocket = ref<WebSocket>();
const temporaryResponseMap: Record<string, any> = {}; // websockettab
// websocket
function debugSocket(executeType?: 'localExec' | 'serverExec') {
websocket.value = getSocket(
reportId.value,
executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? executeRef.value?.localExecuteUrl : ''
);
websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (caseDetail.value.reportId === data.reportId) {
// tabtab
caseDetail.value.response = data.taskResult; //
async function debugSocket(executeType?: 'localExec' | 'serverExec') {
const { createSocket, websocket: _websocket } = useWebsocket({
reportId: reportId.value,
socketUrl: executeType === 'localExec' ? '/ws/debug' : '',
host: executeType === 'localExec' ? executeRef.value?.localExecuteUrl : '',
onMessage: (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (caseDetail.value.reportId === data.reportId) {
// tabtab
caseDetail.value.response = data.taskResult; //
caseDetail.value.executeLoading = false;
executeCase.value = false;
} else {
// tab
temporaryResponseMap[data.reportId] = data.taskResult;
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocket.value?.close();
caseDetail.value.executeLoading = false;
executeCase.value = false;
} else {
// tab
temporaryResponseMap[data.reportId] = data.taskResult;
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocket.value?.close();
caseDetail.value.executeLoading = false;
executeCase.value = false;
}
},
});
await createSocket();
websocket.value = _websocket.value;
}
async function handleExecute(executeType?: 'localExec' | 'serverExec') {
try {
@ -320,7 +322,7 @@
linkFileIds: caseDetail.value.linkFileIds,
uploadFileIds: caseDetail.value.uploadFileIds,
};
debugSocket(executeType); // websocket
await debugSocket(executeType); // websocket
if (executeType === 'serverExec') {
//
res = await runCase(params);

View File

@ -124,9 +124,9 @@
updateCase,
uploadTempFileCase,
} from '@/api/modules/api-test/management';
import { getSocket } from '@/api/modules/project-management/commonScript';
import { useI18n } from '@/hooks/useI18n';
import useShortcutSave from '@/hooks/useShortcutSave';
import useWebsocket from '@/hooks/useWebsocket';
import useAppStore from '@/store/modules/app';
import { getGenerateId } from '@/utils';
@ -277,9 +277,9 @@
}
drawerLoading.value = true;
//
if (!requestCompositionRef.value?.makeRequestParams()) return;
const { linkFileIds, uploadFileIds, request, unLinkFileIds, deleteFileIds } =
requestCompositionRef.value.makeRequestParams();
const requestParams = await requestCompositionRef.value?.makeRequestParams();
if (!requestParams) return;
const { linkFileIds, uploadFileIds, request, unLinkFileIds, deleteFileIds } = requestParams;
const { name, priority, status, tags, id } = detailForm.value;
const params: AddApiCaseParams = {
projectId: appStore.currentProjectId,
@ -339,35 +339,37 @@
const websocket = ref<WebSocket>();
const temporaryResponseMap: Record<string, any> = {}; // websockettab
// websocket
function debugSocket(executeType?: 'localExec' | 'serverExec') {
websocket.value = getSocket(
reportId.value,
executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? executeRef.value?.localExecuteUrl : ''
);
websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (detailForm.value.reportId === data.reportId) {
// tabtab
detailForm.value.response = data.taskResult; //
async function debugSocket(executeType?: 'localExec' | 'serverExec') {
const { createSocket, websocket: _websocket } = useWebsocket({
reportId: reportId.value,
socketUrl: executeType === 'localExec' ? '/ws/debug' : '',
host: executeType === 'localExec' ? executeRef.value?.localExecuteUrl : '',
onMessage: (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (detailForm.value.reportId === data.reportId) {
// tabtab
detailForm.value.response = data.taskResult; //
detailForm.value.executeLoading = false;
} else {
// tab
temporaryResponseMap[data.reportId] = data.taskResult;
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocket.value?.close();
detailForm.value.executeLoading = false;
} else {
// tab
temporaryResponseMap[data.reportId] = data.taskResult;
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocket.value?.close();
detailForm.value.executeLoading = false;
}
},
});
await createSocket();
websocket.value = _websocket.value;
}
async function handleExecute(executeType?: 'localExec' | 'serverExec') {
try {
detailForm.value.executeLoading = true;
detailForm.value.response = cloneDeep(defaultResponse);
const makeRequestParams = requestCompositionRef.value?.makeRequestParams(executeType); // reportIdreportId
const makeRequestParams = await requestCompositionRef.value?.makeRequestParams(executeType); // reportIdreportId
reportId.value = getGenerateId();
detailForm.value.reportId = reportId.value; // ID
let res;
@ -381,7 +383,7 @@
linkFileIds: makeRequestParams?.linkFileIds,
uploadFileIds: makeRequestParams?.uploadFileIds,
};
debugSocket(executeType); // websocket
await debugSocket(executeType); // websocket
if (!(detailForm.value.id as string).startsWith('c') && executeType === 'serverExec') {
//
res = await runCase({

View File

@ -44,34 +44,39 @@ export default function useStepExecute({
* websocket监听
*/
function debugSocket(step: ScenarioStepItem, _scenario: Scenario, reportId: string | number) {
websocketMap[reportId] = getSocket(
reportId || '',
scenario.value.executeType === 'localExec' ? '/ws/debug' : '',
scenario.value.executeType === 'localExec' ? localExecuteUrl?.value : ''
);
websocketMap[reportId].addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (step.reportId === data.reportId) {
// 判断当前查看的tab是否是当前返回的报告的tab是的话直接赋值
data.taskResult.requestResults.forEach((result: RequestResult) => {
if (_scenario.stepResponses[result.stepId] === undefined) {
_scenario.stepResponses[result.stepId] = [];
}
_scenario.stepResponses[result.stepId].push({
...result,
console: data.taskResult.console,
return new Promise((resolve) => {
websocketMap[reportId] = getSocket(
reportId || '',
scenario.value.executeType === 'localExec' ? '/ws/debug' : '',
scenario.value.executeType === 'localExec' ? localExecuteUrl?.value : ''
);
websocketMap[reportId].addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (step.reportId === data.reportId) {
// 判断当前查看的tab是否是当前返回的报告的tab是的话直接赋值
data.taskResult.requestResults.forEach((result: RequestResult) => {
if (_scenario.stepResponses[result.stepId] === undefined) {
_scenario.stepResponses[result.stepId] = [];
}
_scenario.stepResponses[result.stepId].push({
...result,
console: data.taskResult.console,
});
});
});
}
} else if (data.msgType === 'EXEC_END') {
// 执行结束关闭websocket
websocketMap[reportId]?.close();
if (step.reportId === data.reportId) {
step.isExecuting = false;
updateStepStatus([step], _scenario.stepResponses, step.uniqueId);
}
}
} else if (data.msgType === 'EXEC_END') {
// 执行结束关闭websocket
websocketMap[reportId]?.close();
if (step.reportId === data.reportId) {
step.isExecuting = false;
updateStepStatus([step], _scenario.stepResponses, step.uniqueId);
}
}
});
websocketMap[reportId].addEventListener('open', () => {
resolve(true);
});
});
}
@ -82,7 +87,7 @@ export default function useStepExecute({
try {
currentStep.isExecuting = true;
currentStep.executeStatus = ScenarioExecuteStatus.EXECUTING;
debugSocket(currentStep, scenario.value, executeParams.reportId); // 开启websocket
await debugSocket(currentStep, scenario.value, executeParams.reportId); // 开启websocket
const res = await debugScenario({
id: scenario.value.id || '',
grouped: false,

View File

@ -211,45 +211,50 @@
* 开启websocket监听接收执行结果
*/
function debugSocket(scenario: Scenario, executeType?: 'localExec' | 'serverExec') {
websocketMap[scenario.reportId] = getSocket(
scenario.reportId || '',
executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? localExecuteUrl.value : ''
);
websocketMap[scenario.reportId].addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (scenario.reportId === data.reportId) {
// tabtab
data.taskResult.requestResults.forEach((result: RequestResult) => {
if (result.stepId) {
// id
if (scenario.stepResponses[result.stepId] === undefined) {
scenario.stepResponses[result.stepId] = [];
return new Promise((resolve) => {
websocketMap[scenario.reportId] = getSocket(
scenario.reportId || '',
executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? localExecuteUrl.value : ''
);
websocketMap[scenario.reportId].addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (scenario.reportId === data.reportId) {
// tabtab
data.taskResult.requestResults.forEach((result: RequestResult) => {
if (result.stepId) {
// id
if (scenario.stepResponses[result.stepId] === undefined) {
scenario.stepResponses[result.stepId] = [];
}
scenario.stepResponses[result.stepId].push({
...result,
console: data.taskResult.console,
});
if (result.status === ScenarioExecuteStatus.FAKE_ERROR) {
scenario.executeFakeErrorCount += 1;
} else if (result.isSuccessful) {
scenario.executeSuccessCount += 1;
} else {
scenario.executeFailCount += 1;
}
}
scenario.stepResponses[result.stepId].push({
...result,
console: data.taskResult.console,
});
if (result.status === ScenarioExecuteStatus.FAKE_ERROR) {
scenario.executeFakeErrorCount += 1;
} else if (result.isSuccessful) {
scenario.executeSuccessCount += 1;
} else {
scenario.executeFailCount += 1;
}
}
});
});
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocketMap[scenario.reportId]?.close();
if (scenario.reportId === data.reportId) {
scenario.executeLoading = false;
scenario.isExecute = false;
setStepExecuteStatus(scenario);
}
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocketMap[scenario.reportId]?.close();
if (scenario.reportId === data.reportId) {
scenario.executeLoading = false;
scenario.isExecute = false;
setStepExecuteStatus(scenario);
}
}
});
websocketMap[scenario.reportId].addEventListener('open', () => {
resolve(true);
});
});
}
@ -275,8 +280,8 @@
activeScenarioTab.value.stepResponses = {};
activeScenarioTab.value.reportId = executeParams.reportId; // ID
activeScenarioTab.value.executeType = executeType; // ID
debugSocket(activeScenarioTab.value, executeType); // websocket
activeScenarioTab.value.isDebug = !isExecute;
await debugSocket(activeScenarioTab.value, executeType); // websocket
let res;
if (isExecute && executeType !== 'localExec' && !activeScenarioTab.value.isNew) {
//

View File

@ -426,10 +426,10 @@
stopCaseExport,
updateCaseRequest,
} from '@/api/modules/case-management/featureCase';
import { getSocket } from '@/api/modules/project-management/commonScript';
import { getCaseRelatedInfo } from '@/api/modules/project-management/menuManagement';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useWebsocket from '@/hooks/useWebsocket';
import { useAppStore, useTableStore } from '@/store';
import useCacheStore from '@/store/modules/cache/cache';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
@ -1252,26 +1252,31 @@
});
}
// websocket
function startWebsocketGetExportResult() {
websocket.value = getSocket(reportId.value, '/ws/export');
websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
exportingMessage.value.close();
reportId.value = data.fileId;
taskId.value = data.taskId;
if (data.isSuccessful) {
showExportSuccessfulMessage(reportId.value, data.count);
} else {
Message.error({
content: t('common.exportFailed'),
duration: 999999999, //
closable: true,
});
async function startWebsocketGetExportResult() {
const { createSocket, websocket: _websocket } = useWebsocket({
reportId: reportId.value,
socketUrl: '/ws/export',
onMessage: (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
exportingMessage.value.close();
reportId.value = data.fileId;
taskId.value = data.taskId;
if (data.isSuccessful) {
showExportSuccessfulMessage(reportId.value, data.count);
} else {
Message.error({
content: t('common.exportFailed'),
duration: 999999999, //
closable: true,
});
}
websocket.value?.close();
}
websocket.value?.close();
}
},
});
await createSocket();
websocket.value = _websocket.value;
}
function getConfirmFields(option: MsExportDrawerOption[], columnType: string) {
@ -1284,7 +1289,7 @@
exportLoading.value = true;
const { selectedIds, selectAll, excludeIds } = batchParams.value;
reportId.value = getGenerateId();
startWebsocketGetExportResult();
await startWebsocketGetExportResult();
const params = {
projectId: currentProjectId.value,
selectIds: selectAll ? [] : selectedIds,
@ -1329,7 +1334,7 @@
taskId.value = res.taskId;
Message.error(t('caseManagement.featureCase.alreadyExportTasks'));
if (!websocket.value) {
startWebsocketGetExportResult();
await startWebsocketGetExportResult();
}
showExportingMessage();
}