refactor: 增加工作台代码

This commit is contained in:
CaptainB 2022-12-07 15:03:22 +08:00 committed by 刘瑞斌
parent 870ac6242a
commit 09b0e11190
163 changed files with 22495 additions and 2 deletions

4
Jenkinsfile vendored
View File

@ -97,7 +97,7 @@ pipeline {
LOCAL_REPOSITORY=$(./mvnw help:evaluate -Dexpression=settings.localRepository --settings ./settings.xml -q -DforceStdout) LOCAL_REPOSITORY=$(./mvnw help:evaluate -Dexpression=settings.localRepository --settings ./settings.xml -q -DforceStdout)
libraries=('api-test' 'performance-test' 'project-management' 'system-setting' 'test-track' 'report-stat') libraries=('api-test' 'performance-test' 'project-management' 'system-setting' 'test-track' 'report-stat' 'workstation')
for library in "${libraries[@]}"; for library in "${libraries[@]}";
do do
mkdir -p $library/backend/target/dependency && (cd $library/backend/target/dependency; jar -xf ../*.jar; cp $LOCAL_REPOSITORY/io/metersphere/metersphere-xpack/${REVISION}/metersphere-xpack-${REVISION}.jar ./BOOT-INF/lib/) mkdir -p $library/backend/target/dependency && (cd $library/backend/target/dependency; jar -xf ../*.jar; cp $LOCAL_REPOSITORY/io/metersphere/metersphere-xpack/${REVISION}/metersphere-xpack-${REVISION}.jar ./BOOT-INF/lib/)
@ -119,7 +119,7 @@ pipeline {
try { try {
sh '''#!/bin/bash -xe sh '''#!/bin/bash -xe
cd ${WORKSPACE} cd ${WORKSPACE}
libraries=('framework/eureka' 'framework/gateway' 'api-test' 'performance-test' 'project-management' 'report-stat' 'system-setting' 'test-track') libraries=('framework/eureka' 'framework/gateway' 'api-test' 'performance-test' 'project-management' 'report-stat' 'system-setting' 'test-track' 'workstation')
for library in "${libraries[@]}"; for library in "${libraries[@]}";
do do
IMAGE_NAME=${library#*/} IMAGE_NAME=${library#*/}

View File

@ -94,6 +94,7 @@
<module>report-stat</module> <module>report-stat</module>
<module>system-setting</module> <module>system-setting</module>
<module>test-track</module> <module>test-track</module>
<module>workstation</module>
</modules> </modules>
<dependencies> <dependencies>

28
workstation/Dockerfile Normal file
View File

@ -0,0 +1,28 @@
FROM registry.cn-qingdao.aliyuncs.com/metersphere/alpine-openjdk11-jre
LABEL maintainer="FIT2CLOUD <support@fit2cloud.com>"
ARG MS_VERSION=dev
ARG DEPENDENCY=backend/target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
# html 文件
COPY backend/src/main/resources/public /app/public
# 静态文件
COPY backend/src/main/resources/static /app/static
RUN mv /app/lib/ms-jmeter-core-*.jar /app/lib/ms-jmeter-core.jar
ENV JAVA_CLASSPATH=/app:/app/lib/ms-jmeter-core.jar:/opt/jmeter/lib/ext/*:/app/lib/*
ENV JAVA_MAIN_CLASS=io.metersphere.WorkstationApplication
ENV AB_OFF=true
ENV MS_VERSION=${MS_VERSION}
ENV JAVA_OPTIONS="-Dfile.encoding=utf-8 -Djava.awt.headless=true --add-opens java.base/jdk.internal.loader=ALL-UNNAMED"
CMD ["/deployments/run-java.sh"]

36
workstation/backend/.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# Created by .ignore support plugin (hsz.mobi)
.DS_Store
node_modules
node/
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
*.iml
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
src/main/resources/static
src/main/resources/public
src/test/
target
.settings
.project
.classpath
.factorypath
src/main/resources/jmeter/lib/ext

123
workstation/backend/pom.xml Normal file
View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.metersphere</groupId>
<artifactId>workstation-parent</artifactId>
<version>${revision}</version>
</parent>
<artifactId>workstation</artifactId>
<dependencies>
<dependency>
<groupId>io.metersphere</groupId>
<artifactId>sdk</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.metersphere</groupId>
<artifactId>xpack-interface</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.json</include>
<include>**/*.tpl</include>
<include>**/*.js</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<configuration>
<filesets>
<fileset>
<directory>src/main/resources/static</directory>
<includes>
<include>**</include>
</includes>
<followSymlinks>false</followSymlinks>
</fileset>
<fileset>
<directory>src/main/resources/public</directory>
<includes>
<include>**</include>
</includes>
<followSymlinks>false</followSymlinks>
</fileset>
</filesets>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>main-class-placement</id>
<phase>generate-resources</phase>
<configuration>
<skip>${skipAntRunForJenkins}</skip>
<target>
<copy todir="src/main/resources/static">
<fileset dir="../frontend/dist">
<exclude name="*.html"/>
</fileset>
</copy>
<copy todir="src/main/resources/public">
<fileset dir="../frontend/dist">
<include name="*.html"/>
</fileset>
</copy>
<copy todir="src/main/resources/static/assets">
<fileset dir="../../framework/sdk-parent/frontend/src/assets"/>
</copy>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>${java.version}</release>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,34 @@
package io.metersphere;
import io.metersphere.config.JmeterProperties;
import io.metersphere.config.KafkaProperties;
import io.metersphere.config.MinioProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.PropertySource;
@SpringBootApplication(exclude = {
QuartzAutoConfiguration.class,
LdapAutoConfiguration.class,
Neo4jAutoConfiguration.class
})
@PropertySource(value = {
"classpath:commons.properties",
"file:/opt/metersphere/conf/metersphere.properties",
}, encoding = "UTF-8", ignoreResourceNotFound = true)
@ServletComponentScan
@EnableConfigurationProperties({
KafkaProperties.class,
MinioProperties.class,
JmeterProperties.class
})
public class WorkstationApplication {
public static void main(String[] args) {
SpringApplication.run(WorkstationApplication.class, args);
}
}

View File

@ -0,0 +1,16 @@
package io.metersphere.base.mapper.ext;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtApiDefinitionMapper {
int countByIds(@Param("ids") List<String> ids);
int getCountFollow(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
int getCountUpcoming(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
}

View File

@ -0,0 +1,589 @@
<?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.ext.ExtApiDefinitionMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.ApiDefinition">
<id column="id" jdbcType="VARCHAR" property="id"/>
<result column="project_id" jdbcType="VARCHAR" property="projectId"/>
<result column="module_id" jdbcType="VARCHAR" property="moduleId"/>
<result column="module_path" jdbcType="VARCHAR" property="modulePath"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="protocol" jdbcType="VARCHAR" property="protocol"/>
<result column="path" jdbcType="VARCHAR" property="path"/>
<result column="method" jdbcType="VARCHAR" property="method"/>
<result column="description" jdbcType="VARCHAR" property="description"/>
<result column="status" jdbcType="VARCHAR" property="status"/>
<result column="user_id" jdbcType="VARCHAR" property="userId"/>
<result column="create_time" jdbcType="BIGINT" property="createTime"/>
<result column="update_time" jdbcType="BIGINT" property="updateTime"/>
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.ApiDefinitionWithBLOBs">
<result column="request" jdbcType="LONGVARCHAR" property="request"/>
<result column="response" jdbcType="LONGVARCHAR" property="response"/>
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="("
separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="("
separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
id
, project_id, name,module_id,module_path,protocol ,path,method ,description, status, user_id, create_time, update_time
</sql>
<sql id="Blob_Column_List">
request
</sql>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and api_definition.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test='${condition}.id != null'>
and api_definition.num
<include refid="condition">
<property name="object" value="${condition}.id"/>
</include>
</if>
<if test="${condition}.followPeople != null">
and api_definition.id in (
select definition_id from api_definition_follow where follow_id
<include refid="condition">
<property name="object" value="${condition}.followPeople"/>
</include>
)
</if>
<if test="${condition}.updateTime != null">
and api_definition.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.projectName != null">
and project.name
<include refid="condition">
<property name="object" value="${condition}.projectName"/>
</include>
</if>
<if test="${condition}.createTime != null">
and api_definition.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.status != null">
and api_definition.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.isReference != null">
and api_definition.id
<if test='${condition}.isReference.value == "true"'>
in (SELECT reference_id FROM api_scenario_reference_id WHERE reference_id is not null )
</if>
<if test='${condition}.isReference.value == "false"'>
not in (SELECT reference_id FROM api_scenario_reference_id WHERE reference_id is not null )
</if>
</if>
<if test="${condition}.creator != null">
and api_definition.user_id
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
<if test="${condition}.path != null">
and api_definition.path
<include refid="condition">
<property name="object" value="${condition}.path"/>
</include>
</if>
<if test="${condition}.method != null">
and api_definition.method
<include refid="condition">
<property name="object" value="${condition}.method"/>
</include>
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "not like"'>
and (api_definition.tags is null or api_definition.tags
<include refid="condition">
<property name="object" value="${condition}.tags"/>
</include>
)
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "like"'>
and api_definition.tags
<include refid="condition">
<property name="object" value="${condition}.tags"/>
</include>
</if>
<if test="${condition}.module != null">
and api_definition.module_path
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.module"/>
</include>
</if>
<if test="${condition}.moduleIds != null">
and api_definition.module_id
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.moduleIds"/>
</include>
</if>
<if test="${condition}.caseCount != null">
and api_definition.case_total
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.caseCount"/>
</include>
</if>
</sql>
<sql id="condition">
<choose>
<when test='${object}.operator == "like"'>
like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "not like"'>
not like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "in"'>
in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "not in"'>
not in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "between"'>
between #{${object}.value[0]} and #{${object}.value[1]}
</when>
<when test='${object}.operator == "gt"'>
&gt; #{${object}.value}
</when>
<when test='${object}.operator == "lt"'>
&lt; #{${object}.value}
</when>
<when test='${object}.operator == "ge"'>
&gt;= #{${object}.value}
</when>
<when test='${object}.operator == "le"'>
&lt;= #{${object}.value}
</when>
<when test='${object}.operator == "current user"'>
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
</when>
<otherwise>
= #{${object}.value}
</otherwise>
</choose>
</sql>
<sql id="filter">
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='status'">
and api_definition.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='method'">
and api_definition.method in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='user_id'">
and api_definition.user_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='user_name'">
and api_definition.user_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='case_status'">
and api_definition.case_status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='version_id'">
and api_definition.version_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
</foreach>
</if>
</sql>
<sql id="queryWhereCondition">
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.name != null">
and (api_definition.name like CONCAT('%', #{request.name},'%')
or api_definition.tags like CONCAT('%', #{request.name},'%')
or api_definition.num like CONCAT('%', #{request.name},'%')
or api_definition.path like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.protocol != null">
AND api_definition.protocol = #{request.protocol}
</if>
<if test="request.notEqStatus != null">
and (api_definition.status is null or api_definition.status != #{request.notEqStatus})
</if>
<if test="request.id != null">
AND api_definition.id = #{request.id}
</if>
<if test="request.userId != null">
AND api_definition.user_id = #{request.userId}
</if>
<if test="request.createTime >0">
AND api_definition.create_time >= #{request.createTime}
</if>
<if test="request.moduleId != null">
AND api_definition.module_id = #{request.moduleId}
</if>
<if test="request.toBeUpdated != null and request.toBeUpdated==true">
AND api_definition.to_be_updated = #{request.toBeUpdated}
</if>
<if test="request.toBeUpdated != null and request.toBeUpdated==true and request.toBeUpdateTime != null">
AND api_definition.to_be_update_time >= #{request.toBeUpdateTime}
</if>
<if test="request.notInIds != null and request.notInIds.size() > 0">
and api_definition.id not in
<foreach collection="request.notInIds" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</if>
<choose>
<when test="request.moduleIds != null and request.moduleIds.size() > 0">
AND api_definition.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</when>
<when test="request.projectId != null">
AND api_definition.project_id = #{request.projectId}
</when>
</choose>
<include refid="filter"/>
<if test="request.apiCoverage == 'unCovered'">
and
api_definition.id not in (SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status`
!= 'Trash')
<if test=" request.coverageIds != null and request.coverageIds.size() > 0">
and
api_definition.id not in
<foreach collection="request.coverageIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
</if>
<if test="request.apiCoverage == 'coverage' ">
and
(
api_definition.id in (SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status` !=
'Trash')
<if test=" request.coverageIds != null and request.coverageIds.size() > 0">
or
api_definition.id in
<foreach collection="request.coverageIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
)
</if>
<if test="request.apiCaseCoverage == 'unCovered' ">
and api_definition.id not in
(SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status` != 'Trash')
</if>
<if test="request.apiCaseCoverage == 'coverage' ">
and api_definition.id in
(SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status` != 'Trash')
</if>
<if test="request.scenarioCoverage == 'unCovered' ">
<if test=" request.coverageIds != null and request.coverageIds.size() > 0">
and api_definition.id not in
<foreach collection="request.coverageIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
</if>
<if test="request.scenarioCoverage == 'coverage' ">
<if test=" request.coverageIds != null and request.coverageIds.size() > 0">
and api_definition.id in
<foreach collection="request.coverageIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
</if>
<include refid="queryVersionCondition">
<property name="versionTable" value="api_definition"/>
</include>
</where>
</sql>
<sql id="queryWhereConditionWidthProject">
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.name != null">
and (api_definition.name like CONCAT('%', #{request.name},'%')
or api_definition.tags like CONCAT('%', #{request.name},'%')
or api_definition.num like CONCAT('%', #{request.name},'%')
or api_definition.path like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.protocol != null">
AND api_definition.protocol = #{request.protocol}
</if>
<if test="request.notEqStatus != null">
and (api_definition.status is null or api_definition.status != #{request.notEqStatus})
</if>
<if test="request.id != null">
AND api_definition.id = #{request.id}
</if>
<if test="request.userId != null">
AND api_definition.user_id = #{request.userId}
</if>
<if test="request.createTime >0">
AND api_definition.create_time >= #{request.createTime}
</if>
<if test="request.moduleId != null">
AND api_definition.module_id = #{request.moduleId}
</if>
<if test="request.notInIds != null and request.notInIds.size() > 0">
and api_definition.id not in
<foreach collection="request.notInIds" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</if>
<choose>
<when test="request.moduleIds != null and request.moduleIds.size() > 0">
AND api_definition.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</when>
<when test="request.projectId != null">
AND api_definition.project_id = #{request.projectId}
</when>
<when test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</when>
</choose>
<include refid="filter"/>
<if test="request.apiCoverage == 'unCovered' ">
and
api_definition.id not in (SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status`
!= 'Trash')
<if test=" request.coverageIds != null and request.coverageIds.size() > 0">
and
api_definition.id not in
<foreach collection="request.coverageIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
</if>
<if test="request.apiCoverage == 'coverage' ">
and
(
api_definition.id in (SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status` !=
'Trash')
<if test=" request.coverageIds != null and request.coverageIds.size() > 0">
or
api_definition.id in
<foreach collection="request.coverageIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
)
</if>
<if test="request.apiCaseCoverage == 'unCovered' ">
and api_definition.id not in
(SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status` != 'Trash')
</if>
<if test="request.apiCaseCoverage == 'coverage' ">
and api_definition.id in
(SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status` != 'Trash')
</if>
<if test="request.scenarioCoverage == 'unCovered' ">
<if test=" request.coverageIds != null and request.coverageIds.size() > 0">
and api_definition.id not in
<foreach collection="request.coverageIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
</if>
<if test="request.scenarioCoverage == 'coverage' ">
<if test=" request.coverageIds != null and request.coverageIds.size() > 0">
and api_definition.id in
<foreach collection="request.coverageIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
</if>
<include refid="queryVersionCondition">
<property name="versionTable" value="api_definition"/>
</include>
</where>
</sql>
<select id="countByIds" resultType="java.lang.Integer">
select count(id) from api_definition
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
and api_definition.status != 'Trash';
</select>
<select id="getCountFollow" resultType="java.lang.Integer">
select count(*) from api_definition ad where
ad.id in (select af.definition_id from api_definition_follow af where af.follow_id = #{userId,jdbcType=VARCHAR})
and
ad.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and (ad.status is null or ad.status != 'Trash')
</select>
<select id="getCountUpcoming" resultType="java.lang.Integer">
select count(*) from api_definition ad where
ad.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and ad.status in ('Prepare', 'Underway')
and ad.user_id = #{userId,jdbcType=VARCHAR}
</select>
<sql id="queryVersionCondition">
<if test="request.versionId != null">
and ${versionTable}.version_id = #{request.versionId}
</if>
<if test="request.refId != null">
and ${versionTable}.ref_id = #{request.refId}
</if>
<if test="request.versionId == null and request.refId == null and request.id == null">
AND ${versionTable}.latest = 1
</if>
</sql>
<sql id="Same_Where_Clause">
<where>
<if test="blobs">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="blobs" item="blob" separator="or">
<trim prefix="(" prefixOverrides="and" suffix=")">
<if test="blob.method">
and api_definition.method = #{blob.method}
</if>
<if test="blob.path">
and api_definition.path = #{blob.path}
</if>
</trim>
</foreach>
</trim>
</if>
</where>
</sql>
</mapper>

View File

@ -0,0 +1,17 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.request.api.ApiScenarioRequest;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtApiScenarioMapper {
int listModule(@Param("request") ApiScenarioRequest request);
int getCountFollow(@Param("projectIds") List<String> projectIds,@Param("userId") String userId);
int getCountUpcoming(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
}

View File

@ -0,0 +1,397 @@
<?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.ext.ExtApiScenarioMapper">
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="("
separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="condition">
<choose>
<when test='${object}.operator == "like"'>
like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "not like"'>
not like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "in"'>
in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "not in"'>
not in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "between"'>
between #{${object}.value[0]} and #{${object}.value[1]}
</when>
<when test='${object}.operator == "gt"'>
&gt; #{${object}.value}
</when>
<when test='${object}.operator == "lt"'>
&lt; #{${object}.value}
</when>
<when test='${object}.operator == "ge"'>
&gt;= #{${object}.value}
</when>
<when test='${object}.operator == "le"'>
&lt;= #{${object}.value}
</when>
<when test='${object}.operator == "current user"'>
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
</when>
<otherwise>
= #{${object}.value}
</otherwise>
</choose>
</sql>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and api_scenario.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test='${condition}.id != null'>
and (api_scenario.num
<include refid="condition">
<property name="object" value="${condition}.id"/>
</include>
or api_scenario.custom_num
<include refid="condition">
<property name="object" value="${condition}.id"/>
</include>
)
</if>
<if test="${condition}.followPeople != null">
and api_scenario.id in (
select scenario_id from api_scenario_follow where follow_id
<include refid="condition">
<property name="object" value="${condition}.followPeople"/>
</include>
)
</if>
<if test="${condition}.updateTime != null">
and api_scenario.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.createTime != null">
and api_scenario.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.priority != null">
and api_scenario.level
<include refid="condition">
<property name="object" value="${condition}.priority"/>
</include>
</if>
<if test="${condition}.creator != null">
and api_scenario.create_user
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "not like"'>
and (api_scenario.tags is null or api_scenario.tags
<include refid="condition">
<property name="object" value="${condition}.tags"/>
</include>
)
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "like"'>
and api_scenario.tags
<include refid="condition">
<property name="object" value="${condition}.tags"/>
</include>
</if>
<if test="${condition}.lastResult != null">
and api_scenario.last_result
<include refid="condition">
<property name="object" value="${condition}.lastResult"/>
</include>
</if>
<if test="${condition}.status != null">
and api_scenario.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.module != null">
and api_scenario.module_path
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.module"/>
</include>
</if>
<if test="${condition}.moduleIds != null">
and api_scenario.api_scenario_module_id
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.moduleIds"/>
</include>
</if>
<if test="${condition}.stepCount != null">
and api_scenario.step_total
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.stepCount"/>
</include>
</if>
</sql>
<select id="listModule" resultType="java.lang.Integer">
select count(api_scenario.id) from api_scenario
left join project on api_scenario.project_id = project.id
<include refid="queryWhereCondition"/>
</select>
<select id="getCountFollow" resultType="java.lang.Integer">
select count(*) from api_scenario sc where
sc.id in (select sf.scenario_id from api_scenario_follow sf where sf.follow_id = #{userId,jdbcType=VARCHAR})
and
sc.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and (sc.status is null or sc.status != 'Trash');
</select>
<select id="getCountUpcoming" resultType="java.lang.Integer">
select count(*) from api_scenario sc where
sc.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and sc.status in ('Prepare', 'Underway')
and sc.principal = #{userId,jdbcType=VARCHAR}
</select>
<sql id="queryWhereCondition">
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.name != null">
and (api_scenario.name like CONCAT('%', #{request.name},'%')
or api_scenario.tags like CONCAT('%', #{request.name},'%')
or api_scenario.num like CONCAT('%', #{request.name},'%')
or api_scenario.custom_num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.notEqStatus != null">
and (api_scenario.status is null or api_scenario.status != #{request.notEqStatus})
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</if>
<if test="request.id != null">
AND api_scenario.id = #{request.id}
</if>
<if test="request.userId != null">
AND api_scenario.user_id = #{request.userId}
</if>
<if test="request.moduleId != null">
AND api_scenario.api_scenario_module_id = #{request.moduleId}
</if>
<if test="request.projectId != null">
AND api_scenario.project_id = #{request.projectId}
</if>
<if test="request.createTime >0 ">
AND api_scenario.create_time >= #{request.createTime}
</if>
<if test="request.scheduleCreateTime >0 ">
AND api_scenario.id IN
(
SELECT resource_id FROM `schedule`
WHERE `group` = 'API_SCENARIO_TEST'
AND create_time >= #{request.scheduleCreateTime}
)
</if>
<if test="request.ids != null and request.ids.size() > 0">
AND api_scenario.id in
<foreach collection="request.ids" item="itemId" separator="," open="(" close=")">
#{itemId}
</foreach>
</if>
<choose>
<when test="request.moduleIds != null and request.moduleIds.size() > 0">
AND api_scenario.api_scenario_module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</when>
</choose>
<include refid="filter"/>
<if test="request.filters == null || request.filters.size() == 0 ">
and (api_scenario.status is null or api_scenario.status != 'Trash')
</if>
<if test="request.executeStatus == 'unExecute' and request.selectDataType != 'schedule' ">
and (api_scenario.last_result IS NULL or api_scenario.last_result = 'unexecute' or api_scenario.last_result = '')
</if>
<if test="request.executeStatus == 'executeFailed' and request.selectDataType != 'schedule' ">
and api_scenario.last_result = 'Fail'
</if>
<if test="request.executeStatus == 'executePass' and request.selectDataType != 'schedule' ">
and api_scenario.last_result = 'Success'
</if>
<if test="request.executeStatus == 'fakeError' and request.selectDataType != 'schedule' ">
and api_scenario.last_result = 'errorReportResult'
</if>
<if test="request.executeStatus == 'notSuccess' and request.selectDataType != 'schedule' ">
and api_scenario.last_result IN ('Fail','errorReportResult')
</if>
<if test="request.executeStatus == 'executeFailed' and request.selectDataType == 'schedule' ">
AND api_scenario.id IN
(SELECT source_id FROM scenario_execution_info WHERE trigger_mode = 'SCHEDULE' AND result = 'Error')
</if>
<if test="request.executeStatus == 'executePass' and request.selectDataType == 'schedule' ">
AND api_scenario.id IN
(SELECT source_id FROM scenario_execution_info WHERE trigger_mode = 'SCHEDULE' AND result = 'Success')
</if>
<if test="request.executeStatus == 'fakeError' and request.selectDataType == 'schedule' ">
AND api_scenario.id IN
(SELECT source_id FROM scenario_execution_info WHERE trigger_mode = 'SCHEDULE' AND result =
'errorReportResult')
</if>
<if test="request.executeStatus == 'notSuccess' and request.selectDataType == 'schedule' ">
AND api_scenario.id IN
(SELECT source_id FROM scenario_execution_info WHERE trigger_mode = 'SCHEDULE' AND result IN
('errorReportResult','Error'))
</if>
<if test="request.notInTestPlan">
and api_scenario.id not in (
select pc.api_scenario_id
from test_plan_api_scenario pc
where pc.test_plan_id = #{request.planId}
)
</if>
<if test="request.stepTotal !=null">
and api_scenario.step_total is not null and api_scenario.step_total &gt; 0
</if>
<include refid="queryVersionCondition">
<property name="versionTable" value="api_scenario"/>
</include>
</where>
</sql>
<sql id="filter">
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='status'">
and api_scenario.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='user_id'">
and api_scenario.user_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='level'">
and api_scenario.level in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='last_result'">
and api_scenario.last_result in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='project_id'">
and api_scenario.project_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='principal'">
and api_scenario.principal in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='principal_name'">
and api_scenario.principal in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='user_name'">
and api_scenario.user_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='creator' or key=='creator_name'">
and api_scenario.create_user in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='version_id'">
and api_scenario.version_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
</foreach>
</if>
</sql>
<sql id="queryVersionCondition">
<if test="request.versionId != null">
and ${versionTable}.version_id = #{request.versionId}
</if>
<if test="request.refId != null">
and ${versionTable}.ref_id = #{request.refId}
</if>
<if test="request.versionId == null and request.refId == null and request.id == null">
AND ${versionTable}.latest = 1
</if>
</sql>
</mapper>

View File

@ -0,0 +1,18 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.request.api.ApiTestCaseRequest;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtApiTestCaseMapper {
int moduleCount(@Param("request") ApiTestCaseRequest request);
int getCountFollow(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
int getUpdateCount(@Param("userId") String userId,@Param("projectId") String projectId,@Param("statusList") List<String> statusList,@Param("toBeUpdateTime") Long toBeUpdatedTime);
int getCountUpcoming(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
}

View File

@ -0,0 +1,415 @@
<?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.ext.ExtApiTestCaseMapper">
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="("
separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="("
separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
id
, project_id, name,api_definition_id,priority,description, create_user_id, update_user_id, create_time, update_time
</sql>
<sql id="Blob_Column_List">
request
</sql>
<sql id="condition">
<choose>
<when test='${object}.operator == "like"'>
like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "not like"'>
not like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "in"'>
in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
<if test="${object}.value.contains(''.toString())">
or t3.status is null
</if>
<if test="${condition}.exec_result != null">
)
</if>
</when>
<when test='${object}.operator == "not in"'>
not in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
<if test="${condition}.exec_result != null">
)
</if>
</when>
<when test='${object}.operator == "between"'>
between #{${object}.value[0]} and #{${object}.value[1]}
</when>
<when test='${object}.operator == "gt"'>
&gt; #{${object}.value}
</when>
<when test='${object}.operator == "lt"'>
&lt; #{${object}.value}
</when>
<when test='${object}.operator == "ge"'>
&gt;= #{${object}.value}
</when>
<when test='${object}.operator == "le"'>
&lt;= #{${object}.value}
</when>
<when test='${object}.operator == "current user"'>
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
</when>
<otherwise>
= #{${object}.value}
</otherwise>
</choose>
</sql>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and t1.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test='${condition}.id != null'>
and t1.num
<include refid="condition">
<property name="object" value="${condition}.id"/>
</include>
</if>
<if test="${condition}.followPeople != null">
and t1.id in (
select case_id from api_test_case_follow where follow_id
<include refid="condition">
<property name="object" value="${condition}.followPeople"/>
</include>
)
</if>
<if test="${condition}.updateTime != null">
and t1.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.createTime != null">
and t1.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.priority != null">
and t1.priority
<include refid="condition">
<property name="object" value="${condition}.priority"/>
</include>
</if>
<if test="${condition}.creator != null">
and t1.create_user_id
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "not like"'>
and (t1.tags is null or t1.tags
<include refid="condition">
<property name="object" value="${condition}.tags"/>
</include>
)
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "like"'>
and t1.tags
<include refid="condition">
<property name="object" value="${condition}.tags"/>
</include>
</if>
<if test="${condition}.isReference != null">
and t1.id
<if test='${condition}.isReference.value == "true"'>
in (SELECT reference_id FROM api_scenario_reference_id WHERE reference_id is not null )
</if>
<if test='${condition}.isReference.value == "false"'>
not in (SELECT reference_id FROM api_scenario_reference_id WHERE reference_id is not null )
</if>
</if>
<if test="${condition}.path != null">
and a.path
<include refid="condition">
<property name="object" value="${condition}.path"/>
</include>
</if>
<if test="${condition}.status != null">
and t1.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.exec_result != null">
and (t3.status
<include refid="condition">
<property name="object" value="${condition}.exec_result"/>
</include>
</if>
</sql>
<sql id="countCombine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and api_test_case.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.followPeople != null">
and api_test_case.id in (
select case_id from api_test_case_follow where follow_id
<include refid="condition">
<property name="object" value="${condition}.followPeople"/>
</include>
)
</if>
<if test="${condition}.updateTime != null">
and api_test_case.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.createTime != null">
and api_test_case.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.priority != null">
and api_test_case.priority
<include refid="condition">
<property name="object" value="${condition}.priority"/>
</include>
</if>
<if test="${condition}.creator != null">
and api_test_case.create_user_id
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "not like"'>
and (api_test_case.tags is null or api_test_case.tags
<include refid="condition">
<property name="object" value="${condition}.tags"/>
</include>
)
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "like"'>
and api_test_case.tags
<include refid="condition">
<property name="object" value="${condition}.tags"/>
</include>
</if>
<if test="${condition}.isReference != null">
and api_test_case.id
<if test='${condition}.isReference.value == "true"'>
in (SELECT reference_id FROM api_scenario_reference_id WHERE reference_id is not null )
</if>
<if test='${condition}.isReference.value == "false"'>
not in (SELECT reference_id FROM api_scenario_reference_id WHERE reference_id is not null )
</if>
</if>
</sql>
<select id="moduleCount" resultType="java.lang.Integer">
SELECT count(api_test_case.id) FROM api_test_case
inner join project on api_test_case.project_id = project.id
inner JOIN api_definition a ON api_test_case.api_definition_id = a.id
<include refid="criCondition"/>
and a.latest = 1
</select>
<select id="getCountFollow" resultType="java.lang.Integer">
select count(*) from api_test_case ac where
ac.id in (select af.case_id from api_test_case_follow af where af.follow_id = #{userId,jdbcType=VARCHAR})
and
ac.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and (ac.status is null or ac.status != 'Trash');
</select>
<select id="getUpdateCount" resultType="java.lang.Integer">
select count(*) from api_test_case ac where ac.project_id = #{projectId,jdbcType=VARCHAR} and ac.create_user_id = #{userId,jdbcType=VARCHAR}
and case_status in ('Prepare', 'Underway', 'Completed')
<if test="statusList !=null and statusList.size() > 0">
and ( ac.to_be_updated = 'true' or ac.status
in <foreach collection="statusList" item="value" separator="," open="(" close=")">
#{value}
</foreach>)
</if>
<if test="statusList ==null or statusList.size() == 0">
and ac.to_be_updated = 'true'
</if>
<if test="toBeUpdateTime !=null and statusList !=null and statusList.size() > 0">
and (ac.to_be_update_time >= #{toBeUpdateTime} or ( ac.status in
<foreach collection="statusList" item="value" separator="," open="(" close=")">
#{value}
</foreach>
))
</if>
<if test="toBeUpdateTime !=null and (statusList ==null or statusList.size() == 0)">
and ac.to_be_update_time >= #{toBeUpdateTime}
</if>
</select>
<select id="getCountUpcoming" resultType="java.lang.Integer">
select count(*) from api_test_case ac where
ac.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and ac.create_user_id = #{userId,jdbcType=VARCHAR}
and case_status in ('Prepare', 'Underway')
and (ac.status is null or ac.status != 'Trash');
</select>
<sql id="queryWhereCondition">
<where>
<if test="request.name != null and request.name!=''">
and t1.name like CONCAT('%', #{request.name},'%')
</if>
<if test="request.id != null and request.id!=''">
AND t1.id = #{request.id}
</if>
<if test="request.priority != null and request.priority!=''">
AND t1.priority = #{request.priority}
</if>
<if test="request.projectId != null and request.projectId!=''">
AND t1.project_id = #{request.projectId}
</if>
<if test="request.apiDefinitionId != null and request.apiDefinitionId!=''">
AND t1.api_definition_id = #{request.apiDefinitionId}
</if>
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key == 'priority'">
and t1.priority in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='status'">
and t1.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
<if test="key=='status' and values.size == 0">
and (t1.status is null or t1.status != 'Trash')
</if>
</foreach>
</if>
<if test="request.filters == null || request.filters.size() == 0 ">
and (t1.status is null or t1.status != 'Trash')
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and a.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.protocol != null and request.protocol !=''">
and a.protocol = #{request.protocol}
</if>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
</where>
</sql>
<sql id="criCondition">
<where>
<if test="request.combine != null">
<include refid="countCombine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</if>
<if test="request.filters == null || request.filters.size() == 0 ">
and (api_test_case.status is null or api_test_case.status != 'Trash')
</if>
</where>
</sql>
<sql id="queryVersionCondition">
<if test="request.versionId != null">
and ${versionTable}.version_id = #{request.versionId}
</if>
<if test="request.versionId == null and request.id == null">
AND a.latest = 1
</if>
</sql>
</mapper>

View File

@ -0,0 +1,17 @@
package io.metersphere.base.mapper.ext;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtIssuesMapper {
int getCountFollow(@Param("projectIds") List<String> projectIds,@Param("userId") String userId);
int getCountUpcoming(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
int getCountCreat(@Param("projectIds") List<String> projectIds, @Param("userId") String userId,@Param("createTime") Long createTime);
}

View File

@ -0,0 +1,251 @@
<?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.ext.ExtIssuesMapper">
<select id="getCountFollow" resultType="java.lang.Integer">
select count(*) from issues ss where
ss.id in (select sf.issue_id from issue_follow sf where sf.follow_id = #{userId,jdbcType=VARCHAR})
and
ss.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and (ss.status is null or ss.status != 'Trash');
</select>
<select id="getCountUpcoming" resultType="java.lang.Integer">
select count(*) from issues ss where
ss.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and ss.status = 'new'
and ss.creator = #{userId,jdbcType=VARCHAR}
</select>
<select id="getCountCreat" resultType="java.lang.Integer">
select count(*) from issues ss where
ss.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and ss.create_time >= #{createTime}
and ss.creator = #{userId,jdbcType=VARCHAR}
</select>
<sql id="Issue_List_Column">
issues.id, issues.platform_id, issues.num, ifnull(issues.title, '') as title, issues.project_id, issues.create_time, issues.update_time,
ifnull(issues.description, '') as description, issues.status, issues.platform, issues.custom_fields, issues.reporter,
issues.creator,issues.resource_id,issues.platform_status,
issues.lastmodify
</sql>
<sql id="queryWhereCondition">
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.name != null">
and (
issues.title LIKE CONCAT('%', #{request.name}, '%')
or issues.num LIKE CONCAT('%', #{request.name}, '%')
)
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</if>
<if test="request.projectId != null and request.projectId != ''">
and issues.project_id = #{request.projectId}
</if>
<if test="request.resourceId != null and request.resourceId != ''">
and issues.resource_id = #{request.resourceId}
</if>
<if test="request.caseResourceId != null and request.caseResourceId != ''">
<if test="request.refType == 'FUNCTIONAL'">
and (test_case_issues.resource_id = #{request.caseResourceId} or test_case_issues.ref_id = #{request.caseResourceId})
</if>
<if test="request.refType == 'PLAN_FUNCTIONAL'">
and test_case_issues.resource_id = #{request.caseResourceId} and test_case_issues.ref_type
='PLAN_FUNCTIONAL'
</if>
</if>
<if test="request.platform != null and request.platform != ''">
and issues.platform = #{request.platform}
</if>
<if test="request.id != null and request.id != ''">
and issues.id = #{request.id}
</if>
<if test="request.notInIds != null and request.notInIds.size() > 0">
and issues.id not in
<foreach collection="request.notInIds" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</if>
<if test="!request.selectAll and request.exportIds != null and request.exportIds.size > 0">
and issues.id in
<foreach collection="request.exportIds" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</if>
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='status'">
and issues.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key == 'platform'">
AND issues.platform IN
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key == 'creator'">
AND issues.creator IN
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key.startsWith('custom_single')">
and issues.id in (
select resource_id from custom_field_issues
where concat('custom_single-',field_id) = #{key}
and trim(both '"' from value) in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
)
</when>
<when test="key.startsWith('custom_multiple')">
and issues.id in (
select resource_id from custom_field_issues
where concat('custom_multiple-',field_id) = #{key}
and
<foreach collection="values" item="value" separator="or" open="(" close=")">
JSON_CONTAINS(value, #{value})
</foreach>
)
</when>
</choose>
</if>
</foreach>
</if>
and (issues.platform_status != 'delete' or issues.platform_status is NULL)
</where>
</sql>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and issues.title
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.creator != null">
and issues.creator
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
<if test="${condition}.platform != null">
and issues.platform
<include refid="condition">
<property name="object" value="${condition}.platform"/>
</include>
</if>
<if test="${condition}.followPeople != null">
and issues.id in (
select issue_id from issue_follow where follow_id
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.followPeople"/>
</include>
)
</if>
<if test="${condition}.createTime != null">
and issues.create_time
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.customs != null and ${condition}.customs.size() > 0">
<foreach collection="${condition}.customs" item="custom" separator="" open="" close="">
and issues.id in (
select resource_id from custom_field_issues where field_id = #{custom.id}
<choose>
<when test="custom.type == 'multipleMember' or custom.type == 'checkbox' or custom.type == 'multipleSelect'">
and ${custom.value}
</when>
<when test="custom.type == 'date' or custom.type == 'datetime'">
and left(replace(unix_timestamp(trim(both '"' from `value`)), '.', ''), 13)
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="custom"/>
</include>
</when>
<when test="custom.type == 'richText' or custom.type == 'textarea'">
and text_value
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="custom"/>
</include>
</when>
<otherwise>
and trim(both '"' from value)
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="custom"/>
</include>
</otherwise>
</choose>
)
</foreach>
</if>
</sql>
<sql id="condition">
<choose>
<when test='${object}.operator == "like"'>
like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "not like"'>
not like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "in"'>
in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "not in"'>
not in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "between"'>
between #{${object}.value[0]} and #{${object}.value[1]}
</when>
<when test='${object}.operator == "gt"'>
&gt; #{${object}.value}
</when>
<when test='${object}.operator == "lt"'>
&lt; #{${object}.value}
</when>
<when test='${object}.operator == "ge"'>
&gt;= #{${object}.value}
</when>
<when test='${object}.operator == "le"'>
&lt;= #{${object}.value}
</when>
<when test='${object}.operator == "current user"'>
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
</when>
<otherwise>
= #{${object}.value}
</otherwise>
</choose>
</sql>
</mapper>

View File

@ -0,0 +1,16 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.request.track.QueryTestPlanRequest;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtLoadTestMapper {
int moduleCount(@Param("request") QueryTestPlanRequest request);
int getCountFollow(@Param("projectIds") List<String> projectIds,@Param("userId") String userId);
int getCountUpcoming(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
}

View File

@ -0,0 +1,281 @@
<?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.ext.ExtLoadTestMapper">
<sql id="condition">
<choose>
<when test='${object}.operator == "like"'>
LIKE CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "not like"'>
NOT LIKE CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "in"'>
IN
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "not in"'>
NOT IN
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "between"'>
BETWEEN #{${object}.value[0]} AND #{${object}.value[1]}
</when>
<when test='${object}.operator == "gt"'>
&gt; #{${object}.value}
</when>
<when test='${object}.operator == "lt"'>
&lt; #{${object}.value}
</when>
<when test='${object}.operator == "ge"'>
&gt;= #{${object}.value}
</when>
<when test='${object}.operator == "le"'>
&lt;= #{${object}.value}
</when>
<when test='${object}.operator == "current user"'>
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
</when>
<otherwise>
= #{${object}.value}
</otherwise>
</choose>
</sql>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
AND load_test.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test='${condition}.id != null'>
and load_test.num
<include refid="condition">
<property name="object" value="${condition}.id"/>
</include>
</if>
<if test="${condition}.followPeople != null">
and load_test.id in (
select test_id from load_test_follow where follow_id
<include refid="condition">
<property name="object" value="${condition}.followPeople"/>
</include>
)
</if>
<if test="${condition}.updateTime != null">
AND load_test.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.projectName != null">
AND project.name
<include refid="condition">
<property name="object" value="${condition}.projectName"/>
</include>
</if>
<if test="${condition}.createTime != null">
AND load_test.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.status != null">
AND load_test.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.creator != null">
AND load_test.user_id
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
</sql>
<sql id="countCombine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
AND load_test.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.followPeople != null">
and load_test.id in (
select test_id from load_test_follow where follow_id
<include refid="condition">
<property name="object" value="${condition}.followPeople"/>
</include>
)
</if>
<if test="${condition}.updateTime != null">
AND load_test.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.createTime != null">
AND load_test.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.status != null">
AND load_test.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.creator != null">
AND load_test.create_user
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
</sql>
<select id="moduleCount" resultType="java.lang.Integer">
select count(load_test.id) from load_test
left join project on load_test.project_id = project.id
<where>
<if test="request.combine != null">
<include refid="countCombine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</if>
</where>
</select>
<select id="getCountFollow" resultType="java.lang.Integer">
select count(*) from load_test lt where
lt.id in (select lf.test_id from load_test_follow lf where lf.follow_id = #{userId,jdbcType=VARCHAR})
and
lt.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and (lt.status is null or lt.status != 'Trash');
</select>
<select id="getCountUpcoming" resultType="java.lang.Integer">
select count(*) from load_test lt where
lt.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and lt.status = 'error'
and lt.user_id = #{userId,jdbcType=VARCHAR}
</select>
<sql id="filter">
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='status'">
and load_test.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='method'">
and load_test.method in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='user_id'">
and load_test.user_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='user_name'">
and load_test.user_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='case_status'">
and load_test.case_status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='version_id'">
and load_test.version_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
</foreach>
</if>
</sql>
<sql id="queryWhereCondition">
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.name != null">
and (load_test.name like CONCAT('%', #{request.name},'%')
or load_test.num like CONCAT('%', #{request.name},'%')
</if>
<if test="request.notEqStatus != null">
and (load_test.status is null or load_test.status != #{request.notEqStatus})
</if>
<if test="request.id != null">
AND load_test.id = #{request.id}
</if>
<if test="request.userId != null">
AND load_test.user_id = #{request.userId}
</if>
<if test="request.createTime >0">
AND load_test.create_time >= #{request.createTime}
</if>
<if test="request.notInIds != null and request.notInIds.size() > 0">
and load_test.id not in
<foreach collection="request.notInIds" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</if>
<if test="request.projectId != null">
AND load_test.project_id = #{request.projectId}
</if>
<include refid="filter"/>
<include refid="queryVersionCondition">
<property name="versionTable" value="load_test"/>
</include>
</where>
</sql>
<sql id="queryVersionCondition">
<if test="request.versionId != null">
and ${versionTable}.version_id = #{request.versionId}
</if>
<if test="request.refId != null">
and ${versionTable}.ref_id = #{request.refId}
</if>
<if test="request.versionId == null and request.refId == null and request.id == null">
AND ${versionTable}.latest = 1
</if>
</sql>
</mapper>

View File

@ -0,0 +1,14 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.ProjectApplication;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtProjectApplicationMapper {
List<ProjectApplication>selectByProjectIdAndType(@Param("projectId")String projectId,@Param("type")String type);
}

View File

@ -0,0 +1,11 @@
<?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.ext.ExtProjectApplicationMapper">
<select id="selectByProjectIdAndType" resultType="io.metersphere.base.domain.ProjectApplication">
select * from project_application where project_application.project_id = #{projectId,jdbcType=VARCHAR}
and project_application.type = #{type,jdbcType=VARCHAR};
</select>
</mapper>

View File

@ -0,0 +1,15 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.Project;
import io.metersphere.base.domain.Workspace;
import io.metersphere.dto.ProjectDTO;
import io.metersphere.request.ProjectRequest;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface ExtProjectMapper {
List<String> getProjectIdByWorkspaceId(String workspaceId);
}

View File

@ -0,0 +1,78 @@
<?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.ext.ExtProjectMapper">
<sql id="condition">
<choose>
<when test='${object}.operator == "like"'>
LIKE CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "not like"'>
NOT LIKE CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "in"'>
IN
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "not in"'>
NOT IN
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "between"'>
BETWEEN #{${object}.value[0]} AND #{${object}.value[1]}
</when>
<when test='${object}.operator == "gt"'>
&gt; #{${object}.value}
</when>
<when test='${object}.operator == "lt"'>
&lt; #{${object}.value}
</when>
<when test='${object}.operator == "ge"'>
&gt;= #{${object}.value}
</when>
<when test='${object}.operator == "le"'>
&lt;= #{${object}.value}
</when>
<when test='${object}.operator == "current user"'>
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
</when>
<otherwise>
= #{${object}.value}
</otherwise>
</choose>
</sql>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
AND p.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.updateTime != null">
AND p.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.createTime != null">
AND p.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.creator != null">
AND p.create_user
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
</sql>
<select id="getProjectIdByWorkspaceId" resultType="java.lang.String">
SELECT id
FROM project
WHERE workspace_id = #{workspaceId}
</select>
</mapper>

View File

@ -0,0 +1,17 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.request.track.QueryTestCaseRequest;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtTestCaseMapper {
int moduleCount(@Param("request") QueryTestCaseRequest request);
int getCountFollow(@Param("projectIds") List<String> projectIds,@Param("userId") String userId);
int getCountUpcoming(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
}

View File

@ -0,0 +1,487 @@
<?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.ext.ExtTestCaseMapper">
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and test_case.name
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test='${condition}.id != null'>
and (test_case.num
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.id"/>
</include>
or test_case.custom_num
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.id"/>
</include>
)
</if>
<if test="${condition}.followPeople != null">
and test_case.id in (
select case_id from test_case_follow where follow_id
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.followPeople"/>
</include>
)
</if>
<if test="${condition}.module != null">
and test_case.node_path
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.module"/>
</include>
</if>
<if test="${condition}.moduleIds != null">
and test_case.node_id
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.moduleIds"/>
</include>
</if>
<if test="${condition}.priority != null">
and test_case.priority
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.priority"/>
</include>
</if>
<if test="${condition}.createTime != null">
and test_case.create_time
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.type != null">
and test_case.type
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.type"/>
</include>
</if>
<if test="${condition}.updateTime != null">
and test_case.update_time
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.method != null">
and test_case.method
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.method"/>
</include>
</if>
<if test="${condition}.creator != null">
and test_case.create_user
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "not like"'>
and (test_case.tags is null or test_case.tags
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.tags"/>
</include>
)
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "like"'>
and test_case.tags
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.tags"/>
</include>
</if>
<if test="${condition}.reviewStatus != null">
and test_case.review_status
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.reviewStatus"/>
</include>
</if>
<if test="${condition}.demand != null">
<if test="${condition}.demand.operator == 'third_platform'">
and test_case.demand_id in
<foreach collection="${condition}.demand.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</if>
<if test="${condition}.demand.operator == 'other_platform'">
and test_case.demand_name like CONCAT('%', #{${condition}.demand.value},'%')
</if>
</if>
<if test="${condition}.customs != null and ${condition}.customs.size() > 0">
<foreach collection="${condition}.customs" item="custom" separator="" open="" close="">
and test_case.id ${custom.operator} (
select resource_id from custom_field_test_case where field_id = #{custom.id}
<choose>
<when test="custom.type == 'multipleMember' or custom.type == 'checkbox' or custom.type == 'multipleSelect'">
and ${custom.value}
</when>
<when test="custom.type == 'date' or custom.type == 'datetime'">
and left(replace(unix_timestamp(trim(both '"' from `value`)), '.', ''), 13)
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="custom"/>
</include>
</when>
<when test="custom.type == 'richText' or custom.type == 'textarea'">
and text_value
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="custom"/>
</include>
</when>
<otherwise>
and trim(both '"' from value)
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="custom"/>
</include>
</otherwise>
</choose>
)
</foreach>
</if>
</sql>
<sql id="notInQueryWhereCondition">
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.testCaseContainIds != null and request.testCaseContainIds.size() > 0">
and test_case.id not in
<foreach collection="request.testCaseContainIds" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</if>
<if test="request.name != null">
and (test_case.name like CONCAT('%', #{request.name},'%')
or test_case.num like CONCAT('%', #{request.name},'%')
or test_case.tags like CONCAT('%', #{request.name},'%')
or test_case.custom_num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.projectId != null">
AND test_case.project_id = #{request.projectId}
</if>
<if test="request.nodeIds != null and request.nodeIds.size() > 0">
AND test_case.node_id IN
<foreach collection="request.nodeIds" open="(" close=")" separator="," item="nodeId">
#{nodeId}
</foreach>
</if>
<include refid="queryVersionCondition">
<property name="versionTable" value="test_case"/>
</include>
<include refid="filters"/>
</where>
</sql>
<select id="moduleCount" resultType="java.lang.Integer">
select count(test_case.id) from test_case
left join project on test_case.project_id = project.id
<include refid="queryWhereConditionWidthProject"/>
</select>
<select id="getCountFollow" resultType="java.lang.Integer">
select count(*) from test_case tc where
tc.id in (select tf.case_id from test_case_follow tf where tf.follow_id = #{userId,jdbcType=VARCHAR})
and
tc.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and (tc.status is null or tc.status != 'Trash');
</select>
<select id="getCountUpcoming" resultType="java.lang.Integer">
select count(*) from test_case tc where
tc.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and tc.review_status in ('Prepare','Pass','UnPass')
and tc.create_user = #{userId,jdbcType=VARCHAR}
</select>
<sql id="queryPublicCaseWhere">
<where>
<include refid="filters"/>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.statusIsNot != null">
and test_case.status != #{request.statusIsNot}
</if>
<if test="request.notEqStatus != null">
and test_case.status != #{request.notEqStatus}
</if>
<if test="request.name != null">
and (test_case.name like CONCAT('%', #{request.name},'%')
or test_case.num like CONCAT('%', #{request.name},'%')
or test_case.tags like CONCAT('%', #{request.name},'%')
or test_case.custom_num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.ids != null">
and test_case.id in
<foreach collection="request.ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</if>
<if test="request.relevanceCreateTime >0">
and test_case.id in (select test_case_id from test_case_test where test_case_test.create_time >=
#{request.createTime})
</if>
<if test="request.createTime >0">
and test_case.create_time >= #{request.createTime}
</if>
<if test="request.nodeIds != null and request.nodeIds.size() > 0">
and test_case.node_id in
<foreach collection="request.nodeIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
and test_case.case_public = TRUE
<include refid="filters"/>
<if test="request.caseCoverage == 'uncoverage' ">
and test_case.id not in (select distinct test_case_test.test_case_id from test_case_test)
</if>
<if test="request.caseCoverage == 'coverage' ">
and test_case.id in (select distinct test_case_test.test_case_id from test_case_test)
</if>
<include refid="queryVersionCondition">
<property name="versionTable" value="test_case"/>
</include>
</where>
</sql>
<sql id="filters">
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='priority'">
and test_case.priority in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='type'">
and test_case.type in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='review_status'">
and test_case.review_status in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='last_execute_result'">
and test_case.last_execute_result in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='status'">
and test_case.status in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='method'">
and test_case.method in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='version_id'">
and test_case.version_id in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='maintainer'">
and test_case.maintainer in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key.startsWith('custom_single')">
and test_case.id in (
select resource_id from custom_field_test_case where concat('custom_single-',field_id) = #{key}
and trim(both '"' from value) in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
)
</when>
<when test="key.startsWith('custom_multiple')">
and test_case.id in (
select resource_id from custom_field_test_case where concat('custom_multiple-',field_id) = #{key}
and
<foreach collection="values" item="value" separator="or" open="(" close=")">
JSON_CONTAINS(value, #{value})
</foreach>
)
</when>
<when test="key=='create_user'">
and test_case.create_user in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
</choose>
</if>
<if test="key=='status' and (values == null || values.size() == 0)">
and test_case.status != 'Trash'
</if>
</foreach>
</if>
<if test="request.filters == null || request.filters.size() == 0 || !request.filters.containsKey('status')">
and test_case.status != 'Trash'
</if>
</sql>
<sql id="queryWhereCondition">
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.statusIsNot != null">
and test_case.status != #{request.statusIsNot}
</if>
<if test="request.notEqStatus != null">
and test_case.status != #{request.notEqStatus}
</if>
<if test="request.casePublic != null and request.casePublic == true">
and test_case.case_public = true
</if>
<if test="request.name != null">
and (test_case.name like CONCAT('%', #{request.name},'%')
or test_case.num like CONCAT('%', #{request.name},'%')
or test_case.tags like CONCAT('%', #{request.name},'%')
or test_case.custom_num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.ids != null">
and test_case.id in
<foreach collection="request.ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</if>
<if test="request.notInIds != null and request.notInIds.size() > 0">
and test_case.id not in
<foreach collection="request.notInIds" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</if>
<if test="request.relevanceCreateTime >0">
and test_case.id in (select test_case_id from test_case_test where test_case_test.create_time >=
#{request.createTime})
</if>
<if test="request.createTime >0">
and test_case.create_time >= #{request.createTime}
</if>
<if test="request.nodeIds != null and request.nodeIds.size() > 0">
and test_case.node_id in
<foreach collection="request.nodeIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.projectId != null">
and test_case.project_id = #{request.projectId}
</if>
<include refid="filters"/>
<if test="request.caseCoverage == 'uncoverage' ">
and test_case.id not in (
SELECT test_case_id FROM test_case_test WHERE test_type = 'testCase' and test_id IN (select id FROM
api_test_case WHERE `STATUS` is null or status != 'Trash')
UNION
SELECT test_case_id FROM test_case_test WHERE test_type = 'performance' and test_id IN (select id from
load_test)
UNION
SELECT test_case_id FROM test_case_test WHERE test_type = 'automation' and test_id IN (select id FROM
api_scenario WHERE `STATUS` != 'Trash')
)
</if>
<if test="request.caseCoverage == 'coverage' ">
and test_case.id in (
SELECT test_case_id FROM test_case_test WHERE test_type = 'testCase' and test_id IN (select id FROM
api_test_case WHERE `STATUS` is null or status != 'Trash')
UNION
SELECT test_case_id FROM test_case_test WHERE test_type = 'performance' and test_id IN (select id from
load_test)
UNION
SELECT test_case_id FROM test_case_test WHERE test_type = 'automation' and test_id IN (select id FROM
api_scenario WHERE `STATUS` != 'Trash')
)
</if>
<include refid="queryVersionCondition">
<property name="versionTable" value="test_case"/>
</include>
</where>
</sql>
<sql id="queryWhereConditionWidthProject">
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.statusIsNot != null">
and test_case.status != #{request.statusIsNot}
</if>
<if test="request.notEqStatus != null">
and test_case.status != #{request.notEqStatus}
</if>
<if test="request.name != null">
and (test_case.name like CONCAT('%', #{request.name},'%')
or test_case.num like CONCAT('%', #{request.name},'%')
or test_case.tags like CONCAT('%', #{request.name},'%')
or test_case.custom_num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.ids != null">
and test_case.id in
<foreach collection="request.ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</if>
<if test="request.relevanceCreateTime >0">
and test_case.id in (select test_case_id from test_case_test where test_case_test.create_time >=
#{request.createTime})
</if>
<if test="request.createTime >0">
and test_case.create_time >= #{request.createTime}
</if>
<if test="request.nodeIds != null and request.nodeIds.size() > 0">
and test_case.node_id in
<foreach collection="request.nodeIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</if>
<if test="request.projectId != null">
and test_case.project_id = #{request.projectId}
</if>
<include refid="filters"/>
<if test="request.caseCoverage == 'uncoverage' ">
and test_case.id not in (select distinct test_case_test.test_case_id from test_case_test)
</if>
<if test="request.caseCoverage == 'coverage' ">
and test_case.id in (select distinct test_case_test.test_case_id from test_case_test)
</if>
<include refid="queryVersionCondition">
<property name="versionTable" value="test_case"/>
</include>
</where>
</sql>
<sql id="queryVersionCondition">
<if test="request.versionId != null">
and ${versionTable}.version_id = #{request.versionId}
</if>
<if test="request.refId != null">
and ${versionTable}.ref_id = #{request.refId}
</if>
<if test="request.versionId == null and request.refId == null">
AND ${versionTable}.latest = 1
</if>
</sql>
</mapper>

View File

@ -0,0 +1,13 @@
package io.metersphere.base.mapper.ext;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtTestCaseReviewMapper {
int getCountFollow(@Param("projectIds") List<String> projectIds,@Param("userId") String userId);
int getCountUpcoming(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
}

View File

@ -0,0 +1,132 @@
<?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.ext.ExtTestCaseReviewMapper">
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and test_case_review.name
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.createTime != null">
and test_case_review.create_time
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.updateTime != null">
and test_case_review.update_time
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.endTime != null">
and test_case_review.end_time
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.endTime"/>
</include>
</if>
<if test="${condition}.status != null">
and test_case_review.status
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "not like"'>
and (test_case_review.tags is null or test_case_review.tags
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.tags"/>
</include>
)
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "like"'>
and test_case_review.tags
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.tags"/>
</include>
</if>
<if test="${condition}.followPeople != null">
and test_case_review.id in (
select review_id from test_case_review_follow where follow_id
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.followPeople"/>
</include>
)
</if>
<if test="${condition}.creator != null">
and test_case_review.create_user
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
</sql>
<sql id="queryWhereCondition">
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.name != null">
and test_case_review.name like CONCAT('%', #{request.name},'%')
</if>
<if test="request.reviewerId != null">
and test_case_review.id in (select test_case_review_users.review_id from test_case_review_users where test_case_review_users.user_id = #{request.reviewerId})
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</if>
<if test="request.projectId != null">
and test_case_review.project_id = #{request.projectId}
</if>
<include refid="filter"/>
</where>
</sql>
<sql id="filter">
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='stage'">
and test_case_review.stage in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='status'">
and test_case_review.status in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='creator_name'">
and test_case_review.creator in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
</choose>
</if>
</foreach>
</if>
</sql>
<select id="getCountFollow" resultType="java.lang.Integer">
select count(*) from test_case_review tr where
tr.id in (select trf.review_id from test_case_review_follow trf where trf.follow_id = #{userId,jdbcType=VARCHAR})
and
tr.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and (tr.status is null or tr.status != 'Trash');
</select>
<select id="getCountUpcoming" resultType="java.lang.Integer">
select count(*) from test_case_review tr where
tr.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and tr.status in ('Prepare','Underway')
and tr.id in (select test_case_review_users.review_id from test_case_review_users where test_case_review_users.user_id = #{userId,jdbcType=VARCHAR})
</select>
</mapper>

View File

@ -0,0 +1,12 @@
package io.metersphere.base.mapper.ext;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtTestPlanMapper {
int getCountFollow(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
int getCountUpcoming(@Param("projectIds") List<String> projectIds, @Param("userId") String userId);
}

View File

@ -0,0 +1,238 @@
<?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.ext.ExtTestPlanMapper">
<sql id="condition">
<choose>
<when test='${object}.operator == "like"'>
like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "not like"'>
not like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "in"'>
in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "not in"'>
not in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "between"'>
between #{${object}.value[0]} and #{${object}.value[1]}
</when>
<when test='${object}.operator == "gt"'>
&gt; #{${object}.value}
</when>
<when test='${object}.operator == "lt"'>
&lt; #{${object}.value}
</when>
<when test='${object}.operator == "ge"'>
&gt;= #{${object}.value}
</when>
<when test='${object}.operator == "le"'>
&lt;= #{${object}.value}
</when>
<when test='${object}.operator == "current user"'>
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
</when>
<otherwise>
= #{${object}.value}
</otherwise>
</choose>
</sql>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and test_plan.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.followPeople != null">
and test_plan.id in (
select test_plan_id from test_plan_follow where follow_id
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.followPeople"/>
</include>
)
</if>
<if test="${condition}.projectName != null">
and project.name
<include refid="condition">
<property name="object" value="${condition}.projectName"/>
</include>
</if>
<if test="${condition}.createTime != null">
and test_plan.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.actualStartTime != null">
and test_plan.actual_start_time
<include refid="condition">
<property name="object" value="${condition}.actualStartTime"/>
</include>
</if>
<if test="${condition}.actualEndTime != null">
and test_plan.actual_end_time
<include refid="condition">
<property name="object" value="${condition}.actualEndTime"/>
</include>
</if>
<if test="${condition}.planStartTime != null">
and test_plan.planned_start_time
<include refid="condition">
<property name="object" value="${condition}.planStartTime"/>
</include>
</if>
<if test="${condition}.planEndTime != null">
and test_plan.planned_end_time
<include refid="condition">
<property name="object" value="${condition}.planEndTime"/>
</include>
</if>
<if test="${condition}.status != null">
and test_plan.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.updateTime != null">
and test_plan.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.stage != null">
and test_plan.stage
<include refid="condition">
<property name="object" value="${condition}.stage"/>
</include>
</if>
<if test="${condition}.creator != null">
and test_plan.creator
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "not like"'>
and (test_plan.tags is null or test_plan.tags
<include refid="condition">
<property name="object" value="${condition}.tags"/>
</include>
)
</if>
<if test='${condition}.tags != null and ${objectKey}.operator == "like"'>
and test_plan.tags
<include refid="condition">
<property name="object" value="${condition}.tags"/>
</include>
</if>
<if test="${condition}.principal != null">
and test_plan.id in (SELECT test_plan_id FROM test_plan_principal WHERE principal_id
<include refid="condition">
<property name="object" value="${condition}.principal"/>
</include>
)
</if>
</sql>
<sql id="filter">
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='status'">
<choose>
<when test="request.executorOrPrincipal != null">
AND (( test_plan_principal.principal_id =
'${@io.metersphere.commons.utils.SessionUtils@getUserId()}' and
test_plan.status in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
)
or
(test_plan_test_case.executor =
'${@io.metersphere.commons.utils.SessionUtils@getUserId()}' and
test_plan_test_case.status in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
))
</when>
<otherwise>
and test_plan.status in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</otherwise>
</choose>
</when>
<otherwise>
<choose>
<when test="key=='stage'">
and test_plan.stage in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='create_user'">
and test_plan.creator in
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.filterInWrapper"/>
</when>
<when test="key=='schedule_status'">
and
<foreach collection="values" item="value" separator="or" open="(" close=")">
<if test="value == 'OPEN'">
schedule.`enable` = 1
</if>
<if test="value == 'SHUT'">
schedule.`enable` = 0
</if>
<if test="value == 'NOTSET' ">
schedule.id is null
</if>
</foreach>
</when>
</choose>
and test_plan.status != 'Archived'
</otherwise>
</choose>
</if>
<if test="(values == null or values.size() == 0) and request.filters.get('status') == null">
and test_plan.status != 'Archived'
</if>
</foreach>
</if>
</sql>
<select id="getCountFollow" resultType="java.lang.Integer">
select count(*) from test_plan tp where
tp.id in (select tpf.test_plan_id from test_plan_follow tpf where tpf.follow_id = #{userId,jdbcType=VARCHAR})
and
tp.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and (tp.status is null or tp.status != 'Trash');
</select>
<select id="getCountUpcoming" resultType="java.lang.Integer">
select count(*) from test_plan
left join test_plan_principal ON test_plan_principal.test_plan_id = test_plan.id
left join test_plan_test_case on test_plan_test_case.plan_id = test_plan.id
where
test_plan.project_id in
<foreach collection="projectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
and (( test_plan_principal.principal_id = #{userId,jdbcType=VARCHAR}
and
test_plan.status in ('Prepare','Underway')
)
or
(test_plan_test_case.executor = #{userId,jdbcType=VARCHAR}
and
test_plan_test_case.status in ('Prepare','Underway')
))
and (test_plan.status is null or test_plan.status != 'Trash');
</select>
</mapper>

View File

@ -0,0 +1,38 @@
package io.metersphere.request.api;
import io.metersphere.request.BaseQueryRequest;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ApiScenarioRequest extends BaseQueryRequest {
private String id;
private String excludeId;
private String moduleId;
private String name;
private String userId;
private String planId;
private boolean recent = false;
private boolean isSelectThisWeedData;
private long createTime = 0;
private long scheduleCreateTime = 0;
private String executeStatus;
private String selectDataType;
private boolean notInTestPlan;
private String reviewId;
private String versionId;
private String refId;
//操作人
private String operator;
//操作时间
private Long operationTime;
/**
* 是否需要查询环境字段
*/
private boolean selectEnvironment = false;
//测试计划关联场景过滤掉步骤为0的场景
private String stepTotal;
}

View File

@ -0,0 +1,39 @@
package io.metersphere.request.api;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.*;
import java.util.List;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class ApiSyncCaseRequest {
private Boolean protocol;
private Boolean method;
private Boolean path;
private Boolean headers;
private Boolean query;
private Boolean rest;
private Boolean body;
private Boolean delNotSame;
private Boolean runError;
private Boolean unRun;
private List<String> ids;
private Boolean selectAll;
}

View File

@ -0,0 +1,76 @@
package io.metersphere.request.api;
import io.metersphere.request.BaseQueryRequest;
import io.metersphere.request.OrderRequest;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
@Getter
@Setter
public class ApiTestCaseRequest extends BaseQueryRequest {
private String id;
private List<String> ids;
private String planId;
private String projectId;
private String priority;
private String name;
private String environmentId;
private String workspaceId;
private String apiDefinitionId;
private String status;
private String protocol;
private String moduleId;
private List<String> moduleIds;
private List<OrderRequest> orders;
private Map<String, List<String>> filters;
private Map<String, Object> combine;
private boolean isSelectThisWeedData;
private long createTime = 0;
private long updateTime = 0;
private String reviewId;
private String deleteUserId;
private long deleteTime;
/**
* 检查待更新的近三天有更新的或者状态为error的
*/
private boolean toUpdate;
/**
* 是否进入待更新列表
*/
private boolean toBeUpdated;
/**
* 当前时间减去进入待更新的时间
*/
private Long toBeUpdateTime;
/**
* 进入待更新列表用例状态集合
*/
private List<String> statusList;
/**
* 不需要查用例状态
*/
private boolean noSearchStatus;
/**
* 是否需要查询环境字段
*/
private boolean selectEnvironment = false;
/**
* 查询排除一些接口
*/
private List<String> notInIds;
//页面跳转时附带的过滤条件
private String redirectFilter;
//同步配置
private ApiSyncCaseRequest syncConfig;
//全选
private boolean selectAll;
}

View File

@ -0,0 +1,62 @@
package io.metersphere.request.track;
import io.metersphere.request.BaseQueryRequest;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class IssuesRequest extends BaseQueryRequest {
private String title;
private String content;
private String projectId;
private String testCaseId;
private List<String> tapdUsers;
private String userId;
/**
* 关联类型
* 如果类型是 FUNCTIONAL 表示是功能用例查询相关缺陷此时需要把该功能用例对应的测试计划中的用例关联的缺陷一并查出
* 如果是 PLAN_FUNCTIONAL 则只查询该测试计划用例所关联的缺陷
*/
private String refType;
/**
* zentao bug 处理人
*/
private String zentaoUser;
/**
* zentao bug 影响版本
*/
private List<String> zentaoBuilds;
/**
* issues id
*/
private String id;
private String caseId;
private String resourceId;
/**
* 查询用例下的缺陷的用例id或者测试计划的用例id
*/
private String caseResourceId;
private String platform;
private String customFields;
private List<String> testCaseIds;
private List<String> notInIds;
private String requestType;
private String status;
private String defaultCustomFields;
private Boolean isPlanEdit = false;
private String planId;
/**
* 是否根据自定义字段进行排序
*/
private Boolean isCustomSorted = false;
/**
* 自定义字段ID
*/
private String customFieldId;
}

View File

@ -0,0 +1,66 @@
package io.metersphere.request.track;
import io.metersphere.request.BaseQueryRequest;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class QueryTestCaseRequest extends BaseQueryRequest {
private String id;
private String name;
private String relationshipType;
private List<String> testCaseIds;
// 测试计划是否允许重复
private boolean repeatCase;
private String planId;
private String issuesId;
private String userId;
private String reviewId;
private boolean isSelectThisWeedData = false;
private boolean isSelectThisWeedRelevanceData = false;
private String caseCoverage;
private String nodeId;
private String statusIsNot;
private long createTime = 0;
private long relevanceCreateTime = 0;
private List<String> testCaseContainIds;
// 接口定义
private String protocol;
private String apiCaseCoverage;
// 补充场景条件
private String excludeId;
private String moduleId;
private boolean recent = false;
private String executeStatus;
private boolean notInTestPlan;
//操作人
private String operator;
//操作时间
private Long operationTime;
private boolean casePublic;
private long scheduleCreateTime;
private String stepTotal;
private Boolean toBeUpdated;
private String apiCoverage;
private String scenarioCoverage;
}

View File

@ -0,0 +1,18 @@
package io.metersphere.request.track;
import io.metersphere.request.OrderRequest;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
@Getter
@Setter
public class QueryTestPlanRequest extends TestPlanRequest {
private String workspaceId;
private String userId;
private List<OrderRequest> orders;
private Map<String, List<String>> filters;
private Map<String, Object> combine;
}

View File

@ -0,0 +1,43 @@
package io.metersphere.request.track;
import io.metersphere.base.domain.Schedule;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class TestPlanRequest {
private String id;
private String projectId;
private String name;
private String description;
private Long createTime;
private Long updateTime;
private String loadConfiguration;
private String advancedConfiguration;
private String runtimeConfiguration;
private Schedule schedule;
private String testResourcePoolId;
private String refId;
private String versionId;
private List<String> follows;
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,34 @@
package io.metersphere.workstation.controller;
import io.metersphere.workstation.service.WorkstationService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Map;
@RequestMapping("workstation")
@RestController
public class WorkstationController {
@Resource
private WorkstationService workstationService;
@GetMapping("/creat_case_count/list/{isWeek}")
public Map<String, Integer> list(@PathVariable Boolean isWeek) {
return workstationService.getMyCreatedCaseGroupContMap(isWeek);
}
@GetMapping("/follow/total/count/{workstationId}")
public Map<String, Integer> getFollowTotalCount(@PathVariable String workstationId) {
return workstationService.getFollowTotalCount(workstationId);
}
@GetMapping("/coming/total/count/{workstationId}")
public Map<String, Integer> getUpcomingTotalCount(@PathVariable String workstationId) {
return workstationService.getUpcomingTotalCount(workstationId);
}
@GetMapping("/issue/week/count/{workstationId}")
public Integer getIssueWeekCount(@PathVariable String workstationId) {
return workstationService.getIssueWeekCount(workstationId);
}
}

View File

@ -0,0 +1,30 @@
package io.metersphere.workstation.controller.api;
import io.metersphere.workstation.service.api.ApiService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping(path = {
"/api/definition",
"/api/testcase",
"/api/automation",
})
public class ApiController {
@Resource
ApiService apiService;
@PostMapping("/**")
public Object list(HttpServletRequest request, @RequestBody Object param) {
return apiService.post(request.getRequestURI(), param);
}
@GetMapping("/**")
public Object get(HttpServletRequest request) {
return apiService.get(request.getRequestURI());
}
}

View File

@ -0,0 +1,49 @@
package io.metersphere.workstation.controller.api;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.commons.constants.PermissionConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.dto.ProjectDTO;
import io.metersphere.request.ProjectRequest;
import io.metersphere.service.BaseCheckPermissionService;
import io.metersphere.service.BaseProjectService;
import io.metersphere.service.BaseUserService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
@RestController
@RequestMapping()
public class ExtProjectController {
@Resource
private BaseProjectService baseProjectService;
@Resource
private BaseCheckPermissionService baseCheckPermissionService;
@GetMapping("/api/project/member/size/{id}")
public long getProjectMemberSize(@PathVariable String id) {
return baseProjectService.getProjectMemberSize(id);
}
@PostMapping("/api/project/list/{goPage}/{pageSize}")
@RequiresPermissions(PermissionConstants.WORKSPACE_PROJECT_MANAGER_READ)
public Pager<List<ProjectDTO>> getProjectList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ProjectRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, baseProjectService.getProjectList(request));
}
@GetMapping("/api/project/get/owner/ids")
public Collection<String> getOwnerProjectIds() {
return baseCheckPermissionService.getUserRelatedProjectIds();
}
@GetMapping("/api/project/get/owner/details")
public List<ProjectDTO> getOwnerProjects() {
return baseCheckPermissionService.getOwnerProjects();
}
}

