fix(接口测试): 复制的步骤处理关联的文件有误

--story=1016879 --user=陈建星 【Bug转需求】【接口测试】场景-场景步骤复制接口/CASE-接口内存在本地上传的文件-场景内转存-报错 https://www.tapd.cn/55049933/s/1626892
This commit is contained in:
AgAngle 2024-12-09 16:01:04 +08:00 committed by Craftsman
parent cadafca297
commit 06d4d6a79b
7 changed files with 729 additions and 604 deletions

View File

@ -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.Body;
import io.metersphere.api.dto.request.http.body.FormDataBody; import io.metersphere.api.dto.request.http.body.FormDataBody;
import io.metersphere.api.dto.request.http.body.FormDataKV; 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.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.KeyValueParam; import io.metersphere.project.api.KeyValueParam;
import io.metersphere.project.api.assertion.MsScriptAssertion; import io.metersphere.project.api.assertion.MsScriptAssertion;
@ -603,4 +604,18 @@ public class ApiCommonService {
ExecTaskItem execTaskItem = execTaskItems.getFirst(); ExecTaskItem execTaskItem = execTaskItems.getFirst();
return execTaskItem; 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;
}
} }

View File

@ -401,9 +401,9 @@ public class ApiFileResourceService {
return fileId; return fileId;
} }
public List<ApiFileResource> selectByResourceIds(Collection<String> copyFromStepIds) { public List<ApiFileResource> selectByResourceIds(List<String> copyFromStepIds) {
ApiFileResourceExample example = new ApiFileResourceExample(); ApiFileResourceExample example = new ApiFileResourceExample();
example.createCriteria().andResourceIdIn(new ArrayList<>(copyFromStepIds)); example.createCriteria().andResourceIdIn(copyFromStepIds);
return apiFileResourceMapper.selectByExample(example); return apiFileResourceMapper.selectByExample(example);
} }
} }

View File

