From 426ee6aae9239b7d785de4a11bf4c0f360297240 Mon Sep 17 00:00:00 2001 From: guocan Date: Wed, 28 May 2025 10:45:35 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=8C=96=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/controller/TestTaskController.java | 50 +++++- .../service/impl/UiSceneStepsServiceImpl.java | 28 +++- .../com/test/test/task/TestTaskManager.java | 149 ++++++++++++++++++ .../resources/mapper/test/TestTaskMapper.xml | 4 +- .../src/views/test/uiTest/automationTest.vue | 16 +- 5 files changed, 235 insertions(+), 12 deletions(-) create mode 100644 test-test/src/main/java/com/test/test/task/TestTaskManager.java diff --git a/test-test/src/main/java/com/test/test/controller/TestTaskController.java b/test-test/src/main/java/com/test/test/controller/TestTaskController.java index c2da3fd..b808fcb 100644 --- a/test-test/src/main/java/com/test/test/controller/TestTaskController.java +++ b/test-test/src/main/java/com/test/test/controller/TestTaskController.java @@ -15,6 +15,7 @@ import com.test.test.domain.qo.IDQO; import com.test.test.service.ITestTaskLogService; import com.test.test.service.ITestTaskResultService; import com.test.test.service.ITestTaskService; +import com.test.test.task.TestTaskManager; import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Value; import org.springframework.validation.annotation.Validated; @@ -42,6 +43,9 @@ public class TestTaskController extends BaseController { @Resource private ITestTaskLogService taskLogService; + @Resource + private TestTaskManager testTaskManager; + /** * 查询定时任务列表 */ @@ -90,9 +94,20 @@ public class TestTaskController extends BaseController { @Log(title = "定时任务", businessType = BusinessType.INSERT) @PostMapping("/add") public AjaxResult add(@RequestBody TestTask testTask) { - testTask.setCreateBy(getLoginUser().getUsername()); - testTask.setCreateTime(DateUtils.getNowDate()); - return toAjax(testTaskService.insertTestTask(testTask)); + try { + testTask.setCreateBy(getLoginUser().getUsername()); + testTask.setCreateTime(DateUtils.getNowDate()); + int result = testTaskService.insertTestTask(testTask); + + // 如果任务状态是启用且没有被删除,则添加到定时任务管理器 + if (testTask.getStatus() == 0 && "0".equals(testTask.getDelFlag())) { + testTaskManager.addNewTask(testTask); + } + + return toAjax(result); + } catch (Exception e) { + return error("新增失败:" + e.getMessage()); + } } /** @@ -101,7 +116,23 @@ public class TestTaskController extends BaseController { @Log(title = "定时任务", businessType = BusinessType.UPDATE) @PostMapping("/edit") public AjaxResult edit(@RequestBody TestTask testTask) { - return toAjax(testTaskService.updateTestTask(testTask)); + try { + // 获取原任务信息 + TestTask originalTask = testTaskService.selectTestTaskById(testTask.getId()).getTask(); + + // 更新任务 + int result = testTaskService.updateTestTask(testTask); + + // 如果状态或crontab发生变化,更新定时任务 + if (testTask.getStatus() != originalTask.getStatus() || + !testTask.getCrontab().equals(originalTask.getCrontab())) { + testTaskManager.updateTask(testTask); + } + + return toAjax(result); + } catch (Exception e) { + return error("修改失败:" + e.getMessage()); + } } /** @@ -110,7 +141,14 @@ public class TestTaskController extends BaseController { @Log(title = "定时任务", businessType = BusinessType.DELETE) @PostMapping("/del") public AjaxResult remove(@RequestBody IDQO qo) { - return toAjax(testTaskService.deleteTestTaskById(qo.getId())); + try { + // 先从定时任务管理器中移除 + testTaskManager.removeTask(qo.getId()); + // 再删除数据库记录 + return toAjax(testTaskService.deleteTestTaskById(qo.getId())); + } catch (Exception e) { + return error("删除失败:" + e.getMessage()); + } } /** @@ -126,6 +164,4 @@ public class TestTaskController extends BaseController { }); return toAjax(true); } - - } diff --git a/test-test/src/main/java/com/test/test/service/impl/UiSceneStepsServiceImpl.java b/test-test/src/main/java/com/test/test/service/impl/UiSceneStepsServiceImpl.java index 16621ed..88725eb 100644 --- a/test-test/src/main/java/com/test/test/service/impl/UiSceneStepsServiceImpl.java +++ b/test-test/src/main/java/com/test/test/service/impl/UiSceneStepsServiceImpl.java @@ -102,14 +102,40 @@ public class UiSceneStepsServiceImpl implements IUiSceneStepsService { // 配置 ChromeOptions ChromeOptions options = new ChromeOptions(); - options.addArguments("--headless"); + // 基本设置 + options.addArguments("--headless=new"); // 使用新的无头模式 options.addArguments("--no-sandbox"); options.addArguments("--disable-dev-shm-usage"); options.addArguments("--remote-allow-origins=*"); + + // 禁用各种功能以提高稳定性 options.addArguments("--disable-gpu"); options.addArguments("--disable-extensions"); options.addArguments("--disable-plugins"); options.addArguments("--disable-software-rasterizer"); + options.addArguments("--disable-browser-side-navigation"); + options.addArguments("--disable-infobars"); + options.addArguments("--disable-notifications"); + + // 设置窗口大小和内存限制 + options.addArguments("--window-size=1920,1080"); + options.addArguments("--disable-dev-shm-usage"); + options.addArguments("--incognito"); // 无痕模式 + + // 添加性能和稳定性相关的参数 + options.addArguments("--disable-background-networking"); + options.addArguments("--disable-background-timer-throttling"); + options.addArguments("--disable-client-side-phishing-detection"); + options.addArguments("--disable-default-apps"); + options.addArguments("--disable-hang-monitor"); + options.addArguments("--disable-popup-blocking"); + options.addArguments("--disable-prompt-on-repost"); + options.addArguments("--disable-sync"); + options.addArguments("--metrics-recording-only"); + options.addArguments("--no-first-run"); + options.addArguments("--safebrowsing-disable-auto-update"); + options.addArguments("--password-store=basic"); + options.addArguments("--use-mock-keychain"); // 创建 ChromeDriver WebDriver driver = new ChromeDriver(options); diff --git a/test-test/src/main/java/com/test/test/task/TestTaskManager.java b/test-test/src/main/java/com/test/test/task/TestTaskManager.java new file mode 100644 index 0000000..b509bee --- /dev/null +++ b/test-test/src/main/java/com/test/test/task/TestTaskManager.java @@ -0,0 +1,149 @@ +package com.test.test.task; + +import com.test.test.domain.TestTask; +import com.test.test.domain.qo.GroupIdQO; +import com.test.test.service.ITestTaskService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.stereotype.Component; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; + +/** + * 定时任务管理器 + * 负责调度和执行定时任务 + */ +@Slf4j +@Component +public class TestTaskManager { + + @Resource + private ITestTaskService testTaskService; + + @Resource + private TaskScheduler taskScheduler; + + @Value("${test.jmeterHomePath:/opt/apache-jmeter}") + private String jmeterHomePath; + + // 用于存储正在运行的任务 + private final Map> scheduledTasks = new ConcurrentHashMap<>(); + + @PostConstruct + public void init() { + loadTasks(); // 初始化时加载所有启用的定时任务 + } + + /** + * 初始加载所有启用的定时任务 + */ + private void loadTasks() { + try { + GroupIdQO qo = new GroupIdQO(); + List tasks = testTaskService.selectTestTaskList(qo); + if (tasks != null) { + tasks.stream() + .filter(task -> task != null + && task.getStatus() != null + && task.getStatus() == 0 + && "0".equals(task.getDelFlag())) + .forEach(this::scheduleTask); + } + } catch (Exception e) { + log.error("加载定时任务失败", e); + } + } + + /** + * 更新任务 + */ + public void updateTask(TestTask task) { + log.info("更新任务 {}", task.getId()); + Long taskId = task.getId(); + + // 先取消现有任务 + if (scheduledTasks.containsKey(taskId)) { + scheduledTasks.get(taskId).cancel(true); + scheduledTasks.remove(taskId); + } + + // 如果任务状态是启用且没有被删除,则重新调度 + if (task.getStatus() == 0 && "0".equals(task.getDelFlag())) { + log.info("启动任务:{}", task.getId()); + scheduleTask(task); + } + } + + /** + * 添加新任务到定时调度 + */ + public void addNewTask(TestTask task) { + // 只有当任务状态是启用且没有被删除时才创建定时任务 + if (task.getStatus() == 0 && "0".equals(task.getDelFlag())) { + log.info("加入定时任务:{}", task.getId()); + scheduleTask(task); + } + } + + /** + * 移除定时任务 + */ + public void removeTask(Long taskId) { + if (scheduledTasks.containsKey(taskId)) { + scheduledTasks.get(taskId).cancel(true); + scheduledTasks.remove(taskId); + log.info("已移除定时任务: {}", taskId); + } + } + + /** + * 批量移除定时任务 + */ + public void removeTasks(Long[] taskIds) { + for (Long taskId : taskIds) { + removeTask(taskId); + } + } + + /** + * 调度单个任务 + */ + private void scheduleTask(TestTask task) { + try { + if (task.getCrontab() == null || task.getCrontab().trim().isEmpty()) { + log.error("任务 [{}] 的cron表达式为空", task.getName()); + return; + } + + CronTrigger trigger = new CronTrigger(task.getCrontab()); + ScheduledFuture future = taskScheduler.schedule( + () -> executeTask(task), trigger + ); + scheduledTasks.put(task.getId(), future); + log.info("成功调度任务: {}", task.getName()); + } catch (IllegalArgumentException e) { + log.error("无效的cron表达式: " + task.getCrontab(), e); + } catch (Exception e) { + log.error("调度任务时发生错误: " + task.getName(), e); + } + } + + /** + * 执行单个任务 + */ + private void executeTask(TestTask task) { + try { + log.info("开始执行任务: {}", task.getName()); + testTaskService.executeTestTaskById(task.getId(), 1, null, jmeterHomePath, "system"); + } catch (Exception e) { + log.error("执行任务 [{}] 时发生错误", task.getName(), e); + } + } +} \ No newline at end of file diff --git a/test-test/src/main/resources/mapper/test/TestTaskMapper.xml b/test-test/src/main/resources/mapper/test/TestTaskMapper.xml index 30110a1..1237fa6 100644 --- a/test-test/src/main/resources/mapper/test/TestTaskMapper.xml +++ b/test-test/src/main/resources/mapper/test/TestTaskMapper.xml @@ -28,7 +28,9 @@ diff --git a/test-ui/src/views/test/uiTest/automationTest.vue b/test-ui/src/views/test/uiTest/automationTest.vue index ce73534..551a4f4 100644 --- a/test-ui/src/views/test/uiTest/automationTest.vue +++ b/test-ui/src/views/test/uiTest/automationTest.vue @@ -116,7 +116,7 @@ export default { }, // 删除 hadleClickDelete(val) { - this.$modal.confirm('确认删除元素' + '?').then(() => { + this.$modal.confirm('确认删除该场景' + '?').then(() => { deleteAutomation(val.id).then(res => { if (res.code === 200) { this.$modal.msgSuccess("删除成功"); @@ -129,13 +129,23 @@ export default { }, // 查看 handleClickDetail(val) { + const loading = this.$loading({ + lock: true, + text: '执行中...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); executeAutomationData({ id: val.id }).then(res => { + loading.close(); if (res.code === 200) { this.$modal.msgSuccess("执行成功"); } else { - this.$modal.msgSuccess("执行失败"); + this.$modal.msgError("执行失败"); } - }) + }).catch(err => { + loading.close(); + this.$modal.msgError("执行失败"); + }); }, // 分页 handleSizeChange(val) {