View File

@ -0,0 +1,27 @@
package io.metersphere.workstation.controller.performance;
import io.metersphere.workstation.service.performance.PerformanceService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping(path = {
"performance",
})
public class PerformanceController {
@Resource
PerformanceService performanceService;
@PostMapping("/**")
public Object list(HttpServletRequest request, @RequestBody Object param) {
return performanceService.post(request.getRequestURI(), param);
}
@GetMapping("/**")
public Object get(HttpServletRequest request) {
return performanceService.get(request.getRequestURI());
}
}

View File

@ -0,0 +1,27 @@
package io.metersphere.workstation.controller.projectmanage;
import io.metersphere.workstation.service.projectmanage.ProjectManagementService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping(path = {
"/field/template/case",
"/field/template/issue"
})
public class ProjectManagementController {
@Resource
ProjectManagementService projectManagementService;
@PostMapping("/**")
public Object list(HttpServletRequest request, @RequestBody Object param) {
return projectManagementService.post(request.getRequestURI(), param);
}
@GetMapping("/**")
public Object get(HttpServletRequest request) {
return projectManagementService.get(request.getRequestURI());
}
}

View File

@ -0,0 +1,26 @@
package io.metersphere.workstation.controller.system;
import io.metersphere.workstation.service.system.SystemSettingService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping(path = {
"user"
})
public class SystemSettingController {
@Resource
SystemSettingService systemSettingService;
@PostMapping("/**")
public Object list(HttpServletRequest request, @RequestBody Object param) {
return systemSettingService.post(request.getRequestURI(), param);
}
@GetMapping("/**")
public Object get(HttpServletRequest request) {
return systemSettingService.get(request.getRequestURI());
}
}

