feat: 测试计划报告分享

This commit is contained in:
chenjianxing 2021-08-20 10:42:15 +08:00 committed by jianxing
parent d00fde6251
commit 2500b219a4
55 changed files with 645 additions and 362 deletions

View File

@ -1,6 +1,5 @@
package io.metersphere.api.controller;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.*;
@ -11,11 +10,12 @@ import io.metersphere.api.dto.datacount.response.ApiDataCountDTO;
import io.metersphere.api.dto.datacount.response.ExecutedCaseInfoDTO;
import io.metersphere.api.dto.datacount.response.TaskInfoResult;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
import io.metersphere.api.service.*;
import io.metersphere.base.domain.*;
import io.metersphere.base.domain.ApiDefinition;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import io.metersphere.base.domain.ApiTest;
import io.metersphere.base.domain.Schedule;
import io.metersphere.commons.constants.NoticeConstants;
import io.metersphere.commons.utils.CronUtils;
import io.metersphere.commons.utils.PageUtils;
@ -28,13 +28,15 @@ import io.metersphere.dto.ScheduleDao;
import io.metersphere.notice.annotation.SendNotice;
import io.metersphere.service.CheckPermissionService;
import io.metersphere.service.ScheduleService;
import org.apache.jorphan.collections.HashTree;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import static io.metersphere.commons.utils.JsonPathUtils.getListJson;

View File

