feat(接口测试): 任务异常提示优化

--task=1016919 --user=陈建星 任务异常显示优化 https://www.tapd.cn/55049933/s/1609267
This commit is contained in:
AgAngle 2024-11-13 13:48:27 +08:00 committed by Craftsman
parent ab426e9853
commit 35a5b03343
26 changed files with 538 additions and 235 deletions

View File

@ -78,16 +78,19 @@ public class ExecTaskItem implements Serializable {
@Size(min = 1, max = 50, message = "{exec_task_item.executor.length_range}", groups = {Created.class, Updated.class})
private String executor;
@Schema(description = "测试集ID")
private String collectionId;
@Schema(description = "删除标识", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{exec_task_item.deleted.not_blank}", groups = {Created.class})
private Boolean deleted;
@Schema(description = "测试集ID")
private String collectionId;
@Schema(description = "用例表id")
private String caseId;
@Schema(description = "异常信息")
private String errorMessage;
private static final long serialVersionUID = 1L;
public enum Column {
@ -107,9 +110,10 @@ public class ExecTaskItem implements Serializable {
startTime("start_time", "startTime", "BIGINT", false),
endTime("end_time", "endTime", "BIGINT", false),
executor("executor", "executor", "VARCHAR", false),
deleted("deleted", "deleted", "BIT", false),
collectionId("collection_id", "collectionId", "VARCHAR", false),
caseId("case_id", "caseId", "VARCHAR", false);
deleted("deleted", "deleted", "BIT", false),
caseId("case_id", "caseId", "VARCHAR", false),
errorMessage("error_message", "errorMessage", "VARCHAR", false);
private static final String BEGINNING_DELIMITER = "`";

View File

@ -1204,66 +1204,6 @@ public class ExecTaskItemExample {
return (Criteria) this;
}
public Criteria andDeletedIsNull() {
addCriterion("deleted is null");
return (Criteria) this;
}
public Criteria andDeletedIsNotNull() {
addCriterion("deleted is not null");
return (Criteria) this;
}
public Criteria andDeletedEqualTo(Boolean value) {
addCriterion("deleted =", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedNotEqualTo(Boolean value) {
addCriterion("deleted <>", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedGreaterThan(Boolean value) {
addCriterion("deleted >", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedGreaterThanOrEqualTo(Boolean value) {
addCriterion("deleted >=", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedLessThan(Boolean value) {
addCriterion("deleted <", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedLessThanOrEqualTo(Boolean value) {
addCriterion("deleted <=", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedIn(List<Boolean> values) {
addCriterion("deleted in", values, "deleted");
return (Criteria) this;
}
public Criteria andDeletedNotIn(List<Boolean> values) {
addCriterion("deleted not in", values, "deleted");
return (Criteria) this;
}
public Criteria andDeletedBetween(Boolean value1, Boolean value2) {
addCriterion("deleted between", value1, value2, "deleted");
return (Criteria) this;
}
public Criteria andDeletedNotBetween(Boolean value1, Boolean value2) {
addCriterion("deleted not between", value1, value2, "deleted");
return (Criteria) this;
}
public Criteria andCollectionIdIsNull() {
addCriterion("collection_id is null");
return (Criteria) this;
@ -1334,6 +1274,66 @@ public class ExecTaskItemExample {
return (Criteria) this;
}
public Criteria andDeletedIsNull() {
addCriterion("deleted is null");
return (Criteria) this;
}
public Criteria andDeletedIsNotNull() {
addCriterion("deleted is not null");
return (Criteria) this;
}
public Criteria andDeletedEqualTo(Boolean value) {
addCriterion("deleted =", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedNotEqualTo(Boolean value) {
addCriterion("deleted <>", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedGreaterThan(Boolean value) {
addCriterion("deleted >", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedGreaterThanOrEqualTo(Boolean value) {
addCriterion("deleted >=", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedLessThan(Boolean value) {
addCriterion("deleted <", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedLessThanOrEqualTo(Boolean value) {
addCriterion("deleted <=", value, "deleted");
return (Criteria) this;
}
public Criteria andDeletedIn(List<Boolean> values) {
addCriterion("deleted in", values, "deleted");
return (Criteria) this;
}
public Criteria andDeletedNotIn(List<Boolean> values) {
addCriterion("deleted not in", values, "deleted");
return (Criteria) this;
}
public Criteria andDeletedBetween(Boolean value1, Boolean value2) {
addCriterion("deleted between", value1, value2, "deleted");
return (Criteria) this;
}
public Criteria andDeletedNotBetween(Boolean value1, Boolean value2) {
addCriterion("deleted not between", value1, value2, "deleted");
return (Criteria) this;
}
public Criteria andCaseIdIsNull() {
addCriterion("case_id is null");
return (Criteria) this;
@ -1403,6 +1403,76 @@ public class ExecTaskItemExample {
addCriterion("case_id not between", value1, value2, "caseId");
return (Criteria) this;
}
public Criteria andErrorMessageIsNull() {
addCriterion("error_message is null");
return (Criteria) this;
}
public Criteria andErrorMessageIsNotNull() {
addCriterion("error_message is not null");
return (Criteria) this;
}
public Criteria andErrorMessageEqualTo(String value) {
addCriterion("error_message =", value, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageNotEqualTo(String value) {
addCriterion("error_message <>", value, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageGreaterThan(String value) {
addCriterion("error_message >", value, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageGreaterThanOrEqualTo(String value) {
addCriterion("error_message >=", value, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageLessThan(String value) {
addCriterion("error_message <", value, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageLessThanOrEqualTo(String value) {
addCriterion("error_message <=", value, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageLike(String value) {
addCriterion("error_message like", value, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageNotLike(String value) {
addCriterion("error_message not like", value, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageIn(List<String> values) {
addCriterion("error_message in", values, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageNotIn(List<String> values) {
addCriterion("error_message not in", values, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageBetween(String value1, String value2) {
addCriterion("error_message between", value1, value2, "errorMessage");
return (Criteria) this;
}
public Criteria andErrorMessageNotBetween(String value1, String value2) {
addCriterion("error_message not between", value1, value2, "errorMessage");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -18,9 +18,10 @@
<result column="start_time" jdbcType="BIGINT" property="startTime" />
<result column="end_time" jdbcType="BIGINT" property="endTime" />
<result column="executor" jdbcType="VARCHAR" property="executor" />
<result column="deleted" jdbcType="BIT" property="deleted" />
<result column="collection_id" jdbcType="VARCHAR" property="collectionId" />
<result column="deleted" jdbcType="BIT" property="deleted" />
<result column="case_id" jdbcType="VARCHAR" property="caseId" />
<result column="error_message" jdbcType="VARCHAR" property="errorMessage" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -83,7 +84,7 @@
<sql id="Base_Column_List">
id, task_id, resource_id, resource_name, task_origin, `status`, `result`, resource_pool_id,
resource_pool_node, resource_type, project_id, organization_id, thread_id, start_time,
end_time, executor, deleted, collection_id, case_id
end_time, executor, collection_id, deleted, case_id, error_message
</sql>
<select id="selectByExample" parameterType="io.metersphere.system.domain.ExecTaskItemExample" resultMap="BaseResultMap">
select
@ -121,15 +122,15 @@
`result`, resource_pool_id, resource_pool_node,
resource_type, project_id, organization_id,
thread_id, start_time, end_time,
executor, deleted, collection_id,
case_id)
executor, collection_id, deleted,
case_id, error_message)
values (#{id,jdbcType=VARCHAR}, #{taskId,jdbcType=VARCHAR}, #{resourceId,jdbcType=VARCHAR},
#{resourceName,jdbcType=VARCHAR}, #{taskOrigin,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR},
#{result,jdbcType=VARCHAR}, #{resourcePoolId,jdbcType=VARCHAR}, #{resourcePoolNode,jdbcType=VARCHAR},
#{resourceType,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{organizationId,jdbcType=VARCHAR},
#{threadId,jdbcType=VARCHAR}, #{startTime,jdbcType=BIGINT}, #{endTime,jdbcType=BIGINT},
#{executor,jdbcType=VARCHAR}, #{deleted,jdbcType=BIT}, #{collectionId,jdbcType=VARCHAR},
#{caseId,jdbcType=VARCHAR})
#{executor,jdbcType=VARCHAR}, #{collectionId,jdbcType=VARCHAR}, #{deleted,jdbcType=BIT},
#{caseId,jdbcType=VARCHAR}, #{errorMessage,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.system.domain.ExecTaskItem">
insert into exec_task_item
@ -182,15 +183,18 @@
<if test="executor != null">
executor,
</if>
<if test="deleted != null">
deleted,
</if>
<if test="collectionId != null">
collection_id,
</if>
<if test="deleted != null">
deleted,
</if>
<if test="caseId != null">
case_id,
</if>
<if test="errorMessage != null">
error_message,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -241,15 +245,18 @@
<if test="executor != null">
#{executor,jdbcType=VARCHAR},
</if>
<if test="deleted != null">
#{deleted,jdbcType=BIT},
</if>
<if test="collectionId != null">
#{collectionId,jdbcType=VARCHAR},
</if>
<if test="deleted != null">
#{deleted,jdbcType=BIT},
</if>
<if test="caseId != null">
#{caseId,jdbcType=VARCHAR},
</if>
<if test="errorMessage != null">
#{errorMessage,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.system.domain.ExecTaskItemExample" resultType="java.lang.Long">
@ -309,15 +316,18 @@
<if test="record.executor != null">
executor = #{record.executor,jdbcType=VARCHAR},
</if>
<if test="record.deleted != null">
deleted = #{record.deleted,jdbcType=BIT},
</if>
<if test="record.collectionId != null">
collection_id = #{record.collectionId,jdbcType=VARCHAR},
</if>
<if test="record.deleted != null">
deleted = #{record.deleted,jdbcType=BIT},
</if>
<if test="record.caseId != null">
case_id = #{record.caseId,jdbcType=VARCHAR},
</if>
<if test="record.errorMessage != null">
error_message = #{record.errorMessage,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -341,9 +351,10 @@
start_time = #{record.startTime,jdbcType=BIGINT},
end_time = #{record.endTime,jdbcType=BIGINT},
executor = #{record.executor,jdbcType=VARCHAR},
deleted = #{record.deleted,jdbcType=BIT},
collection_id = #{record.collectionId,jdbcType=VARCHAR},
case_id = #{record.caseId,jdbcType=VARCHAR}
deleted = #{record.deleted,jdbcType=BIT},
case_id = #{record.caseId,jdbcType=VARCHAR},
error_message = #{record.errorMessage,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -396,15 +407,18 @@
<if test="executor != null">
executor = #{executor,jdbcType=VARCHAR},
</if>
<if test="deleted != null">
deleted = #{deleted,jdbcType=BIT},
</if>
<if test="collectionId != null">
collection_id = #{collectionId,jdbcType=VARCHAR},
</if>
<if test="deleted != null">
deleted = #{deleted,jdbcType=BIT},
</if>
<if test="caseId != null">
case_id = #{caseId,jdbcType=VARCHAR},
</if>
<if test="errorMessage != null">
error_message = #{errorMessage,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
@ -425,16 +439,17 @@
start_time = #{startTime,jdbcType=BIGINT},
end_time = #{endTime,jdbcType=BIGINT},
executor = #{executor,jdbcType=VARCHAR},
deleted = #{deleted,jdbcType=BIT},
collection_id = #{collectionId,jdbcType=VARCHAR},
case_id = #{caseId,jdbcType=VARCHAR}
deleted = #{deleted,jdbcType=BIT},
case_id = #{caseId,jdbcType=VARCHAR},
error_message = #{errorMessage,jdbcType=VARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into exec_task_item
(id, task_id, resource_id, resource_name, task_origin, `status`, `result`, resource_pool_id,
resource_pool_node, resource_type, project_id, organization_id, thread_id, start_time,
end_time, executor, deleted, collection_id, case_id)
end_time, executor, collection_id, deleted, case_id, error_message)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.taskId,jdbcType=VARCHAR}, #{item.resourceId,jdbcType=VARCHAR},
@ -442,8 +457,8 @@
#{item.result,jdbcType=VARCHAR}, #{item.resourcePoolId,jdbcType=VARCHAR}, #{item.resourcePoolNode,jdbcType=VARCHAR},
#{item.resourceType,jdbcType=VARCHAR}, #{item.projectId,jdbcType=VARCHAR}, #{item.organizationId,jdbcType=VARCHAR},
#{item.threadId,jdbcType=VARCHAR}, #{item.startTime,jdbcType=BIGINT}, #{item.endTime,jdbcType=BIGINT},
#{item.executor,jdbcType=VARCHAR}, #{item.deleted,jdbcType=BIT}, #{item.collectionId,jdbcType=VARCHAR},
#{item.caseId,jdbcType=VARCHAR})
#{item.executor,jdbcType=VARCHAR}, #{item.collectionId,jdbcType=VARCHAR}, #{item.deleted,jdbcType=BIT},
#{item.caseId,jdbcType=VARCHAR}, #{item.errorMessage,jdbcType=VARCHAR})
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
@ -504,15 +519,18 @@
<if test="'executor'.toString() == column.value">
#{item.executor,jdbcType=VARCHAR}
</if>
<if test="'deleted'.toString() == column.value">
#{item.deleted,jdbcType=BIT}
</if>
<if test="'collection_id'.toString() == column.value">
#{item.collectionId,jdbcType=VARCHAR}
</if>
<if test="'deleted'.toString() == column.value">
#{item.deleted,jdbcType=BIT}
</if>
<if test="'case_id'.toString() == column.value">
#{item.caseId,jdbcType=VARCHAR}
</if>
<if test="'error_message'.toString() == column.value">
#{item.errorMessage,jdbcType=VARCHAR}
</if>
</foreach>
)
</foreach>

View File

@ -78,6 +78,8 @@ ALTER TABLE exec_task_item ADD COLUMN case_id VARCHAR(50) COMMENT '用例表id';
CREATE INDEX idx_case_id ON exec_task_item(case_id);
-- 任务项添加测试集字段
ALTER TABLE exec_task_item ADD collection_id varchar(50) NULL COMMENT '测试集ID';
-- 任务项添加异常信息字段
ALTER TABLE exec_task_item ADD error_message varchar(50) NULL COMMENT '异常信息';
-- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -0,0 +1,9 @@
package io.metersphere.sdk.constants;
/**
* 任务项执行异常类型
* @author jianxing
*/
public enum TaskItemErrorMessage {
INVALID_RESOURCE_POOL, CASE_NOT_EXIST
}

View File

@ -560,3 +560,7 @@ user_view.my_follow=我关注的
user_view.my_create=我创建的
user_view.my_review=我评审的
user_view_exist=视图已存在
#task_error_message
task_error_message.invalid_resource_pool=没有可用的资源池
task_error_message.case_not_exist=用例不存在

View File

@ -599,3 +599,7 @@ user_view.my_create=I created
user_view.my_review=I review
user_view.archived=Archived
user_view_exist=The view already exists
#task_error_message
task_error_message.invalid_resource_pool=There is no resource pool available
task_error_message.case_not_exist=The case doesn't exist

View File

@ -594,3 +594,7 @@ user_view.my_create=我创建的
user_view.my_review=我评审的
user_view.archived=已归档
user_view_exist=视图已存在
#task_error_message
task_error_message.invalid_resource_pool=没有可用的资源池
task_error_message.case_not_exist=用例不存在

View File

@ -594,3 +594,7 @@ user_view.my_create=我創建的
user_view.my_review=我評審的
user_view.archived=已歸檔
user_view_exist=視圖已存在
#task_error_message
task_error_message.invalid_resource_pool=沒有可用的資源池
task_error_message.case_not_exist=用例不存在

View File

@ -16,7 +16,9 @@ public enum ApiResultCode implements IResultCode {
API_DEFINITION_MOCK_EXIST(104007, "api_definition_mock_exist"),
API_SCENARIO_EXIST(104008, "api_scenario_exist"),
API_RESPONSE_NAME_CODE_UNIQUE(104009, "api_response_name_code_unique"),
API_SCENARIO_CIRCULAR_REFERENCE(104010, "api_scenario_circular_reference_error")
API_SCENARIO_CIRCULAR_REFERENCE(104010, "api_scenario_circular_reference_error"),
TASK_ERROR_MESSAGE_INVALID_RESOURCE_POOL(104011, "task_error_message.invalid_resource_pool"),
CASE_NOT_EXIST(104012, "task_error_message.case_not_exist"),
;

View File

@ -8,11 +8,14 @@ import io.metersphere.api.service.definition.ApiTestCaseBatchRunService;
import io.metersphere.api.service.definition.ApiTestCaseRunService;
import io.metersphere.sdk.constants.ApiExecuteResourceType;
import io.metersphere.sdk.constants.ApiExecuteRunMode;
import io.metersphere.sdk.constants.TaskItemErrorMessage;
import io.metersphere.sdk.dto.api.task.GetRunScriptRequest;
import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.BooleanUtils;
@ -33,7 +36,8 @@ public class ApiCaseExecuteCallbackService implements ApiExecuteCallbackService
private ApiTestCaseMapper apiTestCaseMapper;
@Resource
private ApiTestCaseBatchRunService apiTestCaseBatchRunService;
@Resource
private ApiCommonService apiCommonService;
public ApiCaseExecuteCallbackService() {
ApiExecuteCallbackServiceInvoker.register(ApiExecuteResourceType.API_CASE, this);
@ -45,6 +49,10 @@ public class ApiCaseExecuteCallbackService implements ApiExecuteCallbackService
@Override
public GetRunScriptResult getRunScript(GetRunScriptRequest request) {
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(request.getTaskItem().getResourceId());
if (apiTestCase == null || apiTestCase.getDeleted()) {
apiCommonService.updateTaskItemErrorMassage(request.getTaskItem().getId(), TaskItemErrorMessage.CASE_NOT_EXIST);
throw new MSException(Translator.get("task_error_message.case_not_exist"));
}
String reportId = initReport(request, apiTestCase);
GetRunScriptResult result = apiTestCaseRunService.getRunScript(request, apiTestCase);
result.setReportId(reportId);

View File

@ -31,18 +31,30 @@ import io.metersphere.project.service.FileAssociationService;
import io.metersphere.project.service.FileMetadataService;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.ExecStatus;
import io.metersphere.sdk.constants.TaskItemErrorMessage;
import io.metersphere.sdk.dto.api.task.GetRunScriptRequest;
import io.metersphere.sdk.dto.api.task.TaskBatchRequestDTO;
import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.domain.ExecTask;
import io.metersphere.system.domain.ExecTaskItem;
import io.metersphere.system.mapper.ExecTaskItemMapper;
import io.metersphere.system.mapper.ExecTaskMapper;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
@ -65,6 +77,10 @@ public class ApiCommonService {
private FileMetadataService fileMetadataService;
@Resource
private CustomFunctionService customFunctionService;
@Resource
private ExecTaskItemMapper execTaskItemMapper;
@Resource
private ExecTaskMapper execTaskMapper;
/**
* 根据 fileId 查找 MsHTTPElement 中的 ApiFile
@ -511,4 +527,55 @@ public class ApiCommonService {
apiReportRelateTask.setTaskResourceId(taskItemId);
return apiReportRelateTask;
}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void batchUpdateTaskItemErrorMassage(TaskItemErrorMessage errorMessage, TaskBatchRequestDTO batchRequest) {
SqlSessionFactory sqlSessionFactory = CommonBeanFactory.getBean(SqlSessionFactory.class);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
if (CollectionUtils.isNotEmpty(batchRequest.getTaskItems())) {
ExecTaskItemMapper batchExecTaskItemMapper = sqlSession.getMapper(ExecTaskItemMapper.class);
batchRequest.getTaskItems().forEach(taskItem -> {
// 更新任务项的异常信息
ExecTaskItem execTaskItem = new ExecTaskItem();
execTaskItem.setId(taskItem.getId());
execTaskItem.setErrorMessage(errorMessage.name());
batchExecTaskItemMapper.updateByPrimaryKeySelective(execTaskItem);
});
}
} finally {
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void updateTaskItemErrorMassage(String taskItemId, TaskItemErrorMessage errorMessage) {
// 更新任务项的异常信息
ExecTaskItem execTaskItem = new ExecTaskItem();
execTaskItem.setId(taskItemId);
execTaskItem.setErrorMessage(errorMessage.name());
execTaskItemMapper.updateByPrimaryKeySelective(execTaskItem);
}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void updateTaskRunningStatus(String taskId) {
ExecTask execTask = new ExecTask();
execTask.setId(taskId);
execTask.setStartTime(System.currentTimeMillis());
execTask.setStatus(ExecStatus.RUNNING.name());
execTaskMapper.updateByPrimaryKeySelective(execTask);
}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void updateTaskItemRunningStatus(GetRunScriptRequest request) {
TaskItem taskItem = request.getTaskItem();
// 更新任务项状态
ExecTaskItem execTaskItem = new ExecTaskItem();
execTaskItem.setId(taskItem.getId());
execTaskItem.setStartTime(System.currentTimeMillis());
execTaskItem.setStatus(ExecStatus.RUNNING.name());
execTaskItem.setThreadId(request.getThreadId());
execTaskItemMapper.updateByPrimaryKeySelective(execTaskItem);
}
}

View File

@ -4,17 +4,15 @@ import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker;
import io.metersphere.api.service.definition.ApiReportService;
import io.metersphere.api.service.scenario.ApiScenarioReportService;
import io.metersphere.api.utils.TaskRunningCache;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.constants.ApiExecuteResourceType;
import io.metersphere.sdk.constants.ApiExecuteRunMode;
import io.metersphere.sdk.dto.api.task.GetRunScriptRequest;
import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.sdk.util.EnumValidator;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.domain.ExecTask;
import io.metersphere.system.domain.ExecTaskItem;
import io.metersphere.system.mapper.ExecTaskItemMapper;
import io.metersphere.system.mapper.ExecTaskMapper;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
@ -28,10 +26,6 @@ import java.util.Optional;
@Transactional(rollbackFor = Exception.class)
public class ApiExecuteResourceService {
@Resource
private ExecTaskMapper execTaskMapper;
@Resource
private ExecTaskItemMapper execTaskItemMapper;
@Resource
private ApiReportService apiReportService;
@Resource
@ -40,6 +34,8 @@ public class ApiExecuteResourceService {
private StringRedisTemplate stringRedisTemplate;
@Resource
private TaskRunningCache taskRunningCache;
@Resource
private ApiCommonService apiCommonService;
public GetRunScriptResult getRunScript(GetRunScriptRequest request) {
@ -79,15 +75,15 @@ public class ApiExecuteResourceService {
// 设置缓存成功说明是第一个任务则设置任务的开始时间和运行状态
if (taskRunningCache.setIfAbsent(taskId)) {
// 将任务状态更新为运行中
updateTaskRunningStatus(taskId);
apiCommonService.updateTaskRunningStatus(taskId);
}
} else {
// 非批量时直接更新任务状态
updateTaskRunningStatus(taskId);
apiCommonService.updateTaskRunningStatus(taskId);
}
// 更新任务项状态
updateTaskItemRunningStatus(request);
apiCommonService.updateTaskItemRunningStatus(request);
// 非调试执行更新报告状态
switch (apiExecuteResourceType) {
@ -98,23 +94,4 @@ public class ApiExecuteResourceService {
default -> throw new MSException("不支持的资源类型: " + request.getResourceType());
}
}
private void updateTaskRunningStatus(String taskId) {
ExecTask execTask = new ExecTask();
execTask.setId(taskId);
execTask.setStartTime(System.currentTimeMillis());
execTask.setStatus(ExecStatus.RUNNING.name());
execTaskMapper.updateByPrimaryKeySelective(execTask);
}
private void updateTaskItemRunningStatus(GetRunScriptRequest request) {
TaskItem taskItem = request.getTaskItem();
// 更新任务项状态
ExecTaskItem execTaskItem = new ExecTaskItem();
execTaskItem.setId(taskItem.getId());
execTaskItem.setStartTime(System.currentTimeMillis());
execTaskItem.setStatus(ExecStatus.RUNNING.name());
execTaskItem.setThreadId(request.getThreadId());
execTaskItemMapper.updateByPrimaryKeySelective(execTaskItem);
}
}

View File

@ -214,10 +214,7 @@ public class ApiExecuteService {
*/
public TaskRequestDTO execute(TaskRequestDTO taskRequest) {
TaskInfo taskInfo = taskRequest.getTaskInfo();
TaskItem taskItem = taskRequest.getTaskItem();
try {
taskInfo = setTaskRequestParams(taskInfo);
// 获取资源池
@ -231,39 +228,10 @@ public class ApiExecuteService {
// 判断是否为 K8S 资源池
boolean isK8SResourcePool = StringUtils.equals(testResourcePoolDTO.getType(), ResourcePoolTypeEnum.K8S.getName());
boolean isDebugMode = ApiExecuteRunMode.isDebug(taskInfo.getRunMode());
if (isK8SResourcePool) {
TestResourceDTO testResourceDTO = new TestResourceDTO();
BeanUtils.copyBean(testResourceDTO, testResourcePoolDTO.getTestResourceReturnDTO());
testResourceDTO.setId(testResourcePoolDTO.getId());
taskInfo.setPerTaskSize(testResourceDTO.getPodThreads());
taskInfo.setPoolSize(testResourceDTO.getConcurrentNumber());
LogUtils.info("开始发送请求【 {}_{} 】到 K8S 资源池执行", taskItem.getReportId(), taskItem.getResourceId());
if (isDebugMode) {
EngineFactory.debugApi(taskRequest, testResourceDTO);
k8sExecute(taskRequest, testResourcePoolDTO);
} else {
EngineFactory.runApi(taskRequest, testResourceDTO);
}
} else {
if (CollectionUtils.isEmpty(testResourcePoolDTO.getTestResourceReturnDTO().getNodesList())) {
throw new MSException(ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG);
}
TestResourceNodeDTO testResourceNodeDTO = getNextExecuteNode(testResourcePoolDTO);
if (!ApiExecuteRunMode.isDebug(taskInfo.getRunMode())) {
updateTaskItemNodeInfo(taskItem, testResourcePoolDTO, testResourceNodeDTO, execTaskItemMapper);
}
taskInfo.setPerTaskSize(Optional.ofNullable(testResourceNodeDTO.getSingleTaskConcurrentNumber()).orElse(3));
taskInfo.setPoolSize(testResourceNodeDTO.getConcurrentNumber());
String endpoint = MsHttpClient.getEndpoint(testResourceNodeDTO.getIp(), testResourceNodeDTO.getPort());
LogUtils.info("开始发送请求【 {}_{} 】到 {} 节点执行", taskItem.getReportId(), taskItem.getResourceId(), endpoint);
if (isDebugMode) {
MsHttpClient.debugApi(endpoint, taskRequest);
} else {
MsHttpClient.runApi(endpoint, taskRequest);
}
nodeExecute(taskRequest, testResourcePoolDTO);
}
} catch (HttpServerErrorException e) {
@ -291,10 +259,87 @@ public class ApiExecuteService {
return taskRequest;
}
private void updateTaskItemNodeInfo(TaskItem taskItem, TestResourcePoolReturnDTO testResourcePoolDTO, TestResourceNodeDTO testResourceNodeDTO, ExecTaskItemMapper execTaskItemMapper) {
private void nodeExecute(TaskRequestDTO taskRequest, TestResourcePoolReturnDTO testResourcePoolDTO) throws Exception {
TaskItem taskItem = taskRequest.getTaskItem();
TaskInfo taskInfo = taskRequest.getTaskInfo();
if (CollectionUtils.isEmpty(testResourcePoolDTO.getTestResourceReturnDTO().getNodesList())) {
throw new MSException(ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG);
}
Set<String> invalidNodeSet = new HashSet<>();
Exception lastException = null;
while (true) {
TestResourceNodeDTO testResourceNodeDTO = getNextExecuteNode(testResourcePoolDTO);
taskInfo.setPerTaskSize(Optional.ofNullable(testResourceNodeDTO.getSingleTaskConcurrentNumber()).orElse(3));
taskInfo.setPoolSize(testResourceNodeDTO.getConcurrentNumber());
String endpoint = MsHttpClient.getEndpoint(testResourceNodeDTO.getIp(), testResourceNodeDTO.getPort());
LogUtils.info("开始发送请求【 {}_{} 】到 {} 节点执行", taskItem.getReportId(), taskItem.getResourceId(), endpoint);
if (invalidNodeSet.contains(endpoint)) {
// 全部节点都异常更新任务项状态
if (!ApiExecuteRunMode.isDebug(taskInfo.getRunMode())) {
apiCommonService.updateTaskItemErrorMassage(taskItem.getId(), TaskItemErrorMessage.INVALID_RESOURCE_POOL);
}
throw lastException;
}
try {
if (ApiExecuteRunMode.isDebug(taskInfo.getRunMode())) {
MsHttpClient.debugApi(endpoint, taskRequest);
} else {
MsHttpClient.runApi(endpoint, taskRequest);
// 更新状态项资源池信息
updateTaskItemNodeInfo(taskItem.getId(), testResourcePoolDTO, testResourceNodeDTO, execTaskItemMapper);
}
break;
} catch (Exception e) {
lastException = e;
LogUtils.error(e);
// 记录异常节点重试
invalidNodeSet.add(endpoint);
}
}
}
private void k8sExecute(TaskRequestDTO taskRequest, TestResourcePoolReturnDTO testResourcePoolDTO) throws Exception {
TaskItem taskItem = taskRequest.getTaskItem();
TaskInfo taskInfo = taskRequest.getTaskInfo();
TestResourceDTO testResourceDTO = new TestResourceDTO();
BeanUtils.copyBean(testResourceDTO, testResourcePoolDTO.getTestResourceReturnDTO());
testResourceDTO.setId(testResourcePoolDTO.getId());
taskInfo.setPerTaskSize(testResourceDTO.getPodThreads());
taskInfo.setPoolSize(testResourceDTO.getConcurrentNumber());
LogUtils.info("开始发送请求【 {}_{} 】到 K8S 资源池执行", taskItem.getReportId(), taskItem.getResourceId());
if (ApiExecuteRunMode.isDebug(taskInfo.getRunMode())) {
EngineFactory.debugApi(taskRequest, testResourceDTO);
} else {
try {
EngineFactory.runApi(taskRequest, testResourceDTO);
} catch (Exception e) {
apiCommonService.updateTaskItemErrorMassage(taskItem.getId(), TaskItemErrorMessage.INVALID_RESOURCE_POOL);
throw e;
}
}
}
private void batchUpdateTaskItemNodeInfo(TestResourcePoolReturnDTO testResourcePool, TestResourceNodeDTO testResourceNode, TaskBatchRequestDTO batchRequest) {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
if (CollectionUtils.isNotEmpty(batchRequest.getTaskItems())) {
ExecTaskItemMapper batchExecTaskItemMapper = sqlSession.getMapper(ExecTaskItemMapper.class);
batchRequest.getTaskItems().forEach(taskItem -> {
// 非调试模式更新任务项的资源池信息
updateTaskItemNodeInfo(taskItem.getId(), testResourcePool, testResourceNode, batchExecTaskItemMapper);
});
}
} finally {
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
}
private void updateTaskItemNodeInfo(String taskItemId, TestResourcePoolReturnDTO testResourcePoolDTO, TestResourceNodeDTO testResourceNodeDTO, ExecTaskItemMapper execTaskItemMapper) {
// 非调试模式更新任务项的资源池信息
ExecTaskItem execTaskItem = new ExecTaskItem();
execTaskItem.setId(taskItem.getId());
execTaskItem.setId(taskItemId);
execTaskItem.setResourcePoolId(testResourcePoolDTO.getId());
execTaskItem.setResourcePoolNode(StringUtils.join(testResourceNodeDTO.getIp(), ":", testResourceNodeDTO.getPort()));
execTaskItemMapper.updateByPrimaryKeySelective(execTaskItem);
@ -319,6 +364,71 @@ public class ApiExecuteService {
// 判断是否为 K8S 资源池
boolean isK8SResourcePool = StringUtils.equals(testResourcePool.getType(), ResourcePoolTypeEnum.K8S.getName());
if (isK8SResourcePool) {
k8sBatchExecute(taskRequest, taskInfo, testResourcePool);
} else {
nodeBatchExecute(taskRequest, taskInfo, testResourcePool);
}
}
private void nodeBatchExecute(TaskBatchRequestDTO taskRequest, TaskInfo taskInfo, TestResourcePoolReturnDTO testResourcePool) {
// 将任务按资源池的数量拆分
List<TestResourceNodeDTO> nodesList = testResourcePool.getTestResourceReturnDTO().getNodesList();
if (CollectionUtils.isEmpty(nodesList)) {
throw new MSException(ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG);
}
// 按资源池节点数量预分组
List<TaskBatchRequestDTO> distributeTasks = getDistributeTaskBatchRequest(taskRequest, nodesList.size());
Iterator<TestResourceNodeDTO> nodeIterator = nodesList.iterator();
Iterator<TaskBatchRequestDTO> distributeTaskIterator = distributeTasks.iterator();
Set<TestResourceNodeDTO> invalidNodeSet = new HashSet<>();
while (distributeTaskIterator.hasNext()) {
TaskBatchRequestDTO subTaskRequest = distributeTaskIterator.next();
boolean success = false;
while (nodeIterator.hasNext()) {
TestResourceNodeDTO testResourceNode = nodeIterator.next();
String endpoint = MsHttpClient.getEndpoint(testResourceNode.getIp(), testResourceNode.getPort());
subTaskRequest.getTaskInfo().setPerTaskSize(Optional.ofNullable(testResourceNode.getSingleTaskConcurrentNumber()).orElse(3));
subTaskRequest.getTaskInfo().setPoolSize(testResourceNode.getConcurrentNumber());
try {
List<String> taskKeys = subTaskRequest.getTaskItems().stream()
.map(TaskItem::getId)
.toList();
LogUtils.info("开始发送批量任务到 {} 节点执行:\n" + taskKeys, endpoint);
MsHttpClient.batchRunApi(endpoint, subTaskRequest);
success = true;
// 更新任务项的资源池信息
if (!ApiExecuteRunMode.isDebug(taskInfo.getRunMode())) {
batchUpdateTaskItemNodeInfo(testResourcePool, testResourceNode, subTaskRequest);
}
break;
} catch (Exception e) {
invalidNodeSet.add(testResourceNode);
LogUtils.error("发送批量任务到 {} 节点执行失败", endpoint);
LogUtils.error(e);
}
if (!nodeIterator.hasNext()) {
// 去除异常节点
invalidNodeSet.forEach(nodesList::remove);
// 资源池不够用重新遍历资源池
nodeIterator = nodesList.iterator();
}
}
if (!success) {
// 更新任务项错误信息
if (!ApiExecuteRunMode.isDebug(taskInfo.getRunMode())) {
apiCommonService.batchUpdateTaskItemErrorMassage(TaskItemErrorMessage.INVALID_RESOURCE_POOL, subTaskRequest);
} else {
throw new MSException(ApiResultCode.TASK_ERROR_MESSAGE_INVALID_RESOURCE_POOL);
}
}
}
}
private void k8sBatchExecute(TaskBatchRequestDTO taskRequest, TaskInfo taskInfo, TestResourcePoolReturnDTO testResourcePool) {
TestResourceDTO testResourceDTO = new TestResourceDTO();
BeanUtils.copyBean(testResourceDTO, testResourcePool.getTestResourceReturnDTO());
testResourceDTO.setId(testResourcePool.getId());
@ -329,66 +439,25 @@ public class ApiExecuteService {
EngineFactory.batchRunApi(taskRequest, testResourceDTO);
} catch (Exception e) {
LogUtils.error(e);
apiCommonService.batchUpdateTaskItemErrorMassage(TaskItemErrorMessage.INVALID_RESOURCE_POOL, taskRequest);
}
} else {
// 将任务按资源池的数量拆分
List<TestResourceNodeDTO> nodesList = testResourcePool.getTestResourceReturnDTO().getNodesList();
if (CollectionUtils.isEmpty(nodesList)) {
throw new MSException(ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG);
}
List<TaskBatchRequestDTO> distributeTasks = new ArrayList<>(nodesList.size());
private List<TaskBatchRequestDTO> getDistributeTaskBatchRequest(TaskBatchRequestDTO taskRequest, int distributeSize) {
List<TaskBatchRequestDTO> distributeTasks = new ArrayList<>(distributeSize);
for (int i = 0; i < taskRequest.getTaskItems().size(); i++) {
TaskBatchRequestDTO distributeTask;
int nodeIndex = i % nodesList.size();
if (distributeTasks.size() < nodesList.size()) {
int nodeIndex = i % distributeSize;
if (distributeTasks.size() < distributeSize) {
distributeTask = BeanUtils.copyBean(new TaskBatchRequestDTO(), taskRequest);
distributeTask.setTaskItems(new ArrayList<>());
distributeTasks.add(distributeTask);
} else {
distributeTask = distributeTasks.get(nodeIndex);
}
taskInfo.setPerTaskSize(Optional.ofNullable(nodesList.get(nodeIndex).getSingleTaskConcurrentNumber()).orElse(3));
distributeTask.getTaskInfo().setPoolSize(nodesList.get(nodeIndex).getConcurrentNumber());
distributeTask.getTaskItems().add(taskRequest.getTaskItems().get(i));
}
for (int i = 0; i < nodesList.size(); i++) {
// todo 优化某个资源池不可用的情况以及清理 executionSet
TestResourceNodeDTO testResourceNode = nodesList.get(i);
TaskBatchRequestDTO subTaskRequest = distributeTasks.get(i);
String endpoint = MsHttpClient.getEndpoint(testResourceNode.getIp(), testResourceNode.getPort());
if (!ApiExecuteRunMode.isDebug(taskInfo.getRunMode())) {
batchUpdateTaskItemNodeInfo(testResourcePool, testResourceNode, subTaskRequest);
}
try {
List<String> taskKeys = subTaskRequest.getTaskItems().stream()
.map(TaskItem::getId)
.toList();
LogUtils.info("开始发送批量任务到 {} 节点执行:\n" + taskKeys, endpoint);
MsHttpClient.batchRunApi(endpoint, subTaskRequest);
} catch (Exception e) {
LogUtils.error("发送批量任务到 {} 节点执行失败", endpoint);
LogUtils.error(e);
}
}
}
}
private void batchUpdateTaskItemNodeInfo(TestResourcePoolReturnDTO testResourcePool, TestResourceNodeDTO testResourceNode, TaskBatchRequestDTO batchRequest) {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
if (CollectionUtils.isNotEmpty(batchRequest.getTaskItems())) {
ExecTaskItemMapper batchExecTaskItemMapper = sqlSession.getMapper(ExecTaskItemMapper.class);
batchRequest.getTaskItems().forEach(taskItem -> {
// 非调试模式更新任务项的资源池信息
updateTaskItemNodeInfo(taskItem, testResourcePool, testResourceNode, batchExecTaskItemMapper);
});
}
} finally {
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
return distributeTasks;
}
protected static boolean validate() {
@ -759,14 +828,14 @@ public class ApiExecuteService {
testElement.setProjectId(projectId);
}
public ApiParamConfig getApiParamConfig(String reportId) {
public ApiParamConfig getApiParamConfig() {
ApiParamConfig paramConfig = new ApiParamConfig();
paramConfig.setTestElementClassPluginIdMap(apiPluginService.getTestElementPluginMap());
paramConfig.setTestElementClassProtocolMap(apiPluginService.getTestElementProtocolMap());
return paramConfig;
}
public ApiParamConfig getApiParamConfig(String reportId, String projectId) {
public ApiParamConfig getApiParamConfig(String projectId) {
ApiParamConfig paramConfig = new ApiParamConfig();
paramConfig.setTestElementClassPluginIdMap(apiPluginService.getTestElementPluginMap());
paramConfig.setTestElementClassProtocolMap(apiPluginService.getTestElementProtocolMap());

View File

@ -39,7 +39,7 @@ public class ApiScenarioExecuteCallbackService implements ApiExecuteCallbackServ
*/
@Override
public GetRunScriptResult getRunScript(GetRunScriptRequest request) {
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(request.getTaskItem().getResourceId());
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRunWithTaskItemErrorMassage(request.getTaskItem().getId(), request.getTaskItem().getResourceId());
String reportId = initReport(request, apiScenarioDetail);
GetRunScriptResult result = apiScenarioRunService.getRunScript(request, apiScenarioDetail);
result.setReportId(reportId);

View File

@ -211,7 +211,7 @@ public class ApiDebugService extends MoveNodeService {
public TaskRequestDTO debug(ApiDebugRunRequest request) {
ApiResourceRunRequest runRequest = apiExecuteService.getApiResourceRunRequest(request);
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(request.getReportId());
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig();
TaskRequestDTO taskRequest = apiExecuteService.getTaskRequest(request.getReportId(), request.getId(), request.getProjectId());
TaskInfo taskInfo = taskRequest.getTaskInfo();

View File

@ -1191,7 +1191,7 @@ public class ApiDefinitionService extends MoveNodeService {
public TaskRequestDTO debug(ApiDefinitionRunRequest request) {
ApiResourceRunRequest runRequest = apiExecuteService.getApiResourceRunRequest(request);
EnvironmentInfoDTO environmentInfoDTO = environmentService.get(request.getEnvironmentId());
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(request.getReportId(), request.getProjectId());
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(request.getProjectId());
TaskRequestDTO taskRequest = apiExecuteService.getTaskRequest(request.getReportId(), request.getId(), request.getProjectId());
TaskInfo taskInfo = taskRequest.getTaskInfo();

View File

@ -156,7 +156,7 @@ public class ApiTestCaseRunService {
public TaskRequestDTO doExecute(TaskRequestDTO taskRequest, ApiResourceRunRequest runRequest, String apiDefinitionId, String envId) {
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(taskRequest.getTaskItem().getReportId(), taskRequest.getTaskInfo().getProjectId());
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(taskRequest.getTaskInfo().getProjectId());
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiDefinitionId);
@ -175,7 +175,7 @@ public class ApiTestCaseRunService {
TaskItem taskItem = request.getTaskItem();
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiTestCase.getApiDefinitionId());
ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(apiTestCase.getId());
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(taskItem.getReportId(), apiTestCase.getProjectId());
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(apiTestCase.getProjectId());
apiParamConfig.setRetryOnFail(request.getRunModeConfig().getRetryOnFail());
apiParamConfig.setRetryConfig(request.getRunModeConfig().getRetryConfig());

View File

@ -3,6 +3,7 @@ package io.metersphere.api.service.scenario;
import io.metersphere.api.constants.ApiResourceType;
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.ApiResourceRunRequest;
@ -34,6 +35,7 @@ import io.metersphere.project.service.EnvironmentGroupService;
import io.metersphere.project.service.EnvironmentService;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.dto.api.task.*;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.DateUtils;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
@ -266,6 +268,21 @@ public class ApiScenarioRunService {
return apiScenarioDetail;
}
public ApiScenarioDetail getForRunWithTaskItemErrorMassage(String taskItemId, String scenarioId) {
try {
ApiScenarioDetail apiScenarioDetail = apiScenarioService.get(scenarioId);
apiScenarioDetail.setSteps(filerDisableSteps(apiScenarioDetail.getSteps()));
return apiScenarioDetail;
} catch (MSException msException) {
if (msException.getErrorCode().equals(ApiResultCode.CASE_NOT_EXIST)) {
// 用例不存在记录任务项错误信息
apiCommonService.updateTaskItemErrorMassage(taskItemId, TaskItemErrorMessage.CASE_NOT_EXIST);
throw new MSException(ApiResultCode.CASE_NOT_EXIST);
}
throw msException;
}
}
/**
* 过滤掉禁用的步骤
*/

View File

@ -3,6 +3,7 @@ package io.metersphere.api.service.scenario;
import io.metersphere.api.constants.ApiResourceType;
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;
@ -1520,7 +1521,7 @@ public class ApiScenarioService extends MoveNodeService {
example.createCriteria().andIdEqualTo(id).andDeletedEqualTo(false);
List<ApiScenario> apiScenarios = apiScenarioMapper.selectByExample(example);
if (CollectionUtils.isEmpty(apiScenarios)) {
throw new MSException(Translator.get("api_scenario_is_not_exist"));
throw new MSException(ApiResultCode.CASE_NOT_EXIST);
}
return apiScenarios.getFirst();
}

View File

@ -1,11 +1,7 @@
package io.metersphere.system.dto.taskhub;
import io.metersphere.system.domain.ExecTaskItem;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
@ -40,4 +36,7 @@ public class TaskHubItemDTO extends ExecTaskItem {
@Schema(description = "组织名称")
private String organizationName;
@Schema(description = "错误信息")
private String errorMessage;
}

View File

@ -373,6 +373,9 @@ public class BaseTaskHubService {
item.setResourcePoolName(resourcePoolMaps.getOrDefault(item.getResourcePoolId(), StringUtils.EMPTY));
item.setProjectName(projectMaps.getOrDefault(item.getProjectId(), StringUtils.EMPTY));
item.setOrganizationName(organizationMaps.getOrDefault(item.getOrganizationId(), StringUtils.EMPTY));
if (StringUtils.isNotBlank(item.getErrorMessage())) {
item.setErrorMessage(Translator.get("task_error_message." + item.getErrorMessage().toLowerCase()));
}
});
}
@ -740,7 +743,7 @@ public class BaseTaskHubService {
public Map<String, Integer> getTaskItemOrder(List<String> taskIdItemIds) {
List<ExecTaskItem> taskItemIds = getTaskItemByIds(taskIdItemIds);
Map<String, List<ExecTaskItem>> nodeResourceMap = taskItemIds.stream()
.filter(item -> StringUtils.equals(item.getResourceType(), ResourcePoolTypeEnum.NODE.getName())) // 根据条件过滤
.filter(item -> StringUtils.contains(item.getResourcePoolNode(), ":")) // 根据条件过滤
.collect(Collectors.groupingBy(ExecTaskItem::getResourcePoolNode));
List<CompletableFuture<Map<String, Integer>>> futures = nodeResourceMap.keySet().stream()

View File

@ -1,18 +1,22 @@
package io.metersphere.plan.service;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker;
import io.metersphere.api.mapper.ApiTestCaseMapper;
import io.metersphere.api.service.ApiCommonService;
import io.metersphere.api.service.ApiExecuteCallbackService;
import io.metersphere.api.service.definition.ApiTestCaseRunService;
import io.metersphere.plan.domain.TestPlanReportApiCase;
import io.metersphere.plan.mapper.TestPlanReportApiCaseMapper;
import io.metersphere.sdk.constants.ApiExecuteResourceType;
import io.metersphere.sdk.constants.TaskItemErrorMessage;
import io.metersphere.sdk.dto.api.notice.ApiNoticeDTO;
import io.metersphere.sdk.dto.api.task.GetRunScriptRequest;
import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.exception.MSException;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -39,6 +43,9 @@ public class PlanRunApiCaseExecuteCallbackService implements ApiExecuteCallbackS
@Resource
private ApiTestCaseRunService apiTestCaseRunService;
@Resource
private ApiCommonService apiCommonService;
public PlanRunApiCaseExecuteCallbackService() {
ApiExecuteCallbackServiceInvoker.register(ApiExecuteResourceType.PLAN_RUN_API_CASE, this);
}
@ -49,7 +56,11 @@ public class PlanRunApiCaseExecuteCallbackService implements ApiExecuteCallbackS
@Override
public GetRunScriptResult getRunScript(GetRunScriptRequest request) {
TestPlanReportApiCase testPlanReportApiCase = testPlanReportApiCaseMapper.selectByPrimaryKey(request.getTaskItem().getResourceId());
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(testPlanReportApiCase.getApiCaseId());
ApiTestCase apiTestCase = testPlanReportApiCase == null ? null : apiTestCaseMapper.selectByPrimaryKey(testPlanReportApiCase.getApiCaseId());
if (testPlanReportApiCase == null || apiTestCase == null || apiTestCase.getDeleted()) {
apiCommonService.updateTaskItemErrorMassage(request.getTaskItem().getId(), TaskItemErrorMessage.CASE_NOT_EXIST);
throw new MSException(ApiResultCode.CASE_NOT_EXIST);
}
String reportId = initReport(request, testPlanReportApiCase, apiTestCase);
GetRunScriptResult result = apiTestCaseRunService.getRunScript(request, apiTestCase);
result.setReportId(reportId);

View File

@ -1,17 +1,21 @@
package io.metersphere.plan.service;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.dto.scenario.ApiScenarioDetail;
import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker;
import io.metersphere.api.service.ApiCommonService;
import io.metersphere.api.service.ApiExecuteCallbackService;
import io.metersphere.api.service.scenario.ApiScenarioRunService;
import io.metersphere.plan.domain.TestPlanReportApiScenario;
import io.metersphere.plan.mapper.TestPlanReportApiScenarioMapper;
import io.metersphere.sdk.constants.ApiExecuteResourceType;
import io.metersphere.sdk.constants.TaskItemErrorMessage;
import io.metersphere.sdk.dto.api.notice.ApiNoticeDTO;
import io.metersphere.sdk.dto.api.task.GetRunScriptRequest;
import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.exception.MSException;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -31,6 +35,8 @@ public class PlanRunApiScenarioExecuteCallbackService implements ApiExecuteCallb
private TestPlanReportApiScenarioMapper testPlanReportApiScenarioMapper;
@Resource
private ApiScenarioRunService apiScenarioRunService;
@Resource
private ApiCommonService apiCommonService;
public PlanRunApiScenarioExecuteCallbackService() {
ApiExecuteCallbackServiceInvoker.register(ApiExecuteResourceType.PLAN_RUN_API_SCENARIO, this);
@ -43,7 +49,11 @@ public class PlanRunApiScenarioExecuteCallbackService implements ApiExecuteCallb
public GetRunScriptResult getRunScript(GetRunScriptRequest request) {
String resourceId = request.getTaskItem().getResourceId();
TestPlanReportApiScenario testPlanReportApiScenario = testPlanReportApiScenarioMapper.selectByPrimaryKey(resourceId);
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(testPlanReportApiScenario.getApiScenarioId());
if (testPlanReportApiScenario == null) {
apiCommonService.updateTaskItemErrorMassage(request.getTaskItem().getId(), TaskItemErrorMessage.CASE_NOT_EXIST);
throw new MSException(ApiResultCode.CASE_NOT_EXIST);
}
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRunWithTaskItemErrorMassage(request.getTaskItem().getId(), testPlanReportApiScenario.getApiScenarioId());
apiScenarioDetail.setEnvironmentId(testPlanReportApiScenario.getEnvironmentId());
apiScenarioDetail.setGrouped(testPlanReportApiScenario.getGrouped());
GetRunScriptResult result = planRunTestPlanApiScenarioService.getRunScript(request);

View File

@ -1,19 +1,23 @@
package io.metersphere.plan.service;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.api.domain.ApiTestCaseRecord;
import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker;
import io.metersphere.api.mapper.ApiTestCaseMapper;
import io.metersphere.api.service.ApiCommonService;
import io.metersphere.api.service.ApiExecuteCallbackService;
import io.metersphere.api.service.definition.ApiTestCaseRunService;
import io.metersphere.plan.domain.TestPlanApiCase;
import io.metersphere.plan.mapper.TestPlanApiCaseMapper;
import io.metersphere.sdk.constants.ApiExecuteResourceType;
import io.metersphere.sdk.constants.TaskItemErrorMessage;
import io.metersphere.sdk.dto.api.notice.ApiNoticeDTO;
import io.metersphere.sdk.dto.api.task.GetRunScriptRequest;
import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.exception.MSException;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@ -36,6 +40,8 @@ public class TestPlanApiCaseExecuteCallbackService implements ApiExecuteCallback
private ApiTestCaseMapper apiTestCaseMapper;
@Resource
private ApiTestCaseRunService apiTestCaseRunService;
@Resource
private ApiCommonService apiCommonService;
public TestPlanApiCaseExecuteCallbackService() {
ApiExecuteCallbackServiceInvoker.register(ApiExecuteResourceType.TEST_PLAN_API_CASE, this);
@ -47,7 +53,11 @@ public class TestPlanApiCaseExecuteCallbackService implements ApiExecuteCallback
@Override
public GetRunScriptResult getRunScript(GetRunScriptRequest request) {
TestPlanApiCase testPlanApiCase = testPlanApiCaseMapper.selectByPrimaryKey(request.getTaskItem().getResourceId());
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId());
ApiTestCase apiTestCase = testPlanApiCase == null ? null : apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId());
if (testPlanApiCase == null || apiTestCase == null || apiTestCase.getDeleted()) {
apiCommonService.updateTaskItemErrorMassage(request.getTaskItem().getId(), TaskItemErrorMessage.CASE_NOT_EXIST);
throw new MSException(ApiResultCode.CASE_NOT_EXIST);
}
String reportId = initReport(request, testPlanApiCase, apiTestCase);
GetRunScriptResult result = apiTestCaseRunService.getRunScript(request, apiTestCase);
result.setReportId(reportId);

View File

@ -1,18 +1,22 @@
package io.metersphere.plan.service;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.dto.scenario.ApiScenarioDetail;
import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker;
import io.metersphere.api.service.ApiCommonService;
import io.metersphere.api.service.ApiExecuteCallbackService;
import io.metersphere.api.service.scenario.ApiScenarioRunService;
import io.metersphere.plan.domain.TestPlanApiScenario;
import io.metersphere.plan.mapper.TestPlanApiScenarioMapper;
import io.metersphere.sdk.constants.ApiExecuteResourceType;
import io.metersphere.sdk.constants.TaskItemErrorMessage;
import io.metersphere.sdk.dto.api.notice.ApiNoticeDTO;
import io.metersphere.sdk.dto.api.task.GetRunScriptRequest;
import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.exception.MSException;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@ -33,6 +37,8 @@ public class TestPlanApiScenarioExecuteCallbackService implements ApiExecuteCall
private TestPlanApiScenarioMapper testPlanApiScenarioMapper;
@Resource
private ApiScenarioRunService apiScenarioRunService;
@Resource
private ApiCommonService apiCommonService;
public TestPlanApiScenarioExecuteCallbackService() {
ApiExecuteCallbackServiceInvoker.register(ApiExecuteResourceType.TEST_PLAN_API_SCENARIO, this);
@ -45,7 +51,11 @@ public class TestPlanApiScenarioExecuteCallbackService implements ApiExecuteCall
public GetRunScriptResult getRunScript(GetRunScriptRequest request) {
TaskItem taskItem = request.getTaskItem();
TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(taskItem.getResourceId());
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(testPlanApiScenario.getApiScenarioId());
if (testPlanApiScenario == null) {
apiCommonService.updateTaskItemErrorMassage(request.getTaskItem().getId(), TaskItemErrorMessage.CASE_NOT_EXIST);
throw new MSException(ApiResultCode.CASE_NOT_EXIST);
}
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRunWithTaskItemErrorMassage(taskItem.getId(), testPlanApiScenario.getApiScenarioId());
apiScenarioDetail.setEnvironmentId(testPlanApiScenario.getEnvironmentId());
apiScenarioDetail.setGrouped(testPlanApiScenario.getGrouped());
String reportId = initReport(request, testPlanApiScenario, apiScenarioDetail);