feat(测试计划): 复制

This commit is contained in:
WangXu10 2024-05-08 11:13:21 +08:00 committed by Craftsman
parent 1fba073345
commit ef1b0c56f3
11 changed files with 260 additions and 38 deletions

View File

@ -19,6 +19,8 @@ ALTER TABLE operation_history MODIFY COLUMN module VARCHAR(100);
ALTER TABLE operation_log MODIFY COLUMN module VARCHAR(100);
CREATE INDEX idx_num ON test_plan_functional_case(num);
-- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -1,6 +1,7 @@
package io.metersphere.plan.controller;
import io.metersphere.plan.constants.TestPlanResourceConfig;
import io.metersphere.plan.domain.TestPlan;
import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanCountResponse;
import io.metersphere.plan.dto.response.TestPlanResponse;
@ -124,4 +125,14 @@ public class TestPlanController {
testPlanService.archived(id, userId);
}
@PostMapping("/copy")
@Operation(summary = "测试计划-复制测试计划")
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ_ADD)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@Log(type = OperationLogType.COPY, expression = "#msClass.copyLog(#request)", msClass = TestPlanLogService.class)
public TestPlan copy(@Validated @RequestBody TestPlanCopyRequest request) {
return testPlanService.copy(request, SessionUtils.getUserId());
}
}

View File

@ -20,15 +20,6 @@ public class BaseAssociateCaseRequest extends TableBatchProcessDTO implements Se
@Schema(description = "模块id")
private List<String> moduleIds;
/*@Schema(description = "类型:项目/测试计划/用例评审", allowableValues = {"PROJECT", "TEST_PLAN", "CASE_REVIEW"})
private String associateType;
@Schema(description = "类型id: 项目id/测试计划id/用例评审id")
private String associateTypeId;
@Schema(description = "用例类型: 功能用例/接口用例/接口场景用例", allowableValues = {"FUNCTIONAL", "API", "API_SCENARIO"})
private List<String> associateCaseType;*/
@Schema(description = "功能用例选中的ids")
private List<String> functionalSelectIds;

View File

@ -0,0 +1,16 @@
package io.metersphere.plan.dto.request;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* @author wx
*/
@Data
public class TestPlanCopyRequest extends TestPlanCreateRequest {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan.id.not_blank}")
private String testPlanId;
}

View File