@ -24,10 +24,7 @@ import io.metersphere.api.service.definition.ApiDefinitionExportService;
import io.metersphere.api.service.definition.ApiDefinitionImportService; import io.metersphere.api.service.definition.ApiDefinitionImportService;
import io.metersphere.api.service.definition.ApiDefinitionModuleService; import io.metersphere.api.service.definition.ApiDefinitionModuleService;
import io.metersphere.api.service.definition.ApiTestCaseService; import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.api.service.scenario.ApiScenarioLogService; import io.metersphere.api.service.scenario.*;
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.utils.ApiDataUtils; import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.api.utils.ApiDefinitionImportUtils; import io.metersphere.api.utils.ApiDefinitionImportUtils;
import io.metersphere.api.utils.ApiScenarioImportUtils; import io.metersphere.api.utils.ApiScenarioImportUtils;
@ -138,6 +135,8 @@ public class ApiScenarioDataTransferService {
@Resource @Resource
private ApiScenarioService apiScenarioService; private ApiScenarioService apiScenarioService;
@Resource @Resource
private ApiScenarioFileService apiScenarioFileService;
@Resource
private ApiFileResourceService apiFileResourceService; private ApiFileResourceService apiFileResourceService;
@Resource @Resource
private SqlSessionFactory sqlSessionFactory; private SqlSessionFactory sqlSessionFactory;
@ -416,7 +415,7 @@ public class ApiScenarioDataTransferService {
} }
// 处理 csv 文件 // 处理 csv 文件
apiScenarioService.handleCsvUpdate(request.getScenarioConfig(), scenario, operator); apiScenarioFileService.handleCsvUpdate(request.getScenarioConfig(), scenario, operator);
}); });
sqlSession.flushStatements(); sqlSession.flushStatements();
@ -627,11 +626,11 @@ public class ApiScenarioDataTransferService {
// 处理 csv 相关数据表 // 处理 csv 相关数据表
this.handleCsvDataUpdate(csvVariables, scenario, List.of(), batchCsvMapper, batchCsvStepMapper); 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( FileAssociationSource source = extFileAssociationMapper.selectNameBySourceTableAndId(
FileAssociationSourceUtil.getQuerySql(ApiFileResourceType.API_SCENARIO.name()), resourceUpdateRequest.getResourceId()); FileAssociationSourceUtil.getQuerySql(ApiFileResourceType.API_SCENARIO.name()), resourceUpdateRequest.getResourceId());

View File

@ -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<String, Object> stepDetails, ApiScenario scenario, String userId) {
try {
// 处理本地上传的文件
List<ApiFileResource> 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<String, String> stepResourceIdMap, ApiScenario scenario, String userId) {
List<String> copyFromStepIds = new ArrayList<>(stepResourceIdMap.values());
List<FileAssociation> fileAssociations = fileAssociationService.selectBySourceIds(copyFromStepIds);
// key 为资源IDvalue 为步骤ID
Map<String, String> 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<String, Object> stepDetails) {
try {
List<ApiFileResource> 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<String, ResourceAddFileParam> 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<String, String> stepResourceIdMap) {
List<String> copyFromStepIds = new ArrayList<>(stepResourceIdMap.values());
List<FileAssociation> fileAssociations = fileAssociationService.selectBySourceIds(copyFromStepIds);
// key 为资源IDvalue 为步骤ID
Map<String, String> 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<ApiFileResource> handleCopyFromStepFiles(Map<String, Object> stepDetails, Map<String, String> copyFromStepIdMap, ApiScenario scenario) {
if (copyFromStepIdMap.isEmpty()) {
return List.of();
}
// 查询 copyFrom 的步骤所关联的文件
List<String> copyFromStepIds = new ArrayList<>(copyFromStepIdMap.values());
List<ApiFileResource> apiFileResources = apiFileResourceService.selectByResourceIds(copyFromStepIds);
if (apiFileResources.isEmpty()) {
return List.of();
}
Map<String, List<ApiFileResource>> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId));
// 查询 copyFrom 步骤的场景ID Map
List<String> hasFileCopyFromStepIds = stepFileMap.keySet().stream().toList();
Map<String, String> copyFromStepScenarioMap = getApiScenarioStepByIds(hasFileCopyFromStepIds).stream()
.collect(Collectors.toMap(ApiScenarioStep::getId, ApiScenarioStep::getScenarioId));
Map<String, String> fileIdMap = new HashMap<>();
List<ApiFileResource> newApiFileResources = new ArrayList<>();
for (String stepId : copyFromStepIdMap.keySet()) {
List<ApiFileResource> 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<ApiFileResource> handleCopyApiFiles(Map<String, Object> stepDetails, Map<String, String> copyApiIdMap, ApiScenario scenario) {
if (copyApiIdMap.isEmpty()) {
return List.of();
}
// 查询 copy 的接口定义所关联的文件
List<String> copyApiIds = new ArrayList<>(copyApiIdMap.values());
List<ApiFileResource> apiFileResources = apiFileResourceService.selectByResourceIds(copyApiIds);
if (apiFileResources.isEmpty()) {
return List.of();
}
Map<String, List<ApiFileResource>> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId));
Map<String, String> fileIdMap = new HashMap<>();
List<ApiFileResource> newApiFileResources = new ArrayList<>();
for (String stepId : copyApiIdMap.keySet()) {
List<ApiFileResource> 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<ApiFileResource> handleCopyApiCaseFiles(Map<String, Object> stepDetails, Map<String, String> copyApiCaseIdMap, ApiScenario scenario) {
if (copyApiCaseIdMap.isEmpty()) {
return List.of();
}
// 查询 copy 的接口定义所关联的文件
List<String> copyApiIds = new ArrayList<>(copyApiCaseIdMap.values());
List<ApiFileResource> apiFileResources = apiFileResourceService.selectByResourceIds(copyApiIds);
if (apiFileResources.isEmpty()) {
return List.of();
}
Map<String, List<ApiFileResource>> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId));
boolean isSave = scenario != null;
Map<String, String> fileIdMap = new HashMap<>();
List<ApiFileResource> newApiFileResources = new ArrayList<>();
for (String stepId : copyApiCaseIdMap.keySet()) {
List<ApiFileResource> 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<String, Object> stepDetails, Map<String, String> 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<ApiScenarioStep> getApiScenarioStepByIds(List<String> 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<String, ResourceUpdateFileParam> 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<String, ResourceAddFileParam> 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<CsvVariable> csvVariables, List<ApiScenarioCsv> 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<CsvVariable> csvVariables, List<ApiScenarioCsv> 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<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) {
// 获取数据库中的本地文件
List<String> dbLocalFileIds = dbCsv.stream()
.filter(c -> BooleanUtils.isFalse(c.getAssociation()))
.map(ApiScenarioCsv::getFileId)
.toList();
// 获取请求中的本地文件
List<String> localFileIds = csvVariables.stream()
.map(CsvVariable::getFile)
.filter(c -> BooleanUtils.isTrue(c.getLocal()))
.map(ApiFile::getFileId).toList();
// 待删除文件
List<String> deleteLocals = ListUtils.subtract(dbLocalFileIds, localFileIds);
resourceUpdateRequest.setDeleteFileIds(deleteLocals);
// 新上传文件
List<String> addLocal = ListUtils.subtract(localFileIds, dbLocalFileIds);
resourceUpdateRequest.setUploadFileIds(addLocal);
}
public List<ApiScenarioCsv> getApiScenarioCsv(String scenarioId) {
ApiScenarioCsvExample apiScenarioCsvExample = new ApiScenarioCsvExample();
apiScenarioCsvExample.createCriteria().andScenarioIdEqualTo(scenarioId);
return apiScenarioCsvMapper.selectByExample(apiScenarioCsvExample);
}
public void deleteCsvResource(List<String> 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<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) {
// 获取数据库中关联的文件id
List<String> dbRefFileIds = dbCsv.stream()
.filter(c -> BooleanUtils.isTrue(c.getAssociation()) && StringUtils.isNotBlank(c.getFileId()))
.map(ApiScenarioCsv::getFileId)
.toList();
// 获取请求中关联的文件id
List<String> refFileIds = csvVariables.stream()
.map(CsvVariable::getFile)
.filter(c -> BooleanUtils.isFalse(c.getLocal()) && StringUtils.isNotBlank(c.getFileId()))
.map(ApiFile::getFileId).toList();
List<String> unlinkFileIds = ListUtils.subtract(dbRefFileIds, refFileIds);
resourceUpdateRequest.setUnLinkFileIds(unlinkFileIds);
List<String> linkFileIds = ListUtils.subtract(refFileIds, dbRefFileIds);
resourceUpdateRequest.setLinkFileIds(linkFileIds);
}
public void handleCsvUpdate(ScenarioConfig scenarioConfig, ApiScenario scenario, String userId) {
if (scenarioConfig == null) {
return;
}
List<CsvVariable> csvVariables = getCsvVariables(scenarioConfig);
List<ApiScenarioCsv> dbCsv = getApiScenarioCsv(scenario.getId());
List<String> dbCsvIds = dbCsv.stream()
.map(ApiScenarioCsv::getId)
.toList();
handleRefUpgradeFile(csvVariables, dbCsv);
// 更新 csv 相关数据表
handleCsvDataUpdate(csvVariables, scenario, dbCsvIds);
// 处理文件的上传和删除
handleCsvFileUpdate(csvVariables, dbCsv, scenario, userId);
}
public List<CsvVariable> getCsvVariables(ScenarioConfig scenarioConfig) {
if (scenarioConfig == null || scenarioConfig.getVariable() == null || scenarioConfig.getVariable().getCsvVariables() == null) {
return List.of();
}
return scenarioConfig.getVariable().getCsvVariables();
}
public void handleCsvDataUpdate(List<CsvVariable> csvVariables, ApiScenario scenario, List<String> dbCsvIds) {
List<String> csvIds = csvVariables.stream()
.map(CsvVariable::getId)
.toList();
List<String> deleteCsvIds = ListUtils.subtract(dbCsvIds, csvIds);
//删除不存在的数据
deleteCsvResource(deleteCsvIds);
Set<String> dbCsvIdSet = dbCsvIds.stream().collect(Collectors.toSet());
List<ApiScenarioCsv> 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<CsvVariable> getCsvVariables(String scenarioId) {
ApiScenarioCsvExample example = new ApiScenarioCsvExample();
example.createCriteria().andScenarioIdEqualTo(scenarioId);
List<ApiScenarioCsv> csvList = apiScenarioCsvMapper.selectByExample(example);
List<CsvVariable> 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<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv) {
try {
// 获取数据库中关联的 csv 文件
List<ApiScenarioCsv> dbAssociationCsvList = dbCsv.stream().filter(ApiScenarioCsv::getAssociation).toList();
Map<String, ApiScenarioCsv> dbAssociationCsvIdMap = dbAssociationCsvList.stream()
.collect(Collectors.toMap(ApiScenarioCsv::getId, Function.identity()));
// 获取与数据库中数据 fileId 不一致的 csv
List<CsvVariable> 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<String> dbAssociationCsvFileIds = changeAssociationCsvList.stream().map(csvVariable -> csvVariable.getFile().getFileId()).toList();
FileMetadataExample fileMetadataExample = new FileMetadataExample();
fileMetadataExample.createCriteria().andIdIn(dbAssociationCsvFileIds);
List<FileMetadata> dbAssociationCsvFiles = fileMetadataMapper.selectByExample(fileMetadataExample);
Map<String, FileMetadata> dbAssociationCsvFileMap = dbAssociationCsvFiles.stream()
.collect(Collectors.toMap(FileMetadata::getId, Function.identity()));
// 查询csv文件的版本信息
List<String> refIds = dbAssociationCsvFiles.stream().map(FileMetadata::getRefId).toList();
FileMetadataExample example = new FileMetadataExample();
example.createCriteria().andRefIdIn(refIds);
List<FileMetadata> fileMetadataList = fileMetadataMapper.selectByExample(example)
.stream()
.sorted(Comparator.comparing(FileMetadata::getUpdateTime).reversed())
.collect(Collectors.toList());
Map<String, List<FileMetadata>> 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<FileMetadata> 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);
}
}
}

View File

@ -7,9 +7,6 @@ import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.*; import io.metersphere.api.domain.*;
import io.metersphere.api.dto.*; import io.metersphere.api.dto.*;
import io.metersphere.api.dto.debug.ApiResourceRunRequest; 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.MsScenario;
import io.metersphere.api.dto.request.controller.MsScriptElement; import io.metersphere.api.dto.request.controller.MsScriptElement;
import io.metersphere.api.dto.request.http.MsHTTPElement; 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.parser.step.StepParserFactory;
import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiCommonService;
import io.metersphere.api.service.ApiExecuteService; 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.ApiDefinitionService;
import io.metersphere.api.service.definition.ApiTestCaseService; import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.api.service.queue.ApiExecutionSetService; 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.api.processor.TimeWaitingProcessor;
import io.metersphere.project.domain.Project; import io.metersphere.project.domain.Project;
import io.metersphere.project.dto.environment.EnvironmentInfoDTO; 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.mapper.ProjectMapper;
import io.metersphere.project.service.EnvironmentGroupService; import io.metersphere.project.service.EnvironmentGroupService;
import io.metersphere.project.service.EnvironmentService; import io.metersphere.project.service.EnvironmentService;
@ -70,6 +63,8 @@ public class ApiScenarioRunService {
@Resource @Resource
private ApiScenarioService apiScenarioService; private ApiScenarioService apiScenarioService;
@Resource @Resource
private ApiScenarioFileService apiScenarioFileService;
@Resource
private ApiExecuteService apiExecuteService; private ApiExecuteService apiExecuteService;
@Resource @Resource
private ApiDefinitionService apiDefinitionService; private ApiDefinitionService apiDefinitionService;
@ -82,8 +77,6 @@ public class ApiScenarioRunService {
@Resource @Resource
private ApiPluginService apiPluginService; private ApiPluginService apiPluginService;
@Resource @Resource
private ApiDefinitionModuleService apiDefinitionModuleService;
@Resource
private ApiCommonService apiCommonService; private ApiCommonService apiCommonService;
@Resource @Resource
private ApiScenarioReportService apiScenarioReportService; private ApiScenarioReportService apiScenarioReportService;
@ -120,14 +113,14 @@ public class ApiScenarioRunService {
msScenario.setScenarioConfig(getScenarioConfig(request, true)); msScenario.setScenarioConfig(getScenarioConfig(request, true));
msScenario.setProjectId(request.getProjectId()); msScenario.setProjectId(request.getProjectId());
List<ApiScenarioCsv> dbCsv = apiScenarioService.getApiScenarioCsv(apiScenario.getId()); List<ApiScenarioCsv> dbCsv = apiScenarioFileService.getApiScenarioCsv(apiScenario.getId());
List<CsvVariable> csvVariables = apiScenarioService.getCsvVariables(msScenario.getScenarioConfig()); List<CsvVariable> csvVariables = apiScenarioService.getCsvVariables(msScenario.getScenarioConfig());
apiScenarioService.handleRefUpgradeFile(csvVariables, dbCsv); apiScenarioFileService.handleRefUpgradeFile(csvVariables, dbCsv);
// 处理特殊的步骤详情 // 处理特殊的步骤详情
ApiScenarioCopyStepMap apiScenarioCopyStepMap = apiScenarioService.addSpecialStepDetails(request.getSteps(), request.getStepDetails()); ApiScenarioCopyStepMap apiScenarioCopyStepMap = apiScenarioService.addSpecialStepDetails(request.getSteps(), request.getStepDetails());
// 处理copy的步骤文件 // 处理copy的步骤文件
apiScenarioService.handleRunCopyStepFiles(request, apiScenarioCopyStepMap, request.getStepDetails()); apiScenarioFileService.handleRunCopyStepFiles(request, apiScenarioCopyStepMap, request.getStepDetails());
ApiResourceRunRequest runRequest = new ApiResourceRunRequest(); ApiResourceRunRequest runRequest = new ApiResourceRunRequest();
runRequest = setFileParam(request, runRequest); runRequest = setFileParam(request, runRequest);
@ -416,15 +409,15 @@ public class ApiScenarioRunService {
msScenario.setResourceId(request.getId()); msScenario.setResourceId(request.getId());
if (hasSave) { if (hasSave) {
List<ApiScenarioCsv> dbCsv = apiScenarioService.getApiScenarioCsv(apiScenario.getId()); List<ApiScenarioCsv> dbCsv = apiScenarioFileService.getApiScenarioCsv(apiScenario.getId());
List<CsvVariable> csvVariables = apiScenarioService.getCsvVariables(msScenario.getScenarioConfig()); List<CsvVariable> csvVariables = apiScenarioService.getCsvVariables(msScenario.getScenarioConfig());
apiScenarioService.handleRefUpgradeFile(csvVariables, dbCsv); apiScenarioFileService.handleRefUpgradeFile(csvVariables, dbCsv);
} }
// 处理特殊的步骤详情 // 处理特殊的步骤详情
ApiScenarioCopyStepMap apiScenarioCopyStepMap = apiScenarioService.addSpecialStepDetails(request.getSteps(), request.getStepDetails()); ApiScenarioCopyStepMap apiScenarioCopyStepMap = apiScenarioService.addSpecialStepDetails(request.getSteps(), request.getStepDetails());
// 处理copy的步骤文件 // 处理copy的步骤文件
apiScenarioService.handleRunCopyStepFiles(request, apiScenarioCopyStepMap, request.getStepDetails()); apiScenarioFileService.handleRunCopyStepFiles(request, apiScenarioCopyStepMap, request.getStepDetails());
ApiScenarioParseTmpParam tmpParam = parse(msScenario, request.getSteps(), request); ApiScenarioParseTmpParam tmpParam = parse(msScenario, request.getSteps(), request);

View File

@ -5,8 +5,10 @@ import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType; import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.controller.result.ApiResultCode; import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.*; import io.metersphere.api.domain.*;
import io.metersphere.api.dto.*; import io.metersphere.api.dto.ApiExecResultDTO;
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest; 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.ExecutePageRequest;
import io.metersphere.api.dto.definition.ExecuteReportDTO; import io.metersphere.api.dto.definition.ExecuteReportDTO;
import io.metersphere.api.dto.export.MetersphereApiScenarioExportResponse; 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.functional.mapper.FunctionalCaseTestMapper;
import io.metersphere.plugin.api.spi.AbstractMsProtocolTestElement; import io.metersphere.plugin.api.spi.AbstractMsProtocolTestElement;
import io.metersphere.plugin.api.spi.AbstractMsTestElement; 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.dto.MoveNodeSortDTO;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper; import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.project.mapper.FileMetadataMapper;
import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.project.service.MoveNodeService; import io.metersphere.project.service.MoveNodeService;
import io.metersphere.sdk.constants.*; import io.metersphere.sdk.constants.*;
@ -141,8 +145,6 @@ public class ApiScenarioService extends MoveNodeService {
@Resource @Resource
private ApiScenarioCsvMapper apiScenarioCsvMapper; private ApiScenarioCsvMapper apiScenarioCsvMapper;
@Resource @Resource
private FileMetadataMapper fileMetadataMapper;
@Resource
private ApiScenarioCsvStepMapper apiScenarioCsvStepMapper; private ApiScenarioCsvStepMapper apiScenarioCsvStepMapper;
@Resource @Resource
private ScheduleService scheduleService; private ScheduleService scheduleService;
@ -170,12 +172,13 @@ public class ApiScenarioService extends MoveNodeService {
private ExtApiScenarioReportMapper extApiScenarioReportMapper; private ExtApiScenarioReportMapper extApiScenarioReportMapper;
@Resource @Resource
private FunctionalCaseTestMapper functionalCaseTestMapper; private FunctionalCaseTestMapper functionalCaseTestMapper;
@Resource
private ApiScenarioFileService apiScenarioFileService;
public static final String PRIORITY = "Priority"; public static final String PRIORITY = "Priority";
public static final String STATUS = "Status"; public static final String STATUS = "Status";
public static final String TAGS = "Tags"; public static final String TAGS = "Tags";
public static final String ENVIRONMENT = "Environment"; 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_TABLE = "api_scenario";
private static final String SCENARIO = "SCENARIO"; private static final String SCENARIO = "SCENARIO";
@ -403,10 +406,10 @@ public class ApiScenarioService extends MoveNodeService {
handCsvFilesAdd(request, creator, scenario); handCsvFilesAdd(request, creator, scenario);
// 处理添加的步骤 // 处理添加的步骤
handleStepAdd(request, scenario); handleStepAdd(request, scenario, creator);
// 处理步骤文件 // 处理步骤文件
handleStepFilesAdd(request, creator, scenario); apiScenarioFileService.handleStepFilesAdd(request, creator, scenario);
return 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())) { if (CollectionUtils.isNotEmpty(request.getSteps())) {
checkCircularRef(scenario.getId(), request.getSteps()); checkCircularRef(scenario.getId(), request.getSteps());
@ -467,16 +470,17 @@ public class ApiScenarioService extends MoveNodeService {
steps.forEach(step -> step.setScenarioId(scenario.getId())); steps.forEach(step -> step.setScenarioId(scenario.getId()));
// 处理特殊的步骤详情 // 处理特殊的步骤详情
ApiScenarioCopyStepMap apiScenarioCopyStepMap = addSpecialStepDetails(request.getSteps(), request.getStepDetails()); ApiScenarioCopyStepMap apiScenarioCopyStepMap = addSpecialStepDetails(request.getSteps(), request.getStepDetails());
// 处理copy的步骤文件
handleSaveCopyStepFiles(apiScenarioCopyStepMap, request.getStepDetails(), scenario);
List<ApiScenarioStepBlob> apiScenarioStepBlobs = getUpdateStepBlobs(steps, request.getStepDetails());
apiScenarioStepBlobs.forEach(step -> step.setScenarioId(scenario.getId()));
if (CollectionUtils.isNotEmpty(steps)) { if (CollectionUtils.isNotEmpty(steps)) {
apiScenarioStepMapper.batchInsert(steps); apiScenarioStepMapper.batchInsert(steps);
} }
// 处理copy的步骤文件
apiScenarioFileService.handleSaveCopyStepFiles(apiScenarioCopyStepMap, request.getStepDetails(), scenario, userId);
List<ApiScenarioStepBlob> apiScenarioStepBlobs = getUpdateStepBlobs(steps, request.getStepDetails());
apiScenarioStepBlobs.forEach(step -> step.setScenarioId(scenario.getId()));
if (CollectionUtils.isNotEmpty(apiScenarioStepBlobs)) { if (CollectionUtils.isNotEmpty(apiScenarioStepBlobs)) {
apiScenarioStepBlobMapper.batchInsert(apiScenarioStepBlobs); apiScenarioStepBlobMapper.batchInsert(apiScenarioStepBlobs);
} }
@ -509,10 +513,10 @@ public class ApiScenarioService extends MoveNodeService {
} }
// 处理 csv 相关数据表 // 处理 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<CsvVariable> getCsvVariables(ScenarioConfig scenarioConfig) { public List<CsvVariable> getCsvVariables(ScenarioConfig scenarioConfig) {
@ -522,38 +526,6 @@ public class ApiScenarioService extends MoveNodeService {
return scenarioConfig.getVariable().getCsvVariables(); return scenarioConfig.getVariable().getCsvVariables();
} }
private void handleStepFilesAdd(ApiScenarioAddRequest request, String creator, ApiScenario scenario) {
Map<String, ResourceAddFileParam> 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<String, ResourceUpdateFileParam> 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<ApiScenarioCsvStep> csvSteps) { private void saveStepCsv(String scenarioId, List<ApiScenarioCsvStep> csvSteps) {
// 先删除 // 先删除
ApiScenarioCsvStepExample csvStepExample = new ApiScenarioCsvStepExample(); 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<CsvVariable> csvVariables = getCsvVariables(scenarioConfig);
List<ApiScenarioCsv> dbCsv = getApiScenarioCsv(scenario.getId());
List<String> 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<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv) {
try {
// 获取数据库中关联的 csv 文件
List<ApiScenarioCsv> dbAssociationCsvList = dbCsv.stream().filter(ApiScenarioCsv::getAssociation).toList();
Map<String, ApiScenarioCsv> dbAssociationCsvIdMap = dbAssociationCsvList.stream()
.collect(Collectors.toMap(ApiScenarioCsv::getId, Function.identity()));
// 获取与数据库中数据 fileId 不一致的 csv
List<CsvVariable> 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<String> dbAssociationCsvFileIds = changeAssociationCsvList.stream().map(csvVariable -> csvVariable.getFile().getFileId()).toList();
FileMetadataExample fileMetadataExample = new FileMetadataExample();
fileMetadataExample.createCriteria().andIdIn(dbAssociationCsvFileIds);
List<FileMetadata> dbAssociationCsvFiles = fileMetadataMapper.selectByExample(fileMetadataExample);
Map<String, FileMetadata> dbAssociationCsvFileMap = dbAssociationCsvFiles.stream()
.collect(Collectors.toMap(FileMetadata::getId, Function.identity()));
// 查询csv文件的版本信息
List<String> refIds = dbAssociationCsvFiles.stream().map(FileMetadata::getRefId).toList();
FileMetadataExample example = new FileMetadataExample();
example.createCriteria().andRefIdIn(refIds);
List<FileMetadata> fileMetadataList = fileMetadataMapper.selectByExample(example)
.stream()
.sorted(Comparator.comparing(FileMetadata::getUpdateTime).reversed())
.collect(Collectors.toList());
Map<String, List<FileMetadata>> 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<FileMetadata> 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<CsvVariable> csvVariables, ApiScenario scenario, List<String> dbCsvIds) {
List<String> csvIds = csvVariables.stream()
.map(CsvVariable::getId)
.toList();
List<String> deleteCsvIds = ListUtils.subtract(dbCsvIds, csvIds);
//删除不存在的数据
deleteCsvResource(deleteCsvIds);
Set<String> dbCsvIdSet = dbCsvIds.stream().collect(Collectors.toSet());
List<ApiScenarioCsv> 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<CsvVariable> csvVariables, List<ApiScenarioCsv> 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<CsvVariable> csvVariables, List<ApiScenarioCsv> 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<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) {
// 获取数据库中关联的文件id
List<String> dbRefFileIds = dbCsv.stream()
.filter(c -> BooleanUtils.isTrue(c.getAssociation()) && StringUtils.isNotBlank(c.getFileId()))
.map(ApiScenarioCsv::getFileId)
.toList();
// 获取请求中关联的文件id
List<String> refFileIds = csvVariables.stream()
.map(CsvVariable::getFile)
.filter(c -> BooleanUtils.isFalse(c.getLocal()) && StringUtils.isNotBlank(c.getFileId()))
.map(ApiFile::getFileId).toList();
List<String> unlinkFileIds = ListUtils.subtract(dbRefFileIds, refFileIds);
resourceUpdateRequest.setUnLinkFileIds(unlinkFileIds);
List<String> linkFileIds = ListUtils.subtract(refFileIds, dbRefFileIds);
resourceUpdateRequest.setLinkFileIds(linkFileIds);
}
public void setCsvLocalFileParam(List<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) {
// 获取数据库中的本地文件
List<String> dbLocalFileIds = dbCsv.stream()
.filter(c -> BooleanUtils.isFalse(c.getAssociation()))
.map(ApiScenarioCsv::getFileId)
.toList();
// 获取请求中的本地文件
List<String> localFileIds = csvVariables.stream()
.map(CsvVariable::getFile)
.filter(c -> BooleanUtils.isTrue(c.getLocal()))
.map(ApiFile::getFileId).toList();
// 待删除文件
List<String> deleteLocals = ListUtils.subtract(dbLocalFileIds, localFileIds);
resourceUpdateRequest.setDeleteFileIds(deleteLocals);
// 新上传文件
List<String> addLocal = ListUtils.subtract(localFileIds, dbLocalFileIds);
resourceUpdateRequest.setUploadFileIds(addLocal);
}
public List<ApiScenarioCsv> getApiScenarioCsv(String scenarioId) {
ApiScenarioCsvExample apiScenarioCsvExample = new ApiScenarioCsvExample();
apiScenarioCsvExample.createCriteria().andScenarioIdEqualTo(scenarioId);
return apiScenarioCsvMapper.selectByExample(apiScenarioCsvExample);
}
private void deleteCsvResource(List<String> 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) { private ApiScenario getAddApiScenario(ApiScenarioAddRequest request, String creator) {
ApiScenario scenario = new ApiScenario(); ApiScenario scenario = new ApiScenario();
BeanUtils.copyBean(scenario, request); BeanUtils.copyBean(scenario, request);
@ -814,10 +583,10 @@ public class ApiScenarioService extends MoveNodeService {
updateApiScenarioStep(request, originScenario, updater); updateApiScenarioStep(request, originScenario, updater);
// 处理步骤文件 // 处理步骤文件
handleStepFilesUpdate(request, updater, originScenario); apiScenarioFileService.handleStepFilesUpdate(request, updater, originScenario);
// 处理 csv 文件 // 处理 csv 文件
handleCsvUpdate(request.getScenarioConfig(), scenario, updater); apiScenarioFileService.handleCsvUpdate(request.getScenarioConfig(), scenario, updater);
return scenario; return scenario;
} }
@ -848,8 +617,6 @@ public class ApiScenarioService extends MoveNodeService {
// 获取待更新的步骤详情 // 获取待更新的步骤详情
ApiScenarioCopyStepMap apiScenarioCopyStepMap = addSpecialStepDetails(steps, request.getStepDetails()); ApiScenarioCopyStepMap apiScenarioCopyStepMap = addSpecialStepDetails(steps, request.getStepDetails());
// 处理copy的步骤文件
handleSaveCopyStepFiles(apiScenarioCopyStepMap, request.getStepDetails(), scenario);
List<ApiScenarioStepBlob> updateStepBlobs = getUpdateStepBlobs(apiScenarioSteps, request.getStepDetails()); List<ApiScenarioStepBlob> updateStepBlobs = getUpdateStepBlobs(apiScenarioSteps, request.getStepDetails());
updateStepBlobs.forEach(step -> step.setScenarioId(scenario.getId())); updateStepBlobs.forEach(step -> step.setScenarioId(scenario.getId()));
@ -862,6 +629,9 @@ public class ApiScenarioService extends MoveNodeService {
deleteStepByScenarioId(scenario.getId()); deleteStepByScenarioId(scenario.getId());
apiScenarioStepMapper.batchInsert(apiScenarioSteps); apiScenarioStepMapper.batchInsert(apiScenarioSteps);
// 处理copy的步骤文件
apiScenarioFileService.handleSaveCopyStepFiles(apiScenarioCopyStepMap, request.getStepDetails(), scenario, userId);
// 详情表-删除已经删除的步骤详情 // 详情表-删除已经删除的步骤详情
SubListUtils.dealForSubList(deleteStepIds, 100, subIds -> { SubListUtils.dealForSubList(deleteStepIds, 100, subIds -> {
ApiScenarioStepBlobExample stepBlobExample = new ApiScenarioStepBlobExample(); ApiScenarioStepBlobExample stepBlobExample = new ApiScenarioStepBlobExample();
@ -1120,281 +890,6 @@ public class ApiScenarioService extends MoveNodeService {
return apiScenarioCopyStepMap; return apiScenarioCopyStepMap;
} }
/**
* 处理保存时copy的步骤中文件
* @param apiScenarioCopyStepMap
* @param stepDetails
*/
public void handleSaveCopyStepFiles(ApiScenarioCopyStepMap apiScenarioCopyStepMap, Map<String, Object> stepDetails, ApiScenario scenario) {
try {
List<ApiFileResource> 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<String, Object> stepDetails) {
try {
List<ApiFileResource> 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<String, ResourceAddFileParam> 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<ApiFileResource> handleCopyFromStepFiles(Map<String, Object> stepDetails, Map<String, String> copyFromStepIdMap, ApiScenario scenario) {
if (copyFromStepIdMap.isEmpty()) {
return List.of();
}
// 查询 copyFrom 的步骤所关联的文件
Collection<String> copyFromStepIds = copyFromStepIdMap.values();
List<ApiFileResource> apiFileResources = apiFileResourceService.selectByResourceIds(copyFromStepIds);
if (apiFileResources.isEmpty()) {
return List.of();
}
Map<String, List<ApiFileResource>> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId));
// 查询 copyFrom 步骤的场景ID Map
List<String> hasFileCopyFromStepIds = stepFileMap.keySet().stream().toList();
Map<String, String> copyFromStepScenarioMap = getApiScenarioStepByIds(hasFileCopyFromStepIds).stream()
.collect(Collectors.toMap(ApiScenarioStep::getId, ApiScenarioStep::getScenarioId));
Map<String, String> fileIdMap = new HashMap<>();
List<ApiFileResource> newApiFileResources = new ArrayList<>();
for (String stepId : copyFromStepIdMap.keySet()) {
List<ApiFileResource> 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<ApiFileResource> handleCopyApiFiles(Map<String, Object> stepDetails, Map<String, String> copyApiIdMap, ApiScenario scenario) {
if (copyApiIdMap.isEmpty()) {
return List.of();
}
// 查询 copy 的接口定义所关联的文件
Collection<String> copyApiIds = copyApiIdMap.values();
List<ApiFileResource> apiFileResources = apiFileResourceService.selectByResourceIds(copyApiIds);
if (apiFileResources.isEmpty()) {
return List.of();
}
Map<String, List<ApiFileResource>> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId));
Map<String, String> fileIdMap = new HashMap<>();
List<ApiFileResource> newApiFileResources = new ArrayList<>();
for (String stepId : copyApiIdMap.keySet()) {
List<ApiFileResource> 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<ApiFileResource> handleCopyApiCaseFiles(Map<String, Object> stepDetails, Map<String, String> copyApiCaseIdMap, ApiScenario scenario) {
if (copyApiCaseIdMap.isEmpty()) {
return List.of();
}
// 查询 copy 的接口定义所关联的文件
Collection<String> copyApiIds = copyApiCaseIdMap.values();
List<ApiFileResource> apiFileResources = apiFileResourceService.selectByResourceIds(copyApiIds);
if (apiFileResources.isEmpty()) {
return List.of();
}
Map<String, List<ApiFileResource>> stepFileMap = apiFileResources.stream().collect(Collectors.groupingBy(ApiFileResource::getResourceId));
boolean isSave = scenario != null;
Map<String, String> fileIdMap = new HashMap<>();
List<ApiFileResource> newApiFileResources = new ArrayList<>();
for (String stepId : copyApiCaseIdMap.keySet()) {
List<ApiFileResource> 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<String, Object> stepDetails, Map<String, String> 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<ApiScenarioStep> getApiScenarioStepByIds(List<String> stepIds) {
if (CollectionUtils.isEmpty(stepIds)) {
List.of();
}
ApiScenarioStepExample example = new ApiScenarioStepExample();
example.createCriteria().andIdIn(stepIds);
return apiScenarioStepMapper.selectByExample(example);
}
/** /**
* @param stepDetails * @param stepDetails
* @param copyFromIdMap key stepIDvalue 为复制的 resourceId 或者 stepId * @param copyFromIdMap key stepIDvalue 为复制的 resourceId 或者 stepId
@ -1476,19 +971,6 @@ public class ApiScenarioService extends MoveNodeService {
return partialRefStepDetail; 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) { public long getNextNum(String projectId) {
return NumGenerator.nextNum(projectId, ApplicationNumScope.API_SCENARIO); return NumGenerator.nextNum(projectId, ApplicationNumScope.API_SCENARIO);
} }
@ -1817,7 +1299,7 @@ public class ApiScenarioService extends MoveNodeService {
} }
//存放csv变量 //存放csv变量
apiScenarioDetail.getScenarioConfig().getVariable().setCsvVariables(getCsvVariables(scenarioId)); apiScenarioDetail.getScenarioConfig().getVariable().setCsvVariables(apiScenarioFileService.getCsvVariables(scenarioId));
// 获取所有步骤 // 获取所有步骤
List<ApiScenarioStepDTO> allSteps = getAllStepsByScenarioIds(List.of(scenarioId)) List<ApiScenarioStepDTO> allSteps = getAllStepsByScenarioIds(List.of(scenarioId))
@ -1884,7 +1366,7 @@ public class ApiScenarioService extends MoveNodeService {
} }
//存放csv变量 //存放csv变量
apiScenarioDetail.getScenarioConfig().getVariable().setCsvVariables(getCsvVariables(apiScenario.getId())); apiScenarioDetail.getScenarioConfig().getVariable().setCsvVariables(apiScenarioFileService.getCsvVariables(apiScenario.getId()));
// 获取所有步骤 // 获取所有步骤
List<ApiScenarioStepDTO> allSteps = getAllStepsByScenarioIds(List.of(apiScenario.getId())) List<ApiScenarioStepDTO> allSteps = getAllStepsByScenarioIds(List.of(apiScenario.getId()))
@ -1949,21 +1431,6 @@ public class ApiScenarioService extends MoveNodeService {
} }
} }
private List<CsvVariable> getCsvVariables(String scenarioId) {
ApiScenarioCsvExample example = new ApiScenarioCsvExample();
example.createCriteria().andScenarioIdEqualTo(scenarioId);
List<ApiScenarioCsv> csvList = apiScenarioCsvMapper.selectByExample(example);
List<CsvVariable> 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); return apiTestCaseService.getExecuteList(request);
} }
public List<OperationHistoryDTO> operationHistoryList(OperationHistoryRequest request) { public List<OperationHistoryDTO> operationHistoryList(OperationHistoryRequest request) {
return operationHistoryService.listWidthTable(request, SCENARIO_TABLE); return operationHistoryService.listWidthTable(request, SCENARIO_TABLE);
} }
@ -2677,7 +2143,7 @@ public class ApiScenarioService extends MoveNodeService {
return; return;
} }
AbstractMsTestElement msTestElement = getAbstractMsTestElement(apiScenarioStepBlob.getContent()); AbstractMsTestElement msTestElement = apiCommonService.getAbstractMsTestElement(apiScenarioStepBlob.getContent());
if (msTestElement != null && msTestElement instanceof MsHTTPElement msHTTPElement) { if (msTestElement != null && msTestElement instanceof MsHTTPElement msHTTPElement) {
List<ApiFile> updateFiles = apiCommonService.getApiFilesByFileId(originFileAssociation.getFileId(), msHTTPElement); List<ApiFile> updateFiles = apiCommonService.getApiFilesByFileId(originFileAssociation.getFileId(), msHTTPElement);
// 替换文件的Id和name // 替换文件的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) { public void handleScenarioFileAssociationUpgrade(FileAssociation originFileAssociation, FileMetadata newFileMetadata) {
String scenarioId = originFileAssociation.getSourceId(); String scenarioId = originFileAssociation.getSourceId();
// 查询步骤详情 // 查询步骤详情
@ -2713,7 +2165,7 @@ public class ApiScenarioService extends MoveNodeService {
return; return;
} }
List<CsvVariable> csvVariables = getCsvVariables(scenarioId); List<CsvVariable> csvVariables = apiScenarioFileService.getCsvVariables(scenarioId);
List<ApiFile> updateFiles = new ArrayList<>(csvVariables.size()); List<ApiFile> updateFiles = new ArrayList<>(csvVariables.size());
for (CsvVariable csvVariable : csvVariables) { for (CsvVariable csvVariable : csvVariables) {
ApiFile apiFile = csvVariable.getFile(); ApiFile apiFile = csvVariable.getFile();
@ -2725,7 +2177,7 @@ public class ApiScenarioService extends MoveNodeService {
// 替换文件的Id和name // 替换文件的Id和name
apiCommonService.replaceApiFileInfo(updateFiles, newFileMetadata); apiCommonService.replaceApiFileInfo(updateFiles, newFileMetadata);
List<String> dbCsvIds = csvVariables.stream().map(CsvVariable::getId).toList(); List<String> dbCsvIds = csvVariables.stream().map(CsvVariable::getId).toList();
handleCsvDataUpdate(csvVariables, apiScenarioMapper.selectByPrimaryKey(originFileAssociation.getSourceId()), dbCsvIds); apiScenarioFileService.handleCsvDataUpdate(csvVariables, apiScenarioMapper.selectByPrimaryKey(originFileAssociation.getSourceId()), dbCsvIds);
} }
} }

View File

@ -255,6 +255,15 @@ public class FileAssociationService {
return this.deleteAndSaveLog(example, logRecord); return this.deleteAndSaveLog(example, logRecord);
} }
public List<FileAssociation> selectBySourceIds(List<String> sourceIds) {
if (CollectionUtils.isEmpty(sourceIds)) {
return List.of();
}
FileAssociationExample example = new FileAssociationExample();
example.createCriteria().andSourceIdIn(sourceIds);
return fileAssociationMapper.selectByExample(example);
}
/** /**
* 取消关联 * 取消关联
* *