feat(项目设置): 测试计划&性能测试的测试报告分享链接的有效时间支持修改

--user=郭雨琦 加时间选择与时间验证
This commit is contained in:
guoyuqi 2022-02-14 19:05:13 +08:00 committed by xiaomeinvG
parent b9983880ea
commit 0d20e7faa9
20 changed files with 919 additions and 12 deletions

View File

@ -15,6 +15,7 @@ import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.service.ProjectApplicationService;
import io.metersphere.track.service.TestPlanApiCaseService; import io.metersphere.track.service.TestPlanApiCaseService;
import io.metersphere.track.service.TestPlanScenarioCaseService; import io.metersphere.track.service.TestPlanScenarioCaseService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -25,6 +26,8 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.*;
import static io.metersphere.api.service.utils.ShareUtill.getTimeMills;
/** /**
* @author song.tianyang * @author song.tianyang
* @Date 2021/2/7 10:37 上午 * @Date 2021/2/7 10:37 上午
@ -44,6 +47,8 @@ public class ShareInfoService {
TestPlanScenarioCaseService testPlanScenarioCaseService; TestPlanScenarioCaseService testPlanScenarioCaseService;
@Resource @Resource
TestPlanReportMapper testPlanReportMapper; TestPlanReportMapper testPlanReportMapper;
@Resource
private ProjectApplicationService projectApplicationService;
public List<ApiDocumentInfoDTO> findApiDocumentSimpleInfoByRequest(ApiDocumentRequest request) { public List<ApiDocumentInfoDTO> findApiDocumentSimpleInfoByRequest(ApiDocumentRequest request) {
if (this.isParamLegitimacy(request)) { if (this.isParamLegitimacy(request)) {
@ -510,8 +515,33 @@ public class ShareInfoService {
*/ */
@Transactional(propagation = Propagation.NOT_SUPPORTED) @Transactional(propagation = Propagation.NOT_SUPPORTED)
public void validateExpired(ShareInfo shareInfo) { public void validateExpired(ShareInfo shareInfo) {
// 有效期24小时 // 有效期根据类型从ProjectApplication中获取
if (shareInfo == null || System.currentTimeMillis() - shareInfo.getUpdateTime() > 1000 * 60 * 60 * 24) { if(shareInfo == null ){
MSException.throwException("连接已失效,请重新获取!");
}
String type = "";
if(shareInfo.getShareType().equals("PERFORMANCE_REPORT")){
type = "PERFORMANCE";
}
if(shareInfo.getShareType().equals("PLAN_DB_REPORT")){
type = "TRACK";
}
if(StringUtils.isBlank(type)){
millisCheck(shareInfo,1000 * 60 * 60 * 24);
}else{
ProjectApplication projectApplication = projectApplicationService.getProjectApplication(SessionUtils.getCurrentProjectId(),type);
if(projectApplication.getProjectId()==null){
millisCheck(shareInfo,1000 * 60 * 60 * 24);
}else {
String expr= projectApplication.getShareReportExpr();
long timeMills = getTimeMills(expr);
millisCheck(shareInfo,timeMills);
}
}
}
private void millisCheck(ShareInfo shareInfo, long millis) {
if (System.currentTimeMillis() - shareInfo.getUpdateTime() > millis) {
shareInfoMapper.deleteByPrimaryKey(shareInfo.getId()); shareInfoMapper.deleteByPrimaryKey(shareInfo.getId());
MSException.throwException("连接已失效,请重新获取!"); MSException.throwException("连接已失效,请重新获取!");
} }

View File

@ -0,0 +1,41 @@
package io.metersphere.api.service.utils;
import org.apache.commons.lang3.StringUtils;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
public class ShareUtill {
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";
public static long getTimeMills(String expr) {
LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.now().withMinute(0).withSecond(0).withNano(0));
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.minusHours(quantity);
} else if (StringUtils.equals(unit, UNIT_DAY)) {
date = localDateTime.minusDays(quantity);
} else if (StringUtils.equals(unit, UNIT_MONTH)) {
date = localDateTime.minusMonths(quantity);
} else if (StringUtils.equals(unit, UNIT_YEAR)) {
date = localDateTime.minusYears(quantity);
}
return date;
}
}

View File