@ -21,8 +21,7 @@ public class TestPlanCreateRequest {
@Size(min = 1, max = 50, message = "{test_plan.project_id.length_range}")
private String projectId;
@Schema(description = "测试计划组ID;测试计划要改为树结构。最上层的为root其余则是父节点ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan.parent_id.not_blank}")
@Schema(description = "测试计划组ID;测试计划要改为树结构。最上层的为NONE其余则是父节点ID")
@Size(min = 1, max = 50, message = "{test_plan.parent_id.length_range}")
private String groupId = TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID;
@ -32,8 +31,7 @@ public class TestPlanCreateRequest {
private String name;
@Schema(description = "测试计划模块ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan.module_id.not_blank}")
@Schema(description = "测试计划模块ID")
@Size(min = 1, max = 50, message = "{test_plan.parent_id.length_range}")
private String moduleId = ModuleConstants.DEFAULT_NODE_ID;

View File

@ -1,5 +1,7 @@
package io.metersphere.plan.dto.request;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.constants.TestPlanConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
@ -20,7 +22,7 @@ public class TestPlanUpdateRequest {
private String name;
@Schema(description = "模块ID")
private String moduleId;
private String moduleId = ModuleConstants.DEFAULT_NODE_ID;
@Schema(description = "标签")
private LinkedHashSet<String> tags;
@ -49,6 +51,6 @@ public class TestPlanUpdateRequest {
private Double passThreshold;
@Schema(description = "测试计划组Id")
private String testPlanGroupId;
private String testPlanGroupId = TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID;
}

View File

@ -22,6 +22,7 @@ import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.ServiceUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
@ -32,6 +33,9 @@ import org.springframework.validation.annotation.Validated;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
@ -104,7 +108,6 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService {
testPlanFunctionalCase.setPos(pox);
testPlanFunctionalCase.setCreateTime(now);
testPlanFunctionalCase.setCreateUser(associationParam.getOperator());
testPlanFunctionalCase.setExecuteUser(associationParam.getOperator());
testPlanFunctionalCaseList.add(testPlanFunctionalCase);
pox += ServiceUtils.POS_STEP;
}
@ -128,4 +131,43 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService {
testPlanResourceLogService.saveSortLog(testPlan, request.getDragNodeId(), new ResourceLogInsertModule(TestPlanResourceConstants.RESOURCE_FUNCTIONAL_CASE, logInsertModule));
return response;
}
/**
* 复制计划时复制功能用例
*
* @param ids
* @param testPlan
*/
public void saveTestPlanByPlanId(List<String> ids, TestPlan testPlan) {
TestPlanFunctionalCaseExample example = new TestPlanFunctionalCaseExample();
example.createCriteria().andTestPlanIdIn(ids);
List<TestPlanFunctionalCase> testPlanFunctionalCases = testPlanFunctionalCaseMapper.selectByExample(example);
if (CollectionUtils.isNotEmpty(testPlanFunctionalCases)) {
Map<String, List<TestPlanFunctionalCase>> collect = testPlanFunctionalCases.stream().collect(Collectors.groupingBy(TestPlanFunctionalCase::getTestPlanId));
List<TestPlanFunctionalCase> associateList = new ArrayList<>();
ids.forEach(id -> {
if (collect.containsKey(id)) {
saveCase(collect.get(id), associateList, testPlan, id);
}
});
testPlanFunctionalCaseMapper.batchInsert(associateList);
}
}
private void saveCase(List<TestPlanFunctionalCase> testPlanFunctionalCases, List<TestPlanFunctionalCase> associateList, TestPlan testPlan, String id) {
AtomicLong pos = new AtomicLong(this.getNextOrder(id));
testPlanFunctionalCases.forEach(item -> {
TestPlanFunctionalCase functionalCase = new TestPlanFunctionalCase();
functionalCase.setTestPlanId(testPlan.getId());
functionalCase.setId(IDGenerator.nextStr());
functionalCase.setNum(NumGenerator.nextNum(testPlan.getNum() + "_" + testPlan.getProjectId(), ApplicationNumScope.TEST_PLAN_FUNCTION_CASE));
functionalCase.setCreateTime(System.currentTimeMillis());
functionalCase.setCreateUser(testPlan.getCreateUser());
functionalCase.setFunctionalCaseId(item.getFunctionalCaseId());
functionalCase.setPos(pos.get());
associateList.add(functionalCase);
pos.updateAndGet(v -> v + ServiceUtils.POS_STEP);
});
}
}

View File

@ -1,6 +1,7 @@
package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlan;
import io.metersphere.plan.dto.request.TestPlanCopyRequest;
import io.metersphere.plan.mapper.TestPlanMapper;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper;
@ -148,4 +149,27 @@ public class TestPlanLogService {
dto.setOriginalValue(JSON.toJSONBytes(testPlan));
return dto;
}
/**
* 复制日志
*
* @param request
* @return
*/
public LogDTO copyLog(TestPlanCopyRequest request) {
LogDTO dto = new LogDTO(
request.getProjectId(),
null,
null,
null,
OperationLogType.COPY.name(),
logModule,
request.getName());
dto.setPath("/test-plan/copy");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(request));
return dto;
}
}

View File