View File

@ -0,0 +1,29 @@
package io.metersphere.workstation.controller.track;
import io.metersphere.workstation.service.track.TrackSettingService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping(path = {
"issues",
"/test/case",
"/test/plan"
})
public class TrackController {
@Resource
TrackSettingService trackPSettingService;
@PostMapping("/**")
public Object list(HttpServletRequest request, @RequestBody Object param) {
return trackPSettingService.post(request.getRequestURI(), param);
}
@GetMapping("/**")
public Object get(HttpServletRequest request) {
return trackPSettingService.get(request.getRequestURI());
}
}

View File

@ -0,0 +1,266 @@
package io.metersphere.workstation.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.metersphere.base.domain.ProjectApplication;
import io.metersphere.base.mapper.ext.*;
import io.metersphere.commons.constants.ExecuteResult;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.request.ApiSyncCaseRequest;
import io.metersphere.request.api.ApiScenarioRequest;
import io.metersphere.request.track.QueryTestCaseRequest;
import io.metersphere.request.track.QueryTestPlanRequest;
import io.metersphere.request.api.ApiTestCaseRequest;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.WeekFields;
import java.util.*;
import static io.metersphere.workstation.util.ShareUtil.getTimeMills;
@Service
@Transactional(rollbackFor = Exception.class)
public class WorkstationService {
@Resource
private ExtApiScenarioMapper extApiScenarioMapper;
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
@Resource
private ExtTestCaseMapper extTestCaseMapper;
@Resource
private ExtLoadTestMapper extLoadTestMapper;
@Resource
private ExtApiDefinitionMapper extApiDefinitionMapper;
@Resource
private ExtIssuesMapper extIssuesMapper;
@Resource
private ExtTestCaseReviewMapper extTestCaseReviewMapper;
@Resource
private ExtTestPlanMapper extTestPlanMapper;
@Resource
private ExtProjectMapper extProjectMapper;
@Resource
private ExtProjectApplicationMapper extProjectApplicationMapper;
private static final String DEFAULT_TIME_DATE = "-3D";
public Map<String, Integer> getMyCreatedCaseGroupContMap(Boolean isWeek) {
long createTime = 0L;
if (isWeek){
Date startDayOfWeek = getStartDayOfWeek();
createTime = startDayOfWeek.getTime();
}
String userId = SessionUtils.getUserId();
//build query condition object
QueryTestPlanRequest testPlanRequest = new QueryTestPlanRequest();
testPlanRequest.setUserId(userId);
testPlanRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
if (isWeek) {
testPlanRequest.setCreateTime(createTime);
}
ApiTestCaseRequest apiTestCaseRequest = new ApiTestCaseRequest();
apiTestCaseRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
if (isWeek) {
apiTestCaseRequest.setCreateTime(createTime);
}
//@see io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.xml:103
Map<String, Object> combine = new HashMap<>(2);
Map<String, String> operatorValue = new HashMap<>(2);
operatorValue.put("operator", "current user");
operatorValue.put("value", "current user");
combine.put("creator", operatorValue);
Map<String, List<String>> filter = new HashMap<>(2);
testPlanRequest.setCombine(combine);
apiTestCaseRequest.setCombine(combine);
ApiScenarioRequest apiScenarioRequest = new ApiScenarioRequest();
apiScenarioRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
apiScenarioRequest.setCombine(combine);
if (isWeek) {
apiScenarioRequest.setCreateTime(createTime);
}
QueryTestCaseRequest testCaseRequest = new QueryTestCaseRequest();
testCaseRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
testCaseRequest.setCombine(combine);
if (isWeek) {
testCaseRequest.setCreateTime(createTime);
}
List<String> status = new ArrayList<>();
status.add("Prepare");
status.add("Pass");
status.add("UnPass");
filter.put("reviewStatus", status);
testCaseRequest.setFilters(filter);
//query db
int apiScenarioCaseCount = extApiScenarioMapper.listModule(apiScenarioRequest);
int apiTestCaseCount = extApiTestCaseMapper.moduleCount(apiTestCaseRequest);
int testCaseCount = extTestCaseMapper.moduleCount(testCaseRequest);
int loadTestCount = extLoadTestMapper.moduleCount(testPlanRequest);
//build result
Map<String, Integer> map = new HashMap<>(4);
map.put("apiScenarioCaseCount", apiScenarioCaseCount);
map.put("apiTestCaseCount", apiTestCaseCount);
map.put("testCaseCount", testCaseCount);
map.put("loadTestCount", loadTestCount);
return map;
}
public Map<String, Integer> getFollowTotalCount(String workstationId){
String userId = SessionUtils.getUserId();
List<String> projectIds = extProjectMapper.getProjectIdByWorkspaceId(workstationId);
if (CollectionUtils.isEmpty(projectIds)) {
return null;
}
int caseFollowCount = extTestCaseMapper.getCountFollow(projectIds, userId);
int planFollowCount = extTestPlanMapper.getCountFollow(projectIds, userId);
int reviewFollowCount = extTestCaseReviewMapper.getCountFollow(projectIds, userId);
int issueFollowCount = extIssuesMapper.getCountFollow(projectIds, userId);
int apiFollowCount = extApiDefinitionMapper.getCountFollow(projectIds, userId);
int apiCaseFollowCount = extApiTestCaseMapper.getCountFollow(projectIds, userId);
int scenarioFollowCount = extApiScenarioMapper.getCountFollow(projectIds, userId);
int loadFollowCount = extLoadTestMapper.getCountFollow(projectIds, userId);
Map<String, Integer> map = new HashMap<>(8);
map.put("track_case", caseFollowCount);
map.put("track_plan", planFollowCount);
map.put("track_review", reviewFollowCount);
map.put("track_issue", issueFollowCount);
map.put("api_definition", apiFollowCount);
map.put("api_case", apiCaseFollowCount);
map.put("api_automation", scenarioFollowCount);
map.put("performance", loadFollowCount);
return map;
}
public Map<String, Integer> getUpcomingTotalCount(String workstationId){
String userId = SessionUtils.getUserId();
List<String> projectIds = extProjectMapper.getProjectIdByWorkspaceId(workstationId);
if (CollectionUtils.isEmpty(projectIds)) {
return null;
}
int caseUpcomingCount = extTestCaseMapper.getCountUpcoming(projectIds, userId);
int planUpcomingCount = extTestPlanMapper.getCountUpcoming(projectIds, userId);
int reviewUpcomingCount = extTestCaseReviewMapper.getCountUpcoming(projectIds, userId);
int issueUpcomingCount = extIssuesMapper.getCountUpcoming(projectIds, userId);
int apiUpcomingCount = extApiDefinitionMapper.getCountUpcoming(projectIds, userId);
int apiCaseUpcomingCount = extApiTestCaseMapper.getCountUpcoming(projectIds, userId);
int updateApiCaseCount = getUpdateApiCaseCount(projectIds, userId);
int apiCaseCount = apiCaseUpcomingCount + updateApiCaseCount;
int scenarioUpcomingCount = extApiScenarioMapper.getCountUpcoming(projectIds, userId);
int loadUpcomingCount = extLoadTestMapper.getCountUpcoming(projectIds, userId);
Map<String, Integer> map = new HashMap<>(8);
map.put("track_case", caseUpcomingCount);
map.put("track_plan", planUpcomingCount);
map.put("track_review", reviewUpcomingCount);
map.put("track_issue", issueUpcomingCount);
map.put("api_definition", apiUpcomingCount);
map.put("api_case",apiCaseCount);
map.put("api_automation", scenarioUpcomingCount);
map.put("performance", loadUpcomingCount);
return map;
}
public int getUpdateApiCaseCount(List<String> projectIds,String userId){
int totalUpdateCount = 0;
for (String projectId : projectIds) {
List<String> syncRuleCaseStatus = getSyncRuleCaseStatus(projectId);
Long toBeUpdatedTime = getToBeUpdatedTime(projectId);
int updateCount = extApiTestCaseMapper.getUpdateCount(userId, projectId, syncRuleCaseStatus, toBeUpdatedTime);
totalUpdateCount = totalUpdateCount+updateCount;
}
return totalUpdateCount;
}
public List<String> getSyncRuleCaseStatus(String projectId) {
List<String> statusList = new ArrayList<>();
List<ProjectApplication> projectApplicationConfigs = extProjectApplicationMapper.selectByProjectIdAndType(projectId,"TRIGGER_UPDATE");
if (CollectionUtils.isEmpty(projectApplicationConfigs)) {
statusList.add(ExecuteResult.API_ERROR.getValue());
return statusList;
}
ProjectApplication projectApplication = projectApplicationConfigs.get(0);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
ApiSyncCaseRequest apiSyncCaseRequest = mapper.readValue(projectApplication.getTypeValue(), ApiSyncCaseRequest.class);
if (apiSyncCaseRequest.getRunError()) {
statusList.add(ExecuteResult.API_ERROR.getValue());
}
if (apiSyncCaseRequest.getUnRun()) {
statusList.add(ExecuteResult.UN_EXECUTE.getValue());
statusList.add("");
}
return statusList;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return statusList;
}
public Long getToBeUpdatedTime(String projectId) {
if (StringUtils.isBlank(projectId)) {
return getTimeMills(System.currentTimeMillis(), DEFAULT_TIME_DATE);
}
List<ProjectApplication> projectApplications = extProjectApplicationMapper.selectByProjectIdAndType(projectId,"OPEN_UPDATE_TIME");
if (CollectionUtils.isEmpty(projectApplications)) {
return getTimeMills(System.currentTimeMillis(), DEFAULT_TIME_DATE);
}
List<ProjectApplication> projectApplicationRules = extProjectApplicationMapper.selectByProjectIdAndType(projectId,"TRIGGER_UPDATE");
ProjectApplication projectApplication = projectApplications.get(0);
String typeValue = projectApplication.getTypeValue();
if (CollectionUtils.isEmpty(projectApplicationRules) && StringUtils.equals(typeValue, "false")) {
return getTimeMills(System.currentTimeMillis(), DEFAULT_TIME_DATE);
} else if (StringUtils.equals(typeValue, "false")) {
return null;
}
List<ProjectApplication> projectApplicationTimes = extProjectApplicationMapper.selectByProjectIdAndType(projectId,"OPEN_UPDATE_RULE_TIME");
if (CollectionUtils.isEmpty(projectApplications)) {
return getTimeMills(System.currentTimeMillis(), DEFAULT_TIME_DATE);
}
ProjectApplication projectApplicationTime = projectApplicationTimes.get(0);
String time = projectApplicationTime.getTypeValue();
if (StringUtils.isNotBlank(time)) {
time = "-" + time;
return getTimeMills(System.currentTimeMillis(), time);
}
return null;
}
public static Date getStartDayOfWeek() {
LocalDate date = LocalDate.now();
TemporalField fieldIso = WeekFields.of(DayOfWeek.MONDAY, 1).dayOfWeek();
LocalDate localDate = LocalDate.from(date);
localDate = localDate.with(fieldIso, 1);
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
}
public Integer getIssueWeekCount(String workstationId) {
String userId = SessionUtils.getUserId();
List<String> projectIds = extProjectMapper.getProjectIdByWorkspaceId(workstationId);
if (CollectionUtils.isEmpty(projectIds)) {
return null;
}
Date startDayOfWeek = getStartDayOfWeek();
Long createTime = startDayOfWeek.getTime();
return extIssuesMapper.getCountCreat(projectIds, userId, createTime);
}
}

View File

@ -0,0 +1,15 @@
package io.metersphere.workstation.service.api;
import io.metersphere.commons.constants.MicroServiceName;
import io.metersphere.service.RemoteService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiService extends RemoteService {
public ApiService() {
super(MicroServiceName.API_TEST);
}
}

View File

@ -0,0 +1,15 @@
package io.metersphere.workstation.service.performance;
import io.metersphere.commons.constants.MicroServiceName;
import io.metersphere.service.RemoteService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(rollbackFor = Exception.class)
public class PerformanceService extends RemoteService {
public PerformanceService() {
super(MicroServiceName.PERFORMANCE_TEST);
}
}

View File

@ -0,0 +1,14 @@
package io.metersphere.workstation.service.projectmanage;
import io.metersphere.commons.constants.MicroServiceName;
import io.metersphere.service.RemoteService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(rollbackFor = Exception.class)
public class ProjectManagementService extends RemoteService {
public ProjectManagementService() {
super(MicroServiceName.PROJECT_MANAGEMENT);
}
}

View File

@ -0,0 +1,17 @@
package io.metersphere.workstation.service.system;
import io.metersphere.commons.constants.MicroServiceName;
import io.metersphere.service.RemoteService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(rollbackFor = Exception.class)
public class SystemSettingService extends RemoteService {
public SystemSettingService() {
super(MicroServiceName.SYSTEM_SETTING);
}
}

View File

@ -0,0 +1,15 @@
package io.metersphere.workstation.service.track;
import io.metersphere.commons.constants.MicroServiceName;
import io.metersphere.service.RemoteService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(rollbackFor = Exception.class)
public class TrackSettingService extends RemoteService {
public TrackSettingService() {
super(MicroServiceName.TEST_TRACK);
}
}

View File

@ -0,0 +1,50 @@
package io.metersphere.workstation.util;
import org.apache.commons.lang3.StringUtils;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class ShareUtil {
private static final String UNIT_HOUR = "H";
private static final String UNIT_DAY = "D";
private static final String UNIT_MONTH = "M";
private static final String UNIT_YEAR = "Y";
/**
* return time + expr
*
* @param time
* @param expr
* @return
*/
public static long getTimeMills(long time, String expr) {
Instant instant = Instant.ofEpochMilli(time);
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
long timeMills = 0;
LocalDateTime date = exprToLocalDateTime(localDateTime, expr);
if (date != null) {
timeMills = date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
return timeMills;
}
public static LocalDateTime exprToLocalDateTime(LocalDateTime localDateTime, String expr) {
LocalDateTime date = null;
String unit = expr.substring(expr.length() - 1);
int quantity = Integer.parseInt(expr.substring(0, expr.length() - 1));
if (StringUtils.equals(unit, UNIT_HOUR)) {
date = localDateTime.plusHours(quantity);
} else if (StringUtils.equals(unit, UNIT_DAY)) {
date = localDateTime.plusDays(quantity);
} else if (StringUtils.equals(unit, UNIT_MONTH)) {
date = localDateTime.plusMonths(quantity);
} else if (StringUtils.equals(unit, UNIT_YEAR)) {
date = localDateTime.plusYears(quantity);
}
return date;
}
}

View File

@ -0,0 +1,8 @@
spring.application.name=workstation
server.port=8007
management.server.port=7007
#
quartz.enabled=false
quartz.scheduler-name=workstationScheduler
#
logging.file.path=/opt/metersphere/logs/workstation

View File

@ -0,0 +1,310 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<property resource="application.properties"/>
<property resource="commons.properties"/>
<property file="/opt/metersphere/conf/metersphere.properties"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<appender name="debugAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<File>${logging.file.path}/debug.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logging.file.path}/history/debug.%d{yyyyMMdd}-%i.log
</FileNamePattern>
<maxHistory>${logger.max.history:-30}</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<Pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</Pattern>
</encoder>
</appender>
<appender name="infoAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<File>${logging.file.path}/info.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logging.file.path}/history/info.%d{yyyyMMdd}-%i.log
</FileNamePattern>
<maxHistory>${logger.max.history:-30}</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<Pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</Pattern>
</encoder>
</appender>
<appender name="errorAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<File>${logging.file.path}/error.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logging.file.path}/history/error.%d{yyyyMMdd}-%i.log
</FileNamePattern>
<maxHistory>${logger.max.history:-30}</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<Pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</Pattern>
</encoder>
</appender>
<appender name="warnAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<File>${logging.file.path}/warn.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logging.file.path}/history/warn.%d{yyyyMMdd}-%i.log
</FileNamePattern>
<maxHistory>${logger.max.history:-30}</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<Pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</Pattern>
</encoder>
</appender>
<appender name="consoleAsyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<queueSize>10000</queueSize>
<appender-ref ref="console"/>
</appender>
<appender name="debugAsyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<queueSize>10000</queueSize>
<appender-ref ref="debugAppender"/>
</appender>
<appender name="infoAsyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<queueSize>10000</queueSize>
<appender-ref ref="infoAppender"/>
</appender>
<appender name="errorAsyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<queueSize>10000</queueSize>
<includeCallerData>true</includeCallerData>
<appender-ref ref="errorAppender"/>
</appender>
<appender name="warnAsyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<queueSize>10000</queueSize>
<includeCallerData>true</includeCallerData>
<appender-ref ref="warnAppender"/>
</appender>
<!-- 系统调用日志输出到指定文件 -->
<appender name="backendApiAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<File>${logging.file.path}/api/info.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logging.file.path}/history/api/info.%d{yyyyMMdd}-%i.log
</FileNamePattern>
<maxHistory>${logger.max.history:-30}</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<Pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</Pattern>
</encoder>
</appender>
<appender name="backendApiAsyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<queueSize>10000</queueSize>
<appender-ref ref="backendApiAppender"/>
</appender>
<logger name="io.metersphere.controller.handler.WebLogAspect" additivity="false">
<level value="${logger.level:INFO}"/>
<appender-ref ref="backendApiAsyncAppender"/>
</logger>
<!-- eureka -->
<appender name="eurekaAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<File>${logging.file.path}/eureka/info.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logging.file.path}/history/eureka/info.%d{yyyyMMdd}-%i.log
</FileNamePattern>
<maxHistory>${logger.max.history:-30}</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<Pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</Pattern>
</encoder>
</appender>
<appender name="eurekaAsyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<queueSize>10000</queueSize>
<appender-ref ref="eurekaAppender"/>
</appender>
<logger name="com.netflix" additivity="false">
<level value="${logger.level:INFO}"/>
<appender-ref ref="eurekaAsyncAppender"/>
</logger>
<!-- 自定义测试计划执行的日志 -->
<appender name="infoTestPlanAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<File>${logging.file.path}/testPlan/info.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logging.file.path}/history/testPlan/info.%d{yyyyMMdd}-%i.log
</FileNamePattern>
<maxHistory>${logger.max.history:-30}</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<Pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</Pattern>
</encoder>
</appender>
<appender name="infoTestPlanAsyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<queueSize>10000</queueSize>
<appender-ref ref="infoTestPlanAppender"/>
</appender>
<logger name="testPlanExecuteLog" additivity="false">
<level value="${logger.level:INFO}"/>
<appender-ref ref="infoTestPlanAsyncAppender"/>
</logger>
<!-- 自定义执行过程日志 -->
<appender name="apiRunLogAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<File>${logging.file.path}/ms-jmeter-run-log.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logging.file.path}/history/ms-jmeter-run-log.%d{yyyyMMdd}-%i.log
</FileNamePattern>
<maxHistory>${logger.max.history:-30}</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<Pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</Pattern>
</encoder>
</appender>
<appender name="runLogAppender" class="ch.qos.logback.classic.AsyncAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<queueSize>10000</queueSize>
<appender-ref ref="apiRunLogAppender"/>
</appender>
<logger name="ms-jmeter-run-log" additivity="false">
<level value="${logger.level:INFO}"/>
<appender-ref ref="runLogAppender"/>
</logger>
<!-- 自定义JMETER输出日志 -->
<root level="INFO">
<appender-ref ref="infoAsyncAppender"/>
<appender-ref ref="console"/>
</root>
<logger name="io.metersphere" additivity="false">
<level value="${logger.level:INFO}"/>
<appender-ref ref="infoAsyncAppender"/>
<appender-ref ref="warnAsyncAppender"/>
<appender-ref ref="errorAsyncAppender"/>
</logger>
<logger name="io.metersphere.base.mapper" level="${logger.sql.level}">
<appender-ref ref="console"/>
</logger>
<!-- <logger name="io.metersphere.xpack.mapper" level="${logger.sql.level}">-->
<!-- <appender-ref ref="console"/>-->
<!-- </logger>-->
<logger name="io.metersphere.WorkstationApplication" additivity="false" level="${logger.level:INFO}">
<appender-ref ref="infoAsyncAppender"/>
</logger>
<!-- <logger name="com.alibaba.nacos.naming.client.listener" additivity="false" level="ERROR"/>-->
<!-- <logger name="org.apache.dubbo" additivity="false" level="ERROR"/>-->
</configuration>

View File

@ -0,0 +1,4 @@
{
"permissions": [],
"resource": []
}

View File

@ -0,0 +1,14 @@
# https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

30
workstation/frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
.DS_Store
node_modules
node/
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
*.iml
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.settings
.project
.classpath
yarn.lock
package-lock.json
pnpm-lock.yaml
.tmp_npm/

View File

@ -0,0 +1,3 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"]
};

