Merge branch 'v1.5' of https://github.com/metersphere/metersphere into v1.5
This commit is contained in:
commit
26119e21c7
|
@ -0,0 +1,64 @@
|
||||||
|
name: Build Docker Image and Push
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- v1*
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- v1*
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Build Docker Image and Push
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
token: ${{ secrets.ACCESS_TOKEN }}
|
||||||
|
|
||||||
|
- name: Inject slug/short variables
|
||||||
|
uses: rlespinasse/github-slug-action@v3.x
|
||||||
|
- name: Cache node modules
|
||||||
|
uses: actions/cache@v2
|
||||||
|
env:
|
||||||
|
cache-name: cache-node-modules
|
||||||
|
with:
|
||||||
|
path: ~/.npm
|
||||||
|
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||||
|
${{ runner.os }}-build-
|
||||||
|
${{ runner.os }}-
|
||||||
|
|
||||||
|
- name: Cache local Maven repository
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.m2/repository
|
||||||
|
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-maven-
|
||||||
|
|
||||||
|
- name: Set up JDK 1.8
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
|
||||||
|
- name: Build with Maven
|
||||||
|
run: mvn -B package --file pom.xml
|
||||||
|
|
||||||
|
- name: Push to Docker Hub
|
||||||
|
uses: docker/build-push-action@v1
|
||||||
|
with:
|
||||||
|
username: metersphere
|
||||||
|
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||||
|
repository: metersphere/metersphere
|
||||||
|
tag_with_ref: true
|
||||||
|
build_args: MS_VERSION=${{ env.GITHUB_REF_SLUG }}-${{ env.GITHUB_SHA_SHORT }}
|
|
@ -1,4 +1,4 @@
|
||||||
FROM registry.fit2cloud.com/metersphere/fabric8-java-alpine-openjdk8-jre
|
FROM metersphere/fabric8-java-alpine-openjdk8-jre
|
||||||
|
|
||||||
MAINTAINER FIT2CLOUD <support@fit2cloud.com>
|
MAINTAINER FIT2CLOUD <support@fit2cloud.com>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package io.metersphere.api.controller;
|
||||||
|
|
||||||
|
import io.metersphere.api.dto.ApiMonitorSearch;
|
||||||
|
import io.metersphere.api.dto.ApiResponseCodeMonitor;
|
||||||
|
import io.metersphere.api.dto.ApiResponseTimeMonitor;
|
||||||
|
import io.metersphere.api.service.APIMonitorService;
|
||||||
|
import io.metersphere.commons.constants.RoleConstants;
|
||||||
|
import org.apache.shiro.authz.annotation.Logical;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(value = "/api/monitor")
|
||||||
|
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
|
||||||
|
public class ApiMonitorController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private APIMonitorService apiMonitorService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有接口
|
||||||
|
*/
|
||||||
|
@GetMapping("/list")
|
||||||
|
public List<ApiMonitorSearch> apiList() {
|
||||||
|
return apiMonitorService.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询响应时间
|
||||||
|
*/
|
||||||
|
@GetMapping("/getResponseTime")
|
||||||
|
public List<ApiResponseTimeMonitor> responseTimeData(@RequestHeader("apiUrl") String url, String startTime, String endTime) {
|
||||||
|
return apiMonitorService.getApiResponseTimeData(url, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询状态码
|
||||||
|
*/
|
||||||
|
@GetMapping("/getResponseCode")
|
||||||
|
public List<ApiResponseCodeMonitor> responseCodeData(@RequestHeader("apiUrl") String url, String startTime, String endTime) {
|
||||||
|
return apiMonitorService.getApiResponseCodeData(url, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询reportId
|
||||||
|
*/
|
||||||
|
@GetMapping("/getReportId")
|
||||||
|
public String searchReportId(@RequestHeader("apiUrl") String url, @RequestParam("startTime") String startTime) {
|
||||||
|
return apiMonitorService.getReportId(url, startTime);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package io.metersphere.api.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ApiMonitorSearch {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package io.metersphere.api.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ApiResponseCodeMonitor {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String reportId;
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private String apiName;
|
||||||
|
|
||||||
|
private String startTime;
|
||||||
|
|
||||||
|
private String responseCode;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package io.metersphere.api.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ApiResponseTimeMonitor {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String reportId;
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private String apiName;
|
||||||
|
|
||||||
|
private String startTime;
|
||||||
|
|
||||||
|
private String responseTime;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package io.metersphere.api.service;
|
||||||
|
|
||||||
|
import io.metersphere.api.dto.ApiMonitorSearch;
|
||||||
|
import io.metersphere.api.dto.ApiResponseCodeMonitor;
|
||||||
|
import io.metersphere.api.dto.ApiResponseTimeMonitor;
|
||||||
|
import io.metersphere.base.mapper.ApiDataViewMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public class APIMonitorService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ApiDataViewMapper apiDataViewMapper;
|
||||||
|
|
||||||
|
public List<ApiMonitorSearch> list() {
|
||||||
|
return apiDataViewMapper.selectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ApiResponseTimeMonitor> getApiResponseTimeData(String apiUrl, String startTime, String endTime) {
|
||||||
|
return apiDataViewMapper.selectResponseTimeByUrl(apiUrl, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ApiResponseCodeMonitor> getApiResponseCodeData(String apiUrl, String startTime, String endTime) {
|
||||||
|
return apiDataViewMapper.selectResponseCodeByUrl(apiUrl, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReportId(String apiUrl, String startTime) {
|
||||||
|
return apiDataViewMapper.selectReportIdByUrlAndStartTime(apiUrl, startTime);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,21 @@
|
||||||
package io.metersphere.api.service;
|
package io.metersphere.api.service;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import io.metersphere.api.dto.APIReportResult;
|
import io.metersphere.api.dto.APIReportResult;
|
||||||
import io.metersphere.api.dto.DeleteAPIReportRequest;
|
import io.metersphere.api.dto.DeleteAPIReportRequest;
|
||||||
import io.metersphere.api.dto.QueryAPIReportRequest;
|
import io.metersphere.api.dto.QueryAPIReportRequest;
|
||||||
import io.metersphere.api.jmeter.TestResult;
|
import io.metersphere.api.jmeter.TestResult;
|
||||||
import io.metersphere.base.domain.*;
|
import io.metersphere.base.domain.*;
|
||||||
|
import io.metersphere.base.mapper.ApiDataViewMapper;
|
||||||
import io.metersphere.base.mapper.ApiTestReportDetailMapper;
|
import io.metersphere.base.mapper.ApiTestReportDetailMapper;
|
||||||
import io.metersphere.base.mapper.ApiTestReportMapper;
|
import io.metersphere.base.mapper.ApiTestReportMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtApiTestReportMapper;
|
import io.metersphere.base.mapper.ext.ExtApiTestReportMapper;
|
||||||
import io.metersphere.commons.constants.APITestStatus;
|
import io.metersphere.commons.constants.APITestStatus;
|
||||||
import io.metersphere.commons.constants.ReportTriggerMode;
|
import io.metersphere.commons.constants.ReportTriggerMode;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
import io.metersphere.commons.utils.ServiceUtils;
|
import io.metersphere.commons.utils.ServiceUtils;
|
||||||
import io.metersphere.dto.DashboardTestDTO;
|
import io.metersphere.dto.DashboardTestDTO;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
|
@ -21,8 +25,11 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -36,6 +43,8 @@ public class APIReportService {
|
||||||
private ApiTestReportDetailMapper apiTestReportDetailMapper;
|
private ApiTestReportDetailMapper apiTestReportDetailMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ExtApiTestReportMapper extApiTestReportMapper;
|
private ExtApiTestReportMapper extApiTestReportMapper;
|
||||||
|
@Resource
|
||||||
|
private ApiDataViewMapper apiDataViewMapper;
|
||||||
|
|
||||||
public List<APIReportResult> list(QueryAPIReportRequest request) {
|
public List<APIReportResult> list(QueryAPIReportRequest request) {
|
||||||
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
|
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
|
||||||
|
@ -63,6 +72,8 @@ public class APIReportService {
|
||||||
public void delete(DeleteAPIReportRequest request) {
|
public void delete(DeleteAPIReportRequest request) {
|
||||||
apiTestReportDetailMapper.deleteByPrimaryKey(request.getId());
|
apiTestReportDetailMapper.deleteByPrimaryKey(request.getId());
|
||||||
apiTestReportMapper.deleteByPrimaryKey(request.getId());
|
apiTestReportMapper.deleteByPrimaryKey(request.getId());
|
||||||
|
apiDataViewMapper.deleteByReportId(request.getId());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteByTestId(String testId) {
|
public void deleteByTestId(String testId) {
|
||||||
|
@ -89,6 +100,8 @@ public class APIReportService {
|
||||||
// report
|
// report
|
||||||
report.setUpdateTime(System.currentTimeMillis());
|
report.setUpdateTime(System.currentTimeMillis());
|
||||||
if (!StringUtils.equals(report.getStatus(), APITestStatus.Debug.name())) {
|
if (!StringUtils.equals(report.getStatus(), APITestStatus.Debug.name())) {
|
||||||
|
//新增每一条接口记录到api_data_view表中
|
||||||
|
creatApiDataView(new String(detail.getContent(), StandardCharsets.UTF_8), report.getId());
|
||||||
if (result.getError() > 0) {
|
if (result.getError() > 0) {
|
||||||
report.setStatus(APITestStatus.Error.name());
|
report.setStatus(APITestStatus.Error.name());
|
||||||
} else {
|
} else {
|
||||||
|
@ -99,6 +112,44 @@ public class APIReportService {
|
||||||
apiTestReportMapper.updateByPrimaryKeySelective(report);
|
apiTestReportMapper.updateByPrimaryKeySelective(report);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void creatApiDataView(String jsonString, String reportId) {
|
||||||
|
List<ApiDataView> listApiDataView = new ArrayList<>();
|
||||||
|
JSONObject jsonObject = JSON.parseObject(jsonString, JSONObject.class);
|
||||||
|
JSONArray jsonArray = jsonObject.getJSONArray("scenarios");
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < jsonArray.size(); i++) {
|
||||||
|
JSONObject jsonInArray = jsonArray.getJSONObject(i);
|
||||||
|
JSONArray jsonRequestResults = jsonInArray.getJSONArray("requestResults");
|
||||||
|
for (int j = 0; j < jsonRequestResults.size(); j++) {
|
||||||
|
JSONObject jsonInResponseResult = jsonRequestResults.getJSONObject(j).getJSONObject("responseResult");
|
||||||
|
String responseTime = jsonInResponseResult.getString("responseTime");
|
||||||
|
String responseCode = jsonInResponseResult.getString("responseCode");
|
||||||
|
String startTime = jsonRequestResults.getJSONObject(j).getString("startTime");
|
||||||
|
String name = jsonRequestResults.getJSONObject(j).getString("name");
|
||||||
|
String url = jsonRequestResults.getJSONObject(j).getString("url");
|
||||||
|
if (StringUtils.isBlank(url)){
|
||||||
|
//如果非http请求不入库
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ApiDataView apiDataView = new ApiDataView();
|
||||||
|
apiDataView.setId(UUID.randomUUID().toString());
|
||||||
|
apiDataView.setReportId(reportId);
|
||||||
|
apiDataView.setApiName(name);
|
||||||
|
apiDataView.setUrl(StringUtils.substringBefore(url,"?"));
|
||||||
|
apiDataView.setResponseTime(responseTime);
|
||||||
|
apiDataView.setStartTime(sdf.format(new Date(Long.parseLong(startTime))));
|
||||||
|
apiDataView.setResponseCode(responseCode);
|
||||||
|
listApiDataView.add(apiDataView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtil.error(e);
|
||||||
|
}
|
||||||
|
apiDataViewMapper.insertListApiData(listApiDataView);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public String create(ApiTest test, String triggerMode) {
|
public String create(ApiTest test, String triggerMode) {
|
||||||
ApiTestReport running = getRunningReport(test.getId());
|
ApiTestReport running = getRunningReport(test.getId());
|
||||||
if (running != null) {
|
if (running != null) {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package io.metersphere.base.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ApiDataView implements Serializable {
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String reportId;
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private String apiName;
|
||||||
|
|
||||||
|
private String startTime;
|
||||||
|
|
||||||
|
private String responseCode;
|
||||||
|
|
||||||
|
private String responseTime;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package io.metersphere.base.mapper;
|
||||||
|
|
||||||
|
import io.metersphere.api.dto.ApiMonitorSearch;
|
||||||
|
import io.metersphere.api.dto.ApiResponseCodeMonitor;
|
||||||
|
import io.metersphere.api.dto.ApiResponseTimeMonitor;
|
||||||
|
import io.metersphere.base.domain.ApiDataView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ApiDataViewMapper {
|
||||||
|
|
||||||
|
List<ApiMonitorSearch> selectAll();
|
||||||
|
|
||||||
|
List<ApiResponseTimeMonitor> selectResponseTimeByUrl(String url,String startTime,String endTime);
|
||||||
|
|
||||||
|
List<ApiResponseCodeMonitor> selectResponseCodeByUrl(String url,String startTime,String endTime);
|
||||||
|
|
||||||
|
Integer insertListApiData(List<ApiDataView> list);
|
||||||
|
|
||||||
|
Integer deleteByReportId(String reportId);
|
||||||
|
|
||||||
|
String selectReportIdByUrlAndStartTime(String apiUrl,String startTime);
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="io.metersphere.base.mapper.ApiDataViewMapper">
|
||||||
|
<resultMap id="apiDataView" type="io.metersphere.base.domain.ApiDataView">
|
||||||
|
<id column="id" property="id"/>
|
||||||
|
<result column="report_id" property="reportId"/>
|
||||||
|
<result column="api_name" property="apiName"/>
|
||||||
|
<result column="url" property="url"/>
|
||||||
|
<result column="response_code" property="startTime"/>
|
||||||
|
<result column="start_time" property="responseCode"/>
|
||||||
|
<result column="response_time" property="responseTime"/>
|
||||||
|
</resultMap>
|
||||||
|
<delete id="deleteByReportId" parameterType="java.lang.String">
|
||||||
|
delete from api_data_view where report_id = #{reportId,jdbcType=VARCHAR}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<select id="selectAll" resultType="io.metersphere.api.dto.ApiMonitorSearch">
|
||||||
|
select distinct url from api_data_view;
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectResponseTimeByUrl" parameterType="java.lang.String"
|
||||||
|
resultType="io.metersphere.api.dto.ApiResponseTimeMonitor">
|
||||||
|
select id,report_id,api_name,start_time,response_time,url
|
||||||
|
from api_data_view
|
||||||
|
<where>
|
||||||
|
<if test="url != null and url != ''">
|
||||||
|
url = #{url}
|
||||||
|
</if>
|
||||||
|
<if test="startTime != null and startTime != '' and endTime != null and endTime != ''">
|
||||||
|
AND date_format(start_time,'%Y-%m-%d %H:%i:%s')
|
||||||
|
between #{startTime} and #{endTime}
|
||||||
|
</if>
|
||||||
|
<if test="startTime == null or startTime == '' or endTime == null or endTime == ''">
|
||||||
|
AND TO_DAYS(start_time) =TO_DAYS(NOW())
|
||||||
|
</if>
|
||||||
|
order by start_time;
|
||||||
|
</where>
|
||||||
|
|
||||||
|
</select>
|
||||||
|
<select id="selectResponseCodeByUrl" resultType="io.metersphere.api.dto.ApiResponseCodeMonitor">
|
||||||
|
select id,report_id,api_name,start_time,response_code,url
|
||||||
|
from api_data_view
|
||||||
|
<where>
|
||||||
|
<if test="url != null and url != ''">
|
||||||
|
url = #{url}
|
||||||
|
</if>
|
||||||
|
<if test="startTime != null and startTime != '' and endTime != null and endTime != ''">
|
||||||
|
AND date_format(start_time,'%Y-%m-%d %H:%i:%s')
|
||||||
|
between #{startTime} and #{endTime} and length(response_code)=3
|
||||||
|
</if>
|
||||||
|
<if test="startTime == null or startTime == '' or endTime == null or endTime == ''">
|
||||||
|
AND TO_DAYS(start_time) =TO_DAYS(NOW()) and length(response_code)=3
|
||||||
|
</if>
|
||||||
|
order by start_time;
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectReportIdByUrlAndStartTime" resultType="java.lang.String">
|
||||||
|
select report_id from api_data_view where response_code != 200 and url=#{apiUrl} and start_time=#{startTime};
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertListApiData" parameterType="java.util.List">
|
||||||
|
insert into api_data_view(id, report_id, api_name,url, response_code, start_time,response_time)
|
||||||
|
values
|
||||||
|
<foreach collection="list" item="item" index="index" separator=",">
|
||||||
|
(
|
||||||
|
#{item.id,jdbcType=VARCHAR},
|
||||||
|
#{item.reportId,jdbcType=VARCHAR},
|
||||||
|
#{item.apiName,jdbcType=VARCHAR},
|
||||||
|
#{item.url,jdbcType=VARCHAR},
|
||||||
|
#{item.responseCode,jdbcType=VARCHAR},
|
||||||
|
#{item.startTime,jdbcType=VARCHAR},
|
||||||
|
#{item.responseTime,jdbcType=VARCHAR}
|
||||||
|
)
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
</mapper>
|
|
@ -372,7 +372,7 @@ public class TestCaseNodeService {
|
||||||
public Map<String, String> createNodes(List<String> nodePaths, String projectId) {
|
public Map<String, String> createNodes(List<String> nodePaths, String projectId) {
|
||||||
List<TestCaseNodeDTO> nodeTrees = getNodeTreeByProjectId(projectId);
|
List<TestCaseNodeDTO> nodeTrees = getNodeTreeByProjectId(projectId);
|
||||||
Map<String, String> pathMap = new HashMap<>();
|
Map<String, String> pathMap = new HashMap<>();
|
||||||
for(String item : nodePaths){
|
for (String item : nodePaths) {
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
throw new ExcelException(Translator.get("test_case_module_not_null"));
|
throw new ExcelException(Translator.get("test_case_module_not_null"));
|
||||||
}
|
}
|
||||||
|
@ -590,6 +590,7 @@ public class TestCaseNodeService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 测试用例同级模块排序
|
* 测试用例同级模块排序
|
||||||
|
*
|
||||||
* @param ids 被拖拽模块相邻的前一个模块 id,
|
* @param ids 被拖拽模块相邻的前一个模块 id,
|
||||||
* 被拖拽的模块 id,
|
* 被拖拽的模块 id,
|
||||||
* 被拖拽模块相邻的后一个模块 id
|
* 被拖拽模块相邻的后一个模块 id
|
||||||
|
@ -635,10 +636,11 @@ public class TestCaseNodeService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按照指定排序方式获取同级模块的列表
|
* 按照指定排序方式获取同级模块的列表
|
||||||
|
*
|
||||||
* @param projectId 所属项目 id
|
* @param projectId 所属项目 id
|
||||||
* @param level node level
|
* @param level node level
|
||||||
* @param parentId node parent id
|
* @param parentId node parent id
|
||||||
* @param order pos 排序方式
|
* @param order pos 排序方式
|
||||||
* @return 按照指定排序方式排序的同级模块列表
|
* @return 按照指定排序方式排序的同级模块列表
|
||||||
*/
|
*/
|
||||||
private List<TestCaseNode> getPos(String projectId, int level, String parentId, String order) {
|
private List<TestCaseNode> getPos(String projectId, int level, String parentId, String order) {
|
||||||
|
@ -654,9 +656,10 @@ public class TestCaseNodeService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新同级模块的 pos 值
|
* 刷新同级模块的 pos 值
|
||||||
|
*
|
||||||
* @param projectId project id
|
* @param projectId project id
|
||||||
* @param level node level
|
* @param level node level
|
||||||
* @param parentId node parent id
|
* @param parentId node parent id
|
||||||
*/
|
*/
|
||||||
private void refreshPos(String projectId, int level, String parentId) {
|
private void refreshPos(String projectId, int level, String parentId) {
|
||||||
List<TestCaseNode> nodes = getPos(projectId, level, parentId, "pos asc");
|
List<TestCaseNode> nodes = getPos(projectId, level, parentId, "pos asc");
|
||||||
|
@ -675,14 +678,15 @@ public class TestCaseNodeService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得同级模块下一个 pos 值
|
* 获得同级模块下一个 pos 值
|
||||||
|
*
|
||||||
* @param projectId project id
|
* @param projectId project id
|
||||||
* @param level node level
|
* @param level node level
|
||||||
* @param parentId node parent id
|
* @param parentId node parent id
|
||||||
* @return 同级模块下一个 pos 值
|
* @return 同级模块下一个 pos 值
|
||||||
*/
|
*/
|
||||||
private double getNextLevelPos(String projectId, int level, String parentId) {
|
private double getNextLevelPos(String projectId, int level, String parentId) {
|
||||||
List<TestCaseNode> list = getPos(projectId, level, parentId, "pos desc");
|
List<TestCaseNode> list = getPos(projectId, level, parentId, "pos desc");
|
||||||
if (!CollectionUtils.isEmpty(list)) {
|
if (!CollectionUtils.isEmpty(list) && list.get(0) != null && list.get(0).getPos() != null) {
|
||||||
return list.get(0).getPos() + 65536;
|
return list.get(0).getPos() + 65536;
|
||||||
} else {
|
} else {
|
||||||
return 65536;
|
return 65536;
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS `api_data_view`
|
||||||
|
(
|
||||||
|
id varchar(50) NOT NULL primary key,
|
||||||
|
report_id varchar(255) NOT NULL,
|
||||||
|
api_name varchar(200) NULL,
|
||||||
|
url varchar(255) NULL,
|
||||||
|
response_code varchar(100) NULL,
|
||||||
|
start_time varchar(20) NULL,
|
||||||
|
response_time varchar(20) default '0' NULL,
|
||||||
|
create_time timestamp default CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
update_time timestamp default CURRENT_TIMESTAMP NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
@ -32,6 +32,11 @@
|
||||||
<el-divider class="menu-divider"/>
|
<el-divider class="menu-divider"/>
|
||||||
<ms-show-all :index="'/api/report/list/all'"/>
|
<ms-show-all :index="'/api/report/list/all'"/>
|
||||||
</el-submenu>
|
</el-submenu>
|
||||||
|
|
||||||
|
|
||||||
|
<el-menu-item v-permission="['test_manager','test_user','test_viewer']" :index="'/api/monitor/view'">
|
||||||
|
{{ $t('commons.monitor') }}
|
||||||
|
</el-menu-item>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
<template>
|
||||||
|
<ms-container>
|
||||||
|
|
||||||
|
<ms-aside-container>
|
||||||
|
<ms-api-monitor-search @getApiUrl="getApiUrl(arguments)" @getTodayData="getTodayData"
|
||||||
|
@initPage="initPage(arguments)"></ms-api-monitor-search>
|
||||||
|
</ms-aside-container>
|
||||||
|
|
||||||
|
<ms-main-container>
|
||||||
|
|
||||||
|
<div class="menu-wrapper">
|
||||||
|
|
||||||
|
<div class="menu-left">
|
||||||
|
<el-radio-group id="date-radio" v-model=radioSelect size="small">
|
||||||
|
<el-radio-button :label="$t('api_monitor.today')" @click.native.prevent="getTodayData()"/>
|
||||||
|
<el-radio-button :label="$t('api_monitor.this_week')" @click.native.prevent="getWeekData()"/>
|
||||||
|
<el-radio-button :label="$t('api_monitor.this_mouth')" @click.native.prevent="getMouthData()"/>
|
||||||
|
</el-radio-group>
|
||||||
|
<el-date-picker id="date-picker"
|
||||||
|
v-model="datePicker"
|
||||||
|
:end-placeholder="$t('api_monitor.end_time')"
|
||||||
|
:range-separator="$t('api_monitor.to')"
|
||||||
|
:start-placeholder="$t('api_monitor.start_time')"
|
||||||
|
class="sales-view-date-picker"
|
||||||
|
size="small"
|
||||||
|
type="daterange"
|
||||||
|
unlink-panels
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss" @blur="getDatePicker"
|
||||||
|
/>
|
||||||
|
<el-tag id="apiInfo" type="info"><a id="api-url-title">{{ apiUrl }}</a></el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ms-api-monitor-chart :api-url="apiUrl" :rspCodeData=this.rspCodeData
|
||||||
|
:rspCodexAxis=this.rspCodexAxis :rspTimeData=this.rspTimeData
|
||||||
|
:rspTimexAxis=this.rspTimexAxis></ms-api-monitor-chart>
|
||||||
|
</ms-main-container>
|
||||||
|
</ms-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import ApiMonitorCharts from '@/business/components/api/monitor/ApiMonitorChart';
|
||||||
|
import MsApiMonitorChart from '@/business/components/api/monitor/ApiMonitorChart';
|
||||||
|
import MsApiMonitorSearch from '@/business/components/api/monitor/ApiMonitorSearch';
|
||||||
|
import MsMainContainer from '@/business/components/common/components/MsMainContainer';
|
||||||
|
import MsAsideContainer from '@/business/components/common/components/MsAsideContainer';
|
||||||
|
import MsContainer from '@/business/components/common/components/MsContainer';
|
||||||
|
import {formatTime} from '@/common/js/format-utils';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MsApiMonitor',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
datePicker: null,
|
||||||
|
rspTimeData: [],
|
||||||
|
rspTimexAxis: [],
|
||||||
|
rspCodeData: [],
|
||||||
|
rspCodexAxis: [],
|
||||||
|
apiUrl: '',
|
||||||
|
radioSelect: this.$t('api_monitor.today'),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
activated() {
|
||||||
|
this.initData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initData() {
|
||||||
|
this.rspTimeData = [];
|
||||||
|
this.rspTimexAxis = [];
|
||||||
|
this.rspCodeData = [];
|
||||||
|
this.rspCodexAxis = [];
|
||||||
|
},
|
||||||
|
initPage(url) {
|
||||||
|
this.apiUrl = url[0];
|
||||||
|
let date1 = new Date();
|
||||||
|
let today1 = formatTime(new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()));
|
||||||
|
let today2 = formatTime(new Date(date1.getFullYear(), date1.getMonth(), date1.getDate() + 1));
|
||||||
|
this.initData();
|
||||||
|
this.getResponseTime(this.apiUrl, today1, today2);
|
||||||
|
this.getResponseCode(this.apiUrl, today1, today2);
|
||||||
|
},
|
||||||
|
getResponseTime(apiUrl, startTime, endTime) {
|
||||||
|
return this.$$get('/api/monitor/getResponseTime',
|
||||||
|
{'startTime': startTime, 'endTime': endTime},
|
||||||
|
{'apiUrl': apiUrl}, response => {
|
||||||
|
Object.values(response.data).forEach(value => {
|
||||||
|
this.rspTimexAxis.push(value.startTime);
|
||||||
|
this.rspTimeData.push(value.responseTime);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getResponseCode(apiUrl, startTime, endTime) {
|
||||||
|
return this.$$get('/api/monitor/getResponseCode',
|
||||||
|
{'startTime': startTime, 'endTime': endTime},
|
||||||
|
{'apiUrl': this.apiUrl}, response => {
|
||||||
|
Object.values(response.data).forEach(value => {
|
||||||
|
this.rspCodexAxis.push(value.startTime);
|
||||||
|
this.rspCodeData.push(value.responseCode);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getDatePicker() {
|
||||||
|
this.initData();
|
||||||
|
this.getResponseTime(this.apiUrl, this.datePicker[0], this.datePicker[1]);
|
||||||
|
this.getResponseCode(this.apiUrl, this.datePicker[0], this.datePicker[1]);
|
||||||
|
},
|
||||||
|
//获取周一日期
|
||||||
|
getFirstDayOfWeek(date) {
|
||||||
|
let day = date.getDay() || 7;
|
||||||
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1 - day);
|
||||||
|
},
|
||||||
|
getApiUrl(payload) {
|
||||||
|
this.apiUrl = payload[0];
|
||||||
|
},
|
||||||
|
//获取今日数据
|
||||||
|
getTodayData() {
|
||||||
|
this.radioSelect = this.$t('api_monitor.today');
|
||||||
|
let date1 = new Date();
|
||||||
|
let today1 = formatTime(new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()));
|
||||||
|
let today2 = formatTime(new Date(date1.getFullYear(), date1.getMonth(), date1.getDate() + 1));
|
||||||
|
this.initData();
|
||||||
|
this.getResponseTime(this.apiUrl, today1, today2);
|
||||||
|
this.getResponseCode(this.apiUrl, today1, today2);
|
||||||
|
},
|
||||||
|
//获取周数据
|
||||||
|
getWeekData() {
|
||||||
|
this.radioSelect = this.$t('api_monitor.this_week');
|
||||||
|
const date1 = new Date();
|
||||||
|
let today = formatTime(date1);
|
||||||
|
let monday = formatTime(this.getFirstDayOfWeek(date1));
|
||||||
|
this.initData();
|
||||||
|
this.getResponseTime(this.apiUrl, monday, today);
|
||||||
|
this.getResponseCode(this.apiUrl, monday, today);
|
||||||
|
},
|
||||||
|
//获取月数据
|
||||||
|
getMouthData() {
|
||||||
|
this.radioSelect = this.$t('api_monitor.this_mouth');
|
||||||
|
const day = new Date(), y = day.getFullYear(), m = day.getMonth();
|
||||||
|
let firstDay = formatTime(new Date(y, m, 1));
|
||||||
|
let today = formatTime(day);
|
||||||
|
this.initData();
|
||||||
|
this.getResponseTime(this.apiUrl, firstDay, today);
|
||||||
|
this.getResponseCode(this.apiUrl, firstDay, today);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
MsApiMonitorChart,
|
||||||
|
MsContainer,
|
||||||
|
MsAsideContainer,
|
||||||
|
MsMainContainer,
|
||||||
|
MsApiMonitorSearch,
|
||||||
|
ApiMonitorCharts,
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.menu-left #apiInfo {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#api-url-title {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<template>
|
||||||
|
<div class="monitor-view">
|
||||||
|
<div class="response-time-view">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<api-response-time-monitor-chart :data="rspTimeData" :xAxis="rspTimexAxis"></api-response-time-monitor-chart>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
<div class="error-monitor-view">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<api-error-monitor-chart :api-url="apiUrl" :data="rspCodeData" :xAxis="rspCodexAxis"></api-error-monitor-chart>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import ApiResponseTimeMonitorChart from '@/business/components/api/monitor/components/ApiResponseTimeMonitorChart';
|
||||||
|
import ApiErrorMonitorChart from '@/business/components/api/monitor/components/ApiErrorMonitorChart';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MsApiMonitorChart',
|
||||||
|
props: [
|
||||||
|
'rspTimeData',
|
||||||
|
'rspTimexAxis',
|
||||||
|
'rspCodeData',
|
||||||
|
'rspCodexAxis',
|
||||||
|
'apiUrl'
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ApiErrorMonitorChart,
|
||||||
|
ApiResponseTimeMonitorChart,
|
||||||
|
},
|
||||||
|
methods: {}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.error-monitor-view {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,100 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-input v-model="input" :placeholder="$t('api_monitor.please_search')" prefix-icon="el-icon-search" type="text"
|
||||||
|
@input="searchAction"></el-input>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
:data="searchResult"
|
||||||
|
:show-header="false"
|
||||||
|
border
|
||||||
|
class="question-tab2"
|
||||||
|
style="width: 100%"
|
||||||
|
@row-click="getRowInfo"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('api_monitor.date')"
|
||||||
|
prop="url"
|
||||||
|
style="width: 100%">
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function throttle(fn, delay) {
|
||||||
|
let t = null,
|
||||||
|
begin = new Date().getTime();
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
const _self = this,
|
||||||
|
args = arguments,
|
||||||
|
cur = new Date().getTime();
|
||||||
|
clearTimeout(t);
|
||||||
|
if (cur - begin >= delay) {
|
||||||
|
fn.apply(_self, args);
|
||||||
|
begin = cur;
|
||||||
|
} else {
|
||||||
|
t = setTimeout(function () {
|
||||||
|
fn.apply(_self, args);
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MsApiMonitorSearch',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
result: {},
|
||||||
|
searchResult: [],
|
||||||
|
items: [],
|
||||||
|
input: '',
|
||||||
|
rowData: '',
|
||||||
|
apiUrl: '',
|
||||||
|
firstUrl: '',
|
||||||
|
rspTimeData: [],
|
||||||
|
rspTimexAxis: [],
|
||||||
|
rspCodeData: [],
|
||||||
|
rspCodexAxis: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
searchAction: throttle(function (e) {
|
||||||
|
this.searchResult = this.items.filter((item) => {
|
||||||
|
if (item.url.includes(this.input)) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 500),
|
||||||
|
search() {
|
||||||
|
this.result = this.$get('/api/monitor/list', response => {
|
||||||
|
this.items = response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getRowInfo(val) {
|
||||||
|
this.rowData = val;
|
||||||
|
this.apiUrl = this.rowData.url;
|
||||||
|
this.$emit('getApiUrl', this.apiUrl, this.firstUrl);
|
||||||
|
this.$emit('getTodayData');
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
activated() {
|
||||||
|
this.searchResult=[];
|
||||||
|
this.result = this.$get('/api/monitor/list', response => {
|
||||||
|
if (response.data.length !== 0) {
|
||||||
|
this.searchResult = response.data;
|
||||||
|
this.firstUrl = response.data[0].url;
|
||||||
|
this.$emit('initPage', this.firstUrl);
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<common-monitor-chart>
|
||||||
|
<template>
|
||||||
|
<div id="response-time-chart" :style="{ width:'100%',height:'100%' }">
|
||||||
|
<chart :options="getOptions()" :style="{ width:'100%' }"></chart>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</common-monitor-chart>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CommonMonitorChart from '@/business/components/api/monitor/components/CommonMonitorChart';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'ApiErrorMonitorChart',
|
||||||
|
components: {CommonMonitorChart},
|
||||||
|
props: [
|
||||||
|
'data',
|
||||||
|
'xAxis',
|
||||||
|
'apiUrl',
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
reportId: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
click(params) {
|
||||||
|
//如果状态不是以2开头
|
||||||
|
if (params.value.substr(0, 1) !== '2') {
|
||||||
|
let startTime = params.name;
|
||||||
|
this.result = this.$$get('/api/monitor/getReportId', {'startTime': startTime}, {
|
||||||
|
'apiUrl': this.apiUrl
|
||||||
|
}, response => {
|
||||||
|
this.reportId = response.data;
|
||||||
|
let reportId = this.reportId
|
||||||
|
let url = '#/api/report/view/' + reportId;
|
||||||
|
let target = '_blank';
|
||||||
|
window.open(url, target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
getOptions() {
|
||||||
|
return {
|
||||||
|
title: {text: 'HTTP状态码趋势'},
|
||||||
|
tooltip: {},
|
||||||
|
toolbox: {
|
||||||
|
show: true,
|
||||||
|
feature: {
|
||||||
|
dataZoom: {
|
||||||
|
yAxisIndex: 'none'
|
||||||
|
},
|
||||||
|
dataView: {
|
||||||
|
readOnly: false
|
||||||
|
},
|
||||||
|
restore: {},
|
||||||
|
saveAsImage: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataZoom: [{
|
||||||
|
start: 0
|
||||||
|
}],
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: this.xAxis
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
min: 100,
|
||||||
|
max: 500,
|
||||||
|
splitNumber: 5
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: this.data,
|
||||||
|
|
||||||
|
lineStyle: {
|
||||||
|
color: '#32CD32'
|
||||||
|
},
|
||||||
|
itemStyle: {},
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,64 @@
|
||||||
|
<template>
|
||||||
|
<common-monitor-chart>
|
||||||
|
<template>
|
||||||
|
<div id="response-time-chart" :style="{ width:'100%',height:'100%' }">
|
||||||
|
<chart :options="getOptions()" :style="{ width:'100%' }"></chart>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</common-monitor-chart>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CommonMonitorChart from '@/business/components/api/monitor/components/CommonMonitorChart';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'ApiResponseTimeMonitorChart',
|
||||||
|
components: {CommonMonitorChart},
|
||||||
|
props: [
|
||||||
|
'data',
|
||||||
|
'xAxis'
|
||||||
|
],
|
||||||
|
methods: {
|
||||||
|
getOptions() {
|
||||||
|
return {
|
||||||
|
title: {text: '响应时间趋势'},
|
||||||
|
tooltip: {},
|
||||||
|
toolbox: {
|
||||||
|
show: true,
|
||||||
|
feature: {
|
||||||
|
dataZoom: {
|
||||||
|
yAxisIndex: 'none'
|
||||||
|
},
|
||||||
|
dataView: {
|
||||||
|
readOnly: false
|
||||||
|
},
|
||||||
|
restore: {},
|
||||||
|
saveAsImage: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: this.xAxis
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
|
||||||
|
},
|
||||||
|
dataZoom: [{
|
||||||
|
start: 0
|
||||||
|
}],
|
||||||
|
series: [{
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: this.data
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<template>
|
||||||
|
<div class="common-card">
|
||||||
|
<div class="chart">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<div class="line"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
title: String,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.title {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
margin: 10px 0;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
|
@ -38,6 +38,11 @@ export default {
|
||||||
path: "report/view/:reportId",
|
path: "report/view/:reportId",
|
||||||
name: "ApiReportView",
|
name: "ApiReportView",
|
||||||
component: () => import('@/business/components/api/report/ApiReportView'),
|
component: () => import('@/business/components/api/report/ApiReportView'),
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
path: 'monitor/view',
|
||||||
|
name: 'ApiMonitor',
|
||||||
|
component: () => import('@/business/components/api/monitor/ApiMonitor'),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
<el-form-item :label="$t('load_test.thread_num')">
|
<el-form-item :label="$t('load_test.thread_num')">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
:placeholder="$t('load_test.input_thread_num')"
|
|
||||||
v-model="threadGroup.threadNumber"
|
v-model="threadGroup.threadNumber"
|
||||||
@change="calculateChart(threadGroup)"
|
@change="calculateChart(threadGroup)"
|
||||||
:min="resourcePoolResourceLength"
|
:min="resourcePoolResourceLength"
|
||||||
|
@ -37,7 +36,6 @@
|
||||||
<el-form-item :label="$t('load_test.duration')">
|
<el-form-item :label="$t('load_test.duration')">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
:placeholder="$t('load_test.duration')"
|
|
||||||
v-model="threadGroup.duration"
|
v-model="threadGroup.duration"
|
||||||
:min="1"
|
:min="1"
|
||||||
@change="calculateChart(threadGroup)"
|
@change="calculateChart(threadGroup)"
|
||||||
|
@ -49,7 +47,6 @@
|
||||||
|
|
||||||
<el-input-number
|
<el-input-number
|
||||||
:disabled="isReadOnly || !threadGroup.rpsLimitEnable"
|
:disabled="isReadOnly || !threadGroup.rpsLimitEnable"
|
||||||
:placeholder="$t('load_test.input_rps_limit')"
|
|
||||||
v-model="threadGroup.rpsLimit"
|
v-model="threadGroup.rpsLimit"
|
||||||
@change="calculateChart(threadGroup)"
|
@change="calculateChart(threadGroup)"
|
||||||
:min="1"
|
:min="1"
|
||||||
|
@ -59,7 +56,6 @@
|
||||||
<el-form-item :label="$t('load_test.ramp_up_time_within')">
|
<el-form-item :label="$t('load_test.ramp_up_time_within')">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
placeholder=""
|
|
||||||
:min="1"
|
:min="1"
|
||||||
:max="threadGroup.duration"
|
:max="threadGroup.duration"
|
||||||
v-model="threadGroup.rampUpTime"
|
v-model="threadGroup.rampUpTime"
|
||||||
|
@ -69,7 +65,6 @@
|
||||||
<el-form-item :label="$t('load_test.ramp_up_time_minutes')">
|
<el-form-item :label="$t('load_test.ramp_up_time_minutes')">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
placeholder=""
|
|
||||||
:min="1"
|
:min="1"
|
||||||
:max="Math.min(threadGroup.threadNumber, threadGroup.rampUpTime)"
|
:max="Math.min(threadGroup.threadNumber, threadGroup.rampUpTime)"
|
||||||
v-model="threadGroup.step"
|
v-model="threadGroup.step"
|
||||||
|
@ -465,7 +460,13 @@ export default {
|
||||||
|
|
||||||
for (let i = 0; i < this.threadGroups.length; i++) {
|
for (let i = 0; i < this.threadGroups.length; i++) {
|
||||||
if (!this.threadGroups[i].threadNumber || !this.threadGroups[i].duration
|
if (!this.threadGroups[i].threadNumber || !this.threadGroups[i].duration
|
||||||
|| !this.threadGroups[i].rampUpTime || !this.threadGroups[i].step || !this.threadGroups[i].rpsLimit) {
|
|| !this.threadGroups[i].rampUpTime || !this.threadGroups[i].step) {
|
||||||
|
this.$warning(this.$t('load_test.pressure_config_params_is_empty'));
|
||||||
|
this.$emit('changeActive', '1');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.threadGroups[i].rpsLimitEnable && !this.threadGroups[i].rpsLimit) {
|
||||||
this.$warning(this.$t('load_test.pressure_config_params_is_empty'));
|
this.$warning(this.$t('load_test.pressure_config_params_is_empty'));
|
||||||
this.$emit('changeActive', '1');
|
this.$emit('changeActive', '1');
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<img class="platform" src="../../../../assets/jira.png" alt="Jira"/>
|
<img class="platform" src="../../../../assets/jira.png" alt="Jira"/>
|
||||||
</el-radio>
|
</el-radio>
|
||||||
<el-radio label="Zentao">
|
<el-radio label="Zentao">
|
||||||
<img class="platform" src="../../../../assets/zentao.jpg" alt="Zentao"/>
|
<img class="zentao_platform" src="../../../../assets/zentao.jpg" alt="Zentao"/>
|
||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,7 +66,12 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.platform {
|
.platform {
|
||||||
height: 90px;
|
height: 80px;
|
||||||
|
vertical-align: middle
|
||||||
|
}
|
||||||
|
|
||||||
|
.zentao_platform {
|
||||||
|
height: 100px;
|
||||||
vertical-align: middle
|
vertical-align: middle
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -70,12 +70,12 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.prototype.$$get = function (url, data, success) {
|
Vue.prototype.$$get = function (url, data, header, success) {
|
||||||
let result = {loading: true};
|
let result = {loading: true};
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return axios.get(url, {params: data});
|
return axios.get(url, {params: data, headers: header});
|
||||||
} else {
|
} else {
|
||||||
axios.get(url, {params: data}).then(response => {
|
axios.get(url, {params: data, headers: header}).then(response => {
|
||||||
then(success, response, result);
|
then(success, response, result);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
exception(error, result);
|
exception(error, result);
|
||||||
|
|
|
@ -4,6 +4,8 @@ import 'echarts/lib/chart/bar'
|
||||||
import 'echarts/lib/chart/pie'
|
import 'echarts/lib/chart/pie'
|
||||||
import 'echarts/lib/component/tooltip'
|
import 'echarts/lib/component/tooltip'
|
||||||
import 'echarts/lib/component/title'
|
import 'echarts/lib/component/title'
|
||||||
|
import 'echarts/lib/component/toolbox';
|
||||||
|
import 'echarts/lib/component/dataZoom';
|
||||||
import 'zrender/lib/svg/svg'
|
import 'zrender/lib/svg/svg'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -120,6 +120,42 @@ export function formatXml(text) {
|
||||||
return outputText.replace(/\s+$/g, '').replace(/\r/g, '\r\n');
|
return outputText.replace(/\s+$/g, '').replace(/\r/g, '\r\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param time 时间
|
||||||
|
* @param cFormat 格式
|
||||||
|
* @returns {string|null} 字符串
|
||||||
|
* @example formatTime('2018-1-29', '{y}/{m}/{d} {h}:{i}:{s}') // -> 2018/01/29 00:00:00
|
||||||
|
*/
|
||||||
|
export function formatTime(time, cFormat) {
|
||||||
|
if (arguments.length === 0) return null;
|
||||||
|
if ((time + '').length === 10) {
|
||||||
|
time = +time * 1000;
|
||||||
|
}
|
||||||
|
let format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}', date;
|
||||||
|
if (typeof time === 'object') {
|
||||||
|
date = time;
|
||||||
|
} else {
|
||||||
|
date = new Date(time);
|
||||||
|
}
|
||||||
|
let formatObj = {
|
||||||
|
y: date.getFullYear(),
|
||||||
|
m: date.getMonth() + 1,
|
||||||
|
d: date.getDate(),
|
||||||
|
h: date.getHours(),
|
||||||
|
i: date.getMinutes(),
|
||||||
|
s: date.getSeconds(),
|
||||||
|
a: date.getDay()
|
||||||
|
};
|
||||||
|
return format.replace(/{([ymdhisa])+}/g, (result, key) => {
|
||||||
|
let value = formatObj[key];
|
||||||
|
if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1];
|
||||||
|
if (result.length > 0 && value < 10) {
|
||||||
|
value = '0' + value;
|
||||||
|
}
|
||||||
|
return value || 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getPrefix(prefixIndex) {
|
function getPrefix(prefixIndex) {
|
||||||
var span = ' ';
|
var span = ' ';
|
||||||
var output = [];
|
var output = [];
|
||||||
|
|
|
@ -162,7 +162,8 @@ export default {
|
||||||
between: "Between",
|
between: "Between",
|
||||||
current_user: "Current user"
|
current_user: "Current user"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
monitor:"monitor"
|
||||||
},
|
},
|
||||||
license: {
|
license: {
|
||||||
title: 'Authorization management',
|
title: 'Authorization management',
|
||||||
|
@ -707,6 +708,16 @@ export default {
|
||||||
running: "The test is reporting",
|
running: "The test is reporting",
|
||||||
not_exist: "Test report does not exist",
|
not_exist: "Test report does not exist",
|
||||||
},
|
},
|
||||||
|
api_monitor: {
|
||||||
|
to:"to",
|
||||||
|
start_time:"Start Time",
|
||||||
|
end_time:"End Time",
|
||||||
|
today:"Today",
|
||||||
|
this_week:"This Week",
|
||||||
|
this_mouth:"This Mouth",
|
||||||
|
please_search:"Please Search",
|
||||||
|
date:"Date"
|
||||||
|
},
|
||||||
test_track: {
|
test_track: {
|
||||||
test_track: "Track",
|
test_track: "Track",
|
||||||
confirm: "Confirm",
|
confirm: "Confirm",
|
||||||
|
|
|
@ -162,7 +162,8 @@ export default {
|
||||||
between: "之间",
|
between: "之间",
|
||||||
current_user: "是当前用户"
|
current_user: "是当前用户"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
monitor: "监控"
|
||||||
},
|
},
|
||||||
license: {
|
license: {
|
||||||
title: '授权管理',
|
title: '授权管理',
|
||||||
|
@ -709,6 +710,16 @@ export default {
|
||||||
running: "测试报告导出中",
|
running: "测试报告导出中",
|
||||||
not_exist: "测试报告不存在",
|
not_exist: "测试报告不存在",
|
||||||
},
|
},
|
||||||
|
api_monitor: {
|
||||||
|
to:"至",
|
||||||
|
start_time:"开始日期",
|
||||||
|
end_time:"结束日期",
|
||||||
|
today:"今日",
|
||||||
|
this_week:"本周",
|
||||||
|
this_mouth:"本月",
|
||||||
|
please_search:"请搜索",
|
||||||
|
date:"日期"
|
||||||
|
},
|
||||||
test_track: {
|
test_track: {
|
||||||
test_track: "测试跟踪",
|
test_track: "测试跟踪",
|
||||||
confirm: "确 定",
|
confirm: "确 定",
|
||||||
|
|
|
@ -162,7 +162,8 @@ export default {
|
||||||
between: "之間",
|
between: "之間",
|
||||||
current_user: "是當前用戶"
|
current_user: "是當前用戶"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
monitor:"監控"
|
||||||
},
|
},
|
||||||
license: {
|
license: {
|
||||||
title: '授權管理',
|
title: '授權管理',
|
||||||
|
@ -710,6 +711,16 @@ export default {
|
||||||
running: "測試報告導出中",
|
running: "測試報告導出中",
|
||||||
not_exist: "測試報告不存在",
|
not_exist: "測試報告不存在",
|
||||||
},
|
},
|
||||||
|
api_monitor: {
|
||||||
|
to:"到",
|
||||||
|
start_time:"開始時間",
|
||||||
|
end_time:"結束時間",
|
||||||
|
today:"今天",
|
||||||
|
this_week:"本週",
|
||||||
|
this_mouth:"本月",
|
||||||
|
please_search:"請搜索",
|
||||||
|
date:"日期"
|
||||||
|
},
|
||||||
test_track: {
|
test_track: {
|
||||||
test_track: "測試跟蹤",
|
test_track: "測試跟蹤",
|
||||||
confirm: "確 定",
|
confirm: "確 定",
|
||||||
|
|
Loading…
Reference in New Issue