feat(接口测试): 接口场景定时任务配置功能开发

This commit is contained in:
Jianguo-Genius 2024-04-01 16:57:22 +08:00 committed by 建国
parent b10350040d
commit 0beb6ebc10
11 changed files with 406 additions and 29 deletions

View File

@ -217,6 +217,17 @@ public class ApiScenarioController {
return apiScenarioService.scheduleConfig(request, SessionUtils.getUserId());
}
@GetMapping(value = "/schedule-config-delete/{scenarioId}")
@Operation(summary = "接口测试-接口场景管理-删除定时任务配置")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.scheduleLog(#request.getScenarioId())", msClass = ApiScenarioLogService.class)
@CheckOwner(resourceId = "#request.getScenarioId()", resourceType = "api_scenario")
@SendNotice(taskType = NoticeConstants.TaskType.SCHEDULE_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getScheduleNotice(#request)", targetClass = ApiScenarioNoticeService.class)
public void deleteScheduleConfig(@PathVariable String scenarioId) {
apiValidateService.validateApiMenuInProject(scenarioId, ApiResource.API_SCENARIO.name());
apiScenarioService.deleteScheduleConfig(scenarioId);
}
@PostMapping(value = "/association/page")
@Operation(summary = "接口测试-接口场景管理-场景引用关系列表")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_READ)

View File

@ -22,14 +22,8 @@ public class ApiScenarioDTO extends ApiScenario {
private String modulePath;
@Schema(description = "环境名称")
private String environmentName;
@Schema(description = "定时任务ID")
private String scheduleId;
@Schema(description = "定时任务是否开启")
private Boolean scheduleEnable;
@Schema(description = "定时任务规则")
private String scheduleCorn;
@Schema(description = "定时任务配置")
private ApiScenarioScheduleConfigRequest scheduleConfig;
@Schema(description = "定时任务下一次执行时间")
private Long scheduleExecuteTime;
private Long nextTriggerTime;
}

View File

@ -44,6 +44,7 @@ import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.domain.EnvironmentGroup;
import io.metersphere.sdk.domain.EnvironmentGroupExample;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileCenter;
@ -236,10 +237,17 @@ public class ApiScenarioService extends MoveNodeService {
}
if (MapUtils.isNotEmpty(scheduleMap) && scheduleMap.containsKey(item.getId())) {
Schedule schedule = scheduleMap.get(item.getId());
item.setScheduleId(schedule.getId());
item.setScheduleEnable(schedule.getEnable());
item.setScheduleCorn(schedule.getValue());
item.setScheduleExecuteTime(getNextTriggerTime(schedule.getValue()));
ApiScenarioScheduleConfigRequest request = new ApiScenarioScheduleConfigRequest();
request.setEnable(schedule.getEnable());
request.setCron(schedule.getValue());
request.setScenarioId(item.getId());
if (schedule.getConfig() != null) {
request.setConfig(JSON.parseObject(schedule.getConfig(), ApiRunModeConfigDTO.class));
}
item.setScheduleConfig(request);
if (schedule.getEnable()) {
item.setNextTriggerTime(getNextTriggerTime(schedule.getValue()));
}
}
});
}
@ -2390,6 +2398,9 @@ public class ApiScenarioService extends MoveNodeService {
}
public void deleteScheduleConfig(String scenarioId) {
scheduleService.deleteByResourceId(scenarioId, ApiScenarioScheduleJob.getJobKey(scenarioId), ApiScenarioScheduleJob.getTriggerKey(scenarioId));
}
public String scheduleConfig(ApiScenarioScheduleConfigRequest scheduleRequest, String operator) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(scheduleRequest.getScenarioId());
ScheduleConfig scheduleConfig = ScheduleConfig.builder()

View File

