diff --git a/Dockerfile b/Dockerfile index c587c39604..ce96082847 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.fit2cloud.com/fit2cloud2/fabric8-java-alpine-openjdk8-jre +FROM registry.fit2cloud.com/metersphere/fabric8-java-alpine-openjdk8-jre MAINTAINER FIT2CLOUD @@ -10,6 +10,5 @@ ENV JAVA_APP_JAR=/opt/apps/backend-1.0.jar ENV AB_OFF=true -ENV JAVA_OPTIONS=-Dfile.encoding=utf-8 - +ENV JAVA_OPTIONS="-Dfile.encoding=utf-8 -Djava.awt.headless=true" CMD ["/deployments/run-java.sh"] diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java index 9eb110c61f..804119e525 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java @@ -3,6 +3,7 @@ package io.metersphere.base.mapper.ext; import io.metersphere.base.domain.TestCase; import io.metersphere.controller.request.ReportRequest; import io.metersphere.controller.request.testcase.QueryTestCaseRequest; +import io.metersphere.controller.request.testplancase.QueryTestPlanCaseRequest; import io.metersphere.dto.ReportDTO; import io.metersphere.dto.TestPlanCaseDTO; import org.apache.ibatis.annotations.Param; @@ -13,5 +14,5 @@ public interface ExtTestCaseMapper { List getTestCaseNames(@Param("request") QueryTestCaseRequest request); - List getTestPlanTestCases(@Param("request") QueryTestCaseRequest request); + List getTestPlanTestCases(@Param("request") QueryTestPlanCaseRequest request); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml index dd0b0deea4..f1af223d20 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml @@ -37,5 +37,8 @@ and t2.name like CONCAT('%', #{request.name},'%') + + and t1.executor = #{request.executor} + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/config/I18nConfig.java b/backend/src/main/java/io/metersphere/config/I18nConfig.java index 3d23aee8a9..8d5a5d5548 100644 --- a/backend/src/main/java/io/metersphere/config/I18nConfig.java +++ b/backend/src/main/java/io/metersphere/config/I18nConfig.java @@ -2,6 +2,7 @@ package io.metersphere.config; import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.i18n.I18nManager; +import io.metersphere.i18n.Translator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -20,6 +21,12 @@ public class I18nConfig { return new I18nManager(dirs); } + @Bean + @ConditionalOnMissingBean + public Translator translator() { + return new Translator(); + } + @Bean @ConditionalOnMissingBean public CommonBeanFactory commonBeanFactory() { diff --git a/backend/src/main/java/io/metersphere/controller/HelloController.java b/backend/src/main/java/io/metersphere/controller/HelloController.java new file mode 100644 index 0000000000..2263748feb --- /dev/null +++ b/backend/src/main/java/io/metersphere/controller/HelloController.java @@ -0,0 +1,21 @@ +package io.metersphere.controller; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@RestController +@RequestMapping("anonymous") +public class HelloController { + @Resource + MessageSource messageSource; + + @GetMapping("hello") + public String hello() { + return messageSource.getMessage("max_thread_insufficient", null, "默认值", LocaleContextHolder.getLocale()); + } +} diff --git a/backend/src/main/java/io/metersphere/controller/LoginController.java b/backend/src/main/java/io/metersphere/controller/LoginController.java index 1b11a4abb5..62b3e8656e 100644 --- a/backend/src/main/java/io/metersphere/controller/LoginController.java +++ b/backend/src/main/java/io/metersphere/controller/LoginController.java @@ -3,13 +3,13 @@ package io.metersphere.controller; import io.metersphere.base.domain.UserRole; import io.metersphere.controller.request.LoginRequest; import io.metersphere.dto.UserDTO; -import io.metersphere.i18n.Translator; import io.metersphere.service.UserService; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.UnauthorizedException; import org.apache.shiro.subject.Subject; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @@ -26,7 +26,7 @@ public class LoginController { @GetMapping(value = "/isLogin") public ResultHolder isLogin() { if (SecurityUtils.getSubject().isAuthenticated()) { - return ResultHolder.success(Translator.getLangDes()); + return ResultHolder.success(LocaleContextHolder.getLocale()); } return ResultHolder.error(""); } diff --git a/backend/src/main/java/io/metersphere/controller/TestPlanTestCaseController.java b/backend/src/main/java/io/metersphere/controller/TestPlanTestCaseController.java index d09f9ade9d..8819e5d113 100644 --- a/backend/src/main/java/io/metersphere/controller/TestPlanTestCaseController.java +++ b/backend/src/main/java/io/metersphere/controller/TestPlanTestCaseController.java @@ -7,6 +7,8 @@ import io.metersphere.base.domain.TestPlanTestCase; import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.Pager; import io.metersphere.controller.request.testcase.QueryTestCaseRequest; +import io.metersphere.controller.request.testcase.TestCaseBatchRequest; +import io.metersphere.controller.request.testplancase.QueryTestPlanCaseRequest; import io.metersphere.dto.TestPlanCaseDTO; import io.metersphere.service.TestPlanTestCaseService; import org.springframework.web.bind.annotation.*; @@ -22,7 +24,7 @@ public class TestPlanTestCaseController { TestPlanTestCaseService testPlanTestCaseService; @PostMapping("/list/{goPage}/{pageSize}") - public Pager> getTestPlanCases(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryTestCaseRequest request){ + public Pager> getTestPlanCases(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryTestPlanCaseRequest request){ Page page = PageHelper.startPage(goPage, pageSize, true); return PageUtils.setPageInfo(page, testPlanTestCaseService.getTestPlanCases(request)); } @@ -32,11 +34,14 @@ public class TestPlanTestCaseController { testPlanTestCaseService.editTestCase(testPlanTestCase); } + @PostMapping("/batch/edit") + public void editTestCaseBath(@RequestBody TestCaseBatchRequest request){ + testPlanTestCaseService.editTestCaseBath(request); + } + @PostMapping("/delete/{id}") public int deleteTestCase(@PathVariable Integer id){ return testPlanTestCaseService.deleteTestCase(id); } - - } diff --git a/backend/src/main/java/io/metersphere/controller/request/testcase/TestCaseBatchRequest.java b/backend/src/main/java/io/metersphere/controller/request/testcase/TestCaseBatchRequest.java new file mode 100644 index 0000000000..8f7caf5e39 --- /dev/null +++ b/backend/src/main/java/io/metersphere/controller/request/testcase/TestCaseBatchRequest.java @@ -0,0 +1,19 @@ +package io.metersphere.controller.request.testcase; + +import io.metersphere.base.domain.TestCase; +import io.metersphere.base.domain.TestPlanTestCase; + +import java.util.List; + +public class TestCaseBatchRequest extends TestPlanTestCase { + + private List ids; + + public List getIds() { + return ids; + } + + public void setIds(List ids) { + this.ids = ids; + } +} diff --git a/backend/src/main/java/io/metersphere/controller/request/testplancase/QueryTestPlanCaseRequest.java b/backend/src/main/java/io/metersphere/controller/request/testplancase/QueryTestPlanCaseRequest.java new file mode 100644 index 0000000000..7acf17512d --- /dev/null +++ b/backend/src/main/java/io/metersphere/controller/request/testplancase/QueryTestPlanCaseRequest.java @@ -0,0 +1,39 @@ +package io.metersphere.controller.request.testplancase; + +import io.metersphere.base.domain.TestCase; +import io.metersphere.base.domain.TestPlanTestCase; + +import java.util.List; + +public class QueryTestPlanCaseRequest extends TestPlanTestCase { + + private List nodeIds; + + private String workspaceId; + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getNodeIds() { + return nodeIds; + } + + public void setNodeIds(List nodeIds) { + this.nodeIds = nodeIds; + } + + public String getWorkspaceId() { + return workspaceId; + } + + public void setWorkspaceId(String workspaceId) { + this.workspaceId = workspaceId; + } +} diff --git a/backend/src/main/java/io/metersphere/i18n/Translator.java b/backend/src/main/java/io/metersphere/i18n/Translator.java index c8556e1fd8..0f0303c466 100644 --- a/backend/src/main/java/io/metersphere/i18n/Translator.java +++ b/backend/src/main/java/io/metersphere/i18n/Translator.java @@ -1,271 +1,22 @@ package io.metersphere.i18n; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.serializer.JavaBeanSerializer; -import com.alibaba.fastjson.serializer.ObjectSerializer; -import com.alibaba.fastjson.serializer.SerializeConfig; -import io.metersphere.commons.constants.I18nConstants; -import io.metersphere.commons.exception.MSException; -import io.metersphere.commons.utils.BeanUtils; -import io.metersphere.commons.utils.CommonBeanFactory; -import io.metersphere.commons.utils.LogUtil; -import io.metersphere.service.SystemParameterService; -import io.metersphere.user.SessionUtils; -import org.apache.commons.collections4.MapUtils; -import org.apache.commons.collections4.map.PassiveExpiringMap; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.commons.text.StringSubstitutor; -import org.springframework.http.HttpHeaders; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import java.lang.reflect.Array; -import java.util.*; -import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; public class Translator { + private static MessageSource messageSource; - public static final String PREFIX = "$[{"; - public static final String SUFFIX = "}]"; - private static final String JSON_SYMBOL = "\":"; - - private static final HashSet IGNORE_KEYS = new HashSet<>(Arrays.asList("id", "password", "passwd")); - - private static Map langCache4Thread = Collections.synchronizedMap(new PassiveExpiringMap(1, TimeUnit.MINUTES)); - - public static String getLangDes() { - return getLang().getDesc(); + @Resource + public void setMessageSource(MessageSource messageSource) { + Translator.messageSource = messageSource; } - public static Lang getLang() { - HttpServletRequest request = getRequest(); - return getLang(request); - } - - public static Object gets(Object keys) { - return gets(getLang(), keys); - } - - public static Object gets(Lang lang, Object keys) { - Map context = I18nManager.getI18nMap().get(lang.getDesc().toLowerCase()); - return translateObject(keys, context); - } - - // 单Key翻译 + /** + * 单Key翻译 + */ public static String get(String key) { - return get(getLang(), key); - } - - public static String get(Lang lang, String key) { - if (StringUtils.isBlank(key)) { - return StringUtils.EMPTY; - } - return translateKey(key, I18nManager.getI18nMap().get(lang.getDesc().toLowerCase())); - } - - public static String toI18nKey(String key) { - return String.format("%s%s%s", PREFIX, key, SUFFIX); - } - - private static HttpServletRequest getRequest() { - try { - return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); - } catch (NullPointerException npe) { - return null; - } - } - - private static Lang getLang(HttpServletRequest request) { - String preferLang = Lang.zh_CN.getDesc(); - - try { - if (request != null) { - Object sessionLang = request.getSession(true).getAttribute(I18nConstants.LANG_COOKIE_NAME); - if (sessionLang != null && StringUtils.isNotBlank(sessionLang.toString())) { - return Lang.getLang(sessionLang.toString()); - } - preferLang = getSystemParameterLanguage(preferLang); - if (StringUtils.isNotBlank(request.getHeader(HttpHeaders.ACCEPT_LANGUAGE))) { - String preferLangWithComma = StringUtils.substringBefore(request.getHeader(HttpHeaders.ACCEPT_LANGUAGE), ";"); - String acceptLanguage = StringUtils.replace(StringUtils.substringBefore(preferLangWithComma, ","), "-", "_"); - if (Lang.getLangWithoutDefault(acceptLanguage) != null) { - preferLang = acceptLanguage; - } - } - if (request.getCookies() != null && request.getCookies().length > 0) { - for (Cookie cookie : request.getCookies()) { - if (StringUtils.equalsIgnoreCase(cookie.getName(), I18nConstants.LANG_COOKIE_NAME)) { - preferLang = cookie.getValue(); - } - } - } - if (SessionUtils.getUser() != null && StringUtils.isNotBlank(SessionUtils.getUser().getLanguage())) { - preferLang = SessionUtils.getUser().getLanguage(); - } - request.getSession(true).setAttribute(I18nConstants.LANG_COOKIE_NAME, preferLang); - } else { - preferLang = getSystemParameterLanguage(preferLang); - } - } catch (Exception e) { - LogUtil.error("Fail to getLang.", e); - } - - return Lang.getLang(preferLang); - } - - private static String getSystemParameterLanguage(String defaultLang) { - String result = defaultLang; - try { - String cachedLang = langCache4Thread.get(I18nConstants.LANG_COOKIE_NAME); - if (StringUtils.isNotBlank(cachedLang)) { - return cachedLang; - } - String systemLanguage = Objects.requireNonNull(CommonBeanFactory.getBean(SystemParameterService.class)).getSystemLanguage(); - if (StringUtils.isNotBlank(systemLanguage)) { - result = systemLanguage; - } - langCache4Thread.put(I18nConstants.LANG_COOKIE_NAME, result); - } catch (Exception e) { - LogUtil.error(e); - } - return result; - - } - - private static Object translateObject(Object javaObject, final Map context) { - if (MapUtils.isEmpty(context)) { - return javaObject; - } - if (javaObject == null) { - return null; - } - - try { - if (javaObject instanceof String) { - String rawString = javaObject.toString(); - if (StringUtils.contains(rawString, JSON_SYMBOL)) { - try { - Object jsonObject = JSON.parse(rawString); - Object a = translateObject(jsonObject, context); - return JSON.toJSONString(a); - } catch (Exception e) { - LogUtil.warn("Failed to translate object " + rawString + ". Error: " + ExceptionUtils.getStackTrace(e)); - return translateRawString(null, rawString, context); - } - - } else { - return translateRawString(null, rawString, context); - } - } - - if (javaObject instanceof Map) { - Map map = (Map) javaObject; - - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() != null) { - if (entry.getValue() instanceof String) { - if (StringUtils.contains(entry.getValue().toString(), JSON_SYMBOL)) { - map.put(entry.getKey(), translateObject(entry.getValue(), context)); - } else { - map.put(entry.getKey(), translateRawString(entry.getKey().toString(), entry.getValue().toString(), context)); - } - } else { - translateObject(entry.getValue(), context); - } - } - } - - } - - if (javaObject instanceof Collection) { - Collection collection = (Collection) javaObject; - for (Object item : collection) { - translateObject(item, context); - } - } - - if (javaObject.getClass().isArray()) { - for (int i = 0; i < Array.getLength(javaObject); ++i) { - Object item = Array.get(javaObject, i); - Array.set(javaObject, i, translateObject(item, context)); - } - } - - ObjectSerializer serializer = SerializeConfig.globalInstance.getObjectWriter(javaObject.getClass()); - if (serializer instanceof JavaBeanSerializer) { - JavaBeanSerializer javaBeanSerializer = (JavaBeanSerializer) serializer; - - try { - Map values = javaBeanSerializer.getFieldValuesMap(javaObject); - for (Map.Entry entry : values.entrySet()) { - if (entry.getValue() != null) { - if (entry.getValue() instanceof String) { - if (StringUtils.contains(entry.getValue().toString(), JSON_SYMBOL)) { - BeanUtils.setFieldValueByName(javaObject, entry.getKey(), translateObject(entry.getValue(), context), String.class); - } else { - BeanUtils.setFieldValueByName(javaObject, entry.getKey(), translateRawString(entry.getKey(), entry.getValue().toString(), context), String.class); - } - } else { - translateObject(entry.getValue(), context); - } - } - } - } catch (Exception e) { - MSException.throwException(e); - } - } - - return javaObject; - } catch (StackOverflowError stackOverflowError) { - try { - return JSON.parseObject(translateRawString(null, JSON.toJSONString(javaObject), context).toString(), javaObject.getClass()); - } catch (Exception e) { - LogUtil.error("Failed to translate object " + javaObject.toString(), e); - return javaObject; - } - } - } - - private static Object translateRawString(String key, String rawString, Map context) { - if (StringUtils.isBlank(rawString)) { - return rawString; - } - for (String ignoreKey : IGNORE_KEYS) { - if (StringUtils.containsIgnoreCase(key, ignoreKey)) { - return rawString; - } - } - if (StringUtils.contains(rawString, PREFIX)) { - rawString = new StringSubstitutor(context, PREFIX, SUFFIX).replace(rawString); - if (StringUtils.contains(rawString, PREFIX)) { - String[] unTrans = StringUtils.substringsBetween(rawString, PREFIX, SUFFIX); - if (unTrans != null) { - for (String unTran : unTrans) { - rawString = StringUtils.replace(rawString, PREFIX + unTran + SUFFIX, unTran); - } - } - } - } - if (key != null) { - String desc = context.get(rawString); - if (StringUtils.isNotBlank(desc)) { - return desc; - } - } - return rawString; - } - - private static String translateKey(String key, Map context) { - if (MapUtils.isEmpty(context)) { - return key; - } - String desc = context.get(StringUtils.replace(StringUtils.replace(key, PREFIX, StringUtils.EMPTY), SUFFIX, StringUtils.EMPTY)); - if (StringUtils.isNotBlank(desc)) { - return desc; - } - return key; + return messageSource.getMessage(key, null, "Not Support Key", LocaleContextHolder.getLocale()); } } diff --git a/backend/src/main/java/io/metersphere/report/JtlResolver.java b/backend/src/main/java/io/metersphere/report/JtlResolver.java index f518c37b38..b54741bee1 100644 --- a/backend/src/main/java/io/metersphere/report/JtlResolver.java +++ b/backend/src/main/java/io/metersphere/report/JtlResolver.java @@ -3,10 +3,21 @@ package io.metersphere.report; import com.opencsv.bean.CsvToBean; import com.opencsv.bean.CsvToBeanBuilder; import com.opencsv.bean.HeaderColumnNameMappingStrategy; +import io.metersphere.base.domain.LoadTestReportWithBLOBs; import io.metersphere.report.base.*; import io.metersphere.report.dto.ErrorsTop5DTO; import io.metersphere.report.dto.RequestStatisticsDTO; import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.report.core.Sample; +import org.apache.jmeter.report.core.SampleMetadata; +import org.apache.jmeter.report.dashboard.JsonizerVisitor; +import org.apache.jmeter.report.processor.*; +import org.apache.jmeter.report.processor.graph.AbstractOverTimeGraphConsumer; +import org.apache.jmeter.report.processor.graph.impl.ActiveThreadsGraphConsumer; +import org.apache.jmeter.report.processor.graph.impl.HitsPerSecondGraphConsumer; +import org.apache.jmeter.report.processor.graph.impl.ResponseTimeOverTimeGraphConsumer; +import org.apache.jmeter.util.JMeterUtils; + import java.io.Reader; import java.io.StringReader; import java.math.BigDecimal; @@ -332,99 +343,116 @@ public class JtlResolver { return testOverview; } - public static List getLoadChartData(String jtlString) { - List chartsDataList = new ArrayList<>(); - List totalMetricList = JtlResolver.resolver(jtlString); - - if (totalMetricList != null) { - for (Metric metric : totalMetricList) { - metric.setTimestamp(stampToDate(DATE_TIME_PATTERN, metric.getTimestamp())); - } - } - Map> collect = Objects.requireNonNull(totalMetricList).stream().collect(Collectors.groupingBy(Metric::getTimestamp)); - List>> entries = new ArrayList<>(collect.entrySet()); - - for (Map.Entry> entry : entries) { - int failSize = 0; - List metrics = entry.getValue(); - Map> metricsMap = metrics.stream().collect(Collectors.groupingBy(Metric::getThreadName)); - int maxUsers = metricsMap.size(); - for (Metric metric : metrics) { - String isSuccess = metric.getSuccess(); - if (!"true".equals(isSuccess)) { - failSize++; - } - } - - String timeStamp = ""; - try { - timeStamp = formatDate(entry.getKey()); - } catch (ParseException e) { - e.printStackTrace(); - } - - ChartsData chartsData = new ChartsData(); - chartsData.setxAxis(timeStamp); - chartsData.setGroupName("hits"); - chartsData.setyAxis(new BigDecimal(metrics.size() * 1.0 / maxUsers)); - chartsDataList.add(chartsData); - - chartsData = new ChartsData(); - chartsData.setxAxis(timeStamp); - chartsData.setGroupName("users"); - chartsData.setyAxis(new BigDecimal(maxUsers)); - chartsDataList.add(chartsData); - - chartsData = new ChartsData(); - chartsData.setxAxis(timeStamp); - chartsData.setGroupName("errors"); - chartsData.setyAxis(new BigDecimal(failSize)); - chartsDataList.add(chartsData); - - } - - return chartsDataList; + Map activeThreadMap = getResultDataMap(jtlString, new ActiveThreadsGraphConsumer()); + Map hitsMap = getResultDataMap(jtlString, new HitsPerSecondGraphConsumer()); + List activeThreadList = new ArrayList<>(); + List hitsList = new ArrayList<>(); + mapResolver(activeThreadMap, activeThreadList, "users"); + mapResolver(hitsMap, hitsList, "hits"); + activeThreadList.addAll(hitsList); + return activeThreadList; } public static List getResponseTimeChartData(String jtlString) { - List chartsDataList = new ArrayList<>(); - List totalMetricList = JtlResolver.resolver(jtlString); + Map activeThreadMap = getResultDataMap(jtlString, new ActiveThreadsGraphConsumer()); + Map responseTimeMap = getResultDataMap(jtlString, new ResponseTimeOverTimeGraphConsumer()); + List activeThreadList = new ArrayList<>(); + List responseTimeList = new ArrayList<>(); + mapResolver(activeThreadMap, activeThreadList, "users"); + mapResolver(responseTimeMap, responseTimeList, "responseTime"); + activeThreadList.addAll(responseTimeList); + return activeThreadList; - totalMetricList.forEach(metric -> { - metric.setTimestamp(stampToDate(DATE_TIME_PATTERN, metric.getTimestamp())); - }); + } + + public static void mapResolver(Map map, List list, String seriesName) { + // ThreadGroup-1 + for (String key : map.keySet()) { + MapResultData mapResultData = (MapResultData) map.get(key); + ResultData maxY = mapResultData.getResult("maxY"); + ListResultData series = (ListResultData) mapResultData.getResult("series"); + if (series.getSize() > 0) { + for (int j = 0; j < series.getSize(); j++) { + MapResultData resultData = (MapResultData) series.get(j); + // data, isOverall, label, isController + ListResultData data = (ListResultData) resultData.getResult("data"); + ValueResultData label = (ValueResultData) resultData.getResult("label"); + + if (data.getSize() > 0) { + for (int i = 0; i < data.getSize(); i++) { + ListResultData listResultData = (ListResultData) data.get(i); + String result = listResultData.accept(new JsonizerVisitor()); + result = result.substring(1, result.length() - 1); + String[] split = result.split(","); + ChartsData chartsData = new ChartsData(); + BigDecimal bigDecimal = new BigDecimal(split[0]); + String timeStamp = bigDecimal.toPlainString(); + String time = null; + try { + time = formatDate(stampToDate(DATE_TIME_PATTERN, timeStamp)); + } catch (ParseException e) { + e.printStackTrace(); + } + chartsData.setxAxis(time); + chartsData.setyAxis(new BigDecimal(split[1].trim())); + if (series.getSize() == 1) { + chartsData.setGroupName(seriesName); + } else { + chartsData.setGroupName((String) label.getValue()); + } + list.add(chartsData); + } + } + } - Map> metricMap = totalMetricList.stream().collect(Collectors.groupingBy(Metric::getTimestamp)); - List>> entries = new ArrayList<>(metricMap.entrySet()); - for (Map.Entry> entry : entries) { - List metricList = entry.getValue(); - Map> metricsMap = metricList.stream().collect(Collectors.groupingBy(Metric::getThreadName)); - int maxUsers = metricsMap.size(); - int sumElapsedTime = metricList.stream().mapToInt(metric -> Integer.parseInt(metric.getElapsed())).sum(); - String timeStamp = ""; - try { - timeStamp = formatDate(entry.getKey()); - } catch (ParseException e) { - e.printStackTrace(); } - - ChartsData chartsData = new ChartsData(); - chartsData.setxAxis(timeStamp); - chartsData.setGroupName("users"); - chartsData.setyAxis(new BigDecimal(maxUsers)); - chartsDataList.add(chartsData); - - ChartsData chartsData2 = new ChartsData(); - chartsData2.setxAxis(timeStamp); - chartsData2.setGroupName("responseTime"); - chartsData2.setyAxis(new BigDecimal(sumElapsedTime * 1.0 / metricList.size())); - chartsDataList.add(chartsData2); - } + } - return chartsDataList; + public static Map getResultDataMap(String jtlString, AbstractOverTimeGraphConsumer timeGraphConsumer) { + int row = 0; + AbstractOverTimeGraphConsumer abstractOverTimeGraphConsumer = timeGraphConsumer; + abstractOverTimeGraphConsumer.setGranularity(60000); + // 这个路径不存在 + JMeterUtils.loadJMeterProperties("jmeter.properties"); + SampleMetadata sampleMetaData = createTestMetaData(); + SampleContext sampleContext = new SampleContext(); + abstractOverTimeGraphConsumer.setSampleContext(sampleContext); + abstractOverTimeGraphConsumer.initialize(); + abstractOverTimeGraphConsumer.startConsuming(); + StringTokenizer tokenizer = new StringTokenizer(jtlString, "\n"); + // 去掉第一行 + tokenizer.nextToken(); + while (tokenizer.hasMoreTokens()) { + String line = tokenizer.nextToken(); + String[] data = line.split(",", -1); + Sample sample = new Sample(row++, sampleMetaData, data); + abstractOverTimeGraphConsumer.consume(sample, 0); + } + abstractOverTimeGraphConsumer.stopConsuming(); + return sampleContext.getData(); + } + + // Create a static SampleMetadataObject + private static SampleMetadata createTestMetaData() { + String columnsString = "timeStamp,elapsed,label,responseCode,responseMessage,threadName,success,failureMessage,bytes,sentBytes,grpThreads,allThreads,URL,Latency,IdleTime,Connect"; + columnsString = "timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,sentBytes,grpThreads,allThreads,URL,Latency,IdleTime,Connect"; + + String[] columns = new String[17]; + int lastComa = 0; + int columnIndex = 0; + for (int i = 0; i < columnsString.length(); i++) { + if (columnsString.charAt(i) == ',') { + columns[columnIndex] = columnsString.substring(lastComa, i); + lastComa = i + 1; + columnIndex++; + } else if (i + 1 == columnsString.length()) { + columns[columnIndex] = columnsString.substring(lastComa, i + 1); + } + } + return new SampleMetadata(',', columns); } public static ReportTimeInfo getReportTimeInfo(String jtlString) { @@ -434,7 +462,7 @@ public class JtlResolver { totalLineList.sort(Comparator.comparing(t0 -> Long.valueOf(t0.getTimestamp()))); String startTimeStamp = totalLineList.get(0).getTimestamp(); - String endTimeStamp = totalLineList.get(totalLineList.size()-1).getTimestamp(); + String endTimeStamp = totalLineList.get(totalLineList.size() - 1).getTimestamp(); DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); String startTime = dtf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(startTimeStamp)), ZoneId.systemDefault())); @@ -445,7 +473,7 @@ public class JtlResolver { Date startDate = new Date(Long.parseLong(startTimeStamp)); Date endDate = new Date(Long.parseLong(endTimeStamp)); long timestamp = endDate.getTime() - startDate.getTime(); - reportTimeInfo.setDuration(String.valueOf(timestamp*1.0 / 1000 / 60)); + reportTimeInfo.setDuration(String.valueOf(timestamp * 1.0 / 1000 / 60)); // todo 时间问题 long seconds = Duration.between(Instant.ofEpochMilli(Long.parseLong(startTimeStamp)), Instant.ofEpochMilli(Long.parseLong(endTimeStamp))).getSeconds(); diff --git a/backend/src/main/java/io/metersphere/service/PerformanceTestService.java b/backend/src/main/java/io/metersphere/service/PerformanceTestService.java index 37b22f71be..850b1eafcf 100644 --- a/backend/src/main/java/io/metersphere/service/PerformanceTestService.java +++ b/backend/src/main/java/io/metersphere/service/PerformanceTestService.java @@ -31,7 +31,7 @@ import java.util.stream.Collectors; @Service @Transactional(rollbackFor = Exception.class) public class PerformanceTestService { - private static final String HEADERS = "timestamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,sentBytes,grpThreads,allThreads,URL,Latency,IdleTime,Connect"; + public static final String HEADERS = "timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,sentBytes,grpThreads,allThreads,URL,Latency,IdleTime,Connect"; @Resource private LoadTestMapper loadTestMapper; diff --git a/backend/src/main/java/io/metersphere/service/TestCaseService.java b/backend/src/main/java/io/metersphere/service/TestCaseService.java index dc167c2a55..1edab7a634 100644 --- a/backend/src/main/java/io/metersphere/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/service/TestCaseService.java @@ -224,7 +224,7 @@ public class TestCaseService { } private List generateExportTemplate() { - List list = new ArrayList(); + List list = new ArrayList<>(); StringBuilder path = new StringBuilder(""); List types = Arrays.asList("functional", "performance", "api"); List methods = Arrays.asList("manual", "auto"); diff --git a/backend/src/main/java/io/metersphere/service/TestPlanTestCaseService.java b/backend/src/main/java/io/metersphere/service/TestPlanTestCaseService.java index 25edba720a..1cf6e470f9 100644 --- a/backend/src/main/java/io/metersphere/service/TestPlanTestCaseService.java +++ b/backend/src/main/java/io/metersphere/service/TestPlanTestCaseService.java @@ -1,10 +1,15 @@ package io.metersphere.service; import io.metersphere.base.domain.TestPlanTestCase; +import io.metersphere.base.domain.TestPlanTestCaseExample; import io.metersphere.base.mapper.TestPlanTestCaseMapper; import io.metersphere.base.mapper.ext.ExtTestCaseMapper; +import io.metersphere.commons.utils.BeanUtils; import io.metersphere.controller.request.testcase.QueryTestCaseRequest; +import io.metersphere.controller.request.testcase.TestCaseBatchRequest; +import io.metersphere.controller.request.testplancase.QueryTestPlanCaseRequest; import io.metersphere.dto.TestPlanCaseDTO; +import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -21,11 +26,10 @@ public class TestPlanTestCaseService { @Resource ExtTestCaseMapper extTestCaseMapper; - public List getTestPlanCases(QueryTestCaseRequest request) { + public List getTestPlanCases(QueryTestPlanCaseRequest request) { return extTestCaseMapper.getTestPlanTestCases(request); } - public void editTestCase(TestPlanTestCase testPlanTestCase) { testPlanTestCase.setUpdateTime(System.currentTimeMillis()); testPlanTestCaseMapper.updateByPrimaryKeySelective(testPlanTestCase); @@ -34,4 +38,15 @@ public class TestPlanTestCaseService { public int deleteTestCase(Integer id) { return testPlanTestCaseMapper.deleteByPrimaryKey(id); } + + public void editTestCaseBath(TestCaseBatchRequest request) { + TestPlanTestCaseExample testPlanTestCaseExample = new TestPlanTestCaseExample(); + testPlanTestCaseExample.createCriteria().andIdIn(request.getIds()); + + TestPlanTestCase testPlanTestCase = new TestPlanTestCase(); + BeanUtils.copyBean(testPlanTestCase, request); + testPlanTestCaseMapper.updateByExampleSelective( + testPlanTestCase, + testPlanTestCaseExample); + } } diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 9310563392..728e362036 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -34,4 +34,6 @@ spring.flyway.locations=classpath:db/migration spring.flyway.table=metersphere_version spring.flyway.baseline-version=0 spring.flyway.encoding=UTF-8 -spring.flyway.validate-on-migrate=false \ No newline at end of file +spring.flyway.validate-on-migrate=false + +spring.messages.basename=i18n/messages \ No newline at end of file diff --git a/backend/src/main/resources/i18n/en-US.json b/backend/src/main/resources/i18n/en-US.json deleted file mode 100644 index c5329f867f..0000000000 --- a/backend/src/main/resources/i18n/en-US.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "error_lang_invalid": "Invalid language parameter", - "load_test_already_exists": "Duplicate load test name", - "project_name_is_null": "Project name cannot be null", - "project_name_already_exists": "The project name already exists", - "workspace_name_is_null": "Workspace name cannot be null", - "workspace_name_already_exists": "The workspace name already exists", - "workspace_does_not_belong_to_user": "The current workspace does not belong to the current user", - "organization_does_not_belong_to_user": "The current organization does not belong to the current user", - "file_cannot_be_null": "File cannot be empty!", - "edit_load_test_not_found": "Cannot edit test, test not found:", - "run_load_test_not_found": "Cannot run test, test not found:", - "run_load_test_file_not_found": "Unable to run test, unable to get test file meta information, test ID:", - "run_load_test_file_content_not_found": "Cannot run test, cannot get test file content, test ID:", - "run_load_test_file_init_error": "Failed to run test, failed to initialize run environment, test ID:", - "load_test_is_running": "Load test is running, please wait.", - "node_deep_limit": "The node depth does not exceed 5 layers!", - "no_nodes_message": "No node message", - "duplicate_node_ip": "Duplicate IPs", - "only_one_k8s": "Only one K8s can be added", - "organization_id_is_null": "Organization ID cannot be null", - "max_thread_insufficient": "The number of concurrent users exceeds" -} \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties new file mode 100644 index 0000000000..634a2c073f --- /dev/null +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -0,0 +1,21 @@ +error_lang_invalid=Invalid language parameter +load_test_already_exists=Duplicate load test name +project_name_is_null=Project name cannot be null +project_name_already_exists=The project name already exists +workspace_name_is_null=Workspace name cannot be null +workspace_name_already_exists=The workspace name already exists +workspace_does_not_belong_to_user=The current workspace does not belong to the current user +organization_does_not_belong_to_user=The current organization does not belong to the current user +file_cannot_be_null=File cannot be empty! +edit_load_test_not_found=Cannot edit test, test not found= +run_load_test_not_found=Cannot run test, test not found= +run_load_test_file_not_found=Unable to run test, unable to get test file meta information, test ID= +run_load_test_file_content_not_found=Cannot run test, cannot get test file content, test ID= +run_load_test_file_init_error=Failed to run test, failed to initialize run environment, test ID= +load_test_is_running=Load test is running, please wait. +node_deep_limit=The node depth does not exceed 5 layers! +no_nodes_message=No node message +duplicate_node_ip=Duplicate IPs +only_one_k8s=Only one K8s can be added +organization_id_is_null=Organization ID cannot be null +max_thread_insufficient=The number of concurrent users exceeds diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties new file mode 100644 index 0000000000..6a6daf57a4 --- /dev/null +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -0,0 +1,21 @@ +error_lang_invalid=\u8BED\u8A00\u53C2\u6570\u9519\u8BEF +load_test_already_exists=\u6D4B\u8BD5\u540D\u79F0\u4E0D\u80FD\u91CD\u590D +project_name_is_null=\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +project_name_already_exists=\u9879\u76EE\u540D\u79F0\u5DF2\u5B58\u5728 +workspace_name_is_null=\u5DE5\u4F5C\u7A7A\u95F4\u540D\u4E0D\u80FD\u4E3A\u7A7A +workspace_name_already_exists=\u5DE5\u4F5C\u7A7A\u95F4\u540D\u5DF2\u5B58\u5728 +workspace_does_not_belong_to_user=\u5F53\u524D\u5DE5\u4F5C\u7A7A\u95F4\u4E0D\u5C5E\u4E8E\u5F53\u524D\u7528\u6237 +organization_does_not_belong_to_user=\u5F53\u524D\u7EC4\u7EC7\u4E0D\u5C5E\u4E8E\u5F53\u524D\u7528\u6237 +file_cannot_be_null=\u6587\u4EF6\u4E0D\u80FD\u4E3A\u7A7A\uFF01 +edit_load_test_not_found=\u65E0\u6CD5\u7F16\u8F91\u6D4B\u8BD5\uFF0C\u672A\u627E\u5230\u6D4B\u8BD5\uFF1A +run_load_test_not_found=\u65E0\u6CD5\u8FD0\u884C\u6D4B\u8BD5\uFF0C\u672A\u627E\u5230\u6D4B\u8BD5\uFF1A +run_load_test_file_not_found=\u65E0\u6CD5\u8FD0\u884C\u6D4B\u8BD5\uFF0C\u65E0\u6CD5\u83B7\u53D6\u6D4B\u8BD5\u6587\u4EF6\u5143\u4FE1\u606F\uFF0C\u6D4B\u8BD5ID\uFF1A +run_load_test_file_content_not_found=\u65E0\u6CD5\u8FD0\u884C\u6D4B\u8BD5\uFF0C\u65E0\u6CD5\u83B7\u53D6\u6D4B\u8BD5\u6587\u4EF6\u5185\u5BB9\uFF0C\u6D4B\u8BD5ID\uFF1A +run_load_test_file_init_error=\u65E0\u6CD5\u8FD0\u884C\u6D4B\u8BD5\uFF0C\u521D\u59CB\u5316\u8FD0\u884C\u73AF\u5883\u5931\u8D25\uFF0C\u6D4B\u8BD5ID\uFF1A +load_test_is_running=\u6D4B\u8BD5\u6B63\u5728\u8FD0\u884C, \u8BF7\u7B49\u5F85 +node_deep_limit=\u8282\u70B9\u6DF1\u5EA6\u4E0D\u8D85\u8FC75\u5C42\uFF01 +no_nodes_message=\u6CA1\u6709\u8282\u70B9\u4FE1\u606F +duplicate_node_ip=\u8282\u70B9 IP \u91CD\u590D +only_one_k8s=\u53EA\u80FD\u6DFB\u52A0\u4E00\u4E2A K8s +organization_id_is_null=\u7EC4\u7EC7 ID \u4E0D\u80FD\u4E3A\u7A7A +max_thread_insufficient=\u5E76\u53D1\u7528\u6237\u6570\u8D85\u989D \ No newline at end of file diff --git a/backend/src/main/resources/i18n/zh-CN.json b/backend/src/main/resources/i18n/zh-CN.json deleted file mode 100644 index 0884b07c0f..0000000000 --- a/backend/src/main/resources/i18n/zh-CN.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "error_lang_invalid": "语言参数错误", - "load_test_already_exists": "测试名称不能重复", - "project_name_is_null": "项目名称不能为空", - "project_name_already_exists": "项目名称已存在", - "workspace_name_is_null": "工作空间名不能为空", - "workspace_name_already_exists": "工作空间名已存在", - "workspace_does_not_belong_to_user": "当前工作空间不属于当前用户", - "organization_does_not_belong_to_user": "当前组织不属于当前用户", - "file_cannot_be_null": "文件不能为空!", - "edit_load_test_not_found": "无法编辑测试,未找到测试:", - "run_load_test_not_found": "无法运行测试,未找到测试:", - "run_load_test_file_not_found": "无法运行测试,无法获取测试文件元信息,测试ID:", - "run_load_test_file_content_not_found": "无法运行测试,无法获取测试文件内容,测试ID:", - "run_load_test_file_init_error": "无法运行测试,初始化运行环境失败,测试ID:", - "load_test_is_running": "测试正在运行, 请等待", - "node_deep_limit": "节点深度不超过5层!", - "no_nodes_message": "没有节点信息", - "duplicate_node_ip": "节点 IP 重复", - "only_one_k8s": "只能添加一个 K8s", - "organization_id_is_null": "组织 ID 不能为空", - "max_thread_insufficient": "并发用户数超额" -} \ No newline at end of file diff --git a/backend/src/test/java/io/metersphere/GraphConsumerTest.java b/backend/src/test/java/io/metersphere/GraphConsumerTest.java index 11ba35593d..4243e4743c 100644 --- a/backend/src/test/java/io/metersphere/GraphConsumerTest.java +++ b/backend/src/test/java/io/metersphere/GraphConsumerTest.java @@ -5,9 +5,13 @@ import io.metersphere.base.mapper.LoadTestReportMapper; import org.apache.commons.io.FileUtils; import org.apache.jmeter.report.core.Sample; import org.apache.jmeter.report.core.SampleMetadata; +import org.apache.jmeter.report.dashboard.JsonizerVisitor; +import org.apache.jmeter.report.processor.ListResultData; +import org.apache.jmeter.report.processor.MapResultData; +import org.apache.jmeter.report.processor.ResultData; import org.apache.jmeter.report.processor.SampleContext; import org.apache.jmeter.report.processor.graph.AbstractOverTimeGraphConsumer; -import org.apache.jmeter.report.processor.graph.impl.LatencyOverTimeGraphConsumer; +import org.apache.jmeter.report.processor.graph.impl.ActiveThreadsGraphConsumer; import org.apache.jmeter.util.JMeterUtils; import org.junit.Before; import org.junit.Test; @@ -16,7 +20,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; -import java.io.File; +import java.util.Map; import java.util.StringTokenizer; @RunWith(SpringRunner.class) @@ -32,8 +36,8 @@ public class GraphConsumerTest { @Before public void init() { - timeGraphConsumer = new LatencyOverTimeGraphConsumer(); - timeGraphConsumer.setTitle("graph title"); + timeGraphConsumer = new ActiveThreadsGraphConsumer(); +// timeGraphConsumer.setTitle("graph title"); timeGraphConsumer.setGranularity(60000); JMeterUtils.loadJMeterProperties("jmeter.properties"); // 这个路径不存在 @@ -56,8 +60,9 @@ public class GraphConsumerTest { @Test public void test2() { + int row = 0; SampleContext sampleContext = new SampleContext(); - sampleContext.setWorkingDirectory(new File("/tmp/test_report/")); +// sampleContext.setWorkingDirectory(new File("/tmp/test_report/")); timeGraphConsumer.setSampleContext(sampleContext); timeGraphConsumer.initialize(); @@ -70,11 +75,33 @@ public class GraphConsumerTest { while (tokenizer.hasMoreTokens()) { String line = tokenizer.nextToken(); String[] data = line.split(",", -1); - Sample sample = new Sample(0, sampleMetaData, data); + Sample sample = new Sample(row++, sampleMetaData, data); timeGraphConsumer.consume(sample, 0); } timeGraphConsumer.stopConsuming(); - System.out.println(sampleContext.getData()); + Map map = sampleContext.getData(); + for (String key : map.keySet()) { + MapResultData mapResultData = (MapResultData) map.get(key); + ResultData maxY = mapResultData.getResult("maxY"); + ListResultData series = (ListResultData) mapResultData.getResult("series"); + if (series.getSize() > 0) { + MapResultData resultData = (MapResultData) series.get(0); + ListResultData data = (ListResultData) resultData.getResult("data"); + if (data.getSize() > 0) { + for (int i = 0; i < data.getSize(); i++) { + ListResultData resultData1 = (ListResultData) data.get(i); + String accept = resultData1.accept(new JsonizerVisitor()); + String[] split = accept.split(","); + System.out.println(resultData1); + System.out.println(accept); + } + } + + } + + } + + System.out.println("+++++++++" + sampleContext.getData()); } diff --git a/frontend/src/business/App.vue b/frontend/src/business/App.vue index 32a77c190e..aef92e802d 100644 --- a/frontend/src/business/App.vue +++ b/frontend/src/business/App.vue @@ -1,10 +1,10 @@