@ -3,6 +3,7 @@ package io.metersphere.plan.service;
import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.TestPlanResourceAssociationParam;
import io.metersphere.plan.dto.request.TestPlanBatchProcessRequest;
import io.metersphere.plan.dto.request.TestPlanCopyRequest;
import io.metersphere.plan.dto.request.TestPlanCreateRequest;
import io.metersphere.plan.dto.request.TestPlanUpdateRequest;
import io.metersphere.plan.dto.response.TestPlanCountResponse;
@ -30,10 +31,7 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
@Service
@ -83,39 +81,62 @@ public class TestPlanService {
* @return
*/
public String add(TestPlanCreateRequest testPlanCreateRequest, String operator, String requestUrl, String requestMethod) {
TestPlan testPlan = savePlanDTO(testPlanCreateRequest, operator, null);
testPlanLogService.saveAddLog(testPlan, operator, requestUrl, requestMethod);
return testPlan.getId();
}
/**
* 保存数据
*
* @param createOrCopyRequest
* @param operator
* @param id 复制的计划/计划组id 判断新增还是复制
*/
private TestPlan savePlanDTO(TestPlanCreateRequest createOrCopyRequest, String operator, String id) {
//检查模块的合法性
this.checkModule(testPlanCreateRequest.getModuleId());
this.checkModule(createOrCopyRequest.getModuleId());
TestPlan createTestPlan = new TestPlan();
BeanUtils.copyBean(createTestPlan, testPlanCreateRequest);
BeanUtils.copyBean(createTestPlan, createOrCopyRequest);
this.validateTestPlan(createTestPlan);
createTestPlan.setId(IDGenerator.nextStr());
long operateTime = System.currentTimeMillis();
createTestPlan.setNum(NumGenerator.nextNum(testPlanCreateRequest.getProjectId(), ApplicationNumScope.TEST_PLAN));
createTestPlan.setNum(NumGenerator.nextNum(createOrCopyRequest.getProjectId(), ApplicationNumScope.TEST_PLAN));
createTestPlan.setCreateUser(operator);
createTestPlan.setUpdateUser(operator);
createTestPlan.setCreateTime(operateTime);
createTestPlan.setUpdateTime(operateTime);
createTestPlan.setStatus(TestPlanConstants.TEST_PLAN_STATUS_PREPARED);
if (StringUtils.equals(createTestPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_PLAN) && !StringUtils.equals(createTestPlan.getGroupId(), TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID)) {
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(createTestPlan.getGroupId());
createTestPlan.setModuleId(testPlan.getModuleId());
}
TestPlanConfig testPlanConfig = new TestPlanConfig();
testPlanConfig.setTestPlanId(createTestPlan.getId());
testPlanConfig.setAutomaticStatusUpdate(testPlanCreateRequest.isAutomaticStatusUpdate());
testPlanConfig.setRepeatCase(testPlanCreateRequest.isRepeatCase());
testPlanConfig.setPassThreshold(testPlanCreateRequest.getPassThreshold());
testPlanConfig.setTestPlanning(testPlanCreateRequest.isTestPlanning());
testPlanConfig.setAutomaticStatusUpdate(createOrCopyRequest.isAutomaticStatusUpdate());
testPlanConfig.setRepeatCase(createOrCopyRequest.isRepeatCase());
testPlanConfig.setPassThreshold(createOrCopyRequest.getPassThreshold());
testPlanConfig.setTestPlanning(createOrCopyRequest.isTestPlanning());
if (testPlanCreateRequest.isGroupOption()) {
if (createOrCopyRequest.isGroupOption()) {
testPlanXPackFactory.getTestPlanGroupService().validateGroup(createTestPlan, testPlanConfig);
}
handleAssociateCase(testPlanCreateRequest, createTestPlan);
if (StringUtils.isBlank(id)) {
handleAssociateCase(createOrCopyRequest, createTestPlan);
} else {
//复制
handleCopy(createTestPlan, id);
}
testPlanMapper.insert(createTestPlan);
testPlanConfigMapper.insert(testPlanConfig);
testPlanLogService.saveAddLog(createTestPlan, operator, requestUrl, requestMethod);
return createTestPlan.getId();
return createTestPlan;
}
/**
@ -322,6 +343,11 @@ public class TestPlanService {
updateTestPlan.setGroupId(request.getTestPlanGroupId());
updateTestPlan.setType(testPlan.getType());
if (StringUtils.equals(updateTestPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_PLAN) && !StringUtils.equals(updateTestPlan.getGroupId(), TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID)) {
TestPlan group = testPlanMapper.selectByPrimaryKey(updateTestPlan.getGroupId());
updateTestPlan.setModuleId(group.getModuleId());
}
if (StringUtils.equals(testPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP) || StringUtils.isNotEmpty(request.getTestPlanGroupId())) {
//修改组移动测试计划进组出组需要特殊的处理方式
testPlanXPackFactory.getTestPlanGroupService().validateGroup(testPlan, null);
@ -403,4 +429,52 @@ public class TestPlanService {
}
extTestPlanMapper.batchUpdateStatus(TestPlanConstants.TEST_PLAN_STATUS_ARCHIVED, userId, System.currentTimeMillis(), ids);
}
/**
* 复制测试计划
*
* @param request
* @param userId
* @return
*/
public TestPlan copy(TestPlanCopyRequest request, String userId) {
TestPlan testPlan = savePlanDTO(request, userId, request.getTestPlanId());
return testPlan;
}
/**
* 处理复制
*
* @param testPlan
* @param id
*/
private void handleCopy(TestPlan testPlan, String id) {
if (StringUtils.equalsIgnoreCase(testPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP)) {
//计划组
TestPlanExample example = new TestPlanExample();
example.createCriteria().andGroupIdEqualTo(id);
List<TestPlan> testPlans = testPlanMapper.selectByExample(example);
if (CollectionUtils.isEmpty(testPlans)) {
return;
}
List<String> ids = testPlans.stream().map(TestPlan::getId).collect(Collectors.toList());
doHandleAssociateCase(ids, testPlan);
} else {
//计划
doHandleAssociateCase(Arrays.asList(id), testPlan);
}
}
/**
* 处理复制 关联用例数据
*
* @param ids
*/
private void doHandleAssociateCase(List<String> ids, TestPlan testPlan) {
testPlanFunctionCaseService.saveTestPlanByPlanId(ids, testPlan);
//TODO 复制关联接口用例/接口场景用例
}
}

View File

@ -121,6 +121,7 @@ public class TestPlanTests extends BaseTest {
private static final String URL_TEST_PLAN_EDIT_FOLLOWER = "/test-plan/edit/follower";
private static final String URL_TEST_PLAN_ARCHIVED = "/test-plan/archived/%s";
private static final String URL_TEST_PLAN_COPY = "/test-plan/copy";
private static String groupTestPlanId7 = null;
private static String groupTestPlanId15 = null;
@ -955,7 +956,6 @@ public class TestPlanTests extends BaseTest {
MvcResult moduleCountResult = this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_MODULE_COUNT, testPlanTableRequest);
String moduleCountReturnData = moduleCountResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
Map<String, Object> moduleCountMap = JSON.parseObject(JSON.toJSONString(JSON.parseObject(moduleCountReturnData, ResultHolder.class).getData()), Map.class);
this.checkModuleCount(moduleCountMap, a1NodeCount, a2NodeCount, a3NodeCount, a1a1NodeCount, a1b1NodeCount);
//反例不写id
updateRequest = new TestPlanUpdateRequest();
this.requestPost(URL_POST_TEST_PLAN_UPDATE, updateRequest).andExpect(status().isBadRequest());
@ -2094,4 +2094,62 @@ public class TestPlanTests extends BaseTest {
}
@Test
@Order(303)
public void testCopy() throws Exception {
//1.计划 无用例
TestPlanCopyRequest copyRequest = new TestPlanCopyRequest();
copyRequest.setTestPlanId("wx_test_plan_id_1");
copyRequest.setProjectId("123");
copyRequest.setName("测试计划复制");
copyRequest.setType(TestPlanConstants.TEST_PLAN_TYPE_PLAN);
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_TEST_PLAN_COPY, copyRequest);
String returnStr = mvcResult.getResponse().getContentAsString();
ResultHolder holder = JSON.parseObject(returnStr, ResultHolder.class);
String returnId = holder.getData().toString();
Assertions.assertNotNull(returnId);
//2.计划 有用例
TestPlanCopyRequest copyRequest1 = new TestPlanCopyRequest();
copyRequest1.setTestPlanId("wx_test_plan_id_4");
copyRequest1.setProjectId("123");
copyRequest1.setName("测试计划复制有用例");
copyRequest1.setType(TestPlanConstants.TEST_PLAN_TYPE_PLAN);
MvcResult mvcResult1 = this.requestPostWithOkAndReturn(URL_TEST_PLAN_COPY, copyRequest1);
String returnStr1 = mvcResult1.getResponse().getContentAsString();
ResultHolder holder1 = JSON.parseObject(returnStr1, ResultHolder.class);
String returnId1 = holder1.getData().toString();
Assertions.assertNotNull(returnId1);
//3.计划组 无计划
TestPlanCopyRequest copyRequest2 = new TestPlanCopyRequest();
copyRequest2.setTestPlanId("wx_test_plan_id_2");
copyRequest2.setProjectId("123");
copyRequest2.setName("测试计划组复制无计划");
copyRequest2.setType(TestPlanConstants.TEST_PLAN_TYPE_GROUP);
MvcResult mvcResult2 = this.requestPostWithOkAndReturn(URL_TEST_PLAN_COPY, copyRequest2);
String returnStr2 = mvcResult2.getResponse().getContentAsString();
ResultHolder holder2 = JSON.parseObject(returnStr2, ResultHolder.class);
String returnId2 = holder2.getData().toString();
Assertions.assertNotNull(returnId2);
//4.计划组 有计划
TestPlanCopyRequest copyRequest3 = new TestPlanCopyRequest();
copyRequest3.setTestPlanId("wx_test_plan_id_5");
copyRequest3.setProjectId("123");
copyRequest3.setName("测试计划组复制有计划");
copyRequest3.setType(TestPlanConstants.TEST_PLAN_TYPE_GROUP);
MvcResult mvcResult3 = this.requestPostWithOkAndReturn(URL_TEST_PLAN_COPY, copyRequest3);
String returnStr3 = mvcResult3.getResponse().getContentAsString();
ResultHolder holder3 = JSON.parseObject(returnStr3, ResultHolder.class);
String returnId3 = holder3.getData().toString();
Assertions.assertNotNull(returnId3);
}
}

