fix(测试跟踪): 修复测试计划报告分享,点击用例报告显示链接失效的缺陷

--bug=1023967 --user=王孝刚
[接口测试]github#22405测试报告链接有效期设置为15天,两三天后点击测试报告链接进去查看失败用例详情报错
https://www.tapd.cn/55049933/s/1345056
This commit is contained in:
wxg0103 2023-03-02 17:52:06 +08:00 committed by jianxing
parent c389ebbdf8
commit c77f04e2d4
13 changed files with 119 additions and 34 deletions

View File

@ -11,4 +11,6 @@ public interface ExtTestPlanApiScenarioMapper {
List<TestPlanApiScenario> selectPlanByIdsAndStatusIsNotTrash(@Param("ids") List<String> ids);
List<TestPlanApiScenario> selectByScenarioIds(@Param("ids") List<String> ids);
String selectPlanIdByTestPlanId(@Param("id") String id);
}

View File

@ -38,4 +38,9 @@
#{v}
</foreach>
</select>
<select id="selectPlanIdByTestPlanId" resultType="java.lang.String">
SELECT plan.project_id from test_plan plan LEFT JOIN
test_plan_report report on plan.id = report.test_plan_id
where report.id =#{id}
</select>
</mapper>

View File

@ -3,12 +3,12 @@ package io.metersphere.controller.plan;
import io.metersphere.api.dto.automation.TestPlanFailureApiDTO;
import io.metersphere.service.ShareInfoService;
import io.metersphere.service.plan.TestPlanApiCaseService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.List;
@RestController
@ -22,25 +22,25 @@ public class ShareTestPlanApiCaseController {
@GetMapping("/list/failure/{shareId}/{planId}")
public List<TestPlanFailureApiDTO> getApiFailureList(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId);
shareInfoService.validate(shareId);
return testPlanApiCaseService.getFailureCases(planId);
}
@GetMapping("/list/errorReport/{shareId}/{planId}")
public List<TestPlanFailureApiDTO> getErrorReportApiCaseList(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId);
shareInfoService.validate(shareId);
return testPlanApiCaseService.getErrorReportCases(planId);
}
@GetMapping("/list/unExecute/{shareId}/{planId}")
public List<TestPlanFailureApiDTO> getUnExecuteCases(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId);
shareInfoService.validate(shareId);
return testPlanApiCaseService.getUnExecuteCases(planId);
}
@GetMapping("/list/all/{shareId}/{planId}")
public List<TestPlanFailureApiDTO> getApiAllList(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId);
shareInfoService.validate(shareId);
return testPlanApiCaseService.getAllCases(planId);
}
}

View File

@ -3,13 +3,12 @@ package io.metersphere.controller.plan;
import io.metersphere.api.dto.ApiReportResult;
import io.metersphere.service.ShareInfoService;
import io.metersphere.service.definition.ApiDefinitionService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
@RestController
@RequestMapping("/share")
public class ShareTestPlanApiReportController {
@ -21,7 +20,7 @@ public class ShareTestPlanApiReportController {
@GetMapping("/api/definition/report/getReport/{shareId}/{testId}")
public ApiReportResult getApiReport(@PathVariable String shareId, @PathVariable String testId) {
shareInfoService.validateExpired(shareId);
shareInfoService.validate(shareId);
return apiDefinitionService.getDbResult(testId);
}
}

View File

@ -1,14 +1,16 @@
package io.metersphere.controller.plan;
import io.metersphere.api.dto.automation.ApiScenarioReportResult;
import io.metersphere.api.dto.automation.TestPlanFailureScenarioDTO;
import io.metersphere.service.ShareInfoService;
import io.metersphere.service.plan.TestPlanScenarioCaseService;
import io.metersphere.service.scenario.ApiScenarioReportService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.List;
@RestController
@ -19,28 +21,37 @@ public class ShareTestPlanScenarioCaseController {
ShareInfoService shareInfoService;
@Resource
TestPlanScenarioCaseService testPlanScenarioCaseService;
@Resource
private ApiScenarioReportService apiReportService;
@GetMapping("/list/failure/{shareId}/{planId}")
public List<TestPlanFailureScenarioDTO> getScenarioFailureList(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId);
shareInfoService.validate(shareId);
return testPlanScenarioCaseService.getFailureCases(planId);
}
@GetMapping("/list/all/{shareId}/{planId}")
public List<TestPlanFailureScenarioDTO> getScenarioAllList(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId);
shareInfoService.validate(shareId);
return testPlanScenarioCaseService.getAllCases(planId);
}
@GetMapping("/list/errorReport/{shareId}/{planId}")
public List<TestPlanFailureScenarioDTO> getScenarioErrorReportList(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId);
shareInfoService.validate(shareId);
return testPlanScenarioCaseService.getErrorReportCases(planId);
}
@GetMapping("/list/unExecute/{shareId}/{planId}")
public List<TestPlanFailureScenarioDTO> getUnExecuteScenarioCases(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId);
shareInfoService.validate(shareId);
return testPlanScenarioCaseService.getUnExecuteCases(planId);
}
@GetMapping("/get/{shareId}/{reportId}")
public ApiScenarioReportResult get(@PathVariable String shareId, @PathVariable String reportId) {
shareInfoService.validate(shareId);
return apiReportService.get(reportId, true);
}
}

