feat: 接口定义定时任务改造

This commit is contained in:
chenjianxing 2021-06-15 16:14:00 +08:00 committed by jianxing
parent 3c9df86106
commit 5eaae502d4
23 changed files with 449 additions and 115 deletions

View File

@ -8,7 +8,6 @@ import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.ReferenceDTO;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.ScheduleInfoSwaggerUrlRequest;
import io.metersphere.api.dto.swaggerurl.SwaggerTaskResult;
import io.metersphere.api.dto.swaggerurl.SwaggerUrlRequest;
import io.metersphere.api.service.ApiDefinitionService;
@ -22,10 +21,8 @@ import io.metersphere.base.domain.Schedule;
import io.metersphere.commons.constants.OperLogConstants;
import io.metersphere.commons.constants.PermissionConstants;
import io.metersphere.commons.json.JSONSchemaGenerator;
import io.metersphere.commons.utils.CronUtils;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.ScheduleRequest;
import io.metersphere.log.annotation.MsAuditLog;
import io.metersphere.service.CheckPermissionService;
@ -39,8 +36,6 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.MalformedURLException;
import java.util.Date;
import java.util.List;
@ -211,12 +206,12 @@ public class ApiDefinitionController {
//定时任务创建
@PostMapping(value = "/schedule/create")
@MsAuditLog(module = "api_definition", type = OperLogConstants.CREATE, title = "#request.scheduleFrom", project = "#request.projectId")
public void createSchedule(@RequestBody ScheduleRequest request) throws MalformedURLException {
public void createSchedule(@RequestBody ScheduleRequest request) {
apiDefinitionService.createSchedule(request);
}
@PostMapping(value = "/schedule/update")
public void updateSchedule(@RequestBody Schedule request) {
public void updateSchedule(@RequestBody ScheduleRequest request) {
apiDefinitionService.updateSchedule(request);
}
@ -229,30 +224,18 @@ public class ApiDefinitionController {
//查找定时任务列表
@GetMapping("/scheduleTask/{projectId}")
public List<SwaggerTaskResult> getSwaggerScheduleList(@PathVariable String projectId) {
List<SwaggerTaskResult> resultList = apiDefinitionService.getSwaggerScheduleList(projectId);
int dataIndex = 1;
for (SwaggerTaskResult swaggerTaskResult :
resultList) {
swaggerTaskResult.setIndex(dataIndex++);
Date nextExecutionTime = CronUtils.getNextTriggerTime(swaggerTaskResult.getRule());
if (nextExecutionTime != null) {
swaggerTaskResult.setNextExecutionTime(nextExecutionTime.getTime());
}
}
return resultList;
return apiDefinitionService.getSwaggerScheduleList(projectId);
}
//更新定时任务
@PostMapping(value = "/schedule/updateByPrimyKey")
public void updateScheduleEnableByPrimyKey(@RequestBody ScheduleInfoSwaggerUrlRequest request) {
Schedule schedule = scheduleService.getSchedule(request.getTaskId());
schedule.setEnable(request.getTaskStatus());
apiDefinitionService.updateSchedule(schedule);
//更新定时任务更新定时任务
@PostMapping(value = "/schedule/switch")
public void updateScheduleEnable(@RequestBody Schedule request) {
apiDefinitionService.switchSchedule(request);
}
//删除定时任务和swaggereUrl
@PostMapping("/schedule/deleteByPrimyKey")
public void deleteSchedule(@RequestBody ScheduleInfoSwaggerUrlRequest request) {
@PostMapping("/schedule/delete")
public void deleteSchedule(@RequestBody ScheduleRequest request) {
apiDefinitionService.deleteSchedule(request);
}

View File

@ -14,12 +14,20 @@ import java.util.List;
public class ApiDefinitionImportUtil {
public static ApiModule getSelectModule(String moduleId) {
return getSelectModule(moduleId, null);
}
public static ApiModule getSelectModule(String moduleId, String userId) {
ApiModuleService apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
if (StringUtils.isNotBlank(moduleId) && !StringUtils.equals("root", moduleId)) {
ApiModule module = new ApiModule();
ApiModuleDTO moduleDTO = apiModuleService.getNode(moduleId);
if (moduleDTO != null) {
BeanUtils.copyBean(module, moduleDTO);
} else {
if (StringUtils.isNotBlank(userId)) {
module.setCreateUser(userId);
}
}
return module;
}
@ -57,7 +65,24 @@ public class ApiDefinitionImportUtil {
return module;
}
public static ApiModule buildModule(ApiModule parentModule, String name, String projectId, String userId) {
ApiModuleService apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
ApiModule module;
if (parentModule != null) {
module = apiModuleService.getNewModule(name, projectId, parentModule.getLevel() + 1);
module.setParentId(parentModule.getId());
} else {
module = apiModuleService.getNewModule(name, projectId, 1);
}
createModule(module, userId);
return module;
}
public static void createModule(ApiModule module) {
createModule(module, null);
}
public static void createModule(ApiModule module, String userId) {
ApiModuleService apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
module.setProtocol(RequestType.HTTP);
if (module.getName().length() > 64) {
@ -65,6 +90,9 @@ public class ApiDefinitionImportUtil {
}
List<ApiModule> apiModules = apiModuleService.selectSameModule(module);
if (CollectionUtils.isEmpty(apiModules)) {
if (StringUtils.isNotBlank(userId)) {
module.setCreateUser(userId);
}
apiModuleService.addNode(module);
} else {
module.setId(apiModules.get(0).getId());

View File

@ -128,7 +128,7 @@ public class MsDefinitionParser extends MsAbstractParser<ApiDefinitionImport> {
Iterator<String> iterator = modules.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
parent = ApiDefinitionImportUtil.buildModule(parent, item, this.projectId);
parent = ApiDefinitionImportUtil.buildModule(parent, item, this.projectId, importRequest.getUserId());
if (!iterator.hasNext()) {
apiDefinition.setModuleId(parent.getId());
String path = apiDefinition.getModulePath() == null ? "" : apiDefinition.getModulePath();

View File

@ -1,27 +1,22 @@
package io.metersphere.api.dto.swaggerurl;
import io.metersphere.base.domain.SwaggerUrlProject;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class SwaggerTaskResult {
public class SwaggerTaskResult extends SwaggerUrlProject {
//序号
private int index;
//定时任务号
private String taskId;
//SwaggerUrlId
private String SwaggerUrlId;
//SwaggerUrl
private String swaggerUrl;
//导入模块
private String modulePath;
//同步规则
private String rule;
//下次同步时间
private Long nextExecutionTime;
//同步开关
private boolean taskStatus;
private boolean enable;
//定时任务类型 swaggerUrlType
private String taskType;
}

View File

@ -203,7 +203,7 @@ public class APITestService {
}
deleteFileByTestId(testId);
apiReportService.deleteByTestId(testId);
scheduleService.deleteByResourceId(testId);
scheduleService.deleteByResourceId(testId, ScheduleGroup.API_TEST.name());
apiTestMapper.deleteByPrimaryKey(testId);
deleteBodyFiles(testId);
}

View File

@ -411,7 +411,7 @@ public class ApiAutomationService {
ids.add(scenarioId);
deleteApiScenarioReport(ids);
scheduleService.deleteScheduleAndJobByResourceId(scenarioId, ScheduleGroup.API_SCENARIO_TEST.name());
scheduleService.deleteByResourceId(scenarioId, ScheduleGroup.API_SCENARIO_TEST.name());
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
example.createCriteria().andApiScenarioIdEqualTo(scenarioId);
List<TestPlanApiScenario> testPlanApiScenarioList = testPlanApiScenarioMapper.selectByExample(example);
@ -485,7 +485,7 @@ public class ApiAutomationService {
}
}
scheduleService.deleteByResourceId(id);
scheduleService.deleteByResourceId(id, ScheduleGroup.API_SCENARIO_TEST.name());
}
if (!testPlanApiScenarioIdList.isEmpty()) {
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
@ -507,7 +507,7 @@ public class ApiAutomationService {
extApiScenarioMapper.removeToGc(apiIds);
//将这些场景的定时任务删除掉
for (String id : apiIds) {
scheduleService.deleteScheduleAndJobByResourceId(id, ScheduleGroup.API_SCENARIO_TEST.name());
scheduleService.deleteByResourceId(id, ScheduleGroup.API_SCENARIO_TEST.name());
}
}

View File

@ -12,7 +12,6 @@ import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImportParserFactory;
import io.metersphere.api.dto.definition.parse.Swagger3Parser;
import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.dto.definition.request.ScheduleInfoSwaggerUrlRequest;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
import io.metersphere.api.dto.scenario.Body;
@ -917,21 +916,22 @@ public class ApiDefinitionService {
}
/*swagger定时导入*/
public void createSchedule(ScheduleRequest request) throws MalformedURLException {
public void createSchedule(ScheduleRequest request) {
/*保存swaggerUrl*/
SwaggerUrlProject swaggerUrlProject = new SwaggerUrlProject();
BeanUtils.copyBean(swaggerUrlProject, request);
swaggerUrlProject.setId(UUID.randomUUID().toString());
swaggerUrlProject.setProjectId(request.getProjectId());
swaggerUrlProject.setSwaggerUrl(request.getResourceId());
swaggerUrlProject.setModuleId(request.getModuleId());
swaggerUrlProject.setModulePath(request.getModulePath());
swaggerUrlProject.setModeId(request.getModeId());
scheduleService.addSwaggerUrlSchedule(swaggerUrlProject);
request.setResourceId(swaggerUrlProject.getId());
Schedule schedule = scheduleService.buildApiTestSchedule(request);
schedule.setProjectId(swaggerUrlProject.getProjectId());
java.net.URL swaggerUrl = new java.net.URL(swaggerUrlProject.getSwaggerUrl());
schedule.setName(swaggerUrl.getHost()); // swagger 定时任务的 name 设置为 swaggerURL 的域名
try {
schedule.setName(new java.net.URL(swaggerUrlProject.getSwaggerUrl()).getHost());
} catch (MalformedURLException e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("URL 格式不正确!");
}
schedule.setJob(SwaggerUrlImportJob.class.getName());
schedule.setGroup(ScheduleGroup.SWAGGER_IMPORT.name());
schedule.setType(ScheduleType.CRON.name());
@ -940,17 +940,39 @@ public class ApiDefinitionService {
}
//关闭
public void updateSchedule(Schedule request) {
public void updateSchedule(ScheduleRequest request) {
SwaggerUrlProject swaggerUrlProject = new SwaggerUrlProject();
BeanUtils.copyBean(swaggerUrlProject, request);
scheduleService.updateSwaggerUrlSchedule(swaggerUrlProject);
// 只修改表达式和名称
Schedule schedule = new Schedule();
schedule.setId(request.getTaskId());
schedule.setValue(request.getValue().trim());
schedule.setEnable(request.getEnable());
try {
schedule.setName(new java.net.URL(swaggerUrlProject.getSwaggerUrl()).getHost());
} catch (MalformedURLException e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("URL 格式不正确!");
}
scheduleService.editSchedule(schedule);
request.setResourceId(swaggerUrlProject.getId());
this.addOrUpdateSwaggerImportCronJob(request);
}
/**
* 列表开关切换
* @param request
*/
public void switchSchedule(Schedule request) {
scheduleService.editSchedule(request);
this.addOrUpdateSwaggerImportCronJob(request);
}
//删除
public void deleteSchedule(ScheduleInfoSwaggerUrlRequest request) {
public void deleteSchedule(ScheduleRequest request) {
swaggerUrlProjectMapper.deleteByPrimaryKey(request.getId());
scheduleMapper.deleteByPrimaryKey(request.getTaskId());
scheduleService.deleteByResourceId(request.getTaskId(), ScheduleGroup.SWAGGER_IMPORT.name());
}
//查询swaggerUrl详情
@ -974,7 +996,17 @@ public class ApiDefinitionService {
}
public List<SwaggerTaskResult> getSwaggerScheduleList(String projectId) {
return extSwaggerUrlScheduleMapper.getSwaggerTaskList(projectId);
List<SwaggerTaskResult> resultList = extSwaggerUrlScheduleMapper.getSwaggerTaskList(projectId);
int dataIndex = 1;
for (SwaggerTaskResult swaggerTaskResult :
resultList) {
swaggerTaskResult.setIndex(dataIndex++);
Date nextExecutionTime = CronUtils.getNextTriggerTime(swaggerTaskResult.getRule());
if (nextExecutionTime != null) {
swaggerTaskResult.setNextExecutionTime(nextExecutionTime.getTime());
}
}
return resultList;
}
private void addOrUpdateSwaggerImportCronJob(Schedule request) {

View File

@ -184,7 +184,9 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
node.setCreateTime(System.currentTimeMillis());
node.setUpdateTime(System.currentTimeMillis());
node.setId(UUID.randomUUID().toString());
node.setCreateUser(SessionUtils.getUserId());
if (StringUtils.isBlank(node.getCreateUser())) {
node.setCreateUser(SessionUtils.getUserId());
}
double pos = getNextLevelPos(node.getProjectId(), node.getLevel(), node.getParentId());
node.setPos(pos);
apiModuleMapper.insertSelective(node);

View File

@ -3,14 +3,17 @@
<mapper namespace="io.metersphere.base.mapper.ext.ExtSwaggerUrlScheduleMapper">
<select id="getSwaggerTaskList" resultType="io.metersphere.api.dto.swaggerurl.SwaggerTaskResult"
parameterType="java.lang.String">
SELECT sup.id as SwaggerUrlId,
sup.swagger_url as swaggerUrl,
sup.module_path as modulePath,
SELECT sup.id,
sup.swagger_url,
sup.module_path,
sup.mode_id,
sup.module_id,
sup.project_id,
sch.value as rule,
sch.enable as taskStatus,
sch.enable,
sch.id as taskId
FROM swagger_url_project sup
INNER JOIN schedule sch ON sup.id = sch.resource_id
WHERE sup.project_id = #{projectId}
</select>
</mapper>
</mapper>

View File

@ -9,14 +9,14 @@ import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import java.util.Collection;
import java.util.Objects;
import static io.metersphere.commons.constants.SessionConstants.ATTR_USER;
public class SessionUtils {
public static String getUserId() {
return Objects.requireNonNull(getUser()).getId();
SessionUser user = getUser();
return user == null ? null : user.getId();
}
public static SessionUser getUser() {

View File

@ -24,4 +24,8 @@ public class ScheduleRequest extends Schedule {
private String modeId;
private String swaggerUrl;
private String taskId;
}

View File

@ -1,12 +1,10 @@
package io.metersphere.job.sechedule;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.definition.ApiSwaggerUrlDTO;
import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.base.domain.SwaggerUrlProject;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.service.ScheduleService;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
@ -16,7 +14,7 @@ public class SwaggerUrlImportJob extends MsScheduleJob {
private ApiDefinitionService apiDefinitionService;
public SwaggerUrlImportJob() {
apiDefinitionService = (ApiDefinitionService) CommonBeanFactory.getBean(ApiDefinitionService.class);
apiDefinitionService = CommonBeanFactory.getBean(ApiDefinitionService.class);
}
@Override
@ -31,6 +29,7 @@ public class SwaggerUrlImportJob extends MsScheduleJob {
request.setPlatform("Swagger2");
request.setUserId(jobDataMap.getString("userId"));
request.setType("schedule");
request.setUserId(jobDataMap.getString("userId"));
apiDefinitionService.apiTestImport(null, request);
}

View File

@ -40,7 +40,7 @@ public class AppStartListener implements ApplicationListener<ApplicationReadyEve
initPythonEnv();
try {
Thread.sleep(3 * 60 * 1000);
Thread.sleep(1 * 60 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

View File

@ -112,7 +112,7 @@ public class PerformanceTestService {
extLoadTestReportMapper.updateJmxContentIfAbsent(record);
});
//delete schedule
scheduleService.deleteByResourceId(testId);
scheduleService.deleteByResourceId(testId, ScheduleGroup.PERFORMANCE_TEST.name());
// delete load_test
loadTestMapper.deleteByPrimaryKey(request.getId());

View File

@ -58,9 +58,15 @@ public class ScheduleService {
schedule.setUpdateTime(System.currentTimeMillis());
scheduleMapper.insert(schedule);
}
public void addSwaggerUrlSchedule(SwaggerUrlProject swaggerUrlProject) {
swaggerUrlProjectMapper.insert(swaggerUrlProject);
}
public void updateSwaggerUrlSchedule(SwaggerUrlProject swaggerUrlProject) {
swaggerUrlProjectMapper.updateByPrimaryKeySelective(swaggerUrlProject);
}
public ApiSwaggerUrlDTO selectApiSwaggerUrlDTO(String id){
return extScheduleMapper.select(id);
}
@ -85,34 +91,23 @@ public class ScheduleService {
return null;
}
public int deleteSchedule(String scheduleId) {
Schedule schedule = scheduleMapper.selectByPrimaryKey(scheduleId);
removeJob(schedule.getResourceId());
return scheduleMapper.deleteByPrimaryKey(scheduleId);
}
public int deleteByResourceId(String resourceId) {
public int deleteByResourceId(String resourceId, String group) {
ScheduleExample scheduleExample = new ScheduleExample();
scheduleExample.createCriteria().andResourceIdEqualTo(resourceId);
removeJob(resourceId);
removeJob(resourceId, group);
return scheduleMapper.deleteByExample(scheduleExample);
}
public int deleteScheduleAndJobByResourceId(String resourceId,String group) {
ScheduleExample scheduleExample = new ScheduleExample();
scheduleExample.createCriteria().andResourceIdEqualTo(resourceId);
removeJob(resourceId,group);
return scheduleMapper.deleteByExample(scheduleExample);
}
public void removeJob(String resourceId,String group) {
if(StringUtils.equals(ScheduleGroup.API_SCENARIO_TEST.name(),group)){
private void removeJob(String resourceId, String group) {
if(StringUtils.equals(ScheduleGroup.API_SCENARIO_TEST.name(), group)){
scheduleManager.removeJob(ApiScenarioTestJob.getJobKey(resourceId), ApiScenarioTestJob.getTriggerKey(resourceId));
}else if(StringUtils.equals(ScheduleGroup.TEST_PLAN_TEST.name(),group)){
} else if(StringUtils.equals(ScheduleGroup.TEST_PLAN_TEST.name(), group)){
scheduleManager.removeJob(TestPlanTestJob.getJobKey(resourceId), TestPlanTestJob.getTriggerKey(resourceId));
}else if(StringUtils.equals(ScheduleGroup.SWAGGER_IMPORT.name(),group)){
} else if(StringUtils.equals(ScheduleGroup.SWAGGER_IMPORT.name(), group)){
scheduleManager.removeJob(SwaggerUrlImportJob.getJobKey(resourceId), SwaggerUrlImportJob.getTriggerKey(resourceId));
}else{
} else if(StringUtils.equals(ScheduleGroup.PERFORMANCE_TEST.name(), group)){
scheduleManager.removeJob(PerformanceTestJob.getJobKey(resourceId), PerformanceTestJob.getTriggerKey(resourceId));
} else {
scheduleManager.removeJob(ApiTestJob.getJobKey(resourceId), ApiTestJob.getTriggerKey(resourceId));
}
}
@ -156,10 +151,6 @@ public class ScheduleService {
return schedule;
}
public void removeJob(String resourceId) {
scheduleManager.removeJob(ApiTestJob.getJobKey(resourceId), ApiTestJob.getTriggerKey(resourceId));
}
public void addOrUpdateCronJob(Schedule request, JobKey jobKey, TriggerKey triggerKey, Class clazz) {
Boolean enable = request.getEnable();
String cronExpression = request.getValue();

View File

@ -298,7 +298,7 @@ public class TestPlanService {
testPlanScenarioCaseService.deleteByPlanId(planId);
//删除定时任务
scheduleService.deleteScheduleAndJobByResourceId(planId, ScheduleGroup.TEST_PLAN_TEST.name());
scheduleService.deleteByResourceId(planId, ScheduleGroup.TEST_PLAN_TEST.name());
int num = testPlanMapper.deleteByPrimaryKey(planId);
List<String> relatedUsers = new ArrayList<>();

View File

@ -13,6 +13,7 @@
@setModuleOptions="setModuleOptions"
@setNodeTree="setNodeTree"
@enableTrash="enableTrash"
@schedule="handleTabsEdit($t('api_test.definition.request.fast_debug'), 'SCHEDULE')"
:type="'edit'"
page-source="definition"
ref="nodeTree"/>
@ -118,6 +119,12 @@
:project-id="projectId"
@saveAsApi="editApi" @refresh="refresh" v-if="currentProtocol==='DUBBO'"/>
</div>
<!-- 定时任务 -->
<div v-if="item.type=== 'SCHEDULE'" class="ms-api-div">
<api-schedule :module-options="nodeTree"/>
</div>
<div v-else-if="item.type=== 'MOCK'" class="ms-api-div">
<mock-config :base-mock-config-data="item.mock"></mock-config>
</div>
@ -173,6 +180,7 @@ import MsTabButton from "@/business/components/common/components/MsTabButton";
import {getLabel} from "@/common/js/tableUtils";
import MockConfig from "@/business/components/api/definition/components/mock/MockConfig";
import ApiSchedule from "@/business/components/api/definition/components/import/ApiSchedule";
export default {
name: "ApiDefinition",
@ -191,6 +199,7 @@ export default {
},
},
components: {
ApiSchedule,
MsTabButton,
MsTableButton,
ApiCaseSimpleList,

View File

@ -127,14 +127,16 @@
id: 'id',
label: 'name',
},
modeOptions: [{
id: 'fullCoverage',
name: this.$t('commons.cover')
},
modeOptions: [
{
id: 'fullCoverage',
name: this.$t('commons.cover')
},
{
id: 'incrementalMerge',
name: this.$t('commons.not_cover')
}],
}
],
protocol: "",
platforms: [
{

View File

@ -0,0 +1,256 @@
<template>
<el-main>
<div class="api-schedule-form">
<el-form :model="formData" :rules="rules" v-loading="result.loading" label-width="140px" ref="form">
<el-row>
<el-col :span="12">
<el-form-item :label="'Swagger URL'" prop="swaggerUrl" class="swagger-url">
<el-input size="small" v-model="formData.swaggerUrl" clearable/>
</el-form-item>
<el-form-item :label="'Cron表达式'" prop="rule">
<el-input :disabled="isReadOnly"
v-model="formData.rule"
size="small"
:placeholder="$t('schedule.please_input_cron_expression')"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('commons.import_module')" prop="moduleId">
<select-tree class="select-tree" size="small"
:data="moduleOptions"
:defaultKey="formData.moduleId"
@getValue="setModule"
:obj="moduleObj" clearable checkStrictly ref="selectTree"/>
<!-- <ms-select-tree :disabled="readOnly" :data="treeNodes" :defaultKey="form.module" :obj="moduleObj"-->
<!-- @getValue="setModule" clearable checkStrictly size="small"/>-->
</el-form-item>
<el-form-item :label="$t('commons.import_mode')" prop="modeId">
<el-select size="small" v-model="formData.modeId" clearable >
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item class="expression-link">
<el-link :disabled="isReadOnly" type="primary" @click="showCronDialog">
{{ $t('schedule.generate_expression') }}
</el-link>
</el-form-item>
<el-form-item>
<crontab-result :ex="formData.rule" ref="crontabResult"/>
</el-form-item>
</el-form>
<div style="margin-top: 20px;" class="clearfix">
<el-button v-if="!formData.id" type="primary" style="float: right" size="mini" @click="saveCron">{{$t('commons.add')}}</el-button>
<div v-else>
<el-button type="primary" style="float: right;margin-left: 10px" size="mini" @click="clear">{{$t('commons.clear')}}</el-button>
<el-button type="primary" style="float: right" size="mini" @click="saveCron">{{$t('commons.update')}}</el-button>
</div>
</div>
</div>
<div class="task-list">
<swagger-task-list
@rowClick="handleRowClick"
ref="taskList"/>
</div>
<el-dialog width="60%" :title="$t('schedule.generate_expression')" :visible.sync="showCron" :modal="false">
<crontab @hide="showCron=false" @fill="crontabFill" :expression="formData.value" ref="crontab"/>
</el-dialog>
</el-main>
</template>
<script>
import MsFormDivider from "@/business/components/common/components/MsFormDivider";
import SwaggerTaskList from "@/business/components/api/definition/components/import/SwaggerTaskList";
import CrontabResult from "@/business/components/common/cron/CrontabResult";
import Crontab from "@/business/components/common/cron/Crontab";
import {cronValidate} from "@/common/js/cron";
import {getCurrentProjectID, getCurrentUser} from "@/common/js/utils";
import SelectTree from "@/business/components/common/select-tree/SelectTree";
export default {
name: "ApiSchedule",
components: {SelectTree, MsFormDivider,SwaggerTaskList, CrontabResult, Crontab},
props: {
customValidate: {
type: Function,
default: () => {return {pass: true};}
},
isReadOnly: {
type: Boolean,
default: false
},
moduleOptions: Array,
},
watch: {
'schedule.value'() {
this.formData.rule = this.formData.value;
}
},
data() {
const validateCron = (rule, ruleVal, callback) => {
let customValidate = this.customValidate(this.getIntervalTime());
if (!ruleVal) {
callback(new Error(this.$t('commons.input_content')));
} else if (!cronValidate(ruleVal)) {
callback(new Error(this.$t('schedule.cron_expression_format_error')));
} else if (!customValidate.pass) {
callback(new Error(customValidate.info));
} else {
callback();
}
};
return {
operation: true,
schedule: {
value: "",
},
showCron: false,
activeName: 'first',
swaggerUrl: String,
projectId: String,
moduleId: String,
paramSwaggerUrlId: String,
modulePath: String,
modeId: String,
rules: {
rule: [{required: true, validator: validateCron, trigger: 'blur'}],
swaggerUrl: [{required: true, trigger: 'blur', message: this.$t('commons.please_fill_content')}],
},
formData: {
swaggerUrl: '',
modeId: this.$t('commons.not_cover'),
moduleId: '',
rule: ''
},
modeOptions: [
{
id: 'fullCoverage',
name: this.$t('commons.cover')
},
{
id: 'incrementalMerge',
name: this.$t('commons.not_cover')
}
],
result: {},
moduleObj: {
id: 'id',
label: 'name',
},
}
},
methods: {
currentUser: () => {
return getCurrentUser();
},
clear() {
this.formData.id = null;
this.formData.moduleId = null;
this.$refs['form'].resetFields();
if (!this.formData.rule) {
this.$refs.crontabResult.resultList = [];
}
this.$nextTick(() => {
this.$refs.selectTree.init();
});
},
crontabFill(value, resultList) {
//
this.formData.rule = value;
this.$refs.crontabResult.resultList = resultList;
this.$refs['form'].validate();
},
showCronDialog() {
this.showCron = true;
},
saveCron() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.intervalShortValidate();
this.saveSchedule();
} else {
return false;
}
});
},
saveSchedule() {
this.formData.projectId = getCurrentProjectID();
this.formData.value = this.formData.rule;
let url = '';
if (this.formData.id) {
url = '/api/definition/schedule/update';
} else {
this.formData.enable = true;
url = '/api/definition/schedule/create';
}
this.$post(url, this.formData, () => {
this.$success(this.$t('commons.save_success'));
this.$refs.taskList.search();
this.clear();
});
},
intervalShortValidate() {
if (this.getIntervalTime() < 3 * 60 * 1000) {
this.$info(this.$t('schedule.cron_expression_interval_short_error'));
}
return true;
},
resultListChange() {
this.$refs['form'].validate();
},
getIntervalTime() {
let resultList = this.$refs.crontabResult.resultList;
let time1 = new Date(resultList[0]);
let time2 = new Date(resultList[1]);
return time2 - time1;
},
setModule(id, data) {
this.formData.moduleId = id;
this.formData.modulePath = data.path;
},
handleRowClick(row) {
Object.assign(this.formData, row);
this.$nextTick(() => {
this.$refs.selectTree.init();
});
}
},
computed: {
isTesterPermission() {
return true;
},
isSwagger2() {
return this.selectedPlatformValue === 'Swagger2';
},
}
}
</script>
<style scoped>
.select-tree {
width: 205px;
display: inline-block;
}
.api-schedule-form,.task-list {
border: #DCDFE6 solid 1px;
padding: 10px;
}
.task-list {
margin-top: 15px;
}
.expression-link {
margin-bottom: 0;
}
</style>

View File

@ -1,21 +1,33 @@
<template>
<el-table border :data="tableData" class="adjust-table table-content" height="300px">
<el-table-column prop="index" :label="$t('api_test.home_page.running_task_list.table_coloum.index')"
width="50"
<el-table border
v-loading="result.loading"
highlight-current-row
@row-click="handleRowClick"
:data="tableData"
class="adjust-table table-content"
height="300px">
<el-table-column prop="index"
width="60"
:label="$t('api_test.home_page.running_task_list.table_coloum.index')"
show-overflow-tooltip/>
<el-table-column prop="SwaggerUrlId">
</el-table-column>
<el-table-column prop="swaggerUrl" :label="$t('swaggerUrl')" width="100" show-overflow-tooltip>
<!-- <el-table-column prop="SwaggerUrlId">-->
<!-- </el-table-column>-->
<el-table-column
prop="swaggerUrl"
:label="$t('swaggerUrl')"
min-width="170" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="modulePath" :label="$t('导入模块')"
width="100" show-overflow-tooltip/>
<el-table-column prop="rule" label="同步规则" width="120"
min-width="100"
show-overflow-tooltip/>
<el-table-column prop="rule" label="同步规则"
min-width="140"
show-overflow-tooltip/>
<el-table-column width="100" :label="$t('api_test.home_page.running_task_list.table_coloum.task_status')">
<template v-slot:default="scope">
<div>
<el-switch
v-model="scope.row.taskStatus"
v-model="scope.row.enable"
class="captcha-img"
@click.native="closeTaskConfirm(scope.row)"
></el-switch>
@ -48,6 +60,7 @@ export default {
data() {
return {
tableData: [],
result: {}
}
},
methods: {
@ -57,32 +70,41 @@ export default {
this.tableData = response.data;
})
},
handleRowClick(row) {
this.$emit('rowClick', row);
},
closeTaskConfirm(row) {
let flag = row.taskStatus;
row.taskStatus = !flag;
if (row.taskStatus) {
let flag = row.enable;
row.enable = !flag;
if (row.enable) {
this.$confirm(this.$t('api_test.home_page.running_task_list.confirm.close_title'), this.$t('commons.prompt'), {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
row.taskStatus = !row.taskStatus
row.enable = !row.enable
this.updateTask(row);
}).catch(() => {
});
} else {
row.taskStatus = !row.taskStatus
row.enable = !row.enable
this.updateTask(row);
}
},
updateTask(taskRow) {
this.result = this.$post('/api/definition/schedule/updateByPrimyKey', taskRow, response => {
let schedule = {
resourceId: taskRow.id,
id: taskRow.taskId,
enable: taskRow.enable,
value: taskRow.rule
}
this.result = this.$post('/api/definition/schedule/switch', schedule, response => {
this.search();
});
},
deleteRowTask(row) {
this.result = this.$post('/api/definition/schedule/deleteByPrimyKey', row, response => {
this.result = this.$post('/api/definition/schedule/delete', row, response => {
this.search();
});
}

View File

@ -30,6 +30,7 @@
@exportAPI="exportAPI"
@saveAsEdit="saveAsEdit"
@refreshTable="$emit('refreshTable')"
@schedule="$emit('schedule')"
@refresh="refresh"
@debug="debug"/>
</template>
@ -47,7 +48,7 @@
import ApiImport from "../import/ApiImport";
import MsNodeTree from "../../../../track/common/NodeTree";
import ApiModuleHeader from "./ApiModuleHeader";
import {buildNodePath, buildTree} from "../../model/NodeTree";
import {buildTree} from "../../model/NodeTree";
import {getCurrentProjectID} from "@/common/js/utils";
export default {

View File

@ -65,6 +65,13 @@ export default {
callback: this.handleImport,
permissions: ['PROJECT_API_DEFINITION:READ+IMPORT_API']
},
{
label: this.$t('定时同步'),
callback: () => {
this.$emit('schedule');
},
permissions: ['PROJECT_API_DEFINITION:READ+IMPORT_API']
},
{
label: this.$t('report.export'),
permissions: ['PROJECT_API_DEFINITION:READ+EXPORT_API'],

View File

@ -40,7 +40,7 @@
import OutsideClick from "@/common/js/outside-click";
export default {
name: 'test-code',
name: 'SelectTree',
directives: {OutsideClick},
props: {
//