View File

@ -1,8 +1,12 @@
INSERT INTO `test_plan`(`id`, `num`, `project_id`, `group_id`, `module_id`, `name`, `status`, `type`, `tags`, `create_time`, `create_user`, `update_time`, `update_user`, `planned_start_time`, `planned_end_time`, `actual_start_time`, `actual_end_time`, `description`)
VALUES
('wx_test_plan_id_1', 5000, 'wx', 'NONE', '1', '测试一下计划', 'PREPARED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wx_test_plan_id_2', 10000, 'wx', 'NONE', '1', '测试一下组', 'PREPARED', 'GROUP', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wx_test_plan_id_3', 15000, 'wx', 'NONE', '1', '测试一下组2', 'PREPARED', 'GROUP', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wx_test_plan_id_4', 20000, 'wx', 'wx_test_plan_id_3', '1', '测试一下计划2', 'PREPARED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wx_test_plan_id_5', 25000, 'wx', 'NONE', '1', '测试一下组3', 'PREPARED', 'GROUP', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wx_test_plan_id_6', 30000, 'wx', 'wx_test_plan_id_5', '1', '测试组3下计划', 'COMPLETED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11');
('wx_test_plan_id_1', 5000, '123', 'NONE', '1', '测试一下计划', 'PREPARED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wx_test_plan_id_2', 10000, '123', 'NONE', '1', '测试一下组', 'PREPARED', 'GROUP', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wx_test_plan_id_3', 15000, '123', 'NONE', '1', '测试一下组2', 'PREPARED', 'GROUP', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wx_test_plan_id_4', 20000, '123', 'wx_test_plan_id_3', '1', '测试一下计划2', 'PREPARED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wx_test_plan_id_5', 25000, '123', 'NONE', '1', '测试一下组3', 'PREPARED', 'GROUP', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wx_test_plan_id_6', 30000, '123', 'wx_test_plan_id_5', '1', '测试组3下计划', 'COMPLETED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11');
INSERT INTO `test_plan_functional_case`(`id`, `num`, `test_plan_id`, `functional_case_id`, `create_time`, `create_user`, `execute_user`, `last_exec_time`, `last_exec_result`, `pos`)
VALUES ('wx_tpfc_1', 5000, 'wx_test_plan_id_4', 'wx_fc_1', 1714980158000, 'admin', NULL, NULL, NULL, 1);