@ -1,14 +1,13 @@
package io.metersphere.api.controller;
import io.metersphere.api.dto.document.ApiDocumentInfoDTO;
import io.metersphere.api.dto.document.ApiDocumentRequest;
import io.metersphere.api.dto.document.ApiDocumentShareDTO;
import io.metersphere.api.dto.document.ApiDocumentShareRequest;
import io.metersphere.api.service.APITestService;
import io.metersphere.api.dto.share.ApiDocumentInfoDTO;
import io.metersphere.api.dto.share.ApiDocumentRequest;
import io.metersphere.api.dto.share.ApiDocumentShareRequest;
import io.metersphere.api.dto.share.ShareInfoDTO;
import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.api.service.ApiDocumentService;
import io.metersphere.api.service.ShareInfoService;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiDocumentShare;
import io.metersphere.base.domain.ShareInfo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@ -23,21 +22,24 @@ import java.util.stream.Collectors;
* @Description
*/
@RestController
@RequestMapping(value = "/api/document")
public class ApiDocumentController {
@RequestMapping(value = "/share/info")
public class ShareInfoController {
@Resource
ApiDocumentService apiDocumentService;
ShareInfoService shareInfoService;
@Resource
ApiDefinitionService apiDefinitionService;
@Resource
APITestService apiTestService;
@PostMapping("/selectApiSimpleInfo")
public List<ApiDocumentInfoDTO> list(@RequestBody ApiDocumentRequest request) {
List<ApiDocumentInfoDTO> returnList = apiDocumentService.findApiDocumentSimpleInfoByRequest(request);
List<ApiDocumentInfoDTO> returnList = shareInfoService.findApiDocumentSimpleInfoByRequest(request);
return returnList;
}
@GetMapping("/get/{id}")
public ShareInfo get(@PathVariable String id) {
return shareInfoService.get(id);
}
@PostMapping("/selectApiInfoByParam")
public List<ApiDocumentInfoDTO> selectApiInfoByParam(@RequestBody ApiDocumentRequest request) {
List<ApiDocumentInfoDTO> returnList = new ArrayList<>();
@ -52,7 +54,7 @@ public class ApiDocumentController {
model.setId(id);
model.setName(id);
}
ApiDocumentInfoDTO returnDTO = apiDocumentService.conversionModelToDTO(model);
ApiDocumentInfoDTO returnDTO = shareInfoService.conversionModelToDTO(model);
returnList.add(returnDTO);
}
}
@ -64,7 +66,7 @@ public class ApiDocumentController {
ApiDefinitionWithBLOBs apiModel = apiDefinitionService.getBLOBs(id);
ApiDocumentInfoDTO returnDTO = new ApiDocumentInfoDTO();
try{
returnDTO = apiDocumentService.conversionModelToDTO(apiModel);
returnDTO = shareInfoService.conversionModelToDTO(apiModel);
}catch (Exception e){
e.printStackTrace();
}
@ -73,9 +75,16 @@ public class ApiDocumentController {
}
@PostMapping("/generateApiDocumentShareInfo")
public ApiDocumentShareDTO generateApiDocumentShareInfo(@RequestBody ApiDocumentShareRequest request) {
ApiDocumentShare apiShare = apiDocumentService.generateApiDocumentShare(request);
ApiDocumentShareDTO returnDTO = apiDocumentService.conversionApiDocumentShareToDTO(apiShare);
public ShareInfoDTO generateApiDocumentShareInfo(@RequestBody ApiDocumentShareRequest request) {
ShareInfo apiShare = shareInfoService.generateApiDocumentShareInfo(request);
ShareInfoDTO returnDTO = shareInfoService.conversionShareInfoToDTO(apiShare);
return returnDTO;
}
@PostMapping("/generateShareInfo")
public ShareInfoDTO generateShareInfo(@RequestBody ShareInfo request) {
ShareInfo apiShare = shareInfoService.generateShareInfo(request);
ShareInfoDTO returnDTO = shareInfoService.conversionShareInfoToDTO(apiShare);
return returnDTO;
}
}

View File

@ -1,5 +0,0 @@
package io.metersphere.api.dto.document;
public enum ApiDocumentShareType {
Single,Batch
}

View File

@ -1,4 +1,4 @@
package io.metersphere.api.dto.document;
package io.metersphere.api.dto.share;
import lombok.Getter;
import lombok.Setter;

View File

@ -1,4 +1,4 @@
package io.metersphere.api.dto.document;
package io.metersphere.api.dto.share;
import lombok.Getter;
import lombok.Setter;

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.document;
package io.metersphere.api.dto.share;
import io.metersphere.base.domain.ShareInfo;
import lombok.Getter;
import lombok.Setter;
@ -12,7 +13,6 @@ import java.util.List;
*/
@Getter
@Setter
public class ApiDocumentShareRequest {
private String shareType;
public class ApiDocumentShareRequest extends ShareInfo {
private List<String> shareApiIdList;
}

View File

@ -1,4 +1,4 @@
package io.metersphere.api.dto.document;
package io.metersphere.api.dto.share;
import lombok.Getter;
import lombok.Setter;

View File

@ -1,4 +1,4 @@
package io.metersphere.api.dto.document;
package io.metersphere.api.dto.share;
import lombok.Getter;
import lombok.Setter;
@ -10,7 +10,7 @@ import lombok.Setter;
*/
@Getter
@Setter
public class ApiDocumentShareDTO {
public class ShareInfoDTO {
private String id;
private String shareUrl;
}

View File

@ -0,0 +1,5 @@
package io.metersphere.api.dto.share;
public enum ShareInfoType {
Single,Batch
}

View File

@ -2,14 +2,13 @@ package io.metersphere.api.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.document.*;
import io.metersphere.api.dto.share.*;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiDocumentShare;
import io.metersphere.base.mapper.ApiDocumentShareMapper;
import io.metersphere.base.mapper.ext.ExtApiDocumentMapper;
import io.metersphere.base.mapper.ext.ExtApiDocumentShareMapper;
import io.metersphere.base.domain.ShareInfo;
import io.metersphere.base.mapper.ShareInfoMapper;
import io.metersphere.base.mapper.ext.ExtShareInfoMapper;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.service.SystemParameterService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@ -25,37 +24,35 @@ import java.util.*;
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiDocumentService {
public class ShareInfoService {
@Resource
ExtApiDocumentMapper extApiDocumentMapper;
ExtShareInfoMapper extShareInfoMapper;
@Resource
ApiDocumentShareMapper apiDocumentShareMapper;
@Resource
ExtApiDocumentShareMapper extApiDocumentShareMapper;
ShareInfoMapper shareInfoMapper;
@Resource
SystemParameterService systemParameterService;
public List<ApiDocumentInfoDTO> findApiDocumentSimpleInfoByRequest(ApiDocumentRequest request) {
if (this.isParamLegitimacy(request)) {
if (request.getProjectId() == null) {
List<String> shareIdList = this.selectShareIdByApiDocumentShareId(request.getShareId());
List<String> shareIdList = this.selectShareIdByShareInfoId(request.getShareId());
request.setApiIdList(shareIdList);
return extApiDocumentMapper.findApiDocumentSimpleInfoByRequest(request);
return extShareInfoMapper.findApiDocumentSimpleInfoByRequest(request);
} else {
return extApiDocumentMapper.findApiDocumentSimpleInfoByRequest(request);
return extShareInfoMapper.findApiDocumentSimpleInfoByRequest(request);
}
} else {
return new ArrayList<>();
}
}
private List<String> selectShareIdByApiDocumentShareId(String shareId) {
private List<String> selectShareIdByShareInfoId(String shareId) {
List<String> shareApiIdList = new ArrayList<>();
ApiDocumentShare share = apiDocumentShareMapper.selectByPrimaryKey(shareId);
ShareInfo share = shareInfoMapper.selectByPrimaryKey(shareId);
if (share != null) {
try {
JSONArray jsonArray = JSONArray.parseArray(share.getShareApiId());
JSONArray jsonArray = JSONArray.parseArray(share.getCustomData());
for (int i = 0; i < jsonArray.size(); i++) {
String apiId = jsonArray.getString(i);
shareApiIdList.add(apiId);
@ -381,40 +378,42 @@ public class ApiDocumentService {
* 搜索的到就返回那条数据搜索不到就新增一条信息
*
* @param request 入参
* @return ApiDocumentShare数据对象
* @return ShareInfo数据对象
*/
public ApiDocumentShare generateApiDocumentShare(ApiDocumentShareRequest request) {
ApiDocumentShare apiDocumentShare = null;
public ShareInfo generateApiDocumentShareInfo(ApiDocumentShareRequest request) {
if (request.getShareApiIdList() != null && !request.getShareApiIdList().isEmpty()
&& StringUtils.equalsAny(request.getShareType(), ApiDocumentShareType.Single.name(), ApiDocumentShareType.Batch.name())) {
&& StringUtils.equalsAny(request.getShareType(), ShareInfoType.Single.name(), ShareInfoType.Batch.name())) {
//将ID进行排序
List<ApiDocumentShare> apiDocumentShareList = this.findByShareTypeAndShareApiIdWithBLOBs(request.getShareType(), request.getShareApiIdList());
if (apiDocumentShareList.isEmpty()) {
String shareApiIdJsonArrayString = this.genShareIdJsonString(request.getShareApiIdList());
long createTime = System.currentTimeMillis();
apiDocumentShare = new ApiDocumentShare();
apiDocumentShare.setId(UUID.randomUUID().toString());
apiDocumentShare.setShareApiId(shareApiIdJsonArrayString);
apiDocumentShare.setCreateUserId(SessionUtils.getUserId());
apiDocumentShare.setCreateTime(createTime);
apiDocumentShare.setUpdateTime(createTime);
apiDocumentShare.setShareType(request.getShareType());
apiDocumentShareMapper.insert(apiDocumentShare);
} else {
return apiDocumentShareList.get(0);
}
ShareInfo shareInfoRequest = new ShareInfo();
BeanUtils.copyBean(shareInfoRequest, request);
shareInfoRequest.setCustomData(genShareIdJsonString(request.getShareApiIdList()));
return generateShareInfo(shareInfoRequest);
}
if (apiDocumentShare == null) {
apiDocumentShare = new ApiDocumentShare();
}
return apiDocumentShare;
return new ShareInfo();
}
private List<ApiDocumentShare> findByShareTypeAndShareApiIdWithBLOBs(String shareType, List<String> shareApiIdList) {
public ShareInfo generateShareInfo(ShareInfo request) {
ShareInfo shareInfo = null;
List<ShareInfo> shareInfos = extShareInfoMapper.selectByShareTypeAndShareApiIdWithBLOBs(request.getShareType(), request.getCustomData());
if (shareInfos.isEmpty()) {
long createTime = System.currentTimeMillis();
shareInfo = new ShareInfo();
shareInfo.setId(UUID.randomUUID().toString());
shareInfo.setCustomData(request.getCustomData());
shareInfo.setCreateUserId(SessionUtils.getUserId());
shareInfo.setCreateTime(createTime);
shareInfo.setUpdateTime(createTime);
shareInfo.setShareType(request.getShareType());
shareInfoMapper.insert(shareInfo);
return shareInfo;
} else {
return shareInfos.get(0);
}
}
private List<ShareInfo> findByShareTypeAndShareApiIdWithBLOBs(String shareType, List<String> shareApiIdList) {
String shareApiIdString = this.genShareIdJsonString(shareApiIdList);
return extApiDocumentShareMapper.selectByShareTypeAndShareApiIdWithBLOBs(shareType, shareApiIdString);
return extShareInfoMapper.selectByShareTypeAndShareApiIdWithBLOBs(shareType, shareApiIdString);
}
/**
@ -428,14 +427,17 @@ public class ApiDocumentService {
return JSONArray.toJSONString(treeSet);
}
public ApiDocumentShareDTO conversionApiDocumentShareToDTO(ApiDocumentShare apiShare) {
ApiDocumentShareDTO returnDTO = new ApiDocumentShareDTO();
if (!StringUtils.isEmpty(apiShare.getShareApiId())) {
BaseSystemConfigDTO baseSystemConfigDTO = systemParameterService.getBaseInfo();
public ShareInfoDTO conversionShareInfoToDTO(ShareInfo apiShare) {
ShareInfoDTO returnDTO = new ShareInfoDTO();
if (!StringUtils.isEmpty(apiShare.getCustomData())) {
String url = "?" + apiShare.getId();
returnDTO.setId(apiShare.getId());
returnDTO.setShareUrl(url);
}
return returnDTO;
}
public ShareInfo get(String id) {
return shareInfoMapper.selectByPrimaryKey(id);
}
}

View File

@ -4,7 +4,7 @@ import java.io.Serializable;
import lombok.Data;
@Data
public class ApiDocumentShare implements Serializable {
public class ShareInfo implements Serializable {
private String id;
private Long createTime;
@ -15,7 +15,7 @@ public class ApiDocumentShare implements Serializable {
private String shareType;
private String shareApiId;
private String customData;
private static final long serialVersionUID = 1L;
}

View File

@ -3,14 +3,14 @@ package io.metersphere.base.domain;
import java.util.ArrayList;
import java.util.List;
public class ApiDocumentShareExample {
public class ShareInfoExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public ApiDocumentShareExample() {
public ShareInfoExample() {
oredCriteria = new ArrayList<Criteria>();
}

View File

@ -1,36 +0,0 @@
package io.metersphere.base.mapper;
import io.metersphere.base.domain.ApiDocumentShare;
import io.metersphere.base.domain.ApiDocumentShareExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface ApiDocumentShareMapper {
long countByExample(ApiDocumentShareExample example);
int deleteByExample(ApiDocumentShareExample example);
int deleteByPrimaryKey(String id);
int insert(ApiDocumentShare record);
int insertSelective(ApiDocumentShare record);
List<ApiDocumentShare> selectByExampleWithBLOBs(ApiDocumentShareExample example);
List<ApiDocumentShare> selectByExample(ApiDocumentShareExample example);
ApiDocumentShare selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") ApiDocumentShare record, @Param("example") ApiDocumentShareExample example);
int updateByExampleWithBLOBs(@Param("record") ApiDocumentShare record, @Param("example") ApiDocumentShareExample example);
int updateByExample(@Param("record") ApiDocumentShare record, @Param("example") ApiDocumentShareExample example);
int updateByPrimaryKeySelective(ApiDocumentShare record);
int updateByPrimaryKeyWithBLOBs(ApiDocumentShare record);
int updateByPrimaryKey(ApiDocumentShare record);
}

View File

@ -0,0 +1,36 @@
package io.metersphere.base.mapper;
import io.metersphere.base.domain.ShareInfo;
import io.metersphere.base.domain.ShareInfoExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface ShareInfoMapper {
long countByExample(ShareInfoExample example);
int deleteByExample(ShareInfoExample example);
int deleteByPrimaryKey(String id);
int insert(ShareInfo record);
int insertSelective(ShareInfo record);
List<ShareInfo> selectByExampleWithBLOBs(ShareInfoExample example);
List<ShareInfo> selectByExample(ShareInfoExample example);
ShareInfo selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") ShareInfo record, @Param("example") ShareInfoExample example);
int updateByExampleWithBLOBs(@Param("record") ShareInfo record, @Param("example") ShareInfoExample example);
int updateByExample(@Param("record") ShareInfo record, @Param("example") ShareInfoExample example);
int updateByPrimaryKeySelective(ShareInfo record);
int updateByPrimaryKeyWithBLOBs(ShareInfo record);
int updateByPrimaryKey(ShareInfo record);
}

View File

@ -1,15 +1,15 @@
<?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.ApiDocumentShareMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.ApiDocumentShare">
<mapper namespace="io.metersphere.base.mapper.ShareInfoMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.ShareInfo">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="create_user_id" jdbcType="VARCHAR" property="createUserId" />
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
<result column="share_type" jdbcType="VARCHAR" property="shareType" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.ApiDocumentShare">
<result column="share_api_id" jdbcType="LONGVARCHAR" property="shareApiId" />
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.ShareInfo">
<result column="custom_data" jdbcType="LONGVARCHAR" property="customData" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -73,9 +73,9 @@
id, create_time, create_user_id, update_time, share_type
</sql>
<sql id="Blob_Column_List">
share_api_id
custom_data
</sql>
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.base.domain.ApiDocumentShareExample" resultMap="ResultMapWithBLOBs">
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.base.domain.ShareInfoExample" resultMap="ResultMapWithBLOBs">
select
<if test="distinct">
distinct
@ -83,7 +83,7 @@
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from api_document_share
from share_info
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
@ -91,13 +91,13 @@
order by ${orderByClause}
</if>
</select>
<select id="selectByExample" parameterType="io.metersphere.base.domain.ApiDocumentShareExample" resultMap="BaseResultMap">
<select id="selectByExample" parameterType="io.metersphere.base.domain.ShareInfoExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from api_document_share
from share_info
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
@ -110,29 +110,29 @@
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from api_document_share
from share_info
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from api_document_share
delete from share_info
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.ApiDocumentShareExample">
delete from api_document_share
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.ShareInfoExample">
delete from share_info
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.ApiDocumentShare">
insert into api_document_share (id, create_time, create_user_id,
update_time, share_type, share_api_id
<insert id="insert" parameterType="io.metersphere.base.domain.ShareInfo">
insert into share_info (id, create_time, create_user_id,
update_time, share_type, custom_data
)
values (#{id,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{createUserId,jdbcType=VARCHAR},
#{updateTime,jdbcType=BIGINT}, #{shareType,jdbcType=VARCHAR}, #{shareApiId,jdbcType=LONGVARCHAR}
#{updateTime,jdbcType=BIGINT}, #{shareType,jdbcType=VARCHAR}, #{customData,jdbcType=LONGVARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ApiDocumentShare">
insert into api_document_share
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ShareInfo">
insert into share_info
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
@ -149,8 +149,8 @@
<if test="shareType != null">
share_type,
</if>
<if test="shareApiId != null">
share_api_id,
<if test="customData != null">
custom_data,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
@ -169,19 +169,19 @@
<if test="shareType != null">
#{shareType,jdbcType=VARCHAR},
</if>
<if test="shareApiId != null">
#{shareApiId,jdbcType=LONGVARCHAR},
<if test="customData != null">
#{customData,jdbcType=LONGVARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.ApiDocumentShareExample" resultType="java.lang.Long">
select count(*) from api_document_share
<select id="countByExample" parameterType="io.metersphere.base.domain.ShareInfoExample" resultType="java.lang.Long">
select count(*) from share_info
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update api_document_share
update share_info
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR},
@ -198,8 +198,8 @@
<if test="record.shareType != null">
share_type = #{record.shareType,jdbcType=VARCHAR},
</if>
<if test="record.shareApiId != null">
share_api_id = #{record.shareApiId,jdbcType=LONGVARCHAR},
<if test="record.customData != null">
custom_data = #{record.customData,jdbcType=LONGVARCHAR},
</if>
</set>
<if test="_parameter != null">
@ -207,19 +207,19 @@
</if>
</update>
<update id="updateByExampleWithBLOBs" parameterType="map">
update api_document_share
update share_info
set id = #{record.id,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
create_user_id = #{record.createUserId,jdbcType=VARCHAR},
update_time = #{record.updateTime,jdbcType=BIGINT},
share_type = #{record.shareType,jdbcType=VARCHAR},
share_api_id = #{record.shareApiId,jdbcType=LONGVARCHAR}
custom_data = #{record.customData,jdbcType=LONGVARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update api_document_share
update share_info
set id = #{record.id,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
create_user_id = #{record.createUserId,jdbcType=VARCHAR},
@ -229,8 +229,8 @@
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.ApiDocumentShare">
update api_document_share
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.ShareInfo">
update share_info
<set>
<if test="createTime != null">
create_time = #{createTime,jdbcType=BIGINT},
@ -244,23 +244,23 @@
<if test="shareType != null">
share_type = #{shareType,jdbcType=VARCHAR},
</if>
<if test="shareApiId != null">
share_api_id = #{shareApiId,jdbcType=LONGVARCHAR},
<if test="customData != null">
custom_data = #{customData,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.ApiDocumentShare">
update api_document_share
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.ShareInfo">
update share_info
set create_time = #{createTime,jdbcType=BIGINT},
create_user_id = #{createUserId,jdbcType=VARCHAR},
update_time = #{updateTime,jdbcType=BIGINT},
share_type = #{shareType,jdbcType=VARCHAR},
share_api_id = #{shareApiId,jdbcType=LONGVARCHAR}
custom_data = #{customData,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.ApiDocumentShare">
update api_document_share
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.ShareInfo">
update share_info
set create_time = #{createTime,jdbcType=BIGINT},
create_user_id = #{createUserId,jdbcType=VARCHAR},
update_time = #{updateTime,jdbcType=BIGINT},

View File

@ -1,12 +0,0 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.document.ApiDocumentInfoDTO;
import io.metersphere.api.dto.document.ApiDocumentRequest;
import io.metersphere.api.dto.document.ApiDocumentSimpleInfoDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtApiDocumentMapper {
List<ApiDocumentInfoDTO> findApiDocumentSimpleInfoByRequest(@Param("request") ApiDocumentRequest request);
}

View File

@ -1,10 +0,0 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.ApiDocumentShare;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtApiDocumentShareMapper {
List<ApiDocumentShare> selectByShareTypeAndShareApiIdWithBLOBs(@Param("shareType") String shareType, @Param("shareApiId") String shareApiIdString);
}

View File

@ -1,21 +0,0 @@
<?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.ExtApiDocumentShareMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.ApiDocumentShare">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="create_user_id" jdbcType="VARCHAR" property="createUserId" />
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
<result column="share_type" jdbcType="VARCHAR" property="shareType" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.ApiDocumentShare">
<result column="share_api_id" jdbcType="LONGVARCHAR" property="shareApiId" />
</resultMap>
<select id="selectByShareTypeAndShareApiIdWithBLOBs" resultMap="ResultMapWithBLOBs">
SELECT id,share_type,share_api_id FROM api_document_share
<where>
share_type = #{shareType} AND share_api_id = #{shareApiId}
</where>
</select>
</mapper>

View File

@ -0,0 +1,13 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.share.ApiDocumentInfoDTO;
import io.metersphere.api.dto.share.ApiDocumentRequest;
import io.metersphere.base.domain.ShareInfo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtShareInfoMapper {
List<ApiDocumentInfoDTO> findApiDocumentSimpleInfoByRequest(@Param("request") ApiDocumentRequest request);
List<ShareInfo> selectByShareTypeAndShareApiIdWithBLOBs(@Param("shareType") String shareType, @Param("customData") String customData);
}

View File

@ -1,7 +1,7 @@
<?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.ExtApiDocumentMapper">
<select id="findApiDocumentSimpleInfoByRequest" resultType="io.metersphere.api.dto.document.ApiDocumentInfoDTO">
<mapper namespace="io.metersphere.base.mapper.ext.ExtShareInfoMapper">
<select id="findApiDocumentSimpleInfoByRequest" resultType="io.metersphere.api.dto.share.ApiDocumentInfoDTO">
SELECT api.id,api.name FROM api_definition api WHERE api.protocol = 'HTTP'
<if test="request.trashEnable == true">
AND api.status = 'Trash'
@ -40,4 +40,16 @@
ORDER BY api.update_time DESC
</if>
</select>
</mapper>
<select id="selectByShareTypeAndShareApiIdWithBLOBs" resultType="io.metersphere.base.domain.ShareInfo">
SELECT id,share_type,custom_data FROM share_info
<where>
share_type = #{shareType}
<if test="customData != null and customData != ''">
AND custom_data = #{customData}
</if>
</where>
</select>
</mapper>

View File

@ -48,9 +48,15 @@ public class ShiroUtils {
filterChainDefinitionMap.put("/403", "anon");
filterChainDefinitionMap.put("/anonymous/**", "anon");
//api-对外文档页面提供的查询接口
filterChainDefinitionMap.put("/api/document/**", "anon");
//分享相关接口
filterChainDefinitionMap.put("/share/info/generateShareInfo", "anon");
filterChainDefinitionMap.put("/share/info/selectApiInfoByParam", "anon");
filterChainDefinitionMap.put("/share/get/**", "anon");
filterChainDefinitionMap.put("/share/info", "apikey, csrf, authc"); // 需要认证
filterChainDefinitionMap.put("/document/**", "anon");
filterChainDefinitionMap.put("/share/**", "anon");
filterChainDefinitionMap.put("/sharePlanReport", "anon");
filterChainDefinitionMap.put("/system/theme", "anon");
filterChainDefinitionMap.put("/system/save/baseurl/**", "anon");
filterChainDefinitionMap.put("/system/timeout", "anon");
@ -67,7 +73,6 @@ public class ShiroUtils {
public static void ignoreCsrfFilter(Map<String, String> filterChainDefinitionMap) {
filterChainDefinitionMap.put("/", "apikey, authc"); // 跳转到 / 不用校验 csrf
filterChainDefinitionMap.put("/language", "apikey, authc");// 跳转到 /language 不用校验 csrf
filterChainDefinitionMap.put("/document", "apikey, authc"); // 跳转到 /document 不用校验 csrf
filterChainDefinitionMap.put("/test/case/file/preview/**", "apikey, authc"); // 预览测试用例附件 不用校验 csrf
filterChainDefinitionMap.put("/mock", "apikey, authc"); // 跳转到 /mock接口 不用校验 csrf
filterChainDefinitionMap.put("/resource/md/get/**", "apikey, authc");

View File

@ -31,4 +31,9 @@ public class IndexController {
public String document() {
return "document.html";
}
@GetMapping(value = "/sharePlanReport")
public String shareRedirect() {
return "share-plan-report.html";
}
}

View File

@ -0,0 +1,101 @@
package io.metersphere.controller;
import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.automation.APIScenarioReportResult;
import io.metersphere.api.dto.automation.TestPlanFailureApiDTO;
import io.metersphere.api.dto.automation.TestPlanFailureScenarioDTO;
import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.api.service.ApiScenarioReportService;
import io.metersphere.base.domain.IssuesDao;
import io.metersphere.track.dto.TestPlanCaseDTO;
import io.metersphere.track.dto.TestPlanLoadCaseDTO;
import io.metersphere.track.dto.TestPlanSimpleReportDTO;
import io.metersphere.track.service.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.util.List;
@Controller
@RequestMapping("share")
public class ShareController {
@Resource
TestPlanService testPlanService;
@Resource
TestPlanTestCaseService testPlanTestCaseService;
@Resource
ApiDefinitionService apiDefinitionService;
@Resource
TestPlanApiCaseService testPlanApiCaseService;
@Resource
TestPlanScenarioCaseService testPlanScenarioCaseService;
@Resource
ApiScenarioReportService apiScenarioReportService;
@Resource
TestPlanLoadCaseService testPlanLoadCaseService;
@Resource
IssuesService issuesService;
// Todo 鉴权
@ResponseBody
@GetMapping("/issues/plan/get/{planId}")
public List<IssuesDao> getIssuesByPlanoId(@PathVariable String planId) {
return issuesService.getIssuesByPlanoId(planId);
}
@ResponseBody
@GetMapping("/test/plan/report/{planId}")
public TestPlanSimpleReportDTO getReport(@PathVariable String planId) {
return testPlanService.getReport(planId);
}
@ResponseBody
@GetMapping("/report/export/{planId}")
public void exportHtmlReport(@PathVariable String planId, HttpServletResponse response) throws UnsupportedEncodingException {
testPlanService.exportPlanReport(planId, response);
}
@ResponseBody
@GetMapping("/test/plan/case/list/failure/{planId}")
public List<TestPlanCaseDTO> getFailureCases(@PathVariable String planId) {
return testPlanTestCaseService.getFailureCases(planId);
}
@ResponseBody
@GetMapping("/test/plan/load/case/list/failure/{planId}")
public List<TestPlanLoadCaseDTO> getLoadFailureCases(@PathVariable String planId) {
return testPlanLoadCaseService.getFailureCases(planId);
}
@ResponseBody
@GetMapping("/test/plan/api/case/list/failure/{planId}")
public List<TestPlanFailureApiDTO> getApiFailureList(@PathVariable String planId) {
return testPlanApiCaseService.getFailureList(planId);
}
@ResponseBody
@GetMapping("/test/plan/scenario/case/list/failure/{planId}")
public List<TestPlanFailureScenarioDTO> getScenarioFailureList(@PathVariable String planId) {
return testPlanScenarioCaseService.getFailureList(planId);
}
@ResponseBody
@GetMapping("/api/definition/report/getReport/{testId}")
public APIReportResult getApiReport(@PathVariable String testId) {
return apiDefinitionService.getDbResult(testId);
}
@ResponseBody
@GetMapping("/api/scenario/report/get/{reportId}")
public APIScenarioReportResult get(@PathVariable String reportId) {
return apiScenarioReportService.get(reportId);
}
}

View File

@ -1352,8 +1352,8 @@ public class TestPlanService {
}
}
} catch (Exception e) {
MSException.throwException(e.getMessage());
LogUtil.error(e.getMessage(), e);
MSException.throwException(e.getMessage());
}
}

View File

@ -88,3 +88,9 @@ ALTER TABLE `api_scenario`
ADD `execute_times` int(11) NULL;
ALTER TABLE `api_scenario_report` ADD `end_time` bigint(13) ;
-- 修改文档分享表
ALTER TABLE api_document_share RENAME TO share_info;
ALTER TABLE share_info change
column share_api_id custom_data longtextCHARACTER
SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'Share Custom Data';

View File

@ -41,6 +41,7 @@ import MsApiReportExport from "./ApiReportExport";
import MsApiReportViewHeader from "./ApiReportViewHeader";
import {RequestFactory} from "../../definition/model/ApiTestModel";
import {windowPrint, getUUID, getCurrentProjectID} from "@/common/js/utils";
import {getScenarioReport, getShareScenarioReport} from "@/network/api";
export default {
name: "MsApiReport",
@ -78,6 +79,7 @@ export default {
debug: Boolean,
isTemplate: Boolean,
templateReport: Object,
isShare: Boolean
},
watch: {
reportId() {
@ -238,14 +240,16 @@ export default {
//
this.report = this.templateReport;
this.buildReport();
} else if (this.isShare) {
getShareScenarioReport(this.reportId, (data) => {
this.report = data || {};
this.buildReport();
});
} else {
if (this.reportId) {
let url = "/api/scenario/report/get/" + this.reportId;
this.$get(url, response => {
this.report = response.data || {};
this.buildReport();
});
}
getScenarioReport(this.reportId, (data) => {
this.report = data || {};
this.buildReport();
});
}
},
buildReport() {

View File

@ -325,7 +325,7 @@ import ApiStatus from "@/business/components/api/definition/components/list/ApiS
import {calculate} from "@/business/components/api/definition/model/ApiTestModel";
import MsJsonCodeEdit from "@/business/components/common/json-schema/JsonSchemaEditor";
import Api from "@/business/components/api/router";
import {uuid} from "@/common/js/utils";
import {generateApiDocumentShareInfo} from "@/network/share";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const apiDocumentBatchShare = (requireComponent!=null&&requireComponent.keys().length) > 0 ? requireComponent("./share/ApiDocumentBatchShare.vue") : {};
@ -486,7 +486,7 @@ export default {
}
simpleRequest.trashEnable = this.trashEnable;
let simpleInfoUrl = "/api/document/selectApiSimpleInfo";
let simpleInfoUrl = "/share/info/selectApiSimpleInfo";
this.$post(simpleInfoUrl, simpleRequest, response => {
@ -503,7 +503,6 @@ export default {
});
},
shareApiDocument(isBatchShare){
let thisHost = window.location.host;
this.shareUrl = "";
this.batchShareUrl = "";
let shareIdArr = [];
@ -523,17 +522,17 @@ export default {
genShareInfoParam.shareApiIdList = shareIdArr;
genShareInfoParam.shareType = shareType;
this.$post("/api/document/generateApiDocumentShareInfo", genShareInfoParam, res => {
generateApiDocumentShareInfo(genShareInfoParam, (data) => {
let thisHost = window.location.host;
if(shareType == "Batch"){
this.batchShareUrl = thisHost+"/document"+res.data.shareUrl;
this.batchShareUrl = thisHost + "/document" + data.shareUrl;
}else{
this.shareUrl = thisHost+"/document"+res.data.shareUrl;
this.shareUrl = thisHost + "/document" + data.shareUrl;
}
}, (error) => {
});
},
selectApiInfo(index,apiId,needUpdateShowArray) {
let simpleInfoUrl = "/api/document/selectApiInfoById/" + apiId;
let simpleInfoUrl = "/share/info/selectApiInfoById/" + apiId;
this.$get(simpleInfoUrl, response => {
let returnData = response.data;
this.$set(this.apiInfoArray,index,returnData);
@ -559,7 +558,7 @@ export default {
}else {
let params = {};
params.apiIdList = apiIdArr;
this.$post("/api/document/selectApiInfoByParam", params, response => {
this.$post("/share/info/selectApiInfoByParam", params, response => {
let returnDatas = response.data;
for(let dataIndex = 0; dataIndex < returnDatas.length;dataIndex ++){
let index = indexArr[dataIndex];

View File

@ -413,7 +413,7 @@ export default {
simpleRequest.moduleIds = this.moduleIds;
}
let simpleInfoUrl = "/api/document/selectApiSimpleInfo";
let simpleInfoUrl = "/share/info/selectApiSimpleInfo";
this.apiInfoArray = [];
this.$post(simpleInfoUrl, simpleRequest, response => {
this.apiInfoArray = response.data;
@ -444,7 +444,7 @@ export default {
genShareInfoParam.shareApiIdList = shareIdArr;
genShareInfoParam.shareType = shareType;
this.$post("/api/document/generateApiDocumentShareInfo", genShareInfoParam, res => {
this.$post("/share/info/generateApiDocumentShareInfo", genShareInfoParam, res => {
if(shareType == "Batch"){
this.batchShareUrl = thisHost+"/document"+res.data.shareUrl;
}else{
@ -454,7 +454,7 @@ export default {
});
},
selectApiInfo(index,apiId) {
let simpleInfoUrl = "/api/document/selectApiInfoById/" + apiId;
let simpleInfoUrl = "/share/info/selectApiInfoById/" + apiId;
this.$get(simpleInfoUrl, response => {
let returnData = response.data;
this.$set(this.apiInfoArray,index,returnData);

View File

@ -25,11 +25,8 @@
:plan-id="planId"/>
<test-plan-load v-if="activeIndex === 'load'" :redirectCharType="redirectCharType" :clickType="clickType"
:plan-id="planId"/>
<!-- <test-case-statistics-report-view :test-plan="currentPlan" v-if="activeIndex === 'report'"/>-->
<test-plan-detail-report :test-plan="currentPlan" v-if="activeIndex === 'report'"/>
<test-report-template-list @openReport="openReport" ref="testReportTemplateList"/>
</div>
</template>
@ -46,8 +43,6 @@ import MsMainContainer from "../../../common/components/MsMainContainer";
import MsTestPlanHeaderBar from "./comonents/head/TestPlanHeaderBar";
import TestPlanFunctional from "./comonents/functional/TestPlanFunctional";
import TestPlanApi from "./comonents/api/TestPlanApi";
import TestCaseStatisticsReportView from "./comonents/report/statistics/TestCaseStatisticsReportView";
import TestReportTemplateList from "./comonents/TestReportTemplateList";
import TestPlanLoad from "@/business/components/track/plan/view/comonents/load/TestPlanLoad";
import {getCurrentProjectID} from "@/common/js/utils";
import TestPlanDetailReport from "./comonents/report/TestPlanDetailReport";
@ -56,8 +51,6 @@ export default {
name: "TestPlanView",
components: {
TestPlanDetailReport,
TestReportTemplateList,
TestCaseStatisticsReportView,
TestPlanApi,
TestPlanFunctional,
MsTestPlanHeaderBar,
@ -133,15 +126,6 @@ export default {
},
handleSelect(key) {
this.activeIndex = key;
if (key === 'report' && !this.currentPlan.reportId) {
this.$refs.testReportTemplateList.open(this.planId);
}
},
openTemplateReport() {
this.$refs.testReportTemplateList.open(this.planId);
},
openReport(planId, id) {
this.currentPlan.reportId = id;
},
reloadMenu() {
this.isMenuShow = false;

View File

@ -2,7 +2,7 @@
<div>
<div class="container">
<el-main>
<test-plan-report-content :is-template="isTemplate" :plan-id="planId" ref="reportContent"/>
<test-plan-report-content :is-template="isTemplate" :is-share="isShare" :plan-id="planId" ref="reportContent"/>
</el-main>
</div>
</div>
@ -21,7 +21,7 @@ export default {
return this.testPlan.id;
},
},
props: ['testPlan', 'isTemplate'],
props: ['testPlan', 'isTemplate', 'isShare'],
}
</script>

View File

@ -5,7 +5,7 @@
<api-result :api-result="report.apiResult"/>
</el-tab-pane>
<el-tab-pane label="失败用例" name="second">
<api-failure-result :report="report" :is-template="isTemplate" :plan-id="planId"/>
<api-failure-result :is-share="isShare" :report="report" :is-template="isTemplate" :plan-id="planId"/>
</el-tab-pane>
<!-- <el-tab-pane label="所有用例" name="fourth">所有用例</el-tab-pane>-->
@ -28,7 +28,7 @@ export default {
};
},
props: [
'report', 'planId', 'isTemplate'
'report', 'planId', 'isTemplate', 'isShare'
],
methods: {
handleClick(tab, event) {

View File

@ -5,10 +5,10 @@
<functional-result :function-result="report.functionResult"/>
</el-tab-pane>
<el-tab-pane label="失败用例" name="second">
<functional-failure-result :is-template="isTemplate" :report="report" :plan-id="planId"/>
<functional-failure-result :is-share="isShare" :is-template="isTemplate" :report="report" :plan-id="planId"/>
</el-tab-pane>
<el-tab-pane label="缺陷列表" name="third">
<functional-issue-list :is-template="isTemplate" :report="report" :plan-id="planId"/>
<functional-issue-list :is-share="isShare" :is-template="isTemplate" :report="report" :plan-id="planId"/>
</el-tab-pane>
<!-- <el-tab-pane label="所有用例" name="fourth">所有用例</el-tab-pane>-->
</el-tabs>
@ -33,7 +33,7 @@ export default {
};
},
props: [
'report','planId', 'isTemplate'
'report','planId', 'isTemplate', 'isShare'
],
methods: {
handleClick(tab, event) {

View File

@ -5,7 +5,7 @@
<load-result :load-result="report.loadResult"/>
</el-tab-pane>
<el-tab-pane label="失败用例" name="second">
<load-failure-result :is-template="isTemplate" :report="report" :plan-id="planId"/>
<load-failure-result :is-share="isShare" :is-template="isTemplate" :report="report" :plan-id="planId"/>
</el-tab-pane>
<!-- <el-tab-pane label="所有用例" name="fourth">所有用例</el-tab-pane>-->
</el-tabs>
@ -34,7 +34,8 @@ export default {
props: [
'report',
'planId',
'isTemplate'
'isTemplate',
'isShare'
],
methods: {
handleClick(tab, event) {

View File

@ -1,19 +1,29 @@
<template>
<el-card v-loading="result.loading">
<el-card v-loading="result ? result.loading : false">
<!-- <el-row v-if="!isTemplate" type="flex" class="head-bar">-->
<div v-if="!isTemplate" class="head-bar head-right">
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleExportHtml()">
{{'导出HTML'}}
</el-button>
<div v-if="!isTemplate && !isShare" class="head-bar head-right">
<el-row>
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleExportHtml()">
{{'导出HTML'}}
</el-button>
</el-row>
<el-row>
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleEditTemplate()">
{{'编辑模板'}}
</el-button>
</el-row>
<el-row>
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleShare()">
{{'分享连接'}}
</el-button>
</el-row>
</div>
<!-- </el-row>-->
<test-plan-report-header :is-template="isTemplate" :report="report" :plan-id="planId"/>
<test-plan-functional-report :is-template="isTemplate" v-if="functionalEnable" :plan-id="planId" :report="report"/>
<test-plan-api-report :is-template="isTemplate" v-if="apiEnable" :report="report" :plan-id="planId"/>
<test-plan-load-report :is-template="isTemplate" v-if="loadEnable" :report="report" :plan-id="planId"/>
<test-plan-report-header :is-template="isTemplate" :is-share="isShare" :report="report" :plan-id="planId"/>
<test-plan-functional-report :is-share="isShare" :is-template="isTemplate" v-if="functionalEnable" :plan-id="planId" :report="report"/>
<test-plan-api-report :is-share="isShare" :is-template="isTemplate" v-if="apiEnable" :report="report" :plan-id="planId"/>
<test-plan-load-report :is-share="isShare" :is-template="isTemplate" v-if="loadEnable" :report="report" :plan-id="planId"/>
</el-card>
</template>
@ -21,9 +31,10 @@
import TestPlanReportHeader from "@/business/components/track/plan/view/comonents/report/detail/TestPlanReportHeader";
import TestPlanFunctionalReport
from "@/business/components/track/plan/view/comonents/report/detail/TestPlanFunctionalReport";
import {getTestPlanReport} from "@/network/test-plan";
import {getShareTestPlanReport, getTestPlanReport} from "@/network/test-plan";
import TestPlanApiReport from "@/business/components/track/plan/view/comonents/report/detail/TestPlanApiReport";
import TestPlanLoadReport from "@/business/components/track/plan/view/comonents/report/detail/TestPlanLoadReport";
import {generateApiDocumentShareInfo, generateShareInfo} from "@/network/share";
export default {
name: "TestPlanReportContent",
components: {
@ -33,7 +44,8 @@ export default {
TestPlanReportHeader},
props: {
planId:String,
isTemplate: Boolean
isTemplate: Boolean,
isShare: Boolean,
},
data() {
return {
@ -61,11 +73,18 @@ export default {
getReport() {
if (this.isTemplate) {
this.report = "#report";
} else if (this.isShare) {
this.result = getShareTestPlanReport(this.planId, (data) => {
this.report = data;
});
} else {
this.result = getTestPlanReport(this.planId, (data) => {
this.report = data;
});
}
},
handleEditTemplate() {
},
handleExportHtml() {
let config = {
@ -73,8 +92,22 @@ export default {
method: 'get',
responseType: 'blob'
};
if (this.isShare) {
config.url = '/share' + config.url;
}
this.result = this.$download(config, this.report.name + '.html');
},
handleShare() {
let param = {
customData: this.planId,
shareType: 'PLAN_REPORT'
};
generateShareInfo(param, (data) => {
let thisHost = window.location.host;
let shareUrl = thisHost + "/sharePlanReport" + data.shareUrl;
console.log(shareUrl);
});
}
}
}
</script>
@ -95,12 +128,14 @@ export default {
.head-right {
text-align: right;
float: right;
}
.head-bar .el-button {
margin-bottom: 10px;
width: 80px;
margin-right: 10px;
display: block;
}
.el-button+.el-button {

View File

@ -22,7 +22,7 @@
</el-col>
</el-row>
<el-form-item :label="'报告总结'">
<el-link v-if="!isTemplate" @click="isEdit = true">
<el-link v-if="!isTemplate && !isShare" @click="isEdit = true">
编辑
</el-link>
</el-form-item>
@ -53,7 +53,8 @@ export default {
props: {
planId: String,
report: Object,
isTemplate: Boolean
isTemplate: Boolean,
isShare: Boolean
},
data() {
return {

View File

@ -58,10 +58,10 @@ import PriorityTableItem from "../../../../../../common/tableItems/planview/Prio
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../../common/tableItems/planview/StatusTableItem";
import {getPlanApiFailureCase} from "@/network/test-plan";
import {getPlanApiFailureCase, getSharePlanApiFailureCase} from "@/network/test-plan";
import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
import {getApiReport} from "@/network/api";
import {getApiReport, getShareApiReport} from "@/network/api";
import MsRequestResultTail from "@/business/components/api/definition/components/response/RequestResultTail";
export default {
name: "ApiCaseFailureResult",
@ -71,7 +71,8 @@ export default {
props: {
planId: String,
isTemplate: Boolean,
report: Object
report: Object,
isShare: Boolean
},
data() {
return {
@ -90,6 +91,13 @@ export default {
if (this.apiCases && this.apiCases.length > 0) {
this.rowClick(this.apiCases[0]);
}
} else if (this.isShare) {
this.result = getSharePlanApiFailureCase(this.planId, (data) => {
this.apiCases = data;
if (data && data.length > 0) {
this.rowClick(data[0]);
}
});
} else {
this.result = getPlanApiFailureCase(this.planId, (data) => {
this.apiCases = data;
@ -102,6 +110,10 @@ export default {
rowClick(row) {
if (this.isTemplate) {
this.response = JSON.parse(row.response);
} else if (this.isShare) {
getShareApiReport(row.id, (data) => {
this.response = JSON.parse(data.content);
});
} else {
getApiReport(row.id, (data) => {
this.response = JSON.parse(data.content);

View File

@ -2,10 +2,10 @@
<div>
<el-tabs type="card">
<el-tab-pane label="接口用例">
<api-case-failure-result :report="report" :is-template="isTemplate" :plan-id="planId"/>
<api-case-failure-result :is-share="isShare" :report="report" :is-template="isTemplate" :plan-id="planId"/>
</el-tab-pane>
<el-tab-pane label="场景用例">
<api-scenario-failure-result :report="report" :is-template="isTemplate" :plan-id="planId"/>
<api-scenario-failure-result :is-share="isShare" :report="report" :is-template="isTemplate" :plan-id="planId"/>
</el-tab-pane>
</el-tabs>
</div>
@ -28,6 +28,7 @@ export default {
props: {
planId: String,
isTemplate: Boolean,
isShare: Boolean,
report: {}
},
data() {

View File

@ -45,7 +45,7 @@
</ms-table>
</el-col>
<el-col :span="16" v-if="scenarioCases.length > 0">
<ms-api-report :template-report="response" :is-template="isTemplate" :infoDb="true" :report-id="reportId"/>
<ms-api-report :is-share="isShare" :template-report="response" :is-template="isTemplate" :infoDb="true" :report-id="reportId"/>
</el-col>
</el-row>
</div>
@ -56,7 +56,7 @@ import PriorityTableItem from "../../../../../../common/tableItems/planview/Prio
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../../common/tableItems/planview/StatusTableItem";
import {getPlanScenarioFailureCase} from "@/network/test-plan";
import {getPlanScenarioFailureCase, getSharePlanScenarioFailureCase} from "@/network/test-plan";
import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
import MsApiReport from "@/business/components/api/automation/report/ApiReportDetail";
@ -68,7 +68,8 @@ export default {
props: {
planId: String,
isTemplate: Boolean,
report: Object
report: Object,
isShare: Boolean
},
data() {
return {
@ -88,6 +89,13 @@ export default {
if (this.scenarioCases && this.scenarioCases.length > 0) {
this.rowClick(this.scenarioCases[0]);
}
} else if (this.isShare) {
this.result = getSharePlanScenarioFailureCase(this.planId, (data) => {
this.scenarioCases = data;
if (data && data.length > 0) {
this.reportId = data[0].reportId;
}
});
} else {
this.result = getPlanScenarioFailureCase(this.planId, (data) => {
this.scenarioCases = data;

View File

@ -69,13 +69,14 @@ import PriorityTableItem from "../../../../../../common/tableItems/planview/Prio
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../../common/tableItems/planview/StatusTableItem";
import {getPlanFunctionFailureCase} from "@/network/test-plan";
import {getPlanFunctionFailureCase, getSharePlanFunctionFailureCase} from "@/network/test-plan";
export default {
name: "FunctionalFailureResult",
components: {StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem},
props: {
planId: String,
isTemplate: Boolean,
isShare: Boolean,
report: {}
},
data() {
@ -90,6 +91,10 @@ export default {
getFailureTestCase() {
if (this.isTemplate) {
this.failureTestCases = this.report.failureTestCases;
} else if (this.isShare) {
getSharePlanFunctionFailureCase(this.planId, (data) => {
this.failureTestCases = data;
});
} else {
getPlanFunctionFailureCase(this.planId, (data) => {
this.failureTestCases = data;

View File

@ -56,8 +56,7 @@ import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
import IssueDescriptionTableItem from "@/business/components/track/issue/IssueDescriptionTableItem";
import {ISSUE_STATUS_MAP} from "@/common/js/table-constants";
import {getIssuesByPlanId} from "@/network/Issue";
import {getIssueTemplate} from "@/network/custom-field-template";
import {getIssuesByPlanId, getShareIssuesByPlanId} from "@/network/Issue";
export default {
name: "FunctionalIssueList",
components: {IssueDescriptionTableItem, MsTableColumn, MsTable},
@ -65,34 +64,27 @@ export default {
return {
data: [],
result: {},
isThirdPart: false,
isThirdPart: false
}
},
props: ['planId', 'isTemplate', 'report'],
props: ['planId', 'isTemplate', 'report', 'isShare'],
computed: {
issueStatusMap() {
return ISSUE_STATUS_MAP;
},
},
mounted() {
if (this.isTemplate) {
this.isThirdPart = this.report.isThirdPartIssue;
} else {
getIssueTemplate()
.then((template) => {
if (template.platform === 'metersphere') {
this.isThirdPart = false;
} else {
this.isThirdPart = true;
}
});
}
this.isThirdPart = this.report.isThirdPartIssue;
this.getIssues();
},
methods: {
getIssues() {
if (this.isTemplate) {
this.data = this.report.issueList;
} else if (this.isShare) {
this.result = getShareIssuesByPlanId(this.planId, (data) => {
this.data = data;
});
} else {
this.result = getIssuesByPlanId(this.planId, (data) => {
this.data = data;

View File

@ -13,11 +13,10 @@
<script>
import MsPieChart from "@/business/components/common/components/MsPieChart";
import MsDoughnutPieChart from "@/business/components/common/components/MsDoughnutPieChart";
export default {
name: "FunctionalResult",
components: {MsDoughnutPieChart, MsPieChart},
components: {MsDoughnutPieChart},
data() {
return {
caseDataMap: new Map([

View File

@ -40,14 +40,15 @@
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../../common/tableItems/planview/StatusTableItem";
import {getPlanLoadFailureCase} from "@/network/test-plan";
import {getPlanLoadFailureCase, getSharePlanLoadFailureCase} from "@/network/test-plan";
export default {
name: "LoadFailureResult",
components: {StatusTableItem, MethodTableItem, TypeTableItem},
props: {
planId: String,
report: Object,
isTemplate: Boolean
isTemplate: Boolean,
isShare: Boolean
},
data() {
return {
@ -61,6 +62,10 @@ export default {
getFailureTestCase() {
if (this.isTemplate) {
this.failureTestCases = this.report.loadFailureTestCases;
} else if (this.isShare) {
getSharePlanLoadFailureCase(this.planId, (data) => {
this.failureTestCases = data;
});
} else {
getPlanLoadFailureCase(this.planId, (data) => {
this.failureTestCases = data;

View File

@ -525,3 +525,18 @@ export function stopFullScreenLoading(loading, timeout) {
loading.close();
}, timeout);
}
export function getShareId() {
let herfUrl = window.location.href;
if(herfUrl.indexOf("?") > 0){
let paramArr = herfUrl.split("?");
if(paramArr.length > 1){
let shareId = paramArr[1];
if(shareId.indexOf("#") > 0){
shareId = shareId.split("#")[0];
}
return shareId;
}
}
return "";
}

View File

@ -8,6 +8,7 @@
<script>
import ApiDocumentAnchor from "@/business/components/api/definition/components/document/ApiDocumentAnchor";
import {getShareId} from "@/common/js/utils";
export default {
name: "ApiDocumentsPage",
@ -39,21 +40,8 @@ export default {
},
methods: {
getUrlParam(){
let herfUrl = window.location.href;
if(herfUrl.indexOf("?") > 0){
let paramArr = herfUrl.split("?");
if(paramArr.length > 1){
let documentId = paramArr[1];
if(documentId.indexOf("#") > 0){
documentId = documentId.split("#")[0];
}
this.documentId = documentId;
}
}
},
selectDocumentInfo(){
this.getUrlParam();
this.documentId = getShareId();
if(this.$refs.apiDocumentAnchor){
this.$refs.apiDocumentAnchor.initApiDocSimpleList();
}

View File

@ -1,6 +1,7 @@
import {post, get} from "@/common/js/ajax";
import {getPageDate} from "@/common/js/tableUtils";
import {getCurrentProjectID} from "@/common/js/utils";
import {baseGet} from "@/network/base-network";
export function buildIssues(page) {
let data = page.data;
@ -33,14 +34,11 @@ export function getIssuesByCaseId(caseId, page) {
}
export function getIssuesByPlanId(planId, callback) {
if (planId) {
return get('/issues/plan/get/' + planId, (response) => {
if (callback) {
callback(response.data);
}
});
}
return {};
return planId ? baseGet('/issues/plan/get/' + planId, callback) : {};
}
export function getShareIssuesByPlanId(planId, callback) {
return planId ? baseGet('/share/issues/plan/get/' + planId, callback) : {};
}
export function buildPlatformIssue(data) {

View File

@ -16,4 +16,12 @@ export function getApiReport(testId, callback) {
return testId ? baseGet('/api/definition/report/getReport/' + testId, callback) : {};
}
export function getShareApiReport(testId, callback) {
return testId ? baseGet('/share/api/definition/report/getReport/' + testId, callback) : {};
}
export function getShareScenarioReport(reportId, callback) {
return reportId ? baseGet('/share/api/scenario/report/get/' + reportId, callback) : {};
}

View File

@ -1,4 +1,4 @@
import {get} from "@/common/js/ajax";
import {get, post} from "@/common/js/ajax";
export function baseGet(url, callback) {
return get(url, (response) => {
@ -7,3 +7,11 @@ export function baseGet(url, callback) {
}
});
}
export function basePost(url, param, callback) {
return post(url, param, (response) => {
if (callback) {
callback(response.data);
}
});
}

View File

@ -0,0 +1,24 @@
import {post} from "@/common/js/ajax";
import {baseGet} from "@/network/base-network";
export function generateApiDocumentShareInfo(param, callback) {
return post("/share/info/generateApiDocumentShareInfo", param, response => {
if (callback) {
callback(response.data);
}
});
}
export function generateShareInfo(param, callback) {
return post("/share/info/generateShareInfo", param, response => {
if (callback) {
callback(response.data);
}
});
}
export function getShareInfo(id, callback) {
return id ? baseGet('/share/info/get/' + id, callback) : {};
}

View File

@ -13,6 +13,16 @@ export function getTestPlanReport(planId, callback) {
}
}
export function getShareTestPlanReport(planId, callback) {
if (planId) {
return get('/share/test/plan/report/' + planId, (response) => {
if (callback) {
callback(response.data);
}
});
}
}
export function editPlanReport(param) {
return post('/test/plan/edit/report', param, () => {
success(i18n.t('commons.save_success'));
@ -23,14 +33,30 @@ export function getPlanFunctionFailureCase(planId, callback) {
return planId ? baseGet('/test/plan/case/list/failure/' + planId, callback) : {};
}
export function getSharePlanFunctionFailureCase(planId, callback) {
return planId ? baseGet('/share/test/plan/case/list/failure/' + planId, callback) : {};
}
export function getPlanScenarioFailureCase(planId, callback) {
return planId ? baseGet('/test/plan/scenario/case/list/failure/' + planId, callback) : {};
}
export function getSharePlanScenarioFailureCase(planId, callback) {
return planId ? baseGet('/share/test/plan/scenario/case/list/failure/' + planId, callback) : {};
}
export function getPlanApiFailureCase(planId, callback) {
return planId ? baseGet('/test/plan/api/case/list/failure/' + planId, callback) : {};
}
export function getSharePlanApiFailureCase(planId, callback) {
return planId ? baseGet('/share/test/plan/api/case/list/failure/' + planId, callback) : {};
}
export function getPlanLoadFailureCase(planId, callback) {
return planId ? baseGet('/test/plan/load/case/list/failure/' + planId, callback) : {};
}
export function getSharePlanLoadFailureCase(planId, callback) {
return planId ? baseGet('/share/test/plan/load/case/list/failure/' + planId, callback) : {};
}

View File

@ -1,36 +1,4 @@
import Vue from 'vue';
import ElementUI, {Button, Col, Form, FormItem, Input, Row, Main, Card, Table, TableColumn} from 'element-ui';
import '@/assets/theme/index.css';
import '@/common/css/menu-header.css';
import '@/common/css/main.css';
import i18n from "@/i18n/i18n";
import chart from "@/common/js/chart";
// import CKEditor from '@ckeditor/ckeditor5-vue';
import PlanReportTemplate from "@/template/report/plan/PlanReportTemplate";
// import PlanReport from "@/template/report/plan/PlanReport";
import planReportUse from "@/template/report/plan/planReportUse";
Vue.use(ElementUI, {
i18n: (key, value) => i18n.t(key, value)
});
Vue.use(Row);
Vue.use(Col);
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Input);
Vue.use(Button);
Vue.use(chart);
Vue.use(Main);
Vue.use(Card);
Vue.use(TableColumn);
Vue.use(Table);
// Vue.use(CKEditor);
new Vue({
el: '#planReport',
i18n,
// render: h => h(PlanReport)
render: h => h(PlanReportTemplate)
});
planReportUse('#planReport', PlanReportTemplate);

View File

@ -0,0 +1,35 @@
import Vue from 'vue';
import ElementUI, {Button, Col, Form, FormItem, Input, Row, Main, Card, Table, TableColumn} from 'element-ui';
import '@/assets/theme/index.css';
import '@/common/css/menu-header.css';
import '@/common/css/main.css';
import i18n from "@/i18n/i18n";
import chart from "@/common/js/chart";
// import CKEditor from '@ckeditor/ckeditor5-vue';
function planReportUse(id, template) {
Vue.use(ElementUI, {
i18n: (key, value) => i18n.t(key, value)
});
Vue.use(Row);
Vue.use(Col);
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Input);
Vue.use(Button);
Vue.use(chart);
Vue.use(Main);
Vue.use(Card);
Vue.use(TableColumn);
Vue.use(Table);
// Vue.use(CKEditor);
new Vue({
el: id,
i18n,
render: h => h(template)
});
}
export default planReportUse;

View File

@ -0,0 +1,33 @@
<template>
<div>
<el-main>
<test-plan-detail-report v-if="visible" :is-share="true" :test-plan="testPlan"/>
</el-main>
</div>
</template>
<script>
import TestPlanDetailReport from "@/business/components/track/plan/view/comonents/report/TestPlanDetailReport";
import {getShareId} from "@/common/js/utils";
import {getShareInfo} from "@/network/share";
export default {
name: "SharePlanReportTemplate",
components: {TestPlanDetailReport},
data() {
return {
testPlan: {"id": "96791ef8-8a75-4335-a48c-832e0ddece5f"},
visible: false
}
},
created() {
let shareId = getShareId();
getShareInfo(shareId, (data) => {
this.testPlan.id = data.customData;
this.visible = true;
});
},
}
</script>
<style scoped>
</style>

View File

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

View File

@ -0,0 +1,4 @@
import planReportUse from "@/template/report/plan/planReportUse";
import SharePlanReportTemplate from "@/template/report/plan/share/SharePlanReportTemplate";
planReportUse('#sharePlanReport', SharePlanReportTemplate);

View File

@ -34,11 +34,16 @@ module.exports = {
template: "src/document/document.html",
filename: "document.html",
},
sharePlanReport: {
entry: "src/template/report/plan/share/share-plan-report.js",
template: "src/template/report/plan/share/share-plan-report.html",
filename: "share-plan-report.html",
},
planReport: {
entry: "src/template/report/plan/plan-report.js",
template: "src/template/report/plan/plan-report.html",
filename: "plan-report.html",
}
},
},
configureWebpack: {
devtool: 'source-map',