feat(项目报告定时任务): 项目报告定时任务可以实时查询图表

项目报告定时任务可以实时查询图表
This commit is contained in:
song-tianyang 2021-12-14 14:58:23 +08:00 committed by song-tianyang
parent 217c30be67
commit f0e6bed4d9
20 changed files with 454 additions and 48 deletions

View File

@ -440,6 +440,11 @@
<artifactId>xmindjbehaveplugin</artifactId> <artifactId>xmindjbehaveplugin</artifactId>
<version>0.8</version> <version>0.8</version>
</dependency> </dependency>
<!-- selenium包 -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
</dependency>
<!-- 基础包 --> <!-- 基础包 -->
<dependency> <dependency>
<groupId>io.metersphere</groupId> <groupId>io.metersphere</groupId>

View File

@ -7,8 +7,11 @@ import io.metersphere.api.dto.share.ShareInfoDTO;
import io.metersphere.api.service.ApiDefinitionService; import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.api.service.ShareInfoService; import io.metersphere.api.service.ShareInfoService;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs; import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ReportStatisticsWithBLOBs;
import io.metersphere.base.domain.ShareInfo; import io.metersphere.base.domain.ShareInfo;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.reportstatistics.dto.ReportStatisticsSaveRequest;
import io.metersphere.reportstatistics.service.ReportStatisticsService;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -29,6 +32,8 @@ public class ShareInfoController {
ShareInfoService shareInfoService; ShareInfoService shareInfoService;
@Resource @Resource
ApiDefinitionService apiDefinitionService; ApiDefinitionService apiDefinitionService;
@Resource
ReportStatisticsService reportStatisticsService;
@PostMapping("/selectApiSimpleInfo") @PostMapping("/selectApiSimpleInfo")
public List<ApiDocumentInfoDTO> list(@RequestBody ApiDocumentRequest request) { public List<ApiDocumentInfoDTO> list(@RequestBody ApiDocumentRequest request) {
@ -44,13 +49,13 @@ public class ShareInfoController {
@PostMapping("/selectApiInfoByParam") @PostMapping("/selectApiInfoByParam")
public List<ApiDocumentInfoDTO> selectApiInfoByParam(@RequestBody ApiDocumentRequest request) { public List<ApiDocumentInfoDTO> selectApiInfoByParam(@RequestBody ApiDocumentRequest request) {
List<ApiDocumentInfoDTO> returnList = new ArrayList<>(); List<ApiDocumentInfoDTO> returnList = new ArrayList<>();
if(request.getApiIdList() != null){ if (request.getApiIdList() != null) {
//要根据ids的顺序进行返回排序 //要根据ids的顺序进行返回排序
List<ApiDefinitionWithBLOBs> apiModels = apiDefinitionService.getBLOBs(request.getApiIdList()); List<ApiDefinitionWithBLOBs> apiModels = apiDefinitionService.getBLOBs(request.getApiIdList());
Map<String,ApiDefinitionWithBLOBs> apiModelMaps = apiModels.stream().collect(Collectors.toMap(ApiDefinitionWithBLOBs :: getId,a->a,(k1,k2)->k1)); Map<String, ApiDefinitionWithBLOBs> apiModelMaps = apiModels.stream().collect(Collectors.toMap(ApiDefinitionWithBLOBs::getId, a -> a, (k1, k2) -> k1));
for(String id : request.getApiIdList()){ for (String id : request.getApiIdList()) {
ApiDefinitionWithBLOBs model = apiModelMaps.get(id); ApiDefinitionWithBLOBs model = apiModelMaps.get(id);
if(model == null){ if (model == null) {
model = new ApiDefinitionWithBLOBs(); model = new ApiDefinitionWithBLOBs();
model.setId(id); model.setId(id);
model.setName(id); model.setName(id);
@ -66,9 +71,9 @@ public class ShareInfoController {
public ApiDocumentInfoDTO selectApiInfoById(@PathVariable String id) { public ApiDocumentInfoDTO selectApiInfoById(@PathVariable String id) {
ApiDefinitionWithBLOBs apiModel = apiDefinitionService.getBLOBs(id); ApiDefinitionWithBLOBs apiModel = apiDefinitionService.getBLOBs(id);
ApiDocumentInfoDTO returnDTO = new ApiDocumentInfoDTO(); ApiDocumentInfoDTO returnDTO = new ApiDocumentInfoDTO();
try{ try {
returnDTO = shareInfoService.conversionModelToDTO(apiModel); returnDTO = shareInfoService.conversionModelToDTO(apiModel);
}catch (Exception e){ } catch (Exception e) {
LogUtil.error(e); LogUtil.error(e);
} }
returnDTO.setSelectedFlag(true); returnDTO.setSelectedFlag(true);
@ -88,4 +93,9 @@ public class ShareInfoController {
ShareInfoDTO returnDTO = shareInfoService.conversionShareInfoToDTO(apiShare); ShareInfoDTO returnDTO = shareInfoService.conversionShareInfoToDTO(apiShare);
return returnDTO; return returnDTO;
} }
@PostMapping("/selectHistoryReportById")
public ReportStatisticsWithBLOBs selectById(@RequestBody ReportStatisticsSaveRequest request) {
return reportStatisticsService.selectById(request.getId());
}
} }

View File

@ -176,4 +176,5 @@ public class PermissionConstants {
public static final String PROJECT_ENTERPRISE_REPORT_EDIT = "PROJECT_ENTERPRISE_REPORT:READ+EDIT"; public static final String PROJECT_ENTERPRISE_REPORT_EDIT = "PROJECT_ENTERPRISE_REPORT:READ+EDIT";
public static final String PROJECT_ENTERPRISE_REPORT_DELETE = "PROJECT_ENTERPRISE_REPORT:READ+DELETE"; public static final String PROJECT_ENTERPRISE_REPORT_DELETE = "PROJECT_ENTERPRISE_REPORT:READ+DELETE";
public static final String PROJECT_ENTERPRISE_REPORT_COPY = "PROJECT_ENTERPRISE_REPORT:READ+COPY"; public static final String PROJECT_ENTERPRISE_REPORT_COPY = "PROJECT_ENTERPRISE_REPORT:READ+COPY";
public static final String PROJECT_ENTERPRISE_REPORT_SCHEDULE = "PROJECT_ENTERPRISE_REPORT:READ+SCHEDULE";
} }

View File

@ -1,5 +1,6 @@
package io.metersphere.commons.constants; package io.metersphere.commons.constants;
public enum ScheduleGroup { public enum ScheduleGroup {
API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST, TEST_PLAN_TEST, SWAGGER_IMPORT, ISSUE_SYNC API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST, TEST_PLAN_TEST, SWAGGER_IMPORT, ISSUE_SYNC,
SCHEDULE_SEND_REPORT
} }

View File

@ -13,7 +13,7 @@ import java.util.Map;
public class ShiroUtils { public class ShiroUtils {
public static void loadBaseFilterChain(Map<String, String> filterChainDefinitionMap){ public static void loadBaseFilterChain(Map<String, String> filterChainDefinitionMap) {
filterChainDefinitionMap.put("/resource/**", "anon"); filterChainDefinitionMap.put("/resource/**", "anon");
filterChainDefinitionMap.put("/*.worker.js", "anon"); filterChainDefinitionMap.put("/*.worker.js", "anon");
@ -53,9 +53,11 @@ public class ShiroUtils {
//分享相关接口 //分享相关接口
filterChainDefinitionMap.put("/share/info/generateShareInfoWithExpired", "anon"); filterChainDefinitionMap.put("/share/info/generateShareInfoWithExpired", "anon");
filterChainDefinitionMap.put("/share/info/selectApiInfoByParam", "anon"); filterChainDefinitionMap.put("/share/info/selectApiInfoByParam", "anon");
filterChainDefinitionMap.put("/share/info/selectHistoryReportById", "anon");
filterChainDefinitionMap.put("/share/get/**", "anon"); filterChainDefinitionMap.put("/share/get/**", "anon");
filterChainDefinitionMap.put("/share/info", "apikey, csrf, authc"); // 需要认证 filterChainDefinitionMap.put("/share/info", "apikey, csrf, authc"); // 需要认证
filterChainDefinitionMap.put("/document/**", "anon"); filterChainDefinitionMap.put("/document/**", "anon");
filterChainDefinitionMap.put("/echartPic/**", "anon");
filterChainDefinitionMap.put("/share/**", "anon"); filterChainDefinitionMap.put("/share/**", "anon");
filterChainDefinitionMap.put("/sharePlanReport", "anon"); filterChainDefinitionMap.put("/sharePlanReport", "anon");
@ -82,14 +84,14 @@ public class ShiroUtils {
filterChainDefinitionMap.put("/resource/md/get/**", "apikey, authc"); filterChainDefinitionMap.put("/resource/md/get/**", "apikey, authc");
} }
public static Cookie getSessionIdCookie(){ public static Cookie getSessionIdCookie() {
SimpleCookie sessionIdCookie = new SimpleCookie(); SimpleCookie sessionIdCookie = new SimpleCookie();
sessionIdCookie.setPath("/"); sessionIdCookie.setPath("/");
sessionIdCookie.setName("MS_SESSION_ID"); sessionIdCookie.setName("MS_SESSION_ID");
return sessionIdCookie; return sessionIdCookie;
} }
public static SessionManager getSessionManager(Long sessionTimeout, CacheManager cacheManager){ public static SessionManager getSessionManager(Long sessionTimeout, CacheManager cacheManager) {
DefaultWebSessionManager sessionManager = new CustomSessionManager(); DefaultWebSessionManager sessionManager = new CustomSessionManager();
sessionManager.setSessionIdUrlRewritingEnabled(false); sessionManager.setSessionIdUrlRewritingEnabled(false);
sessionManager.setDeleteInvalidSessions(true); sessionManager.setDeleteInvalidSessions(true);

View File

@ -32,8 +32,13 @@ public class IndexController {
return "document.html"; return "document.html";
} }
@GetMapping(value = "/echartPic")
public String echartPic() {
return "share-enterprise-report.html";
}
@GetMapping(value = "/sharePlanReport") @GetMapping(value = "/sharePlanReport")
public String shareRedirect() { public String shareRedirect() {
return "share-plan-report.html"; return "share-plan-report.html";
} }
} }

View File

@ -0,0 +1,11 @@
package io.metersphere.reportstatistics.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class HeadlessRequest {
public String url;
public String driverPath;
}

View File

@ -6,16 +6,20 @@ import io.metersphere.base.domain.ReportStatistics;
import io.metersphere.base.domain.ReportStatisticsExample; import io.metersphere.base.domain.ReportStatisticsExample;
import io.metersphere.base.domain.ReportStatisticsWithBLOBs; import io.metersphere.base.domain.ReportStatisticsWithBLOBs;
import io.metersphere.base.mapper.ReportStatisticsMapper; import io.metersphere.base.mapper.ReportStatisticsMapper;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.reportstatistics.dto.ReportStatisticsSaveRequest; import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.reportstatistics.dto.ReportStatisticsType; import io.metersphere.reportstatistics.dto.*;
import io.metersphere.reportstatistics.dto.TestCaseCountTableDTO; import io.metersphere.reportstatistics.dto.charts.Series;
import io.metersphere.reportstatistics.dto.table.TestCaseCountTableDataDTO; import io.metersphere.reportstatistics.dto.table.TestCaseCountTableDataDTO;
import io.metersphere.reportstatistics.dto.table.TestCaseCountTableItemDataDTO; import io.metersphere.reportstatistics.dto.table.TestCaseCountTableItemDataDTO;
import io.metersphere.reportstatistics.dto.table.TestCaseCountTableRowDTO; import io.metersphere.reportstatistics.dto.table.TestCaseCountTableRowDTO;
import io.metersphere.reportstatistics.utils.ChromeUtils;
import io.metersphere.service.SystemParameterService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList; import java.util.ArrayList;
@ -31,19 +35,21 @@ import java.util.UUID;
public class ReportStatisticsService { public class ReportStatisticsService {
@Resource @Resource
private ReportStatisticsMapper reportStatisticsMapper; private ReportStatisticsMapper reportStatisticsMapper;
@Resource
private TestCaseCountService testCaseCountService;
public ReportStatisticsWithBLOBs saveByRequest(ReportStatisticsSaveRequest request) { public ReportStatisticsWithBLOBs saveByRequest(ReportStatisticsSaveRequest request) {
ReportStatisticsWithBLOBs model = new ReportStatisticsWithBLOBs(); ReportStatisticsWithBLOBs model = new ReportStatisticsWithBLOBs();
model.setId(UUID.randomUUID().toString()); model.setId(UUID.randomUUID().toString());
String name = "用例分析报表"; String name = "用例分析报表";
if(StringUtils.equalsIgnoreCase(ReportStatisticsType.TEST_CASE_COUNT.name(),request.getReportType())){ if (StringUtils.equalsIgnoreCase(ReportStatisticsType.TEST_CASE_COUNT.name(), request.getReportType())) {
name = "用例统计报表"; name = "用例统计报表";
model.setReportType(ReportStatisticsType.TEST_CASE_COUNT.name()); model.setReportType(ReportStatisticsType.TEST_CASE_COUNT.name());
}else { } else {
model.setReportType(ReportStatisticsType.TEST_CASE_ANALYSIS.name()); model.setReportType(ReportStatisticsType.TEST_CASE_ANALYSIS.name());
} }
if(StringUtils.isEmpty(request.getName())){ if (StringUtils.isEmpty(request.getName())) {
request.setName(name); request.setName(name);
} }
model.setName(request.getName()); model.setName(request.getName());
@ -66,23 +72,81 @@ public class ReportStatisticsService {
} }
public ReportStatisticsWithBLOBs selectById(String id) { public ReportStatisticsWithBLOBs selectById(String id) {
ReportStatisticsWithBLOBs blob = reportStatisticsMapper.selectByPrimaryKey(id); ReportStatisticsWithBLOBs blob = reportStatisticsMapper.selectByPrimaryKey(id);
JSONObject dataOption = JSONObject.parseObject(blob.getDataOption());
JSONObject selectOption = JSONObject.parseObject(blob.getSelectOption()); JSONObject selectOption = JSONObject.parseObject(blob.getSelectOption());
if(!dataOption.containsKey("showTable")){ JSONObject dataOption = JSONObject.parseObject(blob.getDataOption());
List<TestCaseCountTableDTO> dtos = JSONArray.parseArray(dataOption.getString("tableData"),TestCaseCountTableDTO.class); boolean isReportNeedUpdate = this.isReportNeedUpdate(blob);
TestCaseCountTableDataDTO showTable = this.countShowTable(selectOption.getString("xaxis"), JSONArray.parseArray(selectOption.getString("yaxis"),String.class),dtos); if (isReportNeedUpdate) {
dataOption.put("showTable",showTable); TestCaseCountRequest countRequest = JSONObject.parseObject(blob.getSelectOption(), TestCaseCountRequest.class);
JSONObject returnDataOption = this.reloadDatas(countRequest, dataOption.getString("chartType"));
if (returnDataOption != null) {
dataOption = returnDataOption;
}
}
if (!dataOption.containsKey("showTable")) {
List<TestCaseCountTableDTO> dtos = JSONArray.parseArray(dataOption.getString("tableData"), TestCaseCountTableDTO.class);
TestCaseCountTableDataDTO showTable = this.countShowTable(selectOption.getString("xaxis"), JSONArray.parseArray(selectOption.getString("yaxis"), String.class), dtos);
dataOption.put("showTable", showTable);
blob.setDataOption(dataOption.toString()); blob.setDataOption(dataOption.toString());
} }
return blob; return blob;
} }
private JSONObject reloadDatas(TestCaseCountRequest request, String chartType) {
if (StringUtils.isEmpty(chartType)) {
chartType = "bar";
}
JSONObject returnObject = new JSONObject();
TestCaseCountResponse testCaseCountResponse = testCaseCountService.getReport(request);
if (testCaseCountResponse.getBarChartDTO() != null) {
JSONObject loadOptionObject = new JSONObject();
loadOptionObject.put("legend", JSONObject.toJSON(testCaseCountResponse.getBarChartDTO().getLegend()));
loadOptionObject.put("xAxis", JSONObject.toJSON(testCaseCountResponse.getBarChartDTO().getXAxis()));
loadOptionObject.put("xaxis", JSONObject.toJSON(testCaseCountResponse.getBarChartDTO().getXAxis()));
loadOptionObject.put("yAxis", JSONObject.toJSON(testCaseCountResponse.getBarChartDTO().getYAxis()));
loadOptionObject.put("tooltip", new JSONObject());
loadOptionObject.put("lable", new JSONObject());
if (!CollectionUtils.isEmpty(testCaseCountResponse.getBarChartDTO().getSeries())) {
List<Series> list = testCaseCountResponse.getBarChartDTO().getSeries();
for (Series model : list) {
model.setType(chartType);
}
loadOptionObject.put("series", JSONArray.toJSON(list));
}
loadOptionObject.put("grid", new JSONObject().put("bottom", "75px"));
returnObject.put("loadOption", loadOptionObject);
}
if (testCaseCountResponse.getPieChartDTO() != null) {
JSONObject pieOptionObject = new JSONObject();
pieOptionObject.put("title", JSONObject.toJSON(testCaseCountResponse.getPieChartDTO().getTitle()));
pieOptionObject.put("xAxis", JSONObject.toJSON(testCaseCountResponse.getPieChartDTO().getXAxis()));
if (!CollectionUtils.isEmpty(testCaseCountResponse.getPieChartDTO().getSeries())) {
List<Series> list = testCaseCountResponse.getPieChartDTO().getSeries();
for (Series model : list) {
model.setType(chartType);
}
pieOptionObject.put("series", JSONArray.toJSON(list));
}
pieOptionObject.put("grid", new JSONObject().put("bottom", "75px"));
if (testCaseCountResponse.getPieChartDTO().getWidth() > 0) {
pieOptionObject.put("width", testCaseCountResponse.getPieChartDTO().getWidth());
}
returnObject.put("pieOption", pieOptionObject);
}
if (testCaseCountResponse.getTableDTOs() != null) {
returnObject.put("tableData", JSONArray.toJSON(testCaseCountResponse.getTableDTOs()));
}
returnObject.put("chartType", chartType);
return returnObject;
}
private TestCaseCountTableDataDTO countShowTable(String groupName, List<String> yaxis, List<TestCaseCountTableDTO> dtos) { private TestCaseCountTableDataDTO countShowTable(String groupName, List<String> yaxis, List<TestCaseCountTableDTO> dtos) {
if(yaxis == null){ if (yaxis == null) {
yaxis = new ArrayList<>(); yaxis = new ArrayList<>();
} }
TestCaseCountTableDataDTO returnDTO = new TestCaseCountTableDataDTO(); TestCaseCountTableDataDTO returnDTO = new TestCaseCountTableDataDTO();
String [] headers = new String[]{groupName,"总计","testCase","apiCase","scenarioCase","loadCaseCount"}; String[] headers = new String[]{groupName, "总计", "testCase", "apiCase", "scenarioCase", "loadCaseCount"};
List<TestCaseCountTableItemDataDTO> heads = new ArrayList<>(); List<TestCaseCountTableItemDataDTO> heads = new ArrayList<>();
boolean showTestCase = true; boolean showTestCase = true;
@ -91,19 +155,19 @@ public class ReportStatisticsService {
boolean showLoad = true; boolean showLoad = true;
for (String head : headers) { for (String head : headers) {
if(StringUtils.equalsAnyIgnoreCase(head,groupName,"总计") || yaxis.contains(head)){ if (StringUtils.equalsAnyIgnoreCase(head, groupName, "总计") || yaxis.contains(head)) {
TestCaseCountTableItemDataDTO headData = new TestCaseCountTableItemDataDTO(); TestCaseCountTableItemDataDTO headData = new TestCaseCountTableItemDataDTO();
headData.setId(UUID.randomUUID().toString()); headData.setId(UUID.randomUUID().toString());
headData.setValue(head); headData.setValue(head);
heads.add(headData); heads.add(headData);
}else { } else {
if(StringUtils.equalsIgnoreCase(head,"testCase")){ if (StringUtils.equalsIgnoreCase(head, "testCase")) {
showTestCase = false; showTestCase = false;
}else if(StringUtils.equalsIgnoreCase(head,"apiCase")){ } else if (StringUtils.equalsIgnoreCase(head, "apiCase")) {
showApi = false; showApi = false;
}else if(StringUtils.equalsIgnoreCase(head,"scenarioCase")){ } else if (StringUtils.equalsIgnoreCase(head, "scenarioCase")) {
showScenario = false; showScenario = false;
}else if(StringUtils.equalsIgnoreCase(head,"loadCaseCount")){ } else if (StringUtils.equalsIgnoreCase(head, "loadCaseCount")) {
showLoad = false; showLoad = false;
} }
} }
@ -123,28 +187,28 @@ public class ReportStatisticsService {
countData.setValue(data.getAllCount()); countData.setValue(data.getAllCount());
rowDataList.add(countData); rowDataList.add(countData);
if(showTestCase){ if (showTestCase) {
TestCaseCountTableItemDataDTO itemData = new TestCaseCountTableItemDataDTO(); TestCaseCountTableItemDataDTO itemData = new TestCaseCountTableItemDataDTO();
itemData.setId(UUID.randomUUID().toString()); itemData.setId(UUID.randomUUID().toString());
itemData.setValue(data.getTestCaseCount()); itemData.setValue(data.getTestCaseCount());
rowDataList.add(itemData); rowDataList.add(itemData);
} }
if(showApi){ if (showApi) {
TestCaseCountTableItemDataDTO itemData = new TestCaseCountTableItemDataDTO(); TestCaseCountTableItemDataDTO itemData = new TestCaseCountTableItemDataDTO();
itemData.setId(UUID.randomUUID().toString()); itemData.setId(UUID.randomUUID().toString());
itemData.setValue(data.getApiCaseCount()); itemData.setValue(data.getApiCaseCount());
rowDataList.add(itemData); rowDataList.add(itemData);
} }
if(showScenario){ if (showScenario) {
TestCaseCountTableItemDataDTO itemData = new TestCaseCountTableItemDataDTO(); TestCaseCountTableItemDataDTO itemData = new TestCaseCountTableItemDataDTO();
itemData.setId(UUID.randomUUID().toString()); itemData.setId(UUID.randomUUID().toString());
itemData.setValue(data.getScenarioCaseCount()); itemData.setValue(data.getScenarioCaseCount());
rowDataList.add(itemData); rowDataList.add(itemData);
} }
if(showLoad){ if (showLoad) {
TestCaseCountTableItemDataDTO itemData = new TestCaseCountTableItemDataDTO(); TestCaseCountTableItemDataDTO itemData = new TestCaseCountTableItemDataDTO();
itemData.setId(UUID.randomUUID().toString()); itemData.setId(UUID.randomUUID().toString());
itemData.setValue(data.getLoadCaseCount()); itemData.setValue(data.getLoadCaseCount());
@ -173,16 +237,38 @@ public class ReportStatisticsService {
public List<ReportStatistics> selectByRequest(ReportStatisticsSaveRequest request) { public List<ReportStatistics> selectByRequest(ReportStatisticsSaveRequest request) {
ReportStatisticsExample example = new ReportStatisticsExample(); ReportStatisticsExample example = new ReportStatisticsExample();
ReportStatisticsExample.Criteria criteria = example.createCriteria(); ReportStatisticsExample.Criteria criteria = example.createCriteria();
if(StringUtils.isNotEmpty(request.getProjectId())){ if (StringUtils.isNotEmpty(request.getProjectId())) {
criteria.andProjectIdEqualTo(request.getProjectId()); criteria.andProjectIdEqualTo(request.getProjectId());
} }
if(StringUtils.isNotEmpty(request.getReportType())){ if (StringUtils.isNotEmpty(request.getReportType())) {
criteria.andReportTypeEqualTo(request.getReportType()); criteria.andReportTypeEqualTo(request.getReportType());
} }
if(StringUtils.isNotEmpty(request.getName())){ if (StringUtils.isNotEmpty(request.getName())) {
criteria.andNameLike(request.getName()); criteria.andNameLike(request.getName());
} }
example.setOrderByClause("create_time DESC"); example.setOrderByClause("create_time DESC");
return reportStatisticsMapper.selectByExample(example); return reportStatisticsMapper.selectByExample(example);
}
public String getImageContentById(ReportStatisticsWithBLOBs reportRecordId) {
ChromeUtils chromeUtils = new ChromeUtils();
HeadlessRequest headlessRequest = new HeadlessRequest();
BaseSystemConfigDTO baseInfo = CommonBeanFactory.getBean(SystemParameterService.class).getBaseInfo();
// 占位符
String platformUrl = "http://localhost:8081";
if (baseInfo != null) {
platformUrl = baseInfo.getUrl();
}
platformUrl += "/echartPic?shareId=" + reportRecordId.getId();
headlessRequest.setUrl(platformUrl);
String imageData = chromeUtils.getImageInfo(headlessRequest);
return imageData;
}
public boolean isReportNeedUpdate(ReportStatisticsWithBLOBs model) {
JSONObject selectOption = JSONObject.parseObject(model.getSelectOption());
return selectOption.containsKey("timeType") && StringUtils.equalsIgnoreCase("dynamicTime", selectOption.getString("timeType"));
} }
} }

View File

@ -496,6 +496,13 @@ public class TestCaseCountService {
this.add("50%"); this.add("50%");
}}); }});
Map<String,Object> labelMap = new HashMap<>();
Map<String,Object> normalMap = new HashMap<>();
normalMap.put("show",true);
normalMap.put("formatter","{b}: {c}({d}%)");
labelMap.put("normal",normalMap);
series.setLabel(labelMap);
Title title = new Title(); Title title = new Title();
title.setSubtext(summary.groupName); title.setSubtext(summary.groupName);
title.setLeft(leftPxStr); title.setLeft(leftPxStr);

View File

@ -0,0 +1,75 @@
package io.metersphere.reportstatistics.utils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.reportstatistics.dto.HeadlessRequest;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.chrome.ChromeOptions;
public class ChromeUtils {
private static final String DEFAULT_DRIVERPATH = "/Users/handsomesong/chromeDriver/chromedriver_mac64_m1/chromedriver";
private WebDriver genWebDriver(HeadlessRequest headlessRequest) {
String driverPath = headlessRequest.driverPath;
if (StringUtils.isEmpty(driverPath)) {
driverPath = DEFAULT_DRIVERPATH;
}
//初始化一个chrome浏览器实例driver
ChromeOptions options = new ChromeOptions();
options.addArguments("headless");
options.addArguments("no-sandbox");
options.addArguments("disable-gpu");
options.addArguments("disable-features=NetworkService");
options.addArguments("ignore-certificate-errors");
options.addArguments("silent-launch");
options.addArguments("disable-application-cache");
options.addArguments("disable-web-security");
options.addArguments("no-proxy-server");
options.addArguments("disable-dev-shm-usage");
options.addArguments("lang=zh_CN.UTF-8");
WebDriver driver = null;
try {
System.setProperty(ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY, driverPath);
driver = new ChromeDriver(options);
driver.get(headlessRequest.url);
driver.manage().window().fullscreen();
}catch (Exception e){
if(driver != null){
driver.quit();
driver = null;
}
LogUtil.error(e);
}
return driver;
}
public String getImageInfo(HeadlessRequest request){
WebDriver driver = this.genWebDriver(request);
String files = null;
if(driver != null){
try {
//预留echart动画的加载时间
Thread.sleep(3 * 1000);
String js = "var chartsCanvas = document.getElementById('picChart').getElementsByTagName('canvas')[0];" +
"var imageUrl = null;" +
"if (chartsCanvas!= null) {" +
" imageUrl = chartsCanvas && chartsCanvas.toDataURL('image/png');"+
"return imageUrl;" +
"}";
files = ((JavascriptExecutor)driver).executeScript(js).toString();
}catch (Exception e){
LogUtil.error(e);
}finally {
driver.quit();
}
}
if(StringUtils.isNotEmpty(files)){
return files;
}else {
return null;
}
}
}

@ -1 +1 @@
Subproject commit a41ba52f5495bc68f84a07643442d2b0e9524786 Subproject commit 9dd0111ebafcdab7153a97ab3a3af39f5c69dac5

View File

@ -873,6 +873,12 @@
"resourceId": "PROJECT_ENTERPRISE_REPORT", "resourceId": "PROJECT_ENTERPRISE_REPORT",
"license": true "license": true
}, },
{
"id": "PROJECT_ENTERPRISE_REPORT:READ+SCHEDULE",
"name": "定时发送",
"resourceId": "PROJECT_ENTERPRISE_REPORT",
"license": true
},
{ {
"id": "PROJECT_ENTERPRISE_REPORT:READ+EDIT", "id": "PROJECT_ENTERPRISE_REPORT:READ+EDIT",
"name": "修改报告", "name": "修改报告",

View File

@ -165,6 +165,9 @@ export default {
if (this.paramRow.redirectFrom == 'testPlan') { if (this.paramRow.redirectFrom == 'testPlan') {
paramTestId = this.paramRow.id; paramTestId = this.paramRow.id;
this.scheduleTaskType = "TEST_PLAN_TEST"; this.scheduleTaskType = "TEST_PLAN_TEST";
} else if (this.paramRow.redirectFrom == 'enterpriseReport') {
paramTestId = this.paramRow.id;
this.scheduleTaskType = "ENTERPRISE_REPORT";
} else { } else {
paramTestId = this.paramRow.id; paramTestId = this.paramRow.id;
this.scheduleTaskType = "API_SCENARIO_TEST"; this.scheduleTaskType = "API_SCENARIO_TEST";
@ -192,6 +195,9 @@ export default {
if (row.redirectFrom == 'testPlan') { if (row.redirectFrom == 'testPlan') {
paramTestId = row.id; paramTestId = row.id;
this.scheduleTaskType = "TEST_PLAN_TEST"; this.scheduleTaskType = "TEST_PLAN_TEST";
} else if (row.redirectFrom == 'enterpriseReport') {
paramTestId = row.id;
this.scheduleTaskType = "ENTERPRISE_REPORT";
} else { } else {
paramTestId = row.id; paramTestId = row.id;
this.scheduleTaskType = "API_SCENARIO_TEST"; this.scheduleTaskType = "API_SCENARIO_TEST";
@ -263,6 +269,13 @@ export default {
if (param.id) { if (param.id) {
url = '/schedule/update'; url = '/schedule/update';
} }
} else if (this.scheduleTaskType == 'ENTERPRISE_REPORT') {
param.scheduleFrom = "enterpriseReport";
//
url = '/schedule/create';
if (param.id) {
url = '/schedule/update';
}
} else { } else {
param.scheduleFrom = "scenario"; param.scheduleFrom = "scenario";
if (param.id) { if (param.id) {

View File

@ -94,6 +94,7 @@ export default {
let data = response.data.barChartDTO; let data = response.data.barChartDTO;
let pieData = response.data.pieChartDTO; let pieData = response.data.pieChartDTO;
let selectTableData = response.data.tableDTOs; let selectTableData = response.data.tableDTOs;
console.info(response.data);
this.initPic(data, pieData, selectTableData); this.initPic(data, pieData, selectTableData);
}, error => { }, error => {
this.loading = false; this.loading = false;
@ -101,6 +102,7 @@ export default {
}, },
initPic(barData, pieData, selectTableData) { initPic(barData, pieData, selectTableData) {
this.loading = true; this.loading = true;
this.resetOptions();
if (barData) { if (barData) {
this.loadOption.legend = barData.legend; this.loadOption.legend = barData.legend;
this.loadOption.xAxis = barData.xaxis; this.loadOption.xAxis = barData.xaxis;
@ -172,7 +174,7 @@ export default {
let dataOptionObj = JSON.parse(reportData.dataOption); let dataOptionObj = JSON.parse(reportData.dataOption);
if (dataOptionObj.chartType) { if (dataOptionObj.chartType) {
this.chartType = dataOptionObj.chartType; this.chartType = dataOptionObj.chartType;
}else { } else {
this.chartType = "bar"; this.chartType = "bar";
} }
this.initPic(dataOptionObj.loadOption, dataOptionObj.pieOption, dataOptionObj.tableData); this.initPic(dataOptionObj.loadOption, dataOptionObj.pieOption, dataOptionObj.tableData);
@ -207,6 +209,24 @@ export default {
return ""; return "";
} }
}, },
resetOptions() {
this.loadOption = {
legend: {},
xAxis: {},
yAxis: {},
label: {},
tooltip: {},
series: []
};
this.pieOption = {
legend: {},
label: {},
tooltip: {},
series: [],
title: [],
};
this.tableData = [];
},
selectAndSaveReport(reportName) { selectAndSaveReport(reportName) {
let opt = this.$refs.countFilter.getOption(); let opt = this.$refs.countFilter.getOption();
this.options = opt; this.options = opt;

View File

@ -269,9 +269,6 @@ export default {
this.loading = true; this.loading = true;
this.option = opt; this.option = opt;
this.$nextTick(() => { this.$nextTick(() => {
if(this.option.timeType === "dynamicTime"){
this.init();
}
this.loading = false; this.loading = false;
}); });
}, },

@ -1 +1 @@
Subproject commit dd73a29ed22916b8d7ff70d1062604a17177d3a5 Subproject commit 249ac53686faf82906dcfabf76d66bdc8fda39db

View File

@ -0,0 +1,109 @@
<template>
<report-chart v-if="!needReloading" :read-only="true" :need-full-screen="false" :chart-type="dataOption.chartType"
ref="analysisChart" :load-option="dataOption.loadOption" :pie-option="dataOption.pieOption"/>
</template>
<script>
import ReportChart from "@/business/components/xpack/reportstatistics/projectreport/components/chart/ReportChart";
import {getShareId} from "@/common/js/utils";
export default {
name: "ShareEnterpriseReportTemplate",
components: {ReportChart},
data() {
return {
needReloading: false,
shareId: '',
dataOption: {
chartType: '',
loadOption: {
legend: {},
xAxis: {},
yAxis: {},
label: {},
tooltip: {},
series: []
},
pieOption: {
legend: {},
label: {},
tooltip: {},
series: [],
title: [],
},
},
}
},
created() {
this.initEchartData();
},
methods: {
initEchartData() {
this.shareId = getShareId();
let paramObj = {
id: this.shareId
};
this.resetOptions();
this.$post('/share/info/selectHistoryReportById', paramObj, response => {
let reportData = response.data;
if (reportData) {
let selectOption = JSON.parse(reportData.selectOption);
let data = JSON.parse(reportData.dataOption);
data.selectOption = selectOption;
this.dataOption = data;
console.info(this.dataOption);
this.reloadChart();
}
}, (error) => {
this.$error(this.$t('查找报告失败!'));
return false;
});
},
initPic(loadOptionParam, tableData) {
this.loading = true;
if (loadOptionParam) {
this.loadOption.legend = loadOptionParam.legend;
this.loadOption.xAxis = loadOptionParam.xaxis;
this.loadOption.series = loadOptionParam.series;
this.loadOption.grid = {
bottom: '75px',//
}
this.loadOption.series.forEach(item => {
item.type = this.$refs.analysisChart.chartType;
})
}
if (tableData) {
this.tableData = tableData;
}
this.loading = false;
},
reloadChart() {
console.info("load data over, reload compnents.");
this.$refs.analysisChart.reload();
},
resetOptions() {
this.dataOption = {
chartType: '',
loadOption: {
legend: {},
xAxis: {},
yAxis: {},
label: {},
tooltip: {},
series: []
},
pieOption: {
legend: {},
label: {},
tooltip: {},
series: [],
title: [],
},
};
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="shortcut icon" href="<%= BASE_URL %>favicon.ico">
<title>Enterprise Report</title>
</head>
<body>
<div id="shareEnterpriseReport"></div>
</body>
</html>

View File

@ -0,0 +1,40 @@
import ShareEnterpriseReportTemplate from "./ShareEnterpriseReportTemplate";
import Vue from 'vue';
import ElementUI, {Button, Col, Form, FormItem, Input, Row, Main, Card, Table, TableColumn} from 'element-ui';
import '@/assets/theme/index.css';
import '@/common/css/menu-header.css';
import '@/common/css/main.css';
import i18n from "@/i18n/i18n";
import chart from "@/common/js/chart";
import filters from "@/common/js/filter";
import icon from "@/common/js/icon";
import message from "@/common/js/message";
import Ajax from "@/common/js/ajax";
Vue.use(ElementUI, {
i18n: (key, value) => i18n.t(key, value)
});
Vue.use(Row);
Vue.use(Col);
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Input);
Vue.use(Button);
Vue.use(chart);
Vue.use(Main);
Vue.use(Card);
Vue.use(Ajax)
Vue.use(TableColumn);
Vue.use(Table);
Vue.use(filters);
Vue.use(icon);
Vue.use(message);
new Vue({
el: '#shareEnterpriseReport',
i18n,
render: h => h(ShareEnterpriseReportTemplate)
});

View File

@ -44,6 +44,11 @@ module.exports = {
template: "src/template/report/plan/plan-report.html", template: "src/template/report/plan/plan-report.html",
filename: "plan-report.html", filename: "plan-report.html",
}, },
enterpriseReport: {
entry: "src/template/enterprise/share/share-enterprise-report.js",
template: "src/template/enterprise/share/share-enterprise-report.html",
filename: "share-enterprise-report.html",
},
}, },
configureWebpack: { configureWebpack: {
devtool: 'source-map', devtool: 'source-map',
@ -61,9 +66,9 @@ module.exports = {
return args; return args;
}); });
config.plugin('inline-source-html-planReport') config.plugin('inline-source-html-planReport')
.after('html-planReport') .after('html-planReport')
.use(HtmlWebpackInlineSourcePlugin); .use(HtmlWebpackInlineSourcePlugin);
config.plugins.delete('prefetch'); config.plugins.delete('prefetch');
} }
}; };