@ -12,8 +12,8 @@ import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.request.ApiTransferRequest;
import io.metersphere.api.dto.request.controller.*;
import io.metersphere.api.dto.request.controller.loop.*;
import io.metersphere.api.dto.request.http.MsHeader;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.request.http.MsHeader;
import io.metersphere.api.dto.request.http.QueryParam;
import io.metersphere.api.dto.response.ApiScenarioBatchOperationResponse;
import io.metersphere.api.dto.response.OperationDataInfo;
@ -1945,6 +1945,7 @@ public class ApiScenarioControllerTests extends BaseTest {
@Order(23)
void scheduleTest() throws Exception {
String testUrl = "/schedule-config";
String deleteUrl = "/schedule-config-delete/";
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
this.batchCreateScenarios();
@ -1952,6 +1953,7 @@ public class ApiScenarioControllerTests extends BaseTest {
//使用最后一个场景ID用于做定时任务的测试
String scenarioId = BATCH_OPERATION_SCENARIO_ID.getLast();
deleteUrl += scenarioId;
ApiScenarioScheduleConfigRequest request = new ApiScenarioScheduleConfigRequest();
request.setScenarioId(scenarioId);
@ -1961,6 +1963,7 @@ public class ApiScenarioControllerTests extends BaseTest {
//先测试一下没有开启模块时接口能否使用
apiScenarioBatchOperationTestService.removeApiModule(DEFAULT_PROJECT_ID);
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
this.requestGet(deleteUrl, request).andExpect(status().is5xxServerError());
//恢复
apiScenarioBatchOperationTestService.resetProjectModule(DEFAULT_PROJECT_ID);
MvcResult result = this.requestPostAndReturn(testUrl, request);
@ -2062,6 +2065,10 @@ public class ApiScenarioControllerTests extends BaseTest {
request.setCron(IDGenerator.nextStr());
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
//测试删除
this.requestGetWithOk(deleteUrl, request);
apiScenarioBatchOperationTestService.checkScheduleIsRemove(scenarioId);
}
//30开始是关于删除和恢复的

View File

@ -53,6 +53,14 @@ public class ScheduleService {
return null;
}
public int deleteByResourceId(String scenarioId, JobKey jobKey, TriggerKey triggerKey) {
ScheduleExample scheduleExample = new ScheduleExample();
scheduleExample.createCriteria().andResourceIdEqualTo(scenarioId);
scheduleManager.removeJob(jobKey, triggerKey);
return scheduleMapper.deleteByExample(scheduleExample);
}
public int deleteByResourceId(String resourceId, String group) {
ScheduleExample scheduleExample = new ScheduleExample();
scheduleExample.createCriteria().andResourceIdEqualTo(resourceId);

View File

@ -27,6 +27,8 @@ import {
RecycleScenarioUrl,
ScenarioHistoryUrl,
ScenarioPageUrl,
ScenarioScheduleConfigDeleteUrl,
ScenarioScheduleConfigUrl,
ScenarioTransferFileUrl,
ScenarioTransferModuleOptionsUrl,
ScenarioTrashPageUrl,
@ -44,6 +46,7 @@ import {
ApiScenarioGetModuleParams,
ApiScenarioModuleUpdateParams,
ApiScenarioPageParams,
ApiScenarioScheduleConfig,
ApiScenarioTableItem,
ApiScenarioUpdateDTO,
ExecuteHistoryItem,
@ -155,6 +158,16 @@ export function batchRunScenario(params: ApiScenarioBatchRunParams) {
return MSR.post({ url: BatchRunScenarioUrl, params });
}
// 批量编辑场景
export function scenarioScheduleConfig(params: ApiScenarioScheduleConfig) {
return MSR.post({ url: ScenarioScheduleConfigUrl, params });
}
// 删除定时任务配置
export function deleteScheduleConfig(id: string) {
return MSR.get({ url: ScenarioScheduleConfigDeleteUrl, params: id });
}
// 场景执行历史接口
export function getExecuteHistory(data: ExecutePageParams) {
return MSR.post<CommonList<ExecuteHistoryItem>>({ url: ExecuteHistoryUrl, data });

View File

@ -17,6 +17,8 @@ export const DebugScenarioUrl = '/api/scenario/debug'; // 接口场景调试(
export const ExecuteScenarioUrl = '/api/scenario/run'; // 接口场景执行(保存报告)
export const GetSystemRequestUrl = '/api/scenario/get/system-request'; // 获取导入的系统请求数据
export const FollowScenarioUrl = '/api/scenario/follow'; // 关注/取消关注接口场景
export const ScenarioScheduleConfigUrl = '/api/scenario/schedule-config'; // 场景定时任务
export const ScenarioScheduleConfigDeleteUrl = '/api/scenario/schedule-config-delete/'; // 场景定时任务
export const BatchRecycleScenarioUrl = '/api/scenario/batch-operation/delete-gc'; // 批量删除接口场景
export const BatchMoveScenarioUrl = '/api/scenario/batch-operation/move'; // 批量移动接口场景
export const BatchCopyScenarioUrl = '/api/scenario/batch-operation/copy'; // 批量复制接口场景

View File

@ -46,6 +46,18 @@ export interface ApiScenarioGetModuleParams {
refId?: string;
}
// 场景定时任务配置
export interface ApiScenarioScheduleConfig {
scenarioId: string;
enable: boolean;
cron: string;
config: {
poolId: string;
grouped: false;
environmentId?: string;
};
}
// 场景详情
export interface ApiScenarioTableItem {
id: string;
@ -79,6 +91,7 @@ export interface ApiScenarioTableItem {
description: string;
status: RequestDefinitionStatus;
customFields: ApiDefinitionCustomField[];
scheduleConfig?: ApiScenarioScheduleConfig;
}
// 场景列表查询参数

View File

@ -62,7 +62,46 @@
</a-trigger>
</template>
<template #num="{ record }">
<MsButton type="text" @click="openScenarioTab(record)">{{ record.num }}</MsButton>
<div>
<MsButton type="text" class="float-left" style="margin-right: 4px" @click="openScenarioTab(record)">{{
record.num
}}</MsButton>
<div v-if="record.scheduleConfig && record.scheduleConfig.enable" class="float-right">
<a-tooltip position="top">
<template #content>
<span>
{{ t('apiScenario.schedule.table.tooltip.enable.one') }}
</span>
<br />
<span>
{{
t('apiScenario.schedule.table.tooltip.enable.two', {
time: dayjs(record.nextTriggerTime).format('YYYY-MM-DD HH:mm:ss'),
})
}}
</span>
</template>
<a-tag
size="small"
style="border-color: #00c261; color: #00c261; background-color: transparent"
bordered
@click="openScheduleModal(record)"
>{{ t('apiScenario.schedule.abbreviation') }}</a-tag
>
</a-tooltip>
</div>
<div v-if="record.scheduleConfig && !record.scheduleConfig.enable" class="float-right">
<a-tooltip :content="t('apiScenario.schedule.table.tooltip.disable')" position="top">
<a-tag
size="small"
style="border-color: #d4d4d8; color: #323233; background-color: transparent"
bordered
@click="openScheduleModal(record)"
>{{ t('apiScenario.schedule.abbreviation') }}</a-tag
>
</a-tooltip>
</div>
</div>
</template>
<template #status="{ record }">
<a-select
@ -170,7 +209,10 @@
{{ t('common.copy') }}
</MsButton>
<a-divider v-permission="['PROJECT_API_SCENARIO:READ+ADD']" direction="vertical" :margin="8"></a-divider>
<MsTableMoreAction :list="tableMoreActionList" @select="handleTableMoreActionSelect($event, record)" />
<MsTableMoreAction
:list="getTableMoreActionList(record)"
@select="handleTableMoreActionSelect($event, record)"
/>
</template>
<template v-if="hasAnyPermission(['PROJECT_API_SCENARIO:READ+ADD'])" #empty>
<div class="flex w-full items-center justify-center p-[8px] text-[var(--color-text-4)]">
@ -182,7 +224,117 @@
</template>
</ms-base-table>
</div>
<!-- 定时任务配置-->
<a-modal v-model:visible="showScheduleModal" title-align="start" class="ms-modal-upload ms-modal-medium" :width="600">
<template #title>
<div>
{{ scheduleModalTitle }}
<div class="text-[var(--color-text-4)]">
{{ '' + tableRecord?.name + '' }}
</div>
</div>
</template>
<a-form ref="scheduleConfigRef" class="rounded-[4px]" :model="scheduleConfig" layout="vertical">
<!-- 触发时间-->
<a-form-item :label="t('apiScenario.schedule.task.schedule')">
<a-select v-model:model-value="scheduleConfig.cron">
<template #label="{ data }">
<div class="flex items-center">
{{ data.value }}
<div class="ml-[4px] text-[var(--color-text-4)]">{{ data.label.split('?')[1] }}</div>
</div>
</template>
<a-option v-for="item of syncFrequencyOptions" :key="item.value" :value="item.value" class="block">
<div class="flex w-full items-center justify-between">
{{ item.value }}
<div class="ml-[4px] text-[var(--color-text-4)]">{{ item.label }}</div>
</div>
</a-option>
</a-select>
</a-form-item>
<!-- 环境选择-->
<a-form-item :label="t('case.execute.selectEnv')">
<a-radio-group v-model:model-value="scheduleUseNewEnv" type="radio">
<a-radio :value="false"
>{{ t('case.execute.defaultEnv') }}
<a-tooltip :content="t('case.execute.defaultEnvTip')" position="top">
<icon-question-circle
class="text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]"
size="16"
/>
</a-tooltip>
</a-radio>
<a-radio :value="true">{{ t('case.execute.newEnv') }}</a-radio>
</a-radio-group>
</a-form-item>
<!-- 新环境 -->
<a-form-item
v-if="scheduleUseNewEnv"
field="config.environmentId"
:label="t('case.execute.newEnv')"
:rules="[{ required: true, message: t('apiTestManagement.envRequired') }]"
asterisk-position="end"
required
>
<a-select v-model="scheduleConfig.config.environmentId" :placeholder="t('common.pleaseSelect')">
<a-option v-for="item of environmentList" :key="item.id" :value="item.id">
{{ t(item.name) }}
</a-option>
</a-select>
</a-form-item>
<!-- 运行资源池 设计稿中最后一行取消margin-bottom-->
<a-form-item
field="config.poolId"
:label="t('apiScenario.schedule.config.resource_pool')"
:rules="[{ required: true, message: t('apiTestManagement.poolRequired') }]"
asterisk-position="end"
required
class="mb-0"
>
<a-select v-model="scheduleConfig.config.poolId" :placeholder="t('common.pleaseSelect')">
<a-option v-for="item of environmentList" :key="item.id" :value="item.id">
{{ t(item.name) }}
</a-option>
</a-select>
</a-form-item>
</a-form>
<template #footer>
<div class="flex" :class="['justify-between']">
<div class="flex flex-row items-center justify-center">
<a-switch v-model="scheduleConfig.enable" class="mr-1" size="small" type="line" />
<a-tooltip>
<template #content>
<span>
{{ t('apiScenario.schedule.task.status.tooltip.one') }}
</span>
<br />
<span>
{{ t('apiScenario.schedule.task.status.tooltip.two') }}
</span>
</template>
<span class="flex items-center">
<span class="mr-1">{{ t('apiScenario.schedule.task.status') }}</span>
<span class="mt-[2px]">
<IconQuestionCircle class="h-[16px] w-[16px] text-[rgb(var(--primary-5))]" />
</span>
</span>
</a-tooltip>
</div>
<div class="flex justify-end">
<a-button type="secondary" :disabled="scheduleModalLoading" @click="cancelScheduleModal">
{{ t('common.cancel') }}
</a-button>
<a-button class="ml-3" type="primary" :loading="scheduleModalLoading" @click="saveScheduleModal">
{{ t('common.save') }}
</a-button>
</div>
</div>
</template>
</a-modal>
<!-- 表格批量操作-->
<a-modal v-model:visible="showBatchModal" title-align="start" class="ms-modal-upload ms-modal-medium" :width="480">
<template #title>
{{ t('common.batchEdit') }}
@ -341,8 +493,9 @@
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { computed, ref } from 'vue';
import { FormInstance, Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import dayjs from 'dayjs';
import MsButton from '@/components/pure/ms-button/index.vue';
@ -356,17 +509,19 @@
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
import apiStatus from '@/views/api-test/components/apiStatus.vue';
import BatchRunModal from '@/views/api-test/components/batchRunModal.vue';
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
import operationScenarioModuleTree from '@/views/api-test/scenario/components/operationScenarioModuleTree.vue';
import { getEnvList } from '@/api/modules/api-test/management';
import {
batchEditScenario,
batchOptionScenario,
batchRecycleScenario,
batchRunScenario,
deleteScheduleConfig,
getScenarioPage,
recycleScenario,
scenarioScheduleConfig,
updateScenario,
} from '@/api/modules/api-test/scenario';
import { useI18n } from '@/hooks/useI18n';
@ -375,7 +530,8 @@
import useAppStore from '@/store/modules/app';
import { hasAnyPermission } from '@/utils/permission';
import { ApiScenarioTableItem, ApiScenarioUpdateDTO } from '@/models/apiTest/scenario';
import { Environment } from '@/models/apiTest/management';
import { ApiScenarioScheduleConfig, ApiScenarioTableItem, ApiScenarioUpdateDTO } from '@/models/apiTest/scenario';
import { ApiScenarioStatus } from '@/enums/apiEnum';
import { ReportEnum, ReportStatus } from '@/enums/reportEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
@ -400,6 +556,27 @@
const appStore = useAppStore();
const { t } = useI18n();
const { openModal } = useModal();
const tableRecord = ref<ApiScenarioTableItem>();
const scheduleModalTitle = ref('');
const scheduleConfig = ref<ApiScenarioScheduleConfig>({
scenarioId: '',
enable: false,
cron: '',
config: {
poolId: '',
grouped: false,
},
});
const scheduleUseNewEnv = ref(false);
const environmentList = ref<Environment[]>();
//
async function initEnvList() {
environmentList.value = await getEnvList(appStore.currentProjectId);
}
const scenarioPriorityList = ref([
{
value: 'P0',
@ -435,8 +612,8 @@
sorter: true,
},
fixed: 'left',
width: 100,
showTooltip: true,
width: 140,
showTooltip: false,
columnSelectorDisabled: true,
},
{
@ -601,14 +778,40 @@
},
],
};
const tableMoreActionList = [
{
eventTag: 'delete',
label: t('common.delete'),
permission: ['PROJECT_API_SCENARIO:READ+DELETE'],
danger: true,
},
];
function getTableMoreActionList(tableRow: ApiScenarioTableItem) {
if (tableRow.scheduleConfig) {
//
return [
{
eventTag: 'deleteSchedule',
label: t('apiScenario.schedule.delete'),
permission: ['PROJECT_API_SCENARIO:READ+EXECUTE'],
danger: true,
},
{
eventTag: 'delete',
label: t('common.delete'),
permission: ['PROJECT_API_SCENARIO:READ+DELETE'],
danger: true,
},
];
}
return [
{
eventTag: 'schedule',
label: t('apiScenario.schedule.create'),
permission: ['PROJECT_API_SCENARIO:READ+EXECUTE'],
danger: false,
},
{
eventTag: 'delete',
label: t('common.delete'),
permission: ['PROJECT_API_SCENARIO:READ+DELETE'],
danger: true,
},
];
}
const statusFilterVisible = ref(false);
const statusFilters = ref<string[]>([]);
@ -760,6 +963,56 @@
});
}
const showScheduleModal = ref(false);
const syncFrequencyOptions = [
{ label: t('apiTestManagement.timeTaskHour'), value: '0 0 0/1 * * ? ' },
{ label: t('apiTestManagement.timeTaskSixHour'), value: '0 0 0/6 * * ?' },
{ label: t('apiTestManagement.timeTaskTwelveHour'), value: '0 0 0/12 * * ?' },
{ label: t('apiTestManagement.timeTaskDay'), value: '0 0 0 * * ?' },
];
function resetScheduleConfig(record: ApiScenarioTableItem) {
//
tableRecord.value = record;
if (record.scheduleConfig) {
scheduleConfig.value = cloneDeep(record.scheduleConfig);
} else {
//
scheduleConfig.value = {
scenarioId: record.id,
enable: false,
cron: '0 0 0/1 * * ? ',
config: {
poolId: '',
grouped: false,
},
};
}
scheduleUseNewEnv.value = !!scheduleConfig.value.config.environmentId;
//
initEnvList();
//
if (tableRecord.value.scheduleConfig) {
scheduleModalTitle.value = t('apiScenario.schedule.update');
} else {
scheduleModalTitle.value = t('apiScenario.schedule.create');
}
}
function openScheduleModal(record: ApiScenarioTableItem) {
resetScheduleConfig(record);
showScheduleModal.value = true;
}
async function deleteScenarioSchedule(scenarioId: string) {
try {
await deleteScheduleConfig(scenarioId);
Message.success(t('common.deleteSuccess'));
loadScenarioList(false);
} catch (error) {
console.log(error);
}
}
/**
* 处理表格更多按钮事件
* @param item
@ -769,11 +1022,48 @@
case 'delete':
deleteScenario(record);
break;
case 'schedule':
openScheduleModal(record);
break;
case 'deleteSchedule':
deleteScenarioSchedule(record.id);
break;
default:
break;
}
}
const scheduleConfigRef = ref<FormInstance>();
const scheduleModalLoading = ref(false);
function cancelScheduleModal() {
showScheduleModal.value = false;
}
function saveScheduleModal() {
scheduleConfigRef.value?.validate(async (errors) => {
if (!errors) {
try {
scheduleModalLoading.value = true;
await scenarioScheduleConfig({ ...scheduleConfig.value });
//
if (tableRecord.value?.scheduleConfig) {
Message.success(t('common.updateSuccess'));
} else {
Message.success(t('common.createSuccess'));
}
cancelScheduleModal();
resetSelector();
loadScenarioList(true);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
scheduleModalLoading.value = false;
}
}
});
}
/**
* 处理表格选中
*/
@ -783,7 +1073,9 @@
const showBatchModal = ref(false);
const batchUpdateLoading = ref(false);
const batchFormRef = ref<FormInstance>();
const batchForm = ref({
attr: '',
value: '',

View File

@ -99,4 +99,17 @@ export default {
'As long as the system extracts the returned cookie information from the result of a certain step, the subsequent steps will use this cookie. If a cookie variable is added to the request, it will also be overwritten',
'apiScenario.setting.waitTime.tip':
'When running a scenario, each step of the scenario will wait for a certain time after execution before triggering the next step to start execution',
// 定时任务
'apiScenario.schedule.create': 'Create schedule',
'apiScenario.schedule.update': 'Update schedule',
'apiScenario.schedule.delete': 'Delete schedule',
'apiScenario.schedule.config.resource_pool': 'Resource pool',
'apiScenario.schedule.task.status': 'Task status',
'apiScenario.schedule.task.schedule': 'Trigger time',
'apiScenario.schedule.abbreviation': 'SCHEDULE',
'apiScenario.schedule.task.status.tooltip.one': 'Open: execute schedule task',
'apiScenario.schedule.task.status.tooltip.two': 'Close: stop executing schedule task',
'apiScenario.schedule.table.tooltip.enable.one': 'Schedule is open',
'apiScenario.schedule.table.tooltip.enable.two': 'Next trigger time{time}',
'apiScenario.schedule.table.tooltip.disable': 'Schedule is close',
};

View File

@ -222,4 +222,17 @@ export default {
'apiScenario.setting.share.cookie.tip':
'系统只要从某个步骤的结果内提取到返回的cookie信息则后续步骤都会使用此 cookie 如果请求添加了Cookie 变量也会被覆盖',
'apiScenario.setting.waitTime.tip': '运行场景时,场景的每一个步骤执行后都会等待相应的时间再触发下一个步骤开始执行',
// 定时任务
'apiScenario.schedule.create': '创建定时任务',
'apiScenario.schedule.update': '更新定时任务',
'apiScenario.schedule.delete': '删除定时任务',
'apiScenario.schedule.config.resource_pool': '运行资源池',
'apiScenario.schedule.task.status': '任务状态',
'apiScenario.schedule.task.schedule': '任务触发时间',
'apiScenario.schedule.abbreviation': '定时',
'apiScenario.schedule.task.status.tooltip.one': '开启:执行定时任务',
'apiScenario.schedule.task.status.tooltip.two': '关闭:停止定时任务',
'apiScenario.schedule.table.tooltip.enable.one': '定时任务已开启',
'apiScenario.schedule.table.tooltip.enable.two': '下次运行时间:{time}',
'apiScenario.schedule.table.tooltip.disable': '定时任务未开启',
};