Compare commits
63 Commits
219c6f4e8d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59d0031f75 | ||
|
|
d960cb6553 | ||
|
|
af54b475a2 | ||
|
|
c12be119a3 | ||
|
|
924f5a935f | ||
|
|
963e9a8162 | ||
|
|
5296b22534 | ||
|
|
48b20390d4 | ||
|
|
ef741fcb64 | ||
|
|
c170f9e5ec | ||
| f9774b7eef | |||
|
|
ef4d51876f | ||
|
|
737aaf4482 | ||
| c0f0b96487 | |||
|
|
ca1e8182db | ||
|
|
f8b935378f | ||
| 660871f873 | |||
| cfdc292c1d | |||
|
|
49d63847ee | ||
|
|
efa41a7217 | ||
| 0f25043d8c | |||
| c9e2aa7c57 | |||
|
|
e0910ea033 | ||
| ffed752170 | |||
| 614aa05b42 | |||
|
|
7161354894 | ||
|
|
c6380b6bde | ||
|
|
f394dc4590 | ||
|
|
5cc2cdfc34 | ||
| eee51078b5 | |||
| 74457d2c81 | |||
| a790ffbcba | |||
|
|
eb58fd40c7 | ||
|
|
c6a73afb76 | ||
| 606c3c15ea | |||
| d54b4523bf | |||
| 3fad8f3a5a | |||
|
|
3209f1e35c | ||
|
|
75fcd51dea | ||
| a9d0f68f52 | |||
| e5dc3bab05 | |||
| 080a80d53d | |||
|
|
66498fd750 | ||
| 8636842735 | |||
|
|
66c627d33a | ||
|
|
c71b9d8c63 | ||
| 426ee6aae9 | |||
| 0960370562 | |||
| a3c140db47 | |||
| 64b57fc3ec | |||
| 2965095148 | |||
| e7248e613a | |||
| 441f7d041d | |||
| 85decf2a60 | |||
| c07742651d | |||
| a3e13b3972 | |||
| 78cb16fd19 | |||
| c99f639976 | |||
| 56d4a24e81 | |||
| 09c9c4eaec | |||
| 898fc59b50 | |||
| fa30bb8bc5 | |||
| d1b00e3ebf |
@@ -6,16 +6,16 @@ spring:
|
|||||||
druid:
|
druid:
|
||||||
# 主库数据源
|
# 主库数据源
|
||||||
master:
|
master:
|
||||||
url: jdbc:mysql://47.103.142.5:3306/cmcf-test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
url: jdbc:mysql://120.27.225.67:9609/riskmanage?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||||
username: test
|
username: riskmanage
|
||||||
password: Test123@
|
password: riskmanage@Farben2023
|
||||||
# 从库数据源
|
# 从库数据源
|
||||||
slave:
|
slave:
|
||||||
# 从数据源开关/默认关闭
|
# 从数据源开关/默认关闭
|
||||||
enabled: false
|
enabled: false
|
||||||
url:
|
url:
|
||||||
username:
|
username:
|
||||||
password:
|
password:
|
||||||
# 初始连接数
|
# 初始连接数
|
||||||
initialSize: 5
|
initialSize: 5
|
||||||
# 最小连接池数量
|
# 最小连接池数量
|
||||||
@@ -39,7 +39,7 @@ spring:
|
|||||||
testWhileIdle: true
|
testWhileIdle: true
|
||||||
testOnBorrow: false
|
testOnBorrow: false
|
||||||
testOnReturn: false
|
testOnReturn: false
|
||||||
webStatFilter:
|
webStatFilter:
|
||||||
enabled: true
|
enabled: true
|
||||||
statViewServlet:
|
statViewServlet:
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -58,4 +58,4 @@ spring:
|
|||||||
merge-sql: true
|
merge-sql: true
|
||||||
wall:
|
wall:
|
||||||
config:
|
config:
|
||||||
multi-statement-allow: true
|
multi-statement-allow: true
|
||||||
|
|||||||
@@ -71,13 +71,13 @@ spring:
|
|||||||
# redis 配置
|
# redis 配置
|
||||||
redis:
|
redis:
|
||||||
# 地址
|
# 地址
|
||||||
host: ah.qyyh.net
|
host: 47.99.93.74
|
||||||
# 端口,默认为6379
|
# 端口,默认为6379
|
||||||
port: 15129
|
port: 6379
|
||||||
# 数据库索引
|
# 数据库索引
|
||||||
database: 1
|
database: 1
|
||||||
# 密码
|
# 密码
|
||||||
password:
|
password: foobaredDkhsb
|
||||||
# 连接超时时间
|
# 连接超时时间
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
lettuce:
|
lettuce:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import java.time.LocalDateTime;
|
|||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||||
|
|
||||||
@@ -232,4 +233,14 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
|||||||
}
|
}
|
||||||
return endTime.getTime() - startTime.getTime();
|
return endTime.getTime() - startTime.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化日期为 yyyy-MM-dd HH:mm:ss
|
||||||
|
*/
|
||||||
|
public static String formatDateTime(Date date) {
|
||||||
|
return date.toInstant()
|
||||||
|
.atZone(ZoneId.systemDefault())
|
||||||
|
.toLocalDateTime()
|
||||||
|
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,7 +283,11 @@ public class JMeterGroupUtil {
|
|||||||
threadGroup.setDelay(0);
|
threadGroup.setDelay(0);
|
||||||
if (executeType == 1) {
|
if (executeType == 1) {
|
||||||
threadGroup.setDuration(pressureSecond);
|
threadGroup.setDuration(pressureSecond);
|
||||||
|
threadGroup.setProperty(new TestElementProperty(ThreadGroup.MAIN_CONTROLLER, createLoopController(-1)));
|
||||||
|
threadGroup.setProperty(new BooleanProperty(ThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION, true));
|
||||||
|
threadGroup.setScheduler(true);
|
||||||
} else {
|
} else {
|
||||||
|
threadGroup.setScheduler(false);
|
||||||
threadGroup.setProperty(new TestElementProperty(ThreadGroup.MAIN_CONTROLLER, createLoopController(loops)));
|
threadGroup.setProperty(new TestElementProperty(ThreadGroup.MAIN_CONTROLLER, createLoopController(loops)));
|
||||||
}
|
}
|
||||||
if (errorOperType == 1) {
|
if (errorOperType == 1) {
|
||||||
@@ -297,7 +301,6 @@ public class JMeterGroupUtil {
|
|||||||
} else if (errorOperType == 5) {
|
} else if (errorOperType == 5) {
|
||||||
threadGroup.setProperty(new StringProperty(ThreadGroup.ON_SAMPLE_ERROR, "stoptestnow"));
|
threadGroup.setProperty(new StringProperty(ThreadGroup.ON_SAMPLE_ERROR, "stoptestnow"));
|
||||||
}
|
}
|
||||||
threadGroup.setScheduler(false);
|
|
||||||
threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName());
|
threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName());
|
||||||
threadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName());
|
threadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName());
|
||||||
threadGroup.setProperty(new BooleanProperty(TestElement.ENABLED, true));
|
threadGroup.setProperty(new BooleanProperty(TestElement.ENABLED, true));
|
||||||
@@ -315,6 +318,9 @@ public class JMeterGroupUtil {
|
|||||||
loopController.setProperty(new StringProperty(TestElement.TEST_CLASS, LoopController.class.getName()));
|
loopController.setProperty(new StringProperty(TestElement.TEST_CLASS, LoopController.class.getName()));
|
||||||
loopController.setProperty(new StringProperty(TestElement.NAME, "循环控制器"));
|
loopController.setProperty(new StringProperty(TestElement.NAME, "循环控制器"));
|
||||||
loopController.setProperty(new BooleanProperty(TestElement.ENABLED, true));
|
loopController.setProperty(new BooleanProperty(TestElement.ENABLED, true));
|
||||||
|
if (loops == 0) {
|
||||||
|
loops = 1;
|
||||||
|
}
|
||||||
loopController.setProperty(new IntegerProperty(LoopController.LOOPS, loops));
|
loopController.setProperty(new IntegerProperty(LoopController.LOOPS, loops));
|
||||||
return loopController;
|
return loopController;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author liangdaliang
|
* @author liangdaliang
|
||||||
@@ -66,6 +68,32 @@ public class MySQLExecutor {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String replaceSqlVariables(String sql, Map<String, String> params) {
|
||||||
|
if (sql == null || params == null) {
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
// 正则匹配 ${variable_name}
|
||||||
|
Pattern pattern = Pattern.compile("\\$\\{([^}]*)\\}");
|
||||||
|
Matcher matcher = pattern.matcher(sql);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
String key = matcher.group(1).trim(); // 获取变量名
|
||||||
|
String replacement = params.get(key); // 从 map 中获取对应值
|
||||||
|
|
||||||
|
if (replacement != null) {
|
||||||
|
// 如果值存在,替换并加上单引号(适用于字符串)
|
||||||
|
// 如果你需要支持数字不加引号,请自行判断类型
|
||||||
|
replacement = "'" + replacement.replace("'", "''") + "'";
|
||||||
|
matcher.appendReplacement(sb, replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher.appendTail(sb);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
// public static void main(String[] args) {
|
// public static void main(String[] args) {
|
||||||
// String url = "jdbc:mysql://47.103.142.5:3306/cmcf-test";
|
// String url = "jdbc:mysql://47.103.142.5:3306/cmcf-test";
|
||||||
// List<String> columnNameList = new ArrayList<>();
|
// List<String> columnNameList = new ArrayList<>();
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package com.test.common.utils;
|
package com.test.common.utils;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.openqa.selenium.*;
|
import org.openqa.selenium.*;
|
||||||
import org.openqa.selenium.interactions.Actions;
|
import org.openqa.selenium.interactions.Actions;
|
||||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||||
import org.openqa.selenium.support.ui.Select;
|
import org.openqa.selenium.support.ui.Select;
|
||||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -368,11 +372,53 @@ public class SeleniumUtils {
|
|||||||
return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
|
return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 截屏并保存为文件
|
/**
|
||||||
|
* 截屏并保存为文件
|
||||||
|
* @param filePath 保存路径的基础目录
|
||||||
|
* @return 保存后的文件完整路径
|
||||||
|
*/
|
||||||
public String takeScreenshotAsFile(String filePath) {
|
public String takeScreenshotAsFile(String filePath) {
|
||||||
return ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE).getAbsolutePath();
|
try {
|
||||||
|
// 生成文件名: screenshot_年月日_时分秒.png
|
||||||
|
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||||
|
String fileName = "screenshot_" + timestamp + ".png";
|
||||||
|
|
||||||
|
// 确保目录存在
|
||||||
|
File directory = new File(filePath);
|
||||||
|
if (!directory.exists()) {
|
||||||
|
boolean created = directory.mkdirs();
|
||||||
|
if (!created) {
|
||||||
|
log.error("无法创建目录: {}", filePath);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查目录是否可写
|
||||||
|
if (!directory.canWrite()) {
|
||||||
|
log.error("目录不可写: {}", filePath);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取截图临时文件
|
||||||
|
File tempFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
|
||||||
|
|
||||||
|
// 构建目标文件路径
|
||||||
|
String fullPath = filePath + File.separator + fileName;
|
||||||
|
File targetFile = new File(fullPath);
|
||||||
|
|
||||||
|
// 复制文件到目标路径
|
||||||
|
FileUtils.copyFile(tempFile, targetFile);
|
||||||
|
|
||||||
|
log.info("截图已保存到: {}", targetFile.getAbsolutePath());
|
||||||
|
return targetFile.getAbsolutePath();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("截图保存失败: {}", e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 关闭浏览器
|
// 关闭浏览器
|
||||||
public void quit() {
|
public void quit() {
|
||||||
if (driver != null) {
|
if (driver != null) {
|
||||||
@@ -867,4 +913,41 @@ public class SeleniumUtils {
|
|||||||
driver.get(url);
|
driver.get(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据提取提取窗口信息
|
||||||
|
* @param windowType
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String extractWindowInformation(String windowType) {
|
||||||
|
switch (windowType) {
|
||||||
|
case "1": // 窗口 Handle
|
||||||
|
return driver.getWindowHandle();
|
||||||
|
case "2": // 网页标题
|
||||||
|
return driver.getTitle();
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid windowType: " + windowType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找多个元素
|
||||||
|
* @param locType
|
||||||
|
* @param locValue
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<WebElement> findElements(String locType, String locValue) {
|
||||||
|
return driver.findElements(getLocator(locType, locValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取网页标题
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getTitle(){
|
||||||
|
return driver.getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
package com.test.test.controller;
|
package com.test.test.controller;
|
||||||
|
|
||||||
import java.util.List;
|
import com.test.common.annotation.Log;
|
||||||
|
import com.test.common.core.controller.BaseController;
|
||||||
|
import com.test.common.core.domain.AjaxResult;
|
||||||
|
import com.test.common.core.page.TableDataInfo;
|
||||||
|
import com.test.common.enums.BusinessType;
|
||||||
|
import com.test.common.utils.StringUtils;
|
||||||
|
import com.test.common.utils.poi.ExcelUtil;
|
||||||
import com.test.test.domain.PerformanceTest;
|
import com.test.test.domain.PerformanceTest;
|
||||||
import com.test.test.domain.TestCase;
|
import com.test.test.domain.TestCase;
|
||||||
import com.test.test.domain.qo.PerformanceTestQO;
|
import com.test.test.domain.qo.PerformanceTestQO;
|
||||||
@@ -12,16 +17,12 @@ import com.test.test.service.ITestCaseService;
|
|||||||
import com.test.test.task.DynamicTaskManager;
|
import com.test.test.task.DynamicTaskManager;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import com.test.common.annotation.Log;
|
|
||||||
import com.test.common.core.controller.BaseController;
|
import java.util.List;
|
||||||
import com.test.common.core.domain.AjaxResult;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import com.test.common.enums.BusinessType;
|
|
||||||
import com.test.common.utils.poi.ExcelUtil;
|
|
||||||
import com.test.common.core.page.TableDataInfo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 性能测试Controller
|
* 性能测试Controller
|
||||||
@@ -119,6 +120,7 @@ public class PerformanceTestController extends BaseController {
|
|||||||
@PostMapping("/addAndExecute")
|
@PostMapping("/addAndExecute")
|
||||||
public AjaxResult addAndExecute(@RequestBody PerformanceTestQO performanceTestQO) {
|
public AjaxResult addAndExecute(@RequestBody PerformanceTestQO performanceTestQO) {
|
||||||
try {
|
try {
|
||||||
|
String createBy = getLoginUser().getUsername();
|
||||||
Long l = performanceTestService.insertPerformanceTest(performanceTestQO);
|
Long l = performanceTestService.insertPerformanceTest(performanceTestQO);
|
||||||
// 获取新增的任务完整信息
|
// 获取新增的任务完整信息
|
||||||
PerformanceTestVO newTaskVo = performanceTestService.selectPerformanceTestById(l);
|
PerformanceTestVO newTaskVo = performanceTestService.selectPerformanceTestById(l);
|
||||||
@@ -133,8 +135,10 @@ public class PerformanceTestController extends BaseController {
|
|||||||
}
|
}
|
||||||
// 执行性能测试
|
// 执行性能测试
|
||||||
try {
|
try {
|
||||||
Long l1 = performanceTestCaseReportService.executePerformanceTestAndReport(l, jmeterHomePath, 2);
|
CompletableFuture.runAsync(() -> {
|
||||||
return success(l1);
|
performanceTestCaseReportService.executePerformanceTestAndReport(l, jmeterHomePath, 2, createBy);
|
||||||
|
});
|
||||||
|
return toAjax(true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("执行失败!", e);
|
log.error("执行失败!", e);
|
||||||
return error("执行失败!");
|
return error("执行失败!");
|
||||||
@@ -157,7 +161,8 @@ public class PerformanceTestController extends BaseController {
|
|||||||
Long l = performanceTestService.updatePerformanceTest(performanceTestQO);
|
Long l = performanceTestService.updatePerformanceTest(performanceTestQO);
|
||||||
PerformanceTestVO updatedTask = performanceTestService.selectPerformanceTestById(performanceTestQO.getId());
|
PerformanceTestVO updatedTask = performanceTestService.selectPerformanceTestById(performanceTestQO.getId());
|
||||||
// 如果crontab或crontab_status被修改了,则更新定时任务
|
// 如果crontab或crontab_status被修改了,则更新定时任务
|
||||||
if (!updatedTask.getCrontab().equals(originalTask.getCrontab())
|
if ((!StringUtils.isEmpty(updatedTask.getCrontab()) &&
|
||||||
|
!StringUtils.isEmpty(originalTask.getCrontab()) && !updatedTask.getCrontab().equals(originalTask.getCrontab()))
|
||||||
|| updatedTask.getCrontabStatus() != originalTask.getCrontabStatus()) {
|
|| updatedTask.getCrontabStatus() != originalTask.getCrontabStatus()) {
|
||||||
PerformanceTest entity = new PerformanceTest();
|
PerformanceTest entity = new PerformanceTest();
|
||||||
entity.setId(updatedTask.getId());
|
entity.setId(updatedTask.getId());
|
||||||
@@ -178,6 +183,7 @@ public class PerformanceTestController extends BaseController {
|
|||||||
@Log(title = "性能测试", businessType = BusinessType.UPDATE)
|
@Log(title = "性能测试", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping("/editAndExecute")
|
@PutMapping("/editAndExecute")
|
||||||
public AjaxResult editAndExecute(@RequestBody PerformanceTestQO performanceTestQO) {
|
public AjaxResult editAndExecute(@RequestBody PerformanceTestQO performanceTestQO) {
|
||||||
|
String createBy = getLoginUser().getUsername();
|
||||||
try {
|
try {
|
||||||
PerformanceTestVO originalTask = performanceTestService.selectPerformanceTestById(performanceTestQO.getId());
|
PerformanceTestVO originalTask = performanceTestService.selectPerformanceTestById(performanceTestQO.getId());
|
||||||
Long l = performanceTestService.updatePerformanceTest(performanceTestQO);
|
Long l = performanceTestService.updatePerformanceTest(performanceTestQO);
|
||||||
@@ -193,11 +199,13 @@ public class PerformanceTestController extends BaseController {
|
|||||||
dynamicTaskManager.updateTask(entity);
|
dynamicTaskManager.updateTask(entity);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Long l1 = performanceTestCaseReportService.executePerformanceTestAndReport(l, jmeterHomePath, 2);
|
CompletableFuture.runAsync(() -> {
|
||||||
return success(l1);
|
performanceTestCaseReportService.executePerformanceTestAndReport(l, jmeterHomePath, 2, createBy);
|
||||||
|
});
|
||||||
|
return toAjax(true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("执行失败!", e);
|
log.error("执行失败!", e);
|
||||||
return error("执行失败!");
|
return error("执行失败!"+e);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("修改并执行失败!", e);
|
log.error("修改并执行失败!", e);
|
||||||
@@ -222,7 +230,10 @@ public class PerformanceTestController extends BaseController {
|
|||||||
// @PreAuthorize("@ss.hasPermi('system:test:remove')")
|
// @PreAuthorize("@ss.hasPermi('system:test:remove')")
|
||||||
@GetMapping("/executeNow")
|
@GetMapping("/executeNow")
|
||||||
public AjaxResult executeNow(@RequestParam Long id) {
|
public AjaxResult executeNow(@RequestParam Long id) {
|
||||||
Long l1 = performanceTestCaseReportService.executePerformanceTestAndReport(id, jmeterHomePath, 2);
|
String createBy = getLoginUser().getUsername();
|
||||||
return success(l1);
|
CompletableFuture.runAsync(() -> {
|
||||||
|
performanceTestCaseReportService.executePerformanceTestAndReport(id, jmeterHomePath, 2, createBy);
|
||||||
|
});
|
||||||
|
return toAjax(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.test.common.core.domain.AjaxResult;
|
|||||||
import com.test.common.core.page.TableDataInfo;
|
import com.test.common.core.page.TableDataInfo;
|
||||||
import com.test.common.enums.BusinessType;
|
import com.test.common.enums.BusinessType;
|
||||||
import com.test.common.utils.DateUtils;
|
import com.test.common.utils.DateUtils;
|
||||||
|
import com.test.common.utils.SecurityUtils;
|
||||||
import com.test.common.utils.uuid.IdUtils;
|
import com.test.common.utils.uuid.IdUtils;
|
||||||
import com.test.test.domain.TestCase;
|
import com.test.test.domain.TestCase;
|
||||||
import com.test.test.domain.TestPlanCase;
|
import com.test.test.domain.TestPlanCase;
|
||||||
@@ -127,6 +128,7 @@ public class TestCaseController extends BaseController {
|
|||||||
@PostMapping("/runTestPlanCase")
|
@PostMapping("/runTestPlanCase")
|
||||||
public AjaxResult runTestPlanCase(@RequestBody TestPlanCase testPlanCase) {
|
public AjaxResult runTestPlanCase(@RequestBody TestPlanCase testPlanCase) {
|
||||||
String caseSid = IdUtils.simpleUUID();
|
String caseSid = IdUtils.simpleUUID();
|
||||||
|
testPlanCase.setExecuteBy(SecurityUtils.getUsername());
|
||||||
// 异步执行任务
|
// 异步执行任务
|
||||||
CompletableFuture.runAsync(() -> {
|
CompletableFuture.runAsync(() -> {
|
||||||
testCaseService.executeTestCaseByPlanCase(testPlanCase, jmeterHomePath, caseSid);
|
testCaseService.executeTestCaseByPlanCase(testPlanCase, jmeterHomePath, caseSid);
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ import com.test.test.domain.qo.IDQO;
|
|||||||
import com.test.test.domain.qo.TestPlanAddQO;
|
import com.test.test.domain.qo.TestPlanAddQO;
|
||||||
import com.test.test.domain.qo.TestPlanListQO;
|
import com.test.test.domain.qo.TestPlanListQO;
|
||||||
import com.test.test.domain.vo.TestPlanListVO;
|
import com.test.test.domain.vo.TestPlanListVO;
|
||||||
|
import com.test.test.domain.vo.TestPlanOverviewTrendDataVO;
|
||||||
|
import com.test.test.domain.vo.TestPlanOverviewVO;
|
||||||
import com.test.test.service.ITestPlanService;
|
import com.test.test.service.ITestPlanService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -79,4 +82,20 @@ public class TestPlanController extends BaseController {
|
|||||||
public AjaxResult edit(@RequestBody TestPlan testPlan) {
|
public AjaxResult edit(@RequestBody TestPlan testPlan) {
|
||||||
return toAjax(testPlanService.updateTestPlan(testPlan));
|
return toAjax(testPlanService.updateTestPlan(testPlan));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取测试计划概览信息
|
||||||
|
*/
|
||||||
|
@PostMapping("/planOverview")
|
||||||
|
public TestPlanOverviewVO selectPlanOverview(@RequestBody IDQO qo) {
|
||||||
|
return testPlanService.selectPlanOverview(qo.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取测试计划用例执行趋势数据
|
||||||
|
*/
|
||||||
|
@PostMapping("/planCaseTrendData")
|
||||||
|
public List<TestPlanOverviewTrendDataVO> getPlanCaseTrendData(@RequestBody IDQO qo) {
|
||||||
|
return testPlanService.getCaseTrendData(qo.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ import com.test.common.core.domain.AjaxResult;
|
|||||||
import com.test.common.core.page.TableDataInfo;
|
import com.test.common.core.page.TableDataInfo;
|
||||||
import com.test.common.enums.BusinessType;
|
import com.test.common.enums.BusinessType;
|
||||||
import com.test.common.utils.poi.ExcelUtil;
|
import com.test.common.utils.poi.ExcelUtil;
|
||||||
|
import com.test.test.domain.TestPlan;
|
||||||
import com.test.test.domain.TestPlanDefect;
|
import com.test.test.domain.TestPlanDefect;
|
||||||
|
import com.test.test.domain.TestProject;
|
||||||
|
import com.test.test.domain.qo.IDQO;
|
||||||
import com.test.test.domain.qo.TestPlanDefectQO;
|
import com.test.test.domain.qo.TestPlanDefectQO;
|
||||||
import com.test.test.domain.qo.TestPlanDefectRelQO;
|
import com.test.test.domain.qo.TestPlanDefectRelQO;
|
||||||
import com.test.test.domain.vo.TestPlanDefectVo;
|
import com.test.test.domain.vo.TestPlanDefectVo;
|
||||||
@@ -92,4 +95,26 @@ public class TestPlanDefectController extends BaseController
|
|||||||
{
|
{
|
||||||
return toAjax(testPlanDefectService.deleteTestPlanDefectByIds(ids));
|
return toAjax(testPlanDefectService.deleteTestPlanDefectByIds(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询缺陷关联测试计划列表
|
||||||
|
*/
|
||||||
|
@PostMapping("/defectPlanList")
|
||||||
|
public TableDataInfo defectPlanList(@RequestBody IDQO qo)
|
||||||
|
{
|
||||||
|
startPage();
|
||||||
|
List<TestPlan> list = testPlanDefectService.selectDefectPlanList(qo.getId());
|
||||||
|
return getDataTable(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询缺陷关联需求列表
|
||||||
|
*/
|
||||||
|
@PostMapping("/defectProjectList")
|
||||||
|
public TableDataInfo defectProjectList(@RequestBody IDQO qo)
|
||||||
|
{
|
||||||
|
startPage();
|
||||||
|
List<TestProject> list = testPlanDefectService.selectDefectProjectList(qo.getId());
|
||||||
|
return getDataTable(list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.test.test.controller;
|
|||||||
|
|
||||||
import com.test.common.core.controller.BaseController;
|
import com.test.common.core.controller.BaseController;
|
||||||
import com.test.common.core.page.TableDataInfo;
|
import com.test.common.core.page.TableDataInfo;
|
||||||
|
import com.test.test.domain.TestCase;
|
||||||
|
import com.test.test.domain.TestDefect;
|
||||||
import com.test.test.domain.qo.IDQO;
|
import com.test.test.domain.qo.IDQO;
|
||||||
import com.test.test.domain.vo.TestPlanProjectVo;
|
import com.test.test.domain.vo.TestPlanProjectVo;
|
||||||
import com.test.test.service.ITestPlanProjectService;
|
import com.test.test.service.ITestPlanProjectService;
|
||||||
@@ -36,4 +38,34 @@ public class TestPlanProjectController extends BaseController {
|
|||||||
List<TestPlanProjectVo> list = testPlanProjectService.selectTestPlanProjectList(qo.getId());
|
List<TestPlanProjectVo> list = testPlanProjectService.selectTestPlanProjectList(qo.getId());
|
||||||
return getDataTable(list);
|
return getDataTable(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询需求关联测试计划列表
|
||||||
|
*/
|
||||||
|
@PostMapping("/relatePlanList")
|
||||||
|
public TableDataInfo relatePlanList(@RequestBody IDQO qo) {
|
||||||
|
startPage();
|
||||||
|
List<TestPlanProjectVo> list = testPlanProjectService.selectRelatePlanList(qo.getId());
|
||||||
|
return getDataTable(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询需求关联用例列表
|
||||||
|
*/
|
||||||
|
@PostMapping("/relateCaseList")
|
||||||
|
public TableDataInfo relateCaseList(@RequestBody IDQO qo) {
|
||||||
|
startPage();
|
||||||
|
List<TestCase> list = testPlanProjectService.selectRelateCaseList(qo.getId());
|
||||||
|
return getDataTable(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询需求关联缺陷列表
|
||||||
|
*/
|
||||||
|
@PostMapping("/relateDefectList")
|
||||||
|
public TableDataInfo relateDefectList(@RequestBody IDQO qo) {
|
||||||
|
startPage();
|
||||||
|
List<TestDefect> list = testPlanProjectService.selectRelateDefectList(qo.getId());
|
||||||
|
return getDataTable(list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.test.common.core.controller.BaseController;
|
|||||||
import com.test.common.core.domain.AjaxResult;
|
import com.test.common.core.domain.AjaxResult;
|
||||||
import com.test.common.core.page.TableDataInfo;
|
import com.test.common.core.page.TableDataInfo;
|
||||||
import com.test.common.enums.BusinessType;
|
import com.test.common.enums.BusinessType;
|
||||||
|
import com.test.test.domain.TestReport;
|
||||||
import com.test.test.domain.qo.IDQO;
|
import com.test.test.domain.qo.IDQO;
|
||||||
import com.test.test.domain.qo.TestReportAddQO;
|
import com.test.test.domain.qo.TestReportAddQO;
|
||||||
import com.test.test.domain.vo.TestReportVo;
|
import com.test.test.domain.vo.TestReportVo;
|
||||||
@@ -12,7 +13,7 @@ import com.test.test.service.ITestReportService;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -37,7 +38,7 @@ public class TestReportController extends BaseController {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@PostMapping("/reportList")
|
@PostMapping("/reportList")
|
||||||
public TableDataInfo list(@RequestBody IDQO qo) {
|
public TableDataInfo list(@Validated IDQO qo) {
|
||||||
startPage();
|
startPage();
|
||||||
List<TestReportVo> list = testReportService.selectTestReportList(qo.getId());
|
List<TestReportVo> list = testReportService.selectTestReportList(qo.getId());
|
||||||
return getDataTable(list);
|
return getDataTable(list);
|
||||||
@@ -53,4 +54,36 @@ public class TestReportController extends BaseController {
|
|||||||
public AjaxResult addTestReport(@RequestBody TestReportAddQO testReportAddQO) {
|
public AjaxResult addTestReport(@RequestBody TestReportAddQO testReportAddQO) {
|
||||||
return toAjax(testReportService.addTestReport(testReportAddQO));
|
return toAjax(testReportService.addTestReport(testReportAddQO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询测试计划关联测试报告详情
|
||||||
|
*/
|
||||||
|
@PostMapping("/caseExecuteDetail")
|
||||||
|
public AjaxResult caseExecuteDetail(@RequestBody IDQO id) {
|
||||||
|
return success(testReportService.selectCaseTestReportById(id.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除测试报告
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Log(title = "测试报告", businessType = BusinessType.DELETE)
|
||||||
|
@PostMapping("/delExecuteCaseReport")
|
||||||
|
public AjaxResult delExecuteCaseReport(@RequestBody IDQO id) {
|
||||||
|
TestReport testReport = testReportService.selectCaseTestReportById(id.getId());
|
||||||
|
testReport.setDelFlag("1");
|
||||||
|
return toAjax(testReportService.updateExecuteCaseReport(testReport));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改测试计划关联测试报告
|
||||||
|
* @param testReport
|
||||||
|
*/
|
||||||
|
@Log(title = "测试报告", businessType = BusinessType.UPDATE)
|
||||||
|
@PostMapping("/updateExecuteCaseReport")
|
||||||
|
public AjaxResult updateExecuteCaseReport(@RequestBody TestReport testReport) {
|
||||||
|
return toAjax(testReportService.updateExecuteCaseReport(testReport));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import com.test.test.domain.qo.IDQO;
|
|||||||
import com.test.test.service.ITestTaskLogService;
|
import com.test.test.service.ITestTaskLogService;
|
||||||
import com.test.test.service.ITestTaskResultService;
|
import com.test.test.service.ITestTaskResultService;
|
||||||
import com.test.test.service.ITestTaskService;
|
import com.test.test.service.ITestTaskService;
|
||||||
|
import com.test.test.task.TestTaskManager;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
@@ -42,6 +43,9 @@ public class TestTaskController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ITestTaskLogService taskLogService;
|
private ITestTaskLogService taskLogService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TestTaskManager testTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询定时任务列表
|
* 查询定时任务列表
|
||||||
*/
|
*/
|
||||||
@@ -90,9 +94,24 @@ public class TestTaskController extends BaseController {
|
|||||||
@Log(title = "定时任务", businessType = BusinessType.INSERT)
|
@Log(title = "定时任务", businessType = BusinessType.INSERT)
|
||||||
@PostMapping("/add")
|
@PostMapping("/add")
|
||||||
public AjaxResult add(@RequestBody TestTask testTask) {
|
public AjaxResult add(@RequestBody TestTask testTask) {
|
||||||
testTask.setCreateBy(getLoginUser().getUsername());
|
try {
|
||||||
testTask.setCreateTime(DateUtils.getNowDate());
|
testTask.setCreateBy(getLoginUser().getUsername());
|
||||||
return toAjax(testTaskService.insertTestTask(testTask));
|
testTask.setCreateTime(DateUtils.getNowDate());
|
||||||
|
if (testTask.getStatus() == null) {
|
||||||
|
testTask.setStatus(2);
|
||||||
|
}
|
||||||
|
int result = testTaskService.insertTestTask(testTask);
|
||||||
|
|
||||||
|
// 如果任务状态是启用且没有被删除,则添加到定时任务管理器
|
||||||
|
if (testTask.getStatus() == 0 && "0".equals(testTask.getDelFlag())) {
|
||||||
|
testTaskManager.addNewTask(testTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toAjax(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("新增失败:", e);
|
||||||
|
return error("新增失败:" + e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,7 +120,25 @@ public class TestTaskController extends BaseController {
|
|||||||
@Log(title = "定时任务", businessType = BusinessType.UPDATE)
|
@Log(title = "定时任务", businessType = BusinessType.UPDATE)
|
||||||
@PostMapping("/edit")
|
@PostMapping("/edit")
|
||||||
public AjaxResult edit(@RequestBody TestTask testTask) {
|
public AjaxResult edit(@RequestBody TestTask testTask) {
|
||||||
return toAjax(testTaskService.updateTestTask(testTask));
|
try {
|
||||||
|
testTask.setUpdateBy(getLoginUser().getUsername());
|
||||||
|
testTask.setUpdateTime(DateUtils.getNowDate());
|
||||||
|
// 获取原任务信息
|
||||||
|
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 +147,14 @@ public class TestTaskController extends BaseController {
|
|||||||
@Log(title = "定时任务", businessType = BusinessType.DELETE)
|
@Log(title = "定时任务", businessType = BusinessType.DELETE)
|
||||||
@PostMapping("/del")
|
@PostMapping("/del")
|
||||||
public AjaxResult remove(@RequestBody IDQO qo) {
|
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 +170,4 @@ public class TestTaskController extends BaseController {
|
|||||||
});
|
});
|
||||||
return toAjax(true);
|
return toAjax(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
package com.test.test.controller;
|
package com.test.test.controller;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.test.common.utils.SeleniumUtils;
|
||||||
import com.test.test.domain.UiAutomation;
|
import com.test.test.domain.UiAutomation;
|
||||||
|
import com.test.test.domain.UiReport;
|
||||||
import com.test.test.domain.qo.UiAutomationDataQO;
|
import com.test.test.domain.qo.UiAutomationDataQO;
|
||||||
import com.test.test.domain.qo.UiAutomationQO;
|
import com.test.test.domain.qo.UiAutomationQO;
|
||||||
import com.test.test.service.IUiAutomationService;
|
import com.test.test.service.IUiAutomationService;
|
||||||
import com.test.test.service.IUiSceneStepsService;
|
import com.test.test.service.IUiSceneStepsService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.openqa.selenium.WebDriver;
|
||||||
|
import org.openqa.selenium.chrome.ChromeDriver;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import com.test.common.annotation.Log;
|
import com.test.common.annotation.Log;
|
||||||
import com.test.common.core.controller.BaseController;
|
import com.test.common.core.controller.BaseController;
|
||||||
@@ -110,11 +116,12 @@ public class UiAutomationController extends BaseController
|
|||||||
public AjaxResult executeStep(@RequestParam Long id)
|
public AjaxResult executeStep(@RequestParam Long id)
|
||||||
{
|
{
|
||||||
try{
|
try{
|
||||||
|
String triggerMode = "1"; //手动执行
|
||||||
log.info("执行完成!");
|
log.info("执行完成!");
|
||||||
return success(uiSceneStepsService.executeStep(id));
|
return success(uiSceneStepsService.executeStep(id,triggerMode));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("执行完成!",e);
|
log.error("执行错误!",e);
|
||||||
return error("执行完成!");
|
return error("执行错误!"+e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package com.test.test.controller;
|
package com.test.test.controller;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.test.test.domain.UiReport;
|
import com.test.test.domain.UiReport;
|
||||||
@@ -7,14 +11,7 @@ import com.test.test.service.IUiReportService;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import com.test.common.annotation.Log;
|
import com.test.common.annotation.Log;
|
||||||
import com.test.common.core.controller.BaseController;
|
import com.test.common.core.controller.BaseController;
|
||||||
import com.test.common.core.domain.AjaxResult;
|
import com.test.common.core.domain.AjaxResult;
|
||||||
@@ -70,4 +67,28 @@ public class UiReportController extends BaseController
|
|||||||
{
|
{
|
||||||
return toAjax(uiReportService.deleteUiReportByIds(ids));
|
return toAjax(uiReportService.deleteUiReportByIds(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/screenshot")
|
||||||
|
public void getScreenshot(@RequestParam String path, HttpServletResponse response) throws IOException {
|
||||||
|
File file = new File(path);
|
||||||
|
if (!file.exists()) {
|
||||||
|
try {
|
||||||
|
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setContentType("image/png");
|
||||||
|
try (FileInputStream in = new FileInputStream(file);
|
||||||
|
OutputStream out = response.getOutputStream()) {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int length;
|
||||||
|
while ((length = in.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,13 +44,13 @@ public class PerformanceTestCaseReport extends BaseEntity
|
|||||||
private String tps;
|
private String tps;
|
||||||
|
|
||||||
/** 开始时间 */
|
/** 开始时间 */
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
@Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd")
|
@Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date startTime;
|
private Date startTime;
|
||||||
|
|
||||||
/** 结束时间 */
|
/** 结束时间 */
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
@Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd")
|
@Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date endTime;
|
private Date endTime;
|
||||||
|
|
||||||
/** 耗时(毫秒) */
|
/** 耗时(毫秒) */
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ public class TestCaseLog extends BaseEntity
|
|||||||
@Excel(name = "用例id")
|
@Excel(name = "用例id")
|
||||||
private Long caseId;
|
private Long caseId;
|
||||||
|
|
||||||
|
/** 测试计划id */
|
||||||
|
@Excel(name = "测试计划id")
|
||||||
|
private Long planId;
|
||||||
|
|
||||||
|
/** 测试计划关联用例类型 */
|
||||||
|
@Excel(name = "测试计划关联用例类型")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
/** 操作类别 */
|
/** 操作类别 */
|
||||||
@Excel(name = "操作类别")
|
@Excel(name = "操作类别")
|
||||||
private String operType;
|
private String operType;
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ public class TestPlanCase extends BaseEntity
|
|||||||
private String executeResult;
|
private String executeResult;
|
||||||
|
|
||||||
/** 执行时间 */
|
/** 执行时间 */
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
@Excel(name = "执行时间", width = 30, dateFormat = "yyyy-MM-dd")
|
@Excel(name = "执行时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date executeTime;
|
private Date executeTime;
|
||||||
|
|
||||||
/** 执行人 */
|
/** 执行人 */
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.test.test.domain.qo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AssertionConfigQO {
|
||||||
|
/** 弹窗文本数组 */
|
||||||
|
private List<AssertionQO> popupTexts = new ArrayList<>();
|
||||||
|
|
||||||
|
/** 元素断言数组 */
|
||||||
|
private List<AssertionQO> elementAssertions = new ArrayList<>();
|
||||||
|
|
||||||
|
/** 下拉框数组 */
|
||||||
|
private List<AssertionQO> dropdownBoxes = new ArrayList<>();
|
||||||
|
|
||||||
|
/** 网页标题数组 */
|
||||||
|
private List<AssertionQO> webTitles = new ArrayList<>();
|
||||||
|
}
|
||||||
@@ -18,10 +18,10 @@ public class AssertionQO {
|
|||||||
private String operateObject;
|
private String operateObject;
|
||||||
|
|
||||||
/** 元素库名称 */
|
/** 元素库名称 */
|
||||||
private Integer operateGroupId;
|
private Long operateGroupId;
|
||||||
|
|
||||||
/** 元素id */
|
/** 元素id */
|
||||||
private Integer operateElementId;
|
private Long operateElementId;
|
||||||
|
|
||||||
/** 元素定位类型 id css ……*/
|
/** 元素定位类型 id css ……*/
|
||||||
private String operateLocType;
|
private String operateLocType;
|
||||||
@@ -52,5 +52,5 @@ public class AssertionQO {
|
|||||||
private String isFailedAbort;
|
private String isFailedAbort;
|
||||||
|
|
||||||
/**是否禁用 0否 1是*/
|
/**是否禁用 0否 1是*/
|
||||||
private String isDisabled;
|
private Integer isDisabled;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.test.test.domain.qo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据提取配置(新版嵌套结构)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DataExtractionConfigQO {
|
||||||
|
/** 窗口信息提取配置 */
|
||||||
|
private List<DataExtractionQO> windowExtractions;
|
||||||
|
|
||||||
|
/** 元素信息提取配置 */
|
||||||
|
private List<DataExtractionQO> elementExtractions;
|
||||||
|
}
|
||||||
@@ -8,14 +8,14 @@ import lombok.Data;
|
|||||||
@Data
|
@Data
|
||||||
public class DataExtractionQO {
|
public class DataExtractionQO {
|
||||||
|
|
||||||
/** 1提取元素信息 2提取窗口信息 */
|
/** 1提取窗口信息 2提取元素信息 */
|
||||||
private String informationType;
|
private String informationType;
|
||||||
|
|
||||||
/** 1普通对象(store) 2元素文本(storeText) 3元素值(storeValue) 4元素属性(storeAttribute)
|
/** informationType为2时 1普通对象(store) 2元素文本(storeText) 3元素值(storeValue) 4元素属性(storeAttribute)
|
||||||
* 5CSS属性(storeCssAttribute) 6匹配 xpath 的元素数量(storeXpathCount) */
|
* 5CSS属性(storeCssAttribute) 6匹配 xpath 的元素数量(storeXpathCount) */
|
||||||
private String elementType;
|
private String elementType;
|
||||||
|
|
||||||
/** 1窗口 Handle(storeWindowHandle) 2网页标题(storeTitle) */
|
/** informationType为1时 1窗口 Handle(storeWindowHandle) 2网页标题(storeTitle) */
|
||||||
private String windowType;
|
private String windowType;
|
||||||
|
|
||||||
/**值 */
|
/**值 */
|
||||||
@@ -26,10 +26,10 @@ public class DataExtractionQO {
|
|||||||
private String operateObject;
|
private String operateObject;
|
||||||
|
|
||||||
/** 元素库名称 */
|
/** 元素库名称 */
|
||||||
private Integer operateGroupId;
|
private Long operateGroupId;
|
||||||
|
|
||||||
/** 元素id */
|
/** 元素id */
|
||||||
private Integer operateElementId;
|
private Long operateElementId;
|
||||||
|
|
||||||
/** 元素定位类型 id css ……*/
|
/** 元素定位类型 id css ……*/
|
||||||
private String operateLocType;
|
private String operateLocType;
|
||||||
@@ -37,11 +37,11 @@ public class DataExtractionQO {
|
|||||||
/** 元素定位值 */
|
/** 元素定位值 */
|
||||||
private String operateLocValue;
|
private String operateLocValue;
|
||||||
|
|
||||||
/**元素属性*/
|
/** 普通对象 元素属性 元素css属性*/
|
||||||
private String elementAttribute;
|
private String elementAttribute;
|
||||||
|
|
||||||
/**是否禁用 0否 1是 */
|
/**是否禁用 0否 1是 */
|
||||||
private String isDisabled;
|
private Integer isDisabled;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ package com.test.test.domain.qo;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class IDQO {
|
public class IDQO extends InheritQO{
|
||||||
private Long id;
|
private Long id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.test.test.domain.qo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class InheritQO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -6405523005491017445L;
|
||||||
|
|
||||||
|
private Integer pageNum;
|
||||||
|
|
||||||
|
private Integer pageSize;
|
||||||
|
}
|
||||||
@@ -56,16 +56,13 @@ public class UiHighSettingQO extends BaseEntity
|
|||||||
/** 是否删除 */
|
/** 是否删除 */
|
||||||
private Integer delFlag;
|
private Integer delFlag;
|
||||||
|
|
||||||
/** 等待元素超时时间 */
|
/**其他设置*/
|
||||||
private Integer waitElementTime;
|
OtherSettingsQO otherSettingsQO = new OtherSettingsQO();
|
||||||
|
|
||||||
/** 截图配置 */
|
|
||||||
private Integer screenshotConfiguration;
|
|
||||||
|
|
||||||
/**数据提取*/
|
/**数据提取*/
|
||||||
private List<DataExtractionQO> dataExtractionQOList = new ArrayList<>();
|
private DataExtractionConfigQO dataExtractionQOList = new DataExtractionConfigQO();
|
||||||
|
|
||||||
/**断言*/
|
/**断言*/
|
||||||
private List<AssertionQO> assertionQOList = new ArrayList<>();
|
private AssertionConfigQO assertionQOList = new AssertionConfigQO();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,10 +50,10 @@ public class UiSceneStepsQO extends BaseEntity
|
|||||||
private String operateObject;
|
private String operateObject;
|
||||||
|
|
||||||
/** 元素库名称 */
|
/** 元素库名称 */
|
||||||
private Integer operateGroupId;
|
private Long operateGroupId;
|
||||||
|
|
||||||
/** 元素id */
|
/** 元素id */
|
||||||
private Integer operateElementId;
|
private Long operateElementId;
|
||||||
|
|
||||||
/** 元素定位类型 */
|
/** 元素定位类型 */
|
||||||
private String operateLocType;
|
private String operateLocType;
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.test.test.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 断言报告结果
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AssertionReportVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 断言名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误信息
|
||||||
|
*/
|
||||||
|
private String errorInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功 0否 1是
|
||||||
|
*/
|
||||||
|
private String isSuccess;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.test.test.domain.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TestCaseReportVO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 6972995415807958849L;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String result;
|
||||||
|
|
||||||
|
private Integer caseNum;
|
||||||
|
|
||||||
|
private Integer passNum;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.test.test.domain.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TestPlanOverviewTrendDataVO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -6444876575138174061L;
|
||||||
|
|
||||||
|
private String caseTrendDates;
|
||||||
|
|
||||||
|
private Integer notExecuted;
|
||||||
|
|
||||||
|
private Integer passed;
|
||||||
|
|
||||||
|
private Integer failed;
|
||||||
|
|
||||||
|
private Integer blocked;
|
||||||
|
|
||||||
|
private Integer skipped;
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package com.test.test.domain.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TestPlanOverviewVO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -2634301979620606338L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例总数
|
||||||
|
*/
|
||||||
|
private Integer caseCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已执行用例数
|
||||||
|
*/
|
||||||
|
private Integer executedCaseCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例通过数
|
||||||
|
*/
|
||||||
|
private Integer passedCaseCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例失败数
|
||||||
|
*/
|
||||||
|
private Integer failedCaseCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例阻塞数
|
||||||
|
*/
|
||||||
|
private Integer blockedCaseCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例跳过数
|
||||||
|
*/
|
||||||
|
private Integer skippedCaseCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例未执行数
|
||||||
|
*/
|
||||||
|
private Integer notExecutedCaseCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缺陷数
|
||||||
|
*/
|
||||||
|
private Integer defectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待确认缺陷状态数
|
||||||
|
*/
|
||||||
|
private Integer unconfirmedDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修复中缺陷状态数
|
||||||
|
*/
|
||||||
|
private Integer fixingDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待验证缺陷状态数
|
||||||
|
*/
|
||||||
|
private Integer unverifiedDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无效缺陷状态数
|
||||||
|
*/
|
||||||
|
private Integer invalidDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 挂起缺陷状态数
|
||||||
|
*/
|
||||||
|
private Integer suspendedDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无效等级缺陷数
|
||||||
|
*/
|
||||||
|
private Integer invalidLevelDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轻微等级缺陷数
|
||||||
|
*/
|
||||||
|
private Integer minorLevelDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一般等级缺陷数
|
||||||
|
*/
|
||||||
|
private Integer normalLevelDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 严重等级缺陷数
|
||||||
|
*/
|
||||||
|
private Integer seriousLevelDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 致命等级缺陷数
|
||||||
|
*/
|
||||||
|
private Integer fatalLevelDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 功能逻辑类型缺陷数
|
||||||
|
*/
|
||||||
|
private Integer logicDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI交互类型缺陷数
|
||||||
|
*/
|
||||||
|
private Integer uiDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 性能问题类型缺陷数
|
||||||
|
*/
|
||||||
|
private Integer performanceDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 兼容性问题类型缺陷数
|
||||||
|
*/
|
||||||
|
private Integer compatibilityDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置错误类型缺陷数
|
||||||
|
*/
|
||||||
|
private Integer configurationDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全问题类型缺陷数
|
||||||
|
*/
|
||||||
|
private Integer securityDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装部署类型缺陷数
|
||||||
|
*/
|
||||||
|
private Integer installationDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 其他类型缺陷数
|
||||||
|
*/
|
||||||
|
private Integer otherDefectCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例执行时间
|
||||||
|
*/
|
||||||
|
private List<String> caseTrendDates;
|
||||||
|
}
|
||||||
@@ -9,6 +9,9 @@ public class TestReportVo extends BaseEntity {
|
|||||||
|
|
||||||
private static final long serialVersionUID = -4331077290310280474L;
|
private static final long serialVersionUID = -4331077290310280474L;
|
||||||
|
|
||||||
|
/** 测试报告id */
|
||||||
|
private Long id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 测试报告名称
|
* 测试报告名称
|
||||||
*/
|
*/
|
||||||
@@ -17,6 +20,9 @@ public class TestReportVo extends BaseEntity {
|
|||||||
/** 测试结果(0,未通过,1,通过) */
|
/** 测试结果(0,未通过,1,通过) */
|
||||||
private String result;
|
private String result;
|
||||||
|
|
||||||
|
/** 测试报告(jason格式存储)*/
|
||||||
|
private String report;
|
||||||
|
|
||||||
/** 测试用例类型(0,冒烟测试,1,功能测试,2,回归测试,3,准生产测试,4,生产验证) */
|
/** 测试用例类型(0,冒烟测试,1,功能测试,2,回归测试,3,准生产测试,4,生产验证) */
|
||||||
private Long type;
|
private Long type;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package com.test.test.domain.vo;
|
package com.test.test.domain.vo;
|
||||||
|
|
||||||
import com.test.common.annotation.Excel;
|
import com.test.test.domain.qo.AssertionConfigQO;
|
||||||
import com.test.common.core.domain.BaseEntity;
|
|
||||||
import com.test.test.domain.qo.AssertionQO;
|
import com.test.test.domain.qo.AssertionQO;
|
||||||
import com.test.test.domain.qo.DataExtractionQO;
|
import com.test.test.domain.qo.DataExtractionConfigQO;
|
||||||
import com.test.test.domain.qo.OtherSettingsQO;
|
import com.test.test.domain.qo.OtherSettingsQO;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -48,10 +45,10 @@ public class UiHighSettingVO
|
|||||||
private Integer isDisabled;
|
private Integer isDisabled;
|
||||||
|
|
||||||
/**断言*/
|
/**断言*/
|
||||||
private List<AssertionQO> assertionQOList = new ArrayList<>();
|
private AssertionConfigQO assertionQOList = new AssertionConfigQO();
|
||||||
|
|
||||||
/**数据提取*/
|
/**数据提取*/
|
||||||
private List<DataExtractionQO> dataExtractionQOList = new ArrayList<>();
|
private DataExtractionConfigQO dataExtractionQOList =new DataExtractionConfigQO();
|
||||||
|
|
||||||
/**其他设置*/
|
/**其他设置*/
|
||||||
private OtherSettingsQO otherSettingsQO;
|
private OtherSettingsQO otherSettingsQO;
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ public class UiSceneStepsVO
|
|||||||
private String operateObject;
|
private String operateObject;
|
||||||
|
|
||||||
/** 元素库名称 */
|
/** 元素库名称 */
|
||||||
private Integer operateGroupId;
|
private Long operateGroupId;
|
||||||
|
|
||||||
/** 元素id */
|
/** 元素id */
|
||||||
private Integer operateElementId;
|
private Long operateElementId;
|
||||||
|
|
||||||
/** 元素定位类型 */
|
/** 元素定位类型 */
|
||||||
private String operateLocType;
|
private String operateLocType;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.test.test.mapper;
|
|||||||
|
|
||||||
import com.test.test.domain.TestCaseLog;
|
import com.test.test.domain.TestCaseLog;
|
||||||
|
|
||||||
|
import com.test.test.domain.qo.TestReportAddQO;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,6 +29,14 @@ public interface TestCaseLogMapper
|
|||||||
*/
|
*/
|
||||||
public List<TestCaseLog> selectTestCaseLogList(TestCaseLog testCaseLog);
|
public List<TestCaseLog> selectTestCaseLogList(TestCaseLog testCaseLog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询用例日志列表
|
||||||
|
*
|
||||||
|
* @param testCaseLog 用例日志
|
||||||
|
* @return 用例日志集合
|
||||||
|
*/
|
||||||
|
public String selectTestCaseLogCaseSid(TestReportAddQO testCaseLog);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增用例日志
|
* 新增用例日志
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.test.test.mapper;
|
package com.test.test.mapper;
|
||||||
|
|
||||||
import com.test.test.domain.TestCase;
|
import com.test.test.domain.TestCase;
|
||||||
|
import com.test.test.domain.TestDefect;
|
||||||
import com.test.test.domain.qo.TestCaseListQO;
|
import com.test.test.domain.qo.TestCaseListQO;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -49,4 +50,9 @@ public interface TestCaseMapper
|
|||||||
* 批量删除用例
|
* 批量删除用例
|
||||||
*/
|
*/
|
||||||
int deleteTestCaseByIds(Long[] ids);
|
int deleteTestCaseByIds(Long[] ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询关联的用例列表
|
||||||
|
*/
|
||||||
|
List<TestCase> selectRelateCaseList(Long projectId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,4 +60,9 @@ public interface TestDefectMapper
|
|||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public int deleteTestDefectByIds(Long[] ids);
|
public int deleteTestDefectByIds(Long[] ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询关联的缺陷列表
|
||||||
|
*/
|
||||||
|
List<TestDefect> selectRelateDefectList(Long projectId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.test.test.mapper;
|
package com.test.test.mapper;
|
||||||
|
|
||||||
|
import com.test.test.domain.TestPlan;
|
||||||
import com.test.test.domain.TestPlanDefect;
|
import com.test.test.domain.TestPlanDefect;
|
||||||
import com.test.test.domain.qo.TestPlanDefectQO;
|
import com.test.test.domain.qo.TestPlanDefectQO;
|
||||||
import com.test.test.domain.vo.TestPlanDefectVo;
|
import com.test.test.domain.vo.TestPlanDefectVo;
|
||||||
@@ -67,4 +68,11 @@ public interface TestPlanDefectMapper
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<TestPlanDefect> selectRelList(TestPlanDefect testPlanDefect);
|
List<TestPlanDefect> selectRelList(TestPlanDefect testPlanDefect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询缺陷关联计划列表
|
||||||
|
* @param defectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<TestPlan> selectDefectPlanList(Long defectId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ package com.test.test.mapper;
|
|||||||
import com.test.test.domain.TestPlan;
|
import com.test.test.domain.TestPlan;
|
||||||
import com.test.test.domain.qo.TestPlanListQO;
|
import com.test.test.domain.qo.TestPlanListQO;
|
||||||
import com.test.test.domain.vo.TestPlanListVO;
|
import com.test.test.domain.vo.TestPlanListVO;
|
||||||
|
import com.test.test.domain.vo.TestPlanOverviewTrendDataVO;
|
||||||
|
import com.test.test.domain.vo.TestPlanOverviewVO;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface TestPlanMapper {
|
public interface TestPlanMapper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,4 +42,18 @@ public interface TestPlanMapper {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Long selectPlanId(String serialNumber);
|
Long selectPlanId(String serialNumber);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询测试计划概览信息
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
TestPlanOverviewVO selectPlanOverview(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询测试计划用例执行趋势数据
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<TestPlanOverviewTrendDataVO> getCaseTrendData(Long id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,5 +40,12 @@ public interface TestProjectMapper {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
int updateTestProject(TestProject testProject);
|
int updateTestProject(TestProject testProject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询缺陷关联的测试计划
|
||||||
|
* @param defectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<TestProject> selectRelateProjectList(Long defectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,4 +19,11 @@ public interface TestProjectPlanMapper {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<TestPlanProjectVo> selectTestPlanProjectList(Long planId);
|
List<TestPlanProjectVo> selectTestPlanProjectList(Long planId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询需求关联计划列表
|
||||||
|
* @param projectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<TestPlanProjectVo> selectRelatePlanList(Long projectId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,18 @@ public interface TestReportMapper {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
int addTestReport(TestReport testReport);
|
int addTestReport(TestReport testReport);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询用例执行测试报告详情
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
TestReport selectCaseTestReportById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改用例执行测试报告
|
||||||
|
* @param testReport
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int updateExecuteCaseReport(TestReport testReport);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ public interface IPerformanceTestCaseReportService
|
|||||||
* @param id
|
* @param id
|
||||||
* @param jmeterHomePath
|
* @param jmeterHomePath
|
||||||
* @param triggerType 触发方式:1-定时任务;2-手动
|
* @param triggerType 触发方式:1-定时任务;2-手动
|
||||||
|
* @param createBy 创建人
|
||||||
* @return 返回本次性能测试的批次id
|
* @return 返回本次性能测试的批次id
|
||||||
*/
|
*/
|
||||||
public Long executePerformanceTestAndReport(Long id, String jmeterHomePath, Integer triggerType);
|
public Long executePerformanceTestAndReport(Long id, String jmeterHomePath, Integer triggerType, String createBy);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询性能测试用例报告
|
* 查询性能测试用例报告
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package com.test.test.service;
|
package com.test.test.service;
|
||||||
|
|
||||||
|
import com.test.test.domain.TestPlan;
|
||||||
import com.test.test.domain.TestPlanDefect;
|
import com.test.test.domain.TestPlanDefect;
|
||||||
|
|
||||||
|
import com.test.test.domain.TestProject;
|
||||||
|
import com.test.test.domain.qo.IDQO;
|
||||||
import com.test.test.domain.qo.TestPlanDefectQO;
|
import com.test.test.domain.qo.TestPlanDefectQO;
|
||||||
import com.test.test.domain.qo.TestPlanDefectRelQO;
|
import com.test.test.domain.qo.TestPlanDefectRelQO;
|
||||||
import com.test.test.domain.vo.TestPlanDefectVo;
|
import com.test.test.domain.vo.TestPlanDefectVo;
|
||||||
@@ -62,4 +65,18 @@ public interface ITestPlanDefectService
|
|||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public int deleteTestPlanDefectById(Long id);
|
public int deleteTestPlanDefectById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询缺陷关联的测试计划列表
|
||||||
|
* @param defectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<TestPlan> selectDefectPlanList(Long defectId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询缺陷关联的需求列表
|
||||||
|
* @param defectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<TestProject> selectDefectProjectList(Long defectId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.test.test.service;
|
package com.test.test.service;
|
||||||
|
|
||||||
|
import com.test.test.domain.TestCase;
|
||||||
|
import com.test.test.domain.TestDefect;
|
||||||
import com.test.test.domain.vo.TestPlanProjectVo;
|
import com.test.test.domain.vo.TestPlanProjectVo;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -14,4 +16,25 @@ public interface ITestPlanProjectService {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<TestPlanProjectVo> selectTestPlanProjectList(Long planId);
|
List<TestPlanProjectVo> selectTestPlanProjectList(Long planId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询需求关联测试计划列表
|
||||||
|
* @param projectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<TestPlanProjectVo> selectRelatePlanList(Long projectId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询需求关联用例列表
|
||||||
|
* @param projectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<TestCase> selectRelateCaseList(Long projectId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询需求关联缺陷列表
|
||||||
|
* @param projectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<TestDefect> selectRelateDefectList(Long projectId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import com.test.test.domain.TestPlan;
|
|||||||
import com.test.test.domain.qo.TestPlanAddQO;
|
import com.test.test.domain.qo.TestPlanAddQO;
|
||||||
import com.test.test.domain.qo.TestPlanListQO;
|
import com.test.test.domain.qo.TestPlanListQO;
|
||||||
import com.test.test.domain.vo.TestPlanListVO;
|
import com.test.test.domain.vo.TestPlanListVO;
|
||||||
|
import com.test.test.domain.vo.TestPlanOverviewTrendDataVO;
|
||||||
|
import com.test.test.domain.vo.TestPlanOverviewVO;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,4 +43,18 @@ public interface ITestPlanService {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public int updateTestPlan(TestPlan testPlan);
|
public int updateTestPlan(TestPlan testPlan);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取测试计划概览信息
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
TestPlanOverviewVO selectPlanOverview(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取测试计划用例趋势数据
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<TestPlanOverviewTrendDataVO> getCaseTrendData(Long id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,18 @@ public interface ITestReportService {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public int addTestReport(TestReportAddQO testReportAddQO);
|
public int addTestReport(TestReportAddQO testReportAddQO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新执行用例测试报告
|
||||||
|
* @param testReport
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int updateExecuteCaseReport(TestReport testReport);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询测试报告详情
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public TestReport selectCaseTestReportById(Long id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ public interface IUiSceneStepsService
|
|||||||
* @param automationId 场景id
|
* @param automationId 场景id
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Map<String,Object> executeStep(Long automationId);
|
Map<String,Object> executeStep(Long automationId,String triggerMode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class PerformanceTestCaseReportServiceImpl implements IPerformanceTestCas
|
|||||||
private ITestCaseStepService testCaseStepService;
|
private ITestCaseStepService testCaseStepService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long executePerformanceTestAndReport(Long id, String jmeterHomePath, Integer triggerType) {
|
public Long executePerformanceTestAndReport(Long id, String jmeterHomePath, Integer triggerType, String createBy) {
|
||||||
Long sid = System.currentTimeMillis();
|
Long sid = System.currentTimeMillis();
|
||||||
PerformanceTest performanceTest = performanceTestMapper.selectPerformanceTestById(id);
|
PerformanceTest performanceTest = performanceTestMapper.selectPerformanceTestById(id);
|
||||||
PerformanceTestCase performanceTestCase = new PerformanceTestCase();
|
PerformanceTestCase performanceTestCase = new PerformanceTestCase();
|
||||||
@@ -68,6 +68,9 @@ public class PerformanceTestCaseReportServiceImpl implements IPerformanceTestCas
|
|||||||
jmeterGroupRequest.setRampUpSeconds(performanceTest.getRampUpSeconds());
|
jmeterGroupRequest.setRampUpSeconds(performanceTest.getRampUpSeconds());
|
||||||
Long seconds = performanceTest.getPressureHour() * 3600L + performanceTest.getPressureMinute() * 60L + performanceTest.getPressureSecond() * 1L;
|
Long seconds = performanceTest.getPressureHour() * 3600L + performanceTest.getPressureMinute() * 60L + performanceTest.getPressureSecond() * 1L;
|
||||||
jmeterGroupRequest.setPressureSecond(seconds);
|
jmeterGroupRequest.setPressureSecond(seconds);
|
||||||
|
if (seconds == 0L) {
|
||||||
|
jmeterGroupRequest.setPressureSecond(1L);
|
||||||
|
}
|
||||||
jmeterGroupRequest.setLoopCount(performanceTest.getLoopCount());
|
jmeterGroupRequest.setLoopCount(performanceTest.getLoopCount());
|
||||||
jmeterGroupRequest.setRpsStatus(performanceTest.getRpsStatus());
|
jmeterGroupRequest.setRpsStatus(performanceTest.getRpsStatus());
|
||||||
jmeterGroupRequest.setRpsLimit(performanceTest.getRpsLimit());
|
jmeterGroupRequest.setRpsLimit(performanceTest.getRpsLimit());
|
||||||
@@ -86,6 +89,7 @@ public class PerformanceTestCaseReportServiceImpl implements IPerformanceTestCas
|
|||||||
if (!CollectionUtils.isEmpty(jmeterResultList)) {
|
if (!CollectionUtils.isEmpty(jmeterResultList)) {
|
||||||
LabelStatsEntity lastElement = jmeterResultList.get(jmeterResultList.size() - 1);
|
LabelStatsEntity lastElement = jmeterResultList.get(jmeterResultList.size() - 1);
|
||||||
PerformanceTestCaseReport performanceTestCaseReport = new PerformanceTestCaseReport();
|
PerformanceTestCaseReport performanceTestCaseReport = new PerformanceTestCaseReport();
|
||||||
|
performanceTestCaseReport.setCreateBy(createBy);
|
||||||
performanceTestCaseReport.setPerformanceId(id);
|
performanceTestCaseReport.setPerformanceId(id);
|
||||||
performanceTestCaseReport.setTestCaseId(relateTestCase.getTestCaseId());
|
performanceTestCaseReport.setTestCaseId(relateTestCase.getTestCaseId());
|
||||||
performanceTestCaseReport.setSid(sid);
|
performanceTestCaseReport.setSid(sid);
|
||||||
@@ -163,7 +167,7 @@ public class PerformanceTestCaseReportServiceImpl implements IPerformanceTestCas
|
|||||||
JmeterRequest jmeterRequest = new JmeterRequest();
|
JmeterRequest jmeterRequest = new JmeterRequest();
|
||||||
jmeterRequest.setTestCaseName(testCaseStep.getName());
|
jmeterRequest.setTestCaseName(testCaseStep.getName());
|
||||||
jmeterRequest.setUrl(url);
|
jmeterRequest.setUrl(url);
|
||||||
jmeterRequest.setPort(testCaseStep.getApiPort());
|
jmeterRequest.setPort(testCaseStep.getApiPort() == null ? 80 : testCaseStep.getApiPort());
|
||||||
jmeterRequest.setMethod(testCaseStep.getRequestMethod());
|
jmeterRequest.setMethod(testCaseStep.getRequestMethod());
|
||||||
jmeterRequest.setRequestBody(testCaseStep.getRequestBody());
|
jmeterRequest.setRequestBody(testCaseStep.getRequestBody());
|
||||||
jmeterRequest.setRequestParams(testCaseStep.getRequestParams());
|
jmeterRequest.setRequestParams(testCaseStep.getRequestParams());
|
||||||
|
|||||||
@@ -229,13 +229,15 @@ public class TestCaseServiceImpl implements ITestCaseService
|
|||||||
for (TestPlanCase input : testPlanCaseList) {
|
for (TestPlanCase input : testPlanCaseList) {
|
||||||
boolean isSuccess = true;
|
boolean isSuccess = true;
|
||||||
input.setExecuteTime(DateUtils.getNowDate());
|
input.setExecuteTime(DateUtils.getNowDate());
|
||||||
input.setExecuteBy(SecurityUtils.getUsername());
|
input.setExecuteBy(testPlanCase.getExecuteBy());
|
||||||
TestCase testCase = this.selectTestCaseById(input.getCaseId());
|
TestCase testCase = this.selectTestCaseById(input.getCaseId());
|
||||||
Long id = input.getCaseId();
|
Long id = input.getCaseId();
|
||||||
Map<String, String> contextResultMap = new HashMap<>();
|
Map<String, String> contextResultMap = new HashMap<>();
|
||||||
testCase.setContextResultMap(contextResultMap);
|
testCase.setContextResultMap(contextResultMap);
|
||||||
TestCaseLog testCaseLog = new TestCaseLog();
|
TestCaseLog testCaseLog = new TestCaseLog();
|
||||||
testCaseLog.setCaseId(id);
|
testCaseLog.setCaseId(id);
|
||||||
|
testCaseLog.setPlanId(testPlanCase.getPlanId());
|
||||||
|
testCaseLog.setType(testPlanCase.getType());
|
||||||
testCaseLog.setOperTime(DateUtils.getNowDate());
|
testCaseLog.setOperTime(DateUtils.getNowDate());
|
||||||
testCaseLog.setOperType("执行");
|
testCaseLog.setOperType("执行");
|
||||||
testCaseLog.setCaseSid(caseSid);
|
testCaseLog.setCaseSid(caseSid);
|
||||||
@@ -290,13 +292,14 @@ public class TestCaseServiceImpl implements ITestCaseService
|
|||||||
}
|
}
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
testCaseLog.setOperDetail("成功");
|
testCaseLog.setOperDetail("成功");
|
||||||
input.setExecuteResult("成功");
|
input.setExecuteResult("1");
|
||||||
} else {
|
} else {
|
||||||
testCaseLog.setOperDetail("失败");
|
testCaseLog.setOperDetail("失败");
|
||||||
input.setExecuteResult("失败");
|
input.setExecuteResult("2");
|
||||||
}
|
}
|
||||||
testCaseLogMapper.insertTestCaseLog(testCaseLog);
|
testCaseLogMapper.insertTestCaseLog(testCaseLog);
|
||||||
testPlanCaseMapper.updateTestPlanCase(input);
|
testPlanCaseMapper.updateTestPlanCase(input);
|
||||||
|
System.out.println("111111111111111111111111");
|
||||||
}
|
}
|
||||||
testPlan.setUpdateTime(DateUtils.getNowDate());
|
testPlan.setUpdateTime(DateUtils.getNowDate());
|
||||||
testPlan.setStatus("2");
|
testPlan.setStatus("2");
|
||||||
@@ -339,7 +342,8 @@ public class TestCaseServiceImpl implements ITestCaseService
|
|||||||
useTime = Long.valueOf(resultMap.get("costMiliseconds"));
|
useTime = Long.valueOf(resultMap.get("costMiliseconds"));
|
||||||
}
|
}
|
||||||
testCaseResult.setUseTime(useTime);
|
testCaseResult.setUseTime(useTime);
|
||||||
if ("fail".equals(assignmentResultMap.get("assertionResult"))) {
|
String responseCode = resultMap.get("responseCode");
|
||||||
|
if ("fail".equals(assignmentResultMap.get("assertionResult")) || responseCode == null || resultMap.get("responseCode").startsWith("Non HTTP response code")) {
|
||||||
testCaseResult.setStatus("失败");
|
testCaseResult.setStatus("失败");
|
||||||
testCaseResultMapper.insertTestCaseResult(testCaseResult);
|
testCaseResultMapper.insertTestCaseResult(testCaseResult);
|
||||||
return false;
|
return false;
|
||||||
@@ -393,7 +397,10 @@ public class TestCaseServiceImpl implements ITestCaseService
|
|||||||
// sql查询特有字段结果存储
|
// sql查询特有字段结果存储
|
||||||
Map<String, String> sqlSpecialResultMap = new HashMap<>();
|
Map<String, String> sqlSpecialResultMap = new HashMap<>();
|
||||||
// 获取所有sql查询集合对象
|
// 获取所有sql查询集合对象
|
||||||
List<Map<String, Object>> resultMapList = MySQLExecutor.executeQuery(testCaseStep.getSqlCommand(), url, testDatasource.getUsername(), testDatasource.getPassword(), columnNameList, sqlSpecialResultMap);
|
String sqlCommand = testCaseStep.getSqlCommand();
|
||||||
|
sqlCommand = MySQLExecutor.replaceSqlVariables(sqlCommand, testCase.getContextResultMap());
|
||||||
|
log.info("{}###Replace value sqlCommand:{}", testCase.getContextResultMap().keySet(), sqlCommand);
|
||||||
|
List<Map<String, Object>> resultMapList = MySQLExecutor.executeQuery(sqlCommand, url, testDatasource.getUsername(), testDatasource.getPassword(), columnNameList, sqlSpecialResultMap);
|
||||||
if (!CollectionUtils.isEmpty(resultMapList)) {
|
if (!CollectionUtils.isEmpty(resultMapList)) {
|
||||||
SqlResult sqlResult = new SqlResult();
|
SqlResult sqlResult = new SqlResult();
|
||||||
sqlResult.setColumnNameList(columnNameList);
|
sqlResult.setColumnNameList(columnNameList);
|
||||||
@@ -644,7 +651,10 @@ public class TestCaseServiceImpl implements ITestCaseService
|
|||||||
*/
|
*/
|
||||||
private String dealDataSourceTestCaseStepAssignment(List<Map<String, Object>> resultMapList, Map<String, String> assignmentResultMap, Map<String, String> sqlSpecialResultMap, String assignment) {
|
private String dealDataSourceTestCaseStepAssignment(List<Map<String, Object>> resultMapList, Map<String, String> assignmentResultMap, Map<String, String> sqlSpecialResultMap, String assignment) {
|
||||||
if (!StringUtils.isEmpty(assignment) && !"[]".equals(assignment)) {
|
if (!StringUtils.isEmpty(assignment) && !"[]".equals(assignment)) {
|
||||||
Gson gson = new Gson();
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Byte.class, new TinyIntTypeAdapter())
|
||||||
|
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeConverter())
|
||||||
|
.create();
|
||||||
String responseBody = gson.toJson(resultMapList);
|
String responseBody = gson.toJson(resultMapList);
|
||||||
Type ruleListType = new TypeToken<List<ExtractionRule>>() {}.getType();
|
Type ruleListType = new TypeToken<List<ExtractionRule>>() {}.getType();
|
||||||
// 将 JSON 字符串解析为 List<ExtractionRule>
|
// 将 JSON 字符串解析为 List<ExtractionRule>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -216,14 +217,23 @@ public class TestCaseStepServiceImpl implements ITestCaseStepService {
|
|||||||
JmeterRequest jmeterRequest = new JmeterRequest();
|
JmeterRequest jmeterRequest = new JmeterRequest();
|
||||||
jmeterRequest.setId(id);
|
jmeterRequest.setId(id);
|
||||||
jmeterRequest.setUrl(url);
|
jmeterRequest.setUrl(url);
|
||||||
jmeterRequest.setPort(testCaseStep.getApiPort());
|
jmeterRequest.setPort(testCaseStep.getApiPort() == null ? 80 : testCaseStep.getApiPort());
|
||||||
jmeterRequest.setMethod(testCaseStep.getRequestMethod());
|
jmeterRequest.setMethod(testCaseStep.getRequestMethod());
|
||||||
jmeterRequest.setRequestBody(testCaseStep.getRequestBody());
|
jmeterRequest.setRequestBody(testCaseStep.getRequestBody());
|
||||||
jmeterRequest.setRequestParams(testCaseStep.getRequestParams());
|
jmeterRequest.setRequestParams(testCaseStep.getRequestParams());
|
||||||
jmeterRequest.setRequestHeader(testCaseStep.getRequestHeader());
|
jmeterRequest.setRequestHeader(testCaseStep.getRequestHeader());
|
||||||
jmeterRequest.setJmeterHomePath(jmeterHomePath);
|
jmeterRequest.setJmeterHomePath(jmeterHomePath);
|
||||||
log.info("getRequestHeader:{}", jmeterRequest.getRequestHeader());
|
log.info("getRequestHeader:{}", jmeterRequest.getRequestHeader());
|
||||||
Map<String, String> resultMap = JMeterUtil.getJmeterResult(jmeterRequest);
|
Map<String, String> resultMap = new HashMap<>();
|
||||||
|
try {
|
||||||
|
resultMap = JMeterUtil.getJmeterResult(jmeterRequest);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("JMeterUtil异常:", e);
|
||||||
|
resultMap.put("requestHeader", jmeterRequest.getRequestHeader());
|
||||||
|
resultMap.put("requestBody", jmeterRequest.getRequestBody());
|
||||||
|
resultMap.put("responseHeader", "");
|
||||||
|
resultMap.put("responseBody", "");
|
||||||
|
}
|
||||||
|
|
||||||
return resultMap;
|
return resultMap;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package com.test.test.service.impl;
|
package com.test.test.service.impl;
|
||||||
|
|
||||||
import com.test.common.utils.DateUtils;
|
import com.test.common.utils.DateUtils;
|
||||||
|
import com.test.test.domain.TestPlan;
|
||||||
import com.test.test.domain.TestPlanDefect;
|
import com.test.test.domain.TestPlanDefect;
|
||||||
|
import com.test.test.domain.TestProject;
|
||||||
|
import com.test.test.domain.qo.IDQO;
|
||||||
import com.test.test.domain.qo.TestPlanDefectQO;
|
import com.test.test.domain.qo.TestPlanDefectQO;
|
||||||
import com.test.test.domain.qo.TestPlanDefectRelQO;
|
import com.test.test.domain.qo.TestPlanDefectRelQO;
|
||||||
import com.test.test.domain.vo.TestPlanDefectVo;
|
import com.test.test.domain.vo.TestPlanDefectVo;
|
||||||
import com.test.test.mapper.TestPlanDefectMapper;
|
import com.test.test.mapper.TestPlanDefectMapper;
|
||||||
|
import com.test.test.mapper.TestProjectMapper;
|
||||||
import com.test.test.service.ITestPlanDefectService;
|
import com.test.test.service.ITestPlanDefectService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -25,6 +29,9 @@ public class TestPlanDefectServiceImpl implements ITestPlanDefectService
|
|||||||
@Resource
|
@Resource
|
||||||
private TestPlanDefectMapper testPlanDefectMapper;
|
private TestPlanDefectMapper testPlanDefectMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TestProjectMapper testProjectMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询测试计划测试缺陷关联
|
* 查询测试计划测试缺陷关联
|
||||||
*
|
*
|
||||||
@@ -118,4 +125,24 @@ public class TestPlanDefectServiceImpl implements ITestPlanDefectService
|
|||||||
{
|
{
|
||||||
return testPlanDefectMapper.deleteTestPlanDefectById(id);
|
return testPlanDefectMapper.deleteTestPlanDefectById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询缺陷关联计划列表
|
||||||
|
* @param defectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<TestPlan> selectDefectPlanList(Long defectId) {
|
||||||
|
return testPlanDefectMapper.selectDefectPlanList(defectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询缺陷关联需求列表
|
||||||
|
* @param defectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<TestProject> selectDefectProjectList(Long defectId) {
|
||||||
|
return testProjectMapper.selectRelateProjectList(defectId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package com.test.test.service.impl;
|
package com.test.test.service.impl;
|
||||||
|
|
||||||
|
import com.test.test.domain.TestCase;
|
||||||
|
import com.test.test.domain.TestDefect;
|
||||||
import com.test.test.domain.vo.TestPlanProjectVo;
|
import com.test.test.domain.vo.TestPlanProjectVo;
|
||||||
|
import com.test.test.mapper.TestCaseMapper;
|
||||||
|
import com.test.test.mapper.TestDefectMapper;
|
||||||
import com.test.test.mapper.TestProjectPlanMapper;
|
import com.test.test.mapper.TestProjectPlanMapper;
|
||||||
import com.test.test.service.ITestPlanProjectService;
|
import com.test.test.service.ITestPlanProjectService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -18,6 +22,12 @@ public class TestPlanProjectServiceImpl implements ITestPlanProjectService {
|
|||||||
@Resource
|
@Resource
|
||||||
private TestProjectPlanMapper testProjectPlanMapper;
|
private TestProjectPlanMapper testProjectPlanMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TestCaseMapper testCaseMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TestDefectMapper testDefectMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询测试计划需求关联列表
|
* 查询测试计划需求关联列表
|
||||||
* @param planId
|
* @param planId
|
||||||
@@ -27,4 +37,34 @@ public class TestPlanProjectServiceImpl implements ITestPlanProjectService {
|
|||||||
public List<TestPlanProjectVo> selectTestPlanProjectList(Long planId) {
|
public List<TestPlanProjectVo> selectTestPlanProjectList(Long planId) {
|
||||||
return testProjectPlanMapper.selectTestPlanProjectList(planId);
|
return testProjectPlanMapper.selectTestPlanProjectList(planId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询需求关联计划列表
|
||||||
|
* @param projectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<TestPlanProjectVo> selectRelatePlanList(Long projectId) {
|
||||||
|
return testProjectPlanMapper.selectRelatePlanList(projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询需求关联用例列表
|
||||||
|
* @param projectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<TestCase> selectRelateCaseList(Long projectId) {
|
||||||
|
return testCaseMapper.selectRelateCaseList(projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询需求关联缺陷列表
|
||||||
|
* @param projectId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<TestDefect> selectRelateDefectList(Long projectId) {
|
||||||
|
return testDefectMapper.selectRelateDefectList(projectId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,15 @@ import com.test.test.domain.TestPlan;
|
|||||||
import com.test.test.domain.qo.TestPlanAddQO;
|
import com.test.test.domain.qo.TestPlanAddQO;
|
||||||
import com.test.test.domain.qo.TestPlanListQO;
|
import com.test.test.domain.qo.TestPlanListQO;
|
||||||
import com.test.test.domain.vo.TestPlanListVO;
|
import com.test.test.domain.vo.TestPlanListVO;
|
||||||
|
import com.test.test.domain.vo.TestPlanOverviewTrendDataVO;
|
||||||
|
import com.test.test.domain.vo.TestPlanOverviewVO;
|
||||||
import com.test.test.mapper.TestPlanMapper;
|
import com.test.test.mapper.TestPlanMapper;
|
||||||
import com.test.test.mapper.TestProjectPlanMapper;
|
import com.test.test.mapper.TestProjectPlanMapper;
|
||||||
import com.test.test.service.ITestPlanService;
|
import com.test.test.service.ITestPlanService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -82,4 +87,39 @@ public class TestPlanServiceImpl implements ITestPlanService {
|
|||||||
testPlan.setUpdateTime(DateUtils.getNowDate());
|
testPlan.setUpdateTime(DateUtils.getNowDate());
|
||||||
return testPlanMapper.updateTestPlan(testPlan);
|
return testPlanMapper.updateTestPlan(testPlan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取测试计划概览信息
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public TestPlanOverviewVO selectPlanOverview(Long id) {
|
||||||
|
TestPlanOverviewVO overviewVO = testPlanMapper.selectPlanOverview(id);
|
||||||
|
overviewVO.setCaseTrendDates(getCaseTrendDates());
|
||||||
|
return overviewVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取测试计划用例趋势数据
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<TestPlanOverviewTrendDataVO> getCaseTrendData(Long id) {
|
||||||
|
return testPlanMapper.getCaseTrendData(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getCaseTrendDates() {
|
||||||
|
List<String> dates = new ArrayList<>();
|
||||||
|
LocalDate today = LocalDate.now();
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
|
||||||
|
for (int i = 6; i >= 0; i--) {
|
||||||
|
LocalDate date = today.minusDays(i);
|
||||||
|
dates.add(date.format(formatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dates;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
package com.test.test.service.impl;
|
package com.test.test.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.test.common.utils.DateUtils;
|
import com.test.common.utils.DateUtils;
|
||||||
import com.test.common.utils.SecurityUtils;
|
import com.test.common.utils.SecurityUtils;
|
||||||
|
import com.test.common.utils.StringUtils;
|
||||||
import com.test.common.utils.bean.BeanUtils;
|
import com.test.common.utils.bean.BeanUtils;
|
||||||
|
import com.test.test.domain.TestCaseResult;
|
||||||
import com.test.test.domain.TestReport;
|
import com.test.test.domain.TestReport;
|
||||||
import com.test.test.domain.qo.TestReportAddQO;
|
import com.test.test.domain.qo.TestReportAddQO;
|
||||||
|
import com.test.test.domain.vo.TestCaseReportVO;
|
||||||
import com.test.test.domain.vo.TestReportVo;
|
import com.test.test.domain.vo.TestReportVo;
|
||||||
|
import com.test.test.mapper.TestCaseLogMapper;
|
||||||
|
import com.test.test.mapper.TestCaseResultMapper;
|
||||||
import com.test.test.mapper.TestPlanReportMapper;
|
import com.test.test.mapper.TestPlanReportMapper;
|
||||||
import com.test.test.mapper.TestReportMapper;
|
import com.test.test.mapper.TestReportMapper;
|
||||||
import com.test.test.service.ITestReportService;
|
import com.test.test.service.ITestReportService;
|
||||||
@@ -29,6 +35,12 @@ public class TestReportServiceImpl implements ITestReportService {
|
|||||||
@Resource
|
@Resource
|
||||||
private TestPlanReportMapper testPlanReportMapper;
|
private TestPlanReportMapper testPlanReportMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TestCaseLogMapper testCaseLogMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TestCaseResultMapper testCaseResultMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询测试报告列表
|
* 查询测试报告列表
|
||||||
* @param planId
|
* @param planId
|
||||||
@@ -46,11 +58,33 @@ public class TestReportServiceImpl implements ITestReportService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int addTestReport(TestReportAddQO testReportAddQO) {
|
public int addTestReport(TestReportAddQO testReportAddQO) {
|
||||||
|
String caseSid = testCaseLogMapper.selectTestCaseLogCaseSid(testReportAddQO);
|
||||||
|
if (StringUtils.isEmpty(caseSid)) {
|
||||||
|
throw new RuntimeException("未执行用例,无法生成报告");
|
||||||
|
}
|
||||||
|
TestCaseResult testCaseResult = new TestCaseResult();
|
||||||
|
testCaseResult.setCaseSid(caseSid);
|
||||||
|
List<TestCaseResult> testCaseResults = testCaseResultMapper.selectTestCaseResultList(testCaseResult);
|
||||||
|
int count = testCaseResults.size();
|
||||||
|
int passNum = (int)testCaseResults.stream().filter(result -> "成功".equals(result.getStatus())).count();
|
||||||
|
if (count == passNum) {
|
||||||
|
testReportAddQO.setResult("1");
|
||||||
|
}
|
||||||
|
TestCaseReportVO testCaseReportVO = new TestCaseReportVO();
|
||||||
|
testCaseReportVO.setName(testReportAddQO.getName());
|
||||||
|
testCaseReportVO.setResult("0");
|
||||||
|
testCaseReportVO.setCaseNum(count);
|
||||||
|
testCaseReportVO.setPassNum(passNum);
|
||||||
|
String reportJson = JSONObject.toJSONString(testCaseReportVO);
|
||||||
TestReport testReport = new TestReport();
|
TestReport testReport = new TestReport();
|
||||||
|
if (count == passNum) {
|
||||||
|
testReport.setResult("1");
|
||||||
|
}
|
||||||
BeanUtils.copyProperties(testReportAddQO,testReport);
|
BeanUtils.copyProperties(testReportAddQO,testReport);
|
||||||
testReport.setSerialNumber(generateSerialNumber());
|
testReport.setSerialNumber(generateSerialNumber());
|
||||||
testReport.setResult("0");
|
testReport.setResult("0");
|
||||||
testReport.setStatus("0");
|
testReport.setStatus("0");
|
||||||
|
testReport.setReport(reportJson);
|
||||||
testReport.setCreateTime(DateUtils.getNowDate());
|
testReport.setCreateTime(DateUtils.getNowDate());
|
||||||
testReport.setDelFlag("0");
|
testReport.setDelFlag("0");
|
||||||
testReport.setUpdateBy(SecurityUtils.getUsername());
|
testReport.setUpdateBy(SecurityUtils.getUsername());
|
||||||
@@ -61,6 +95,27 @@ public class TestReportServiceImpl implements ITestReportService {
|
|||||||
return testPlanReportMapper.insertTestPlanReport(testReportAddQO);
|
return testPlanReportMapper.insertTestPlanReport(testReportAddQO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新执行用例报告
|
||||||
|
* @param testReport
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int updateExecuteCaseReport(TestReport testReport) {
|
||||||
|
testReport.setUpdateTime(DateUtils.getNowDate());
|
||||||
|
return testReportMapper.updateExecuteCaseReport(testReport);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询报告
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public TestReport selectCaseTestReportById(Long id) {
|
||||||
|
return testReportMapper.selectCaseTestReportById(id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成随机序列号
|
* 生成随机序列号
|
||||||
* @return
|
* @return
|
||||||
|
|||||||
@@ -70,7 +70,16 @@ public class TestTaskServiceImpl implements ITestTaskService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int insertTestTask(TestTask testTask) {
|
public int insertTestTask(TestTask testTask) {
|
||||||
return testTaskMapper.insertTestTask(testTask);
|
String operUser = testTask.getCreateBy();
|
||||||
|
int i = testTaskMapper.insertTestTask(testTask);
|
||||||
|
TestTaskLog testTaskLog = new TestTaskLog();
|
||||||
|
testTaskLog.setTaskId(testTask.getId());
|
||||||
|
testTaskLog.setOperType("新增");
|
||||||
|
testTaskLog.setOperDetail("操作人:" + operUser + "新增了【" + testTask.getName() + "】定时任务测试用例");
|
||||||
|
testTaskLog.setOperUser(operUser);
|
||||||
|
testTaskLog.setOperTime(DateUtils.getNowDate());
|
||||||
|
testTaskLogMapper.insertTestTaskLog(testTaskLog);
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,8 +87,17 @@ public class TestTaskServiceImpl implements ITestTaskService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int updateTestTask(TestTask testTask) {
|
public int updateTestTask(TestTask testTask) {
|
||||||
|
String operUser = testTask.getCreateBy();
|
||||||
testTask.setUpdateTime(DateUtils.getNowDate());
|
testTask.setUpdateTime(DateUtils.getNowDate());
|
||||||
return testTaskMapper.updateTestTask(testTask);
|
int i = testTaskMapper.updateTestTask(testTask);
|
||||||
|
TestTaskLog testTaskLog = new TestTaskLog();
|
||||||
|
testTaskLog.setTaskId(testTask.getId());
|
||||||
|
testTaskLog.setOperType("修改");
|
||||||
|
testTaskLog.setOperDetail("操作人:" + operUser + "修改了【" + testTask.getName() + "】定时任务测试用例");
|
||||||
|
testTaskLog.setOperUser(operUser);
|
||||||
|
testTaskLog.setOperTime(DateUtils.getNowDate());
|
||||||
|
testTaskLogMapper.insertTestTaskLog(testTaskLog);
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,13 +132,15 @@ public class TestTaskServiceImpl implements ITestTaskService {
|
|||||||
log.error("定时任务已删除,不能执行!");
|
log.error("定时任务已删除,不能执行!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TestTaskLog testTaskLog = new TestTaskLog();
|
if (triggerType == 2) {
|
||||||
testTaskLog.setTaskId(id);
|
TestTaskLog testTaskLog = new TestTaskLog();
|
||||||
testTaskLog.setOperType("执行");
|
testTaskLog.setTaskId(id);
|
||||||
testTaskLog.setOperDetail("操作人:" + operUser + "提交执行了【" + testTask.getName() + "】定时任务测试用例");
|
testTaskLog.setOperType("执行");
|
||||||
testTaskLog.setOperUser(operUser);
|
testTaskLog.setOperDetail("操作人:" + operUser + "提交执行了【" + testTask.getName() + "】定时任务测试用例");
|
||||||
testTaskLog.setOperTime(DateUtils.getNowDate());
|
testTaskLog.setOperUser(operUser);
|
||||||
testTaskLogMapper.insertTestTaskLog(testTaskLog);
|
testTaskLog.setOperTime(DateUtils.getNowDate());
|
||||||
|
testTaskLogMapper.insertTestTaskLog(testTaskLog);
|
||||||
|
}
|
||||||
TestTaskResult testTaskResult = new TestTaskResult();
|
TestTaskResult testTaskResult = new TestTaskResult();
|
||||||
testTaskResult.setTaskId(id);
|
testTaskResult.setTaskId(id);
|
||||||
testTaskResult.setTriggerTime(DateUtils.getNowDate());
|
testTaskResult.setTriggerTime(DateUtils.getNowDate());
|
||||||
@@ -148,7 +168,8 @@ public class TestTaskServiceImpl implements ITestTaskService {
|
|||||||
// 开始执行定时任务逻辑。。。
|
// 开始执行定时任务逻辑。。。
|
||||||
if (triggerType == 1) {
|
if (triggerType == 1) {
|
||||||
// 添加定时任务定时执行
|
// 添加定时任务定时执行
|
||||||
taskManagerService.addTask(testTask, jmeterHomePath, testTaskCaseList, testTaskResult);
|
// taskManagerService.addTask(testTask, jmeterHomePath, testTaskCaseList, testTaskResult);
|
||||||
|
taskManagerService.executeTaskWithTestCases(testTask, jmeterHomePath, testTaskCaseList, testTaskResult);
|
||||||
} else {
|
} else {
|
||||||
// 手动立即执行
|
// 手动立即执行
|
||||||
taskManagerService.executeTaskWithTestCases(testTask, jmeterHomePath, testTaskCaseList, testTaskResult);
|
taskManagerService.executeTaskWithTestCases(testTask, jmeterHomePath, testTaskCaseList, testTaskResult);
|
||||||
|
|||||||
@@ -76,16 +76,44 @@ public class UiAutomationServiceImpl implements IUiAutomationService {
|
|||||||
new TypeReference<List<AssertionQO>>() {
|
new TypeReference<List<AssertionQO>>() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
uiHighSettingVO.setAssertionQOList(assertionList);
|
List<AssertionQO> popupTexts = new ArrayList<>();
|
||||||
|
List<AssertionQO> elementAssertions = new ArrayList<>();
|
||||||
|
List<AssertionQO> dropdownBoxes = new ArrayList<>();
|
||||||
|
List<AssertionQO> webTitles = new ArrayList<>();
|
||||||
|
for (AssertionQO extractionQO : assertionList) {
|
||||||
|
if ("1".equals(extractionQO.getAssertionType())) {
|
||||||
|
popupTexts.add(extractionQO);
|
||||||
|
} else if ("2".equals(extractionQO.getAssertionType())) {
|
||||||
|
elementAssertions.add(extractionQO);
|
||||||
|
} else if ("3".equals(extractionQO.getAssertionType())) {
|
||||||
|
dropdownBoxes.add(extractionQO);
|
||||||
|
} else if ("4".equals(extractionQO.getAssertionType())) {
|
||||||
|
webTitles.add(extractionQO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uiHighSettingVO.getAssertionQOList().setPopupTexts(popupTexts);
|
||||||
|
uiHighSettingVO.getAssertionQOList().setElementAssertions(elementAssertions);
|
||||||
|
uiHighSettingVO.getAssertionQOList().setDropdownBoxes(dropdownBoxes);
|
||||||
|
uiHighSettingVO.getAssertionQOList().setWebTitles(webTitles);
|
||||||
// 转换extractionDataJson 数据提取
|
// 转换extractionDataJson 数据提取
|
||||||
if (StringUtils.isNotBlank(uiHighSetting.getExtractionDataJson())) {
|
if (StringUtils.isNotBlank(uiHighSetting.getExtractionDataJson())) {
|
||||||
List<DataExtractionQO> extractionData = null;
|
List<DataExtractionQO> extractionData = null;
|
||||||
extractionData = objectMapper.readValue(
|
extractionData = objectMapper.readValue(
|
||||||
uiHighSetting.getExtractionDataJson(),
|
uiHighSetting.getExtractionDataJson(),
|
||||||
new TypeReference<List<DataExtractionQO>>(){
|
new TypeReference<List<DataExtractionQO>>() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
uiHighSettingVO.setDataExtractionQOList(extractionData);
|
List<DataExtractionQO> windowExtractions = new ArrayList<>();
|
||||||
|
List<DataExtractionQO> elementExtractions = new ArrayList<>();
|
||||||
|
for (DataExtractionQO extractionQO : extractionData) {
|
||||||
|
if ("1".equals(extractionQO.getInformationType())) {
|
||||||
|
windowExtractions.add(extractionQO);
|
||||||
|
} else if ("2".equals(extractionQO.getInformationType())) {
|
||||||
|
elementExtractions.add(extractionQO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uiHighSettingVO.getDataExtractionQOList().setWindowExtractions(windowExtractions);
|
||||||
|
uiHighSettingVO.getDataExtractionQOList().setElementExtractions(elementExtractions);
|
||||||
}
|
}
|
||||||
// 转换otherSetting 其他设置
|
// 转换otherSetting 其他设置
|
||||||
if (StringUtils.isNotBlank(uiHighSetting.getOtherSetting())) {
|
if (StringUtils.isNotBlank(uiHighSetting.getOtherSetting())) {
|
||||||
@@ -122,7 +150,25 @@ public class UiAutomationServiceImpl implements IUiAutomationService {
|
|||||||
new TypeReference<List<AssertionQO>>() {
|
new TypeReference<List<AssertionQO>>() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
uiHighSettingVO.setAssertionQOList(assertionList);
|
List<AssertionQO> popupTexts = new ArrayList<>();
|
||||||
|
List<AssertionQO> elementAssertions = new ArrayList<>();
|
||||||
|
List<AssertionQO> dropdownBoxes = new ArrayList<>();
|
||||||
|
List<AssertionQO> webTitles = new ArrayList<>();
|
||||||
|
for (AssertionQO extractionQO : assertionList) {
|
||||||
|
if ("1".equals(extractionQO.getAssertionType())) {
|
||||||
|
popupTexts.add(extractionQO);
|
||||||
|
} else if ("2".equals(extractionQO.getAssertionType())) {
|
||||||
|
elementAssertions.add(extractionQO);
|
||||||
|
} else if ("3".equals(extractionQO.getAssertionType())) {
|
||||||
|
dropdownBoxes.add(extractionQO);
|
||||||
|
} else if ("4".equals(extractionQO.getAssertionType())) {
|
||||||
|
webTitles.add(extractionQO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uiHighSettingVO.getAssertionQOList().setPopupTexts(popupTexts);
|
||||||
|
uiHighSettingVO.getAssertionQOList().setElementAssertions(elementAssertions);
|
||||||
|
uiHighSettingVO.getAssertionQOList().setDropdownBoxes(dropdownBoxes);
|
||||||
|
uiHighSettingVO.getAssertionQOList().setWebTitles(webTitles);
|
||||||
// 转换extractionDataJson 数据提取
|
// 转换extractionDataJson 数据提取
|
||||||
if (StringUtils.isNotBlank(uiHighSetting.getExtractionDataJson())) {
|
if (StringUtils.isNotBlank(uiHighSetting.getExtractionDataJson())) {
|
||||||
List<DataExtractionQO> extractionData = null;
|
List<DataExtractionQO> extractionData = null;
|
||||||
@@ -131,7 +177,17 @@ public class UiAutomationServiceImpl implements IUiAutomationService {
|
|||||||
new TypeReference<List<DataExtractionQO>>() {
|
new TypeReference<List<DataExtractionQO>>() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
uiHighSettingVO.setDataExtractionQOList(extractionData);
|
List<DataExtractionQO> windowExtractions = new ArrayList<>();
|
||||||
|
List<DataExtractionQO> elementExtractions = new ArrayList<>();
|
||||||
|
for (DataExtractionQO extractionQO : extractionData) {
|
||||||
|
if ("1".equals(extractionQO.getInformationType())) {
|
||||||
|
windowExtractions.add(extractionQO);
|
||||||
|
} else if ("2".equals(extractionQO.getInformationType())) {
|
||||||
|
elementExtractions.add(extractionQO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uiHighSettingVO.getDataExtractionQOList().setWindowExtractions(windowExtractions);
|
||||||
|
uiHighSettingVO.getDataExtractionQOList().setElementExtractions(elementExtractions);
|
||||||
}
|
}
|
||||||
// 转换otherSetting 其他设置
|
// 转换otherSetting 其他设置
|
||||||
if (StringUtils.isNotBlank(uiHighSetting.getOtherSetting())) {
|
if (StringUtils.isNotBlank(uiHighSetting.getOtherSetting())) {
|
||||||
@@ -150,6 +206,7 @@ public class UiAutomationServiceImpl implements IUiAutomationService {
|
|||||||
}
|
}
|
||||||
return uiHighSettingVOList;
|
return uiHighSettingVOList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询ui自动化列表
|
* 查询ui自动化列表
|
||||||
*
|
*
|
||||||
@@ -187,7 +244,6 @@ public class UiAutomationServiceImpl implements IUiAutomationService {
|
|||||||
uiAutomation.setUpdateTime(DateUtils.getNowDate());
|
uiAutomation.setUpdateTime(DateUtils.getNowDate());
|
||||||
uiAutomation.setCreateBy(SecurityUtils.getUsername());
|
uiAutomation.setCreateBy(SecurityUtils.getUsername());
|
||||||
uiAutomation.setUpdateBy(SecurityUtils.getUsername());
|
uiAutomation.setUpdateBy(SecurityUtils.getUsername());
|
||||||
// uiAutomation.setStatus("1"); //未开始
|
|
||||||
uiAutomation.setDutyBy(SecurityUtils.getUsername());
|
uiAutomation.setDutyBy(SecurityUtils.getUsername());
|
||||||
uiAutomation.setDelFlag(0);
|
uiAutomation.setDelFlag(0);
|
||||||
uiAutomation.setCrontabStatus(0); //未开启
|
uiAutomation.setCrontabStatus(0); //未开启
|
||||||
@@ -218,22 +274,50 @@ public class UiAutomationServiceImpl implements IUiAutomationService {
|
|||||||
uiHighSetting.setCreateBy(SecurityUtils.getUsername());
|
uiHighSetting.setCreateBy(SecurityUtils.getUsername());
|
||||||
uiHighSetting.setCreateTime(DateUtils.getNowDate());
|
uiHighSetting.setCreateTime(DateUtils.getNowDate());
|
||||||
try {
|
try {
|
||||||
|
List<DataExtractionQO> combinedList1 = new ArrayList<>();
|
||||||
|
// 安全添加 windowExtractions
|
||||||
|
if (uiHighSettingQO.getDataExtractionQOList() != null &&
|
||||||
|
uiHighSettingQO.getDataExtractionQOList().getWindowExtractions() != null) {
|
||||||
|
combinedList1.addAll(uiHighSettingQO.getDataExtractionQOList().getWindowExtractions());
|
||||||
|
}
|
||||||
|
// 安全添加 elementExtractions
|
||||||
|
if (uiHighSettingQO.getDataExtractionQOList() != null &&
|
||||||
|
uiHighSettingQO.getDataExtractionQOList().getElementExtractions() != null) {
|
||||||
|
combinedList1.addAll(uiHighSettingQO.getDataExtractionQOList().getElementExtractions());
|
||||||
|
}
|
||||||
//数据提取
|
//数据提取
|
||||||
String jsonStr = objectMapper.writeValueAsString(uiHighSettingQO.getDataExtractionQOList());
|
String jsonStr = objectMapper.writeValueAsString(combinedList1);
|
||||||
if (jsonStr != null) {
|
if (jsonStr != null) {
|
||||||
uiHighSetting.setExtractionDataJson(jsonStr);
|
uiHighSetting.setExtractionDataJson(jsonStr);
|
||||||
}
|
}
|
||||||
//断言
|
List<AssertionQO> combinedList2 = new ArrayList<>();
|
||||||
jsonStr = objectMapper.writeValueAsString(uiHighSettingQO.getAssertionQOList());
|
// 安全添加 popupTexts
|
||||||
|
if (uiHighSettingQO.getAssertionQOList() != null &&
|
||||||
|
uiHighSettingQO.getAssertionQOList().getPopupTexts() != null) {
|
||||||
|
combinedList2.addAll(uiHighSettingQO.getAssertionQOList().getPopupTexts());
|
||||||
|
}
|
||||||
|
// 安全添加 elementAssertions
|
||||||
|
if (uiHighSettingQO.getAssertionQOList() != null &&
|
||||||
|
uiHighSettingQO.getAssertionQOList().getElementAssertions() != null) {
|
||||||
|
combinedList2.addAll(uiHighSettingQO.getAssertionQOList().getElementAssertions());
|
||||||
|
}
|
||||||
|
// 安全添加 dropdownBoxes
|
||||||
|
if (uiHighSettingQO.getAssertionQOList() != null &&
|
||||||
|
uiHighSettingQO.getAssertionQOList().getDropdownBoxes() != null) {
|
||||||
|
combinedList2.addAll(uiHighSettingQO.getAssertionQOList().getDropdownBoxes());
|
||||||
|
}
|
||||||
|
// 安全添加 webTitles
|
||||||
|
if (uiHighSettingQO.getAssertionQOList() != null &&
|
||||||
|
uiHighSettingQO.getAssertionQOList().getWebTitles() != null) {
|
||||||
|
combinedList2.addAll(uiHighSettingQO.getAssertionQOList().getWebTitles());
|
||||||
|
}
|
||||||
|
jsonStr = objectMapper.writeValueAsString(combinedList2);
|
||||||
if (jsonStr != null) {
|
if (jsonStr != null) {
|
||||||
uiHighSetting.setAssertionJson(jsonStr);
|
uiHighSetting.setAssertionJson(jsonStr);
|
||||||
}
|
}
|
||||||
//其他设置
|
//其他设置
|
||||||
if (uiHighSettingQO.getWaitElementTime() != null || uiHighSettingQO.getScreenshotConfiguration() != null) {
|
if (uiHighSettingQO.getOtherSettingsQO() != null) {
|
||||||
OtherSettingsQO settings = new OtherSettingsQO();
|
jsonStr = objectMapper.writeValueAsString(uiHighSettingQO.getOtherSettingsQO());
|
||||||
settings.setWaitElementTime(uiHighSettingQO.getWaitElementTime());
|
|
||||||
settings.setScreenshotConfiguration(uiHighSettingQO.getScreenshotConfiguration());
|
|
||||||
jsonStr = objectMapper.writeValueAsString(settings);
|
|
||||||
uiHighSetting.setOtherSetting(jsonStr);
|
uiHighSetting.setOtherSetting(jsonStr);
|
||||||
}
|
}
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
@@ -297,22 +381,51 @@ public class UiAutomationServiceImpl implements IUiAutomationService {
|
|||||||
uiHighSetting.setCreateBy(SecurityUtils.getUsername());
|
uiHighSetting.setCreateBy(SecurityUtils.getUsername());
|
||||||
uiHighSetting.setCreateTime(DateUtils.getNowDate());
|
uiHighSetting.setCreateTime(DateUtils.getNowDate());
|
||||||
try {
|
try {
|
||||||
|
List<DataExtractionQO> combinedList1 = new ArrayList<>();
|
||||||
|
// 安全添加 windowExtractions
|
||||||
|
if (uiHighSettingQO.getDataExtractionQOList() != null &&
|
||||||
|
uiHighSettingQO.getDataExtractionQOList().getWindowExtractions() != null) {
|
||||||
|
combinedList1.addAll(uiHighSettingQO.getDataExtractionQOList().getWindowExtractions());
|
||||||
|
}
|
||||||
|
// 安全添加 elementExtractions
|
||||||
|
if (uiHighSettingQO.getDataExtractionQOList() != null &&
|
||||||
|
uiHighSettingQO.getDataExtractionQOList().getElementExtractions() != null) {
|
||||||
|
combinedList1.addAll(uiHighSettingQO.getDataExtractionQOList().getElementExtractions());
|
||||||
|
}
|
||||||
//数据提取
|
//数据提取
|
||||||
String jsonStr = objectMapper.writeValueAsString(uiHighSettingQO.getDataExtractionQOList());
|
String jsonStr = objectMapper.writeValueAsString(combinedList1);
|
||||||
if (jsonStr != null) {
|
if (jsonStr != null) {
|
||||||
uiHighSetting.setExtractionDataJson(jsonStr);
|
uiHighSetting.setExtractionDataJson(jsonStr);
|
||||||
}
|
}
|
||||||
//断言
|
// 断言
|
||||||
jsonStr = objectMapper.writeValueAsString(uiHighSettingQO.getAssertionQOList());
|
List<AssertionQO> combinedList2 = new ArrayList<>();
|
||||||
|
// 安全添加 popupTexts
|
||||||
|
if (uiHighSettingQO.getAssertionQOList() != null &&
|
||||||
|
uiHighSettingQO.getAssertionQOList().getPopupTexts() != null) {
|
||||||
|
combinedList2.addAll(uiHighSettingQO.getAssertionQOList().getPopupTexts());
|
||||||
|
}
|
||||||
|
// 安全添加 elementAssertions
|
||||||
|
if (uiHighSettingQO.getAssertionQOList() != null &&
|
||||||
|
uiHighSettingQO.getAssertionQOList().getElementAssertions() != null) {
|
||||||
|
combinedList2.addAll(uiHighSettingQO.getAssertionQOList().getElementAssertions());
|
||||||
|
}
|
||||||
|
// 安全添加 dropdownBoxes
|
||||||
|
if (uiHighSettingQO.getAssertionQOList() != null &&
|
||||||
|
uiHighSettingQO.getAssertionQOList().getDropdownBoxes() != null) {
|
||||||
|
combinedList2.addAll(uiHighSettingQO.getAssertionQOList().getDropdownBoxes());
|
||||||
|
}
|
||||||
|
// 安全添加 webTitles
|
||||||
|
if (uiHighSettingQO.getAssertionQOList() != null &&
|
||||||
|
uiHighSettingQO.getAssertionQOList().getWebTitles() != null) {
|
||||||
|
combinedList2.addAll(uiHighSettingQO.getAssertionQOList().getWebTitles());
|
||||||
|
}
|
||||||
|
jsonStr = objectMapper.writeValueAsString(combinedList2);
|
||||||
if (jsonStr != null) {
|
if (jsonStr != null) {
|
||||||
uiHighSetting.setAssertionJson(jsonStr);
|
uiHighSetting.setAssertionJson(jsonStr);
|
||||||
}
|
}
|
||||||
//其他设置
|
//其他设置
|
||||||
if (uiHighSettingQO.getWaitElementTime() != null || uiHighSettingQO.getScreenshotConfiguration() != null) {
|
if (uiHighSettingQO.getOtherSettingsQO() != null) {
|
||||||
OtherSettingsQO settings = new OtherSettingsQO();
|
jsonStr = objectMapper.writeValueAsString(uiHighSettingQO.getOtherSettingsQO());
|
||||||
settings.setWaitElementTime(uiHighSettingQO.getWaitElementTime());
|
|
||||||
settings.setScreenshotConfiguration(uiHighSettingQO.getScreenshotConfiguration());
|
|
||||||
jsonStr = objectMapper.writeValueAsString(settings);
|
|
||||||
uiHighSetting.setOtherSetting(jsonStr);
|
uiHighSetting.setOtherSetting(jsonStr);
|
||||||
}
|
}
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public class UiReportServiceImpl implements IUiReportService
|
|||||||
UiAutomation automation = uiAutomationMapper.selectUiAutomationById(automationId);
|
UiAutomation automation = uiAutomationMapper.selectUiAutomationById(automationId);
|
||||||
String name = automation.getName();
|
String name = automation.getName();
|
||||||
UiReport uiReport = new UiReport();
|
UiReport uiReport = new UiReport();
|
||||||
uiReport.setName(name + "-" + DateUtils.getTime());
|
uiReport.setName(name + "-" + DateUtils.formatDateTime(startTime));
|
||||||
uiReport.setReportType("场景");
|
uiReport.setReportType("场景");
|
||||||
uiReport.setCreateTime(startTime);
|
uiReport.setCreateTime(startTime);
|
||||||
uiReport.setCreateBy(SecurityUtils.getUsername());
|
uiReport.setCreateBy(SecurityUtils.getUsername());
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -30,16 +30,16 @@ public class DynamicTaskManager {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private PerformanceTestMapper performanceTestMapper;
|
private PerformanceTestMapper performanceTestMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private TaskScheduler taskScheduler;
|
private TaskScheduler taskScheduler;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private IPerformanceTestCaseReportService performanceTestCaseReportService;
|
private IPerformanceTestCaseReportService performanceTestCaseReportService;
|
||||||
|
|
||||||
private final Map<Long, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
|
private final Map<Long, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
|
||||||
private final Map<Long, PerformanceTest> taskCache = new ConcurrentHashMap<>();
|
private final Map<Long, PerformanceTest> taskCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
loadTasks(); // 初始加载
|
loadTasks(); // 初始加载
|
||||||
@@ -104,7 +104,7 @@ public class DynamicTaskManager {
|
|||||||
List<PerformanceTest> tasks = performanceTestMapper.selectPerformanceTestList(performanceTest);
|
List<PerformanceTest> tasks = performanceTestMapper.selectPerformanceTestList(performanceTest);
|
||||||
tasks.forEach(this::scheduleTask);
|
tasks.forEach(this::scheduleTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleTask(PerformanceTest task) {
|
private void scheduleTask(PerformanceTest task) {
|
||||||
try {
|
try {
|
||||||
CronTrigger trigger = new CronTrigger(task.getCrontab());
|
CronTrigger trigger = new CronTrigger(task.getCrontab());
|
||||||
@@ -118,17 +118,17 @@ public class DynamicTaskManager {
|
|||||||
log.error("无效的cron表达式: " + task.getCrontab());
|
log.error("无效的cron表达式: " + task.getCrontab());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeTask(PerformanceTest task) {
|
private void executeTask(PerformanceTest task) {
|
||||||
// 执行业务逻辑
|
// 执行业务逻辑
|
||||||
log.info("执行任务: " + task.getId());
|
log.info("执行任务: " + task.getId());
|
||||||
try{
|
try{
|
||||||
performanceTestCaseReportService.executePerformanceTestAndReport(task.getId(),jmeterHomePath,1);
|
performanceTestCaseReportService.executePerformanceTestAndReport(task.getId(),jmeterHomePath,1,"system");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("执行失败!",e);
|
log.error("执行失败!",e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshTasks() {
|
private void refreshTasks() {
|
||||||
PerformanceTest performanceTest =new PerformanceTest();
|
PerformanceTest performanceTest =new PerformanceTest();
|
||||||
performanceTest.setCrontabStatus(1);
|
performanceTest.setCrontabStatus(1);
|
||||||
@@ -137,7 +137,7 @@ public class DynamicTaskManager {
|
|||||||
Set<Long> currentIds = currentTasks.stream()
|
Set<Long> currentIds = currentTasks.stream()
|
||||||
.map(PerformanceTest::getId)
|
.map(PerformanceTest::getId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
// 处理新增或更新的任务
|
// 处理新增或更新的任务
|
||||||
currentTasks.forEach(task -> {
|
currentTasks.forEach(task -> {
|
||||||
Long taskId = task.getId();
|
Long taskId = task.getId();
|
||||||
@@ -162,4 +162,4 @@ public class DynamicTaskManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
149
test-test/src/main/java/com/test/test/task/TestTaskManager.java
Normal file
149
test-test/src/main/java/com/test/test/task/TestTaskManager.java
Normal file
@@ -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<Long, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
loadTasks(); // 初始化时加载所有启用的定时任务
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始加载所有启用的定时任务
|
||||||
|
*/
|
||||||
|
private void loadTasks() {
|
||||||
|
try {
|
||||||
|
GroupIdQO qo = new GroupIdQO();
|
||||||
|
List<TestTask> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,6 +47,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="delFlag != null and delFlag != ''"> and a.del_flag = #{delFlag}</if>
|
<if test="delFlag != null and delFlag != ''"> and a.del_flag = #{delFlag}</if>
|
||||||
<if test="status != null and status != ''"> and a.status = #{status}</if>
|
<if test="status != null and status != ''"> and a.status = #{status}</if>
|
||||||
</where>
|
</where>
|
||||||
|
order by a.id desc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectPerformanceTestCaseReportById" parameterType="Long" resultMap="PerformanceTestCaseReportResult">
|
<select id="selectPerformanceTestCaseReportById" parameterType="Long" resultMap="PerformanceTestCaseReportResult">
|
||||||
|
|||||||
@@ -36,10 +36,21 @@
|
|||||||
where id = #{id}
|
where id = #{id}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectTestCaseLogCaseSid" resultType="java.lang.String">
|
||||||
|
SELECT case_sid
|
||||||
|
FROM test_case_log
|
||||||
|
WHERE plan_id = #{planId}
|
||||||
|
AND type = #{type}
|
||||||
|
ORDER BY id
|
||||||
|
DESC limit 1
|
||||||
|
</select>
|
||||||
|
|
||||||
<insert id="insertTestCaseLog" parameterType="TestCaseLog" useGeneratedKeys="true" keyProperty="id">
|
<insert id="insertTestCaseLog" parameterType="TestCaseLog" useGeneratedKeys="true" keyProperty="id">
|
||||||
insert into test_case_log
|
insert into test_case_log
|
||||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||||
<if test="caseId != null">case_id,</if>
|
<if test="caseId != null">case_id,</if>
|
||||||
|
<if test="planId != null">plan_id,</if>
|
||||||
|
<if test="type != null">type,</if>
|
||||||
<if test="operType != null">oper_type,</if>
|
<if test="operType != null">oper_type,</if>
|
||||||
<if test="operDetail != null">oper_detail,</if>
|
<if test="operDetail != null">oper_detail,</if>
|
||||||
<if test="operUser != null">oper_user,</if>
|
<if test="operUser != null">oper_user,</if>
|
||||||
@@ -48,6 +59,8 @@
|
|||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="caseId != null">#{caseId},</if>
|
<if test="caseId != null">#{caseId},</if>
|
||||||
|
<if test="planId != null">#{planId},</if>
|
||||||
|
<if test="type != null">#{type},</if>
|
||||||
<if test="operType != null">#{operType},</if>
|
<if test="operType != null">#{operType},</if>
|
||||||
<if test="operDetail != null">#{operDetail},</if>
|
<if test="operDetail != null">#{operDetail},</if>
|
||||||
<if test="operUser != null">#{operUser},</if>
|
<if test="operUser != null">#{operUser},</if>
|
||||||
|
|||||||
@@ -138,4 +138,24 @@
|
|||||||
#{id}
|
#{id}
|
||||||
</foreach>
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
|
<select id="selectRelateCaseList" resultType="TestCase">
|
||||||
|
SELECT DISTINCT
|
||||||
|
tc.id,
|
||||||
|
tc.group_id,
|
||||||
|
tc.project_id,
|
||||||
|
tc.name,
|
||||||
|
tc.importance,
|
||||||
|
tc.status,
|
||||||
|
tc.del_flag,
|
||||||
|
tc.create_by AS createBy,
|
||||||
|
tc.create_time,
|
||||||
|
tc.update_by,
|
||||||
|
tc.update_time
|
||||||
|
FROM test_project_plan tpp
|
||||||
|
LEFT JOIN test_plan_case tpc ON tpc.plan_id = tpp.plan_id
|
||||||
|
LEFT JOIN test_case tc ON tc.id = tpc.case_id
|
||||||
|
WHERE tpp.project_id = #{projectId}
|
||||||
|
AND tpc.del_flag = '0'
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -148,4 +148,30 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
#{id}
|
#{id}
|
||||||
</foreach>
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
|
<select id="selectRelateDefectList" resultType="TestDefect">
|
||||||
|
SELECT DISTINCT
|
||||||
|
td.id,
|
||||||
|
td.serial_number AS serialNumber,
|
||||||
|
td.name,
|
||||||
|
td.outline,
|
||||||
|
td.detail,
|
||||||
|
td.status,
|
||||||
|
su.user_name AS manager,
|
||||||
|
td.dev,
|
||||||
|
td.level,
|
||||||
|
td.type,
|
||||||
|
td.reappearance,
|
||||||
|
td.test,
|
||||||
|
td.mail,
|
||||||
|
td.version,
|
||||||
|
td.del_flag,
|
||||||
|
td.create_time AS createTime
|
||||||
|
FROM test_project_plan tpp
|
||||||
|
LEFT JOIN test_plan_defect tpd ON tpd.plan_id = tpp.plan_id
|
||||||
|
LEFT JOIN test_defect td ON td.id = tpd.defect_id
|
||||||
|
LEFT JOIN sys_user su ON su.user_id = td.manager
|
||||||
|
WHERE tpp.project_id = #{projectId}
|
||||||
|
AND tpd.del_flag = '0'
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
</if>
|
</if>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<insert id="insertTestPlanDefect" parameterType="TestPlanDefect">
|
<insert id="insertTestPlanDefect" parameterType="TestPlanDefect">
|
||||||
insert into test_plan_defect
|
insert into test_plan_defect
|
||||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||||
<if test="id != null">id,</if>
|
<if test="id != null">id,</if>
|
||||||
@@ -124,4 +124,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
#{id}
|
#{id}
|
||||||
</foreach>
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
|
<select id="selectDefectPlanList" resultType="TestPlan">
|
||||||
|
SELECT DISTINCT
|
||||||
|
tp.id,
|
||||||
|
tp.serial_number AS serialNumber,
|
||||||
|
tp.name ,
|
||||||
|
tp.status,
|
||||||
|
su.user_name AS manager,
|
||||||
|
tp.create_time AS createTime,
|
||||||
|
tp.version,
|
||||||
|
tp.del_flag
|
||||||
|
FROM test_plan_defect tpd
|
||||||
|
LEFT JOIN test_plan tp ON tp.id = tpd.plan_id
|
||||||
|
LEFT JOIN sys_user su ON su.user_id = tp.manager
|
||||||
|
WHERE tpd.defect_id = #{defectId}
|
||||||
|
AND tpd.del_flag = '0'
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -149,19 +149,23 @@
|
|||||||
(SELECT COUNT(1) FROM test_plan_case tpc
|
(SELECT COUNT(1) FROM test_plan_case tpc
|
||||||
WHERE tpc.plan_id = tp.id
|
WHERE tpc.plan_id = tp.id
|
||||||
AND tpc.del_flag = '0'
|
AND tpc.del_flag = '0'
|
||||||
AND tpc.type = '1') AS functionTestPassNum,
|
AND tpc.type = '1'
|
||||||
|
AND tpc.execute_result = '1') AS functionTestPassNum,
|
||||||
(SELECT COUNT(1) FROM test_plan_case tpc
|
(SELECT COUNT(1) FROM test_plan_case tpc
|
||||||
WHERE tpc.plan_id = tp.id
|
WHERE tpc.plan_id = tp.id
|
||||||
AND tpc.del_flag = '0'
|
AND tpc.del_flag = '0'
|
||||||
AND tpc.type = '2') AS regressionTestPassNum,
|
AND tpc.type = '2'
|
||||||
|
AND tpc.execute_result = '1') AS regressionTestPassNum,
|
||||||
(SELECT COUNT(1) FROM test_plan_case tpc
|
(SELECT COUNT(1) FROM test_plan_case tpc
|
||||||
WHERE tpc.plan_id = tp.id
|
WHERE tpc.plan_id = tp.id
|
||||||
AND tpc.del_flag = '0'
|
AND tpc.del_flag = '0'
|
||||||
AND tpc.type = '3') AS preProductionTestPassNum,
|
AND tpc.type = '3'
|
||||||
|
AND tpc.execute_result = '1') AS preProductionTestPassNum,
|
||||||
(SELECT COUNT(1) FROM test_plan_case tpc
|
(SELECT COUNT(1) FROM test_plan_case tpc
|
||||||
WHERE tpc.plan_id = tp.id
|
WHERE tpc.plan_id = tp.id
|
||||||
AND tpc.del_flag = '0'
|
AND tpc.del_flag = '0'
|
||||||
AND tpc.type = '4') AS productionTestPassNum,
|
AND tpc.type = '4'
|
||||||
|
AND tpc.execute_result = '1') AS productionTestPassNum,
|
||||||
tp.version,
|
tp.version,
|
||||||
(
|
(
|
||||||
SELECT COUNT(1) FROM test_plan_defect tpd
|
SELECT COUNT(1) FROM test_plan_defect tpd
|
||||||
@@ -203,4 +207,56 @@
|
|||||||
<select id="selectPlanId" resultType="Long">
|
<select id="selectPlanId" resultType="Long">
|
||||||
select id from test_plan where serial_number = #{serialNumber} and del_flag = '0'
|
select id from test_plan where serial_number = #{serialNumber} and del_flag = '0'
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
<select id="selectPlanOverview" resultType="TestPlanOverviewVO">
|
||||||
|
SELECT
|
||||||
|
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0') AS caseCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result != '0') AS executedCaseCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result = '1') AS passedCaseCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result = '2') AS failedCaseCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result = '0') AS notExecutedCaseCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result = '3') AS blockedCaseCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result = '4') AS skippedCaseCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd WHERE tpd.plan_id = #{id} AND tpd.del_flag = '0') AS defectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.status = '0' AND tpd.del_flag = '0') AS unconfirmedDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.status = '1' AND tpd.del_flag = '0') AS fixingDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.status = '5' AND tpd.del_flag = '0') AS unverifiedDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.status = '3' AND tpd.del_flag = '0') AS invalidDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.status = '4' AND tpd.del_flag = '0') AS suspendedDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.level = '0' AND tpd.del_flag = '0') AS invalidLevelDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.level = '1' AND tpd.del_flag = '0') AS minorLevelDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.level = '2' AND tpd.del_flag = '0') AS normalLevelDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.level = '3' AND tpd.del_flag = '0') AS seriousLevelDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.level = '4' AND tpd.del_flag = '0') AS fatalLevelDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '0' AND tpd.del_flag = '0') AS logicDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '1' AND tpd.del_flag = '0') AS uiDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '2' AND tpd.del_flag = '0') AS performanceDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '3' AND tpd.del_flag = '0') AS compatibilityDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '4' AND tpd.del_flag = '0') AS configurationDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '5' AND tpd.del_flag = '0') AS securityDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '6' AND tpd.del_flag = '0') AS installationDefectCount,
|
||||||
|
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '7' AND tpd.del_flag = '0') AS otherDefectCount
|
||||||
|
FROM test_plan tp
|
||||||
|
WHERE tp.id = #{id}
|
||||||
|
AND tp.del_flag = '0'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getCaseTrendData" resultType="TestPlanOverviewTrendDataVO">
|
||||||
|
SELECT
|
||||||
|
DATE_FORMAT(tpc.execute_time, '%Y-%m-%d') AS caseTrendDates,
|
||||||
|
SUM(CASE WHEN tpc.execute_result = '0' THEN 1 ELSE 0 END) AS notExecuted,
|
||||||
|
SUM(CASE WHEN tpc.execute_result = '1' THEN 1 ELSE 0 END) AS passed,
|
||||||
|
SUM(CASE WHEN tpc.execute_result = '2' THEN 1 ELSE 0 END) AS failed,
|
||||||
|
SUM(CASE WHEN tpc.execute_result = '3' THEN 1 ELSE 0 END) AS blocked,
|
||||||
|
SUM(CASE WHEN tpc.execute_result = '4' THEN 1 ELSE 0 END) AS skipped
|
||||||
|
FROM test_plan_case tpc
|
||||||
|
LEFT JOIN test_plan tp ON tp.id = tpc.plan_id
|
||||||
|
WHERE tpc.del_flag = '0'
|
||||||
|
AND tpc.plan_id = #{id}
|
||||||
|
AND tp.del_flag = '0'
|
||||||
|
AND tpc.execute_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
|
||||||
|
GROUP BY DATE_FORMAT(tpc.execute_time, '%Y-%m-%d')
|
||||||
|
ORDER BY DATE_FORMAT(tpc.execute_time, '%Y-%m-%d') ASC
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -155,4 +155,28 @@
|
|||||||
<include refid="selectTestProjectVo"/>
|
<include refid="selectTestProjectVo"/>
|
||||||
WHERE id = #{id}
|
WHERE id = #{id}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectRelateProjectList" resultType="TestProject">
|
||||||
|
SELECT DISTINCT
|
||||||
|
tp.id,
|
||||||
|
tp.serial_number AS serialNumber,
|
||||||
|
tp.name,
|
||||||
|
tp.outline,
|
||||||
|
tp.detail,
|
||||||
|
tp.priority,
|
||||||
|
tp.estimated_time AS estimatedTime,
|
||||||
|
tp.source,
|
||||||
|
tp.type,
|
||||||
|
tp.status,
|
||||||
|
su.user_name AS manager,
|
||||||
|
tp.create_time AS createTime,
|
||||||
|
tp.version,
|
||||||
|
tp.del_flag
|
||||||
|
FROM test_plan_defect tpd
|
||||||
|
LEFT JOIN test_project_plan tpp ON tpp.plan_id = tpd.plan_id
|
||||||
|
LEFT JOIN test_project tp ON tp.id = tpp.project_id
|
||||||
|
LEFT JOIN sys_user su ON su.user_id = tp.manager
|
||||||
|
WHERE tpd.defect_id = #{defectId}
|
||||||
|
AND tpp.del_flag = '0'
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -45,4 +45,21 @@
|
|||||||
WHERE tpp.plan_id = #{planId}
|
WHERE tpp.plan_id = #{planId}
|
||||||
AND tpp.del_flag = '0'
|
AND tpp.del_flag = '0'
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectRelatePlanList" resultType="TestPlanProjectVo">
|
||||||
|
SELECT
|
||||||
|
tp.id,
|
||||||
|
tp.serial_number AS serialNumber,
|
||||||
|
tp.name ,
|
||||||
|
tp.status,
|
||||||
|
su.user_name AS manager,
|
||||||
|
tp.create_time AS createTime,
|
||||||
|
tp.version,
|
||||||
|
tp.del_flag
|
||||||
|
FROM test_project_plan tpp
|
||||||
|
LEFT JOIN test_plan tp ON tp.id = tpp.plan_id
|
||||||
|
LEFT JOIN sys_user su ON su.user_id = tp.manager
|
||||||
|
WHERE tpp.project_id = #{projectId}
|
||||||
|
AND tpp.del_flag = '0'
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -31,17 +31,72 @@
|
|||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
<resultMap type="TestReport" id="TestReportResult">
|
||||||
|
<result property="id" column="id"/>
|
||||||
|
<result property="serialNumber" column="serial_number"/>
|
||||||
|
<result property="name" column="name"/>
|
||||||
|
<result property="result" column="result"/>
|
||||||
|
<result property="status" column="status"/>
|
||||||
|
<result property="report" column="report"/>
|
||||||
|
<result property="updateBy" column="update_by"/>
|
||||||
|
<result property="createTime" column="create_time"/>
|
||||||
|
<result property="updateTime" column="update_time"/>
|
||||||
|
<result property="version" column="version"/>
|
||||||
|
<result property="delFlag" column="del_flag"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="selectTestReportVo">
|
||||||
|
SELECT id,
|
||||||
|
serial_number,
|
||||||
|
name,
|
||||||
|
result,
|
||||||
|
status,
|
||||||
|
report,
|
||||||
|
update_by,
|
||||||
|
create_time,
|
||||||
|
update_time,
|
||||||
|
version,
|
||||||
|
del_flag
|
||||||
|
FROM test_report
|
||||||
|
</sql>
|
||||||
|
|
||||||
<select id="selectTestReportList" parameterType="Long" resultType="TestReportVo">
|
<select id="selectTestReportList" parameterType="Long" resultType="TestReportVo">
|
||||||
SELECT
|
SELECT tr.id AS id,
|
||||||
tr.name AS name,
|
tr.name AS name,
|
||||||
tr.result AS result,
|
tr.result AS result,
|
||||||
tr.status AS status,
|
tr.report AS report,
|
||||||
tpr.type AS type,
|
tr.status AS status,
|
||||||
tr.create_time AS createTime,
|
tpr.type AS type,
|
||||||
tr.update_by AS updateBy
|
tr.create_time AS createTime,
|
||||||
|
tr.update_by AS updateBy,
|
||||||
|
tr.update_time AS updateTime
|
||||||
FROM test_plan_report tpr
|
FROM test_plan_report tpr
|
||||||
LEFT JOIN test_report tr ON tr.id = tpr.report_id
|
LEFT JOIN test_report tr ON tr.id = tpr.report_id
|
||||||
WHERE tpr.plan_id = #{planId}
|
WHERE tpr.plan_id = #{planId}
|
||||||
AND tpr.del_flag = '0'
|
AND tpr.del_flag = '0'
|
||||||
|
AND tr.del_flag = '0'
|
||||||
|
ORDER BY tr.create_time DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectCaseTestReportById" parameterType="Long" resultMap="TestReportResult">
|
||||||
|
<include refid="selectTestReportVo"/>
|
||||||
|
where id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<update id="updateExecuteCaseReport" parameterType="testReport">
|
||||||
|
update test_report
|
||||||
|
<trim prefix="SET" suffixOverrides=",">
|
||||||
|
<if test="serialNumber != null">serial_number = #{serialNumber},</if>
|
||||||
|
<if test="name != null">name = #{name},</if>
|
||||||
|
<if test="result != null">result = #{result},</if>
|
||||||
|
<if test="status != null">status = #{status},</if>
|
||||||
|
<if test="report != null">report = #{report},</if>
|
||||||
|
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||||
|
<if test="createTime != null">create_time = #{createTime},</if>
|
||||||
|
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||||
|
<if test="version != null">version = #{version},</if>
|
||||||
|
<if test="delFlag != null">del_flag = #{delFlag},</if>
|
||||||
|
</trim>
|
||||||
|
where id = #{id}
|
||||||
|
</update>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -28,7 +28,9 @@
|
|||||||
<select id="selectTestTaskList" parameterType="GroupIdQO" resultMap="TestTaskResult">
|
<select id="selectTestTaskList" parameterType="GroupIdQO" resultMap="TestTaskResult">
|
||||||
<include refid="selectTestTaskVo"/>
|
<include refid="selectTestTaskVo"/>
|
||||||
<where>
|
<where>
|
||||||
group_id = #{groupId}
|
<if test="groupId != null">
|
||||||
|
group_id = #{groupId}
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
order by create_time desc
|
order by create_time desc
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectUiAutomationVo">
|
<sql id="selectUiAutomationVo">
|
||||||
select id, group_id, name, case_level, status, label, create_by, duty_by, run_environment, update_time, steps_number, execution_result, pass_rate, crontab, crontab_status, del_flag,create_time,update_by from ui_automation
|
select id, group_id, name, case_level, status, label, create_by, duty_by, run_environment, update_time, steps_number, execution_result, pass_rate, crontab, crontab_status, del_flag,create_time,update_by,description from ui_automation
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectUiAutomationList" parameterType="UiAutomation" resultMap="UiAutomationResult">
|
<select id="selectUiAutomationList" parameterType="UiAutomation" resultMap="UiAutomationResult">
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
<select id="selectUiHighSettingById" parameterType="Long" resultMap="UiHighSettingResult">
|
<select id="selectUiHighSettingById" parameterType="Long" resultMap="UiHighSettingResult">
|
||||||
<include refid="selectUiHighSettingVo"/>
|
<include refid="selectUiHighSettingVo"/>
|
||||||
where scene_steps_id = #{sceneStepsId} and del_flag = 0 order by operate_type asc
|
where scene_steps_id = #{sceneStepsId} and del_flag = 0 order by setting_type,order_number asc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<insert id="insertUiHighSetting" parameterType="UiHighSetting" useGeneratedKeys="true" keyProperty="id">
|
<insert id="insertUiHighSetting" parameterType="UiHighSetting" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
"js-cookie": "3.0.1",
|
"js-cookie": "3.0.1",
|
||||||
"jsencrypt": "3.0.0-rc.1",
|
"jsencrypt": "3.0.0-rc.1",
|
||||||
"json-editor-vue": "^0.17.3",
|
"json-editor-vue": "^0.17.3",
|
||||||
|
"moment": "^2.30.1",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"quill": "2.0.2",
|
"quill": "2.0.2",
|
||||||
"screenfull": "5.0.2",
|
"screenfull": "5.0.2",
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export function editAndExecuteTest(data) {
|
|||||||
// 测试-编辑-立即执行
|
// 测试-编辑-立即执行
|
||||||
export function executeTest(query) {
|
export function executeTest(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/test/performanceTest/executeNow?' + query,
|
url: '/test/performanceTest/executeNow?id=' + query,
|
||||||
method: 'get',
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ const api = {
|
|||||||
addBug: 'test/defect/addBug',
|
addBug: 'test/defect/addBug',
|
||||||
delBug: 'test/defect/delBug',
|
delBug: 'test/defect/delBug',
|
||||||
getBugDetail: 'test/defect/bugDetail',
|
getBugDetail: 'test/defect/bugDetail',
|
||||||
updateBug: 'test/defect/editBug'
|
updateBug: 'test/defect/editBug',
|
||||||
|
getDefectPlanList: 'testPlan/defect/defectPlanList',
|
||||||
|
getDefectProjectList: 'testPlan/defect/defectProjectList',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBugList(data) {
|
export function getBugList(data) {
|
||||||
@@ -49,3 +51,19 @@ export function updateBug(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDefectPlanList(id) {
|
||||||
|
return request({
|
||||||
|
url: api.getDefectPlanList,
|
||||||
|
method: 'post',
|
||||||
|
data: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDefectProjectList(id) {
|
||||||
|
return request({
|
||||||
|
url: api.getDefectProjectList,
|
||||||
|
method: 'post',
|
||||||
|
data: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ const api = {
|
|||||||
addProject: 'test/project/addProject',
|
addProject: 'test/project/addProject',
|
||||||
delProject: 'test/project/delProject',
|
delProject: 'test/project/delProject',
|
||||||
getProjectDetail: 'test/project/projectDetail',
|
getProjectDetail: 'test/project/projectDetail',
|
||||||
updateProject: 'test/project/editProject'
|
updateProject: 'test/project/editProject',
|
||||||
|
relatePlanList: 'test/testPlanProject/relatePlanList',
|
||||||
|
relateCaseList: 'test/testPlanProject/relateCaseList',
|
||||||
|
relateDefectList: 'test/testPlanProject/relateDefectList',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function managerList(data) {
|
export function managerList(data) {
|
||||||
@@ -58,3 +61,27 @@ export function updateProject(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRelatePlanList(id) {
|
||||||
|
return request({
|
||||||
|
url: api.relatePlanList,
|
||||||
|
method: 'post',
|
||||||
|
data: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRelateCaseTableList(id) {
|
||||||
|
return request({
|
||||||
|
url: api.relateCaseList,
|
||||||
|
method: 'post',
|
||||||
|
data: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRelateDefectList(id) {
|
||||||
|
return request({
|
||||||
|
url: api.relateDefectList,
|
||||||
|
method: 'post',
|
||||||
|
data: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ const api = {
|
|||||||
testPlanDetail: 'test/testPlan/planDetail',
|
testPlanDetail: 'test/testPlan/planDetail',
|
||||||
testPlanProjectList: '/test/testPlanProject/list',
|
testPlanProjectList: '/test/testPlanProject/list',
|
||||||
addTestReport: 'test/testReport/addTestReport',
|
addTestReport: 'test/testReport/addTestReport',
|
||||||
getTestReportList: 'test/testReport/reportList'
|
getTestReportList: 'test/testReport/reportList',
|
||||||
|
delExecuteCaseReport: 'test/testReport/delExecuteCaseReport',
|
||||||
|
updateExecuteCaseReport: 'test/testReport/updateExecuteCaseReport',
|
||||||
|
getPlanOverview:'test/testPlan/planOverview',
|
||||||
|
getPlanCaseTrendData: 'test/testPlan/planCaseTrendData',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTestPlanList(data) {
|
export function getTestPlanList(data) {
|
||||||
@@ -67,10 +71,42 @@ export function addTestReport(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTestReportList(id) {
|
export function getTestReportList(data) {
|
||||||
return request({
|
return request({
|
||||||
url: api.getTestReportList,
|
url: api.getTestReportList,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
params: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function delExecuteCaseReport(id) {
|
||||||
|
return request({
|
||||||
|
url: api.delExecuteCaseReport,
|
||||||
|
method: 'post',
|
||||||
|
data: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateExecuteCaseReport(data) {
|
||||||
|
return request({
|
||||||
|
url: api.updateExecuteCaseReport,
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlanOverview(id) {
|
||||||
|
return request({
|
||||||
|
url: api.getPlanOverview,
|
||||||
|
method: 'post',
|
||||||
|
data: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlanCaseTrendData(id) {
|
||||||
|
return request({
|
||||||
|
url: api.getPlanCaseTrendData,
|
||||||
|
method: 'post',
|
||||||
data: {id}
|
data: {id}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,3 +24,13 @@ export function getTestReportDetail(query) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 测试报告 - 获取截图
|
||||||
|
export function getScreenshot(path) {
|
||||||
|
return request({
|
||||||
|
url: '/test/report/screenshot',
|
||||||
|
method: 'get',
|
||||||
|
params: { path },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ export const constantRoutes = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/performance/edit',
|
path: '/performance/edit/:id',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
children: [
|
children: [
|
||||||
@@ -173,7 +173,7 @@ export const constantRoutes = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/performance/report/detail',
|
path: '/performance/report/detail/:id',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
children: [
|
children: [
|
||||||
@@ -187,7 +187,7 @@ export const constantRoutes = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/ui-test/report/detail',
|
path: '/ui-test/report/detail/:id',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
children: [
|
children: [
|
||||||
@@ -215,7 +215,7 @@ export const constantRoutes = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/ui-test/automation/edit',
|
path: '/ui-test/automation/edit/:id',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
children: [
|
children: [
|
||||||
@@ -223,11 +223,23 @@ export const constantRoutes = [
|
|||||||
path: '',
|
path: '',
|
||||||
component: () => import('@/views/test/uiTest/editScene'),
|
component: () => import('@/views/test/uiTest/editScene'),
|
||||||
name: 'EditScene',
|
name: 'EditScene',
|
||||||
noCache: true,
|
|
||||||
meta: { title: '编辑场景', activeMenu: '/ui-test' }
|
meta: { title: '编辑场景', activeMenu: '/ui-test' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/ui-test/automation-test',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: () => import('@/views/test/uiTest/automationTest'),
|
||||||
|
name: 'AutomationTestView',
|
||||||
|
meta: { title: 'UI自动化测试', activeMenu: '/ui-test' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/testplan/overview',
|
path: '/testplan/overview',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
@@ -242,6 +254,20 @@ export const constantRoutes = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/testplan/casereport/platformReport',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: () => import('@/views/test/testplan/caseReport/platformReport'),
|
||||||
|
name: 'platformReport',
|
||||||
|
noCache: true,
|
||||||
|
meta: { title: '平台报告', activeMenu: '/testplan' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
// 动态路由,基于用户权限动态去加载
|
// 动态路由,基于用户权限动态去加载
|
||||||
|
|||||||
136
test-ui/src/views/test/bug/components/bugDetail.vue
Normal file
136
test-ui/src/views/test/bug/components/bugDetail.vue
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form ref="detailForm" :model="detailForm" label-width="110px" label-position="right">
|
||||||
|
<el-container>
|
||||||
|
<el-main>
|
||||||
|
<el-form-item label="缺陷概要" prop="outline">
|
||||||
|
{{ detailForm.outline }}
|
||||||
|
</el-form-item>
|
||||||
|
<!-- 描述 -->
|
||||||
|
<el-form-item label="描述" prop="detail">
|
||||||
|
{{ detailForm.detail }}
|
||||||
|
</el-form-item>
|
||||||
|
</el-main>
|
||||||
|
<el-aside width="450px" class="basic-information">
|
||||||
|
<h3>基础信息</h3>
|
||||||
|
<el-form-item label="缺陷ID" prop="serialNumber">
|
||||||
|
{{detailForm.serialNumber}}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="缺陷名称" prop="name">
|
||||||
|
{{ detailForm.name }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="经办人" prop="manager">
|
||||||
|
{{ managerName }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="严重程度" prop="level">
|
||||||
|
{{ detailForm.level }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="缺陷类型" prop="type">
|
||||||
|
{{ detailForm.type }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="缺陷状态" prop="status">
|
||||||
|
{{ detailForm.status }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="能否复现" prop="reappearance">
|
||||||
|
{{ reappearanceLabel }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="开发人" prop="dev">
|
||||||
|
{{ detailForm.dev }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="测试人" prop="test">
|
||||||
|
{{ detailForm.test }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="版本" prop="version">
|
||||||
|
{{ detailForm.version }}
|
||||||
|
</el-form-item>
|
||||||
|
</el-aside>
|
||||||
|
</el-container>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
|
||||||
|
import {getBugDetail} from "@/api/test/bug";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'bugDetail',
|
||||||
|
components: {SimpleOptions},
|
||||||
|
dicts: ['severity_level', 'bug_type', 'bug_status'],
|
||||||
|
props: {
|
||||||
|
defectId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
managerName: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
managerList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
reappearanceOptions: [{
|
||||||
|
value: '0',
|
||||||
|
label: '必然复现'
|
||||||
|
}, {
|
||||||
|
value: '1',
|
||||||
|
label: '偶发复现'
|
||||||
|
}],
|
||||||
|
managerList: [],
|
||||||
|
detailForm: {
|
||||||
|
outline: '',
|
||||||
|
detail: '',
|
||||||
|
serialNumber: '',
|
||||||
|
name: '',
|
||||||
|
manager: '',
|
||||||
|
level: '',
|
||||||
|
type: '',
|
||||||
|
status: '',
|
||||||
|
reappearance: '',
|
||||||
|
dev: '',
|
||||||
|
test: '',
|
||||||
|
version: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
reappearanceLabel() {
|
||||||
|
const option = this.reappearanceOptions.find(
|
||||||
|
(opt) => opt.value === this.detailForm.reappearance
|
||||||
|
);
|
||||||
|
return option ? option.label : '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
defectId(newVal){
|
||||||
|
this.getDetail()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getDetail() {
|
||||||
|
getBugDetail(this.defectId).then(res => {
|
||||||
|
this.detailForm = res.data;
|
||||||
|
this.detailForm.level = this.dict.type.severity_level.find(e => e.value === res.data.level).label;
|
||||||
|
this.detailForm.type = this.dict.type.bug_type.find(e => e.value === res.data.type).label;
|
||||||
|
this.detailForm.status = this.dict.type.bug_status.find(e => e.value === res.data.status).label;
|
||||||
|
this.detailForm.dev = this.managerList.find(e => e.value === parseInt(res.data.dev,10)).label;
|
||||||
|
this.detailForm.test = this.managerList.find(e => e.value === parseInt(res.data.test,10)).label;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.basic-information {
|
||||||
|
background-color: rgb(248, 248, 249) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
65
test-ui/src/views/test/bug/components/defectPlan.vue
Normal file
65
test-ui/src/views/test/bug/components/defectPlan.vue
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-table v-loading="loading" :data="list" height="500">
|
||||||
|
<el-table-column prop="statusLabel" label="状态" width="180"></el-table-column>
|
||||||
|
<el-table-column prop="name" label="计划名称"></el-table-column>
|
||||||
|
<el-table-column prop="manager" label="负责人"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"></pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getDefectPlanList} from "@/api/test/bug";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'defectPlan',
|
||||||
|
dicts: ['status'],
|
||||||
|
props: {
|
||||||
|
defectId: {
|
||||||
|
type: Number,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 遮罩层
|
||||||
|
loading: true,
|
||||||
|
// 总条数
|
||||||
|
total: 0,
|
||||||
|
// 表格数据
|
||||||
|
list: [],
|
||||||
|
// 查询参数
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
defectId(newVal){
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
getDefectPlanList(this.defectId).then(response => {
|
||||||
|
this.list = response.rows.map(item => {
|
||||||
|
const matched = this.dict.type.status.find(e => e.value === item.status);
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
statusLabel: matched ? matched.label : '未知状态'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
74
test-ui/src/views/test/bug/components/defectProject.vue
Normal file
74
test-ui/src/views/test/bug/components/defectProject.vue
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-Table v-loading="loading" :data="list" height="500">
|
||||||
|
<el-table-column prop="serialNumber" label="ID" align="center"/>
|
||||||
|
<el-table-column prop="version" label="版本" align="center"/>
|
||||||
|
<el-table-column prop="outline" label="概要" align="center"/>
|
||||||
|
<el-table-column prop="priority" label="优先级" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :type="priorityColor[scope.row.priority]">{{ scope.row.priority }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<dict-tag :options="dict.type.status" :value="row.status"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="manager" label="负责人" align="center"/>
|
||||||
|
<el-table-column prop="createTime" label="创建时间" align="center"/>
|
||||||
|
</el-Table>
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getDefectProjectList} from "@/api/test/bug";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'defectProject',
|
||||||
|
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
|
||||||
|
props: {
|
||||||
|
defectId: {
|
||||||
|
type: Number,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
list: [],
|
||||||
|
loading: false,
|
||||||
|
total: 0,
|
||||||
|
priorityColor: {
|
||||||
|
'P0': 'danger',
|
||||||
|
'P1': 'warning',
|
||||||
|
'P2': 'info',
|
||||||
|
'P3': 'success',
|
||||||
|
},
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
defectId(newVal){
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true
|
||||||
|
getDefectProjectList(this.defectId).then(response => {
|
||||||
|
this.list = response.rows
|
||||||
|
this.total = response.total
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
</el-collapse>
|
</el-collapse>
|
||||||
|
|
||||||
<!-- 标签页 -->
|
<!-- 标签页 -->
|
||||||
<el-Table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
<el-Table v-loading="loading" :data="list" @selection-change="handleSelectionChange" @row-click="handleRowClick">
|
||||||
<el-table-column type="selection"/>
|
<el-table-column type="selection"/>
|
||||||
<el-table-column prop="serialNumber" label="ID" align="center"/>
|
<el-table-column prop="serialNumber" label="ID" align="center"/>
|
||||||
<el-table-column prop="outline" label="概要" align="center"/>
|
<el-table-column prop="outline" label="概要" align="center"/>
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
<el-table-column prop="createTime" label="创建时间" align="center"/>
|
<el-table-column prop="createTime" label="创建时间" align="center"/>
|
||||||
<el-table-column label="操作" align="left" fixed="right">
|
<el-table-column label="操作" align="left" fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEdit(scope.row.id)">编辑</el-button>
|
<el-button size="mini" type="text" icon="el-icon-edit" @click.native.stop="handleEdit(scope.row.id)">编辑</el-button>
|
||||||
<el-button size="mini" type="text" icon="el-icon-delete" @click.native.stop="handleDelete(scope.row.id)">删除
|
<el-button size="mini" type="text" icon="el-icon-delete" @click.native.stop="handleDelete(scope.row.id)">删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -237,6 +237,25 @@
|
|||||||
<el-button type="primary" @click="editSubmitForm">确 定</el-button>
|
<el-button type="primary" @click="editSubmitForm">确 定</el-button>
|
||||||
</span>
|
</span>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<el-dialog :visible.sync="detailOpen" width="90%">
|
||||||
|
<template #title>
|
||||||
|
<div class="detail-title">
|
||||||
|
<el-row>
|
||||||
|
<span>查看缺陷-{{ title }}</span>
|
||||||
|
<el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal" @select="handleSelect">
|
||||||
|
<el-menu-item index="1">缺陷详情</el-menu-item>
|
||||||
|
<el-menu-item index="2">缺陷动态</el-menu-item>
|
||||||
|
<el-menu-item index="3">已关联测试计划</el-menu-item>
|
||||||
|
<el-menu-item index="4">已关联需求</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<bug-detail :managerList="managerList" :managerName="managerName" :defectId="defectId" v-show="activeIndex === '1'"></bug-detail>
|
||||||
|
<defect-plan :defectId="defectId" v-show="activeIndex === '3'"></defect-plan>
|
||||||
|
<defect-project :defectId="defectId" v-show="activeIndex === '4'"></defect-project>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -245,10 +264,13 @@ import {managerList} from "@/api/test/project";
|
|||||||
import {addBug, delBug, getBugDetail, getBugList, updateBug} from "@/api/test/bug";
|
import {addBug, delBug, getBugDetail, getBugList, updateBug} from "@/api/test/bug";
|
||||||
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
|
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
|
||||||
import {requestDownload} from "@/utils/request";
|
import {requestDownload} from "@/utils/request";
|
||||||
|
import BugDetail from "@/views/test/bug/components/bugDetail.vue";
|
||||||
|
import DefectPlan from "@/views/test/bug/components/defectPlan.vue";
|
||||||
|
import DefectProject from "@/views/test/bug/components/defectProject.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'defect',
|
name: 'defect',
|
||||||
components: {SimpleOptions},
|
components: {DefectProject, DefectPlan, BugDetail, SimpleOptions},
|
||||||
dicts: ['severity_level', 'bug_type', 'bug_status'],
|
dicts: ['severity_level', 'bug_type', 'bug_status'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -294,6 +316,10 @@ export default {
|
|||||||
addOpen: false,
|
addOpen: false,
|
||||||
//编辑弹窗
|
//编辑弹窗
|
||||||
editOpen: false,
|
editOpen: false,
|
||||||
|
detailOpen: false,
|
||||||
|
activeIndex: '1',
|
||||||
|
defectId: 0,
|
||||||
|
managerName: '',
|
||||||
selectedRows: [],
|
selectedRows: [],
|
||||||
selectedData: [],
|
selectedData: [],
|
||||||
managerList: [],
|
managerList: [],
|
||||||
@@ -336,6 +362,16 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleRowClick(row) {
|
||||||
|
this.defectId = row.id
|
||||||
|
this.activeIndex = '1'
|
||||||
|
this.managerName = row.manager
|
||||||
|
this.detailOpen = true;
|
||||||
|
this.title = row.serialNumber;
|
||||||
|
},
|
||||||
|
handleSelect(key) {
|
||||||
|
this.activeIndex = key
|
||||||
|
},
|
||||||
handleSelectionChange(selection) {
|
handleSelectionChange(selection) {
|
||||||
this.selectedRows = selection;
|
this.selectedRows = selection;
|
||||||
},
|
},
|
||||||
@@ -437,6 +473,8 @@ export default {
|
|||||||
getBugDetail(id).then((res) => {
|
getBugDetail(id).then((res) => {
|
||||||
this.editForm = res.data
|
this.editForm = res.data
|
||||||
this.editForm.manager = parseInt(res.data.manager, 10);
|
this.editForm.manager = parseInt(res.data.manager, 10);
|
||||||
|
this.editForm.dev = parseInt(res.data.dev, 10);
|
||||||
|
this.editForm.test = parseInt(res.data.test, 10);
|
||||||
this.editOpen = true
|
this.editOpen = true
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.getPerformaceData()
|
this.getPerformaceData()
|
||||||
},
|
},
|
||||||
|
activated() {
|
||||||
|
this.getPerformaceData()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 创建测试
|
// 创建测试
|
||||||
addTest() {
|
addTest() {
|
||||||
@@ -68,7 +71,7 @@ export default {
|
|||||||
},
|
},
|
||||||
// 编辑
|
// 编辑
|
||||||
hadleClickEdit(val) {
|
hadleClickEdit(val) {
|
||||||
this.$tab.openPage("修改测试", "/performance/edit", { id: val.id });
|
this.$tab.openPage(`修改测试_${val.id}`, `/performance/edit/${val.id}`, { id: val.id });
|
||||||
},
|
},
|
||||||
// 删除
|
// 删除
|
||||||
hadleClickDelete(val) {
|
hadleClickDelete(val) {
|
||||||
|
|||||||
@@ -14,10 +14,10 @@
|
|||||||
<div class="date-top">
|
<div class="date-top">
|
||||||
<i class="el-icon-date"></i>
|
<i class="el-icon-date"></i>
|
||||||
<span class="date-title">SCHEDULER</span>
|
<span class="date-title">SCHEDULER</span>
|
||||||
<el-switch v-model="switchOpen" @change="switchChange"></el-switch>
|
<el-switch v-model="addForm.crontabStatus" :active-value="1" :inactive-value="0" @change="handleSwitchChange"></el-switch>
|
||||||
</div>
|
</div>
|
||||||
<div class="date-bottom">
|
<div class="date-bottom" v-if="addForm.crontabStatus === 1">
|
||||||
<span>下次执行时间</span>
|
<span>下次执行时间:{{ nextExecutionTime }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<el-table-column prop="name" label="场景名称" />
|
<el-table-column prop="name" label="场景名称" />
|
||||||
<el-table-column prop="status" label="Enable/Disable">
|
<el-table-column prop="status" label="Enable/Disable">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-switch v-model="scope.row.status" active-value="1" inactive-value="0"></el-switch>
|
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0"></el-switch>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="action" label="操作">
|
<el-table-column prop="action" label="操作">
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="压力配置" name="second">
|
<el-tab-pane label="压力配置" name="second">
|
||||||
<div class="pressure-header">
|
<div class="pressure-header">
|
||||||
<div class="title">速兑通接口</div>
|
<div class="title">{{ addForm.performanceName }}</div>
|
||||||
<div class="pressure-title">并发用户数{{ addForm.concurrentThreads }},压测时长{{ addForm.pressureHour }}时{{
|
<div class="pressure-title">并发用户数{{ addForm.concurrentThreads }},压测时长{{ addForm.pressureHour }}时{{
|
||||||
addForm.pressureMinute }}分{{ addForm.pressureSecond }}秒</div>
|
addForm.pressureMinute }}分{{ addForm.pressureSecond }}秒</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -124,8 +124,6 @@
|
|||||||
<div class="crontab-wrap">
|
<div class="crontab-wrap">
|
||||||
<div class="title">Crontab表达式</div>
|
<div class="title">Crontab表达式</div>
|
||||||
<el-input v-model="addForm.crontab" @input="updateExecutionTimes" placeholder="请输入crontab表达式" />
|
<el-input v-model="addForm.crontab" @input="updateExecutionTimes" placeholder="请输入crontab表达式" />
|
||||||
<div class="title" style="margin-left: 50px;">定时任务开关</div>
|
|
||||||
<el-switch v-model="addForm.crontabStatus" active-value="1" inactive-value="0"></el-switch>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="near-time">
|
<div class="near-time">
|
||||||
<div class="title">最近5次运行时间</div>
|
<div class="title">最近5次运行时间</div>
|
||||||
@@ -135,8 +133,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<span slot="footer" class="dialog-footer">
|
<span slot="footer" class="dialog-footer">
|
||||||
<el-button @click="dialogVisibleTime = false, switchOpen = false">取 消</el-button>
|
<el-button @click="dialogVisibleTime = false, addForm.crontabStatus = false">取 消</el-button>
|
||||||
<el-button type="primary" @click="dialogVisibleTime = false">确 定</el-button>
|
<el-button type="primary" @click="handleDialogConfirm">确 定</el-button>
|
||||||
</span>
|
</span>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -144,6 +142,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { addTest, addAndExecuteTest, getTestCaseList } from '../../../api/performance';
|
import { addTest, addAndExecuteTest, getTestCaseList } from '../../../api/performance';
|
||||||
|
import parser from 'cron-parser'; // 添加 cron-parser 依赖
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "PerformanceAdd",
|
name: "PerformanceAdd",
|
||||||
@@ -162,7 +161,7 @@ export default {
|
|||||||
rpsStatus: '0', // rps状态:0关闭,1开启,默认0
|
rpsStatus: '0', // rps状态:0关闭,1开启,默认0
|
||||||
rpsLimit: '0', // 每分钟rps上限数,默认0
|
rpsLimit: '0', // 每分钟rps上限数,默认0
|
||||||
crontab: '', // crontab表达式
|
crontab: '', // crontab表达式
|
||||||
crontabStatus: '0', // 定时任务状态:0关闭,1开启,默认0
|
crontabStatus: 0, // 定时任务状态:0关闭,1开启,默认0
|
||||||
loopCount: '0', // 迭代次数,默认0
|
loopCount: '0', // 迭代次数,默认0
|
||||||
},
|
},
|
||||||
activeName: 'first',
|
activeName: 'first',
|
||||||
@@ -177,26 +176,10 @@ export default {
|
|||||||
searchScene: '',
|
searchScene: '',
|
||||||
changeList: [],
|
changeList: [],
|
||||||
multipleSelection: [],
|
multipleSelection: [],
|
||||||
switchOpen: false,
|
|
||||||
dialogVisibleTime: false,
|
dialogVisibleTime: false,
|
||||||
activeTime: 'first',
|
activeTime: 'first',
|
||||||
executionTimeList: [
|
executionTimeList: [], // 修改为空数组
|
||||||
{
|
nextExecutionTime: '', // 添加下次执行时间
|
||||||
time: "2025-02-18 10:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2025-02-19 10:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2025-02-20 10:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2025-02-21 10:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2025-02-22 10:00:00",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
validation: false, // 校验
|
validation: false, // 校验
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -227,6 +210,29 @@ export default {
|
|||||||
handleWithSave() {
|
handleWithSave() {
|
||||||
this.validationForm()
|
this.validationForm()
|
||||||
if (this.validation === false) { return }
|
if (this.validation === false) { return }
|
||||||
|
|
||||||
|
// 如果开启了定时任务,验证 crontab 表达式
|
||||||
|
if (this.addForm.crontabStatus === 1) {
|
||||||
|
const crontab = this.addForm.crontab.trim();
|
||||||
|
if (!crontab) {
|
||||||
|
this.$message({ message: '请输入 Crontab 表达式', type: 'warning' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证 crontab 表达式格式
|
||||||
|
if (!this.validateCrontab(crontab)) {
|
||||||
|
this.$message.error('无效的 crontab 表达式格式,请输入正确的 crontab 表达式');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser.parseExpression(crontab);
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('无效的 crontab 表达式,请修正后再保存');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.changeList.forEach(item => {
|
this.changeList.forEach(item => {
|
||||||
var par = {
|
var par = {
|
||||||
testCaseId: item.id,
|
testCaseId: item.id,
|
||||||
@@ -313,37 +319,130 @@ export default {
|
|||||||
})
|
})
|
||||||
this.dialogVisible = false
|
this.dialogVisible = false
|
||||||
},
|
},
|
||||||
switchChange(val) {
|
|
||||||
this.dialogVisibleTime = val
|
|
||||||
},
|
|
||||||
handleClose() {
|
handleClose() {
|
||||||
this.dialogVisibleTime = false
|
this.dialogVisibleTime = false
|
||||||
this.switchOpen = false
|
this.addForm.crontabStatus = false
|
||||||
},
|
},
|
||||||
// 根据crontab表达式更新执行时间
|
// 添加 crontab 表达式验证函数
|
||||||
|
validateCrontab(crontab) {
|
||||||
|
// crontab 表达式必须包含 5 个或 6 个空格分隔的字段
|
||||||
|
const fields = crontab.trim().split(/\s+/);
|
||||||
|
if (fields.length < 5 || fields.length > 6) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证每个字段的格式
|
||||||
|
const patterns = {
|
||||||
|
seconds: /^(\*|([0-9]|[1-5][0-9])(,[0-9]|[1-5][0-9])*|\*\/[1-9][0-9]?|[0-9]|[1-5][0-9]\/[1-9][0-9]?)$/,
|
||||||
|
minutes: /^(\*|([0-9]|[1-5][0-9])(,[0-9]|[1-5][0-9])*|\*\/[1-9][0-9]?|[0-9]|[1-5][0-9]\/[1-9][0-9]?)$/,
|
||||||
|
hours: /^(\*|([0-9]|1[0-9]|2[0-3])(,[0-9]|1[0-9]|2[0-3])*|\*\/[1-9][0-9]?|[0-9]|1[0-9]|2[0-3]\/[1-9][0-9]?)$/,
|
||||||
|
dayOfMonth: /^(\*|([1-9]|[12][0-9]|3[01])(,[1-9]|[12][0-9]|3[01])*|\*\/[1-9][0-9]?|[1-9]|[12][0-9]|3[01]\/[1-9][0-9]?|\?|L|W|LW|1W|2W|3W|4W|5W|6W|7W|8W|9W|10W|11W|12W|13W|14W|15W|16W|17W|18W|19W|20W|21W|22W|23W|24W|25W|26W|27W|28W|29W|30W|31W)$/,
|
||||||
|
month: /^(\*|([1-9]|1[0-2])(,[1-9]|1[0-2])*|\*\/[1-9][0-9]?|[1-9]|1[0-2]\/[1-9][0-9]?|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$/,
|
||||||
|
dayOfWeek: /^(\*|([0-6]|SUN|MON|TUE|WED|THU|FRI|SAT)(,[0-6]|SUN|MON|TUE|WED|THU|FRI|SAT)*|\*\/[1-9][0-9]?|[0-6]\/[1-9][0-9]?|\?|L|6L|5L|4L|3L|2L|1L|1#1|1#2|1#3|1#4|1#5|2#1|2#2|2#3|2#4|2#5|3#1|3#2|3#3|3#4|3#5|4#1|4#2|4#3|4#4|4#5|5#1|5#2|5#3|5#4|5#5|6#1|6#2|6#3|6#4|6#5|7#1|7#2|7#3|7#4|7#5)$/
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据字段数量确定验证顺序
|
||||||
|
const validationOrder = fields.length === 6
|
||||||
|
? ['seconds', 'minutes', 'hours', 'dayOfMonth', 'month', 'dayOfWeek']
|
||||||
|
: ['minutes', 'hours', 'dayOfMonth', 'month', 'dayOfWeek'];
|
||||||
|
|
||||||
|
// 验证每个字段
|
||||||
|
const isValid = fields.every((field, index) => {
|
||||||
|
const pattern = patterns[validationOrder[index]];
|
||||||
|
return pattern && pattern.test(field);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 dayOfMonth 和 dayOfWeek 不能同时为具体值
|
||||||
|
const dayOfMonthIndex = fields.length === 6 ? 3 : 2;
|
||||||
|
const dayOfWeekIndex = fields.length === 6 ? 5 : 4;
|
||||||
|
const dayOfMonth = fields[dayOfMonthIndex];
|
||||||
|
const dayOfWeek = fields[dayOfWeekIndex];
|
||||||
|
|
||||||
|
if (dayOfMonth !== '*' && dayOfWeek !== '*' && dayOfMonth !== '?' && dayOfWeek !== '?') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 更新执行时间列表
|
||||||
updateExecutionTimes() {
|
updateExecutionTimes() {
|
||||||
const crontab = this.addForm.crontab.trim();
|
const crontab = this.addForm.crontab.trim();
|
||||||
if (!crontab) {
|
if (!crontab) {
|
||||||
this.executionTimeList = [];
|
this.executionTimeList = [];
|
||||||
|
this.nextExecutionTime = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首先验证 crontab 表达式格式
|
||||||
|
if (!this.validateCrontab(crontab)) {
|
||||||
|
this.$message.error('无效的 crontab 表达式格式');
|
||||||
|
this.executionTimeList = [];
|
||||||
|
this.nextExecutionTime = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 解析crontab表达式
|
// 解析crontab表达式
|
||||||
const interval = cronParser.parseExpression(crontab);
|
const interval = parser.parseExpression(crontab);
|
||||||
const times = [];
|
const times = [];
|
||||||
|
|
||||||
// 获取最近三次执行时间
|
// 获取最近5次执行时间
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
const nextTime = interval.next().toDate(); // 获取 Date 对象
|
const nextTime = interval.next().toDate();
|
||||||
const formattedTime = this.formatDate(nextTime); // 格式化时间
|
times.push({
|
||||||
times.push({ time: formattedTime });
|
time: this.formatDate(nextTime)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.executionTimeList = times;
|
this.executionTimeList = times;
|
||||||
|
this.nextExecutionTime = times[0].time; // 设置下次执行时间
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('无效的crontab表达式:', error);
|
this.$message.error('无效的 crontab 表达式');
|
||||||
this.executionTimeList = [{ time: '无效的crontab表达式' }];
|
this.executionTimeList = [];
|
||||||
|
this.nextExecutionTime = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 格式化日期
|
||||||
|
formatDate(date) {
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hour = String(date.getHours()).padStart(2, '0');
|
||||||
|
const minute = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const second = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
||||||
|
},
|
||||||
|
// 修改确定按钮的处理函数
|
||||||
|
handleDialogConfirm() {
|
||||||
|
const crontab = this.addForm.crontab.trim();
|
||||||
|
if (!crontab) {
|
||||||
|
this.$message.warning('请输入 Crontab 表达式');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首先验证 crontab 表达式格式
|
||||||
|
if (!this.validateCrontab(crontab)) {
|
||||||
|
this.$message.error('无效的 crontab 表达式格式,请输入正确的 crontab 表达式');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser.parseExpression(crontab);
|
||||||
|
this.updateExecutionTimes();
|
||||||
|
this.dialogVisibleTime = false;
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('无效的 crontab 表达式,请修正后再确定');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 添加开关变化处理函数
|
||||||
|
handleSwitchChange(val) {
|
||||||
|
if (val === 1) {
|
||||||
|
this.dialogVisibleTime = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<div class="name-wrap">测试名称</div>
|
<div class="name-wrap">测试名称</div>
|
||||||
<el-input v-model="addForm.performanceName" placeholder="请输入名称" maxlength="255" show-word-limit></el-input>
|
<el-input v-model="addForm.performanceName" placeholder="请输入名称" maxlength="255" show-word-limit></el-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="save">
|
<div class="save" v-loading="loading">
|
||||||
<el-button type="primary" plain @click="handleWithSave">保存</el-button>
|
<el-button type="primary" plain @click="handleWithSave">保存</el-button>
|
||||||
<el-button type="primary" plain @click="handleWithSaveAndExecute">保存并执行</el-button>
|
<el-button type="primary" plain @click="handleWithSaveAndExecute">保存并执行</el-button>
|
||||||
<el-button type="primary" plain @click="handleWithExecute">立即执行</el-button>
|
<el-button type="primary" plain @click="handleWithExecute">立即执行</el-button>
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
<div class="date-top">
|
<div class="date-top">
|
||||||
<i class="el-icon-date"></i>
|
<i class="el-icon-date"></i>
|
||||||
<span class="date-title">SCHEDULER</span>
|
<span class="date-title">SCHEDULER</span>
|
||||||
<el-switch v-model="switchOpen" @change="switchChange"></el-switch>
|
<el-switch v-model="addForm.crontabStatus" :active-value="1" :inactive-value="0" @change="handleSwitchChange"></el-switch>
|
||||||
</div>
|
</div>
|
||||||
<div class="date-bottom">
|
<div class="date-bottom" v-if="addForm.crontabStatus === 1">
|
||||||
<span>下次执行时间</span>
|
<span>下次执行时间:{{ nextExecutionTime }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
<el-table-column prop="testCaseName" label="场景名称" />
|
<el-table-column prop="testCaseName" label="场景名称" />
|
||||||
<el-table-column prop="status" label="Enable/Disable">
|
<el-table-column prop="status" label="Enable/Disable">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-switch v-model="scope.row.status" active-value="1" inactive-value="0"></el-switch>
|
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0"></el-switch>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="action" label="操作">
|
<el-table-column prop="action" label="操作">
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="压力配置" name="second">
|
<el-tab-pane label="压力配置" name="second">
|
||||||
<div class="pressure-header">
|
<div class="pressure-header">
|
||||||
<div class="title">速兑通接口</div>
|
<div class="title">{{ addForm.performanceName }}</div>
|
||||||
<div class="pressure-title">并发用户数{{ addForm.concurrentThreads }},压测时长{{ addForm.pressureHour }}时{{
|
<div class="pressure-title">并发用户数{{ addForm.concurrentThreads }},压测时长{{ addForm.pressureHour }}时{{
|
||||||
addForm.pressureMinute }}分{{ addForm.pressureSecond }}秒</div>
|
addForm.pressureMinute }}分{{ addForm.pressureSecond }}秒</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -125,8 +125,6 @@
|
|||||||
<div class="crontab-wrap">
|
<div class="crontab-wrap">
|
||||||
<div class="title">Crontab表达式</div>
|
<div class="title">Crontab表达式</div>
|
||||||
<el-input v-model="addForm.crontab" @input="updateExecutionTimes" placeholder="请输入crontab表达式" />
|
<el-input v-model="addForm.crontab" @input="updateExecutionTimes" placeholder="请输入crontab表达式" />
|
||||||
<div class="title" style="margin-left: 50px;">定时任务开关</div>
|
|
||||||
<el-switch v-model="addForm.crontabStatus" active-value="1" inactive-value="0"></el-switch>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="near-time">
|
<div class="near-time">
|
||||||
<div class="title">最近5次运行时间</div>
|
<div class="title">最近5次运行时间</div>
|
||||||
@@ -136,8 +134,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<span slot="footer" class="dialog-footer">
|
<span slot="footer" class="dialog-footer">
|
||||||
<el-button @click="dialogVisibleTime = false, switchOpen = false">取 消</el-button>
|
<el-button @click="dialogVisibleTime = false, addForm.crontabStatus = 0">取 消</el-button>
|
||||||
<el-button type="primary" @click="dialogVisibleTime = false">确 定</el-button>
|
<el-button type="primary" @click="handleDialogConfirm">确 定</el-button>
|
||||||
</span>
|
</span>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -145,6 +143,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { editTest, editAndExecuteTest, getTestCaseList, getTestDetail, executeTest } from '../../../api/performance';
|
import { editTest, editAndExecuteTest, getTestCaseList, getTestDetail, executeTest } from '../../../api/performance';
|
||||||
|
import {runTestPlanCase} from "../../../api/test/planCase";
|
||||||
|
import parser from 'cron-parser'; // 添加 cron-parser 依赖
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "PerformanceEdit",
|
name: "PerformanceEdit",
|
||||||
@@ -163,9 +163,10 @@ export default {
|
|||||||
rpsStatus: '0', // rps状态:0关闭,1开启,默认0
|
rpsStatus: '0', // rps状态:0关闭,1开启,默认0
|
||||||
rpsLimit: '0', // 每分钟rps上限数,默认0
|
rpsLimit: '0', // 每分钟rps上限数,默认0
|
||||||
crontab: '', // crontab表达式
|
crontab: '', // crontab表达式
|
||||||
crontabStatus: '0', // 定时任务状态:0关闭,1开启,默认0
|
crontabStatus: 0, // 定时任务状态:0关闭,1开启,默认0
|
||||||
loopCount: '0', // 迭代次数,默认0
|
loopCount: '0', // 迭代次数,默认0
|
||||||
},
|
},
|
||||||
|
loading: false,
|
||||||
activeName: 'first',
|
activeName: 'first',
|
||||||
sceneList: [], // 场景列表
|
sceneList: [], // 场景列表
|
||||||
dialogVisible: false, // 场景列表
|
dialogVisible: false, // 场景列表
|
||||||
@@ -178,26 +179,10 @@ export default {
|
|||||||
searchScene: '',
|
searchScene: '',
|
||||||
changeList: [],
|
changeList: [],
|
||||||
multipleSelection: [],
|
multipleSelection: [],
|
||||||
switchOpen: false,
|
|
||||||
dialogVisibleTime: false,
|
dialogVisibleTime: false,
|
||||||
activeTime: 'first',
|
activeTime: 'first',
|
||||||
executionTimeList: [
|
executionTimeList: [], // 修改为空数组
|
||||||
{
|
nextExecutionTime: '', // 添加下次执行时间
|
||||||
time: "2025-02-18 10:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2025-02-19 10:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2025-02-20 10:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2025-02-21 10:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2025-02-22 10:00:00",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
validation: false, // 校验
|
validation: false, // 校验
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -205,6 +190,25 @@ export default {
|
|||||||
this.getTestCaseData()
|
this.getTestCaseData()
|
||||||
this.getTestDetailData()
|
this.getTestDetailData()
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
// 监听 crontab 和 crontabStatus 的变化
|
||||||
|
'addForm.crontab': {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal && this.addForm.crontabStatus === 1) {
|
||||||
|
this.updateExecutionTimes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'addForm.crontabStatus': {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal === 1 && this.addForm.crontab) {
|
||||||
|
this.updateExecutionTimes();
|
||||||
|
} else if (newVal === 0) {
|
||||||
|
this.nextExecutionTime = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 校验
|
// 校验
|
||||||
validationForm() {
|
validationForm() {
|
||||||
@@ -229,6 +233,29 @@ export default {
|
|||||||
handleWithSave() {
|
handleWithSave() {
|
||||||
this.validationForm()
|
this.validationForm()
|
||||||
if (this.validation === false) { return }
|
if (this.validation === false) { return }
|
||||||
|
|
||||||
|
// 如果开启了定时任务,验证 crontab 表达式
|
||||||
|
if (this.addForm.crontabStatus === 1) {
|
||||||
|
const crontab = this.addForm.crontab.trim();
|
||||||
|
if (!crontab) {
|
||||||
|
this.$message({ message: '请输入 Crontab 表达式', type: 'warning' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证 crontab 表达式格式
|
||||||
|
if (!this.validateCrontab(crontab)) {
|
||||||
|
this.$message.error('无效的 crontab 表达式格式,请输入正确的 crontab 表达式');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser.parseExpression(crontab);
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('无效的 crontab 表达式,请修正后再保存');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.addForm.performanceTestCaseVOList = this.changeList
|
this.addForm.performanceTestCaseVOList = this.changeList
|
||||||
editTest(this.addForm).then(res => {
|
editTest(this.addForm).then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
@@ -247,6 +274,7 @@ export default {
|
|||||||
this.$message({ message: '请输入Crontab表达式', type: 'warning' })
|
this.$message({ message: '请输入Crontab表达式', type: 'warning' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
this.loading = true;
|
||||||
editAndExecuteTest(this.addForm).then(res => {
|
editAndExecuteTest(this.addForm).then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.$message({ message: '编辑成功', type: 'success' })
|
this.$message({ message: '编辑成功', type: 'success' })
|
||||||
@@ -254,18 +282,24 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$message({ message: '编辑失败', type: 'error' })
|
this.$message({ message: '编辑失败', type: 'error' })
|
||||||
}
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false;
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 立即执行
|
// 立即执行
|
||||||
handleWithExecute() {
|
handleWithExecute() {
|
||||||
executeTest(this.$route.query.id).then(res => {
|
const myId = this.$route.query.id;
|
||||||
if (res.code === 200) {
|
this.$modal.confirm('是否确认立即执行性能测试?').then(function () {
|
||||||
this.$message({ message: '执行成功', type: 'success' })
|
return executeTest(myId);
|
||||||
this.$tab.closeOpenPage({ path: "/performance/performance" });
|
}).then((res) => {
|
||||||
} else {
|
if (res.code === 200) {
|
||||||
this.$message({ message: '执行失败', type: 'error' })
|
this.$modal.msgSuccess("提交执行成功");
|
||||||
}
|
} else {
|
||||||
})
|
this.$message({ message: '提交执行失败', type: 'error' })
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
// 取消
|
// 取消
|
||||||
handleWithCancel() {
|
handleWithCancel() {
|
||||||
@@ -314,18 +348,11 @@ export default {
|
|||||||
getTestDetail(this.$route.query.id).then(res => {
|
getTestDetail(this.$route.query.id).then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.addForm = res.data
|
this.addForm = res.data
|
||||||
res.data.performanceTestCaseVOList.forEach(item => {
|
|
||||||
item.status = String(item.status)
|
|
||||||
})
|
|
||||||
this.changeList = res.data.performanceTestCaseVOList
|
this.changeList = res.data.performanceTestCaseVOList
|
||||||
this.sceneList.forEach(row => {
|
// 如果有定时任务,更新下次执行时间
|
||||||
this.changeList.forEach(item => {
|
if (this.addForm.crontabStatus === 1 && this.addForm.crontab) {
|
||||||
if (row.id === item.testCaseId) {
|
this.updateExecutionTimes();
|
||||||
this.multipleSelection.push(row)
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this.addForm.rpsStatus = String(res.data.rpsStatus)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -357,37 +384,130 @@ export default {
|
|||||||
|
|
||||||
this.dialogVisible = false
|
this.dialogVisible = false
|
||||||
},
|
},
|
||||||
switchChange(val) {
|
|
||||||
this.dialogVisibleTime = val
|
|
||||||
},
|
|
||||||
handleClose() {
|
handleClose() {
|
||||||
this.dialogVisibleTime = false
|
this.dialogVisibleTime = false
|
||||||
this.switchOpen = false
|
this.addForm.crontabStatus = 0
|
||||||
},
|
},
|
||||||
// 根据crontab表达式更新执行时间
|
// 添加 crontab 表达式验证函数
|
||||||
|
validateCrontab(crontab) {
|
||||||
|
// crontab 表达式必须包含 5 个或 6 个空格分隔的字段
|
||||||
|
const fields = crontab.trim().split(/\s+/);
|
||||||
|
if (fields.length < 5 || fields.length > 6) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证每个字段的格式
|
||||||
|
const patterns = {
|
||||||
|
seconds: /^(\*|([0-9]|[1-5][0-9])(,[0-9]|[1-5][0-9])*|\*\/[1-9][0-9]?|[0-9]|[1-5][0-9]\/[1-9][0-9]?)$/,
|
||||||
|
minutes: /^(\*|([0-9]|[1-5][0-9])(,[0-9]|[1-5][0-9])*|\*\/[1-9][0-9]?|[0-9]|[1-5][0-9]\/[1-9][0-9]?)$/,
|
||||||
|
hours: /^(\*|([0-9]|1[0-9]|2[0-3])(,[0-9]|1[0-9]|2[0-3])*|\*\/[1-9][0-9]?|[0-9]|1[0-9]|2[0-3]\/[1-9][0-9]?)$/,
|
||||||
|
dayOfMonth: /^(\*|([1-9]|[12][0-9]|3[01])(,[1-9]|[12][0-9]|3[01])*|\*\/[1-9][0-9]?|[1-9]|[12][0-9]|3[01]\/[1-9][0-9]?|\?|L|W|LW|1W|2W|3W|4W|5W|6W|7W|8W|9W|10W|11W|12W|13W|14W|15W|16W|17W|18W|19W|20W|21W|22W|23W|24W|25W|26W|27W|28W|29W|30W|31W)$/,
|
||||||
|
month: /^(\*|([1-9]|1[0-2])(,[1-9]|1[0-2])*|\*\/[1-9][0-9]?|[1-9]|1[0-2]\/[1-9][0-9]?|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$/,
|
||||||
|
dayOfWeek: /^(\*|([0-6]|SUN|MON|TUE|WED|THU|FRI|SAT)(,[0-6]|SUN|MON|TUE|WED|THU|FRI|SAT)*|\*\/[1-9][0-9]?|[0-6]\/[1-9][0-9]?|\?|L|6L|5L|4L|3L|2L|1L|1#1|1#2|1#3|1#4|1#5|2#1|2#2|2#3|2#4|2#5|3#1|3#2|3#3|3#4|3#5|4#1|4#2|4#3|4#4|4#5|5#1|5#2|5#3|5#4|5#5|6#1|6#2|6#3|6#4|6#5|7#1|7#2|7#3|7#4|7#5)$/
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据字段数量确定验证顺序
|
||||||
|
const validationOrder = fields.length === 6
|
||||||
|
? ['seconds', 'minutes', 'hours', 'dayOfMonth', 'month', 'dayOfWeek']
|
||||||
|
: ['minutes', 'hours', 'dayOfMonth', 'month', 'dayOfWeek'];
|
||||||
|
|
||||||
|
// 验证每个字段
|
||||||
|
const isValid = fields.every((field, index) => {
|
||||||
|
const pattern = patterns[validationOrder[index]];
|
||||||
|
return pattern && pattern.test(field);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 dayOfMonth 和 dayOfWeek 不能同时为具体值
|
||||||
|
const dayOfMonthIndex = fields.length === 6 ? 3 : 2;
|
||||||
|
const dayOfWeekIndex = fields.length === 6 ? 5 : 4;
|
||||||
|
const dayOfMonth = fields[dayOfMonthIndex];
|
||||||
|
const dayOfWeek = fields[dayOfWeekIndex];
|
||||||
|
|
||||||
|
if (dayOfMonth !== '*' && dayOfWeek !== '*' && dayOfMonth !== '?' && dayOfWeek !== '?') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 更新执行时间列表
|
||||||
updateExecutionTimes() {
|
updateExecutionTimes() {
|
||||||
const crontab = this.form.crontab.trim();
|
const crontab = this.addForm.crontab.trim();
|
||||||
if (!crontab) {
|
if (!crontab) {
|
||||||
this.executionTimeList = [];
|
this.executionTimeList = [];
|
||||||
|
this.nextExecutionTime = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首先验证 crontab 表达式格式
|
||||||
|
if (!this.validateCrontab(crontab)) {
|
||||||
|
this.$message.error('无效的 crontab 表达式格式');
|
||||||
|
this.executionTimeList = [];
|
||||||
|
this.nextExecutionTime = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 解析crontab表达式
|
// 解析crontab表达式
|
||||||
const interval = cronParser.parseExpression(crontab);
|
const interval = parser.parseExpression(crontab);
|
||||||
const times = [];
|
const times = [];
|
||||||
|
|
||||||
// 获取最近三次执行时间
|
// 获取最近5次执行时间
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
const nextTime = interval.next().toDate(); // 获取 Date 对象
|
const nextTime = interval.next().toDate();
|
||||||
const formattedTime = this.formatDate(nextTime); // 格式化时间
|
times.push({
|
||||||
times.push({ time: formattedTime });
|
time: this.formatDate(nextTime)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.executionTimeList = times;
|
this.executionTimeList = times;
|
||||||
|
this.nextExecutionTime = times[0].time; // 设置下次执行时间
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('无效的crontab表达式:', error);
|
this.$message.error('无效的 crontab 表达式');
|
||||||
this.executionTimeList = [{ time: '无效的crontab表达式' }];
|
this.executionTimeList = [];
|
||||||
|
this.nextExecutionTime = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 格式化日期
|
||||||
|
formatDate(date) {
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hour = String(date.getHours()).padStart(2, '0');
|
||||||
|
const minute = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const second = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
||||||
|
},
|
||||||
|
// 修改确定按钮的处理函数
|
||||||
|
handleDialogConfirm() {
|
||||||
|
const crontab = this.addForm.crontab.trim();
|
||||||
|
if (!crontab) {
|
||||||
|
this.$message.warning('请输入 Crontab 表达式');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首先验证 crontab 表达式格式
|
||||||
|
if (!this.validateCrontab(crontab)) {
|
||||||
|
this.$message.error('无效的 crontab 表达式格式,请输入正确的 crontab 表达式');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser.parseExpression(crontab);
|
||||||
|
this.updateExecutionTimes();
|
||||||
|
this.dialogVisibleTime = false;
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('无效的 crontab 表达式,请修正后再确定');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 添加开关变化处理函数
|
||||||
|
handleSwitchChange(val) {
|
||||||
|
if (val === 1) {
|
||||||
|
this.dialogVisibleTime = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
<el-table-column prop="endTime" label="结束时间" align="center" sortable />
|
<el-table-column prop="endTime" label="结束时间" align="center" sortable />
|
||||||
<el-table-column prop="triggerType" label="触发方式" align="center" sortable>
|
<el-table-column prop="triggerType" label="触发方式" align="center" sortable>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div v-if="scope.row.triggerType === 1">按持续时间</div>
|
<div v-if="scope.row.triggerType === 1">定时任务</div>
|
||||||
<div v-else>按迭代次数</div>
|
<div v-else>手动</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="status" label="状态" align="center" width="150" sortable>
|
<el-table-column prop="status" label="状态" align="center" width="150" sortable>
|
||||||
@@ -65,7 +65,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
// 查看
|
// 查看
|
||||||
hadleClickDetail(val) {
|
hadleClickDetail(val) {
|
||||||
this.$tab.openPage("报告详情", "/performance/report/detail", { id: val.id });
|
this.$tab.openPage(`报告详情_${val.id}`, `/performance/report/detail/${val.id}`, { id: val.id });
|
||||||
},
|
},
|
||||||
// 删除
|
// 删除
|
||||||
hadleClickDelete(val) {
|
hadleClickDelete(val) {
|
||||||
@@ -86,8 +86,14 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 分页
|
// 分页
|
||||||
handleSizeChange() { },
|
handleSizeChange(val) {
|
||||||
handleCurrentChange() { },
|
this.serachForm.pageSize = val;
|
||||||
|
this.getReportListData();
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.serachForm.pageNum = val;
|
||||||
|
this.getReportListData();
|
||||||
|
},
|
||||||
serachList() {
|
serachList() {
|
||||||
this.getReportListData()
|
this.getReportListData()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
<el-tab-pane :label="'已完成(' + statusCounts.completed + ')'" name="2"></el-tab-pane>
|
<el-tab-pane :label="'已完成(' + statusCounts.completed + ')'" name="2"></el-tab-pane>
|
||||||
<el-tab-pane :label="'已终止(' + statusCounts.terminated + ')'" name="3"></el-tab-pane>
|
<el-tab-pane :label="'已终止(' + statusCounts.terminated + ')'" name="3"></el-tab-pane>
|
||||||
|
|
||||||
<el-Table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
<el-Table v-loading="loading" :data="list" @selection-change="handleSelectionChange" @row-click="handleRowClick">
|
||||||
<el-table-column type="selection"/>
|
<el-table-column type="selection"/>
|
||||||
<el-table-column prop="serialNumber" label="ID" align="center"/>
|
<el-table-column prop="serialNumber" label="ID" align="center"/>
|
||||||
<el-table-column prop="version" label="版本" align="center"/>
|
<el-table-column prop="version" label="版本" align="center"/>
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
<el-table-column prop="createTime" label="创建时间" align="center"/>
|
<el-table-column prop="createTime" label="创建时间" align="center"/>
|
||||||
<el-table-column label="操作" align="left" fixed="right">
|
<el-table-column label="操作" align="left" fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEdit(scope.row.id)">编辑</el-button>
|
<el-button size="mini" type="text" icon="el-icon-edit" @click.native.stop="handleEdit(scope.row.id)">编辑</el-button>
|
||||||
<el-button size="mini" type="text" icon="el-icon-delete" @click.native.stop="handleDelete(scope.row.id)">删除
|
<el-button size="mini" type="text" icon="el-icon-delete" @click.native.stop="handleDelete(scope.row.id)">删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -234,6 +234,32 @@
|
|||||||
<el-button type="primary" @click="editSubmitForm">确 定</el-button>
|
<el-button type="primary" @click="editSubmitForm">确 定</el-button>
|
||||||
</span>
|
</span>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="detailOpen" width="90%">
|
||||||
|
<template #title>
|
||||||
|
<div class="detail-title">
|
||||||
|
<el-row>
|
||||||
|
<span>查看需求-{{ title }}</span>
|
||||||
|
<el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal" @select="handleSelect">
|
||||||
|
<el-menu-item index="1">需求详情</el-menu-item>
|
||||||
|
<el-menu-item index="2">需求动态</el-menu-item>
|
||||||
|
<el-menu-item index="3">已关联测试计划</el-menu-item>
|
||||||
|
<el-menu-item index="4">已关联用例</el-menu-item>
|
||||||
|
<el-menu-item index="5">已关联缺陷</el-menu-item>
|
||||||
|
<el-menu-item index="6">评审</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<project-detail :managerName="managerName" :projectId="projectId" v-show="activeIndex === '1'"></project-detail>
|
||||||
|
<relate-plan :projectId="projectId" v-show="activeIndex === '3'"></relate-plan>
|
||||||
|
<relateCaseTable :projectId="projectId" v-show="activeIndex === '4'"></relateCaseTable>
|
||||||
|
<relateDefectTable :projectId="projectId" v-show="activeIndex === '5'"></relateDefectTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="detailOpen = false">取 消</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -241,10 +267,14 @@
|
|||||||
import {addProject, delProject, getProjectDetail, getProjectList, managerList, updateProject} from "@/api/test/project";
|
import {addProject, delProject, getProjectDetail, getProjectList, managerList, updateProject} from "@/api/test/project";
|
||||||
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
|
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
|
||||||
import {requestDownload} from "@/utils/request";
|
import {requestDownload} from "@/utils/request";
|
||||||
|
import ProjectDetail from "@/views/test/project/projectDetail/projectDetail.vue";
|
||||||
|
import RelatePlan from "@/views/test/project/projectDetail/relatePlan.vue";
|
||||||
|
import RelateCaseTable from "@/views/test/project/projectDetail/relateCaseTable.vue";
|
||||||
|
import RelateDefectTable from "@/views/test/project/projectDetail/relateDefectTable.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'project',
|
name: 'project',
|
||||||
components: {SimpleOptions},
|
components: {RelatePlan, ProjectDetail, SimpleOptions , RelateCaseTable , RelateDefectTable},
|
||||||
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
|
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -281,6 +311,8 @@ export default {
|
|||||||
total: 0,
|
total: 0,
|
||||||
list: [],
|
list: [],
|
||||||
title: '',
|
title: '',
|
||||||
|
projectId: 0,
|
||||||
|
managerName: '',
|
||||||
// 遮罩层
|
// 遮罩层
|
||||||
loading: false,
|
loading: false,
|
||||||
editSubmitLoading: false,
|
editSubmitLoading: false,
|
||||||
@@ -289,9 +321,11 @@ export default {
|
|||||||
addOpen: false,
|
addOpen: false,
|
||||||
//编辑弹窗
|
//编辑弹窗
|
||||||
editOpen: false,
|
editOpen: false,
|
||||||
|
detailOpen: false,
|
||||||
selectedRows: [],
|
selectedRows: [],
|
||||||
selectedData: [],
|
selectedData: [],
|
||||||
managerList: [],
|
managerList: [],
|
||||||
|
activeIndex: '1',
|
||||||
queryParams: {
|
queryParams: {
|
||||||
serialNumber: '',
|
serialNumber: '',
|
||||||
outline: '',
|
outline: '',
|
||||||
@@ -327,6 +361,16 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleRowClick(row) {
|
||||||
|
this.projectId = row.id
|
||||||
|
this.activeIndex = '1'
|
||||||
|
this.managerName = row.manager
|
||||||
|
this.detailOpen = true;
|
||||||
|
this.title = row.serialNumber;
|
||||||
|
},
|
||||||
|
handleSelect(key) {
|
||||||
|
this.activeIndex = key
|
||||||
|
},
|
||||||
handleSelectionChange(selection) {
|
handleSelectionChange(selection) {
|
||||||
this.selectedRows = selection;
|
this.selectedRows = selection;
|
||||||
},
|
},
|
||||||
|
|||||||
109
test-ui/src/views/test/project/projectDetail/projectDetail.vue
Normal file
109
test-ui/src/views/test/project/projectDetail/projectDetail.vue
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<template>
|
||||||
|
<div class="project-detail">
|
||||||
|
<el-form ref="editForm" :model="detailForm" label-width="110px" label-position="right">
|
||||||
|
<el-container>
|
||||||
|
<el-main>
|
||||||
|
<el-form-item label="需求概要" prop="outline">
|
||||||
|
{{ detailForm.outline }}
|
||||||
|
</el-form-item>
|
||||||
|
<!-- 描述 -->
|
||||||
|
<el-form-item label="描述" prop="detail">
|
||||||
|
{{ detailForm.detail }}
|
||||||
|
</el-form-item>
|
||||||
|
</el-main>
|
||||||
|
<el-aside width="450px" class="basic-information">
|
||||||
|
<h3>基础信息</h3>
|
||||||
|
<el-form-item label="需求名称" prop="name">
|
||||||
|
{{ detailForm.name }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
{{ detailForm.status }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="负责人" prop="manager">
|
||||||
|
{{ managerName }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="优先级" prop="priority">
|
||||||
|
{{ detailForm.priority }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="预期完成时间" prop="estimatedTime">
|
||||||
|
{{ detailForm.estimatedTime }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="需求来源" prop="source">
|
||||||
|
{{ detailForm.source }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="需求类型" prop="type">
|
||||||
|
{{ detailForm.type }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="版本" prop="version">
|
||||||
|
{{ detailForm.version }}
|
||||||
|
</el-form-item>
|
||||||
|
</el-aside>
|
||||||
|
</el-container>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
|
||||||
|
import {getProjectDetail} from "@/api/test/project";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'projectDetail',
|
||||||
|
components: {SimpleOptions},
|
||||||
|
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
|
||||||
|
props: {
|
||||||
|
projectId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
managerName: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
detailForm: {
|
||||||
|
outline: '',
|
||||||
|
detail: '',
|
||||||
|
name: '',
|
||||||
|
status: '',
|
||||||
|
manager: '',
|
||||||
|
priority: '',
|
||||||
|
estimatedTime: '',
|
||||||
|
source: '',
|
||||||
|
type: '',
|
||||||
|
version: ''
|
||||||
|
},
|
||||||
|
managerList: [],
|
||||||
|
editOpen: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
projectId(newVal){
|
||||||
|
this.getDetail()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
getDetail() {
|
||||||
|
getProjectDetail(this.projectId).then(res => {
|
||||||
|
this.detailForm = res.data;
|
||||||
|
this.detailForm.source = this.dict.type.project_source.find(e => e.value === res.data.source).label;
|
||||||
|
this.detailForm.type = this.dict.type.project_type.find(e => e.value === res.data.type).label;
|
||||||
|
this.detailForm.status = this.dict.type.status.find(e => e.value === res.data.status).label;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.basic-information {
|
||||||
|
background-color: rgb(248, 248, 249) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-table v-loading="loading" :data="list" height="500">
|
||||||
|
<el-table-column prop="name" label="用例名称"></el-table-column>
|
||||||
|
<el-table-column prop="status" label="用例状态" :formatter="row => ['','草稿', '通过', '不通过'][row.status]"></el-table-column>
|
||||||
|
<el-table-column prop="createBy" label="创建人"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import {getRelateCaseTableList} from "@/api/test/project";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'relateCaseTable',
|
||||||
|
props: {
|
||||||
|
projectId: {
|
||||||
|
type: Number,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
list: [],
|
||||||
|
total: 0,
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
projectId(newVal){
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true
|
||||||
|
getRelateCaseTableList(this.projectId).then(res => {
|
||||||
|
this.list = res.rows
|
||||||
|
this.total = res.total
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-table v-loading="loading" :data="list" height="500">
|
||||||
|
<el-table-column prop="serialNumber" label="ID"></el-table-column>
|
||||||
|
<el-table-column prop="outline" label="摘要"></el-table-column>
|
||||||
|
<el-table-column prop="status" label="缺陷状态">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<dict-tag :options="dict.type.bug_status" :value="row.status"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="manager" label="经办人"></el-table-column>
|
||||||
|
<el-table-column prop="level" label="严重程度">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<dict-tag :options="dict.type.severity_level" :value="row.level"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="创建时间"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import {getRelateDefectList} from "@/api/test/project";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'relateCaseTable',
|
||||||
|
props: {
|
||||||
|
projectId: {
|
||||||
|
type: Number,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dicts: ['severity_level', 'bug_status'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 遮罩层
|
||||||
|
loading: true,
|
||||||
|
// 总条数
|
||||||
|
total: 0,
|
||||||
|
// 表格数据
|
||||||
|
list: [],
|
||||||
|
// 查询参数
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
projectId(newVal){
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true
|
||||||
|
getRelateDefectList(this.projectId).then(response => {
|
||||||
|
this.list = response.rows
|
||||||
|
this.total = response.total
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
63
test-ui/src/views/test/project/projectDetail/relatePlan.vue
Normal file
63
test-ui/src/views/test/project/projectDetail/relatePlan.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-table v-loading="loading" :data="list" height="500">
|
||||||
|
<el-table-column prop="statusLabel" label="状态" width="180"></el-table-column>
|
||||||
|
<el-table-column prop="name" label="计划名称"></el-table-column>
|
||||||
|
<el-table-column prop="manager" label="负责人"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"></pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getRelatePlanList} from "@/api/test/project";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'relatePlan',
|
||||||
|
dicts: ['status'],
|
||||||
|
props: {
|
||||||
|
projectId: {
|
||||||
|
type: Number,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
list: [],
|
||||||
|
loading: false,
|
||||||
|
total: 0,
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
projectId(newVal){
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
getRelatePlanList(this.projectId).then(response => {
|
||||||
|
this.list = response.rows.map(item => {
|
||||||
|
const matched = this.dict.type.status.find(e => e.value === item.status);
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
statusLabel: matched ? matched.label : '未知状态'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<div class="table-content">
|
<div class="table-content">
|
||||||
<el-table :data="tableData" border style="width: 100%">
|
<el-table :data="tableData" border style="width: 100%">
|
||||||
<el-table-column prop="triggerTime" label="触发时间"> </el-table-column>
|
<el-table-column prop="triggerTime" label="触发时间"> </el-table-column>
|
||||||
<el-table-column prop="startTime" label="开始时间"> </el-table-column>
|
<!-- <el-table-column prop="startTime" label="开始时间"> </el-table-column>-->
|
||||||
<el-table-column prop="triggerType" label="触发方式" :formatter="row => ['','定时任务', '手动'][row.triggerType]">
|
<el-table-column prop="triggerType" label="触发方式" :formatter="row => ['','定时任务', '手动'][row.triggerType]">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="resultDesc" label="情况描述"> </el-table-column>
|
<el-table-column prop="resultDesc" label="情况描述"> </el-table-column>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-tabs style="margin-top: 10px;">
|
<el-tabs style="margin-top: 10px;">
|
||||||
<el-Table v-loading="loading" :data="list">
|
<el-Table v-loading="loading" :data="list" @row-click="handleRowClick">
|
||||||
<el-table-column prop="name" label="测试报告名称" align="center"/>
|
<el-table-column prop="name" label="测试报告名称" align="center"/>
|
||||||
<el-table-column prop="result" label="测试结果" align="center">
|
<el-table-column prop="result" label="测试结果" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -35,6 +35,11 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="updateBy" label="最后更新人" align="center"/>
|
<el-table-column prop="updateBy" label="最后更新人" align="center"/>
|
||||||
<el-table-column prop="updateTime" label="最后更新时间" align="center"/>
|
<el-table-column prop="updateTime" label="最后更新时间" align="center"/>
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="text" size="mini" @click.native.stop="handleDel(row.id)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-Table>
|
</el-Table>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<pagination
|
<pagination
|
||||||
@@ -65,7 +70,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
|
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
|
||||||
import {addTestReport, getTestReportList} from "@/api/test/testPlan";
|
import {addTestReport, delExecuteCaseReport, getTestReportList} from "@/api/test/testPlan";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'caseReport',
|
name: 'caseReport',
|
||||||
@@ -91,7 +96,7 @@ export default {
|
|||||||
planId: '',
|
planId: '',
|
||||||
},
|
},
|
||||||
queryParams: {
|
queryParams: {
|
||||||
planId: '',
|
id: '',
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
},
|
},
|
||||||
@@ -101,14 +106,38 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
activated() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
console.log('getlist')
|
||||||
|
this.getList()
|
||||||
|
})
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getList()
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleRowClick(row){
|
||||||
|
this.$tab.openPage('平台报告', '/testplan/casereport/platformReport',
|
||||||
|
{id: row.id,
|
||||||
|
name: row.name,
|
||||||
|
type: row.type,
|
||||||
|
planId: row.planId,
|
||||||
|
report: row.report
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
handleDel(id) {
|
||||||
|
this.$modal.confirm('是否确认删除测试报告?').then(function () {
|
||||||
|
return delExecuteCaseReport(id);
|
||||||
|
}).then(() => {
|
||||||
|
this.getList();
|
||||||
|
this.$modal.msgSuccess("删除成功");
|
||||||
|
});
|
||||||
|
},
|
||||||
getList() {
|
getList() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.queryParams.planId = this.planId
|
this.queryParams.id = Number(this.planId)
|
||||||
getTestReportList(this.queryParams.planId).then(res => {
|
getTestReportList(this.queryParams).then(res => {
|
||||||
this.list = res.rows
|
this.list = res.rows
|
||||||
this.total = res.total
|
this.total = res.total
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|||||||
416
test-ui/src/views/test/testplan/caseReport/platformReport.vue
Normal file
416
test-ui/src/views/test/testplan/caseReport/platformReport.vue
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="24" class="page-header">
|
||||||
|
<el-page-header :title="reportTitle" @back="goBack" ></el-page-header>
|
||||||
|
<div class="header-buttons" v-if="isEdit">
|
||||||
|
<span>测试结果</span>
|
||||||
|
<el-select v-model="selectedResult" placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="item in resultOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-button type="primary" icon="el-icon-setting" @click="configureModule">配置模块</el-button>
|
||||||
|
<el-button type="primary" size="medium" @click="saveReport">保存报告</el-button>
|
||||||
|
<el-button type="danger" size="medium" @click="isEdit = false">取消编辑</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="header-buttons" v-else>
|
||||||
|
<el-button type="primary" icon="el-icon-setting" @click="configureModule">下载报告</el-button>
|
||||||
|
<el-button type="primary" size="medium" @click="isEdit = true">编辑报告</el-button>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div class="report-container" :class="testStatusClass">
|
||||||
|
<div class="report-header">
|
||||||
|
<div class="report-title" v-if="isEdit">
|
||||||
|
<el-input v-model="reportTitle" style="width: 300px" placeholder="请输入报告名称"/>
|
||||||
|
<div class="report-info">
|
||||||
|
<span>关联项目:中移商业保理平台</span>
|
||||||
|
<span>测试阶段:{{ reportType }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="report-title" v-else>
|
||||||
|
<span>{{ reportTitle }}</span>
|
||||||
|
<div class="report-info">
|
||||||
|
<span>关联项目:中移商业保理平台</span>
|
||||||
|
<span>测试阶段:{{ reportType }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="report-overview">
|
||||||
|
<div class="overview-item">
|
||||||
|
<div class="value">{{ reportData.caseNum }}</div>
|
||||||
|
<span class="label">执行用例数</span>
|
||||||
|
</div>
|
||||||
|
<div class="overview-item">
|
||||||
|
<span class="value">{{ passRate }}%</span>
|
||||||
|
<div class="progress-bar-container">
|
||||||
|
<div class="progress-bar" :style="{ width: passRate + '%' }"></div>
|
||||||
|
</div>
|
||||||
|
<div class="label">用例通过率</div>
|
||||||
|
</div>
|
||||||
|
<div class="overview-item">
|
||||||
|
<div class="value">0</div>
|
||||||
|
<span class="label">缺陷数</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-card>
|
||||||
|
<el-row>
|
||||||
|
<span style="font-size: 18px;color: #4f587e">用例分布</span>
|
||||||
|
</el-row>
|
||||||
|
<div class="charts">
|
||||||
|
<el-card shadow="hover" class="chart-item">
|
||||||
|
<h3>执行结果分布</h3>
|
||||||
|
<div id="result-distribution-chart"></div>
|
||||||
|
</el-card>
|
||||||
|
<el-card shadow="hover" class="chart-item">
|
||||||
|
<h3>用例类型分布</h3>
|
||||||
|
<div id="case-type-distribution-chart"></div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import router from "@/router";
|
||||||
|
import {updateExecuteCaseReport} from "@/api/test/testPlan";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'report',
|
||||||
|
dicts: ['test_type'],
|
||||||
|
props: {
|
||||||
|
planId: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isEdit: false,
|
||||||
|
reportId: '',
|
||||||
|
reportType: '',
|
||||||
|
reportTitle: '',
|
||||||
|
reportData: {},
|
||||||
|
selectedResult: '', // 选择的测试结果
|
||||||
|
resultOptions: [ // 测试结果选项
|
||||||
|
{ value: '1', label: '通过' },
|
||||||
|
{ value: '0', label: '不通过' }
|
||||||
|
],
|
||||||
|
|
||||||
|
// 执行结果分布数据
|
||||||
|
resultDistributionData: {
|
||||||
|
series: [
|
||||||
|
{ value: 0, name: '通过' ,itemStyle: {color: '#6fcdac'}},
|
||||||
|
{ value: 0, name: '失败' ,itemStyle: {color: '#e97d64'}},
|
||||||
|
{ value: 0, name: '跳过' ,itemStyle: {color: '#7484a1'}},
|
||||||
|
{ value: 0, name: '阻塞' ,itemStyle: {color: '#f5c539'}},
|
||||||
|
{ value: 0, name: '未执行' ,itemStyle: {color: '#cecece'}}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// 用例类型分布数据
|
||||||
|
caseTypeDistributionData: {
|
||||||
|
series: [
|
||||||
|
{ value: 0, name: '接口用例' ,itemStyle: {color: '#6fcdac'} }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.reportId = this.$route.query.id;
|
||||||
|
this.reportTitle = this.$route.query.name;
|
||||||
|
this.reportType = this.$route.query.type;
|
||||||
|
this.reportData = JSON.parse(this.$route.query.report);
|
||||||
|
this.selectedResult = this.reportData.result;
|
||||||
|
|
||||||
|
// 更新饼图数据
|
||||||
|
this.resultDistributionData.series[0].value = this.reportData.passNum || 0;
|
||||||
|
this.resultDistributionData.series[1].value = this.reportData.caseNum - this.reportData.passNum || 0;
|
||||||
|
|
||||||
|
// 更新接口用例饼图数据
|
||||||
|
this.caseTypeDistributionData.series[0].value = this.reportData.caseNum || 0;
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'dict.type.test_type': {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal && newVal.length > 0) {
|
||||||
|
const targetType = this.$route.query.type;
|
||||||
|
this.reportType = newVal.find(e => String(e.value) === String(targetType)).label;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
passRate() {
|
||||||
|
if (this.reportData.caseNum === 0) return 0;
|
||||||
|
return (this.reportData.passNum / this.reportData.caseNum) * 100;
|
||||||
|
},
|
||||||
|
testStatusClass() {
|
||||||
|
return this.selectedResult === '0' ? 'status-not-passed' : 'status-passed';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initResultDistributionChart();
|
||||||
|
this.initCaseTypeDistributionChart();
|
||||||
|
|
||||||
|
|
||||||
|
// 强制重绘图表以应用最新数据
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const chartDom = document.getElementById('result-distribution-chart');
|
||||||
|
if (chartDom) {
|
||||||
|
const myChart = echarts.getInstanceByDom(chartDom);
|
||||||
|
myChart.setOption({
|
||||||
|
series: [{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
data: this.resultDistributionData.series
|
||||||
|
}]
|
||||||
|
}, true); // true 表示合并选项
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initResultDistributionChart() {
|
||||||
|
const chartDom = document.getElementById('result-distribution-chart');
|
||||||
|
const myChart = echarts.init(chartDom);
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
right: 'right',
|
||||||
|
textStyle: {
|
||||||
|
color: '#666'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: '20',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: this.resultDistributionData.series
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
myChart.setOption(option);
|
||||||
|
},
|
||||||
|
initCaseTypeDistributionChart() {
|
||||||
|
const chartDom = document.getElementById('case-type-distribution-chart');
|
||||||
|
const myChart = echarts.init(chartDom);
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
right: 'right',
|
||||||
|
textStyle: {
|
||||||
|
color: '#666'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: this.caseTypeDistributionData.series
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
myChart.setOption(option);
|
||||||
|
},
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
this.$router.back();
|
||||||
|
// 关闭当前页面
|
||||||
|
this.$tab.closePage(router.currentRoute);
|
||||||
|
},
|
||||||
|
|
||||||
|
configureModule() {
|
||||||
|
// 配置模块的逻辑
|
||||||
|
console.log('Configure module');
|
||||||
|
},
|
||||||
|
|
||||||
|
saveReport() {
|
||||||
|
let reportData = {
|
||||||
|
caseNum: this.reportData.caseNum,
|
||||||
|
name: this.reportData.name,
|
||||||
|
passNum: this.reportData.passNum,
|
||||||
|
result: this.selectedResult // 使用外部的 result 值替换
|
||||||
|
};
|
||||||
|
// 构造要提交的数据对象
|
||||||
|
const reportDataToSave = {
|
||||||
|
id: this.reportId,
|
||||||
|
name: this.reportTitle,
|
||||||
|
result: this.selectedResult,
|
||||||
|
report: JSON.stringify(reportData)
|
||||||
|
};
|
||||||
|
|
||||||
|
updateExecuteCaseReport(reportDataToSave)
|
||||||
|
.then(response => {
|
||||||
|
this.$message.success('报告保存成功');
|
||||||
|
// 可选:更新页面状态为非编辑模式
|
||||||
|
this.isEdit = false;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$message.error('报告保存失败');
|
||||||
|
console.error('保存报告失败:', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.report-container {
|
||||||
|
height: 300px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
&.status-not-passed {
|
||||||
|
background: linear-gradient(180deg, #e7998b, #e8b1b0);
|
||||||
|
}
|
||||||
|
&.status-passed {
|
||||||
|
background: linear-gradient(180deg, #6fcdac, #9ed2bb);
|
||||||
|
}
|
||||||
|
.report-header {
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
|
||||||
|
.report-title {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-info {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-overview {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
color: white;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.overview-item {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar-container {
|
||||||
|
width: 100px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: #e0e0e0; /* 背景颜色表示未完成部分 */
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
background-color: white; /* 进度条颜色 */
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
//.progress-bar {
|
||||||
|
// width: 100px;
|
||||||
|
// height: 10px;
|
||||||
|
// background-color: white;
|
||||||
|
// margin-top: 10px;
|
||||||
|
// border-radius: 5px;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.charts {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-top: 20px;
|
||||||
|
.chart-item {
|
||||||
|
width: 45%;
|
||||||
|
h3 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
#result-distribution-chart, #case-type-distribution-chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.page-header {
|
||||||
|
::v-deep.el-page-header__title {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #000000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 100px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: solid 1px #e6e6e6;
|
||||||
|
|
||||||
|
.header-buttons {
|
||||||
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select, .el-button {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<el-Table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
<el-Table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
||||||
<el-table-column type="selection"/>
|
<el-table-column type="selection"/>
|
||||||
<el-table-column prop="caseName" label="测试用例名称" align="center"/>
|
<el-table-column prop="caseName" label="测试用例名称" align="center"/>
|
||||||
<el-table-column prop="executeResult" label="执行结果" align="center"/>
|
<el-table-column prop="executeResult" label="执行结果" :formatter="row => ['未执行', '成功', '失败'][row.executeResult]" align="center"/>
|
||||||
<el-table-column prop="createBy" label="创建人" align="center"/>
|
<el-table-column prop="createBy" label="创建人" align="center"/>
|
||||||
<el-table-column prop="executeBy" label="最近执行人" align="center"/>
|
<el-table-column prop="executeBy" label="最近执行人" align="center"/>
|
||||||
<el-table-column prop="executeTime" label="最近执行时间" align="center"/>
|
<el-table-column prop="executeTime" label="最近执行时间" align="center"/>
|
||||||
@@ -176,7 +176,9 @@ export default {
|
|||||||
return runTestPlanCase(queryParams);
|
return runTestPlanCase(queryParams);
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
this.$modal.msgSuccess("提交执行成功");
|
this.$modal.msgSuccess("提交执行成功");
|
||||||
this.open = true;
|
// this.open = true;
|
||||||
|
this.loading = false;
|
||||||
|
}).finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -123,7 +123,7 @@
|
|||||||
placement="top-start"
|
placement="top-start"
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
>
|
>
|
||||||
<span v-if="scope.row.smokeTest === null">暂无关联用例</span>
|
<span v-if="scope.row.functionTest === null">暂无关联用例</span>
|
||||||
<div v-else class="custom-content">
|
<div v-else class="custom-content">
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
placement="top-start"
|
placement="top-start"
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
>
|
>
|
||||||
<span v-if="scope.row.smokeTest === null">暂无关联用例</span>
|
<span v-if="scope.row.regressionTest === null">暂无关联用例</span>
|
||||||
<div v-else class="custom-content">
|
<div v-else class="custom-content">
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
placement="top-start"
|
placement="top-start"
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
>
|
>
|
||||||
<span v-if="scope.row.smokeTest === null">暂无关联用例</span>
|
<span v-if="scope.row.preProductionTest === null">暂无关联用例</span>
|
||||||
<div v-else class="custom-content">
|
<div v-else class="custom-content">
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
@@ -210,7 +210,7 @@
|
|||||||
placement="top-start"
|
placement="top-start"
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
>
|
>
|
||||||
<span v-if="scope.row.smokeTest === null">暂无关联用例</span>
|
<span v-if="scope.row.productionTest === null">暂无关联用例</span>
|
||||||
<div v-else class="custom-content">
|
<div v-else class="custom-content">
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
@@ -375,6 +375,9 @@ export default {
|
|||||||
{id: row.id,
|
{id: row.id,
|
||||||
name: row.name,
|
name: row.name,
|
||||||
testStatus: row.status,
|
testStatus: row.status,
|
||||||
|
startPlanTime: row.startPlanTime,
|
||||||
|
endPlanTime: row.endPlanTime,
|
||||||
|
manager: row.manager,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleTabClick(tab) {
|
handleTabClick(tab) {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<!-- 测试计划界面 -->
|
<!-- 测试计划界面 -->
|
||||||
<probably-view :planId="planId" v-show="activeIndex === '1'"></probably-view>
|
<probably-view :overViewData="overViewData" :planId="planId" v-show="activeIndex === '1'"></probably-view>
|
||||||
<case-execute :planId="planId" :name="testTitle" v-show="activeIndex === '2'"></case-execute>
|
<case-execute :planId="planId" :name="testTitle" v-show="activeIndex === '2'"></case-execute>
|
||||||
<case-report :planId="planId" v-show="activeIndex === '3'"></case-report>
|
<case-report :planId="planId" v-show="activeIndex === '3'"></case-report>
|
||||||
<test-defects :planId="planId" :name="testTitle" v-show="activeIndex === '4'"></test-defects>
|
<test-defects :planId="planId" :name="testTitle" v-show="activeIndex === '4'"></test-defects>
|
||||||
@@ -66,6 +66,7 @@ export default {
|
|||||||
'3': '已终止'
|
'3': '已终止'
|
||||||
},
|
},
|
||||||
planId: '',
|
planId: '',
|
||||||
|
overViewData: {},
|
||||||
testStatus: '',
|
testStatus: '',
|
||||||
testTitle: '',
|
testTitle: '',
|
||||||
activeTab: 'overview',// 默认选中的标签页
|
activeTab: 'overview',// 默认选中的标签页
|
||||||
@@ -114,6 +115,11 @@ export default {
|
|||||||
this.planId = this.$route.query.id;
|
this.planId = this.$route.query.id;
|
||||||
this.testTitle = this.$route.query.name
|
this.testTitle = this.$route.query.name
|
||||||
this.testStatus = this.$route.query.testStatus
|
this.testStatus = this.$route.query.testStatus
|
||||||
|
this.overViewData = {
|
||||||
|
startTime: this.$route.query.startPlanTime,
|
||||||
|
endTime: this.$route.query.endPlanTime,
|
||||||
|
manager: this.$route.query.manager
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -17,14 +17,13 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<!-- <span>起止时间:{{ startToEndTime }}</span>-->
|
<span>起止时间:{{ overViewData.startTime }} ~ {{ overViewData.endTime }}</span>
|
||||||
<span>起止时间:2020-04-01 ~ 2020-04-30</span>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="7">
|
<el-col :span="7">
|
||||||
<span>当前测试计划已经开始 3 天,距离截止时间还有 52 天</span>
|
<span>当前测试计划已经开始 {{ daysElapsed }} 天,距离截止时间还有 {{ daysRemaining }} 天</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<span>负责人:{{ }}</span>
|
<span>负责人:{{ overViewData.manager }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,10 +33,10 @@
|
|||||||
<div class="progress-content">
|
<div class="progress-content">
|
||||||
<div id="executionProgressChart" style="width: 70%; height: 200px;"></div>
|
<div id="executionProgressChart" style="width: 70%; height: 200px;"></div>
|
||||||
<div class="progress-text">
|
<div class="progress-text">
|
||||||
<p style="font-size: 40px;margin: auto">100%</p>
|
<p style="font-size: 40px;margin: auto">{{ executionProgress || 0 }}%</p>
|
||||||
<p>执行进度</p>
|
<p>执行进度</p>
|
||||||
<p>用例总数 1</p>
|
<p>用例总数 {{ viewData.caseCount }}</p>
|
||||||
<p><span style="color: #409EFF">●</span> 已执行用例 1</p>
|
<p><span style="color: #6e92ef">●</span> 已执行用例 {{ viewData.executedCaseCount }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -47,13 +46,13 @@
|
|||||||
<div class="progress-content">
|
<div class="progress-content">
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col class="rate-content" :span="12">
|
<el-col class="rate-content" :span="12">
|
||||||
<p>100%</p>
|
<p>{{ executionPassRate || 0 }}%</p>
|
||||||
<el-progress :percentage="100" status="success"></el-progress>
|
<el-progress :percentage="(viewData.passedCaseCount / viewData.executedCaseCount).toFixed(2) * 100 || 0" status="success"></el-progress>
|
||||||
<p>执行用例通过率</p>
|
<p>执行用例通过率</p>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col class="rate-content" :span="12">
|
<el-col class="rate-content" :span="12">
|
||||||
<p>100%</p>
|
<p>{{ overallPassRate || 0 }}%</p>
|
||||||
<el-progress :percentage="100" status="success"></el-progress>
|
<el-progress :percentage="(viewData.passedCaseCount / viewData.caseCount).toFixed(2) * 100 || 0" status="success"></el-progress>
|
||||||
<p>总体用例通过率</p>
|
<p>总体用例通过率</p>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -64,14 +63,14 @@
|
|||||||
<el-card shadow="hover">
|
<el-card shadow="hover">
|
||||||
<div class="progress-content">
|
<div class="progress-content">
|
||||||
<div class="defect-content">
|
<div class="defect-content">
|
||||||
<p>0</p>
|
<p>{{ viewData.defectCount }}</p>
|
||||||
<p>缺陷数</p>
|
<p>缺陷数</p>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="4">0</el-col>
|
<el-col :span="4">{{ viewData.unconfirmedDefectCount }}</el-col>
|
||||||
<el-col :span="4">0</el-col>
|
<el-col :span="4">{{ viewData.fixingDefectCount }}</el-col>
|
||||||
<el-col :span="4">0</el-col>
|
<el-col :span="4">{{ viewData.unverifiedDefectCount }}</el-col>
|
||||||
<el-col :span="4">0</el-col>
|
<el-col :span="4">{{ viewData.invalidDefectCount }}</el-col>
|
||||||
<el-col :span="4">0</el-col>
|
<el-col :span="4">{{ viewData.suspendedDefectCount }}</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="4">待确认</el-col>
|
<el-col :span="4">待确认</el-col>
|
||||||
@@ -98,13 +97,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-card shadow="hover">
|
<el-card shadow="hover">
|
||||||
<div class="distribution-content">
|
<div class="distribution-content">
|
||||||
<el-progress type="circle" :percentage="100" color="#67C23A"></el-progress>
|
<div id="distributionChart" style="width: 70%; height: 200px;"></div>
|
||||||
<div class="distribution-text">
|
<div class="distribution-text">
|
||||||
<p><span style="background-color: #67C23A; display: inline-block; width: 10px; height: 10px;"></span> 通过 1</p>
|
<p><span style="background-color: #9cdab9; display: inline-block; width: 10px; height: 10px;"></span> 通过 {{ viewData.passedCaseCount }}</p>
|
||||||
<p><span style="background-color: #F56C6C; display: inline-block; width: 10px; height: 10px;"></span> 失败 0</p>
|
<p><span style="background-color: #d4836a; display: inline-block; width: 10px; height: 10px;"></span> 失败 {{ viewData.failedCaseCount }}</p>
|
||||||
<p><span style="background-color: #E6A23C; display: inline-block; width: 10px; height: 10px;"></span> 阻塞 0</p>
|
<p><span style="background-color: #e9c456; display: inline-block; width: 10px; height: 10px;"></span> 阻塞 {{ viewData.blockedCaseCount }}</p>
|
||||||
<p><span style="background-color: #F56C6C; display: inline-block; width: 10px; height: 10px;"></span> 跳过 0</p>
|
<p><span style="background-color: #79849e; display: inline-block; width: 10px; height: 10px;"></span> 跳过 {{ viewData.skippedCaseCount }}</p>
|
||||||
<p><span style="background-color: #909399; display: inline-block; width: 10px; height: 10px;"></span> 未执行 0</p>
|
<p><span style="background-color: #cdcdcd; display: inline-block; width: 10px; height: 10px;"></span> 未执行 {{ viewData.notExecutedCaseCount }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -117,12 +116,34 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-card shadow="hover">
|
<el-card shadow="hover">
|
||||||
<div class="distribution-text" style="height: 200px">
|
<div class="distribution-text" style="height: 200px">
|
||||||
<line-chart :data="trendData" :chart-data="trendData"></line-chart>
|
<div id="caseTrendChart" style="width: 100%; height: 200px;"></div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card class="box-card">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span>缺陷等级分布</span>
|
||||||
|
</div>
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<div id="defectLevelChart" style="width: 100%; height: 200px;"></div>
|
||||||
|
</el-card>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card class="box-card">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span>缺陷类型分布</span>
|
||||||
|
</div>
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<div id="defectTypeChart" style="width: 100%; height: 200px;"></div>
|
||||||
|
</el-card>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
<el-card class="box-card">
|
<el-card class="box-card">
|
||||||
<div>
|
<div>
|
||||||
关联需求信息
|
关联需求信息
|
||||||
@@ -160,25 +181,29 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LineChart from '@/views/dashboard/LineChart';
|
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import {getTestPlanProjectList} from "@/api/test/testPlan";
|
import moment from 'moment';
|
||||||
|
import {getPlanCaseTrendData, getPlanOverview, getTestPlanProjectList} from "@/api/test/testPlan";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'probablyView',
|
name: 'probablyView',
|
||||||
components: {
|
|
||||||
LineChart,
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
planId: {
|
planId: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
overViewData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
manager: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
|
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
|
||||||
mounted() {
|
|
||||||
this.initExecutionProgressChart();
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
priorityColor: {
|
priorityColor: {
|
||||||
@@ -192,42 +217,84 @@ export default {
|
|||||||
list: [],
|
list: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
myChart: null,
|
myChart: null,
|
||||||
startToEndTime: '',
|
distributionChart: null,
|
||||||
|
lineChart: null,
|
||||||
|
viewData: [],
|
||||||
|
caseTrendData: [],
|
||||||
|
// 执行进度
|
||||||
|
executionProgress: 0,
|
||||||
|
// 执行用例通过率
|
||||||
|
executionPassRate: 0,
|
||||||
|
// 总体用例通过率
|
||||||
|
overallPassRate: 0,
|
||||||
defectSlider: 0,
|
defectSlider: 0,
|
||||||
queryParams: {
|
queryParams: {
|
||||||
planId: '',
|
planId: '',
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
},
|
},
|
||||||
trendData: {
|
|
||||||
labels: ['2025-04-01', '2025-04-02', '2025-04-03'],
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: '通过',
|
|
||||||
backgroundColor: '#67C23A',
|
|
||||||
data: [0, 0.5, 1],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '失败',
|
|
||||||
backgroundColor: '#F56C6C',
|
|
||||||
data: [0, 0, 0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '跳过',
|
|
||||||
backgroundColor: '#E6A23C',
|
|
||||||
data: [0, 0, 0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '阻塞',
|
|
||||||
backgroundColor: '#909399',
|
|
||||||
data: [0, 0, 0],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
// 饼图1
|
||||||
|
const chartDom = document.getElementById('executionProgressChart');
|
||||||
|
this.myChart = echarts.init(chartDom);
|
||||||
|
const Observer = new ResizeObserver(() =>{
|
||||||
|
this.myChart.resize();
|
||||||
|
})
|
||||||
|
Observer.observe(chartDom);
|
||||||
|
|
||||||
|
// 饼图2
|
||||||
|
const chartDom2 = document.getElementById('distributionChart');
|
||||||
|
this.distributionChart = echarts.init(chartDom2);
|
||||||
|
const Observer2 = new ResizeObserver(() =>{
|
||||||
|
this.distributionChart.resize();
|
||||||
|
})
|
||||||
|
Observer2.observe(chartDom2);
|
||||||
|
|
||||||
|
// 折线趋势图
|
||||||
|
const lineChartDom = document.getElementById('caseTrendChart');
|
||||||
|
this.lineChart = echarts.init(lineChartDom);
|
||||||
|
const Observer3 = new ResizeObserver(() =>{
|
||||||
|
this.lineChart.resize();
|
||||||
|
})
|
||||||
|
Observer3.observe(lineChartDom);
|
||||||
|
|
||||||
|
// 柱状图1
|
||||||
|
const barChartDom = document.getElementById('defectLevelChart');
|
||||||
|
this.barChart = echarts.init(barChartDom);
|
||||||
|
const Observer4 = new ResizeObserver(() =>{
|
||||||
|
this.barChart.resize();
|
||||||
|
})
|
||||||
|
Observer4.observe(barChartDom);
|
||||||
|
|
||||||
|
// 柱状图2
|
||||||
|
const barChartDom2 = document.getElementById('defectTypeChart');
|
||||||
|
this.barChart2 = echarts.init(barChartDom2);
|
||||||
|
const Observer5 = new ResizeObserver(() =>{
|
||||||
|
this.barChart2.resize();
|
||||||
|
})
|
||||||
|
Observer5.observe(barChartDom2);
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getList();
|
this.getList();
|
||||||
|
this.getInfoList();
|
||||||
|
this.getTrendData();
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
daysElapsed() {
|
||||||
|
if (!this.overViewData.startTime) return 0;
|
||||||
|
const start = moment(this.overViewData.startTime);
|
||||||
|
const now = moment();
|
||||||
|
return now.diff(start, 'days');
|
||||||
|
},
|
||||||
|
daysRemaining() {
|
||||||
|
if (!this.overViewData.endTime) return 0;
|
||||||
|
const end = moment(this.overViewData.endTime);
|
||||||
|
const now = moment();
|
||||||
|
const diffDays = end.diff(now, 'days');
|
||||||
|
return diffDays > 0 ? diffDays : 0; // 确保不显示负数
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getList() {
|
getList() {
|
||||||
@@ -239,11 +306,37 @@ export default {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getInfoList() {
|
||||||
|
getPlanOverview(this.queryParams.planId).then(response => {
|
||||||
|
this.viewData = response;
|
||||||
|
this.executionProgress = (this.viewData.executedCaseCount / this.viewData.caseCount).toFixed(2) * 100 || 0;
|
||||||
|
if (this.viewData.executedCaseCount === 0) {
|
||||||
|
this.executionPassRate = 0;
|
||||||
|
} else {
|
||||||
|
this.executionPassRate = ((this.viewData.passedCaseCount / this.viewData.executedCaseCount).toFixed(2) * 100).toFixed(2) || 0 ;
|
||||||
|
}
|
||||||
|
if (this.viewData.caseCount === 0) {
|
||||||
|
this.overallPassRate = 0;
|
||||||
|
} else {
|
||||||
|
this.overallPassRate = ((this.viewData.passedCaseCount / this.viewData.caseCount).toFixed(2) * 100).toFixed(2) || 0;
|
||||||
|
}
|
||||||
|
this.initExecutionProgressChart();
|
||||||
|
this.initDistributionChart();
|
||||||
|
this.initCaseTrendChart();
|
||||||
|
this.initDefectLevelChart();
|
||||||
|
this.initDefectTypeChart();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getTrendData(){
|
||||||
|
getPlanCaseTrendData(this.queryParams.planId).then(response => {
|
||||||
|
this.caseTrendData = response;
|
||||||
|
this.initCaseTrendChart();
|
||||||
|
})
|
||||||
|
},
|
||||||
// 初始化执行进度饼图
|
// 初始化执行进度饼图
|
||||||
initExecutionProgressChart() {
|
initExecutionProgressChart() {
|
||||||
const chartDom = document.getElementById('executionProgressChart');
|
|
||||||
const myChart = echarts.init(chartDom);
|
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item'
|
trigger: 'item'
|
||||||
@@ -254,36 +347,230 @@ export default {
|
|||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: 'Access From',
|
name: '执行进度',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['40%', '70%'],
|
radius: ['40%', '70%'],
|
||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
label: {
|
label: {
|
||||||
show: false,
|
formatter: '{b}: {c}%',
|
||||||
position: 'center'
|
position: 'inside'
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
fontSize: 40,
|
|
||||||
fontWeight: 'bold'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
labelLine: {
|
|
||||||
show: false
|
|
||||||
},
|
},
|
||||||
data: [
|
data: [
|
||||||
{value: 1},
|
{ value: this.executionProgress, name: '执行进度' , itemStyle: { color: '#6e92ef'}},
|
||||||
|
{ value: 100 - this.executionProgress, name: '剩余进度' , itemStyle: { color: '#f5f5f5'}}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
myChart.setOption(option);
|
option && this.myChart.setOption(option);
|
||||||
// 监听窗口大小变化,自动调整图表大小
|
},
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
myChart.resize();
|
// 初始化执行分布饼图
|
||||||
});
|
initDistributionChart() {
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '用例状态',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['50%', '70%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{ value: this.viewData.passedCaseCount, itemStyle: { color: '#9cdab9' } },
|
||||||
|
{ value: this.viewData.failedCaseCount, itemStyle: { color: '#d4836a' } },
|
||||||
|
{ value: this.viewData.blockedCaseCount, itemStyle: { color: '#e9c456' } },
|
||||||
|
{ value: this.viewData.skippedCaseCount, itemStyle: { color: '#79849e' } },
|
||||||
|
{ value: this.viewData.notExecutedCaseCount, itemStyle: { color: '#cdcdcd' } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
option && this.distributionChart.setOption(option);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 初始化折线图
|
||||||
|
initCaseTrendChart() {
|
||||||
|
const dates = this.caseTrendData.map(item => item.caseTrendDates);
|
||||||
|
|
||||||
|
const passedData = this.caseTrendData.map(item => item.passed);
|
||||||
|
const failedData = this.caseTrendData.map(item => item.failed);
|
||||||
|
const blockedData = this.caseTrendData.map(item => item.blocked);
|
||||||
|
const skippedData = this.caseTrendData.map(item => item.skipped);
|
||||||
|
const notExecutedData = this.caseTrendData.map(item => item.notExecuted);
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['阻塞', '通过', '跳过', '失败', '未执行'],
|
||||||
|
left: 'left'
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: dates
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '阻塞',
|
||||||
|
type: 'line',
|
||||||
|
data: blockedData
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '通过',
|
||||||
|
type: 'line',
|
||||||
|
data: passedData
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '跳过',
|
||||||
|
type: 'line',
|
||||||
|
data: skippedData
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '失败',
|
||||||
|
type: 'line',
|
||||||
|
data: failedData
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '未执行',
|
||||||
|
type: 'line',
|
||||||
|
data: notExecutedData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
option && this.lineChart.setOption(option);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 初始化柱状图
|
||||||
|
initDefectLevelChart() {
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['缺陷数']
|
||||||
|
},
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
data: ['致命', '严重', '一般', '轻微', '无效'],
|
||||||
|
axisTick: {
|
||||||
|
alignWithLabel: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '缺陷数',
|
||||||
|
type: 'bar',
|
||||||
|
barWidth: '30%',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#D68972FF' // 更改柱状图颜色为橙色
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
this.viewData.fatalLevelDefectCount,
|
||||||
|
this.viewData.seriousLevelDefectCount,
|
||||||
|
this.viewData.normalLevelDefectCount,
|
||||||
|
this.viewData.minorLevelDefectCount,
|
||||||
|
this.viewData.invalidLevelDefectCount
|
||||||
|
] // 更新数据以匹配图片
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
option && this.barChart.setOption(option);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 初始化柱状图
|
||||||
|
initDefectTypeChart() {
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['缺陷数']
|
||||||
|
},
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
data: ['功能逻辑', 'UI交互', '性能问题', '兼容性问题', '配置错误','安全问题', '安装部署' ,'其他'],
|
||||||
|
axisTick: {
|
||||||
|
alignWithLabel: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '缺陷数',
|
||||||
|
type: 'bar',
|
||||||
|
barWidth: '30%',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#D68972FF' // 更改柱状图颜色为橙色
|
||||||
|
},
|
||||||
|
data: [this.viewData.logicDefectCount,
|
||||||
|
this.viewData.uiDefectCount,
|
||||||
|
this.viewData.performanceDefectCount,
|
||||||
|
this.viewData.compatibilityDefectCount,
|
||||||
|
this.viewData.configurationDefectCount,
|
||||||
|
this.viewData.securityDefectCount,
|
||||||
|
this.viewData.installationDefectCount,
|
||||||
|
this.viewData.otherDefectCount
|
||||||
|
] // 更新数据以匹配图片
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
option && this.barChart2.setOption(option);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,7 +59,8 @@
|
|||||||
<el-button size="mini" @click="handleClickSave">保存</el-button>
|
<el-button size="mini" @click="handleClickSave">保存</el-button>
|
||||||
</div>
|
</div>
|
||||||
<SceneStep v-show="informationForm.uiSceneStepsVOList.length > 0" :detail="changeStep" />
|
<SceneStep v-show="informationForm.uiSceneStepsVOList.length > 0" :detail="changeStep" />
|
||||||
<AdvancedSetting v-show="informationForm.uiSceneStepsVOList.length > 0" />
|
<AdvancedSetting v-show="informationForm.uiSceneStepsVOList.length > 0" :detail="changeStep"
|
||||||
|
@changeSetting="changeSetting" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 添加步骤 -->
|
<!-- 添加步骤 -->
|
||||||
<div class="add-btn">
|
<div class="add-btn">
|
||||||
@@ -80,7 +81,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import AdvancedSetting from './advancedSetting.vue'
|
import AdvancedSetting from './advancedSetting.vue'
|
||||||
import SceneStep from './sceneStep.vue'
|
import SceneStep from './sceneStep.vue'
|
||||||
import { getAutomationDetail, updateAutomation } from '../../../api/uiTest/automationTest'
|
import { getAutomationDetail, addAutomation } from '../../../api/uiTest/automationTest'
|
||||||
import { listGroup } from "../../../api/test/group"
|
import { listGroup } from "../../../api/test/group"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -109,7 +110,7 @@ export default {
|
|||||||
},
|
},
|
||||||
stepList: [],
|
stepList: [],
|
||||||
defaultActive: 0, // 当前激活菜单的 index
|
defaultActive: 0, // 当前激活菜单的 index
|
||||||
changeStep: null, // 选中的步骤
|
changeStep: { operateType: null }, // 选中的步骤
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -125,6 +126,10 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
// 修改高级设置
|
||||||
|
changeSetting(val) {
|
||||||
|
this.informationForm.uiSceneStepsVOList[this.defaultActive - 1].uiHighSettingVOList = val
|
||||||
|
},
|
||||||
|
|
||||||
// 删除步骤
|
// 删除步骤
|
||||||
deleteStep(index) {
|
deleteStep(index) {
|
||||||
@@ -175,17 +180,21 @@ export default {
|
|||||||
handleClickSave() {
|
handleClickSave() {
|
||||||
if (this.informationForm.name === null || this.informationForm.name === '') {
|
if (this.informationForm.name === null || this.informationForm.name === '') {
|
||||||
this.$modal.msgWarning("请输入名称!");
|
this.$modal.msgWarning("请输入名称!");
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (this.informationForm.groupId === null || this.informationForm.groupId === '') {
|
if (this.informationForm.groupId === null || this.informationForm.groupId === '') {
|
||||||
this.$modal.msgWarning("请选择模块!");
|
this.$modal.msgWarning("请选择模块!");
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (this.informationForm.status === null || this.informationForm.status === '') {
|
if (this.informationForm.status === null || this.informationForm.status === '') {
|
||||||
this.$modal.msgWarning("请选择状态");
|
this.$modal.msgWarning("请选择状态");
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (this.informationForm.dutyBy === null || this.informationForm.dutyBy === '') {
|
if (this.informationForm.dutyBy === null || this.informationForm.dutyBy === '') {
|
||||||
this.$modal.msgWarning("请输入责任人");
|
this.$modal.msgWarning("请输入责任人");
|
||||||
|
return
|
||||||
}
|
}
|
||||||
updateAutomation(this.informationForm).then(res => {
|
addAutomation(this.informationForm).then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.$modal.msgSuccess("编辑成功")
|
this.$modal.msgSuccess("编辑成功")
|
||||||
this.$tab.closeOpenPage({ path: "/ui-test/automation-test" });
|
this.$tab.closeOpenPage({ path: "/ui-test/automation-test" });
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user