View File

@ -13,6 +13,7 @@ import io.metersphere.base.mapper.ShareInfoMapper;
import io.metersphere.base.mapper.UserMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioReportMapper;
import io.metersphere.base.mapper.ext.ExtShareInfoMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanApiScenarioMapper;
import io.metersphere.commons.constants.ProjectApplicationType;
import io.metersphere.commons.constants.PropertyConstant;
import io.metersphere.commons.constants.ShareType;
@ -21,15 +22,16 @@ import io.metersphere.commons.utils.*;
import io.metersphere.i18n.Translator;
import io.metersphere.service.definition.ApiModuleService;
import io.metersphere.service.scenario.ApiScenarioReportService;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
@ -58,6 +60,8 @@ public class ShareInfoService extends BaseShareInfoService {
private ApiScenarioReportService apiScenarioReportService;
@Resource
private BaseProjectApplicationService baseProjectApplicationService;
@Resource
ExtTestPlanApiScenarioMapper extTestPlanApiScenarioMapper;
public Pager<List<ApiDocumentInfoDTO>> selectApiInfoByParam(ApiDocumentRequest apiDocumentRequest, int goPage, int pageSize) {
this.iniApiDocumentRequest(apiDocumentRequest);
@ -627,15 +631,14 @@ public class ShareInfoService extends BaseShareInfoService {
}
}
public void validate(String shareId, String customData) {
public void validate(String shareId) {
ShareInfo shareInfo = shareInfoMapper.selectByPrimaryKey(shareId);
validateExpired(shareInfo);
if (ObjectUtils.isNotEmpty(shareInfo)) {
String projectId = extTestPlanApiScenarioMapper.selectPlanIdByTestPlanId(shareInfo.getCustomData());
validateExpiredTestPlan(shareInfo, projectId);
}
if (shareInfo == null) {
MSException.throwException("ShareInfo not exist!");
} else {
if (!StringUtils.equals(customData, shareInfo.getCustomData())) {
MSException.throwException("ShareInfo validate failure!");
}
}
}
}

View File

@ -37,6 +37,10 @@ export function getShareScenarioReport(shareId, reportId) {
return get('/api/scenario/report/get/' + shareId + '/' + reportId);
}
export function getSharePlanScenarioReport(shareId, reportId) {
return get('/share/test/plan/scenario/case/get/' + shareId + '/' + reportId);
}
export function getScenarioReportStepDetail(stepId) {
return get('/api/scenario/report/get/step/detail/' + stepId);
}

View File

@ -126,6 +126,7 @@ import {
getScenarioReport,
getScenarioReportDetail,
getShareScenarioReport,
getSharePlanScenarioReport,
reportReName,
} from '../../../api/scenario-report';
import { STEP } from '../../automation/scenario/Setting';
@ -459,6 +460,15 @@ export default {
this.buildReport();
}
} else if (this.isShare) {
if (this.isPlan) {
getSharePlanScenarioReport(this.shareId, this.reportId).then((res) => {
let data = res.data;
if (data) {
this.checkReport(data);
this.handleGetScenarioReport(data);
}
});
}
if (this.reportId) {
getShareScenarioReport(this.shareId, this.reportId).then((res) => {
let data = res.data;

View File

@ -1,17 +1,25 @@
package io.metersphere.service;
import io.metersphere.base.domain.ProjectApplication;
import io.metersphere.base.domain.ShareInfo;
import io.metersphere.base.mapper.ShareInfoMapper;
import io.metersphere.base.mapper.ext.BaseShareInfoMapper;
import io.metersphere.commons.constants.ProjectApplicationType;
import io.metersphere.commons.exception.MSException;
import io.metersphere.dto.ShareInfoDTO;
import io.metersphere.i18n.Translator;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.util.List;
import java.util.UUID;
import static io.metersphere.commons.user.ShareUtil.getTimeMills;
@Service
@Transactional(rollbackFor = Exception.class)
public class BaseShareInfoService {
@ -20,6 +28,8 @@ public class BaseShareInfoService {
BaseShareInfoMapper baseShareInfoMapper;
@Resource
ShareInfoMapper shareInfoMapper;
@Resource
private BaseProjectApplicationService baseProjectApplicationService;
/**
* 生成分享连接
@ -60,4 +70,35 @@ public class BaseShareInfoService {
public ShareInfo get(String id) {
return shareInfoMapper.selectByPrimaryKey(id);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void validateExpiredTestPlan(ShareInfo shareInfo, String projectId) {
// 有效期根据类型从ProjectApplication中获取
if (shareInfo == null) {
MSException.throwException(Translator.get("connection_expired"));
}
String type = ProjectApplicationType.TRACK_SHARE_REPORT_TIME.toString();
if (StringUtils.isBlank(type) || Strings.isBlank(projectId)) {
millisCheck(System.currentTimeMillis() - shareInfo.getUpdateTime(), 1000 * 60 * 60 * 24, shareInfo.getId());
} else {
ProjectApplication projectApplication = baseProjectApplicationService.getProjectApplication(projectId, type);
if (projectApplication.getTypeValue() == null) {
millisCheck(System.currentTimeMillis() - shareInfo.getUpdateTime(), 1000 * 60 * 60 * 24, shareInfo.getId());
} else {
String expr = projectApplication.getTypeValue();
long timeMills = getTimeMills(shareInfo.getUpdateTime(), expr);
millisCheck(System.currentTimeMillis(), timeMills, shareInfo.getId());
}
}
}
private void millisCheck(long compareMillis, long millis, String shareInfoId) {
if (compareMillis > millis) {
shareInfoMapper.deleteByPrimaryKey(shareInfoId);
MSException.throwException(Translator.get("connection_expired"));
}
}
}

View File

@ -41,4 +41,6 @@ public interface ExtTestPlanLoadCaseMapper {
List<String> selectIdByLoadCaseReportIdAndStatusIsRun(String reportId);
void updateStatusNullById(String id);
String selectPlanIdByTestPlanId(@Param("id") String id);
}

View File

@ -348,6 +348,11 @@
where load_report_id = #{planId}
and `status` = 'run'
</select>
<select id="selectPlanIdByTestPlanId" resultType="java.lang.String">
SELECT plan.project_id from test_plan plan LEFT JOIN
test_plan_report report on plan.id = report.test_plan_id
where report.id =#{id}
</select>
<update id="updateStatusNullById">
update test_plan_load_case tplc
set status = null

View File

@ -4,9 +4,9 @@ import io.metersphere.plan.dto.TestPlanLoadCaseDTO;
import io.metersphere.plan.request.LoadCaseReportRequest;
import io.metersphere.plan.service.TestPlanLoadCaseService;
import io.metersphere.service.ShareInfoService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import java.util.List;
@RestController
@ -20,19 +20,19 @@ public class ShareTestPlanLoadCaseController {
@GetMapping("/list/failure/{shareId}/{planId}")
public List<TestPlanLoadCaseDTO> getLoadFailureCases(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId);
shareInfoService.validate(shareId);
return testPlanLoadCaseService.getFailureCases(planId);
}
@GetMapping("/list/all/{shareId}/{planId}")
public List<TestPlanLoadCaseDTO> getLoadAllCases(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId);
shareInfoService.validate(shareId);
return testPlanLoadCaseService.getAllCases(planId);
}
@PostMapping("/report/exist/{shareId}")
public Boolean isExistReport(@PathVariable String shareId, @RequestBody LoadCaseReportRequest request) {
shareInfoService.validateExpired(shareId);
shareInfoService.validate(shareId);
return testPlanLoadCaseService.isExistReport(request);
}
}

View File

@ -5,42 +5,45 @@ import io.metersphere.base.domain.ProjectApplication;
import io.metersphere.base.domain.ShareInfo;
import io.metersphere.base.mapper.LoadTestReportMapper;
import io.metersphere.base.mapper.ShareInfoMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper;
import io.metersphere.commons.constants.ProjectApplicationType;
import io.metersphere.commons.exception.MSException;
import io.metersphere.i18n.Translator;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import static io.metersphere.commons.user.ShareUtil.getTimeMills;
@Service
public class ShareInfoService {
public class ShareInfoService extends BaseShareInfoService {
@Resource
private ShareInfoMapper shareInfoMapper;
@Resource
private BaseProjectApplicationService baseProjectApplicationService;
@Resource
private LoadTestReportMapper loadTestReportMapper;
@Resource
private ExtTestPlanLoadCaseMapper extTestPlanLoadCaseMapper;
public void validateExpired(String shareId) {
ShareInfo shareInfo = shareInfoMapper.selectByPrimaryKey(shareId);
this.validateExpired(shareInfo);
}
public void validate(String shareId, String customData) {
public void validate(String shareId) {
ShareInfo shareInfo = shareInfoMapper.selectByPrimaryKey(shareId);
validateExpired(shareInfo);
if (ObjectUtils.isNotEmpty(shareInfo)) {
String projectId = extTestPlanLoadCaseMapper.selectPlanIdByTestPlanId(shareInfo.getCustomData());
validateExpiredTestPlan(shareInfo, projectId);
}
if (shareInfo == null) {
MSException.throwException("ShareInfo not exist!");
} else {
if (!StringUtils.equals(customData, shareInfo.getCustomData())) {
MSException.throwException("ShareInfo validate failure!");
}
}
}