@ -0,0 +1,15 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
@Data
public class ProjectApplication implements Serializable {
private String projectId;
private String type;
private String shareReportExpr;
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,410 @@
package io.metersphere.base.domain;
import java.util.ArrayList;
import java.util.List;
public class ProjectApplicationExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public ProjectApplicationExample() {
oredCriteria = new ArrayList<Criteria>();
}
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
public String getOrderByClause() {
return orderByClause;
}
public void setDistinct(boolean distinct) {
this.distinct = distinct;
}
public boolean isDistinct() {
return distinct;
}
public List<Criteria> getOredCriteria() {
return oredCriteria;
}
public void or(Criteria criteria) {
oredCriteria.add(criteria);
}
public Criteria or() {
Criteria criteria = createCriteriaInternal();
oredCriteria.add(criteria);
return criteria;
}
public Criteria createCriteria() {
Criteria criteria = createCriteriaInternal();
if (oredCriteria.size() == 0) {
oredCriteria.add(criteria);
}
return criteria;
}
protected Criteria createCriteriaInternal() {
Criteria criteria = new Criteria();
return criteria;
}
public void clear() {
oredCriteria.clear();
orderByClause = null;
distinct = false;
}
protected abstract static class GeneratedCriteria {
protected List<Criterion> criteria;
protected GeneratedCriteria() {
super();
criteria = new ArrayList<Criterion>();
}
public boolean isValid() {
return criteria.size() > 0;
}
public List<Criterion> getAllCriteria() {
return criteria;
}
public List<Criterion> getCriteria() {
return criteria;
}
protected void addCriterion(String condition) {
if (condition == null) {
throw new RuntimeException("Value for condition cannot be null");
}
criteria.add(new Criterion(condition));
}
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
throw new RuntimeException("Value for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value));
}
protected void addCriterion(String condition, Object value1, Object value2, String property) {
if (value1 == null || value2 == null) {
throw new RuntimeException("Between values for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value1, value2));
}
public Criteria andProjectIdIsNull() {
addCriterion("project_id is null");
return (Criteria) this;
}
public Criteria andProjectIdIsNotNull() {
addCriterion("project_id is not null");
return (Criteria) this;
}
public Criteria andProjectIdEqualTo(String value) {
addCriterion("project_id =", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotEqualTo(String value) {
addCriterion("project_id <>", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdGreaterThan(String value) {
addCriterion("project_id >", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdGreaterThanOrEqualTo(String value) {
addCriterion("project_id >=", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdLessThan(String value) {
addCriterion("project_id <", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdLessThanOrEqualTo(String value) {
addCriterion("project_id <=", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdLike(String value) {
addCriterion("project_id like", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotLike(String value) {
addCriterion("project_id not like", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdIn(List<String> values) {
addCriterion("project_id in", values, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotIn(List<String> values) {
addCriterion("project_id not in", values, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdBetween(String value1, String value2) {
addCriterion("project_id between", value1, value2, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotBetween(String value1, String value2) {
addCriterion("project_id not between", value1, value2, "projectId");
return (Criteria) this;
}
public Criteria andTypeIsNull() {
addCriterion("`type` is null");
return (Criteria) this;
}
public Criteria andTypeIsNotNull() {
addCriterion("`type` is not null");
return (Criteria) this;
}
public Criteria andTypeEqualTo(String value) {
addCriterion("`type` =", value, "type");
return (Criteria) this;
}
public Criteria andTypeNotEqualTo(String value) {
addCriterion("`type` <>", value, "type");
return (Criteria) this;
}
public Criteria andTypeGreaterThan(String value) {
addCriterion("`type` >", value, "type");
return (Criteria) this;
}
public Criteria andTypeGreaterThanOrEqualTo(String value) {
addCriterion("`type` >=", value, "type");
return (Criteria) this;
}
public Criteria andTypeLessThan(String value) {
addCriterion("`type` <", value, "type");
return (Criteria) this;
}
public Criteria andTypeLessThanOrEqualTo(String value) {
addCriterion("`type` <=", value, "type");
return (Criteria) this;
}
public Criteria andTypeLike(String value) {
addCriterion("`type` like", value, "type");
return (Criteria) this;
}
public Criteria andTypeNotLike(String value) {
addCriterion("`type` not like", value, "type");
return (Criteria) this;
}
public Criteria andTypeIn(List<String> values) {
addCriterion("`type` in", values, "type");
return (Criteria) this;
}
public Criteria andTypeNotIn(List<String> values) {
addCriterion("`type` not in", values, "type");
return (Criteria) this;
}
public Criteria andTypeBetween(String value1, String value2) {
addCriterion("`type` between", value1, value2, "type");
return (Criteria) this;
}
public Criteria andTypeNotBetween(String value1, String value2) {
addCriterion("`type` not between", value1, value2, "type");
return (Criteria) this;
}
public Criteria andShareReportExprIsNull() {
addCriterion("share_report_expr is null");
return (Criteria) this;
}
public Criteria andShareReportExprIsNotNull() {
addCriterion("share_report_expr is not null");
return (Criteria) this;
}
public Criteria andShareReportExprEqualTo(String value) {
addCriterion("share_report_expr =", value, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprNotEqualTo(String value) {
addCriterion("share_report_expr <>", value, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprGreaterThan(String value) {
addCriterion("share_report_expr >", value, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprGreaterThanOrEqualTo(String value) {
addCriterion("share_report_expr >=", value, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprLessThan(String value) {
addCriterion("share_report_expr <", value, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprLessThanOrEqualTo(String value) {
addCriterion("share_report_expr <=", value, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprLike(String value) {
addCriterion("share_report_expr like", value, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprNotLike(String value) {
addCriterion("share_report_expr not like", value, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprIn(List<String> values) {
addCriterion("share_report_expr in", values, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprNotIn(List<String> values) {
addCriterion("share_report_expr not in", values, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprBetween(String value1, String value2) {
addCriterion("share_report_expr between", value1, value2, "shareReportExpr");
return (Criteria) this;
}
public Criteria andShareReportExprNotBetween(String value1, String value2) {
addCriterion("share_report_expr not between", value1, value2, "shareReportExpr");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {
protected Criteria() {
super();
}
}
public static class Criterion {
private String condition;
private Object value;
private Object secondValue;
private boolean noValue;
private boolean singleValue;
private boolean betweenValue;
private boolean listValue;
private String typeHandler;
public String getCondition() {
return condition;
}
public Object getValue() {
return value;
}
public Object getSecondValue() {
return secondValue;
}
public boolean isNoValue() {
return noValue;
}
public boolean isSingleValue() {
return singleValue;
}
public boolean isBetweenValue() {
return betweenValue;
}
public boolean isListValue() {
return listValue;
}
public String getTypeHandler() {
return typeHandler;
}
protected Criterion(String condition) {
super();
this.condition = condition;
this.typeHandler = null;
this.noValue = true;
}
protected Criterion(String condition, Object value, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.typeHandler = typeHandler;
if (value instanceof List<?>) {
this.listValue = true;
} else {
this.singleValue = true;
}
}
protected Criterion(String condition, Object value) {
this(condition, value, null);
}
protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.secondValue = secondValue;
this.typeHandler = typeHandler;
this.betweenValue = true;
}
protected Criterion(String condition, Object value, Object secondValue) {
this(condition, value, secondValue, null);
}
}
}

View File

@ -0,0 +1,24 @@
package io.metersphere.base.mapper;
import io.metersphere.base.domain.Project;
import io.metersphere.base.domain.ProjectApplication;
import io.metersphere.base.domain.ProjectApplicationExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface ProjectApplicationMapper {
long countByExample(ProjectApplicationExample example);
int deleteByExample(ProjectApplicationExample example);
int insert(ProjectApplication record);
int insertSelective(ProjectApplication record);
List<ProjectApplication> selectByExample(ProjectApplicationExample example);
int updateByExampleSelective(@Param("record") ProjectApplication record, @Param("example") ProjectApplicationExample example);
int updateByExample(@Param("record") ProjectApplication record, @Param("example") ProjectApplicationExample example);
}

View File

@ -0,0 +1,153 @@
<?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.ProjectApplicationMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.ProjectApplication">
<result column="project_id" jdbcType="VARCHAR" property="projectId" />
<result column="type" jdbcType="VARCHAR" property="type" />
<result column="share_report_expr" jdbcType="VARCHAR" property="shareReportExpr" />
</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">
project_id, `type`, share_report_expr
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.ProjectApplicationExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from project_application
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.ProjectApplicationExample">
delete from project_application
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.ProjectApplication">
insert into project_application (project_id, `type`, share_report_expr
)
values (#{projectId,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR}, #{shareReportExpr,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ProjectApplication">
insert into project_application
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="projectId != null">
project_id,
</if>
<if test="type != null">
`type`,
</if>
<if test="shareReportExpr != null">
share_report_expr,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="projectId != null">
#{projectId,jdbcType=VARCHAR},
</if>
<if test="type != null">
#{type,jdbcType=VARCHAR},
</if>
<if test="shareReportExpr != null">
#{shareReportExpr,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.ProjectApplicationExample" resultType="java.lang.Long">
select count(*) from project_application
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update project_application
<set>
<if test="record.projectId != null">
project_id = #{record.projectId,jdbcType=VARCHAR},
</if>
<if test="record.type != null">
`type` = #{record.type,jdbcType=VARCHAR},
</if>
<if test="record.shareReportExpr != null">
share_report_expr = #{record.shareReportExpr,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update project_application
set project_id = #{record.projectId,jdbcType=VARCHAR},
`type` = #{record.type,jdbcType=VARCHAR},
share_report_expr = #{record.shareReportExpr,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
</mapper>

View File

@ -0,0 +1,28 @@
package io.metersphere.controller;
import io.metersphere.base.domain.ProjectApplication;
import io.metersphere.commons.constants.OperLogConstants;
import io.metersphere.commons.constants.OperLogModule;
import io.metersphere.log.annotation.MsAuditLog;
import io.metersphere.service.ProjectApplicationService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping(value = "/project_application")
public class ProjectApplicationController {
@Resource
private ProjectApplicationService projectApplicationService;
@PostMapping("/update")
@MsAuditLog(module = OperLogModule.PROJECT_PROJECT_MANAGER, type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#projectApplication)", content = "#msClass.getLogDetails(#projectApplication)", msClass = ProjectApplicationService.class)
public void updateProject(@RequestBody ProjectApplication projectApplication) {
projectApplicationService.updateProjectApplication(projectApplication);
}
@GetMapping("/get/{projectId}/{type}")
public ProjectApplication getProjectApplication(@PathVariable String projectId,@PathVariable String type) {
return projectApplicationService.getProjectApplication(projectId,type);
}
}

View File

@ -181,6 +181,7 @@ public class ShareController {
@GetMapping("/performance/report/get-advanced-config/{shareId}/{reportId}") @GetMapping("/performance/report/get-advanced-config/{shareId}/{reportId}")
public String getAdvancedConfig(@PathVariable String shareId, @PathVariable String reportId) { public String getAdvancedConfig(@PathVariable String shareId, @PathVariable String reportId) {
shareInfoService.validate(shareId, reportId);
return performanceReportService.getAdvancedConfiguration(reportId); return performanceReportService.getAdvancedConfiguration(reportId);
} }

View File

@ -22,6 +22,7 @@ public class SystemReference {
public static Map<String, String> mailColumns = new LinkedHashMap<>(); public static Map<String, String> mailColumns = new LinkedHashMap<>();
public static Map<String, String> baseColumns = new LinkedHashMap<>(); public static Map<String, String> baseColumns = new LinkedHashMap<>();
public static Map<String, String> groupColumns = new LinkedHashMap<>(); public static Map<String, String> groupColumns = new LinkedHashMap<>();
public static Map<String, String> projectApplicationColumns = new LinkedHashMap<>();
static { static {
@ -42,6 +43,7 @@ public class SystemReference {
ldapColumns.clear(); ldapColumns.clear();
mailColumns.clear(); mailColumns.clear();
groupColumns.clear(); groupColumns.clear();
projectApplicationColumns.clear();
userColumns.put("name", "用户名称"); userColumns.put("name", "用户名称");
userColumns.put("createUser", "创建人"); userColumns.put("createUser", "创建人");
@ -110,6 +112,10 @@ public class SystemReference {
projectColumns.put("name", "名称"); projectColumns.put("name", "名称");
projectColumns.put("description", "描述"); projectColumns.put("description", "描述");
projectApplicationColumns.put("projectId", "项目ID");
projectApplicationColumns.put("type", "类型");
projectApplicationColumns.put("shareReportExpr", "报告分享链接时间");
jarColumns.put("name", "名称"); jarColumns.put("name", "名称");
jarColumns.put("fileName", "文件名称"); jarColumns.put("fileName", "文件名称");
jarColumns.put("description", "描述"); jarColumns.put("description", "描述");

View File

@ -0,0 +1,58 @@
package io.metersphere.service;
import com.alibaba.fastjson.JSON;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ProjectApplicationMapper;
import io.metersphere.base.mapper.ProjectMapper;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.log.utils.ReflexObjectUtil;
import io.metersphere.log.vo.DetailColumn;
import io.metersphere.log.vo.OperatingLogDetails;
import io.metersphere.log.vo.system.SystemReference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
@Service
@Transactional(rollbackFor = Exception.class)
public class ProjectApplicationService {
@Resource
private ProjectApplicationMapper projectApplicationMapper;
@Resource
private ProjectMapper projectMapper;
public void updateProjectApplication(ProjectApplication projectApplication){
ProjectApplicationExample projectApplicationExample = new ProjectApplicationExample();
projectApplicationExample.createCriteria().andProjectIdEqualTo(projectApplication.getProjectId()).andTypeEqualTo(projectApplication.getType());
projectApplicationMapper.updateByExample(projectApplication,projectApplicationExample);
}
public String getLogDetails(ProjectApplication projectApplication) {
if (projectApplication != null) {
List<DetailColumn> columns = ReflexObjectUtil.getColumns(projectApplication, SystemReference.projectApplicationColumns);
Project project = projectMapper.selectByPrimaryKey(projectApplication.getProjectId());
if (project==null) {
return null;
}
DetailColumn column = new DetailColumn("项目名称", "projectName", project.getName(), null);
columns.add(column);
OperatingLogDetails details = new OperatingLogDetails(JSON.toJSONString(project.getId()), project.getId(), "修改链接报告时间", Objects.requireNonNull(SessionUtils.getUser()).getCreateUser(), columns);
return JSON.toJSONString(details);
}
return null;
}
public ProjectApplication getProjectApplication(String projectId, String type) {
ProjectApplicationExample projectApplicationExample = new ProjectApplicationExample();
projectApplicationExample.createCriteria().andProjectIdEqualTo(projectId).andTypeEqualTo(type);
List<ProjectApplication> projectApplications = projectApplicationMapper.selectByExample(projectApplicationExample);
if(projectApplications==null||projectApplications.size()==0){
return new ProjectApplication();
}
return projectApplications.get(0);
}
}

View File

@ -108,6 +108,9 @@ public class ProjectService {
private TestPlanReportService testPlanReportService; private TestPlanReportService testPlanReportService;
@Resource @Resource
private ApiScenarioReportService apiScenarioReportService; private ApiScenarioReportService apiScenarioReportService;
@Resource
private ProjectApplicationMapper projectApplicationMapper;
public Project addProject(Project project) { public Project addProject(Project project) {
if (StringUtils.isBlank(project.getName())) { if (StringUtils.isBlank(project.getName())) {
@ -164,6 +167,15 @@ public class ProjectService {
projectVersion.setStatus("open"); projectVersion.setStatus("open");
projectVersionService.addProjectVersion(projectVersion); projectVersionService.addProjectVersion(projectVersion);
} }
//创建新项目也创建相关新项目的应用分测试跟踪接口性能
ProjectApplication projectApplication = new ProjectApplication();
projectApplication.setProjectId(project.getId());
//每个新项目都会有测试跟踪/性能报告分享链接的有效时间,默认时间24H
projectApplication.setType("TRACK");
projectApplicationMapper.insert(projectApplication);
projectApplication.setType("PERFORMANCE");
projectApplicationMapper.insert(projectApplication);
return project; return project;
} }
@ -301,6 +313,14 @@ public class ProjectService {
reportIdList.forEach(reportId -> performanceReportService.deleteReport(reportId)); reportIdList.forEach(reportId -> performanceReportService.deleteReport(reportId));
} }
}); });
//删除分享报告时间
delReportTime(projectId,"PERFORMANCE");
}
private void delReportTime(String projectId,String type) {
ProjectApplicationExample projectApplicationExample = new ProjectApplicationExample();
projectApplicationExample.createCriteria().andProjectIdEqualTo(projectId).andTypeEqualTo(type);
projectApplicationMapper.deleteByExample(projectApplicationExample);
} }
private void deleteTrackResourceByProjectId(String projectId) { private void deleteTrackResourceByProjectId(String projectId) {
@ -311,6 +331,8 @@ public class ProjectService {
}); });
} }
testCaseService.deleteTestCaseByProjectId(projectId); testCaseService.deleteTestCaseByProjectId(projectId);
//删除分享报告时间
delReportTime(projectId,"TRACK");
} }
private void deleteAPIResourceByProjectId(String projectId) { private void deleteAPIResourceByProjectId(String projectId) {
@ -324,6 +346,9 @@ public class ProjectService {
}); });
} }
public void updateProject(Project project) { public void updateProject(Project project) {
//查询之前的TCP端口用于检查是否需要开启/关闭 TCP接口 //查询之前的TCP端口用于检查是否需要开启/关闭 TCP接口
int lastTcpNum = 0; int lastTcpNum = 0;

View File

@ -52,6 +52,46 @@ DELIMITER ;
CALL test_personal(); CALL test_personal();
DROP PROCEDURE IF EXISTS test_personal; DROP PROCEDURE IF EXISTS test_personal;
DROP PROCEDURE IF EXISTS project_appl;
DELIMITER //
CREATE PROCEDURE project_appl()
BEGIN
#声明结束标识
DECLARE end_flag int DEFAULT 0;
DECLARE projectId varchar(64);
#声明游标 group_curosr
DECLARE project_curosr CURSOR FOR SELECT DISTINCT id FROM project;
#设置终止标志
DECLARE CONTINUE HANDLER FOR NOT FOUND SET end_flag=1;
#打开游标
OPEN project_curosr;
#遍历游标
REPEAT
#获取当前游标指针记录,取出值赋给自定义的变量
FETCH project_curosr INTO projectId;
#利用取到的值进行数据库的操作
INSERT INTO project_application (project_id, type, share_report_expr)
VALUES (projectId, 'TRACK', '24H'),
(projectId, 'PERFORMANCE', '24H');
# 根据 end_flag 判断是否结束
UNTIL end_flag END REPEAT;
#关闭游标
close project_curosr;
END
//
DELIMITER ;
CALL project_appl();
DROP PROCEDURE IF EXISTS project_appl;
ALTER TABLE api_definition_exec_result ALTER TABLE api_definition_exec_result
ADD project_id varchar(50); ADD project_id varchar(50);

View File

@ -71,7 +71,7 @@
<!--<table tableName="test_plan"/>--> <!--<table tableName="test_plan"/>-->
<!--<table tableName="api_scenario_report"/>--> <!--<table tableName="api_scenario_report"/>-->
<!--<table tableName="test_case_review"/>--> <!--<table tableName="test_case_review"/>-->
<table tableName="api_scenario_report"/> <table tableName="project_application"/>
<!--<table tableName="enterprise_test_report_send_record"/>--> <!--<table tableName="enterprise_test_report_send_record"/>-->
<!--<table tableName="test_case_review_api_case"/> <!--<table tableName="test_case_review_api_case"/>
<table tableName="test_case_review_load"/> <table tableName="test_case_review_load"/>

View File

@ -37,7 +37,7 @@
width="300"> width="300">
<p>{{ shareUrl }}</p> <p>{{ shareUrl }}</p>
<span style="color: red;float: left;margin-left: 10px;">{{ <span style="color: red;float: left;margin-left: 10px;">{{
$t('test_track.report.valid_for_24_hours') $t('commons.validity_period')+application.shareReportExpr
}}</span> }}</span>
<div style="text-align: right; margin: 0"> <div style="text-align: right; margin: 0">
<el-button type="primary" size="mini" :disabled="!shareUrl" <el-button type="primary" size="mini" :disabled="!shareUrl"
@ -153,7 +153,7 @@ import MsReportTestOverview from './components/TestOverview';
import MsContainer from "../../common/components/MsContainer"; import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer"; import MsMainContainer from "../../common/components/MsMainContainer";
import {exportPdf, hasPermission} from "@/common/js/utils"; import {exportPdf, getCurrentProjectID, hasPermission} from "@/common/js/utils";
import html2canvas from 'html2canvas'; import html2canvas from 'html2canvas';
import MsPerformanceReportExport from "./PerformanceReportExport"; import MsPerformanceReportExport from "./PerformanceReportExport";
import {Message} from "element-ui"; import {Message} from "element-ui";
@ -216,6 +216,7 @@ export default {
], ],
testDeleted: false, testDeleted: false,
shareUrl: "", shareUrl: "",
application:{}
}; };
}, },
methods: { methods: {
@ -375,6 +376,14 @@ export default {
let thisHost = window.location.host; let thisHost = window.location.host;
this.shareUrl = thisHost + "/sharePerformanceReport" + data.shareUrl; this.shareUrl = thisHost + "/sharePerformanceReport" + data.shareUrl;
}); });
this.getProjectApplication();
},
getProjectApplication(){
this.$get('/project_application/get/' + getCurrentProjectID()+"/PERFORMANCE", res => {
if(res.data){
this.application = res.data;
}
});
}, },
exportReportReset() { exportReportReset() {
this.reportExportVisible = false; this.reportExportVisible = false;

View File

@ -28,6 +28,8 @@
</app-manage-item> </app-manage-item>
<timing-item ref="trackTimingItem" :choose.sync="form.cleanTrackReport" :expr.sync="form.cleanTrackReportExpr" <timing-item ref="trackTimingItem" :choose.sync="form.cleanTrackReport" :expr.sync="form.cleanTrackReportExpr"
@chooseChange="chooseChange" :title="$t('project.timing_clean_plan_report')"/> @chooseChange="chooseChange" :title="$t('project.timing_clean_plan_report')"/>
<timing-item ref="trackTimingItem" :choose.sync="application.shareReport" :expr.sync="application.shareReportExpr" :share-link="true"
@chooseChange="chooseChangeApply" :title="$t('report.report_sharing_link')"/>
</el-row> </el-row>
</el-tab-pane> </el-tab-pane>
@ -92,6 +94,8 @@
<el-row style="margin-top: 15px"> <el-row style="margin-top: 15px">
<timing-item ref="loadTimingItem" :choose.sync="form.cleanLoadReport" :expr.sync="form.cleanLoadReportExpr" <timing-item ref="loadTimingItem" :choose.sync="form.cleanLoadReport" :expr.sync="form.cleanLoadReportExpr"
@chooseChange="chooseChange" :title="$t('project.timing_clean_load_report')"/> @chooseChange="chooseChange" :title="$t('project.timing_clean_load_report')"/>
<timing-item ref="trackTimingItem" :choose.sync="application.shareReport" :expr.sync="application.shareReportExpr" :share-link="true"
@chooseChange="chooseChangeApply" :title="$t('report.report_sharing_link')"/>
</el-row> </el-row>
</el-tab-pane> </el-tab-pane>
@ -125,6 +129,11 @@ export default {
MsMainContainer, MsMainContainer,
MsContainer MsContainer
}, },
watch: {
activeName(val) {
this.getProjectApplication();
},
},
data() { data() {
return { return {
activeName: 'test_track', activeName: 'test_track',
@ -136,6 +145,10 @@ export default {
cleanLoadReport: false, cleanLoadReport: false,
cleanLoadReportExpr: "" cleanLoadReportExpr: ""
}, },
application:{
shareReport:'',
shareReportExpr:'',
},
count: 0, count: 0,
isXpack: false, isXpack: false,
result: {}, result: {},
@ -165,10 +178,40 @@ export default {
this.init(); this.init();
}); });
}, },
chooseChangeApply(){
if(!this.application.shareReport){
return;
}
this.$post("/project_application/update", this.application, () => {
this.$success(this.$t('commons.save_success'));
this.init();
}, () => {
this.init();
});
},
getProjectApplication(){
let type;
if(this.activeName==='test_track'){
type = 'TRACK'
}else if(this.activeName==='performance'){
type = 'PERFORMANCE'
}else if(this.activeName==='api'){
type = 'API'
}
if(type){
this.$get('/project_application/get/' + this.projectId+"/"+type, res => {
if(res.data){
res.data.shareReport = true;
this.application = res.data;
}
});
}
},
init() { init() {
this.result = this.$get('/project/get/' + this.projectId, res => { this.result = this.$get('/project/get/' + this.projectId, res => {
this.form = res.data; this.form = res.data;
}) });
this.getProjectApplication();
} }
} }
}; };

View File

@ -1,8 +1,10 @@
<template> <template>
<app-manage-item :title="title" :append-span="3" :middle-span="12" :prepend-span="9"> <app-manage-item :title="title" :append-span="3" :middle-span="12" :prepend-span="9">
<template #middle> <template #middle>
<span class="timing_name">{{ $t('project.keep_recent') }}</span> <span class="timing_name" v-if="shareLink">{{ $t('commons.validity_period') }}</span>
<el-select v-model="selfQuantity" placeholder=" " size="mini" <span class="timing_name" v-if="!shareLink">{{ $t('project.keep_recent') }}</span>
<el-select v-model="selfQuantity" placeholder=" " size="mini" filterable
allow-create
class="timing_select" :disabled="selfChoose"> class="timing_select" :disabled="selfChoose">
<el-option <el-option
v-for="item in quantityOptions" v-for="item in quantityOptions"
@ -20,7 +22,7 @@
:value="item.value"> :value="item.value">
</el-option> </el-option>
</el-select> </el-select>
<span class="timing_name" style="margin-left: 3px;">{{ $t('commons.report') }}</span> <span class="timing_name" v-if="!shareLink" style="margin-left: 3px;">{{ $t('commons.report') }}</span>
</template> </template>
<template #append> <template #append>
<el-switch v-model="selfChoose" @change="chooseChange"></el-switch> <el-switch v-model="selfChoose" @change="chooseChange"></el-switch>
@ -41,7 +43,7 @@ export default {
type: Boolean, type: Boolean,
default() { default() {
return false; return false;
} },
}, },
expr: { expr: {
type: String, type: String,
@ -54,7 +56,13 @@ export default {
default() { default() {
return ""; return "";
} }
} },
shareLink: {
type: Boolean,
default() {
return false;
},
},
}, },
watch: { watch: {
expr(val) { expr(val) {

View File

@ -5,7 +5,7 @@
placement="right" placement="right"
width="300"> width="300">
<p>{{shareUrl}}</p> <p>{{shareUrl}}</p>
<span style="color: red;float: left;margin-left: 10px;">{{ $t('test_track.report.valid_for_24_hours') }}</span> <span style="color: red;float: left;margin-left: 10px;">{{ $t('commons.validity_period')+application.shareReportExpr}}</span>
<div style="text-align: right; margin: 0"> <div style="text-align: right; margin: 0">
<el-button type="primary" size="mini" :disabled="!shareUrl" <el-button type="primary" size="mini" :disabled="!shareUrl"
v-clipboard:copy="shareUrl">{{ $t("commons.copy") }}</el-button> v-clipboard:copy="shareUrl">{{ $t("commons.copy") }}</el-button>
@ -42,6 +42,7 @@ import {generateShareInfoWithExpired} from "@/network/share";
import TestPlanReportEdit import TestPlanReportEdit
from "@/business/components/track/plan/view/comonents/report/detail/component/TestPlanReportEdit"; from "@/business/components/track/plan/view/comonents/report/detail/component/TestPlanReportEdit";
import {editPlanReport, saveTestPlanReport} from "@/network/test-plan"; import {editPlanReport, saveTestPlanReport} from "@/network/test-plan";
import {getCurrentProjectID} from "@/common/js/utils";
export default { export default {
name: "TestPlanReportButtons", name: "TestPlanReportButtons",
components: { components: {
@ -59,6 +60,7 @@ export default {
result: {}, result: {},
isTestManagerOrTestUser: true, isTestManagerOrTestUser: true,
shareUrl: '', shareUrl: '',
application:{},
}; };
}, },
methods: { methods: {
@ -77,6 +79,14 @@ export default {
let thisHost = window.location.host; let thisHost = window.location.host;
this.shareUrl = thisHost + "/sharePlanReport" + data.shareUrl; this.shareUrl = thisHost + "/sharePlanReport" + data.shareUrl;
}); });
this.getProjectApplication();
},
getProjectApplication(){
this.$get('/project_application/get/' + getCurrentProjectID()+"/TRACK", res => {
if(res.data){
this.application = res.data;
}
});
}, },
handleSave() { handleSave() {
let param = {}; let param = {};

View File

@ -234,6 +234,7 @@ export default {
cancel_follow_success: "Cancel Follow Success", cancel_follow_success: "Cancel Follow Success",
generate_test_data: "Generate test data", generate_test_data: "Generate test data",
type: "Type", type: "Type",
validity_period:'Validity Period',
please_select_a_deadline: "Please select a deadline", please_select_a_deadline: "Please select a deadline",
relationship: { relationship: {
name: 'Dependencies', name: 'Dependencies',
@ -895,6 +896,7 @@ export default {
api_test_report: 'Api Test Report', api_test_report: 'Api Test Report',
load_test_report: 'Load Test Report', load_test_report: 'Load Test Report',
test_plan_report: 'Test Plan Report', test_plan_report: 'Test Plan Report',
report_sharing_link: 'Report Sharing Link',
recent: 'My recent Report', recent: 'My recent Report',
search_by_name: 'Search by Name', search_by_name: 'Search by Name',
batch_add_monitor_tips: 'Format: name, IP, Port, description<br/>such as: item 1, 192.168.1.52, 9100, test', batch_add_monitor_tips: 'Format: name, IP, Port, description<br/>such as: item 1, 192.168.1.52, 9100, test',

View File

@ -234,6 +234,7 @@ export default {
follow_success: "关注成功", follow_success: "关注成功",
cancel_follow_success: "取消关注成功", cancel_follow_success: "取消关注成功",
type: "类型", type: "类型",
validity_period:'有效期',
please_select_a_deadline: "请选择截止时间", please_select_a_deadline: "请选择截止时间",
relationship: { relationship: {
name: '依赖关系', name: '依赖关系',
@ -899,6 +900,7 @@ export default {
api_test_report: '接口测试报告', api_test_report: '接口测试报告',
load_test_report: '性能测试报告', load_test_report: '性能测试报告',
test_plan_report: '测试计划报告', test_plan_report: '测试计划报告',
report_sharing_link:'报告分享链接',
recent: '我最近的报告', recent: '我最近的报告',
search_by_name: '根据名称搜索', search_by_name: '根据名称搜索',
batch_add_monitor_tips: '格式:名称,IP,Port,描述<br/>如项目1,192.168.1.52,9100,测试', batch_add_monitor_tips: '格式:名称,IP,Port,描述<br/>如项目1,192.168.1.52,9100,测试',

View File

@ -234,6 +234,7 @@ export default {
follow_success: "關註成功", follow_success: "關註成功",
cancel_follow_success: "取消關註成功", cancel_follow_success: "取消關註成功",
type: "類型", type: "類型",
validity_period:'有效期',
please_select_a_deadline: "請選擇截止時間", please_select_a_deadline: "請選擇截止時間",
relationship: { relationship: {
name: '依賴關系', name: '依賴關系',
@ -899,6 +900,7 @@ export default {
api_test_report: '接口測試報告', api_test_report: '接口測試報告',
load_test_report: '性能測試報告', load_test_report: '性能測試報告',
test_plan_report: '測試計劃報告', test_plan_report: '測試計劃報告',
report_sharing_link: '報告分享鏈接',
recent: '我最近的報告', recent: '我最近的報告',
search_by_name: '根據名稱搜索', search_by_name: '根據名稱搜索',
batch_add_monitor_tips: '格式:名稱,IP,Port,描述<br/>如項目1,192.168.1.52,9100,測試', batch_add_monitor_tips: '格式:名稱,IP,Port,描述<br/>如項目1,192.168.1.52,9100,測試',