View File

@ -0,0 +1,113 @@
{
"name": "workstation",
"version": "1.0.0",
"private": true,
"scripts": {
"workstation": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@ckeditor/ckeditor5-build-classic": "^18.0.0",
"@ckeditor/ckeditor5-vue": "^1.0.1",
"@form-create/element-ui": "^2.5.8",
"@fortawesome/fontawesome-svg-core": "^1.2.26",
"@fortawesome/free-brands-svg-icons": "^5.13.0",
"@fortawesome/free-regular-svg-icons": "^5.12.0",
"@fortawesome/free-solid-svg-icons": "^5.12.0",
"@fortawesome/vue-fontawesome": "^0.1.9",
"axios": "^0.27.2",
"diffable-html": "^4.0.0",
"echarts": "^5.0.2",
"el-table-infinite-scroll": "^1.0.10",
"el-tree-transfer": "^2.4.7",
"element-resize-detector": "^1.2.4",
"element-ui": "^2.15.7",
"fit2cloud-ui": "^1.8.0",
"html2canvas": "^1.0.0-rc.7",
"js-base64": "^3.4.4",
"jsencrypt": "^3.1.0",
"json-bigint": "^1.0.0",
"json-schema-faker": "^0.5.0-rcv.32",
"json5": "^2.1.3",
"jsondiffpatch": "^0.4.1",
"jsoneditor": "^9.5.6",
"jsonpath": "^1.1.0",
"jspdf": "^2.3.1",
"lodash.isboolean": "^3.0.3",
"lodash.isempty": "^4.4.0",
"lodash.isinteger": "^4.0.4",
"lodash.isnull": "^3.0.0",
"lodash.isnumber": "^3.0.3",
"lodash.isobject": "^3.0.2",
"lodash.isstring": "^4.0.1",
"mavon-editor": "2.9.1",
"md5": "^2.3.0",
"metersphere-frontend": "file:../../framework/sdk-parent/frontend",
"mockjs": "^1.1.0",
"normalize.css": "^8.0.1",
"nprogress": "^0.2.0",
"pdfjs-dist": "2.5.207",
"pinia": "^2.0.14",
"pinia-plugin-persistedstate": "^1.6.3",
"sha.js": "^2.4.11",
"vue": "^2.7.3",
"vue-calendar-heatmap": "^0.8.4",
"vue-clipboard2": "^0.3.1",
"vue-echarts": "^6.0.0",
"vue-float-action-button": "^0.6.6",
"vue-i18n": "^8.15.3",
"vue-jsonpath-picker": "^1.1.5",
"vue-minder-editor-plus": "1.1.2",
"vue-papa-parse": "^2.0.0",
"vue-pdf": "^4.2.0",
"vue-router": "^3.1.3",
"vue-virtual-scroll-list": "^2.3.3",
"vue2-ace-editor": "0.0.15",
"vuedraggable": "^2.24.3",
"xml-js": "^1.6.11",
"yan-progress": "^1.0.3"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "^5.0.7",
"@vue/cli-plugin-eslint": "^5.0.7",
"@vue/cli-service": "^5.0.7",
"core-js": "^3.19.1",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^7.20.0",
"mockjs": "^1.1.0",
"sass": "^1.43.4",
"sass-loader": "^10.1.1",
"svg-sprite-loader": "^6.0.11",
"vue-template-compiler": "^2.7.3"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {
"vue/no-unused-components": "off",
"no-console": "off",
"no-unused-vars": "off",
"no-unused-expressions": "off",
"no-unused-labels": "off",
"no-undef": "off",
"no-useless-escape": "off"
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<packaging>pom</packaging>
<parent>
<groupId>io.metersphere</groupId>
<artifactId>workstation-parent</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>workstation-frontend</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>${frontend-maven-plugin.version}</version>
<executions>
<execution>
<!-- optional: you don't really need execution ids, but it looks nice in your build log. -->
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<!-- optional: default phase is "generate-resources" -->
<phase>generate-resources</phase>
<configuration>
<nodeVersion>${node.version}</nodeVersion>
<npmVersion>${npm.version}</npmVersion>
</configuration>
</execution>
<!-- Install all project dependencies -->
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<!-- optional: The default argument is actually
"install", so unless you need to run some other yarn command,
you can remove this whole <configuration> section.
-->
<arguments>install</arguments>
</configuration>
</execution>
<!-- Build and minify static files -->
<execution>
<id>npm run build</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<configuration>
<filesets>
<fileset>
<directory>dist</directory>
<includes>
<include>**</include>
</includes>
<followSymlinks>false</followSymlinks>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>
</build>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="">
<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="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -0,0 +1,5 @@
<template>
<div id="app">
<router-view/>
</div>
</template>

View File

@ -0,0 +1,71 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
const API_URL = '/api/definition/';
const CASE_URL = '/api/testcase/';
const SYNC_URL = '/api/sync/';
/*api*/
export function getDefinitionPage(page, pageSize, params) {
return post(API_URL+'list/' + page + '/' + pageSize, params);
}
export function batchEditByParams(params) {
return post(API_URL+'edit-batch', params);
}
export function getApiReportDetail(id) {
let url = API_URL+'report/get/' + id;
return get(url);
}
export function getDefinitionById(id) {
return get('/api/definition/get/' + id);
}
/*case*/
export function editApiTestCaseOrder(request, callback) {
return post(CASE_URL+'edit/order', request, callback);
}
export function apiTestCasePage(page, pageSize, params) {
return post(CASE_URL+'list/' + page + '/' + pageSize, params);
}
export function getApiCaseById(id) {
return get(CASE_URL+'get/' + id);
}
export function getCaseById(id) {
return get(CASE_URL+'get-details/' + id);
}
export function editApiCaseByParam(params) {
return post(CASE_URL+'edit-batch', params);
}
export function testCaseBatchRun(params) {
return post(CASE_URL+'batch/run', params);
}
/*sync*/
export function batchSyncCase(params) {
return post(SYNC_URL+'case/batch', params);
}
export function batchIgnoreCase(params) {
return post(SYNC_URL+'case/batch/ignore', params);
}
export function batchIgnoreApi(params) {
return post(SYNC_URL+'/batch/ignore', params);
}
export function batchSyncApi(params){
return post(SYNC_URL+'batch', params);
}

View File

@ -0,0 +1,48 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
export function getApiModules(projectId, protocol, currentVersion) {
let url = '/api/module/list/' + projectId + '/' + protocol + (currentVersion ? '/' + currentVersion : '');
return get(url);
}
export function getApiModuleByProjectIdAndProtocol(projectId, protocol) {
let url = '/api/module/list/' + projectId + '/' + protocol;
return get(url);
}
export function getApiModuleByTrash(projectId, protocol, currentVersion) {
let url = '/api/module/trash/list/' + projectId + '/' + protocol + '/' + (currentVersion ? '/' + currentVersion : '');
return get(url);
}
export function getUserDefaultApiType() {
let url = '/api/module/default-type';
return get(url);
}
export function trashCount(projectId, currentProtocol) {
let url = '/api/module/trash-count/' + projectId + '/' + currentProtocol;
return get(url);
}
export function editModule(param) {
return post('/api/module/edit', param);
}
export function addModule(param) {
return post('/api/module/add', param);
}
export function delModule(param) {
return post('/api/module/delete', param);
}
export function dragModule(param) {
return post('/api/module/drag', param);
}
export function posModule(param) {
return post('/api/module/pos', param);
}

View File

@ -0,0 +1,53 @@
import {get, post} from 'metersphere-frontend/src/plugins/request';
export function getEnvironmentPages(goPage, pageSize, param) {
return post(`/environment/list/${goPage}/${pageSize}`, param);
}
export function getEnvironments(projectId) {
return get(`/environment/list/${projectId}`);
}
export function delEnvironmentById(envId) {
return get(`/environment/delete/${envId}`);
}
export function getEnvironmentGroupPages(goPage, pageSize, param) {
return post(`/environment/group/list/${goPage}/${pageSize}`, param);
}
export function getAllEnvironmentGroups(param) {
return post('/environment/group/get/all', param);
}
export function copyEnvironmentGroup(groupId) {
return get(`/environment/group/copy/${groupId}`);
}
export function delEnvironmentGroup(groupId) {
return get(`/environment/group/delete/${groupId}`);
}
export function modifyEnvironmentGroup(group) {
return post('/environment/group/modify', group);
}
export function updateEnvironmentGroup(group) {
return post('/environment/group/update', group);
}
export function addEnvironmentGroup(group) {
return post('/environment/group/add', group);
}
export function getEnvGroupProject(environmentId) {
return get(`/environment/group/project/list/${environmentId}`);
}
export function environmentGetALl() {
return post('/environment/group/get/all', {});
}
export function getEnvironmentOptions(params) {
return post('/environment/group/get/option', params);
}

View File

@ -0,0 +1,26 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
export function apiCountByProjectId(projectId) {
return get('/home/api/count/' + projectId);
}
export function countApiCoverageByProjectId(projectId) {
return get('/home/api/coverage/' + projectId);
}
export function apiCaseCountByProjectId(projectId) {
return get('/home/api/case/count/' + projectId);
}
export function scheduleTaskCountByProjectId(projectId) {
return get('/home/schedule/task/count/' + projectId);
}
export function apiRunningTask(id, params) {
return post('/home/running/task/' + id, params);
}
export function databaseValidate(params) {
return post('/home/database/validate', params);
}

View File

@ -0,0 +1,24 @@
import {getUUID} from "metersphere-frontend/src/utils";
import {get, request} from "metersphere-frontend/src/plugins/request"
export function uploadMarkDownImg(file) {
let param = {
id: getUUID().substring(0, 8)
};
file.prefix = param.id;
param.fileName = file.name.substring(file.name.lastIndexOf('.'));
let config = {
method: 'POST',
url: '/plugin/add',
data: param,
headers: {
'Content-Type': undefined
}
};
return request(config);
}
export function deleteMarkDownImg(file) {
return get('/resource/md/delete/' + file[1].name);
}

View File

@ -0,0 +1,78 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import {getIssueTemplate} from "metersphere-frontend/src/api/custom-field-template";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getUUID} from "metersphere-frontend/src/utils";
import {getCurrentProject} from "@/api/project";
import {JIRA} from "metersphere-frontend/src/utils/constants";
export function getJiraIssueType(param) {
return post('/issues/jira/issuetype', param);
}
export function getIssues(page) {
return post(`issues/list/${page.currentPage}/${page.pageSize}`, page.condition);
}
export function syncIssues(success) {
let url = 'issues/sync/';
if (hasLicense()) {
url = 'xpack/issue/sync/';
}
// 浏览器默认策略请求同一个url可能导致 stalled 时间过长加个uuid防止请求阻塞
url = url + getCurrentProjectID() + "?stamp=" + getUUID();
return get(url, success);
}
export function getDashboardIssues(page) {
return post(`issues/dashboard/list/${page.currentPage}/${page.pageSize}`, page.condition);
}
export function getIssuePartTemplateWithProject(callback) {
getCurrentProject().then((response) => {
let currentProject = response.data;
if (enableThirdPartTemplate(currentProject)) {
getIssueThirdPartTemplate()
.then((template) => {
if (callback)
callback(template, currentProject);
});
} else {
getIssueTemplate()
.then((template) => {
if (callback)
callback(template, currentProject);
});
}
});
}
export function enableThirdPartTemplate(currentProject) {
return currentProject && currentProject.thirdPartTemplate && currentProject.platform === JIRA;
}
export function getIssueThirdPartTemplate() {
return get('/issues/thirdpart/template/' + getCurrentProjectID())
.then((response) => {
let template = response.data;
if (template.customFields) {
template.customFields.forEach(item => {
if (item.options) {
item.options = JSON.parse(item.options);
}
});
}
return template
});
}
export function getIssuesCount(param) {
return post('/issues/status/count', param);
}
export function getIssuesWeekCount(workstationId) {
return get('/workstation/issue/week/count/'+workstationId);
}

View File

@ -0,0 +1,7 @@
import {post} from "metersphere-frontend/src/plugins/request"
export function saveLicense(data) {
return post("/samples/license/save", data)
}

View File

@ -0,0 +1,6 @@
import {post} from "metersphere-frontend/src/plugins/request";
export function editLoadTestCaseOrder(request, callback) {
return post('/performance/edit/order', request, callback);
}

View File

@ -0,0 +1,226 @@
import {get, post, request} from "metersphere-frontend/src/plugins/request"
export function getDashboardHeatmap() {
return get('/performance/dashboard/tests')
}
export function getRecentTests(condition) {
return post('/performance/recent/5', condition)
}
export function getRecentReports(condition) {
return post('/performance/report/recent/5', condition)
}
export function getReportCount(testId) {
return get(`/performance/test/report-count/${testId}`)
}
export function getPerformanceVersions(testId) {
return get(`/performance/versions/${testId}`)
}
export function deleteCurrentVersionTest(test) {
return get(`performance/delete/${test.versionId}/${test.refId}`);
}
export function searchTests(goPage, pageSize, condition) {
return post(`/performance/list/${goPage}/${pageSize}`, condition)
}
export function copyTest(test) {
return post(`/performance/copy`, {id: test.id})
}
export function runTest(test) {
return post('/performance/run', {id: test.id, triggerMode: 'MANUAL'})
}
export function deleteTest(data) {
return post(`/performance/delete`, data)
}
export function deleteTestBatch(data) {
return post(`/performance/delete/batch`, data)
}
export function getTest(id) {
return get(`/performance/get/${id}`)
}
export function getFollows(id) {
return get(`/performance/test/follow/${id}`)
}
export function getTestVersionHistory(id) {
return get(`/performance/versions/${id}`)
}
export function getTestByVersion(versionId, refId) {
return get(`/performance/get/${versionId}/${refId}`)
}
export function syncScenario(param) {
return get(`/performance/sync/scenario`, param)
}
export function saveSchedule(param) {
let url = '/performance/schedule/create';
if (param.id) {
url = '/performance/schedule/update';
}
return post(url, param);
}
export function saveTest(test, formData) {
let savePath = "/performance/save"
let editPath = "/performance/edit"
let url = test.id ? editPath : savePath;
let config = {
method: 'POST',
url: url,
data: formData,
headers: {
'Content-Type': undefined
}
}
return request(config);
}
export function saveFollows(testId, param) {
return post(`/performance/test/update/follows/${testId}`, param)
}
export function getFiles(id) {
return get(`/performance/file/metadata/${id}`)
}
export function getMetadataById(id) {
return get(`/performance/file/getMetadataById/${id}`)
}
export function getResourcePools(isShare) {
let url = '/testresourcepool/list/quota/valid';
if (isShare) {
url = '/share/testresourcepool/list/quota/valid';
}
return get(url);
}
export function getLoadConfig(testId, reportId, isShare) {
let url = '';
if (testId) {
url = '/performance/get-load-config/' + testId;
}
if (reportId) {
url = '/performance/report/get-load-config/' + reportId;
}
if (!url) {
return;
}
if (isShare) {
url = '/share/performance/report/get-load-config/' + reportId;
}
return get(url);
}
export function getJmxContent(testId, reportId, isShare) {
let url = '';
if (testId) {
url = '/performance/get-jmx-content/' + testId;
}
if (reportId) {
url = '/performance/report/get-jmx-content/' + reportId;
}
if (!url) {
return;
}
if (isShare) {
url = '/share/performance/report/get-jmx-content/' + reportId;
}
return get(url)
}
export function getAdvancedConfig(type, testId, reportId, isShare, shareId) {
let url = '/performance/get-advanced-config/' + testId;
if (type) {
url = '/performance/report/get-advanced-config/' + reportId;
}
if (isShare) {
url = '/share/performance/report/get-advanced-config/' + shareId + '/' + reportId;
}
return get(url);
}
export function getJmxContents(jmxIds) {
return post('/performance/export/jmx', jmxIds)
}
export function downloadFile(url, file) {
let data = {
name: file.name,
id: file.id,
};
let config = {
url: url,
method: 'post',
data: data,
responseType: 'blob'
};
return request(config);
}
export function getProjectFiles(type, projectId, currentPage, pageSize, condition) {
return post(`/performance/project/${type}/${projectId}/${currentPage}/${pageSize}`, condition)
}
export function getNoticeTasks(testId) {
return get(`/notice/search/message/${testId}`);
}
export function getProjectFileByName(projectId, data) {
return post(`/performance/file/${projectId}/getMetadataByName`, data)
}
export function uploadFiles(projectId, formData) {
let url = `/project/upload/files/${projectId}`;
let options = {
method: 'POST',
url: url,
data: formData,
headers: {
'Content-Type': undefined
}
};
return request(options);
}
export function updateFile(fileId, formData) {
let url = `/project/upload/file/${fileId}`;
let options = {
method: 'POST',
url: url,
data: formData,
headers: {
'Content-Type': undefined
}
};
return request(options);
}
export function deleteFile(fileId) {
return get(`/project/delete/file/${fileId}`)
}
export function listSchedule(data) {
return post('/performance/list/schedule', data)
}
export function updateSchedule(schedule) {
return post('/performance/schedule/update', schedule);
}

View File

@ -0,0 +1,39 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
export function listAllProject() {
return get("/project/listAll")
}
export function getProject(id) {
return get(`/project/get/${id}`)
}
export function getProjectMemberSize(id) {
return get(`/project/member/size/${id}`)
}
export function versionEnableByProjectId(projectId) {
return get('/project/version/enable/' + projectId);
}
export function getUserProjectList(data) {
return post("/project/list/related", data)
}
export function getOwnerProjectIds() {
let url = '/api/project/get/owner/ids';
return get(url);
}
export function getOwnerProjects() {
return get('/api/project/get/owner/details');
}
export function getCurrentProject(callback) {
return getProject(getCurrentProjectID(), callback);
}
export function getProjectApplication(projectId){
return get('/project_application/get/config/' + projectId + '/OPEN_UPDATE_RULE')
}

View File

@ -0,0 +1,156 @@
import {get, post, request} from 'metersphere-frontend/src/plugins/request'
export function getScenarioById(scenarioId) {
return get('/api/automation/get/' + scenarioId);
}
export function getScenarioWithBLOBsById(scenarioId) {
return get('/api/automation/scenario-details/' + scenarioId);
}
export function getScenarioList(currentPage, pageSize, condition) {
let url = '/api/automation/list/' + currentPage + '/' + pageSize;
return post(url, condition);
}
export function getScenarioByTrash(condition) {
return post('/api/automation/list/all/trash', condition);
}
export function getScenarioByProjectId(projectId) {
return get('/api/automation/env-project-ids/' + projectId);
}
export function checkScenarioEnv(scenarioId) {
return get('/api/automation/env-valid' + scenarioId);
}
export function execStop(reportId) {
return get('/api/automation/stop/' + reportId);
}
export function execBatchStop(res) {
return post('/api/automation/stop/batch', res);
}
export function getFollowByScenarioId(scenarioId) {
return get('/api/automation/follow/' + scenarioId);
}
export function getScenarioVersions(scenarioId) {
return get('/api/automation/versions/' + scenarioId);
}
export function delByScenarioId(scenarioId) {
return get('/api/automation/delete/' + scenarioId);
}
export function delByScenarioIdAndRefId(scenarioId, refId) {
return get('/api/automation/delete/' + scenarioId + '/' + refId);
}
export function deleteBatchByCondition(params) {
return post('/api/automation/del-batch', params);
}
export function apiScenarioAll(params) {
return post('/api/automation/id/all/', params);
}
export function getApiScenarios(params) {
return post('/api/automation/get-scenario-list', params);
}
export function getReference(params) {
return post('/api/automation/getReference', params);
}
export function genPerformanceTestJmx(params) {
return post('/api/automation/gen-jmx', params);
}
export function apiScenarioEnv(params) {
return post('/api/automation/env', params);
}
export function getApiScenarioProjectIdByConditions(params) {
return post('/api/automation/list-project-ids', params);
}
export function createSchedule(params) {
return post('/api/automation/schedule/create', params);
}
export function updateSchedule(params) {
return post('/api/automation/schedule/update', params);
}
export function setScenarioDomain(params) {
return post('/api/automation/set-domain', params);
}
export function listWithIds(params) {
return post('/api/automation/list-blobs', params);
}
export function getApiScenarioEnv(params) {
return post('/api/automation/scenario-env', params);
}
export function batchEditScenario(params) {
return post('/api/automation/batch/edit', params);
}
export function batchCopyScenario(params) {
return post('/api/automation/batch/copy', params);
}
export function updateScenarioEnv(params) {
return post('/api/automation/batch/update/env', params);
}
export function scenarioPlan(params) {
return post('/api/automation/scenario/plan', params);
}
export function runBatch(params) {
return post('/api/automation/run/batch', params);
}
export function scenarioRun(params) {
return post('/api/automation/run', params);
}
export function scenarioReduction(params) {
return post('/api/automation/reduction', params);
}
export function scenarioAllIds(params) {
return post('/api/automation/id/all', params);
}
export function checkBeforeDelete(params) {
return post('/api/automation/get-del-details', params);
}
export function removeScenarioToGcByBatch(params) {
return post('/api/automation/move-gc-batch', params);
}
export function exportScenario(params) {
return post('/api/automation/export', params);
}
export function batchGenPerformanceTestJmx(params) {
return post('/api/automation/gen-jmx-batch', params);
}
export function updateScenarioFollows(id, params) {
return post('/api/automation/update/follows/' + id, params);
}
export function getScenarioReference(params) {
return post('/api/automation/getReference', params);
}

View File

@ -0,0 +1,40 @@
import {post,get} from "metersphere-frontend/src/plugins/request";
export function editTestCaseOrder(request, callback) {
return post('/test/case/edit/order', request, callback);
}
export function getTestCasePages(goPage, pageSize, param) {
return post(`/test/case/list/${goPage}/${pageSize}`, param);
}
export function getTestCaseListById(id) {
return get(`test/case/get/${id}`);
}
export function getTestCaseReviewPages(goPage, pageSize, param) {
return post(`/test/case/review/list/${goPage}/${pageSize}`, param);
}
export function getTestCaseReviewProject(param) {
return post(`/test/case/review/project`,param);
}
export function getTestCaseReviewer(param) {
return post(`/test/case/review/reviewer`,param);
}
export function getTestCaseStep(id) {
return get( `/test/case/get/step/${id}`);
}
export function buildPagePath({pageNum, pageSize, path}) {
return path + "/" + pageNum + "/" + pageSize;
}
export function testCaseList({pageNum, pageSize}, param) {
let url = buildPagePath({pageNum, pageSize, path: 'list'});
return post('/test/case/' + url, param);
}

View File

@ -0,0 +1,23 @@
import {post,get} from "metersphere-frontend/src/plugins/request";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
export function getPlanStageOption(projectID) {
return get('/test/plan/get/stage/option/' + projectID);
}
export function getPrincipalById(id) {
return get('/test/plan/principal/' + id);
}
export function getDashboardPlanList(goPage, pageSize,param) {
return post('/test/plan/dashboard/list/' + goPage+ "/" + pageSize,param);
}
export function getPlanList(goPage, pageSize,param) {
return post('/test/plan/list/' + goPage+ "/" + pageSize,param);
}
export function editPlan(param) {
return post('/test/plan/edit',param);
}

View File

@ -0,0 +1,7 @@
import {get} from "metersphere-frontend/src/plugins/request"
export function getTestResourcePools() {
let url = '/testresourcepool/list/quota/valid';
return get(url);
}

View File

@ -0,0 +1,6 @@
import {get} from 'metersphere-frontend/src/plugins/request'
export function listUsers(page, size) {
return get(`/samples/user-management/list/${page}/${size}`)
}

View File

@ -0,0 +1,19 @@
/* 前后端不分离的登录方式 */
import {get, post, put} from "metersphere-frontend/src/plugins/request"
export function getProjectMember() {
return get('/user/project/member/list');
}
export function getProjectMemberUserFilter(callBack) {
return get('/user/project/member/list').then((r) => {
if (callBack) {
let filter = r.data.map(u => {
return {text: u.name, value: u.id};
});
callBack(filter);
}
});
}

View File

@ -0,0 +1,13 @@
import {post,get} from "metersphere-frontend/src/plugins/request"
export function getMyCreatedCaseGroupContMap(param) {
return get('/workstation/creat_case_count/list/'+param);
}
export function getFollowTotalCount(workstationId) {
return get('/workstation/follow/total/count/'+ workstationId);
}
export function getUpcomingTotalCount(workstationId) {
return get('/workstation/coming/total/count/'+ workstationId);
}

View File

@ -0,0 +1,35 @@
<template>
<el-col>
<w-s-header/>
<div>
<transition>
<keep-alive>
<router-view/>
</keep-alive>
</transition>
</div>
</el-col>
</template>
<script>
import WSHeader from "./head/WSHeader";
export default {
name: "Workstation",
components: {WSHeader},
data() {
return {
baseUrl: "workstation"
}
},
activated() {
this.$refs.table.doLayout()
},
mounted() {
console.log('workstation')
}
}
</script>
<style scoped>
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,736 @@
<template>
<div>
<div class="changeTap" v-if="showTap">
<span :class="isFinish ? showTextColor: unShowTextColor"
@click="changeTabState('finish')">{{ $t('commons.to_be_completed') }}</span><span></span><span
@click="changeTabState('update')"
:class="isFinish ? unShowTextColor: showTextColor">{{ $t('commons.pending_upgrade') }}</span>
</div>
<ms-table
class="table-card-nopadding"
:table-is-loading="this.result"
:data="tableData"
:screen-height="isRelate ? 'calc(100vh - 400px)' : screenHeight"
:condition="condition"
:page-size="pageSize"
:enableSelection="false"
:total="total"
:fields.sync="fields"
:field-key=tableHeaderKey
:remember-order="true"
row-key="id"
:row-order-group-id="condition.projectId"
@refresh="search(projectId)"
@callBackSelectAll="callBackSelectAll"
@callBackSelect="callBackSelect"
ref="scenarioTable">
<ms-table-column
prop="deleteTime"
sortable
v-if="this.trashEnable"
:fields-width="fieldsWidth"
:label="$t('commons.delete_time')"
min-width="150px">
<template v-slot:default="scope">
<span>{{ scope.row.deleteTime | datetimeFormat }}</span>
</template>
</ms-table-column>
<ms-table-column
prop="deleteUser"
:fields-width="fieldsWidth"
v-if="this.trashEnable"
:label="$t('commons.delete_user')"
min-width="120"/>
<span v-for="(item) in fields" :key="item.key">
<ms-table-column v-if="item.id === 'num' && !customNum"
prop="num"
label="ID"
sortable
:fields-width="fieldsWidth"
min-width="120px">
<template slot-scope="scope">
<!--<span style="cursor:pointer" v-if="isReadOnly"> {{ scope.row.num }} </span>-->
<el-tooltip :content="$t('commons.edit')">
<a style="cursor:pointer" @click="edit(scope.row)"> {{ scope.row.num }} </a>
</el-tooltip>
</template>
</ms-table-column>
<ms-table-column
v-if="item.id === 'num' && customNum" prop="customNum"
label="ID"
sortable
:fields-width="fieldsWidth"
min-width="120px">
<template slot-scope="scope">
<!--<span style="cursor:pointer" v-if="isReadOnly"> {{ scope.row.customNum }} </span>-->
<el-tooltip :content="$t('commons.edit')">
<a style="cursor:pointer" @click="edit(scope.row)"> {{ scope.row.customNum }} </a>
</el-tooltip>
</template>
</ms-table-column>
<ms-table-column prop="name"
sortable
:field="item"
:fields-width="fieldsWidth"
:label="$t('api_test.automation.scenario_name')"
min-width="150px"/>
<ms-table-column
prop="level"
sortable
:field="item"
:fields-width="fieldsWidth"
:filters="apiscenariofilters.LEVEL_FILTERS"
min-width="130px"
:label="$t('api_test.automation.case_level')">
<template v-slot:default="scope">
<priority-table-item :value="scope.row.level"/>
</template>
</ms-table-column>
<ms-table-column
:label="$t('project.version.name')"
:field="item"
:fields-width="fieldsWidth"
min-width="100px"
prop="versionId">
<template v-slot:default="scope">
<span>{{ scope.row.versionName }}</span>
</template>
</ms-table-column>
<ms-table-column prop="status"
:label="$t('test_track.plan.plan_status')"
sortable
:field="item"
:fields-width="fieldsWidth"
:filters="apiscenariofilters.STATUS_FILTERS"
min-width="120px">
<template v-slot:default="scope">
<plan-status-table-item :value="scope.row.status"/>
</template>
</ms-table-column>
<ms-table-column prop="principalName"
min-width="150px"
:label="$t('api_test.definition.api_principal')"
:filters="userFilters"
:field="item"
:fields-width="fieldsWidth"
sortable/>
<ms-table-column prop="tags"
:field="item"
v-if="isShowAllColumn"
:fields-width="fieldsWidth"
min-width="120px"
:showOverflowTooltip="false"
:label="$t('api_test.automation.tag')">
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:content="itemName" :show-tooltip="scope.row.tags.length===1&&itemName.length*12<=120"
tooltip style="margin-left: 0px; margin-right: 2px"/>
<span/>
</template>
</ms-table-column>
<ms-table-column prop="userName" min-width="120px"
:label="$t('api_test.automation.creator')"
:filters="userFilters"
v-if="isShowAllColumn"
:field="item"
:fields-width="fieldsWidth"
sortable="custom"/>
<ms-table-column prop="createTime"
:field="item"
v-if="isFinish"
:fields-width="fieldsWidth"
:label="$t('commons.create_time')"
sortable
min-width="180px">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</ms-table-column>
<ms-table-column prop="lastResult"
:label="$t('api_test.automation.last_result')"
:filters="apiscenariofilters.RESULT_FILTERS"
v-if="!isFinish"
:field="item"
:fields-width="fieldsWidth"
sortable
min-width="130px">
<template v-slot:default="{row}">
<el-link type="success" @click="showReport(row)" v-if="row.lastResult === 'Success'">
{{ $t('api_test.automation.success') }}
</el-link>
<el-link type="danger" @click="showReport(row)" v-else-if="row.lastResult === 'Fail'">
{{ $t('api_test.automation.fail') }}
</el-link>
</template>
</ms-table-column>
<ms-table-column prop="updateTime"
v-if="!isFinish"
:field="item"
:fields-width="fieldsWidth"
:label="$t('api_test.automation.update_time')"
sortable
min-width="180px">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
</ms-table-column>
<ms-table-column prop="projectName"
:field="item"
:fields-width="fieldsWidth"
min-width="120px"
:label="$t('report.project_name')"
/>
</span>
</ms-table>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</div>
</template>
<script>
import {getCurrentProjectID, getCurrentUserId, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {getUUID} from "metersphere-frontend/src/utils";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import {API_SCENARIO_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import {API_SCENARIO_LIST} from "metersphere-frontend/src/utils/constants";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {getCustomTableWidth, getLastTableSortField,} from "metersphere-frontend/src/utils/tableUtils";
import {API_SCENARIO_FILTERS} from "metersphere-frontend/src/utils/table-constants";
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import HeaderLabelOperate from "metersphere-frontend/src/components/head/HeaderLabelOperate";
import {TYPE_TO_C} from "@/business/module/api/scenario/Setting";
import {getCustomTableHeaderByXpack} from "@/business/component/js/table-head-util";
import {getProjectVersions} from "metersphere-frontend/src/api/version";
import {useApiStore} from "@/store";
import {getScenarioById, getScenarioList} from "@/api/scenario";
import {getProject} from "@/api/project";
import {getProjectMember} from "@/api/user";
export default {
name: "AutomationTableList",
components: {
MsTable,
MsTag,
MsTableColumn,
HeaderLabelOperate,
HeaderCustom: () => import("metersphere-frontend/src/components/head/HeaderCustom"),
BatchMove: () => import("@/business/module/api/BatchMove"),
EnvironmentSelect: () => import("@/business/module/environment/EnvSelect"),
BatchEdit: () => import("@/business/module/api/BatchEdit"),
PlanStatusTableItem: () => import("@/business/module/plan/PlanStatusTableItem"),
PriorityTableItem: () => import("@/business/module/track/PriorityTableItem"),
MsTablePagination: () => import("metersphere-frontend/src/components/pagination/TablePagination"),
},
props: {
referenced: {
type: Boolean,
default: false,
},
isReferenceTable: {
type: Boolean,
default: false,
},
selectNodeIds: Array,
selectProjectId: {
type: String,
default: ""
},
trashEnable: {
type: Boolean,
default: false,
},
moduleTree: {
type: Array,
default() {
return [];
},
},
moduleOptions: {
type: Array,
default() {
return [];
},
},
//
isReadOnly: {
type: Boolean,
default: false,
},
initApiTableOpretion: String,
isRelate: Boolean,
isFocus: {
type: Boolean,
default: false,
},
isCreation: {
type: Boolean,
default: false,
},
isShowAllColumn: {
type: Boolean,
default: true,
},
isSelectAll: {
type: Boolean,
default: false,
},
showTap: {
type: Boolean,
default: false,
},
currentVersion: String,
screenHeight: {
type: [Number, String],
default() {
return 'calc(100vh - 218px)';
}
}, //
},
data() {
return {
showTextColor: "showTextColor",
unShowTextColor: "unShowTextColor",
isFinish: true,
projectName: "",
result: false,
tableHeaderKey: "API_SCENARIO",
type: API_SCENARIO_LIST,
fields: getCustomTableHeaderByXpack('API_SCENARIO_HEAD'),
fieldsWidth: getCustomTableWidth('API_SCENARIO'),
condition: {
components: API_SCENARIO_CONFIGS,
},
scenarioId: "",
currentScenario: {},
schedule: {},
tableData: [],
selectDataRange: 'all',
currentPage: 1,
pageSize: 10,
total: 0,
reportId: "",
showReportId: "",
projectEnvMap: new Map(),
batchReportId: "",
content: {},
infoDb: false,
runVisible: false,
showReportVisible: false,
planVisible: false,
runData: [],
report: {},
selectDataSize: 0,
selectAll: false,
userFilters: [],
operators: [],
selectRows: new Set(),
isStop: false,
enableOrderDrag: true,
debugData: {},
buttons: [],
typeArr: [
{id: 'level', name: this.$t('test_track.case.priority')},
{id: 'status', name: this.$t('test_track.plan.plan_status')},
{
id: 'principal',
name: this.$t('api_test.definition.request.responsible'),
optionMethod: this.getPrincipalOptions
},
{id: 'projectEnv', name: this.$t('api_test.definition.request.run_env')},
],
valueArr: {
level: [
{name: 'P0', id: 'P0'},
{name: 'P1', id: 'P1'},
{name: 'P2', id: 'P2'},
{name: 'P3', id: 'P3'}
],
status: [
{name: this.$t('test_track.plan.plan_status_prepare'), id: 'Prepare'},
{name: this.$t('test_track.plan.plan_status_running'), id: 'Underway'},
{name: this.$t('test_track.plan.plan_status_completed'), id: 'Completed'}
],
principal: [],
environmentId: [],
projectEnv: [],
projectId: ''
},
apiscenariofilters: {},
versionFilters: [],
store:{}
};
},
created() {
this.store = useApiStore();
this.apiscenariofilters = API_SCENARIO_FILTERS();
this.$EventBus.$on('hide', id => {
this.hideStopBtn(id);
});
this.projectId = getCurrentProjectID();
if (!this.projectName || this.projectName === "") {
this.getProjectName();
}
if (this.isFocus) {
if (this.condition.filters) {
delete this.condition.filters['user_id']
}
this.condition.combine = {followPeople: {operator: "current user", value: "current user",}}
} else if (this.isCreation) {
if (this.condition.filters) {
delete this.condition.filters['user_id']
}
this.condition.combine = {creator: {operator: "current user", value: "current user",}}
} else {
if (this.isFinish) {
if (this.condition.combine) {
delete this.condition.combine
}
if (this.condition.filters) {
this.condition.filters.status = ["Prepare", "Underway"];
this.condition.filters.principal = [getCurrentUserId()];
} else {
this.condition.filters = {status: ["Prepare", "Underway"], principal: [getCurrentUserId()]}
}
} else {
this.condition.combine = {status: {operator: "in", value: ["Fail"]}}
if (this.condition.filters) {
this.condition.filters.principal = [getCurrentUserId()];
} else {
this.condition.filters = {principal: [getCurrentUserId()]};
}
}
}
if (this.trashEnable) {
if (this.condition.filters) {
this.condition.filters.status = ["Trash"];
} else {
this.condition.filters = {status: ["Trash"]};
}
this.condition.moduleIds = [];
}
if (this.trashEnable) {
this.condition.orders = [{"name": "delete_time", "type": "desc"}];
} else {
this.condition.orders = getLastTableSortField(this.tableHeaderKey);
}
this.condition.versionId = this.currentVersion;
this.search();
this.getPrincipalOptions([]);
//
if (this.$route.query.resourceId) {
getScenarioById(this.$route.query.resourceId).then((response) => {
this.edit(response.data);
});
}
this.getVersionOptions();
},
beforeDestroy() {
this.$EventBus.$off("hide");
},
watch: {
selectNodeIds() {
this.currentPage = 1;
this.$refs.scenarioTable.clear();
this.selectProjectId ? this.search(this.selectProjectId) : this.search();
},
trashEnable() {
if (this.trashEnable) {
if (this.condition.filters) {
this.condition.filters.status = ["Trash"];
} else {
this.condition.filters = {status: ["Trash"]};
}
this.condition.moduleIds = [];
} else {
if (this.condition.filters) {
this.condition.filters.status = ["Prepare", "Underway", "Completed"];
} else {
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
}
}
this.$refs.scenarioTable.clear();
this.search();
},
isFinish() {
if (!this.isFocus) {
if (this.isFinish) {
delete this.condition.combine
if (this.condition.filters) {
this.condition.filters.status = ["Prepare", "Underway"];
this.condition.filters.principal = [getCurrentUserId()];
} else {
this.condition.filters = {status: ["Prepare", "Underway"], principal: [getCurrentUserId()]}
}
} else {
this.condition.combine = {status: {operator: "in", value: ["Fail"]}}
if (this.condition.filters) {
this.condition.filters.principal = [getCurrentUserId()];
} else {
this.condition.filters = {principal: [getCurrentUserId()]};
}
}
}
this.search();
},
currentVersion() {
this.condition.versionId = this.currentVersion;
// checkbox
this.search();
this.getVersionOptions(this.currentVersion);
},
},
computed: {
isNotRunning() {
return "Running" !== this.report.status;
},
customNum() {
return this.store.currentProjectIsCustomNum;
},
},
methods: {
getProjectName() {
getProject(this.projectId).then(response => {
let project = response.data;
if (project) {
this.projectName = project.name;
}
});
},
search(projectId) {
if (this.needRefreshModule()) {
this.$emit('refreshTree');
}
if (this.selectProjectId) {
projectId = this.selectProjectId;
}
this.selectRows = new Set();
this.condition.moduleIds = this.selectNodeIds;
if (this.trashEnable) {
if (this.condition.filters) {
this.condition.filters.status = ["Trash"];
} else {
this.condition.filters = {status: ["Trash"]};
}
this.condition.moduleIds = [];
}
// todo
if (this.isSelectAll === false) {
if (projectId != null && typeof projectId === 'string') {
this.condition.projectId = projectId;
} else if (this.projectId != null) {
this.condition.projectId = this.projectId;
}
}
this.enableOrderDrag = this.condition.orders.length <= 0;
//
this.condition.selectThisWeedData = false;
this.condition.executeStatus = null;
this.isSelectThissWeekData();
switch (this.selectDataRange) {
case 'thisWeekCount':
this.condition.selectThisWeedData = true;
break;
case 'unExecute':
this.condition.executeStatus = 'unExecute';
break;
case 'executeFailed':
this.condition.executeStatus = 'executeFailed';
break;
case 'executePass':
this.condition.executeStatus = 'executePass';
break;
}
this.condition.workspaceId = getCurrentWorkspaceId();
this.result = getScenarioList(this.currentPage,this.pageSize,this.condition).then(response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.tableData.forEach(item => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
});
this.$emit('getTrashCase');
});
},
getPrincipalOptions(option) {
getProjectMember(response => {
option.push(...response.data);
if (this.isCreation) {
response.data.map(u => {
if (u.id === getCurrentUserId()) {
let a = {text: u.name, value: u.id};
this.userFilters.push(a);
}
});
} else {
this.userFilters = response.data.map(u => {
return {text: u.name, value: u.id};
});
}
});
},
edit(row) {
let uuid = getUUID();
let apiResolve = this.$router.resolve({
path: '/api/automation/'+uuid+'/scenario/edit:'+row.id+'/'+row.projectId+'/'+getCurrentWorkspaceId(),
});
window.open(apiResolve.href, '_blank');
},
sort(stepArray) {
for (let i in stepArray) {
stepArray[i].index = Number(i) + 1;
if (!stepArray[i].resourceId) {
stepArray[i].resourceId = getUUID();
}
if (!stepArray[i].clazzName) {
stepArray[i].clazzName = TYPE_TO_C.get(stepArray[i].type);
}
if (stepArray[i] && stepArray[i].authManager && !stepArray[i].authManager.clazzName) {
stepArray[i].authManager.clazzName = TYPE_TO_C.get(stepArray[i].authManager.type);
}
if (stepArray[i].hashTree && stepArray[i].hashTree.length > 0) {
this.sort(stepArray[i].hashTree);
}
}
},
showReport(row) {
this.showReportVisible = true;
this.infoDb = true;
this.showReportId = row.reportId;
},
//
isSelectThissWeekData() {
let dataRange = this.$route.params.dataSelectRange;
this.selectDataRange = dataRange;
},
changeSelectDataRangeAll() {
this.$emit("changeSelectDataRangeAll");
},
openScenario(item) {
this.$emit('openScenario', item);
},
needRefreshModule() {
if (this.initApiTableOpretion === '0') {
return true;
} else {
this.$emit('updateInitApiTableOpretion', '0');
return false;
}
},
callBackSelectAll(selection) {
this.$emit('selection', selection);
},
callBackSelect(selection) {
this.$emit('selection', selection);
},
hideStopBtn(scenarioId) {
for (let data of this.tableData) {
if (scenarioId && scenarioId === data.id) {
this.$set(data, "isStop", false);
}
}
},
changeTabState(name) {
if (name === 'update') {
this.isFinish = false;
} else {
this.isFinish = true;
}
},
getVersionOptions(currentVersion) {
if (hasLicense()) {
getProjectVersions(getCurrentProjectID()).then(response => {
if (currentVersion) {
this.versionFilters = response.data.filter(u => u.id === currentVersion).map(u => {
return {text: u.name, value: u.id};
});
} else {
this.versionFilters = response.data.map(u => {
return {text: u.name, value: u.id};
});
}
});
}
},
},
};
</script>
<style type="text/css" scoped>
.showTextColor {
color: #783987;
cursor: pointer;
}
.unShowTextColor {
cursor: pointer;
}
.changeTap {
margin: auto;
width: 50%;
text-align: center;
}
</style>
<style scoped>
:deep(.el-drawer__header) {
margin-bottom: 0px;
}
:deep(.el-table__fixed-body-wrapper) {
z-index: auto !important;
}
:deep(.el-table__fixed-right) {
height: 100% !important;
}
:deep(.el-card__header) {
padding: 10px;
}
</style>

View File

@ -0,0 +1,276 @@
<template>
<div>
<ms-table
:table-is-loading="this.loading"
:data="tableData"
:condition="condition"
:page-size="pageSize"
:total="total"
:batch-operators="buttons"
:enable-selection="true"
:fields.sync="fields"
:field-key="fieldKey"
row-key="id"
operator-width="190px"
@refresh="change"
ref="caseTable"
>
<span v-for="(item) in fields" :key="item.key">
<ms-table-column
prop="num"
label="ID"
:field="item"
:fields-width="fieldsWidth"
min-width="80px"
sortable>
<template slot-scope="scope">
<el-tooltip :content="$t('commons.edit')">
<a style="cursor:pointer" @click="openNewCase(scope.row)"> {{ scope.row.num }} </a>
</el-tooltip>
</template>
</ms-table-column>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
prop="name"
sortable
min-width="160px"
:label="$t('test_track.case.name')">
<template slot-scope="scope">
<el-tooltip :content="$t('commons.edit')">
<a style="cursor:pointer" @click="openNewCase(scope.row)"> {{ scope.row.name }} </a>
</el-tooltip>
</template>
</ms-table-column>
<ms-table-column
prop="priority"
:filters="priorityFilters"
:field="item"
:fields-width="fieldsWidth"
min-width="120px"
sortable
:label="$t('test_track.case.priority')">
<template v-slot:default="scope">
<priority-table-item :value="scope.row.priority"></priority-table-item>
</template>
</ms-table-column>
<ms-table-column
prop="caseStatus"
:filters="caseStatusFilters"
:field="item"
:fields-width="fieldsWidth"
min-width="120px"
:label="$t('commons.status')">
<template v-slot:default="scope">
<plan-status-table-item :value="scope.row.caseStatus"/>
</template>
</ms-table-column>
<ms-table-column
prop="execResult"
:filters="statusFilters"
:field="item"
:fields-width="fieldsWidth"
min-width="120px"
:label="$t('test_track.plan_view.execute_result')">
<template v-slot:default="scope">
<el-link :disabled="!scope.row.execResult || scope.row.execResult==='PENDING'">
<ms-api-report-status :status="scope.row.execResult"/>
</el-link>
</template>
</ms-table-column>
<ms-table-column
prop="passRate"
:field="item"
:fields-width="fieldsWidth"
min-width="100px"
:label="$t('commons.pass_rate')">
</ms-table-column>
<ms-table-column
sortable="custom"
prop="path"
min-width="180px"
:field="item"
:fields-width="fieldsWidth"
:label="'API'+ $t('api_test.definition.api_path')"/>
<ms-table-column v-if="item.id==='tags'" prop="tags" width="120px" :label="$t('commons.tag')">
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:show-tooltip="scope.row.tags.length===1&&itemName.length*12<=120"
:content="itemName" style="margin-left: 0px; margin-right: 2px"/>
<span/>
</template>
</ms-table-column>
<ms-table-column
:label="$t('project.version.name')"
:field="item"
:fields-width="fieldsWidth"
:filters="versionFilters"
min-width="100px"
prop="versionId">
<template v-slot:default="scope">
<span>{{ scope.row.versionName }}</span>
</template>
</ms-table-column>
<ms-table-column
prop="environment"
:field="item"
:fields-width="fieldsWidth"
:label="$t('commons.environment')"
>
</ms-table-column>
<ms-table-column
prop="createUser"
:field="item"
:fields-width="fieldsWidth"
:label="$t('commons.create_user')"/>
<ms-table-column
sortable="updateTime"
min-width="160px"
:field="item"
:fields-width="fieldsWidth"
:label="$t('api_test.definition.api_last_time')"
prop="updateTime">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
</ms-table-column>
<ms-table-column
prop="createTime"
:field="item"
:fields-width="fieldsWidth"
:label="$t('commons.create_time')"
sortable
min-width="180px">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</ms-table-column>
</span>
</ms-table>
<ms-table-pagination
:change="change"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"/>
</div>
</template>
<script>
import PriorityTableItem from "@/business/module/track/PriorityTableItem";
import MsTag from "metersphere-frontend/src/components/MsTag";
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import {getCustomTableHeader, getCustomTableWidth} from "metersphere-frontend/src/utils/tableUtils";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import PlanStatusTableItem from "@/business/module/plan/PlanStatusTableItem";
import {REPORT_STATUS} from "@/business/component/js/commons";
import {getApiReportDetail} from "@/api/api";
import MsApiReportStatus from "@/business/module/api/ApiReportStatus";
export default {
name: "BaseApiCaseTable",
data() {
return {
currentPage: 1,
pageSize: 10,
fields: getCustomTableHeader('API_CASE', []),
fieldsWidth: getCustomTableWidth('API_CASE'),
fieldKey: "API_CASE",
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
statusFilters: REPORT_STATUS,
caseStatusFilters: [
{text: this.$t('test_track.plan.plan_status_prepare'), value: 'Prepare'},
{text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'},
{text: this.$t('test_track.plan.plan_status_completed'), value: 'Completed'},
],
}
},
props: {
loading: Boolean,
tableData: Array,
buttons: Array,
total: Number,
versionFilters: Array,
apiDefinitionId: String,
condition: {
type: Object,
default() {
return {};
}
},
},
components: {
PriorityTableItem,
MsTag,
MsTable,
MsTableColumn,
MsTablePagination,
PlanStatusTableItem,
MsApiReportStatus
},
methods: {
change() {
this.$emit("getCaseListById",
this.apiDefinitionId,
this.currentPage,
this.pageSize
);
},
getSelectIds() {
return this.$refs.caseTable.selectIds
},
openNewCase(row) {
this.$emit('openNewCase', row)
},
getStatusClass(status) {
switch (status) {
case "success":
return "ms-success";
case "error":
return "ms-error";
case "Running":
return "ms-running";
default:
return "ms-unexecute";
}
},
getStatusTitle(status) {
switch (status) {
case "success":
return this.$t('api_test.automation.success');
case "error":
return this.$t('api_test.automation.fail');
case "Running":
return this.$t('commons.testing');
default:
return this.$t('api_test.home_page.detail_card.unexecute');
}
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,713 @@
<template>
<div class="card-container">
<ms-table
:table-is-loading="page.result.loading"
:data="page.data"
:enableSelection="false"
:condition="condition"
:total="page.total"
:page-size.sync="page.pageSize"
:screen-height="screenHeight"
:remember-order="true"
row-key="id"
:row-order-group-id="projectId"
:row-order-func="editTestCaseOrder"
@handlePageChange="initTableData"
@handleRowClick="handleEdit"
:fields.sync="fields"
:field-key="tableHeaderKey"
@refresh="initTableData"
ref="table">
<ms-table-column
prop="deleteTime"
sortable
v-if="this.trashEnable"
:fields-width="fieldsWidth"
:label="$t('commons.delete_time')"
min-width="150px">
<template v-slot:default="scope">
<span>{{ scope.row.deleteTime | datetimeFormat }}</span>
</template>
</ms-table-column>
<ms-table-column
prop="deleteUserId"
:fields-width="fieldsWidth"
v-if="this.trashEnable"
:label="$t('commons.delete_user')"
min-width="120"/>
<span v-for="item in fields" :key="item.key">
<ms-table-column
v-if="!customNum"
:field="item"
:fields-width="fieldsWidth"
prop="num"
sortable
:label="$t('commons.id')"
min-width="80"/>
<ms-table-column
v-if="item.id === 'num' && customNum"
:fields-width="fieldsWidth"
prop="customNum"
sortable
:label="$t('commons.id')"
min-width="80"/>
<ms-table-column
prop="name"
sortable
:field="item"
:fields-width="fieldsWidth"
:label="$t('commons.name')"
min-width="120"
/>
<ms-table-column
prop="nodePath"
:field="item"
v-if="isShowAllColumn"
:fields-width="fieldsWidth"
:label="$t('test_track.case.module')"
min-width="150px">
</ms-table-column>
<ms-table-column
prop="projectName"
:field="item"
v-if="!isShowAllColumn"
:fields-width="fieldsWidth"
:label="$t('report.project_name')"
min-width="150px">
</ms-table-column>
<ms-table-column
:label="$t('project.version.name')"
:field="item"
:fields-width="fieldsWidth"
min-width="100px"
prop="versionId">
<template v-slot:default="scope">
<span>{{ scope.row.versionName }}</span>
</template>
</ms-table-column>
<ms-table-column :label="$t('test_track.case.case_desc')" prop="desc" :field="item" min-width="120px"
v-if="isShowAllColumn">
<template v-slot:default="scope">
<el-link @click.stop="getCase(scope.row.id)" style="color:#783887;">{{ $t('commons.preview') }}</el-link>
</template>
</ms-table-column>
<ms-table-column
sortable
prop="createUser"
min-width="120"
:field="item"
:fields-width="fieldsWidth"
:label="$t('commons.create_user')"
:filters="userFilter">
<template v-slot:default="scope">
{{ getCreateUserName(scope.row.createUser) }}
</template>
</ms-table-column>
<test-case-review-status-table-item
:field="item"
:fields-width="fieldsWidth"/>
<test-plan-case-status-table-item
prop="lastExecuteResult"
:field="item"
:fields-width="fieldsWidth"/>
<ms-table-column
prop="tags"
:field="item"
:fields-width="fieldsWidth"
:label="$t('commons.tag')"
min-width="80">
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:show-tooltip="scope.row.tags.length===1&&itemName.length*12<=80"
:content="itemName" style="margin-left: 0px; margin-right: 2px"/>
<span/>
</template>
</ms-table-column>
<ms-table-column
prop="updateTime"
sortable
v-if="isShowAllColumn"
:field="item"
:fields-width="fieldsWidth"
:label="$t('commons.update_time')"
min-width="150px">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
</ms-table-column>
<ms-table-column prop="createTime"
:field="item"
:fields-width="fieldsWidth"
:label="$t('commons.create_time')"
sortable
min-width="150px">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</ms-table-column>
<ms-table-column v-for="field in testCaseTemplate.customFields" :key="field.id"
:filters="getCustomFieldFilter(field)"
:field="item"
:fields-width="fieldsWidth"
:label="field.system ? $t(systemFiledMap[field.name]) :field.name"
:min-width="120"
:prop="field.name">
<template v-slot="scope">
<span v-if="field.name === '用例等级'">
<priority-table-item
:value="getCustomFieldValue(scope.row, field) ? getCustomFieldValue(scope.row, field) : scope.row.priority"/>
</span>
<span v-else>
{{ getCustomFieldValue(scope.row, field) }}
</span>
</template>
</ms-table-column>
</span>
</ms-table>
<ms-table-pagination :change="initTableData" :current-page.sync="page.currentPage" :page-size.sync="page.pageSize"
:total="page.total"/>
<test-case-preview ref="testCasePreview" :loading="rowCaseResult.loading"/>
</div>
</template>
<script>
import {TEST_CASE_LIST} from "metersphere-frontend/src/utils/constants";
import {
getCustomFieldFilter,
getCustomFieldValue, getCustomTableHeader,
getCustomTableWidth,
getLastTableSortField,
getPageInfo, getTableHeaderWithCustomFields,
initCondition, parseCustomFilesForList,
} from "metersphere-frontend/src/utils/tableUtils";
import HeaderLabelOperate from "metersphere-frontend/src/components/head/HeaderLabelOperate";
import {getCurrentProjectID, getCurrentUserId, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {getProjectMember, getProjectMemberUserFilter} from "@/api/user";
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import {SYSTEM_FIELD_NAME_MAP} from "metersphere-frontend/src/utils/table-constants";
import {editTestCaseOrder, getTestCaseListById, getTestCasePages, getTestCaseStep, testCaseList} from "@/api/test-case";
import MsTablePagination from 'metersphere-frontend/src/components/pagination/TablePagination';
import {TEST_CASE_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import PriorityTableItem from "@/business/module/track/PriorityTableItem";
import StatusTableItem from "@/business/module/track/StatusTableItem";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import {getProject} from "@/api/project";
import {getProjectVersions} from "metersphere-frontend/src/api/version";
import useStore, {useApiStore} from "@/store";
import {getTestTemplate} from "metersphere-frontend/src/api/custom-field-template";
import {getAdvSearchCustomField} from "metersphere-frontend/src/components/search/custom-component";
import TestCaseReviewStatusTableItem from "@/business/othermodule/track/TestCaseReviewStatusTableItem";
import TestPlanCaseStatusTableItem from "@/business/othermodule/track/TestPlanCaseStatusTableItem";
import TestCasePreview from "@/business/othermodule/track/TestCasePreview";
import {getUUID, parseTag} from "metersphere-frontend/src/utils";
import {uuid} from "@/model/ApiTestModel";
import {
getCustomFieldValueForTrack,
getCustomTableHeaderByXpack,
getTableHeaderWithCustomFieldsByXpack
} from "@/business/component/js/table-head-util";
export default {
name: "TableList",
components: {
MsTableColumn,
MsTable,
HeaderLabelOperate,
MsTablePagination,
PriorityTableItem,
StatusTableItem,
MsTag,
TestCaseReviewStatusTableItem,
TestPlanCaseStatusTableItem,
TestCasePreview
},
data() {
return {
projectName: "",
type: TEST_CASE_LIST,
tableHeaderKey: "TRACK_TEST_CASE",
tableLabel: [],
condition: {
components: TEST_CASE_CONFIGS,
combine: {
creator: {
operator: "current user",
value: "current user",
}
}
},
versionFilters: [],
statusFilters: [
{text: this.$t('test_track.case.status_prepare'), value: 'Prepare'},
{text: this.$t('test_track.case.status_running'), value: 'Underway'},
{text: this.$t('test_track.case.status_finished'), value: 'Completed'},
],
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
typeArr: [],
valueArr: {},
selectDataRange: "all",
testCaseTemplate: {},
members: [],
page: getPageInfo(),
fields: getCustomTableHeader('TRACK_TEST_CASE'),
fieldsWidth: getCustomTableWidth('TRACK_TEST_CASE'),
memberMap: new Map(),
rowCase: {},
rowCaseResult: {},
store:{},
userFilter:[]
};
},
props: {
treeNodes: {
type: Array
},
trashEnable: {
type: Boolean,
default: false,
},
isFocus: {
type: Boolean,
default: false,
},
isShowAllColumn: {
type: Boolean,
default: true,
},
isSelectAll: {
type: Boolean,
default: false,
},
isCreation: {
type: Boolean,
default: false,
},
currentVersion: String,
screenHeight: {
type: [Number, String],
default() {
return 'calc(100vh - 218px)';
}
}, //
},
computed: {
projectId() {
return getCurrentProjectID();
},
selectNodeIds() {
return this.store.testCaseSelectNodeIds;
},
moduleOptions() {
return this.store.testCaseModuleOptions;
},
systemFiledMap() {
return SYSTEM_FIELD_NAME_MAP;
},
editTestCaseOrder() {
return editTestCaseOrder;
},
customNum() {
return this.store.currentProjectIsCustomNum;
},
},
created () {
getProjectMemberUserFilter((data) => {
this.userFilter = data;
});
if (this.isFocus) {
if (this.condition.filters) {
delete this.condition.filters['user_id']
}
if (this.condition.userId) {
delete this.condition.userId
}
this.condition.combine = {followPeople: {operator: "current user", value: "current user",}}
} else if (this.isCreation) {
if (this.condition.filters) {
delete this.condition.filters['user_id']
}
this.condition.userId = getCurrentUserId();
}
this.getTemplateField();
this.store = useApiStore();
this.$emit('setCondition', this.condition);
if (this.trashEnable) {
if (this.condition.filters) {
this.condition.filters.status = ["Trash"];
} else {
this.condition.filters = {status: ["Trash"]};
}
} else {
if (this.condition.filters) {
this.condition.filters.reviewStatus = ["Prepare", "Pass", "UnPass"];
} else {
this.condition.filters = {reviewStatus: ["Prepare", "Pass", "UnPass"]};
}
}
this.condition.versionId = this.currentVersion;
this.initTableData();
let redirectParam = this.$route.query.dataSelectRange;
this.checkRedirectEditPage(redirectParam);
if (!this.projectName || this.projectName === "") {
this.getProjectName();
}
this.getVersionOptions();
},
activated() {
this.getTemplateField();
if (this.condition.filters) {
this.condition.filters.reviewStatus = ["Prepare", "Pass", "UnPass"];
} else {
this.condition.filters = {reviewStatus: ["Prepare", "Pass", "UnPass"]};
}
let ids = this.$route.params.ids;
if (ids) {
this.condition.ids = ids;
}
this.initTableData();
this.condition.ids = null;
},
watch: {
selectNodeIds() {
this.page.currentPage = 1;
if (!this.trashEnable) {
if (this.condition.filters) {
this.condition.filters.status = [];
} else {
this.condition.filters = {status: []}
}
}
initCondition(this.condition, false);
this.initTableData();
},
condition() {
this.$emit('setCondition', this.condition);
},
trashEnable() {
if (this.trashEnable) {
//
if (this.condition.filters) {
this.condition.filters.status = ["Trash"];
} else {
this.condition.filters = {status: ["Trash"]}
}
this.condition.moduleIds = [];
initCondition(this.condition, false);
this.initTableData();
} else {
if (this.condition.filters) {
this.condition.filters.status = [];
} else {
this.condition.filters = {status: []}
}
}
},
currentVersion() {
this.condition.versionId = this.currentVersion;
this.initTableData();
this.getVersionOptions(this.currentVersion);
},
},
methods: {
getCreateUserName(userId) {
let user = this.userFilter.filter(item => item.value === userId);
return user.length > 0 ? user[0].text : "";
},
getTemplateField() {
this.loading = true;
let p1 = getProjectMember((data) => {
this.members = data;
this.members.forEach(item => {
this.memberMap.set(item.id, item.name);
});
});
let p2 = getTestTemplate();
Promise.all([p1, p2]).then((data) => {
let template = data[1];
this.testCaseTemplate = template;
this.fields = getTableHeaderWithCustomFields(this.tableHeaderKey, this.testCaseTemplate.customFields, this.members);
// todo
this.condition.components = this.condition.components.filter(item => item.custom !== true);
let comp = getAdvSearchCustomField(this.condition, this.testCaseTemplate.customFields);
//
comp.filter(element => {
if (element.label === '责任人') {
element.label = this.$t('custom_field.case_maintainer')
}
if (element.label === '用例等级') {
element.label = this.$t('custom_field.case_priority')
}
if (element.label === '用例状态') {
element.label = this.$t('custom_field.case_status')
// TAB
if (this.trashEnable) {
element.options = [{text: this.$t('test_track.plan.plan_status_trash'), value: 'Trash'}];
} else {
element.options.forEach(option => {
option.text = this.$t(option.text);
});
}
}
})
this.condition.components.push(...comp);
this.setTestCaseDefaultValue(template);
this.$nextTick(() => {
if (this.$refs.table) {
this.$refs.table.resetHeader();
}
this.loading = false;
});
});
},
setTestCaseDefaultValue(template) {
let testCaseDefaultValue = {};
template.customFields.forEach(item => {
if (item.system) {
if (item.defaultValue) {
testCaseDefaultValue[item.name] = JSON.parse(item.defaultValue);
} else {
testCaseDefaultValue[item.name] = "";
}
}
if (item.name === '用例等级') {
item.columnKey = 'priority';
} else if (item.name === '责任人') {
item.columnKey = 'maintainer';
} else if (item.name === '用例状态') {
item.columnKey = 'status';
}
});
useStore().testCaseDefaultValue = testCaseDefaultValue;
},
getCustomFieldValue(row, field) {
let value = getCustomFieldValueForTrack(row, field, this.members);
if (!value) {
if (field.name === '用例等级') {
return row.priority;
}
if (field.name === '责任人') {
return row.maintainerName;
}
if (field.name === '用例状态') {
return row.status;
}
}
return value;
},
getCustomFieldFilter(field) {
if (field.name === '用例状态') {
let option = null;
if (!this.trashEnable) {
option = [];
field.options.forEach((item) => {
option.push({
text: this.$t(item.text),
value: item.value
})
});
}
return option;
}
return getCustomFieldFilter(field, this.userFilter);
},
checkRedirectEditPage(redirectParam) {
if (redirectParam != null) {
getTestCaseListById(id).then(response => {
let testCase = response.data;
testCase.label = "redirect";
this.$emit('testCaseEdit', testCase);
});
}
},
getProjectName() {
getProject(this.projectId).then(response => {
let project = response.data;
if (project) {
this.projectName = project.name;
}
});
},
getSelectDataRange() {
let dataRange = this.$route.params.dataSelectRange;
let dataType = this.$route.params.dataType;
this.selectDataRange = dataType === 'case' ? dataRange : 'all';
},
initTableData() {
this.condition.planId = "";
this.condition.nodeIds = [];
//initCondition(this.condition);
initCondition(this.condition, this.condition.selectAll);
this.condition.orders = getLastTableSortField(this.tableHeaderKey);
if (this.planId) {
// param.planId = this.planId;
this.condition.planId = this.planId;
}
if (!this.trashEnable) {
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
// param.nodeIds = this.selectNodeIds;
this.condition.nodeIds = this.selectNodeIds;
}
}
this.getData();
},
getData() {
this.getSelectDataRange();
this.condition.selectThisWeedData = false;
this.condition.selectThisWeedRelevanceData = false;
this.condition.caseCoverage = null;
this.condition.filters.reviewStatus = ["Prepare", "Pass", "UnPass"];
switch (this.selectDataRange) {
case 'thisWeekCount':
this.condition.selectThisWeedData = true;
break;
case 'thisWeekRelevanceCount':
this.condition.selectThisWeedRelevanceData = true;
break;
case 'uncoverage':
this.condition.caseCoverage = 'uncoverage';
break;
case 'coverage':
this.condition.caseCoverage = 'coverage';
break;
case 'notReviewed':
this.condition.filters.review_status = ['Prepare'];
break;
case 'reviewSuccess':
this.condition.filters.review_status = ['Pass'];
break;
case 'reviewFail':
this.condition.filters.review_status = ['UnPass'];
break;
}
if (this.trashEnable) {
//
let versionIds = this.condition.filters.version_id;
this.condition.filters.status = ["Trash"];
if (versionIds) {
this.condition.filters.version_id = versionIds;
}
}
if (this.projectId) {
this.condition.projectId = this.projectId;
this.$emit('setCondition', this.condition);
this.loading = true;
testCaseList({pageNum: this.page.currentPage, pageSize: this.page.pageSize}, this.condition)
.then(response => {
this.loading = false;
let data = response.data;
this.page.total = data.itemCount;
this.page.data = data.listObject;
parseCustomFilesForList(this.page.data);
parseTag(this.page.data);
this.page.data.forEach(item => {
let nodePath = item.nodePath;
if (item.customFields) {
item.customFields = JSON.parse(item.customFields);
}
if (nodePath.startsWith("/未规划用例", "0")) {
item.nodePath = nodePath.replaceAll("/未规划用例", "/" + this.$t('api_test.unplanned_case'));
}
});
});
}
},
search() {
this.initTableData();
},
handleEdit(testCase, column) {
this.$router.push({
path: '/track/case/edit',
query:{
caseId:testCase.id,
},
});
},
refresh() {
this.$refs.table.clear();
this.$emit('refresh');
},
refreshAll() {
this.$refs.table.clear();
this.$emit('refreshAll');
},
getCase(id) {
this.$refs.testCasePreview.open();
this.rowCaseResult.loading = true;
getTestCaseStep(id)
.then(response => {
this.rowCase = response.data;
this.rowCase.steps = JSON.parse(this.rowCase.steps);
if (!this.rowCase.steps || this.rowCase.length < 1) {
this.rowCase.steps = [{
num: 1,
desc: '',
result: ''
}];
}
if (!this.rowCase.stepModel) {
this.rowCase.stepModel = "STEP";
}
this.$refs.testCasePreview.setData(this.rowCase);
});
},
getVersionOptions() {
if (hasLicense()) {
getProjectVersions(getCurrentProjectID()).then(response => {
this.versionFilters = response.data.map(u => {
return {text: u.name, value: u.id};
});
});
}
},
}
};
</script>
<style scoped>
.operate-button > div {
display: inline-block;
margin-left: 10px;
}
</style>

View File

@ -0,0 +1,36 @@
<template>
<ms-table-column
:label="$t('test_track.issue.description')"
prop="description"
:field="field"
min-width="120"
:fields-width="fieldsWidth">
<template v-slot:default="scope">
<el-popover
placement="right"
width="500"
trigger="hover"
popper-class="issues-popover">
<ms-mark-down-text prop="description" :data="scope.row" :disabled="true"/>
<el-button slot="reference" type="text">{{ $t('test_track.issue.preview') }}</el-button>
</el-popover>
</template>
</ms-table-column>
</template>
<script>
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import MsMarkDownText from "metersphere-frontend/src/components/MsMarkDownText";
export default {
name: "IssueDescriptionTableItem",
components: {MsMarkDownText, MsTableColumn},
props: {
field: Object,
fieldsWidth: Object
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,363 @@
<template>
<el-card class="table-card">
<ms-table
:table-is-loading="page.result.loading"
:data="page.data"
:enableSelection="false"
:condition="page.condition"
:total="page.total"
:page-size.sync="page.pageSize"
:show-select-all="false"
:screen-height="screenHeight"
:remember-order="true"
@handlePageChange="getIssues"
@handleRowClick="handleEdit"
:fields.sync="fields"
:field-key="tableHeaderKey"
@refresh="getIssues"
ref="table"
>
<span v-for="(item) in fields" :key="item.key">
<ms-table-column
:label="$t('test_track.issue.id')"
prop="num"
:field="item"
sortable
min-width="100"
:fields-width="fieldsWidth">
</ms-table-column>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
:label="$t('test_track.issue.title')"
min-width="100"
prop="title">
</ms-table-column>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
sortable
min-width="110"
:label="$t('test_track.issue.platform_status') "
prop="platformStatus">
<template v-slot="scope">
<span
v-if="scope.row.platform ==='Zentao'">{{ scope.row.platformStatus ? issueStatusMap[scope.row.platformStatus] : '--' }}</span>
<span v-else>{{ scope.row.platformStatus ? scope.row.platformStatus : '--' }}</span>
</template>
</ms-table-column>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
:filters="platformFilters"
:label="$t('test_track.issue.platform')"
min-width="100"
prop="platform">
</ms-table-column>
<ms-table-column
prop="createTime"
:field="item"
:fields-width="fieldsWidth"
:label="$t('commons.create_time')"
sortable
min-width="140px">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</ms-table-column>
<ms-table-column
prop="projectName"
:field="item"
:fields-width="fieldsWidth"
:label="$t('test_track.issue.issue_project')"
min-width="80">
<template v-slot="scope">
{{ scope.row.projectName ? scope.row.projectName : '--' }}
</template>
</ms-table-column>
<ms-table-column
:field="item"
v-if="isShowAllColumn"
:fields-width="fieldsWidth"
column-key="creator"
min-width="100"
:label="$t('custom_field.issue_creator')"
prop="creatorName">
</ms-table-column>
<ms-table-column
:field="item"
v-if="isShowAllColumn"
:fields-width="fieldsWidth"
:label="$t('test_track.issue.issue_resource')"
min-width="120"
prop="resourceName">
<template v-slot="scope">
<el-link v-if="scope.row.resourceName" @click="$router.push('/track/plan/view/' + scope.row.resourceId)">
{{ scope.row.resourceName }}
</el-link>
<span v-else>
--
</span>
</template>
</ms-table-column>
<issue-description-table-item :fields-width="fieldsWidth" :field="item" v-if="isShowAllColumn"/>
<ms-table-column
:field="item"
v-if="isShowAllColumn"
:fields-width="fieldsWidth"
:label="item.label"
prop="caseCount">
<template v-slot="scope">
<router-link
:to="scope.row.caseCount > 0 ? {name: 'testCase', params: { projectId: 'all', ids: scope.row.caseIds }} : {}">
{{ scope.row.caseCount }}
</router-link>
</template>
</ms-table-column>
<div v-if="isShowAllColumn">
<ms-table-column v-for="field in issueTemplate.customFields" :key="field.id"
:field="item"
min-width="120"
:fields-width="fieldsWidth"
:label="field.system ? $t(systemNameMap[field.name]) :field.name"
:prop="field.name">
<template v-slot="scope">
<span v-if="field.name === '状态'">
{{ getCustomFieldValue(scope.row, field) ? getCustomFieldValue(scope.row, field) : issueStatusMap[scope.row.status] }}
</span>
<span v-else>
{{ getCustomFieldValue(scope.row, field) }}
</span>
</template>
</ms-table-column>
</div>
</span>
</ms-table>
<ms-table-pagination :change="getIssues" :current-page.sync="page.currentPage" :page-size.sync="page.pageSize"
:total="page.total"/>
</el-card>
</template>
<script>
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import MsTableOperators from "metersphere-frontend/src/components/MsTableOperators";
import MsTableButton from "metersphere-frontend/src/components/MsTableButton";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import {ISSUE_PLATFORM_OPTION, ISSUE_STATUS_MAP, SYSTEM_FIELD_NAME_MAP} from "metersphere-frontend/src/utils/table-constants";
import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader";
import {getDashboardIssues, getIssuePartTemplateWithProject, getIssues} from "@/api/issue";
import {
getCustomFieldValue,
getCustomTableWidth,
getLastTableSortField,
getPageDate,
getPageInfo, parseCustomFilesForList
} from "metersphere-frontend/src/utils/tableUtils";
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {getProjectMember} from "@/api/user";
import {getTableHeaderWithCustomFieldsByXpack} from "@/business/component/js/table-head-util";
import {LOCAL} from "metersphere-frontend/src/utils/constants";
import IssueDescriptionTableItem from "@/business/component/IssueDescriptionTableItem";
import {getUUID} from "metersphere-frontend/src/utils";
export default {
name: "IssueTableList",
components: {
MsMainContainer,
MsContainer,
IssueDescriptionTableItem,
MsTableHeader,
MsTablePagination, MsTableButton, MsTableOperators, MsTableColumn, MsTable
},
data() {
return {
page: getPageInfo(),
fields: [],
tableHeaderKey: "ISSUE_LIST",
fieldsWidth: getCustomTableWidth('ISSUE_LIST'),
issueTemplate: {},
members: [],
isThirdPart: false,
};
},
activated() {
},
props: {
isFocus: {
type: Boolean,
default: false,
},
isShowAllColumn: {
type: Boolean,
default: true,
},
isSelectAll: {
type: Boolean,
default: false,
},
isCreation: {
type: Boolean,
default: false,
},
isDashboard: {
type: Boolean,
default: false,
},
screenHeight: {
type: [Number, String],
default() {
return 'calc(100vh - 160px)';
}
}, //
},
computed: {
platformFilters() {
return ISSUE_PLATFORM_OPTION;
},
issueStatusMap() {
return ISSUE_STATUS_MAP;
},
systemNameMap() {
return SYSTEM_FIELD_NAME_MAP;
},
projectId() {
return getCurrentProjectID();
},
},
methods: {
getCustomFieldValue(row, field) {
let value = getCustomFieldValue(row, field, this.members);
if (!value) {
if (field.name === '处理人') {
return row.maintainerName;
}
}
return value;
},
initFields(template) {
this.issueTemplate = template;
if (this.issueTemplate.platform === LOCAL) {
this.isThirdPart = false;
} else {
this.isThirdPart = true;
}
this.fields = getTableHeaderWithCustomFieldsByXpack('ISSUE_LIST_HEAD', this.issueTemplate.customFields);
if (!this.isThirdPart) {
for (let i = 0; i < this.fields.length; i++) {
if (this.fields[i].id === 'platformStatus') {
this.fields.splice(i, 1);
break;
}
}
//
let removeField = {id: 'platformStatus', name: 'platformStatus', remove: true};
this.issueTemplate.customFields.push(removeField);
}
this.$nextTick(() => {
if (this.$refs.table) {
this.$refs.table.reloadTable();
}
});
},
getIssues() {
if (this.isSelectAll === false) {
this.page.condition.projectId = this.projectId;
}
if (this.isFocus) {
this.page.condition.combine = {
followPeople: {
operator: "current user",
value: "current user",
}
}
} else if (this.isCreation) {
if (this.page.condition.filters) {
delete this.condition.filters['user_id']
}
this.page.condition.combine = {
creator: {
operator: "current user",
value: "current user",
}
}
} else {
if (this.page.condition.filters) {
this.page.condition.filters.status = ["new"];
} else {
this.page.condition.filters = {status: ["new"]};
}
this.page.condition.combine = {
creator: {
operator: "current user",
value: "current user",
}
}
}
this.page.condition.workspaceId = getCurrentWorkspaceId();
this.page.condition.orders = getLastTableSortField(this.tableHeaderKey);
if (this.isDashboard) {
this.page.result.loading = getDashboardIssues(this.page).then((response)=>{
let data = response.data;
this.page.total = data.itemCount;
this.page.data = data.listObject;
parseCustomFilesForList(this.page.data);
});
} else {
this.page.result.loading = getIssues(this.page).then((response)=>{
let data = response.data;
this.page.total = data.itemCount;
this.page.data = data.listObject;
parseCustomFilesForList(this.page.data);
});
}
},
handleEdit(resource) {
let issueData = this.$router.resolve({path:'/track/issue',query:{id:resource.id}});
window.open(issueData.href, '_blank');
},
},
created() {
this.page.result.loading = true;
this.$nextTick(() => {
getProjectMember((data) => {
this.members = data;
});
getIssuePartTemplateWithProject((template) => {
this.initFields(template);
this.page.result.loading = false;
});
this.getIssues();
});
}
};
</script>
<style scoped>
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
.el-table {
cursor: pointer;
}
</style>

View File

@ -0,0 +1,356 @@
<template>
<div class="card-container">
<ms-table :table-is-loading="this.result"
:data="tableData"
:condition="condition"
:page-size="pageSize"
:total="total"
:screen-height="screenHeight"
:field-key="tableHeaderKey"
:remember-order="true"
row-key="id"
:row-order-group-id="projectId"
:row-order-func="editLoadTestCaseOrder"
:enable-selection="false"
@refresh="search"
:disable-header-config="true"
ref="table">
<el-table-column
prop="num"
label="ID"
width="80"
show-overflow-tooltip>
<template v-slot:default="scope">
<span @click="link(scope.row)" style="cursor: pointer;">{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
prop="name"
:label="$t('commons.name')"
show-overflow-tooltip>
<template v-slot:default="scope">
<span @click="link(scope.row)" style="cursor: pointer;">{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
prop="status"
column-key="status"
:filters="statusFilters"
:label="$t('commons.status')">
<template v-slot:default="scope">
<ms-performance-test-status :row="scope.row"/>
</template>
</el-table-column>
<el-table-column
v-if="versionEnable"
:label="$t('project.version.name')"
column-key="versionId"
min-width="100px"
prop="versionId">
<template v-slot:default="scope">
<span>{{ scope.row.versionName }}</span>
</template>
</el-table-column>
<el-table-column
prop="userName"
sortable="custom"
width="110"
:filters="userFilters"
column-key="user_id"
:label="$t('load_test.user_name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
width="160"
sortable
prop="createTime"
:label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column
prop="projectName"
width="120"
:label="$t('load_test.project_name')"
>
</el-table-column>
<el-table-column
width="200"
sortable
v-if="isShowAllColumn"
prop="updateTime"
:label="$t('commons.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column
prop="reportCount"
v-if="isShowAllColumn"
:label="$t('report.load_test_report')"
width="150">
<template v-slot:default="scope">
<el-link v-if="scope.row.reportCount > 0" @click="reports(scope.row)">
{{ scope.row.reportCount }}
</el-link>
<span v-else> {{ scope.row.reportCount }}</span>
</template>
</el-table-column>
</ms-table>
<ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</div>
</template>
<script>
import {getCurrentProjectID, getCurrentUserId, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import {getLastTableSortField} from "metersphere-frontend/src/utils/tableUtils";
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import {editLoadTestCaseOrder} from "@/api/load-test";
import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader";
import MsTableOperator from "metersphere-frontend/src/components/MsTableOperator";
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import MsTableOperators from "metersphere-frontend/src/components/MsTableOperators";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import MsPerformanceTestStatus from "@/business/module/performance/PerformanceTestStatus";
import {TEST_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import {getProjectMember} from "@/api/user";
import {getProjectVersions} from "metersphere-frontend/src/api/version";
import {searchTests} from "@/api/performance";
import {versionEnableByProjectId} from "@/api/project";
export default {
name: "PerformanceTableList",
components: {
MsTable,
MsTableHeader,
MsPerformanceTestStatus,
MsTablePagination,
MsTableOperator,
MsContainer,
MsMainContainer,
MsTableOperators
},
data() {
return {
tableHeaderKey: "PERFORMANCE_TEST_TABLE",
result: false,
condition: {
components: TEST_CONFIGS,
combine: {
creator: {
operator: "current user",
value: "current user",
}
}
},
projectId: null,
tableData: [],
versionFilters: [],
currentPage: 1,
pageSize: 10,
total: 0,
loading: false,
testId: null,
statusFiltersSelect: [
{text: 'Error', value: 'Error'}
],
statusFiltersAll: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'}
],
statusFilters: [],
userFilters: [],
versionEnable: false,
};
},
watch: {
'$route'(to) {
if (to.name !== 'perPlan') {
return;
}
this.projectId = to.params.projectId;
this.initTableData();
},
currentVersion() {
this.condition.versionId = this.currentVersion;
this.initTableData();
// checkbox
this.getVersionOptions(this.currentVersion);
},
},
computed: {
editLoadTestCaseOrder() {
return editLoadTestCaseOrder;
}
},
props: {
isFocus: {
type: Boolean,
default: false,
},
isCreation: {
type: Boolean,
default: false,
},
isShowAllColumn: {
type: Boolean,
default: true,
},
isSelectAll: {
type: Boolean,
default: false,
},
currentVersion: String,
screenHeight: {
type: [Number, String],
default() {
return 'calc(100vh - 200px)';
}
}, //
},
created: function () {
this.projectId = getCurrentProjectID();
if (this.isShowAllColumn) {
if (this.isCreation || this.isFocus) {
this.statusFilters = this.statusFiltersAll
} else {
this.statusFilters = this.statusFiltersSelect
}
} else {
if (this.isFocus) {
this.statusFilters = this.statusFiltersAll
} else {
this.statusFilters = this.statusFiltersSelect
}
}
this.condition.versionId = this.currentVersion;
this.initTableData();
this.getMaintainerOptions();
this.getVersionOptions();
this.checkVersionEnable();
},
methods: {
getMaintainerOptions() {
getProjectMember((data) => {
if (this.isCreation) {
data.map(u => {
if (u.id === getCurrentUserId()) {
let a = {text: u.name, value: u.id};
this.userFilters.push(a);
}
});
} else {
this.userFilters = data.map(u => {
return {text: u.name, value: u.id};
});
}
});
},
initTableData() {
this.condition.orders = getLastTableSortField(this.tableHeaderKey);
if (this.isFocus) {
if (this.condition.filters) {
delete this.condition.filters['user_id']
}
this.condition.combine = {followPeople: {operator: "current user", value: "current user",}}
} else if (this.isCreation) {
if (this.condition.filters) {
delete this.condition.filters['user_id']
}
this.condition.combine = {creator: {operator: "current user", value: "current user",}}
} else {
if (this.condition.filters) {
this.condition.filters.status = ["Error"];
} else {
this.condition.filters = {status: ["Error"]};
}
}
if (this.isSelectAll === false) {
this.condition.projectId = getCurrentProjectID();
}
this.condition.workspaceId = getCurrentWorkspaceId();
this.result = searchTests(this.currentPage, this.pageSize, this.condition)
.then(response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
search(combine) {
this.initTableData(combine);
},
link(row) {
let performanceResolve = this.$router.resolve({
path: '/performance/test/edit/' + row.id,
query: {projectId: row.projectId}
});
window.open(performanceResolve.href, '_blank');
},
create() {
if (!getCurrentProjectID()) {
this.$warning(this.$t('commons.check_project_tip'));
return;
}
this.$router.push('/performance/test/create');
},
getVersionOptions(currentVersion) {
if (hasLicense()) {
getProjectVersions(getCurrentProjectID()).then(response => {
if (currentVersion) {
this.versionFilters = response.data.filter(u => u.id === currentVersion).map(u => {
return {text: u.name, value: u.id};
});
} else {
this.versionFilters = response.data.map(u => {
return {text: u.name, value: u.id};
});
}
});
}
},
checkVersionEnable() {
if (!this.projectId) {
return;
}
if (hasLicense()) {
versionEnableByProjectId(this.projectId).then(response => {
this.versionEnable = response.data;
this.loading = false;
this.$nextTick(() => {
this.loading = true;
});
});
}
},
}
};
</script>
<style scoped>
</style>

View File

@ -0,0 +1,444 @@
<template>
<el-card class="table-card" v-loading="cardResult.loading">
<el-table
border
class="adjust-table"
:data="tableData"
@filter-change="filter"
@sort-change="sort"
:height="screenHeight"
:table-is-loading="this.result"
@row-click="intoPlan">
<template v-for="(item, index) in tableLabel">
<el-table-column
v-if="item.id === 'name'"
prop="name"
:label="$t('commons.name')"
show-overflow-tooltip
:key="index">
</el-table-column>
<el-table-column
v-if="item.id === 'userName'"
prop="principalName"
:label="$t('test_track.plan.plan_principal')"
show-overflow-tooltip
:key="index">
</el-table-column>
<el-table-column
v-if="item.id === 'createUser'"
prop="createUser"
:label="$t('commons.create_user')"
show-overflow-tooltip
:key="index">
</el-table-column>
<el-table-column
v-if="item.id === 'status'"
prop="status"
column-key="status"
:filters="statusFilters"
:filtered-value="['Prepare', 'Underway']"
:label="$t('test_track.plan.plan_status')"
show-overflow-tooltip
:min-width="100"
:key="index">
<template v-slot:default="scope">
<span @click.stop="clickt = 'stop'">
<el-dropdown class="test-case-status" @command="statusChange">
<span class="el-dropdown-link">
<plan-status-table-item :value="scope.row.status"/>
</span>
<el-dropdown-menu slot="dropdown" chang>
<el-dropdown-item :disabled="!hasEditPermission" :command="{item: scope.row, status: 'Prepare'}">
{{ $t('test_track.plan.plan_status_prepare') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!hasEditPermission"
:command="{item: scope.row, status: 'Underway'}">
{{ $t('test_track.plan.plan_status_running') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!hasEditPermission"
:command="{item: scope.row, status: 'Finished'}">
{{ $t('test_track.plan.plan_status_finished') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!hasEditPermission"
:command="{item: scope.row, status: 'Completed'}">
{{ $t('test_track.plan.plan_status_completed') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!hasEditPermission"
:command="{item: scope.row, status: 'Archived'}">
{{ $t('test_track.plan.plan_status_archived') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</span>
</template>
</el-table-column>
<el-table-column
v-if="item.id === 'stage'&& isShowAllColumn"
prop="stage"
column-key="stage"
:filters="stageFilters"
:label="$t('test_track.plan.plan_stage')"
show-overflow-tooltip
:min-width="110"
:key="index">
<template v-slot:default="scope">
<plan-stage-table-item :option="stageOption" :stage="scope.row.stage"/>
</template>
</el-table-column>
<el-table-column
v-if="item.id === 'testRate'&& isShowAllColumn"
prop="testRate"
:label="$t('test_track.home.test_rate')"
min-width="100"
show-overflow-tooltip
:key="index">
<template v-slot:default="scope">
<el-progress :percentage="scope.row.testRate"></el-progress>
</template>
</el-table-column>
<el-table-column v-if="item.id === 'tags'&& isShowAllColumn" prop="tags"
:label="$t('api_test.automation.tag')" :key="index">
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:content="itemName" style="margin-left: 0px; margin-right: 2px"></ms-tag>
</template>
</el-table-column>
<el-table-column
v-if="item.id === 'projectName'"
prop="projectName"
:label="$t('test_track.plan.plan_project')"
show-overflow-tooltip
:key="index">
</el-table-column>
<el-table-column
v-if="item.id === 'passRate'&& isShowAllColumn"
prop="passRate"
:label="$t('commons.pass_rate')"
show-overflow-tooltip
:min-width="110"
:key="index">
</el-table-column>
<el-table-column
v-if="item.id === 'plannedStartTime'"
sortable
prop="plannedStartTime"
:label="$t('test_track.plan.planned_start_time')"
show-overflow-tooltip
:min-width="150"
:key="index">
<template v-slot:default="scope">
<span>{{ scope.row.plannedStartTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column
v-if="item.id === 'plannedEndTime'"
sortable
prop="plannedEndTime"
:label="$t('test_track.plan.planned_end_time')"
show-overflow-tooltip
:min-width="150"
:key="index">
<template v-slot:default="scope">
<span>{{ scope.row.plannedEndTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column
v-if="item.id === 'actualStartTime' && isShowAllColumn"
sortable
prop="actualStartTime"
:min-width="170"
:label="$t('test_track.plan.actual_start_time')"
show-overflow-tooltip
:key="index">
<template v-slot:default="scope">
<span>{{ scope.row.actualStartTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column
v-if="item.id === 'actualEndTime' && isShowAllColumn"
sortable
:min-width="170"
prop="actualEndTime"
:label="$t('test_track.plan.actual_end_time')"
show-overflow-tooltip
:key="index">
<template v-slot:default="scope">
<span>{{ scope.row.actualEndTime | datetimeFormat }}</span>
</template>
</el-table-column>
</template>
</el-table>
<ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
</template>
<script>
import {_filter, _sort, saveLastTableSortField} from "metersphere-frontend/src/utils/tableUtils";
import {TEST_PLAN_LIST} from "metersphere-frontend/src/utils/constants";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {hasPermission} from "metersphere-frontend/src/utils/permission";
import PlanStageTableItem from "@/business/module/plan/PlanStageTableItem";
import PlanStatusTableItem from "@/business/module/plan/PlanStatusTableItem";
import MsTag from "metersphere-frontend/src/components/MsTag";
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import MsTablePagination from 'metersphere-frontend/src/components/pagination/TablePagination';
import {TEST_PLAN_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import {getCustomTableHeaderByXpack} from "@/business/component/js/table-head-util";
import {editPlan, getDashboardPlanList, getPlanList, getPlanStageOption, getPrincipalById} from "@/api/test-plan";
export default {
name: "PlanTableList",
components: {
PlanStageTableItem,
PlanStatusTableItem,
MsDialogFooter,
MsTablePagination,
MsTag
},
props: {
isFocus: {
type: Boolean,
default: false,
},
isCreation: {
type: Boolean,
default: false,
},
isShowAllColumn: {
type: Boolean,
default: true,
},
isSelectAll: {
type: Boolean,
default: false,
},
isDashboard: {
type: Boolean,
default: false,
},
screenHeight: {
type: [Number, String],
default() {
return 'calc(100vh - 160px)';
}
}, //
},
data() {
return {
createUser: "",
type: TEST_PLAN_LIST,
tableHeaderKey: "TEST_PLAN_LIST",
tableLabel: [],
result: false,
cardResult: {},
enableDeleteTip: false,
condition: {
components: TEST_PLAN_CONFIGS,
executorOrPrincipal: "current",
},
currentPage: 1,
pageSize: 10,
hasEditPermission: false,
total: 0,
tableData: [],
statusFilters: [
{text: this.$t('test_track.plan.plan_status_prepare'), value: 'Prepare'},
{text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'},
{text: this.$t('test_track.plan.plan_status_finished'), value: 'Finished'},
{text: this.$t('test_track.plan.plan_status_completed'), value: 'Completed'},
{text: this.$t('test_track.plan.plan_status_archived'), value: 'Archived'}
],
stageFilters: [
{text: this.$t('test_track.plan.smoke_test'), value: 'smoke'},
{text: this.$t('test_track.plan.system_test'), value: 'system'},
{text: this.$t('test_track.plan.regression_test'), value: 'regression'},
],
currentPlanId: "",
stageOption: []
};
},
watch: {
'$route'(to, from) {
if (to.path.indexOf("/track/plan/all") >= 0) {
this.initTableData();
}
}
},
created() {
this.projectId = this.$route.params.projectId;
if (!this.projectId) {
this.projectId = getCurrentProjectID();
}
this.hasEditPermission = hasPermission('PROJECT_TRACK_PLAN:READ+EDIT');
getPlanStageOption(this.projectId).then(r => {
this.stageOption = r.data;
})
this.initTableData();
},
methods: {
init() {
this.initTableData();
},
initTableData() {
if (this.planId) {
this.condition.planId = this.planId;
}
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
this.condition.nodeIds = this.selectNodeIds;
}
if (!this.projectId) {
return;
}
if (this.isFocus) {
if (this.condition.filters) {
delete this.condition.filters['user_id']
}
this.condition.combine = {followPeople: {operator: "current user", value: "current user",}}
if (this.condition.executorOrPrincipal) {
delete this.condition.executorOrPrincipal
}
} else if (this.isCreation) {
if (this.condition.filters) {
delete this.condition.filters['user_id']
}
if (this.condition.executorOrPrincipal) {
delete this.condition.executorOrPrincipal
}
this.condition.combine = {creator: {operator: "current user", value: "current user",}}
} else {
if (!this.condition.filters) {
this.condition.filters = {status: ["Prepare", "Underway"]}
} else if (this.condition.filters.status == null) {
this.condition.filters.status = ["Prepare", "Underway"];
}
}
if (this.isSelectAll === false) {
this.condition.projectId = getCurrentProjectID();
}
this.condition.workspaceId = getCurrentWorkspaceId();
if (this.isDashboard) {
getDashboardPlanList(this.currentPage,this.pageSize,this.condition).then(response => {
let data = response.data;
this.dealResponseData(data)
});
} else {
getPlanList(this.currentPage,this.pageSize,this.condition).then(response => {
let data = response.data;
this.dealResponseData(data)
});
}
this.tableLabel = getCustomTableHeaderByXpack('TEST_PLAN_LIST_HEAD');
},
dealResponseData(data){
this.total = data.itemCount;
this.tableData = data.listObject;
this.tableData.forEach(item => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
item.passRate = item.passRate + '%';
getPrincipalById(item.id).then(res => {
let data = res.data;
let principal = "";
let principalIds = data.map(d => d.id);
if (data) {
data.forEach(d => {
if (principal !== "") {
principal = principal + "、" + d.name;
} else {
principal = principal + d.name;
}
})
}
this.$set(item, "principalName", principal);
// id
this.$set(item, "principals", principalIds);
})
});
},
statusChange(data) {
if (!hasPermission('PROJECT_TRACK_PLAN:READ+EDIT')) {
return;
}
let oldStatus = data.item.status;
let newStatus = data.status;
let param = {};
param.id = data.item.id;
param.status = newStatus;
editPlan(param).then(()=>{
for (let i = 0; i < this.tableData.length; i++) {
if (this.tableData[i].id === param.id) { //
if (oldStatus !== "Completed" && newStatus === "Completed") {
this.tableData[i].actualEndTime = Date.now();
} // ->=null
else if (oldStatus !== "Underway" && newStatus === "Underway") {
this.tableData[i].actualStartTime = Date.now();
this.tableData[i].actualEndTime = "";
} // ->=null
else if (oldStatus !== "Prepare" && newStatus === "Prepare") {
this.tableData[i].actualStartTime = this.tableData[i].actualEndTime = "";
} // ->=null
this.tableData[i].status = newStatus;
break;
}
}
});
},
intoPlan(row, column, event) {
if (column.label !== this.$t('commons.operating')) {
let testPlanResolve = this.$router.resolve({
path: '/track/plan/view/' + row.id,
query: {projectId: row.projectId}
});
window.open(testPlanResolve.href, '_blank');
}
},
filter(filters) {
_filter(filters, this.condition);
this.initTableData()
},
sort(column) {
//
if (this.condition.orders) {
this.condition.orders = [];
}
_sort(column, this.condition);
this.saveSortField(this.tableHeaderKey, this.condition.orders);
this.initTableData();
},
saveSortField(key, orders) {
saveLastTableSortField(key, JSON.stringify(orders));
},
}
};
</script>
<style scoped>
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
.el-table {
cursor: pointer;
}
.schedule-btn >>> .el-button {
margin-left: 10px;
color: #85888E;
border-color: #85888E;
border-width: thin;
}
.scenario-ext-btn {
margin-left: 10px;
}
</style>

View File

@ -0,0 +1,177 @@
<template>
<div>
<el-row v-if="currentTodo==='api_definition' || currentTodo==='api_case'">
<el-col class="protocol-col" :span="9">
<el-select class="protocol-select" size="small" v-model="protocol" @change="changeProtocol">
<el-option
v-for="item in options"
:key="item.value"
:name="item.name"
:value="item.value"
:disabled="item.disabled">
</el-option>
</el-select>
</el-col>
<el-col :span="15">
<el-input
size="small"
:placeholder="$t('api_test.request.parameters_mock_filter_tips')"
v-model="filterText">
</el-input>
</el-col>
</el-row>
<el-row v-else>
<el-input
size="small"
:placeholder="$t('api_test.request.parameters_mock_filter_tips')"
v-model="filterText">
</el-input>
</el-row>
<el-tree
class="filter-tree"
:data="treeDate"
node-key="id"
:props="defaultProps"
default-expand-all
@node-click="nodeClick"
:highlight-current=true
:filter-node-method="filterNode"
ref="tree">
</el-tree>
</div>
</template>
<script>
import {getCurrentProjectID, getCurrentUserId, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {PROJECT_NAME} from "metersphere-frontend/src/utils/constants";
import {getUserProjectList} from "metersphere-frontend/src/api/project";
export default {
name: 'ProjectMenu',
components: {
},
props: {
currentTodo: String
},
data() {
return {
checked: true,
result: {},
currentProject: {},
currentNode: {},
childNodes: [],
projectId: '',
projectName: PROJECT_NAME,
projects: [],
filterText: '',
protocol: 'HTTP',
treeDate: [{
id: 1,
name: this.$t('commons.all_project'),
disabled: true,
children: [],
}],
defaultProps: {
children: 'children',
label: 'name'
},
operators: [{
label: '创建项目',
//callback: this.addTestCase,
permissions: ['PROJECT_TRACK_CASE:READ+CREATE']
}],
options: [
{name: 'DUBBO', value: 'DUBBO'},
{name: 'HTTP', value: 'HTTP'},
{name: 'SQL', value: 'SQL'},
{name: 'TCP', value: 'TCP'},]
}
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
},
childNodes() {
const nodeList = this.$refs.tree.root.childNodes[0].childNodes;
this.childNodes = nodeList;
for (let i = 0; i < nodeList.length; i++) {
if (nodeList[i].data.id === getCurrentProjectID()) {
this.$refs.tree.setCurrentKey(getCurrentProjectID())
}
}
}
},
methods: {
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
handleClose(key, keyPath) {
},
nodeClick(data, node, refNode) {
if (data.id === 1) {
return
}
this.changeProject(null, null, data);
},
getProject() {
let paramData = {
userId: getCurrentUserId(),
workspaceId: getCurrentWorkspaceId()
}
getUserProjectList(paramData).then(res => {
let data = res.data;
if (data && data.length > 0) {
const index = data.findIndex(d => d.id === getCurrentProjectID());
this.projects = data;
this.treeDate[0].children = data
if (index !== -1) {
this.projectId = data[index].id;
this.projectName = data[index].name;
this.changeProject(null, null, data[index]);
} else {
this.projectId = data[0].id;
this.projectName = data[0].name;
this.changeProject(data[0]);
}
}
})
},
changeProject(key, keyPath, project) {
this.currentProject = project;
if (key) {
this.$emit('setProject', key);
} else {
this.$emit('setProject', project.id);
}
//
this.$emit('refreshNode');
},
changeProtocol(protocol) {
this.$emit('setCurrentProtocol', protocol);
}
},
created() {
this.getProject();
},
mounted() {
this.$nextTick(function () {
//
this.childNodes = this.$refs.tree.root.childNodes[0].childNodes;
})
}
}
</script>
<style scoped>
.workstation-card {
height: 100%;
color: #0a0a0a;
}
</style>

View File

@ -0,0 +1,264 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<el-table
border
class="adjust-table"
:data="tableData"
@filter-change="filter"
:height="screenHeight"
@row-click="intoReview">
<template v-for="(item, index) in tableLabel">
<el-table-column
v-if="item.id==='name'"
prop="name"
:label="$t('test_track.review.review_name')"
show-overflow-tooltip
:key="index">
</el-table-column>
<el-table-column
v-if="item.id==='reviewer'"
prop="reviewer"
:label="$t('test_track.review.reviewer')"
show-overflow-tooltip
:key="index">
</el-table-column>
<el-table-column
v-if="item.id==='creatorName'"
prop="creatorName"
:label="$t('test_track.review.review_creator')"
show-overflow-tooltip
:key="index"
>
</el-table-column>
<el-table-column
v-if="item.id==='status'"
prop="status"
column-key="status"
:label="$t('test_track.review.review_status')"
show-overflow-tooltip
:key="index"
>
<template v-slot:default="scope">
<span class="el-dropdown-link">
<plan-status-table-item :value="scope.row.status"/>
</span>
</template>
</el-table-column>
<el-table-column v-if="item.id === 'tags' && isShowAllColumn" prop="tags"
:label="$t('api_test.automation.tag')" :key="index">
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:content="itemName" style="margin-left: 0; margin-right: 2px"></ms-tag>
</template>
</el-table-column>
<el-table-column
v-if="item.id==='createTime'"
prop="createTime"
:min-width="110"
:label="$t('commons.create_time')"
show-overflow-tooltip
:key="index"
>
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column
v-if="item.id==='endTime'"
prop="endTime"
:label="$t('test_track.review.end_time')"
show-overflow-tooltip
:key="index">
<template v-slot:default="scope">
<span>{{ scope.row.endTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column
v-if="item.id==='projectName'"
prop="projectName"
:label="$t('test_track.review.review_project')"
show-overflow-tooltip
:key="index">
</el-table-column>
</template>
</el-table>
<ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
</template>
<script>
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {
_filter,
getLastTableSortField,
} from "metersphere-frontend/src/utils/tableUtils";
import {Test_Case_Review} from "@/model/JsonData";
import {TEST_CASE_REVIEW_LIST} from "metersphere-frontend/src/utils/constants";
import HeaderCustom from "metersphere-frontend/src/components/head/HeaderCustom";
import HeaderLabelOperate from "metersphere-frontend/src/components/head/HeaderLabelOperate";
import MsTag from "metersphere-frontend/src/components/MsTag";
import MsDeleteConfirm from "metersphere-frontend/src/components/MsDeleteConfirm";
import MsTableOperator from "metersphere-frontend/src/components/MsTableOperator";
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton";
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader";
import PlanStatusTableItem from "@/business/module/plan/PlanStatusTableItem";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import {getCustomTableHeaderByXpack} from "@/business/component/js/table-head-util";
import {getTestCaseReviewer, getTestCaseReviewPages, getTestCaseReviewProject,} from "@/api/test-case";
export default {
name: "ReviewTableList",
components: {
MsTag,
HeaderLabelOperate,
HeaderCustom,
MsDeleteConfirm,
MsTableOperator,
MsTableOperatorButton,
MsDialogFooter,
MsTableHeader,
MsTablePagination,
PlanStatusTableItem
},
props:{
isFocus:{
type: Boolean,
default: false,
},
isCreation:{
type: Boolean,
default: false,
},
isShowAllColumn:{
type: Boolean,
default: true,
},
isSelectAll:{
type: Boolean,
default: false,
},
screenHeight: {
type: [Number, String],
default() {
return 'calc(100vh - 160px)';
}
},
},
data() {
return {
type: TEST_CASE_REVIEW_LIST,
headerItems: Test_Case_Review,
tableLabel: [],
tableHeaderKey:"TEST_CASE_REVIEW",
result: {},
condition: {},
tableData: [],
isTestManagerOrTestUser: false,
currentPage: 1,
pageSize: 10,
total: 0,
statusFilters: [
{text: this.$t('test_track.plan.plan_status_prepare'), value: 'Prepare'},
{text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'},
{text: this.$t('test_track.plan.plan_status_completed'), value: 'Completed'}
],
};
},
watch: {
},
created() {
this.isTestManagerOrTestUser = true;
this.condition.orders = getLastTableSortField(this.tableHeaderKey);
this.initTableData();
},
computed: {
projectId() {
return getCurrentProjectID();
},
},
methods: {
initTableData() {
let lastWorkspaceId = getCurrentWorkspaceId();
this.condition.workspaceId = lastWorkspaceId;
if (!this.projectId) {
return;
}
if(this.isFocus){
if(this.condition.filters){
delete this.condition.filters['user_id']
}
if(this.condition.reviewerId){
delete this.condition.reviewerId
}
this.condition.combine= {followPeople: {operator: "current user", value: "current user",}}
}else if(this.isCreation){
if(this.condition.filters){
delete this.condition.filters['user_id']
}
if(this.condition.reviewerId){
delete this.condition.reviewerId
}
this.condition.combine= { creator: {operator: "current user", value: "current user",}}
} else {
if(!this.condition.filters){
this.condition.filters={status: ["Prepare", "Underway","Finished"]}
}
if(!this.condition.reviewerId){
this.condition.reviewerId = "currentUserId"
}
}
if(this.isSelectAll===false){
this.condition.projectId = this.projectId;
}
this.result =getTestCaseReviewPages(this.currentPage,this.pageSize,this.condition).then(response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.tableData.forEach(item => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
});
for (let i = 0; i < this.tableData.length; i++) {
getTestCaseReviewProject({id: this.tableData[i].id}).then(res => {
let arr = res.data;
let projectIds = arr.filter(d => d.id !== this.tableData[i].projectId).map(data => data.id);
this.$set(this.tableData[i], "projectIds", projectIds);
});
getTestCaseReviewer({id: this.tableData[i].id}).then(res => {
let arr = res.data;
let reviewer = arr.map(data => data.name).join("、");
let userIds = arr.map(data => data.id);
this.$set(this.tableData[i], "reviewer", reviewer);
this.$set(this.tableData[i], "userIds", userIds);
});
}
});
this.tableLabel = getCustomTableHeaderByXpack('TEST_CASE_REVIEW_HEAD');
},
intoReview(row) {
let reviewResolve = this.$router.resolve({path:'/track/review/view/' + row.id,query:{projectId:row.projectId}});
window.open(reviewResolve.href, '_blank');
},
filter(filters) {
_filter(filters, this.condition);
this.initTableData();
},
}
};
</script>
<style scoped>
</style>

View File

@ -0,0 +1,120 @@
<template>
<el-card class="card-content" v-if="isShow">
<el-button-group v-if="isShowChangeButton">
<el-tooltip v-if="leftButtonEnable" class="item" effect="dark" :content="leftTip" placement="left">
<el-button plain style=":width=leftContent.length;height: 32px;padding: 5px 8px;" :class="{active: leftActive}" @click="changeTab('left')">{{leftContent}}</el-button>
</el-tooltip>
<el-tooltip v-if="middleButtonEnable" class="item" effect="dark" :content="middleTip" placement="top">
<el-button plain style=":width=middleContent.length;height: 32px;padding: 1px;" :class="{active: middleActive}" @click="changeTab('middle')">{{middleContent}}</el-button>
</el-tooltip>
<el-tooltip v-if="rightButtonEnable" class="item" effect="dark" :content="rightTip" placement="right">
<el-button plain style=":width=rightContent.length;height: 32px;padding: 5px 8px;" :class="{active: rightActive}" @click="changeTab('right')">
{{rightContent}}
</el-button>
</el-tooltip>
</el-button-group>
<slot name="version"></slot>
<template v-slot:header>
<slot name="header"></slot>
</template>
<slot></slot>
</el-card>
</template>
<script>
export default {
name: "StatusTapButton",
data() {
return {
isShow: true,
showApiList:false,
showTestCaseList:false,
showDocList:true,
}
},
props: {
activeStatus: String,
isShowChangeButton: {
type: Boolean,
default: true
},
leftButtonEnable: {
type: Boolean,
default: true
},
middleButtonEnable: {
type: Boolean,
default: true
},
rightButtonEnable: {
type: Boolean,
default: true
},
leftContent: {
type: String,
default: 'left'
},
middleContent: {
type: String,
default: 'middle'
},
rightContent: {
type: String,
default: 'right'
},
leftTip: {
type: String,
default: 'left'
},
middleTip: {
type: String,
default: 'middle'
},
rightTip: {
type: String,
default: 'right'
},
},
computed: {
leftActive() {
return this.activeStatus === 'left';
},
middleActive() {
return this.activeStatus === 'middle';
},
rightActive() {
return this.activeStatus === 'right';
},
},
methods: {
changeTab(tabType){
this.$emit("update:activeStatus", tabType);
},
},
}
</script>
<style scoped>
.active {
border: solid 1px #6d317c!important;
background-color: var(--primary_color)!important;
color: #FFFFFF!important;
}
.case-button {
border-left: solid 1px var(--primary_color);
}
.item{
border: solid 1px var(--primary_color);
}
</style>

View File

@ -0,0 +1,47 @@
<template>
<div>
<el-row>
<el-col :span="4">{{ $t('api_test.mock.req_param') + ":" }}</el-col>
<el-col :span="20" style="color: var(--primary_color)">
<el-checkbox v-model="fromData.headers">{{ "Header" + '\xa0\xa0' }}</el-checkbox>
<el-checkbox v-model="fromData.query">{{ $t('api_test.definition.request.query_param') }}</el-checkbox>
<el-checkbox v-model="fromData.rest">{{ $t('api_test.definition.request.rest_param') }}</el-checkbox>
<el-checkbox v-model="fromData.body">{{ $t('api_test.request.body') }}</el-checkbox>
</el-col>
</el-row>
<el-row>
<el-col :span="4">{{ $t('api_test.definition.request.other_config') + ":" }}</el-col>
<el-col :span="20" style="color: var(--primary_color)">
<el-checkbox v-model="fromData.delNotSame">{{ $t('workstation.delNotSame') }}</el-checkbox>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "SyncSettings",
data() {
return {
fromData: {
protocol: true,
method: true,
path: true,
headers: true,
query: true,
rest: true,
body: true,
delNotSame: true,
runError: true,
unRun: true,
}
}
}
}
</script>
<style scoped>
.el-row {
margin-bottom: 3px;
}
</style>

View File

@ -0,0 +1,124 @@
/* 用例等级 */
export const PRIORITY = [
{name: 'P0', id: 'P0'},
{name: 'P1', id: 'P1'},
{name: 'P2', id: 'P2'},
{name: 'P3', id: 'P3'}
];
export const CASE_ORDER = [
{label: 'api_test.definition.request.grade_order_asc', name: 'priority', type: 'desc', id: 'grade_order_asc'},
{label: 'api_test.definition.request.grade_order_desc', name: 'priority', type: 'asc', id: 'grade_order_desc'},
{
label: 'api_test.definition.request.create_time_order_asc',
name: 'create_time',
type: 'asc',
id: 'create_time_order_asc'
},
{
label: 'api_test.definition.request.create_time_order_desc',
name: 'create_time',
type: 'desc',
id: 'create_time_order_desc'
},
{
label: 'api_test.definition.request.update_time_order_asc',
name: 'update_time',
type: 'asc',
id: 'update_time_order_asc'
},
{
label: 'api_test.definition.request.update_time_order_desc',
name: 'update_time',
type: 'desc',
id: 'update_time_order_desc'
}
];
export const OPTIONS = [
{value: 'HTTP', name: 'HTTP'},
{value: 'TCP', name: 'TCP'},
{value: 'SQL', name: 'SQL'},
{value: 'DUBBO', name: 'DUBBO'}
];
export const DEFAULT_DATA = [{
"id": "gc",
"name": "回收站",
"level": 1,
"children": [],
}, {
"id": "root",
"name": "全部模块",
"level": 0,
"children": [],
}];
export const REQ_METHOD = [
{id: 'GET', label: 'GET'},
{id: 'POST', label: 'POST'},
{id: 'PUT', label: 'PUT'},
{id: 'PATCH', label: 'PATCH'},
{id: 'DELETE', label: 'DELETE'},
{id: 'OPTIONS', label: 'OPTIONS'},
{id: 'HEAD', label: 'HEAD'},
{id: 'CONNECT', label: 'CONNECT'}
];
export const TCP_METHOD = [
{id: 'TCP', label: 'TCP'}
];
export const SQL_METHOD = [
{id: 'SQL', label: 'SQL'}
];
export const DUBBO_METHOD = [
{id: 'dubbo://', label: 'dubbo://'},
];
export const CASE_PRIORITY = [
{id: 'P0', label: 'P0'},
{id: 'P1', label: 'P1'},
{id: 'P2', label: 'P2'},
{id: 'P3', label: 'P3'}
];
export const REVIEW_STATUS = [
{id: 'Prepare', label: '未评审'},
{id: 'Pass', label: '通过'},
{id: 'UnPass', label: '未通过'}
];
export const API_STATUS = [
{id: 'Prepare', label: 'test_track.plan.plan_status_prepare'},
{id: 'Underway', label: 'test_track.plan.plan_status_running'},
{id: 'Completed', label: 'test_track.plan.plan_status_completed'}
];
export const TEST = [
{id: 'performance', name: '性能测试', module: 'performance'},
{id: 'testcase', name: '接口用例', module: 'api'},
{id: 'automation', name: '场景测试', module: 'api'}
];
export const TEST_CASE = [
{value: 'HTTP', label: 'HTTP', leaf: true},
{value: 'TCP', label: 'TCP', leaf: true},
{value: 'DUBBO', label: 'DUBBO', leaf: true},
{value: 'SQL', label: 'SQL', leaf: true}
];
export const API_METHOD_COLOUR = [
['GET', "#61AFFE"], ['POST', '#49CC90'], ['PUT', '#fca130'],
['PATCH', '#E2EE11'], ['DELETE', '#f93e3d'], ['OPTIONS', '#0EF5DA'],
['HEAD', '#8E58E7'], ['CONNECT', '#90AFAE'],
['DUBBO', '#C36EEF'], ['dubbo://', '#C36EEF'], ['SQL', '#0AEAD4'], ['TCP', '#0A52DF'],
];
export const REQUIRED = [
{name: '必填', id: true},
{name: '非必填', id: false}
];
export const RESULT_MAP = new Map([
['success', '通过'],
['error', '未通过'],
['default', '未执行'],
['errorReportResult', '误报']
]);

View File

@ -0,0 +1,26 @@
/* 报告状态 */
export const REPORT_STATUS = [
{text: 'Pending', value: 'PENDING'},
{text: 'Running', value: 'RUNNING'},
{text: 'Rerunning', value: 'RERUNNING'},
{text: 'Success', value: 'SUCCESS'},
{text: 'Error', value: 'ERROR'},
{text: "FakeError", value: 'FAKE_ERROR'},
{text: 'Stopped', value: 'STOPPED'},
]
export function getReportStatusColor(status) {
if (status) {
status = status.toUpperCase();
}
if (status === 'SUCCESS') {
return '#5daf34';
} else if (status === 'FAKE_ERROR') {
return '#F6972A';
} else if (status === 'ERROR') {
return '#FE6F71';
} else {
return '';
}
}

View File

@ -0,0 +1,127 @@
import {X_PACK_TABLE_HEADERS} from "../js/xpack-table-headers";
import {
generateTableHeaderKey,
getCustomFieldsKeys, getCustomFieldValue,
translateLabel
} from "metersphere-frontend/src/utils/tableUtils";
import {updateCustomFieldTemplate} from "metersphere-frontend/src/api/custom-field-template";
import i18n from "@/i18n";
/**
* 获取所有字段
* @param key
* @returns {*[]}
*/
export function getTableHeaderWithCustomFieldsByXpack(key,customFields,projectMembers = []) {
let fields = [...X_PACK_TABLE_HEADERS[key]];
fields = JSON.parse(JSON.stringify(fields));
translateLabel(fields);
let keys = getCustomFieldsKeys(customFields);
projectMembers.forEach(member => {
member['text'] = member.name;
// 高级搜索使用
member['label'] = member.name;
member['value'] = member.id;
member['showLabel'] = member.name + "(" + member.id + ")";
})
customFields.forEach(item => {
if (!item.key) {
// 兼容旧版更新key
item.key = generateTableHeaderKey(keys, customFields);
return updateCustomFieldTemplate({id: item.id, key: item.key});
}
let field = {
id: item.name,
key: item.key,
label: item.name,
isCustom: true
}
fields.push(field);
if ((item.type === 'member' || item.type === 'multipleMember') && projectMembers && projectMembers.length > 0) {
item.options = projectMembers;
}
});
return getCustomTableHeaderByFiledSetting(key, fields);
}
/**
* 获取 localStorage 的值过滤
* @param key
* @param fieldSetting
* @returns {[]|*}
*/
function getCustomTableHeaderByFiledSetting(key, fieldSetting) {
let fieldStr = localStorage.getItem(key);
if (fieldStr !== null) {
let fields = [];
for (let i = 0; i < fieldStr.length; i++) {
let fieldKey = fieldStr[i];
for (let j = 0; j < fieldSetting.length; j++) {
let item = fieldSetting[j];
if (item.key === fieldKey) {
fields.push(item);
break;
}
}
}
return fields;
}
return fieldSetting;
}
export function getAllFieldWithCustomFieldsByXpack(key, customFields) {
let fieldSetting = [...X_PACK_TABLE_HEADERS[key]];
fieldSetting = JSON.parse(JSON.stringify(fieldSetting));
translateLabel(fieldSetting);
if (customFields) {
customFields.forEach(item => {
let field = {
id: item.name,
key: item.key,
label: item.name,
isCustom: true
}
fieldSetting.push(field);
});
}
return fieldSetting;
}
export function getCustomTableHeaderByXpack(key, customFields) {
let fieldSetting = getAllFieldWithCustomFieldsByXpack(key, customFields);
return getCustomTableHeaderByFiledSetting(key, fieldSetting);
}
export function getCustomFieldValueForTrack(row, field, members) {
if (field.name === '用例状态' && field.system) {
return parseStatus(row, field.options);
}
return getCustomFieldValue(row, field, members);
}
function parseStatus(row, options) {
if (options) {
for (let option of options) {
if (option.value === row.status) {
return option.system ? i18n.t(option.text) : option.text;
}
}
}
return row.status;
}
/**
* 获取对应表格的列宽
* @param key
* @returns {{}|any}
*/
export function getCustomTableWidth(key) {
let fieldStr = localStorage.getItem(key + '_WITH');
if (fieldStr !== null) {
let fields = JSON.parse(fieldStr);
return fields;
}
return {};
}

View File

@ -0,0 +1,164 @@
import {CUSTOM_TABLE_HEADER} from "metersphere-frontend/src/utils/default-table-header";
//测试计划-功能用例
const TRACK_HEADER = {
TEST_PLAN_FUNCTION_TEST_CASE: [
{id: 'num', key: '1', label: 'commons.id'},
{id: 'name', key: '2', label: 'commons.name'},
{id: 'versionId', key: 'b', label: 'project.version.name', xpack: true},
{id: 'tags', key: '3', label: 'commons.tag'},
{id: 'nodePath', key: '4', label: 'test_track.case.module'},
{id: 'projectName', key: '5', label: 'test_track.review.review_project'},
{id: 'issuesContent', key: '6', label: 'test_track.issue.issue'},
{id: 'executor', key: '7', label: 'test_track.plan_view.executor'},
{id: 'maintainerName', key: 'c', label: 'test_track.plan.plan_principal'},
{id: 'status', key: '8', label: 'test_track.plan_view.execute_result'},
{id: 'updateTime', key: '9', label: 'commons.update_time'},
{id: 'createTime', key: 'a', label: 'commons.create_time'},
],
//测试计划
TEST_PLAN_LIST: [
{id: 'name', key: '1', label: 'commons.name'},
{id: 'status', key: '3', label: 'test_track.plan.plan_status'},
{id: 'stage', key: '4', label: 'test_track.plan.plan_stage'},
{id: 'testRate', key: '5', label: 'test_track.home.test_rate'},
{id: 'projectName', key: '6', label: 'test_track.plan.plan_project'},
{id: 'plannedStartTime', key: '7', label: 'test_track.plan.planned_start_time'},
{id: 'plannedEndTime', key: '8', label: 'test_track.plan.planned_end_time'},
{id: 'actualStartTime', key: '9', label: 'test_track.plan.actual_start_time'},
{id: 'actualEndTime', key: 'a', label: 'test_track.plan.actual_end_time'},
{id: 'tags', key: 'b', label: 'commons.tag'},
{id: 'scheduleStatus', key: 'c', label: 'commons.trigger_mode.schedule'},
{id: 'passRate', key: 'e', label: 'commons.pass_rate'},
{id: 'createUser', key: 'f', label: 'commons.create_user'},
{id: 'testPlanTestCaseCount', key: 'g', label: 'test_track.plan.test_plan_test_case_count'},
{id: 'testPlanApiCaseCount', key: 'h', label: 'test_track.plan.test_plan_api_case_count'},
{id: 'testPlanApiScenarioCount', key: 'i', label: 'test_track.plan.test_plan_api_scenario_count'},
{id: 'testPlanLoadCaseCount', key: 'j', label: 'test_track.plan.test_plan_load_case_count'},
{id: 'principalName', key: 'k', label: 'test_track.plan.plan_principal'},
],
//测试计划-api用例
TEST_PLAN_API_CASE: [
{id: 'num', key: '1', label: 'commons.id'},
{id: 'name', key: '2', label: 'api_test.definition.api_name'},
{id: 'versionId', key: 'd', label: 'commons.version'},
{id: 'priority', key: '3', label: 'test_track.case.priority'},
{id: 'path', key: '4', label: 'api_test.definition.api_path'},
{id: 'createUser', key: '5', label: 'api_test.creator'},
{id: 'tags', key: '7', label: 'commons.tag'},
{id: 'execResult', key: '8', label: 'test_track.plan.execute_result'},
{id: 'maintainer', key: '9', label: 'api_test.definition.request.responsible'},
{id: 'updateTime', key: 'a', label: 'commons.update_time'},
{id: 'createTime', key: 'b', label: 'commons.create_time'},
{id: 'environmentName', key: 'c', label: 'commons.environment'},
],
//测试计划-性能用例
TEST_PLAN_LOAD_CASE: [
{id: 'num', key: '1', label: 'commons.id'},
{id: 'caseName', key: '2', label: 'commons.name'},
{id: 'versionId', key: '9', label: 'commons.version'},
{id: 'projectName', key: '3', label: 'load_test.project_name'},
{id: 'userName', key: '4', label: 'load_test.user_name'},
{id: 'createTime', key: '5', label: 'commons.create_time'},
{id: 'status', key: '6', label: 'commons.status'},
{id: 'caseStatus', key: '7', label: 'test_track.plan.load_case.execution_status'},
{id: 'loadReportId', key: '8', label: 'test_track.plan.load_case.report'},
],
//测试计划-场景用例
TEST_PLAN_SCENARIO_CASE: [
{id: 'num', key: '1', label: 'commons.id'},
{id: 'name', key: '2', label: 'api_test.automation.scenario_name'},
{id: 'versionId', key: 'd', label: 'commons.version'},
{id: 'level', key: '3', label: 'api_test.automation.case_level'},
{id: 'tagNames', key: '4', label: 'api_test.automation.tag'},
{id: 'stepTotal', key: '7', label: 'api_test.automation.step'},
{id: 'envs', key: '8', label: 'commons.environment'},
{id: 'passRate', key: '9', label: 'api_test.automation.passing_rate'},
{id: 'maintainer', key: 'a', label: 'api_test.definition.request.responsible'},
{id: 'createUser', key: '5', label: 'api_test.automation.creator'},
{id: 'updateTime', key: '6', label: 'commons.update_time'},
{id: 'createTime', key: 'b', label: 'commons.create_time'},
{id: 'lastResult', key: 'c', label: 'api_test.automation.last_result'},
],
//测试计划-UI用例
TEST_PLAN_UI_SCENARIO_CASE: [
{id: 'num', key: '1', label: 'commons.id'},
{id: 'name', key: '2', label: 'api_test.automation.scenario_name'},
{id: 'versionId', key: 'd', label: 'commons.version'},
{id: 'level', key: '3', label: 'api_test.automation.case_level'},
{id: 'tagNames', key: '4', label: 'api_test.automation.tag'},
{id: 'stepTotal', key: '7', label: 'api_test.automation.step'},
{id: 'passRate', key: '9', label: 'api_test.automation.passing_rate'},
{id: 'maintainer', key: 'a', label: 'api_test.definition.request.responsible'},
{id: 'createUser', key: '5', label: 'api_test.automation.creator'},
{id: 'updateTime', key: '6', label: 'commons.update_time'},
{id: 'createTime', key: 'b', label: 'commons.create_time'},
{id: 'lastResult', key: 'c', label: 'api_test.automation.last_result'},
],
//测试用例
TRACK_TEST_CASE: [
{id: 'num', key: '1', label: 'commons.id'},
{id: 'name', key: '2', label: 'commons.name'},
{id: 'reviewStatus', key: '3', label: 'test_track.case.status'},
{id: 'tags', key: '4', label: 'commons.tag'},
{id: 'versionId', key: 'b', label: 'project.version.name', xpack: true},
{id: 'nodePath', key: '5', label: 'test_track.case.module'},
{id: 'updateTime', key: '6', label: 'commons.update_time'},
{id: 'createUser', key: '7', label: 'commons.create_user'},
{id: 'createTime', key: '8', label: 'commons.create_time'},
{id: 'desc', key: '9', label: 'test_track.case.case_desc'},
{id: 'lastExecuteResult', key: '0', label: 'test_track.plan_view.execute_result'},
],
// 公共用例库
TRACK_PUBLIC_TEST_CASE: [
{id: 'num', key: '1', label: 'commons.id'},
{id: 'name', key: '2', label: 'commons.name'},
{id: 'reviewStatus', key: '3', label: 'test_track.case.status'},
{id: 'tags', key: '4', label: 'commons.tag'},
{id: 'versionId', key: 'b', label: 'project.version.name', xpack: true},
{id: 'projectName', key: '5', label: 'test_track.case.project'},
{id: 'updateTime', key: '6', label: 'commons.update_time'},
{id: 'createName', key: '7', label: 'commons.create_user'},
{id: 'createTime', key: '8', label: 'commons.create_time'},
{id: 'desc', key: '9', label: 'test_track.case.case_desc'},
{id: 'lastExecuteResult', key: '0', label: 'test_track.plan_view.execute_result'},
],
//缺陷列表
ISSUE_LIST: [
{id: 'num', key: '1', label: 'test_track.issue.id'},
{id: 'title', key: '2', label: 'test_track.issue.title'},
{id: 'platformStatus', key: '3', label: 'test_track.issue.status'},
{id: 'platform', key: '4', label: 'test_track.issue.platform'},
{id: 'creatorName', key: '5', label: 'custom_field.issue_creator'},
{id: 'resourceName', key: '6', label: 'test_track.issue.issue_resource'},
{id: 'description', key: '7', label: 'test_track.issue.description'},
{id: 'caseCount', key: '9', label: 'api_test.definition.api_case_number'},
{id: 'createTime', key: '8', label: 'commons.create_time'},
],
//用例评审
TEST_CASE_REVIEW: [
{id: 'name', key: '1', label: 'test_track.review.review_name'},
{id: 'reviewer', key: '2', label: 'test_track.review.reviewer'},
{id: 'projectName', key: '3', label: 'test_track.review.review_project'},
{id: 'creatorName', key: '4', label: 'test_track.review.creator'},
{id: 'status', key: '5', label: 'test_track.review.review_status'},
{id: 'createTime', key: '6', label: 'commons.create_time'},
{id: 'endTime', key: '7', label: 'test_track.review.end_time'},
{id: 'tags', key: '8', label: 'commons.tag'},
],
//用例评审-功能用例
TEST_CASE_REVIEW_FUNCTION_TEST_CASE: [
{id: 'num', key: '1', label: 'commons.id'},
{id: 'name', key: '2', label: 'commons.name'},
{id: 'versionId', key: 'b', label: 'commons.version'},
{id: 'priority', key: '3', label: 'test_track.case.priority'},
{id: 'nodePath', key: '5', label: 'test_track.case.module'},
{id: 'projectName', key: '6', label: 'test_track.review.review_project'},
{id: 'reviewerName', key: '7', label: 'test_track.review.reviewer'},
{id: 'reviewStatus', key: '8', label: 'test_track.case.status'},
{id: 'updateTime', key: '9', label: 'commons.update_time'},
{id: 'maintainerName', key: 'a', label: 'custom_field.case_maintainer'},
],
}
Object.assign(CUSTOM_TABLE_HEADER, TRACK_HEADER);

View File

@ -0,0 +1,72 @@
export function getUrl(d) {
let url = "/#";
let resourceId = d.sourceId;
if (resourceId && (resourceId.startsWith("\"") || resourceId.startsWith("["))) {
resourceId = JSON.parse(d.sourceId);
}
if (resourceId instanceof Array) {
if (resourceId.length === 1) {
resourceId = resourceId[0];
} else {
return url;
}
}
switch (d.type) {
case "HTTPSamplerProxy":
switch (d.refType){
case "API":
url += "/api/definition?resourceId=" + resourceId;
break;
case "CASE":
url += "/api/definition?caseId=" + d.id+"&projectId="+d.projectId+"&workspaceId="+d.workspaceId;
break;
}
break;
case "JDBCSampler":
switch (d.refType){
case "API":
url += "/api/definition?resourceId=" + resourceId;
break;
case "CASE":
url += "/api/definition?caseId=" + d.id+"&projectId="+d.projectId+"&workspaceId="+d.workspaceId;
break;
}
break;
case "DebugSampler":
switch (d.refType){
case "API":
url += "/api/definition?resourceId=" + resourceId;
break;
case "CASE":
url += "/api/definition?caseId=" + d.id+"&projectId="+d.projectId+"&workspaceId="+d.workspaceId;
break;
}
break;
case "DubboSampler":
switch (d.refType){
case "API":
url += "/api/definition?resourceId=" + resourceId;
break;
case "CASE":
url += "/api/definition?caseId=" + d.id+"&projectId="+d.projectId+"&workspaceId="+d.workspaceId;
break;
}
break;
case "TCPSampler":
switch (d.refType){
case "API":
url += "/api/definition?resourceId=" + resourceId;
break;
case "CASE":
url += "/api/definition?caseId=" + d.id+"&projectId="+d.projectId+"&workspaceId="+d.workspaceId;
break;
}
break;
case "scenario":
url += "/api/automation?resourceId=" + resourceId;
break;
default:
break;
}
return url;
}

View File

@ -0,0 +1,180 @@
export let X_PACK_TABLE_HEADERS = {
//接口定义
API_DEFINITION_HEAD: [
{id: 'num', label: "ID"},
{id: 'name', label: 'api_test.definition.api_name'},
{id: 'method', label: 'api_test.definition.api_type'},
{id: 'versionId', key: 'b', label: 'project.version.name', xpack: true},
{id: 'userName', label: 'api_test.definition.api_principal'},
{id: 'path', label: 'api_test.definition.api_path'},
{id: 'tags', label: 'commons.tag'},
{id: 'updateTime', label: 'api_test.definition.api_last_time'},
{id: 'caseTotal', label: 'api_test.definition.api_case_number'},
{id: 'caseStatus', label: 'api_test.definition.api_case_status'},
{id: 'casePassingRate', label: 'api_test.definition.api_case_passing_rate'},
{id: 'status', label: 'api_test.definition.api_status'},
{id: 'createTime', label: 'commons.create_time'},
{id: 'projectName',label: 'report.project_name'},
],
//接口用例
API_CASE_HEAD: [
{id: 'num', label: "ID"},
{id: 'name', label: 'test_track.case.name'},
{id: 'priority', label: 'test_track.case.priority'},
{id: 'path', label: 'api_test.definition.api_definition_path'},
{id: 'versionId', key: 'b', label: 'project.version.name', xpack: true},
{id: 'execResult', label: 'test_track.plan_view.execute_result'},
{id: 'caseStatus', label: 'commons.status'},
{id: 'tags', label: 'commons.tag'},
{id: 'createUser', label: 'api_test.creator'},
{id: 'updateTime', label: 'api_test.definition.api_last_time'},
{id: 'createTime', label: 'commons.create_time'},
{id: 'projectName', label: 'report.project_name'},
{id: 'passRate', label: 'commons.pass_rate'},
{id: 'environment', label: 'commons.environment'},
],
//场景测试
API_SCENARIO_HEAD: [
{id: 'num', label: "ID"},
{id: 'name', label: 'api_report.scenario_name'},
{id: 'level', label: 'api_test.automation.case_level'},
{id: 'status', label: 'test_track.plan.plan_status'},
{id: 'versionId', key: 'b', label: 'project.version.name', xpack: true},
{id: 'tags', label: 'commons.tag'},
{id: 'userName', label: 'api_test.automation.creator'},
{id: 'principalName',label: 'api_test.definition.api_principal'},
{id: 'environmentMap', label: 'commons.environment'},
{id: 'updateTime', label: 'api_test.definition.api_last_time'},
{id: 'stepTotal', label: 'api_test.automation.step'},
{id: 'lastResult', label: 'api_test.automation.last_result'},
{id: 'passRate', label: 'api_test.automation.passing_rate'},
{id: 'createTime', label: 'commons.create_time'},
{id: 'projectName', label: 'report.project_name'},
],
//用例评审
TEST_CASE_REVIEW_HEAD: [
{id: 'name', label: 'test_track.review.review_name'},
{id: 'reviewer', label: 'test_track.review.reviewer'},
{id: 'projectName', key: 'p', label: 'report.project_name'},
{id: 'creatorName', label: 'test_track.review.review_creator'},
{id: 'status', label: 'test_track.review.review_status'},
{id: 'createTime', label: 'commons.create_time'},
{id: 'endTime', label: 'test_track.review.end_time'},
{id: 'tags', label: 'commons.tag'},
],
//用例评审-功能用例
TEST_CASE_REVIEW_FUNCTION_TEST_CASE_HEAD: [
{id: 'num', label: 'commons.id'},
{id: 'name', label: 'commons.name'},
{id: 'priority', label: 'test_track.case.priority'},
{id: 'type', label: 'test_track.case.type'},
{id: 'nodePath', label: 'test_track.case.module'},
{id: 'projectName', label: 'report.project_name'},
{id: 'reviewerName', label: 'test_track.review.reviewer'},
{id: 'reviewStatus', label: 'test_track.case.status'},
{id: 'updateTime', label: 'commons.update_time'},
{id: 'maintainerName', label: 'custom_field.case_maintainer'},
],
//测试计划
TEST_PLAN_LIST_HEAD: [
{id: 'name', label: 'commons.name'},
{id: 'userName', label: 'test_track.plan.plan_principal'},
{id: 'status', label: 'test_track.plan.plan_status'},
{id: 'stage', label: 'test_track.plan.plan_stage'},
{id: 'testRate', label: 'test_track.home.test_rate'},
{id: 'projectName', label: 'report.project_name'},
{id: 'plannedStartTime', label: 'test_track.plan.planned_start_time'},
{id: 'plannedEndTime',label: 'test_track.plan.planned_end_time'},
{id: 'actualStartTime', label: 'test_track.plan.actual_start_time'},
{id: 'actualEndTime', label: 'test_track.plan.actual_end_time'},
{id: 'tags', label: 'commons.tag'},
{id: 'executionTimes', label: 'commons.execution_times'},
{id: 'passRate', label: 'commons.pass_rate'},
{id: 'createUser', label: 'commons.create_user'},
],
//测试计划-功能用例
TEST_PLAN_FUNCTION_TEST_CASE_HEAD: [
{id: 'num', label: 'commons.id'},
{id: 'name', label: 'commons.name'},
{id: 'tags', label: 'commons.tag'},
{id: 'nodePath', label: 'test_track.case.module'},
{id: 'projectName', label: 'report.project_name'},
{id: 'issuesContent', label: 'test_track.issue.issue'},
{id: 'executor', label: 'test_track.plan_view.executor'},
{id: 'status', label: 'test_track.plan_view.execute_result'},
{id: 'updateTime', label: 'commons.update_time'},
{id: 'createTime', label: 'commons.create_time'},
],
//测试计划-api用例
TEST_PLAN_API_CASE_HEAD: [
{id: 'num', label: 'commons.id'},
{id: 'name', label: 'api_test.definition.api_name'},
{id: 'priority', label: 'test_track.case.priority'},
{id: 'path', label: 'api_test.definition.api_path'},
{id: 'createUser', label: 'api_test.creator'},
{id: 'tags', label: 'commons.tag'},
{id: 'execResult', label: 'test_track.plan.execute_result'},
{id: 'maintainer', label: 'api_test.definition.request.responsible'},
{id: 'updateTime', label: 'api_test.automation.update_time'},
{id: 'createTime', label: 'commons.create_time'},
{id: 'environmentName', label: 'commons.environment'},
],
//测试计划-性能用例
TEST_PLAN_LOAD_CASE_HEAD: [
{id: 'num', label: 'commons.id'},
{id: 'caseName',label: 'commons.name'},
{id: 'projectName', label: 'report.project_name'},
{id: 'userName', label: 'load_test.user_name'},
{id: 'createTime', label: 'commons.create_time'},
{id: 'status', label: 'commons.status'},
{id: 'caseStatus', label: 'test_track.plan.load_case.execution_status'},
{id: 'loadReportId', label: 'test_track.plan.load_case.report'},
],
//测试计划-场景用例
TEST_PLAN_SCENARIO_CASE_HEAD: [
{id: 'num', label: 'commons.id'},
{id: 'name', label: 'api_test.automation.scenario_name'},
{id: 'level', label: 'api_test.automation.case_level'},
{id: 'tagNames', label: 'api_test.automation.tag'},
{id: 'stepTotal', label: 'api_test.automation.step'},
{id: 'envs', label: 'commons.environment'},
{id: 'passRate', label: 'api_test.automation.passing_rate'},
{id: 'maintainer', label: 'api_test.definition.request.responsible'},
{id: 'createUser', label: 'api_test.automation.creator'},
{id: 'updateTime', label: 'api_test.automation.update_time'},
{id: 'createTime', label: 'commons.create_time'},
{id: 'lastResult', label: 'api_test.automation.last_result'},
{id: 'projectName', label: 'report.project_name'},
],
//测试用例
TRACK_TEST_CASE_HEAD: [
{id: 'num', label: 'commons.id'},
{id: 'name', label: 'commons.name'},
{id: 'reviewStatus', label: 'test_track.case.status'},
{id: 'versionId', key: 'b', label: 'project.version.name', xpack: true},
{id: 'tags', label: 'commons.tag'},
{id: 'nodePath', label: 'test_track.case.module'},
{id: 'updateTime', label: 'commons.update_time'},
{id: 'createUser', label: 'commons.create_user'},
{id: 'createTime', label: 'commons.create_time'},
{id: 'desc', label: 'test_track.case.case_desc'},
{id: 'projectName', key: 'p', label: 'report.project_name'},
{id: 'caseStatus', label: 'commons.status'},
],
//缺陷列表
ISSUE_LIST_HEAD: [
{id: 'num', label: 'test_track.issue.id'},
{id: 'title', label: 'test_track.issue.title'},
{id: 'platformStatus', label: 'test_track.issue.status'},
{id: 'platform', label: 'test_track.issue.platform'},
{id: 'creatorName',label: 'custom_field.issue_creator'},
{id: 'resourceName', label: 'test_track.issue.issue_resource'},
{id: 'description', label: 'test_track.issue.description'},
{id: 'caseCount', label: 'api_test.definition.api_case_number'},
{id: 'createTime', label: 'commons.create_time'},
{id: 'projectName', label: 'report.project_name'},
]
}

View File

@ -0,0 +1,31 @@
<template>
<workstation-detail :is-creation=true :current-todo-name="currentTodo"></workstation-detail>
</template>
<script>
import WorkstationDetail from "@/business/detail/WorkstationDetail"
export default {
name: 'Creation',
components: {
WorkstationDetail
},
watch: {},
data() {
return {
currentTodo:''
}
},
methods: {},
created() {
if (this.$route.query.name) {
this.currentTodo = this.$route.query.name
}
}
}
</script>
<style scoped>
.workstation-card {
height: 100%;
}
</style>

View File

@ -0,0 +1,56 @@
<template>
<div style="background-color:#F5F6F7;height: calc(100vh);">
<div class="api-home-layout">
<el-row style="margin-top: 12px;margin-bottom: 16px">
<my-dashboard-card style="height: 182px" card-type="upcoming" ></my-dashboard-card>
</el-row>
<el-row style="margin-bottom: 16px">
<my-dashboard-card style="height: 182px" card-type="focus"></my-dashboard-card>
</el-row>
<el-row :gutter=16 >
<el-col :span="12" >
<my-case-card style="height: 350px"></my-case-card>
</el-col>
<el-col :span="12" >
<my-flaw-card style="height: 350px"></my-flaw-card>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MyCaseCard from "./components/MyCaseCard";
import FocusCard from "./components/FocusCard";
import UpcomingCard from "./components/UpcomingCard";
import MyFlawCard from "./components/MyFlawCard";
import MyDashboardCard from "@/business/dashboard/components/MyDashboardCard";
export default {
name: 'DashBoard',
components: {
MyCaseCard,
MsMainContainer,
MsContainer,
MyFlawCard,
UpcomingCard,
FocusCard,
MyDashboardCard
},
data() {
return {}
},
mounted() {
},
methods: {}
}
</script>
<style scoped>
.api-home-layout {
margin: 16px 24px;
min-width: 1100px;
}
</style>

View File

@ -0,0 +1,322 @@
<template>
<el-card class="table-card" body-style="padding:10px;">
<el-row :gutter="10">
<el-col :span="12">
<span class="top-left-css">{{ $t('workstation.focus') }}</span>
</el-col>
<el-col :span="12" class="top-right-css">
<i class="el-icon-refresh" @click="refresh()"></i>
<el-select class="select-todo"
v-model="currentTodo"
filterable
@change="updateUpcoming"
default-first-option
placeholder="请选择关注">
<el-option
v-for="item in realTodoList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
</el-row>
<el-row :gutter="10">
<ms-main-container>
<div v-if="toReLoad">
<case-table-list
v-if="currentTodo === 'TRACK_CASE'"
:tree-nodes="treeNodes"
@refresh="refresh"
@setCondition="setCondition"
:custom-num="custom_num"
:is-focus=true
:is-show-all-column=false
screen-height="40vh"
ref="caseTableList">
</case-table-list>
<plan-table-list
v-if="currentTodo === 'TRACK_PLAN'"
:is-focus=true
:is-show-all-column=false
screen-height="40vh"
ref="testPlanList">
</plan-table-list>
<review-table-list
v-if="currentTodo === 'TRACK_REVIEW'"
:is-focus=true
:is-show-all-column=false
screen-height="40vh"
ref="testPlanList">
</review-table-list>
<issue-table-list
v-if="currentTodo === 'TRACK_ISSUE'"
@handlePageChange="getIssues"
@refresh="getIssues"
:is-show-all-column=false
:is-select-all=true
:is-focus=true
screen-height="40vh"
ref="issueTableList"
>
</issue-table-list>
<api-definition-table-list
v-if="currentTodo === 'API_DEFINITION'"
@runTest="runTest"
:visible="true"
:trash-enable="true"
:queryDataType="queryDataType"
:is-read-only="false"
@refreshTable="refresh"
ref="apiDefinitionTableList"
:is-focus=true
:is-show-all-column=false
screen-height="40vh"
>
</api-definition-table-list>
<performance-table-list
v-if="currentTodo === 'PERFORMANCE'"
ref="performanceTableList"
:is-focus=true
:is-show-all-column=false
screen-height="40vh"
>
</performance-table-list>
<automation-table-list
v-if="currentTodo === 'API_AUTOMATION'"
:is-focus=true
:is-show-all-column=false
screen-height="40vh"
ref="automationTableList">
</automation-table-list>
<api-case-table-list
v-if="currentTodo === 'API_CASE'"
:is-focus=true
:is-show-all-column=false
screen-height="40vh"
ref="apiCaseTableList"
>
</api-case-table-list>
</div>
</ms-main-container>
</el-row>
</el-card>
</template>
<script>
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import CaseTableList from "@/business/component/CaseTableList";
import {getUUID} from "metersphere-frontend/src/utils";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {hasPermissions} from "metersphere-frontend/src/utils/permission";
import {getIssues, syncIssues} from "@/api/issue";
import PlanTableList from "@/business/component/PlanTableList";
import ReviewTableList from "@/business/component/ReviewTableList";
import {getLastTableSortField} from "metersphere-frontend/src/utils/tableUtils";
import ApiDefinitionTableList from "@/business/component/ApiDefinitionTableList";
import PerformanceTableList from "@/business/component/PerformanceTableList";
import AutomationTableList from "@/business/component/AutomationTableList";
import ApiCaseTableList from "@/business/component/ApiCaseTableList";
import {WORKSTATION} from "metersphere-frontend/src/utils/constants";
import IssueTableList from "@/business/component/IssueTableList";
export default {
name: "FocusCard",
components: {
MsMainContainer,
IssueTableList,
CaseTableList,
PlanTableList,
ReviewTableList,
ApiDefinitionTableList,
PerformanceTableList,
AutomationTableList,
ApiCaseTableList
},
watch: {},
data() {
return {
toReLoad: true,
currentTodo: '',
todoList: [
{
value: 'TRACK_CASE',
label: this.$t('workstation.table_name.track_case'),
permission: ['PROJECT_TRACK_CASE:READ']
},
{
value: 'TRACK_PLAN',
label: this.$t('workstation.table_name.track_plan'),
permission: ['PROJECT_TRACK_PLAN:READ']
}, {
value: 'TRACK_REVIEW',
label: this.$t('workstation.table_name.track_review'),
permission: ['PROJECT_TRACK_REVIEW:READ']
}, {
value: 'TRACK_ISSUE',
label: this.$t('workstation.table_name.track_issue'),
permission: ['PROJECT_TRACK_ISSUE:READ']
}, {
value: 'API_DEFINITION',
label: this.$t('workstation.table_name.api_definition'),
permission: ['PROJECT_API_DEFINITION:READ']
}, {
value: 'API_CASE',
label: this.$t('workstation.table_name.api_case'),
permission: ['PROJECT_API_SCENARIO:READ']
}, {
value: 'API_AUTOMATION',
label: this.$t('workstation.table_name.api_automation'),
permission: ['PROJECT_API_SCENARIO:READ']
}, {
value: 'PERFORMANCE',
label: this.$t('workstation.table_name.performance'),
permission: ['PROJECT_PERFORMANCE_TEST:READ']
}
],
realTodoList: [],
condition: {},
custom_num: false,
treeNodes: [],
activeTab: "api",
apiTabs: [{
title: this.$t('api_test.definition.api_title'),
name: 'default',
type: "list",
closable: false
}],
apiDefaultTab: 'default',
selectCase: {},
};
},
methods: {
updateUpcoming(value) {
sessionStorage.setItem(WORKSTATION.FOCUS, value);
},
refresh() {
this.toReLoad = false;
this.$nextTick(function () {
this.toReLoad = true
})
},
setCondition(data) {
this.condition = data;
},
changeRedirectParam(redirectIDParam) {
this.redirectID = redirectIDParam;
if (redirectIDParam != null) {
if (this.redirectFlag === "none") {
this.activeName = "default";
this.addListener();
this.redirectFlag = "redirected";
}
} else {
this.redirectFlag = "none";
}
},
setTreeNodes(data) {
this.treeNodes = data;
},
syncIssues() {
this.page.result = syncIssues(() => {
this.getIssues();
});
},
getIssues() {
this.page.condition.projectId = this.projectId;
this.page.condition.orders = getLastTableSortField(this.tableHeaderKey);
this.page.result = getIssues(this.page);
},
runTest(data) {
this.activeTab = "test";
this.handleTabsEdit(this.$t("commons.api"), "TEST", data);
this.setTabTitle(data);
},
setTabTitle(data) {
for (let index in this.apiTabs) {
let tab = this.apiTabs[index];
if (tab.name === this.apiDefaultTab) {
tab.title = this.$t('api_test.definition.request.edit_api') + "-" + data.name;
break;
}
}
},
handleTabsEdit(targetName, action, api) {
if (!this.projectId) {
this.$warning(this.$t('commons.check_project_tip'));
return;
}
if (targetName === undefined || targetName === null) {
targetName = this.$t('api_test.definition.request.title');
}
let newTabName = getUUID();
this.apiTabs.push({
title: targetName,
name: newTabName,
closable: true,
type: action,
api: api,
});
if (action === "ADD") {
this.activeTab = "api";
}
this.apiDefaultTab = newTabName;
},
},
computed: {
projectId() {
return getCurrentProjectID();
},
selectNode() {
return this.$store.state.testCaseSelectNode;
},
queryDataType: function () {
let routeParam = this.$route.params.dataType;
let redirectIDParam = this.$route.params.redirectID;
this.changeRedirectParam(redirectIDParam);
return routeParam;
},
},
created() {
for (let i = 0; i < this.todoList.length; i++) {
let todo = this.todoList[i];
if (hasPermissions(...todo.permission)) {
this.realTodoList.push(todo)
}
}
if (sessionStorage.getItem(WORKSTATION.FOCUS)) {
this.currentTodo = sessionStorage.getItem(WORKSTATION.FOCUS);
} else {
if (this.realTodoList.length > 0) {
this.currentTodo = this.realTodoList[0].value
}
}
}
}
</script>
<style scoped>
.top-left-css {
margin-left: 49px;
font-weight: 650;
font-style: normal;
font-size: 17px;
}
.top-right-css {
text-align: right;
}
.select-todo {
width: 33%;
padding-left: 10px;
}
.el-icon-refresh {
cursor: pointer;
}
.ms-main-container {
height: calc(100vh - 200px);
}
</style>

View File

@ -0,0 +1,138 @@
<template>
<el-card shadow="never" body-style="margin-top: 24px; padding: 0;border:none;" class="table-card">
<el-row :gutter="10" >
<el-col :span="24">
<span class="top-css">{{ $t('workstation.creation_case') }}</span>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col v-if="showCount">
<ms-border-pie-chart :pie-data="loadCharData" :autoresize ="true" :text="totalCount.toString()" :text-title="$t('workstation.case_count')"
:subtext="subtextStr" :radius="['70%', '96%']"
:height="255"/>
</el-col>
<el-col v-else>
<img style="height: 100px;width: 100px;padding-top: 10%;padding-left: 40%;"
src="/assets/figma/icon_none.svg"/>
<p class="right-other-css" v-permission="['PROJECT_TRACK_CASE:READ']">{{ $t('workstation.creation_case_tip') }} &nbsp;&nbsp;<span style="color: var(--primary_color)" @click="toCreatCase()">{{$t('permission.project_track_case.create') }}</span></p>
</el-col>
</el-row>
</el-card>
</template>
<script>
import MsBorderPieChart from "metersphere-frontend/src/components/MsBorderPieChart";
import {getMyCreatedCaseGroupContMap} from "@/api/workstation";
export default{
name: "MyCaseCard",
components: {MsBorderPieChart},
data() {
return {
showCount :false,
totalCount:0,
weekTotalCount:0,
subtextStr:"",
loadCharData:[]
};
},
methods:{
getCaseCount() {
let isWeek = false;
this.result = getMyCreatedCaseGroupContMap(isWeek).then(response => {
let tableData = response.data
const testCaseCount = {
value:tableData.testCaseCount===0?'':tableData.testCaseCount,
name:this.$t('workstation.table_name.track_case'),
}
this.loadCharData.push(testCaseCount)
const apiTestCaseCount = {
value:tableData.apiTestCaseCount===0?'':tableData.apiTestCaseCount,
name:this.$t('workstation.table_name.api_case'),
}
this.loadCharData.push(apiTestCaseCount)
const apiScenarioCaseCount = {
value:tableData.apiScenarioCaseCount===0?'':tableData.apiScenarioCaseCount,
name:this.$t('workstation.table_name.scenario_case'),
}
this.loadCharData.push(apiScenarioCaseCount)
const loadTestCount = {
value:tableData.loadTestCount===0?'':tableData.loadTestCount,
name:this.$t('test_track.plan.load_case.case'),
}
this.loadCharData.push(loadTestCount)
this.totalCount = tableData.testCaseCount+tableData.apiTestCaseCount+tableData.apiScenarioCaseCount+tableData.loadTestCount;
if (this.totalCount > 0) {
this.getCaseWeekCount();
}
});
},
toCreatCase(){
let caseData = this.$router.resolve({
path: '/track/case/create',
});
window.open(caseData.href, '_blank');
},
getCaseWeekCount(){
let isWeek = true;
getMyCreatedCaseGroupContMap(isWeek).then(response => {
let tableData = response.data
this.weekTotalCount = tableData.testCaseCount+tableData.apiTestCaseCount+tableData.apiScenarioCaseCount+tableData.loadTestCount;
if (this.weekTotalCount){
this.subtextStr = "本周:+"+this.weekTotalCount+" >" ;
}
this.showCount = true;
});
}
},
created() {
this.getCaseCount();
}
}
</script>
<style scoped>
.table-card{
height: 100%;
}
.right-css{
text-align: right;
margin-top: 100px;
}
.right-two-css{
font-weight: 650;
color: #783987;
font-size: 21px;
}
.right-one-css{
font-weight: 700;
font-size: 43px;
color: #783987;
}
.top-css{
font-weight: 650;
font-style: normal;
font-size: 18px;
align-self: flex-start;
padding: 0px 0px 0px 0px;
box-sizing: border-box;
width: 100%;
color:#000000;
margin-left: 24px;
line-height: 26px;
}
.right-other-css{
color: #969393;
cursor: pointer;
padding-left: 34%;
}
.table-card{
border: none;
color: rgba(192, 196, 204, 0.98);
}
</style>

View File

@ -0,0 +1,196 @@
<template>
<el-card shadow="never"
body-style="margin-top: 24px;padding: 0;" class="table-card">
<el-row :gutter="10">
<el-col :span="24">
<span class="top-css">{{ title }}</span>
</el-col>
</el-row>
<el-row>
<div class="row-card">
<el-card v-for="(option,index) in contentArray" :key="index"
body-style="padding-top: 16px; padding-left: 16px; padding-bottom: 16px;"
class="card-info" shadow="never" @click.native="gotoDetail(option.name)">
<div class="card-name">{{option.label}}</div>
<div class="card-value">{{option.value}}</div>
</el-card>
</div>
</el-row>
</el-card>
</template>
<script>
import {getFollowTotalCount, getUpcomingTotalCount} from "@/api/workstation";
import {getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
export default {
name: "MyUpcomingCard",
props: {
cardType: {
type: String,
default() {
return "upcoming"
}
}
},
data() {
return {
contentArray:[
{
name: 'track_case',
value:0,
label: this.$t('workstation.table_name.track_case'),
},
{
name:"track_plan",
value:0,
label: this.$t('workstation.table_name.track_plan'),
},
{
name:"track_review",
value:0,
label: this.$t('workstation.table_name.track_review'),
},
{
name:"track_issue",
value:0,
label: this.$t('workstation.table_name.track_issue'),
},
{
name:"api_definition",
value:0,
label: this.$t('workstation.table_name.api_definition'),
},
{
name:"api_case",
value:0,
label: this.$t('workstation.table_name.api_case'),
},
{
name:"api_automation",
value:0,
label: this.$t('workstation.table_name.api_automation'),
},
{
name:"performance",
value:0,
label: this.$t('workstation.table_name.performance'),
},
],
title:"",
};
},
methods:{
gotoDetail(name){
if (this.cardType === 'upcoming') {
let upcoming =this.$router.resolve({
path: "/workstation/upcoming",
query: {name: name}
});
window.open(upcoming.href, '_blank');
} else {
let focus =this.$router.resolve({
path: "/workstation/focus",
query: {name: name}
});
window.open(focus.href, '_blank');
}
},
getData(){
if (this.cardType !== 'upcoming'){
getFollowTotalCount(getCurrentWorkspaceId()).then(response => {
let tableData = response.data
if (tableData) {
this.contentArray.forEach(m => {
let countName = m.name;
m.value = tableData[countName];
});
}
});
} else {
getUpcomingTotalCount(getCurrentWorkspaceId()).then(response => {
let tableData = response.data
if (tableData) {
this.contentArray.forEach(m => {
let countName = m.name;
m.value = tableData[countName];
});
}
});
}
}
},
created() {
if (this.cardType === 'upcoming') {
this.title = this.$t('workstation.upcoming');
} else {
this.title = this.$t('workstation.focus');
}
this.getData();
},
}
</script>
<style lang="scss" scoped>
.row-card{
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
flex-wrap: nowrap;
margin-top: 24px;
margin-left: 8px;
margin-right: 24px
}
.card-info{
background: #FFFFFF;
border: 1px solid #DEE0E3;
border-radius: 4px;
height: 82px;
margin-left: 16px;
width: 100%;
cursor: pointer;
&:hover {
background: #F5F6F7;
}
}
.top-css {
left: 24px;
font-weight: 650;
font-style: normal;
font-size: 18px;
align-self: flex-start;
padding: 0px 0px 0px 0px;
box-sizing: border-box;
width: 100%;
color:#000000;
margin-left: 24px;
line-height: 26px;
}
.card-name{
color: #646A73;
font-size: 14px;
line-height: 22px;
height: 22px;
font-weight: 400
}
.card-value{
color: var(--primary_color);
height: 28px;
font-weight: 500;
font-size: 20px;
line-height: 28px;
display: flex;
align-items: center;
letter-spacing: -0.01em;
}
.table-card{
border: none;
color: rgba(192, 196, 204, 0.98);
}
</style>

Some files were not shown because too many files have changed in this diff Show More