From 06d4d6a79b4d57714b1aaa5fe916fc6d3591e448 Mon Sep 17 00:00:00 2001 From: AgAngle <1323481023@qq.com> Date: Mon, 9 Dec 2024 16:01:04 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E7=9A=84=E6=AD=A5=E9=AA=A4=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=85=B3=E8=81=94=E7=9A=84=E6=96=87=E4=BB=B6=E6=9C=89=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --story=1016879 --user=陈建星 【Bug转需求】【接口测试】场景-场景步骤复制接口/CASE-接口内存在本地上传的文件-场景内转存-报错 https://www.tapd.cn/55049933/s/1626892 --- .../api/service/ApiCommonService.java | 15 + .../api/service/ApiFileResourceService.java | 4 +- .../ApiScenarioDataTransferService.java | 15 +- .../scenario/ApiScenarioFileService.java | 657 ++++++++++++++++++ .../scenario/ApiScenarioRunService.java | 23 +- .../service/scenario/ApiScenarioService.java | 610 +--------------- .../service/FileAssociationService.java | 9 + 7 files changed, 729 insertions(+), 604 deletions(-) create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioFileService.java diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiCommonService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiCommonService.java index e9331c2a88..0da718f01a 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiCommonService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiCommonService.java @@ -13,6 +13,7 @@ import io.metersphere.api.dto.request.http.body.BinaryBody; import io.metersphere.api.dto.request.http.body.Body; import io.metersphere.api.dto.request.http.body.FormDataBody; import io.metersphere.api.dto.request.http.body.FormDataKV; +import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.project.api.KeyValueParam; import io.metersphere.project.api.assertion.MsScriptAssertion; @@ -603,4 +604,18 @@ public class ApiCommonService { ExecTaskItem execTaskItem = execTaskItems.getFirst(); return execTaskItem; } + + public AbstractMsTestElement getAbstractMsTestElement(byte[] msTestElementByte) { + return getAbstractMsTestElement(new String(msTestElementByte)); + } + + public AbstractMsTestElement getAbstractMsTestElement(String msTestElementStr) { + try { + return ApiDataUtils.parseObject(msTestElementStr, AbstractMsTestElement.class); + // 如果插件删除,会转换异常 + } catch (Exception e) { + LogUtils.error(e); + } + return null; + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiFileResourceService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiFileResourceService.java index e57e8ddb6e..b7c2de6c4e 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiFileResourceService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiFileResourceService.java @@ -401,9 +401,9 @@ public class ApiFileResourceService { return fileId; } - public List selectByResourceIds(Collection copyFromStepIds) { + public List selectByResourceIds(List copyFromStepIds) { ApiFileResourceExample example = new ApiFileResourceExample(); - example.createCriteria().andResourceIdIn(new ArrayList<>(copyFromStepIds)); + example.createCriteria().andResourceIdIn(copyFromStepIds); return apiFileResourceMapper.selectByExample(example); } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiScenarioDataTransferService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiScenarioDataTransferService.java index a0c15db223..61663c83ce 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiScenarioDataTransferService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiScenarioDataTransferService.java @@ -24,10 +24,7 @@ import io.metersphere.api.service.definition.ApiDefinitionExportService; import io.metersphere.api.service.definition.ApiDefinitionImportService; import io.metersphere.api.service.definition.ApiDefinitionModuleService; import io.metersphere.api.service.definition.ApiTestCaseService; -import io.metersphere.api.service.scenario.ApiScenarioLogService; -import io.metersphere.api.service.scenario.ApiScenarioModuleService; -import io.metersphere.api.service.scenario.ApiScenarioRunService; -import io.metersphere.api.service.scenario.ApiScenarioService; +import io.metersphere.api.service.scenario.*; import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.api.utils.ApiDefinitionImportUtils; import io.metersphere.api.utils.ApiScenarioImportUtils; @@ -138,6 +135,8 @@ public class ApiScenarioDataTransferService { @Resource private ApiScenarioService apiScenarioService; @Resource + private ApiScenarioFileService apiScenarioFileService; + @Resource private ApiFileResourceService apiFileResourceService; @Resource private SqlSessionFactory sqlSessionFactory; @@ -416,7 +415,7 @@ public class ApiScenarioDataTransferService { } // 处理 csv 文件 - apiScenarioService.handleCsvUpdate(request.getScenarioConfig(), scenario, operator); + apiScenarioFileService.handleCsvUpdate(request.getScenarioConfig(), scenario, operator); }); sqlSession.flushStatements(); @@ -627,11 +626,11 @@ public class ApiScenarioDataTransferService { // 处理 csv 相关数据表 this.handleCsvDataUpdate(csvVariables, scenario, List.of(), batchCsvMapper, batchCsvStepMapper); // 处理文件的上传 因为是导入调用的,有些文件可能不存在,所以要进行判断 - ApiFileResourceUpdateRequest resourceUpdateRequest = apiScenarioService.getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), operator); + ApiFileResourceUpdateRequest resourceUpdateRequest = apiScenarioFileService.getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), operator); // 设置本地文件相关参数 - apiScenarioService.setCsvLocalFileParam(csvVariables, List.of(), resourceUpdateRequest); + apiScenarioFileService.setCsvLocalFileParam(csvVariables, List.of(), resourceUpdateRequest); // 设置关联文件相关参数 - apiScenarioService.setCsvLinkFileParam(csvVariables, List.of(), resourceUpdateRequest); + apiScenarioFileService.setCsvLinkFileParam(csvVariables, List.of(), resourceUpdateRequest); FileAssociationSource source = extFileAssociationMapper.selectNameBySourceTableAndId( FileAssociationSourceUtil.getQuerySql(ApiFileResourceType.API_SCENARIO.name()), resourceUpdateRequest.getResourceId()); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioFileService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioFileService.java new file mode 100644 index 0000000000..f40afe9219 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioFileService.java @@ -0,0 +1,657 @@ +package io.metersphere.api.service.scenario; + +import io.metersphere.api.domain.*; +import io.metersphere.api.dto.*; +import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest; +import io.metersphere.api.dto.scenario.*; +import io.metersphere.api.mapper.*; +import io.metersphere.api.service.ApiCommonService; +import io.metersphere.api.service.ApiFileResourceService; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import io.metersphere.project.domain.FileAssociation; +import io.metersphere.project.domain.FileMetadata; +import io.metersphere.project.domain.FileMetadataExample; +import io.metersphere.project.mapper.FileMetadataMapper; +import io.metersphere.project.service.FileAssociationService; +import io.metersphere.sdk.constants.*; +import io.metersphere.sdk.util.*; +import io.metersphere.system.log.constants.OperationLogModule; +import io.metersphere.system.uid.IDGenerator; +import jakarta.annotation.Resource; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiScenarioFileService { + @Resource + private ApiFileResourceService apiFileResourceService; + @Resource + private FileAssociationService fileAssociationService; + @Resource + private ApiFileResourceMapper apiFileResourceMapper; + @Resource + private ApiScenarioCsvMapper apiScenarioCsvMapper; + @Resource + private ApiCommonService apiCommonService; + @Resource + private ApiScenarioStepMapper apiScenarioStepMapper; + @Resource + private ApiScenarioCsvStepMapper apiScenarioCsvStepMapper; + @Resource + private FileMetadataMapper fileMetadataMapper; + + /** + * 处理保存时,copy的步骤中文件 + * @param apiScenarioCopyStepMap + * @param stepDetails + */ + public void handleSaveCopyStepFiles(ApiScenarioCopyStepMap apiScenarioCopyStepMap, Map stepDetails, ApiScenario scenario, String userId) { + try { + // 处理本地上传的文件 + List apiFileResources = new ArrayList<>(); + apiFileResources.addAll(handleCopyFromStepFiles(stepDetails, apiScenarioCopyStepMap.getCopyFromStepIdMap(), scenario)); + apiFileResources.addAll(handleCopyApiFiles(stepDetails, apiScenarioCopyStepMap.getIsNewApiResourceMap(), scenario)); + apiFileResources.addAll(handleCopyApiCaseFiles(stepDetails, apiScenarioCopyStepMap.getIsNewApiCaseResourceMap(), scenario)); + if (CollectionUtils.isNotEmpty(apiFileResources)) { + // 插入步骤和文件的关联关系 + apiFileResources.forEach(apiFileResource -> apiFileResource.setProjectId(scenario.getProjectId())); + apiFileResourceMapper.batchInsert(apiFileResources); + } + + // 处理关联的文件 + handleCopyStepAssociationFiles(apiScenarioCopyStepMap.getCopyFromStepIdMap(), scenario, userId); + handleCopyStepAssociationFiles(apiScenarioCopyStepMap.getIsNewApiResourceMap(), scenario, userId); + handleCopyStepAssociationFiles(apiScenarioCopyStepMap.getIsNewApiCaseResourceMap(), scenario, userId); + } catch (Exception e) { + LogUtils.error(e); + } + } + + /** + * 处理保存时,copy的步骤中的关联文件 + * @param stepResourceIdMap + * @param scenario + * @param userId + */ + private void handleCopyStepAssociationFiles(Map stepResourceIdMap, ApiScenario scenario, String userId) { + List copyFromStepIds = new ArrayList<>(stepResourceIdMap.values()); + List fileAssociations = fileAssociationService.selectBySourceIds(copyFromStepIds); + // key 为资源ID,value 为步骤ID + Map resourceStepIdMap = new HashMap<>(); + stepResourceIdMap.forEach((k, v) -> resourceStepIdMap.put(v, k)); + for (FileAssociation fileAssociation : fileAssociations) { + String stepId = resourceStepIdMap.get(fileAssociation.getSourceId()); + if (StringUtils.isNotBlank(stepId)) { + fileAssociation.setSourceId(stepId); + fileAssociation.setSourceType(FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO_STEP); + // 关联文件 + fileAssociationService.association(stepId, FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO_STEP, List.of(fileAssociation.getFileId()), + apiFileResourceService.createFileLogRecord(userId, scenario.getProjectId(), OperationLogModule.API_SCENARIO_MANAGEMENT_SCENARIO)); + } + } + } + + /** + * 处理调试执行时,copy的步骤中文件 + * @param debugRequest + * @param apiScenarioCopyStepMap + * @param stepDetails + */ + public void handleRunCopyStepFiles(ApiScenarioDebugRequest debugRequest, ApiScenarioCopyStepMap apiScenarioCopyStepMap, Map stepDetails) { + try { + List apiFileResources = new ArrayList<>(); + apiFileResources.addAll(handleCopyFromStepFiles(stepDetails, apiScenarioCopyStepMap.getCopyFromStepIdMap(), null)); + apiFileResources.addAll(handleCopyApiFiles(stepDetails, apiScenarioCopyStepMap.getIsNewApiResourceMap(), null)); + apiFileResources.addAll(handleCopyApiCaseFiles(stepDetails, apiScenarioCopyStepMap.getIsNewApiCaseResourceMap(), null)); + + if (debugRequest.getStepFileParam() == null) { + debugRequest.setStepFileParam(new HashMap<>()); + } + // 将copy的步骤中的文件设置为新上传的临时文件,执行时从临时目录获取 + Map stepFileParam = debugRequest.getStepFileParam(); + for (ApiFileResource apiFileResource : apiFileResources) { + stepFileParam.putIfAbsent(apiFileResource.getResourceId(), new ResourceAddFileParam()); + ResourceAddFileParam resourceAddFileParam = stepFileParam.get(apiFileResource.getResourceId()); + if (resourceAddFileParam.getUploadFileIds() == null) { + resourceAddFileParam.setUploadFileIds(new ArrayList<>()); + } + resourceAddFileParam.getUploadFileIds().add(apiFileResource.getFileId()); + } + + handleCopyStepAssociationFiles(debugRequest, apiScenarioCopyStepMap.getCopyFromStepIdMap()); + handleCopyStepAssociationFiles(debugRequest, apiScenarioCopyStepMap.getIsNewApiResourceMap()); + handleCopyStepAssociationFiles(debugRequest, apiScenarioCopyStepMap.getIsNewApiCaseResourceMap()); + } catch (Exception e) { + LogUtils.error(e); + } + } + + /** + * 处理执行时,copy的步骤中的关联文件 + * @param stepResourceIdMap + */ + private void handleCopyStepAssociationFiles(ApiScenarioDebugRequest debugRequest, Map stepResourceIdMap) { + List copyFromStepIds = new ArrayList<>(stepResourceIdMap.values()); + List fileAssociations = fileAssociationService.selectBySourceIds(copyFromStepIds); + // key 为资源ID,value 为步骤ID + Map resourceStepIdMap = new HashMap<>(); + stepResourceIdMap.forEach((k, v) -> resourceStepIdMap.put(v, k)); + for (FileAssociation fileAssociation : fileAssociations) { + String stepId = resourceStepIdMap.get(fileAssociation.getSourceId()); + if (StringUtils.isNotBlank(stepId)) { + debugRequest.getStepFileParam().putIfAbsent(stepId, new ResourceAddFileParam()); + ResourceAddFileParam resourceAddFileParam = debugRequest.getStepFileParam().get(stepId); + if (resourceAddFileParam.getLinkFileIds() == null) { + resourceAddFileParam.setLinkFileIds(new ArrayList<>()); + } + // 将copy的步骤中的文件设置为新上传的关联文件,执行时从关联文件中获取 + resourceAddFileParam.getLinkFileIds().add(fileAssociation.getFileId()); + } + } + } + + /** + * 处理 copy 的步骤中的文件 + * 复制文件 + * 创建关联关系 + * 替换文件ID + * @param stepDetails + * @param copyFromStepIdMap + */ + private List handleCopyFromStepFiles(Map stepDetails, Map copyFromStepIdMap, ApiScenario scenario) { + if (copyFromStepIdMap.isEmpty()) { + return List.of(); + } + // 查询 copyFrom 的步骤所关联的文件 + List copyFromStepIds = new ArrayList<>(copyFromStepIdMap.values()); + List apiFileResources = apiFileResourceService.selectByResourceIds(copyFromStepIds); + if (apiFileResources.isEmpty()) { + return List.of(); + } + Map> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId)); + + // 查询 copyFrom 步骤的场景ID Map + List hasFileCopyFromStepIds = stepFileMap.keySet().stream().toList(); + Map copyFromStepScenarioMap = getApiScenarioStepByIds(hasFileCopyFromStepIds).stream() + .collect(Collectors.toMap(ApiScenarioStep::getId, ApiScenarioStep::getScenarioId)); + + Map fileIdMap = new HashMap<>(); + List newApiFileResources = new ArrayList<>(); + for (String stepId : copyFromStepIdMap.keySet()) { + List originApiFileResources = stepFileMap.get(copyFromStepIdMap.get(stepId)); + if (CollectionUtils.isEmpty(originApiFileResources)) { + continue; + } + + boolean isSave = scenario != null; + + String newFileId = IDGenerator.nextStr(); + for (ApiFileResource originApiFileResource : originApiFileResources) { + String sourceDir = DefaultRepositoryDir.getApiScenarioStepDir(originApiFileResource.getProjectId(), + copyFromStepScenarioMap.get(originApiFileResource.getResourceId()), originApiFileResource.getResourceId()); + + // 如果是保存,则copy到正式目录,如果是执行,则copy到临时目录 + String targetDir = isSave ? DefaultRepositoryDir.getApiScenarioStepDir(scenario.getProjectId(), scenario.getId(), stepId) + : DefaultRepositoryDir.getSystemTempDir(); + + // 复制文件 + apiFileResourceService.copyFile(sourceDir + "/" + originApiFileResource.getFileId(), + targetDir + "/" + newFileId, + originApiFileResource.getFileName()); + + // 记录步骤和文件信息 + ApiFileResource newApiFileResource = getStepApiFileResource(stepId, newFileId, originApiFileResource.getFileName()); + newApiFileResources.add(newApiFileResource); + + // 记录文件ID映射 + fileIdMap.put(originApiFileResource.getFileId(), newFileId); + } + + // 替换详情中的文件ID + replaceCopyStepFileId(stepDetails, fileIdMap, stepId); + } + return newApiFileResources; + } + + /** + * 处理复制的接口定义步骤中的文件 + * 复制文件 + * 创建关联关系 + * 替换文件ID + * @param stepDetails + */ + private List handleCopyApiFiles(Map stepDetails, Map copyApiIdMap, ApiScenario scenario) { + if (copyApiIdMap.isEmpty()) { + return List.of(); + } + // 查询 copy 的接口定义所关联的文件 + List copyApiIds = new ArrayList<>(copyApiIdMap.values()); + List apiFileResources = apiFileResourceService.selectByResourceIds(copyApiIds); + if (apiFileResources.isEmpty()) { + return List.of(); + } + Map> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId)); + + Map fileIdMap = new HashMap<>(); + List newApiFileResources = new ArrayList<>(); + for (String stepId : copyApiIdMap.keySet()) { + List originApiFileResources = stepFileMap.get(copyApiIdMap.get(stepId)); + if (CollectionUtils.isEmpty(originApiFileResources)) { + continue; + } + + String newFileId = IDGenerator.nextStr(); + for (ApiFileResource originApiFileResource : originApiFileResources) { + String sourceDir = DefaultRepositoryDir.getApiDefinitionDir(originApiFileResource.getProjectId(), originApiFileResource.getResourceId()); + + boolean isSave = scenario != null; + + // 如果是保存,则copy到正式目录,如果是执行,则copy到临时目录 + String targetDir = isSave ? DefaultRepositoryDir.getApiScenarioStepDir(scenario.getProjectId(), scenario.getId(), stepId) + : DefaultRepositoryDir.getSystemTempDir(); + + // 复制文件 + apiFileResourceService.copyFile(sourceDir + "/" + originApiFileResource.getFileId(), + targetDir + "/" + newFileId, + originApiFileResource.getFileName()); + + // 记录步骤和文件信息 + ApiFileResource newApiFileResource = getStepApiFileResource(stepId, newFileId, originApiFileResource.getFileName()); + newApiFileResources.add(newApiFileResource); + + // 记录文件ID映射 + fileIdMap.put(originApiFileResource.getFileId(), newFileId); + } + + // 替换详情中的文件ID + replaceCopyStepFileId(stepDetails, fileIdMap, stepId); + } + + return newApiFileResources; + } + + /** + * 处理复制的接口用例步骤中的文件 + * 复制文件 + * 创建关联关系 + * 替换文件ID + * @param stepDetails + */ + private List handleCopyApiCaseFiles(Map stepDetails, Map copyApiCaseIdMap, ApiScenario scenario) { + if (copyApiCaseIdMap.isEmpty()) { + return List.of(); + } + // 查询 copy 的接口定义所关联的文件 + List copyApiIds = new ArrayList<>(copyApiCaseIdMap.values()); + List apiFileResources = apiFileResourceService.selectByResourceIds(copyApiIds); + if (apiFileResources.isEmpty()) { + return List.of(); + } + Map> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId)); + + boolean isSave = scenario != null; + Map fileIdMap = new HashMap<>(); + List newApiFileResources = new ArrayList<>(); + for (String stepId : copyApiCaseIdMap.keySet()) { + List originApiFileResources = stepFileMap.get(copyApiCaseIdMap.get(stepId)); + if (CollectionUtils.isEmpty(originApiFileResources)) { + continue; + } + + String newFileId = IDGenerator.nextStr(); + for (ApiFileResource originApiFileResource : originApiFileResources) { + String sourceDir = DefaultRepositoryDir.getApiCaseDir(originApiFileResource.getProjectId(), originApiFileResource.getResourceId()); + + // 如果是保存,则copy到正式目录,如果是执行,则copy到临时目录 + String targetDir = isSave ? DefaultRepositoryDir.getApiScenarioStepDir(scenario.getProjectId(), scenario.getId(), stepId) + : DefaultRepositoryDir.getSystemTempDir(); + + // 复制文件 + apiFileResourceService.copyFile(sourceDir + "/" + originApiFileResource.getFileId(), + targetDir + "/" + newFileId, + originApiFileResource.getFileName()); + + // 记录步骤和文件信息 + ApiFileResource newApiFileResource = getStepApiFileResource(stepId, newFileId, originApiFileResource.getFileName()); + newApiFileResources.add(newApiFileResource); + + // 记录文件ID映射 + fileIdMap.put(originApiFileResource.getFileId(), newFileId); + } + + // 替换详情中的文件ID + replaceCopyStepFileId(stepDetails, fileIdMap, stepId); + } + + return newApiFileResources; + } + + /** + * 替换复制的步骤中详情的文件ID + * @param stepDetails + * @param fileIdMap + * @param stepId + */ + private void replaceCopyStepFileId(Map stepDetails, Map fileIdMap, String stepId) { + Object stepDetail = stepDetails.get(stepId); + if (stepDetail != null) { + // 替换详情中的文件ID + if (stepDetail instanceof byte[] detailBytes) { + AbstractMsTestElement msTestElement = apiCommonService.getAbstractMsTestElement(detailBytes); + for (ApiFile apiFile : apiCommonService.getApiFiles(msTestElement)) { + if (fileIdMap.get(apiFile.getFileId()) != null) { + apiFile.setFileId(fileIdMap.get(apiFile.getFileId())); + } + } + stepDetails.put(stepId, msTestElement); + } else if (stepDetail instanceof AbstractMsTestElement msTestElement) { + for (ApiFile apiFile : apiCommonService.getApiFiles(msTestElement)) { + if (fileIdMap.get(apiFile.getFileId()) != null) { + apiFile.setFileId(fileIdMap.get(apiFile.getFileId())); + } + } + } + } + } + + private ApiFileResource getStepApiFileResource(String stepId, String fileId, String fileName) { + ApiFileResource apiFileResource = new ApiFileResource(); + apiFileResource.setFileId(fileId); + apiFileResource.setResourceId(stepId); + apiFileResource.setResourceType(ApiFileResourceType.API_SCENARIO_STEP.name()); + apiFileResource.setCreateTime(System.currentTimeMillis()); + apiFileResource.setFileName(fileName); + return apiFileResource; + } + + private List getApiScenarioStepByIds(List stepIds) { + if (CollectionUtils.isEmpty(stepIds)) { + List.of(); + } + ApiScenarioStepExample example = new ApiScenarioStepExample(); + example.createCriteria().andIdIn(stepIds); + return apiScenarioStepMapper.selectByExample(example); + } + + private ApiFileResourceUpdateRequest getStepApiFileResourceUpdateRequest(String userId, ApiScenario scenario, String stepId, ResourceAddFileParam fileParam) { + ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(stepId, scenario.getProjectId(), userId); + String apiScenarioStepDir = DefaultRepositoryDir.getApiScenarioStepDir(scenario.getProjectId(), scenario.getId(), stepId); + resourceUpdateRequest.setFileAssociationSourceType(FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO_STEP); + resourceUpdateRequest.setApiResourceType(ApiFileResourceType.API_SCENARIO_STEP); + resourceUpdateRequest.setFolder(apiScenarioStepDir); + resourceUpdateRequest = BeanUtils.copyBean(resourceUpdateRequest, fileParam); + return resourceUpdateRequest; + } + + public void handleStepFilesUpdate(ApiScenarioUpdateRequest request, String updater, ApiScenario scenario) { + Map stepFileParam = request.getStepFileParam(); + if (MapUtils.isNotEmpty(stepFileParam)) { + stepFileParam.forEach((stepId, fileParam) -> { + // 处理步骤文件 + ApiFileResourceUpdateRequest resourceUpdateRequest = getStepApiFileResourceUpdateRequest(updater, scenario, stepId, fileParam); + apiFileResourceService.updateFileResource(resourceUpdateRequest); + }); + } + } + + public void handleStepFilesAdd(ApiScenarioAddRequest request, String creator, ApiScenario scenario) { + Map stepFileParam = request.getStepFileParam(); + if (MapUtils.isNotEmpty(stepFileParam)) { + stepFileParam.forEach((stepId, fileParam) -> { + // 处理步骤文件 + ApiFileResourceUpdateRequest resourceUpdateRequest = getStepApiFileResourceUpdateRequest(creator, scenario, stepId, fileParam); + apiFileResourceService.addFileResource(resourceUpdateRequest); + }); + } + } + + public void handleCsvFileAdd(List csvVariables, List dbCsv, ApiScenario scenario, String userId) { + ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), userId); + // 设置本地文件相关参数 + setCsvLocalFileParam(csvVariables, dbCsv, resourceUpdateRequest); + // 设置关联文件相关参数 + setCsvLinkFileParam(csvVariables, dbCsv, resourceUpdateRequest); + apiFileResourceService.addFileResource(resourceUpdateRequest); + } + + public void handleCsvFileUpdate(List csvVariables, List dbCsv, ApiScenario scenario, String userId) { + ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), userId); + // 设置本地文件相关参数 + setCsvLocalFileParam(csvVariables, dbCsv, resourceUpdateRequest); + // 设置关联文件相关参数 + setCsvLinkFileParam(csvVariables, dbCsv, resourceUpdateRequest); + apiFileResourceService.updateFileResource(resourceUpdateRequest); + } + + public ApiFileResourceUpdateRequest getApiFileResourceUpdateRequest(String sourceId, String projectId, String operator) { + String apiScenarioDir = DefaultRepositoryDir.getApiScenarioDir(projectId, sourceId); + ApiFileResourceUpdateRequest resourceUpdateRequest = new ApiFileResourceUpdateRequest(); + resourceUpdateRequest.setProjectId(projectId); + resourceUpdateRequest.setFolder(apiScenarioDir); + resourceUpdateRequest.setResourceId(sourceId); + resourceUpdateRequest.setApiResourceType(ApiFileResourceType.API_SCENARIO); + resourceUpdateRequest.setOperator(operator); + resourceUpdateRequest.setLogModule(OperationLogModule.API_SCENARIO_MANAGEMENT_SCENARIO); + resourceUpdateRequest.setFileAssociationSourceType(FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO); + return resourceUpdateRequest; + } + + public void setCsvLocalFileParam(List csvVariables, List dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) { + // 获取数据库中的本地文件 + List dbLocalFileIds = dbCsv.stream() + .filter(c -> BooleanUtils.isFalse(c.getAssociation())) + .map(ApiScenarioCsv::getFileId) + .toList(); + + // 获取请求中的本地文件 + List localFileIds = csvVariables.stream() + .map(CsvVariable::getFile) + .filter(c -> BooleanUtils.isTrue(c.getLocal())) + .map(ApiFile::getFileId).toList(); + + // 待删除文件 + List deleteLocals = ListUtils.subtract(dbLocalFileIds, localFileIds); + resourceUpdateRequest.setDeleteFileIds(deleteLocals); + // 新上传文件 + List addLocal = ListUtils.subtract(localFileIds, dbLocalFileIds); + resourceUpdateRequest.setUploadFileIds(addLocal); + } + + public List getApiScenarioCsv(String scenarioId) { + ApiScenarioCsvExample apiScenarioCsvExample = new ApiScenarioCsvExample(); + apiScenarioCsvExample.createCriteria().andScenarioIdEqualTo(scenarioId); + return apiScenarioCsvMapper.selectByExample(apiScenarioCsvExample); + } + + public void deleteCsvResource(List deleteCsvIds) { + if (CollectionUtils.isNotEmpty(deleteCsvIds)) { + ApiScenarioCsvExample example = new ApiScenarioCsvExample(); + example.createCriteria().andIdIn(deleteCsvIds); + apiScenarioCsvMapper.deleteByExample(example); + + ApiScenarioCsvStepExample stepExample = new ApiScenarioCsvStepExample(); + stepExample.createCriteria().andIdIn(deleteCsvIds); + apiScenarioCsvStepMapper.deleteByExample(stepExample); + } + } + + public void setCsvLinkFileParam(List csvVariables, List dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) { + // 获取数据库中关联的文件id + List dbRefFileIds = dbCsv.stream() + .filter(c -> BooleanUtils.isTrue(c.getAssociation()) && StringUtils.isNotBlank(c.getFileId())) + .map(ApiScenarioCsv::getFileId) + .toList(); + + // 获取请求中关联的文件id + List refFileIds = csvVariables.stream() + .map(CsvVariable::getFile) + .filter(c -> BooleanUtils.isFalse(c.getLocal()) && StringUtils.isNotBlank(c.getFileId())) + .map(ApiFile::getFileId).toList(); + + List unlinkFileIds = ListUtils.subtract(dbRefFileIds, refFileIds); + resourceUpdateRequest.setUnLinkFileIds(unlinkFileIds); + List linkFileIds = ListUtils.subtract(refFileIds, dbRefFileIds); + resourceUpdateRequest.setLinkFileIds(linkFileIds); + } + + public void handleCsvUpdate(ScenarioConfig scenarioConfig, ApiScenario scenario, String userId) { + if (scenarioConfig == null) { + return; + } + + List csvVariables = getCsvVariables(scenarioConfig); + List dbCsv = getApiScenarioCsv(scenario.getId()); + List dbCsvIds = dbCsv.stream() + .map(ApiScenarioCsv::getId) + .toList(); + + handleRefUpgradeFile(csvVariables, dbCsv); + + // 更新 csv 相关数据表 + handleCsvDataUpdate(csvVariables, scenario, dbCsvIds); + + // 处理文件的上传和删除 + handleCsvFileUpdate(csvVariables, dbCsv, scenario, userId); + } + + public List getCsvVariables(ScenarioConfig scenarioConfig) { + if (scenarioConfig == null || scenarioConfig.getVariable() == null || scenarioConfig.getVariable().getCsvVariables() == null) { + return List.of(); + } + return scenarioConfig.getVariable().getCsvVariables(); + } + + public void handleCsvDataUpdate(List csvVariables, ApiScenario scenario, List dbCsvIds) { + + List csvIds = csvVariables.stream() + .map(CsvVariable::getId) + .toList(); + + List deleteCsvIds = ListUtils.subtract(dbCsvIds, csvIds); + + //删除不存在的数据 + deleteCsvResource(deleteCsvIds); + + Set dbCsvIdSet = dbCsvIds.stream().collect(Collectors.toSet()); + + List addCsvList = new ArrayList<>(); + csvVariables.stream().forEach(item -> { + ApiScenarioCsv scenarioCsv = new ApiScenarioCsv(); + BeanUtils.copyBean(scenarioCsv, item); + scenarioCsv.setScenarioId(scenario.getId()); + scenarioCsv.setProjectId(scenario.getProjectId()); + + ApiFile file = item.getFile(); + scenarioCsv.setFileId(file.getFileId()); + scenarioCsv.setFileName(file.getFileName()); + scenarioCsv.setAssociation(BooleanUtils.isFalse(file.getLocal())); + if (!dbCsvIdSet.contains(item.getId())) { + addCsvList.add(scenarioCsv); + } else { + apiScenarioCsvMapper.updateByPrimaryKey(scenarioCsv); + } + }); + + if (CollectionUtils.isNotEmpty(addCsvList)) { + apiScenarioCsvMapper.batchInsert(addCsvList); + } + } + + public List getCsvVariables(String scenarioId) { + ApiScenarioCsvExample example = new ApiScenarioCsvExample(); + example.createCriteria().andScenarioIdEqualTo(scenarioId); + List csvList = apiScenarioCsvMapper.selectByExample(example); + List csvVariables = csvList.stream().map(apiScenarioCsv -> { + CsvVariable csvVariable = BeanUtils.copyBean(new CsvVariable(), apiScenarioCsv); + ApiFile apiFile = new ApiFile(); + apiFile.setFileId(apiScenarioCsv.getFileId()); + apiFile.setLocal(!apiScenarioCsv.getAssociation()); + apiFile.setFileName(apiScenarioCsv.getFileName()); + csvVariable.setFile(apiFile); + return csvVariable; + }).collect(Collectors.toList()); + return csvVariables; + } + + /** + * 当文件管理更新了关联资源的 csv 文件版本时 + * 前端文件并未更新,这里使用时,进行对比,使用较新的文件版本 + * + * @param csvVariables + * @param dbCsv + */ + public void handleRefUpgradeFile(List csvVariables, List dbCsv) { + try { + // 获取数据库中关联的 csv 文件 + List dbAssociationCsvList = dbCsv.stream().filter(ApiScenarioCsv::getAssociation).toList(); + Map dbAssociationCsvIdMap = dbAssociationCsvList.stream() + .collect(Collectors.toMap(ApiScenarioCsv::getId, Function.identity())); + + // 获取与数据库中数据 fileId 不一致的 csv + List changeAssociationCsvList = csvVariables.stream().filter(csvVariable -> { + ApiScenarioCsv apiScenarioCsv = dbAssociationCsvIdMap.get(csvVariable.getId()); + if (apiScenarioCsv != null && csvVariable.getFile() != null && StringUtils.isNotBlank(csvVariable.getFile().getFileId()) + && !StringUtils.equals(apiScenarioCsv.getFileId(), csvVariable.getFile().getFileId())) { + return true; + } + return false; + }).toList(); + + if (CollectionUtils.isEmpty(changeAssociationCsvList)) { + return; + } + + // 查询关联的csv文件信息 + List dbAssociationCsvFileIds = changeAssociationCsvList.stream().map(csvVariable -> csvVariable.getFile().getFileId()).toList(); + FileMetadataExample fileMetadataExample = new FileMetadataExample(); + fileMetadataExample.createCriteria().andIdIn(dbAssociationCsvFileIds); + List dbAssociationCsvFiles = fileMetadataMapper.selectByExample(fileMetadataExample); + Map dbAssociationCsvFileMap = dbAssociationCsvFiles.stream() + .collect(Collectors.toMap(FileMetadata::getId, Function.identity())); + + // 查询csv文件的版本信息 + List refIds = dbAssociationCsvFiles.stream().map(FileMetadata::getRefId).toList(); + FileMetadataExample example = new FileMetadataExample(); + example.createCriteria().andRefIdIn(refIds); + List fileMetadataList = fileMetadataMapper.selectByExample(example) + .stream() + .sorted(Comparator.comparing(FileMetadata::getUpdateTime).reversed()) + .collect(Collectors.toList()); + Map> refFileMap = fileMetadataList.stream().collect(Collectors.groupingBy(FileMetadata::getRefId)); + + for (CsvVariable changeAssociation : changeAssociationCsvList) { + String fileId = changeAssociation.getFile().getFileId(); + FileMetadata fileMetadata = dbAssociationCsvFileMap.get(fileId); + ApiScenarioCsv dbAssociationCsv = dbAssociationCsvIdMap.get(changeAssociation.getId()); + // 遍历同一文件的不同版本 + List refFileList = refFileMap.get(fileMetadata.getRefId()); + if (refFileList != null) { + for (FileMetadata refFile : refFileList) { + if (StringUtils.equals(refFile.getId(), fileId)) { + // 如果前端参数的版本较新,则不处理 + break; + } else if (StringUtils.equals(refFile.getId(), dbAssociationCsv.getFileId())) { + // 如果数据库中的文件版本较新,则说明文件管理中更新了当前引用的文件版本,使用数据库中的文件信息 + changeAssociation.getFile().setFileId(dbAssociationCsv.getFileId()); + changeAssociation.getFile().setFileName(dbAssociationCsv.getFileName()); + break; + } + } + } + } + } catch (Exception e) { + LogUtils.error(e); + } + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioRunService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioRunService.java index 2591e64edf..0b2b7b3ff3 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioRunService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioRunService.java @@ -7,9 +7,6 @@ import io.metersphere.api.controller.result.ApiResultCode; import io.metersphere.api.domain.*; import io.metersphere.api.dto.*; import io.metersphere.api.dto.debug.ApiResourceRunRequest; -import io.metersphere.api.dto.definition.ApiModuleDTO; -import io.metersphere.api.dto.definition.EnvApiModuleRequest; -import io.metersphere.api.dto.definition.EnvApiTreeDTO; import io.metersphere.api.dto.request.MsScenario; import io.metersphere.api.dto.request.controller.MsScriptElement; import io.metersphere.api.dto.request.http.MsHTTPElement; @@ -22,7 +19,6 @@ import io.metersphere.api.parser.step.StepParser; import io.metersphere.api.parser.step.StepParserFactory; import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiExecuteService; -import io.metersphere.api.service.definition.ApiDefinitionModuleService; import io.metersphere.api.service.definition.ApiDefinitionService; import io.metersphere.api.service.definition.ApiTestCaseService; import io.metersphere.api.service.queue.ApiExecutionSetService; @@ -31,9 +27,6 @@ import io.metersphere.project.api.processor.MsProcessor; import io.metersphere.project.api.processor.TimeWaitingProcessor; import io.metersphere.project.domain.Project; import io.metersphere.project.dto.environment.EnvironmentInfoDTO; -import io.metersphere.project.dto.environment.http.HttpConfig; -import io.metersphere.project.dto.environment.http.HttpConfigModuleMatchRule; -import io.metersphere.project.dto.environment.http.SelectModule; import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.service.EnvironmentGroupService; import io.metersphere.project.service.EnvironmentService; @@ -70,6 +63,8 @@ public class ApiScenarioRunService { @Resource private ApiScenarioService apiScenarioService; @Resource + private ApiScenarioFileService apiScenarioFileService; + @Resource private ApiExecuteService apiExecuteService; @Resource private ApiDefinitionService apiDefinitionService; @@ -82,8 +77,6 @@ public class ApiScenarioRunService { @Resource private ApiPluginService apiPluginService; @Resource - private ApiDefinitionModuleService apiDefinitionModuleService; - @Resource private ApiCommonService apiCommonService; @Resource private ApiScenarioReportService apiScenarioReportService; @@ -120,14 +113,14 @@ public class ApiScenarioRunService { msScenario.setScenarioConfig(getScenarioConfig(request, true)); msScenario.setProjectId(request.getProjectId()); - List dbCsv = apiScenarioService.getApiScenarioCsv(apiScenario.getId()); + List dbCsv = apiScenarioFileService.getApiScenarioCsv(apiScenario.getId()); List csvVariables = apiScenarioService.getCsvVariables(msScenario.getScenarioConfig()); - apiScenarioService.handleRefUpgradeFile(csvVariables, dbCsv); + apiScenarioFileService.handleRefUpgradeFile(csvVariables, dbCsv); // 处理特殊的步骤详情 ApiScenarioCopyStepMap apiScenarioCopyStepMap = apiScenarioService.addSpecialStepDetails(request.getSteps(), request.getStepDetails()); // 处理copy的步骤文件 - apiScenarioService.handleRunCopyStepFiles(request, apiScenarioCopyStepMap, request.getStepDetails()); + apiScenarioFileService.handleRunCopyStepFiles(request, apiScenarioCopyStepMap, request.getStepDetails()); ApiResourceRunRequest runRequest = new ApiResourceRunRequest(); runRequest = setFileParam(request, runRequest); @@ -416,15 +409,15 @@ public class ApiScenarioRunService { msScenario.setResourceId(request.getId()); if (hasSave) { - List dbCsv = apiScenarioService.getApiScenarioCsv(apiScenario.getId()); + List dbCsv = apiScenarioFileService.getApiScenarioCsv(apiScenario.getId()); List csvVariables = apiScenarioService.getCsvVariables(msScenario.getScenarioConfig()); - apiScenarioService.handleRefUpgradeFile(csvVariables, dbCsv); + apiScenarioFileService.handleRefUpgradeFile(csvVariables, dbCsv); } // 处理特殊的步骤详情 ApiScenarioCopyStepMap apiScenarioCopyStepMap = apiScenarioService.addSpecialStepDetails(request.getSteps(), request.getStepDetails()); // 处理copy的步骤文件 - apiScenarioService.handleRunCopyStepFiles(request, apiScenarioCopyStepMap, request.getStepDetails()); + apiScenarioFileService.handleRunCopyStepFiles(request, apiScenarioCopyStepMap, request.getStepDetails()); ApiScenarioParseTmpParam tmpParam = parse(msScenario, request.getSteps(), request); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java index 787d98376a..e057111b7d 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java @@ -5,8 +5,10 @@ import io.metersphere.api.constants.ApiScenarioStepRefType; import io.metersphere.api.constants.ApiScenarioStepType; import io.metersphere.api.controller.result.ApiResultCode; import io.metersphere.api.domain.*; -import io.metersphere.api.dto.*; -import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest; +import io.metersphere.api.dto.ApiExecResultDTO; +import io.metersphere.api.dto.ApiFile; +import io.metersphere.api.dto.ReferenceDTO; +import io.metersphere.api.dto.ReferenceRequest; import io.metersphere.api.dto.definition.ExecutePageRequest; import io.metersphere.api.dto.definition.ExecuteReportDTO; import io.metersphere.api.dto.export.MetersphereApiScenarioExportResponse; @@ -31,10 +33,12 @@ import io.metersphere.functional.domain.FunctionalCaseTestExample; import io.metersphere.functional.mapper.FunctionalCaseTestMapper; import io.metersphere.plugin.api.spi.AbstractMsProtocolTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement; -import io.metersphere.project.domain.*; +import io.metersphere.project.domain.FileAssociation; +import io.metersphere.project.domain.FileMetadata; +import io.metersphere.project.domain.Project; +import io.metersphere.project.domain.ProjectExample; import io.metersphere.project.dto.MoveNodeSortDTO; import io.metersphere.project.mapper.ExtBaseProjectVersionMapper; -import io.metersphere.project.mapper.FileMetadataMapper; import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.service.MoveNodeService; import io.metersphere.sdk.constants.*; @@ -141,8 +145,6 @@ public class ApiScenarioService extends MoveNodeService { @Resource private ApiScenarioCsvMapper apiScenarioCsvMapper; @Resource - private FileMetadataMapper fileMetadataMapper; - @Resource private ApiScenarioCsvStepMapper apiScenarioCsvStepMapper; @Resource private ScheduleService scheduleService; @@ -170,12 +172,13 @@ public class ApiScenarioService extends MoveNodeService { private ExtApiScenarioReportMapper extApiScenarioReportMapper; @Resource private FunctionalCaseTestMapper functionalCaseTestMapper; + @Resource + private ApiScenarioFileService apiScenarioFileService; public static final String PRIORITY = "Priority"; public static final String STATUS = "Status"; public static final String TAGS = "Tags"; public static final String ENVIRONMENT = "Environment"; - public static final String SCHEDULE = "Schedule"; private static final String SCENARIO_TABLE = "api_scenario"; private static final String SCENARIO = "SCENARIO"; @@ -403,10 +406,10 @@ public class ApiScenarioService extends MoveNodeService { handCsvFilesAdd(request, creator, scenario); // 处理添加的步骤 - handleStepAdd(request, scenario); + handleStepAdd(request, scenario, creator); // 处理步骤文件 - handleStepFilesAdd(request, creator, scenario); + apiScenarioFileService.handleStepFilesAdd(request, creator, scenario); return scenario; } @@ -457,7 +460,7 @@ public class ApiScenarioService extends MoveNodeService { } } - private void handleStepAdd(ApiScenarioAddRequest request, ApiScenario scenario) { + private void handleStepAdd(ApiScenarioAddRequest request, ApiScenario scenario, String userId) { // 插入步骤 if (CollectionUtils.isNotEmpty(request.getSteps())) { checkCircularRef(scenario.getId(), request.getSteps()); @@ -467,16 +470,17 @@ public class ApiScenarioService extends MoveNodeService { steps.forEach(step -> step.setScenarioId(scenario.getId())); // 处理特殊的步骤详情 ApiScenarioCopyStepMap apiScenarioCopyStepMap = addSpecialStepDetails(request.getSteps(), request.getStepDetails()); - // 处理copy的步骤文件 - handleSaveCopyStepFiles(apiScenarioCopyStepMap, request.getStepDetails(), scenario); - - List apiScenarioStepBlobs = getUpdateStepBlobs(steps, request.getStepDetails()); - apiScenarioStepBlobs.forEach(step -> step.setScenarioId(scenario.getId())); if (CollectionUtils.isNotEmpty(steps)) { apiScenarioStepMapper.batchInsert(steps); } + // 处理copy的步骤文件 + apiScenarioFileService.handleSaveCopyStepFiles(apiScenarioCopyStepMap, request.getStepDetails(), scenario, userId); + + List apiScenarioStepBlobs = getUpdateStepBlobs(steps, request.getStepDetails()); + apiScenarioStepBlobs.forEach(step -> step.setScenarioId(scenario.getId())); + if (CollectionUtils.isNotEmpty(apiScenarioStepBlobs)) { apiScenarioStepBlobMapper.batchInsert(apiScenarioStepBlobs); } @@ -509,10 +513,10 @@ public class ApiScenarioService extends MoveNodeService { } // 处理 csv 相关数据表 - handleCsvDataUpdate(csvVariables, scenario, List.of()); + apiScenarioFileService.handleCsvDataUpdate(csvVariables, scenario, List.of()); // 处理文件的上传 - handleCsvFileAdd(csvVariables, List.of(), scenario, creator); + apiScenarioFileService.handleCsvFileAdd(csvVariables, List.of(), scenario, creator); } public List getCsvVariables(ScenarioConfig scenarioConfig) { @@ -522,38 +526,6 @@ public class ApiScenarioService extends MoveNodeService { return scenarioConfig.getVariable().getCsvVariables(); } - private void handleStepFilesAdd(ApiScenarioAddRequest request, String creator, ApiScenario scenario) { - Map stepFileParam = request.getStepFileParam(); - if (MapUtils.isNotEmpty(stepFileParam)) { - stepFileParam.forEach((stepId, fileParam) -> { - // 处理步骤文件 - ApiFileResourceUpdateRequest resourceUpdateRequest = getStepApiFileResourceUpdateRequest(creator, scenario, stepId, fileParam); - apiFileResourceService.addFileResource(resourceUpdateRequest); - }); - } - } - - private void handleStepFilesUpdate(ApiScenarioUpdateRequest request, String updater, ApiScenario scenario) { - Map stepFileParam = request.getStepFileParam(); - if (MapUtils.isNotEmpty(stepFileParam)) { - stepFileParam.forEach((stepId, fileParam) -> { - // 处理步骤文件 - ApiFileResourceUpdateRequest resourceUpdateRequest = getStepApiFileResourceUpdateRequest(updater, scenario, stepId, fileParam); - apiFileResourceService.updateFileResource(resourceUpdateRequest); - }); - } - } - - private ApiFileResourceUpdateRequest getStepApiFileResourceUpdateRequest(String userId, ApiScenario scenario, String stepId, ResourceAddFileParam fileParam) { - ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(stepId, scenario.getProjectId(), userId); - String apiScenarioStepDir = DefaultRepositoryDir.getApiScenarioStepDir(scenario.getProjectId(), scenario.getId(), stepId); - resourceUpdateRequest.setFileAssociationSourceType(FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO_STEP); - resourceUpdateRequest.setApiResourceType(ApiFileResourceType.API_SCENARIO_STEP); - resourceUpdateRequest.setFolder(apiScenarioStepDir); - resourceUpdateRequest = BeanUtils.copyBean(resourceUpdateRequest, fileParam); - return resourceUpdateRequest; - } - private void saveStepCsv(String scenarioId, List csvSteps) { // 先删除 ApiScenarioCsvStepExample csvStepExample = new ApiScenarioCsvStepExample(); @@ -566,209 +538,6 @@ public class ApiScenarioService extends MoveNodeService { } } - public void handleCsvUpdate(ScenarioConfig scenarioConfig, ApiScenario scenario, String userId) { - if (scenarioConfig == null) { - return; - } - - List csvVariables = getCsvVariables(scenarioConfig); - List dbCsv = getApiScenarioCsv(scenario.getId()); - List dbCsvIds = dbCsv.stream() - .map(ApiScenarioCsv::getId) - .toList(); - - handleRefUpgradeFile(csvVariables, dbCsv); - - // 更新 csv 相关数据表 - handleCsvDataUpdate(csvVariables, scenario, dbCsvIds); - - // 处理文件的上传和删除 - handleCsvFileUpdate(csvVariables, dbCsv, scenario, userId); - } - - /** - * 当文件管理更新了关联资源的 csv 文件版本时 - * 前端文件并未更新,这里使用时,进行对比,使用较新的文件版本 - * - * @param csvVariables - * @param dbCsv - */ - public void handleRefUpgradeFile(List csvVariables, List dbCsv) { - try { - // 获取数据库中关联的 csv 文件 - List dbAssociationCsvList = dbCsv.stream().filter(ApiScenarioCsv::getAssociation).toList(); - Map dbAssociationCsvIdMap = dbAssociationCsvList.stream() - .collect(Collectors.toMap(ApiScenarioCsv::getId, Function.identity())); - - // 获取与数据库中数据 fileId 不一致的 csv - List changeAssociationCsvList = csvVariables.stream().filter(csvVariable -> { - ApiScenarioCsv apiScenarioCsv = dbAssociationCsvIdMap.get(csvVariable.getId()); - if (apiScenarioCsv != null && csvVariable.getFile() != null && StringUtils.isNotBlank(csvVariable.getFile().getFileId()) - && !StringUtils.equals(apiScenarioCsv.getFileId(), csvVariable.getFile().getFileId())) { - return true; - } - return false; - }).toList(); - - if (CollectionUtils.isEmpty(changeAssociationCsvList)) { - return; - } - - // 查询关联的csv文件信息 - List dbAssociationCsvFileIds = changeAssociationCsvList.stream().map(csvVariable -> csvVariable.getFile().getFileId()).toList(); - FileMetadataExample fileMetadataExample = new FileMetadataExample(); - fileMetadataExample.createCriteria().andIdIn(dbAssociationCsvFileIds); - List dbAssociationCsvFiles = fileMetadataMapper.selectByExample(fileMetadataExample); - Map dbAssociationCsvFileMap = dbAssociationCsvFiles.stream() - .collect(Collectors.toMap(FileMetadata::getId, Function.identity())); - - // 查询csv文件的版本信息 - List refIds = dbAssociationCsvFiles.stream().map(FileMetadata::getRefId).toList(); - FileMetadataExample example = new FileMetadataExample(); - example.createCriteria().andRefIdIn(refIds); - List fileMetadataList = fileMetadataMapper.selectByExample(example) - .stream() - .sorted(Comparator.comparing(FileMetadata::getUpdateTime).reversed()) - .collect(Collectors.toList()); - Map> refFileMap = fileMetadataList.stream().collect(Collectors.groupingBy(FileMetadata::getRefId)); - - for (CsvVariable changeAssociation : changeAssociationCsvList) { - String fileId = changeAssociation.getFile().getFileId(); - FileMetadata fileMetadata = dbAssociationCsvFileMap.get(fileId); - ApiScenarioCsv dbAssociationCsv = dbAssociationCsvIdMap.get(changeAssociation.getId()); - // 遍历同一文件的不同版本 - List refFileList = refFileMap.get(fileMetadata.getRefId()); - if (refFileList != null) { - for (FileMetadata refFile : refFileList) { - if (StringUtils.equals(refFile.getId(), fileId)) { - // 如果前端参数的版本较新,则不处理 - break; - } else if (StringUtils.equals(refFile.getId(), dbAssociationCsv.getFileId())) { - // 如果数据库中的文件版本较新,则说明文件管理中更新了当前引用的文件版本,使用数据库中的文件信息 - changeAssociation.getFile().setFileId(dbAssociationCsv.getFileId()); - changeAssociation.getFile().setFileName(dbAssociationCsv.getFileName()); - break; - } - } - } - } - } catch (Exception e) { - LogUtils.error(e); - } - } - - private void handleCsvDataUpdate(List csvVariables, ApiScenario scenario, List dbCsvIds) { - - List csvIds = csvVariables.stream() - .map(CsvVariable::getId) - .toList(); - - List deleteCsvIds = ListUtils.subtract(dbCsvIds, csvIds); - - //删除不存在的数据 - deleteCsvResource(deleteCsvIds); - - Set dbCsvIdSet = dbCsvIds.stream().collect(Collectors.toSet()); - - List addCsvList = new ArrayList<>(); - csvVariables.stream().forEach(item -> { - ApiScenarioCsv scenarioCsv = new ApiScenarioCsv(); - BeanUtils.copyBean(scenarioCsv, item); - scenarioCsv.setScenarioId(scenario.getId()); - scenarioCsv.setProjectId(scenario.getProjectId()); - - ApiFile file = item.getFile(); - scenarioCsv.setFileId(file.getFileId()); - scenarioCsv.setFileName(file.getFileName()); - scenarioCsv.setAssociation(BooleanUtils.isFalse(file.getLocal())); - if (!dbCsvIdSet.contains(item.getId())) { - addCsvList.add(scenarioCsv); - } else { - apiScenarioCsvMapper.updateByPrimaryKey(scenarioCsv); - } - }); - - if (CollectionUtils.isNotEmpty(addCsvList)) { - apiScenarioCsvMapper.batchInsert(addCsvList); - } - } - - public void handleCsvFileAdd(List csvVariables, List dbCsv, ApiScenario scenario, String userId) { - ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), userId); - // 设置本地文件相关参数 - setCsvLocalFileParam(csvVariables, dbCsv, resourceUpdateRequest); - // 设置关联文件相关参数 - setCsvLinkFileParam(csvVariables, dbCsv, resourceUpdateRequest); - apiFileResourceService.addFileResource(resourceUpdateRequest); - } - - private void handleCsvFileUpdate(List csvVariables, List dbCsv, ApiScenario scenario, String userId) { - ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), userId); - // 设置本地文件相关参数 - setCsvLocalFileParam(csvVariables, dbCsv, resourceUpdateRequest); - // 设置关联文件相关参数 - setCsvLinkFileParam(csvVariables, dbCsv, resourceUpdateRequest); - apiFileResourceService.updateFileResource(resourceUpdateRequest); - } - - public void setCsvLinkFileParam(List csvVariables, List dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) { - // 获取数据库中关联的文件id - List dbRefFileIds = dbCsv.stream() - .filter(c -> BooleanUtils.isTrue(c.getAssociation()) && StringUtils.isNotBlank(c.getFileId())) - .map(ApiScenarioCsv::getFileId) - .toList(); - - // 获取请求中关联的文件id - List refFileIds = csvVariables.stream() - .map(CsvVariable::getFile) - .filter(c -> BooleanUtils.isFalse(c.getLocal()) && StringUtils.isNotBlank(c.getFileId())) - .map(ApiFile::getFileId).toList(); - - List unlinkFileIds = ListUtils.subtract(dbRefFileIds, refFileIds); - resourceUpdateRequest.setUnLinkFileIds(unlinkFileIds); - List linkFileIds = ListUtils.subtract(refFileIds, dbRefFileIds); - resourceUpdateRequest.setLinkFileIds(linkFileIds); - } - - public void setCsvLocalFileParam(List csvVariables, List dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) { - // 获取数据库中的本地文件 - List dbLocalFileIds = dbCsv.stream() - .filter(c -> BooleanUtils.isFalse(c.getAssociation())) - .map(ApiScenarioCsv::getFileId) - .toList(); - - // 获取请求中的本地文件 - List localFileIds = csvVariables.stream() - .map(CsvVariable::getFile) - .filter(c -> BooleanUtils.isTrue(c.getLocal())) - .map(ApiFile::getFileId).toList(); - - // 待删除文件 - List deleteLocals = ListUtils.subtract(dbLocalFileIds, localFileIds); - resourceUpdateRequest.setDeleteFileIds(deleteLocals); - // 新上传文件 - List addLocal = ListUtils.subtract(localFileIds, dbLocalFileIds); - resourceUpdateRequest.setUploadFileIds(addLocal); - } - - public List getApiScenarioCsv(String scenarioId) { - ApiScenarioCsvExample apiScenarioCsvExample = new ApiScenarioCsvExample(); - apiScenarioCsvExample.createCriteria().andScenarioIdEqualTo(scenarioId); - return apiScenarioCsvMapper.selectByExample(apiScenarioCsvExample); - } - - private void deleteCsvResource(List deleteCsvIds) { - if (CollectionUtils.isNotEmpty(deleteCsvIds)) { - ApiScenarioCsvExample example = new ApiScenarioCsvExample(); - example.createCriteria().andIdIn(deleteCsvIds); - apiScenarioCsvMapper.deleteByExample(example); - - ApiScenarioCsvStepExample stepExample = new ApiScenarioCsvStepExample(); - stepExample.createCriteria().andIdIn(deleteCsvIds); - apiScenarioCsvStepMapper.deleteByExample(stepExample); - } - } - private ApiScenario getAddApiScenario(ApiScenarioAddRequest request, String creator) { ApiScenario scenario = new ApiScenario(); BeanUtils.copyBean(scenario, request); @@ -814,10 +583,10 @@ public class ApiScenarioService extends MoveNodeService { updateApiScenarioStep(request, originScenario, updater); // 处理步骤文件 - handleStepFilesUpdate(request, updater, originScenario); + apiScenarioFileService.handleStepFilesUpdate(request, updater, originScenario); // 处理 csv 文件 - handleCsvUpdate(request.getScenarioConfig(), scenario, updater); + apiScenarioFileService.handleCsvUpdate(request.getScenarioConfig(), scenario, updater); return scenario; } @@ -848,8 +617,6 @@ public class ApiScenarioService extends MoveNodeService { // 获取待更新的步骤详情 ApiScenarioCopyStepMap apiScenarioCopyStepMap = addSpecialStepDetails(steps, request.getStepDetails()); - // 处理copy的步骤文件 - handleSaveCopyStepFiles(apiScenarioCopyStepMap, request.getStepDetails(), scenario); List updateStepBlobs = getUpdateStepBlobs(apiScenarioSteps, request.getStepDetails()); updateStepBlobs.forEach(step -> step.setScenarioId(scenario.getId())); @@ -862,6 +629,9 @@ public class ApiScenarioService extends MoveNodeService { deleteStepByScenarioId(scenario.getId()); apiScenarioStepMapper.batchInsert(apiScenarioSteps); + // 处理copy的步骤文件 + apiScenarioFileService.handleSaveCopyStepFiles(apiScenarioCopyStepMap, request.getStepDetails(), scenario, userId); + // 详情表-删除已经删除的步骤详情 SubListUtils.dealForSubList(deleteStepIds, 100, subIds -> { ApiScenarioStepBlobExample stepBlobExample = new ApiScenarioStepBlobExample(); @@ -1120,281 +890,6 @@ public class ApiScenarioService extends MoveNodeService { return apiScenarioCopyStepMap; } - /** - * 处理保存时,copy的步骤中文件 - * @param apiScenarioCopyStepMap - * @param stepDetails - */ - public void handleSaveCopyStepFiles(ApiScenarioCopyStepMap apiScenarioCopyStepMap, Map stepDetails, ApiScenario scenario) { - try { - List apiFileResources = new ArrayList<>(); - apiFileResources.addAll(handleCopyFromStepFiles(stepDetails, apiScenarioCopyStepMap.getCopyFromStepIdMap(), scenario)); - apiFileResources.addAll(handleCopyApiFiles(stepDetails, apiScenarioCopyStepMap.getIsNewApiResourceMap(), scenario)); - apiFileResources.addAll(handleCopyApiCaseFiles(stepDetails, apiScenarioCopyStepMap.getIsNewApiCaseResourceMap(), scenario)); - if (CollectionUtils.isNotEmpty(apiFileResources)) { - // 插入步骤和文件的关联关系 - apiFileResources.forEach(apiFileResource -> apiFileResource.setProjectId(scenario.getProjectId())); - apiFileResourceMapper.batchInsert(apiFileResources); - } - } catch (Exception e) { - LogUtils.error(e); - } - } - - /** - * 处理调试执行时,copy的步骤中文件 - * @param debugRequest - * @param apiScenarioCopyStepMap - * @param stepDetails - */ - public void handleRunCopyStepFiles(ApiScenarioDebugRequest debugRequest, ApiScenarioCopyStepMap apiScenarioCopyStepMap, Map stepDetails) { - try { - List apiFileResources = new ArrayList<>(); - apiFileResources.addAll(handleCopyFromStepFiles(stepDetails, apiScenarioCopyStepMap.getCopyFromStepIdMap(), null)); - apiFileResources.addAll(handleCopyApiFiles(stepDetails, apiScenarioCopyStepMap.getIsNewApiResourceMap(), null)); - apiFileResources.addAll(handleCopyApiCaseFiles(stepDetails, apiScenarioCopyStepMap.getIsNewApiCaseResourceMap(), null)); - - if (debugRequest.getStepFileParam() == null) { - debugRequest.setStepFileParam(new HashMap<>()); - } - // 将copy的步骤中的文件设置为新上传的临时文件,执行时从临时目录获取 - Map stepFileParam = debugRequest.getStepFileParam(); - for (ApiFileResource apiFileResource : apiFileResources) { - stepFileParam.putIfAbsent(apiFileResource.getResourceId(), new ResourceAddFileParam()); - ResourceAddFileParam resourceAddFileParam = stepFileParam.get(apiFileResource.getResourceId()); - if (resourceAddFileParam.getUploadFileIds() == null) { - resourceAddFileParam.setUploadFileIds(new ArrayList<>()); - } - resourceAddFileParam.getUploadFileIds().add(apiFileResource.getFileId()); - } - } catch (Exception e) { - LogUtils.error(e); - } - } - - /** - * 处理 copy 的步骤中的文件 - * 复制文件 - * 创建关联关系 - * 替换文件ID - * @param stepDetails - * @param copyFromStepIdMap - */ - private List handleCopyFromStepFiles(Map stepDetails, Map copyFromStepIdMap, ApiScenario scenario) { - if (copyFromStepIdMap.isEmpty()) { - return List.of(); - } - // 查询 copyFrom 的步骤所关联的文件 - Collection copyFromStepIds = copyFromStepIdMap.values(); - List apiFileResources = apiFileResourceService.selectByResourceIds(copyFromStepIds); - if (apiFileResources.isEmpty()) { - return List.of(); - } - Map> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId)); - - // 查询 copyFrom 步骤的场景ID Map - List hasFileCopyFromStepIds = stepFileMap.keySet().stream().toList(); - Map copyFromStepScenarioMap = getApiScenarioStepByIds(hasFileCopyFromStepIds).stream() - .collect(Collectors.toMap(ApiScenarioStep::getId, ApiScenarioStep::getScenarioId)); - - Map fileIdMap = new HashMap<>(); - List newApiFileResources = new ArrayList<>(); - for (String stepId : copyFromStepIdMap.keySet()) { - List originApiFileResources = stepFileMap.get(copyFromStepIdMap.get(stepId)); - if (CollectionUtils.isEmpty(originApiFileResources)) { - continue; - } - - boolean isSave = scenario != null; - - String newFileId = IDGenerator.nextStr(); - for (ApiFileResource originApiFileResource : originApiFileResources) { - String sourceDir = DefaultRepositoryDir.getApiScenarioStepDir(originApiFileResource.getProjectId(), - copyFromStepScenarioMap.get(originApiFileResource.getResourceId()), originApiFileResource.getResourceId()); - - // 如果是保存,则copy到正式目录,如果是执行,则copy到临时目录 - String targetDir = isSave ? DefaultRepositoryDir.getApiScenarioStepDir(scenario.getProjectId(), scenario.getId(), stepId) - : DefaultRepositoryDir.getSystemTempDir(); - - // 复制文件 - apiFileResourceService.copyFile(sourceDir + "/" + originApiFileResource.getFileId(), - targetDir + "/" + newFileId, - originApiFileResource.getFileName()); - - // 记录步骤和文件信息 - ApiFileResource newApiFileResource = getStepApiFileResource(stepId, newFileId, originApiFileResource.getFileName()); - newApiFileResources.add(newApiFileResource); - - // 记录文件ID映射 - fileIdMap.put(originApiFileResource.getFileId(), newFileId); - } - - // 替换详情中的文件ID - replaceCopyStepFileId(stepDetails, fileIdMap, stepId); - } - return newApiFileResources; - } - - /** - * 处理复制的接口定义步骤中的文件 - * 复制文件 - * 创建关联关系 - * 替换文件ID - * @param stepDetails - */ - private List handleCopyApiFiles(Map stepDetails, Map copyApiIdMap, ApiScenario scenario) { - if (copyApiIdMap.isEmpty()) { - return List.of(); - } - // 查询 copy 的接口定义所关联的文件 - Collection copyApiIds = copyApiIdMap.values(); - List apiFileResources = apiFileResourceService.selectByResourceIds(copyApiIds); - if (apiFileResources.isEmpty()) { - return List.of(); - } - Map> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId)); - - Map fileIdMap = new HashMap<>(); - List newApiFileResources = new ArrayList<>(); - for (String stepId : copyApiIdMap.keySet()) { - List originApiFileResources = stepFileMap.get(copyApiIdMap.get(stepId)); - if (CollectionUtils.isEmpty(originApiFileResources)) { - continue; - } - - String newFileId = IDGenerator.nextStr(); - for (ApiFileResource originApiFileResource : originApiFileResources) { - String sourceDir = DefaultRepositoryDir.getApiDefinitionDir(originApiFileResource.getProjectId(), originApiFileResource.getResourceId()); - - boolean isSave = scenario != null; - - // 如果是保存,则copy到正式目录,如果是执行,则copy到临时目录 - String targetDir = isSave ? DefaultRepositoryDir.getApiScenarioStepDir(scenario.getProjectId(), scenario.getId(), stepId) - : DefaultRepositoryDir.getSystemTempDir(); - - // 复制文件 - apiFileResourceService.copyFile(sourceDir + "/" + originApiFileResource.getFileId(), - targetDir + "/" + newFileId, - originApiFileResource.getFileName()); - - // 记录步骤和文件信息 - ApiFileResource newApiFileResource = getStepApiFileResource(stepId, newFileId, originApiFileResource.getFileName()); - newApiFileResources.add(newApiFileResource); - - // 记录文件ID映射 - fileIdMap.put(originApiFileResource.getFileId(), newFileId); - } - - // 替换详情中的文件ID - replaceCopyStepFileId(stepDetails, fileIdMap, stepId); - } - - return newApiFileResources; - } - - /** - * 处理复制的接口用例步骤中的文件 - * 复制文件 - * 创建关联关系 - * 替换文件ID - * @param stepDetails - */ - private List handleCopyApiCaseFiles(Map stepDetails, Map copyApiCaseIdMap, ApiScenario scenario) { - if (copyApiCaseIdMap.isEmpty()) { - return List.of(); - } - // 查询 copy 的接口定义所关联的文件 - Collection copyApiIds = copyApiCaseIdMap.values(); - List apiFileResources = apiFileResourceService.selectByResourceIds(copyApiIds); - if (apiFileResources.isEmpty()) { - return List.of(); - } - Map> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId)); - - boolean isSave = scenario != null; - Map fileIdMap = new HashMap<>(); - List newApiFileResources = new ArrayList<>(); - for (String stepId : copyApiCaseIdMap.keySet()) { - List originApiFileResources = stepFileMap.get(copyApiCaseIdMap.get(stepId)); - if (CollectionUtils.isEmpty(originApiFileResources)) { - continue; - } - - String newFileId = IDGenerator.nextStr(); - for (ApiFileResource originApiFileResource : originApiFileResources) { - String sourceDir = DefaultRepositoryDir.getApiCaseDir(originApiFileResource.getProjectId(), originApiFileResource.getResourceId()); - - // 如果是保存,则copy到正式目录,如果是执行,则copy到临时目录 - String targetDir = isSave ? DefaultRepositoryDir.getApiScenarioStepDir(scenario.getProjectId(), scenario.getId(), stepId) - : DefaultRepositoryDir.getSystemTempDir(); - - // 复制文件 - apiFileResourceService.copyFile(sourceDir + "/" + originApiFileResource.getFileId(), - targetDir + "/" + newFileId, - originApiFileResource.getFileName()); - - // 记录步骤和文件信息 - ApiFileResource newApiFileResource = getStepApiFileResource(stepId, newFileId, originApiFileResource.getFileName()); - newApiFileResources.add(newApiFileResource); - - // 记录文件ID映射 - fileIdMap.put(originApiFileResource.getFileId(), newFileId); - } - - // 替换详情中的文件ID - replaceCopyStepFileId(stepDetails, fileIdMap, stepId); - } - - return newApiFileResources; - } - - /** - * 替换复制的步骤中详情的文件ID - * @param stepDetails - * @param fileIdMap - * @param stepId - */ - private void replaceCopyStepFileId(Map stepDetails, Map fileIdMap, String stepId) { - Object stepDetail = stepDetails.get(stepId); - if (stepDetail != null) { - // 替换详情中的文件ID - if (stepDetail instanceof byte[] detailBytes) { - AbstractMsTestElement msTestElement = getAbstractMsTestElement(detailBytes); - for (ApiFile apiFile : apiCommonService.getApiFiles(msTestElement)) { - if (fileIdMap.get(apiFile.getFileId()) != null) { - apiFile.setFileId(fileIdMap.get(apiFile.getFileId())); - } - } - stepDetails.put(stepId, msTestElement); - } else if (stepDetail instanceof AbstractMsTestElement msTestElement) { - for (ApiFile apiFile : apiCommonService.getApiFiles(msTestElement)) { - if (fileIdMap.get(apiFile.getFileId()) != null) { - apiFile.setFileId(fileIdMap.get(apiFile.getFileId())); - } - } - } - } - } - - private ApiFileResource getStepApiFileResource(String stepId, String fileId, String fileName) { - ApiFileResource apiFileResource = new ApiFileResource(); - apiFileResource.setFileId(fileId); - apiFileResource.setResourceId(stepId); - apiFileResource.setResourceType(ApiFileResourceType.API_SCENARIO_STEP.name()); - apiFileResource.setCreateTime(System.currentTimeMillis()); - apiFileResource.setFileName(fileName); - return apiFileResource; - } - - private List getApiScenarioStepByIds(List stepIds) { - if (CollectionUtils.isEmpty(stepIds)) { - List.of(); - } - ApiScenarioStepExample example = new ApiScenarioStepExample(); - example.createCriteria().andIdIn(stepIds); - return apiScenarioStepMapper.selectByExample(example); - } - /** * @param stepDetails * @param copyFromIdMap key 为 stepID,value 为复制的 resourceId 或者 stepId @@ -1476,19 +971,6 @@ public class ApiScenarioService extends MoveNodeService { return partialRefStepDetail; } - public ApiFileResourceUpdateRequest getApiFileResourceUpdateRequest(String sourceId, String projectId, String operator) { - String apiScenarioDir = DefaultRepositoryDir.getApiScenarioDir(projectId, sourceId); - ApiFileResourceUpdateRequest resourceUpdateRequest = new ApiFileResourceUpdateRequest(); - resourceUpdateRequest.setProjectId(projectId); - resourceUpdateRequest.setFolder(apiScenarioDir); - resourceUpdateRequest.setResourceId(sourceId); - resourceUpdateRequest.setApiResourceType(ApiFileResourceType.API_SCENARIO); - resourceUpdateRequest.setOperator(operator); - resourceUpdateRequest.setLogModule(OperationLogModule.API_SCENARIO_MANAGEMENT_SCENARIO); - resourceUpdateRequest.setFileAssociationSourceType(FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO); - return resourceUpdateRequest; - } - public long getNextNum(String projectId) { return NumGenerator.nextNum(projectId, ApplicationNumScope.API_SCENARIO); } @@ -1817,7 +1299,7 @@ public class ApiScenarioService extends MoveNodeService { } //存放csv变量 - apiScenarioDetail.getScenarioConfig().getVariable().setCsvVariables(getCsvVariables(scenarioId)); + apiScenarioDetail.getScenarioConfig().getVariable().setCsvVariables(apiScenarioFileService.getCsvVariables(scenarioId)); // 获取所有步骤 List allSteps = getAllStepsByScenarioIds(List.of(scenarioId)) @@ -1884,7 +1366,7 @@ public class ApiScenarioService extends MoveNodeService { } //存放csv变量 - apiScenarioDetail.getScenarioConfig().getVariable().setCsvVariables(getCsvVariables(apiScenario.getId())); + apiScenarioDetail.getScenarioConfig().getVariable().setCsvVariables(apiScenarioFileService.getCsvVariables(apiScenario.getId())); // 获取所有步骤 List allSteps = getAllStepsByScenarioIds(List.of(apiScenario.getId())) @@ -1949,21 +1431,6 @@ public class ApiScenarioService extends MoveNodeService { } } - private List getCsvVariables(String scenarioId) { - ApiScenarioCsvExample example = new ApiScenarioCsvExample(); - example.createCriteria().andScenarioIdEqualTo(scenarioId); - List csvList = apiScenarioCsvMapper.selectByExample(example); - List csvVariables = csvList.stream().map(apiScenarioCsv -> { - CsvVariable csvVariable = BeanUtils.copyBean(new CsvVariable(), apiScenarioCsv); - ApiFile apiFile = new ApiFile(); - apiFile.setFileId(apiScenarioCsv.getFileId()); - apiFile.setLocal(!apiScenarioCsv.getAssociation()); - apiFile.setFileName(apiScenarioCsv.getFileName()); - csvVariable.setFile(apiFile); - return csvVariable; - }).collect(Collectors.toList()); - return csvVariables; - } /** * 设置部分引用的步骤的启用状态 @@ -2664,7 +2131,6 @@ public class ApiScenarioService extends MoveNodeService { return apiTestCaseService.getExecuteList(request); } - public List operationHistoryList(OperationHistoryRequest request) { return operationHistoryService.listWidthTable(request, SCENARIO_TABLE); } @@ -2677,7 +2143,7 @@ public class ApiScenarioService extends MoveNodeService { return; } - AbstractMsTestElement msTestElement = getAbstractMsTestElement(apiScenarioStepBlob.getContent()); + AbstractMsTestElement msTestElement = apiCommonService.getAbstractMsTestElement(apiScenarioStepBlob.getContent()); if (msTestElement != null && msTestElement instanceof MsHTTPElement msHTTPElement) { List updateFiles = apiCommonService.getApiFilesByFileId(originFileAssociation.getFileId(), msHTTPElement); // 替换文件的Id和name @@ -2690,20 +2156,6 @@ public class ApiScenarioService extends MoveNodeService { } } - private AbstractMsTestElement getAbstractMsTestElement(byte[] msTestElementByte) { - return getAbstractMsTestElement(new String(msTestElementByte)); - } - - private AbstractMsTestElement getAbstractMsTestElement(String msTestElementStr) { - try { - return ApiDataUtils.parseObject(msTestElementStr, AbstractMsTestElement.class); - // 如果插件删除,会转换异常 - } catch (Exception e) { - LogUtils.error(e); - } - return null; - } - public void handleScenarioFileAssociationUpgrade(FileAssociation originFileAssociation, FileMetadata newFileMetadata) { String scenarioId = originFileAssociation.getSourceId(); // 查询步骤详情 @@ -2713,7 +2165,7 @@ public class ApiScenarioService extends MoveNodeService { return; } - List csvVariables = getCsvVariables(scenarioId); + List csvVariables = apiScenarioFileService.getCsvVariables(scenarioId); List updateFiles = new ArrayList<>(csvVariables.size()); for (CsvVariable csvVariable : csvVariables) { ApiFile apiFile = csvVariable.getFile(); @@ -2725,7 +2177,7 @@ public class ApiScenarioService extends MoveNodeService { // 替换文件的Id和name apiCommonService.replaceApiFileInfo(updateFiles, newFileMetadata); List dbCsvIds = csvVariables.stream().map(CsvVariable::getId).toList(); - handleCsvDataUpdate(csvVariables, apiScenarioMapper.selectByPrimaryKey(originFileAssociation.getSourceId()), dbCsvIds); + apiScenarioFileService.handleCsvDataUpdate(csvVariables, apiScenarioMapper.selectByPrimaryKey(originFileAssociation.getSourceId()), dbCsvIds); } } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileAssociationService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileAssociationService.java index 8c9e9fed39..e8b5ea1386 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileAssociationService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileAssociationService.java @@ -255,6 +255,15 @@ public class FileAssociationService { return this.deleteAndSaveLog(example, logRecord); } + public List selectBySourceIds(List sourceIds) { + if (CollectionUtils.isEmpty(sourceIds)) { + return List.of(); + } + FileAssociationExample example = new FileAssociationExample(); + example.createCriteria().andSourceIdIn(sourceIds); + return fileAssociationMapper.selectByExample(example); + } + /** * 取消关联 *