feat(测试跟踪): 测试用例上传附件
This commit is contained in:
parent
69a29e2c0f
commit
8ce89cf4a7
|
@ -0,0 +1,13 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TestCaseFile implements Serializable {
|
||||
private String caseId;
|
||||
|
||||
private String fileId;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -0,0 +1,340 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TestCaseFileExample {
|
||||
protected String orderByClause;
|
||||
|
||||
protected boolean distinct;
|
||||
|
||||
protected List<Criteria> oredCriteria;
|
||||
|
||||
public TestCaseFileExample() {
|
||||
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 andCaseIdIsNull() {
|
||||
addCriterion("case_id is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdIsNotNull() {
|
||||
addCriterion("case_id is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdEqualTo(String value) {
|
||||
addCriterion("case_id =", value, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdNotEqualTo(String value) {
|
||||
addCriterion("case_id <>", value, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdGreaterThan(String value) {
|
||||
addCriterion("case_id >", value, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("case_id >=", value, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdLessThan(String value) {
|
||||
addCriterion("case_id <", value, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdLessThanOrEqualTo(String value) {
|
||||
addCriterion("case_id <=", value, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdLike(String value) {
|
||||
addCriterion("case_id like", value, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdNotLike(String value) {
|
||||
addCriterion("case_id not like", value, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdIn(List<String> values) {
|
||||
addCriterion("case_id in", values, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdNotIn(List<String> values) {
|
||||
addCriterion("case_id not in", values, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdBetween(String value1, String value2) {
|
||||
addCriterion("case_id between", value1, value2, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCaseIdNotBetween(String value1, String value2) {
|
||||
addCriterion("case_id not between", value1, value2, "caseId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdIsNull() {
|
||||
addCriterion("file_id is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdIsNotNull() {
|
||||
addCriterion("file_id is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdEqualTo(String value) {
|
||||
addCriterion("file_id =", value, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdNotEqualTo(String value) {
|
||||
addCriterion("file_id <>", value, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdGreaterThan(String value) {
|
||||
addCriterion("file_id >", value, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("file_id >=", value, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdLessThan(String value) {
|
||||
addCriterion("file_id <", value, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdLessThanOrEqualTo(String value) {
|
||||
addCriterion("file_id <=", value, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdLike(String value) {
|
||||
addCriterion("file_id like", value, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdNotLike(String value) {
|
||||
addCriterion("file_id not like", value, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdIn(List<String> values) {
|
||||
addCriterion("file_id in", values, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdNotIn(List<String> values) {
|
||||
addCriterion("file_id not in", values, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdBetween(String value1, String value2) {
|
||||
addCriterion("file_id between", value1, value2, "fileId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andFileIdNotBetween(String value1, String value2) {
|
||||
addCriterion("file_id not between", value1, value2, "fileId");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package io.metersphere.base.mapper;
|
||||
|
||||
import io.metersphere.base.domain.TestCaseFile;
|
||||
import io.metersphere.base.domain.TestCaseFileExample;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface TestCaseFileMapper {
|
||||
long countByExample(TestCaseFileExample example);
|
||||
|
||||
int deleteByExample(TestCaseFileExample example);
|
||||
|
||||
int insert(TestCaseFile record);
|
||||
|
||||
int insertSelective(TestCaseFile record);
|
||||
|
||||
List<TestCaseFile> selectByExample(TestCaseFileExample example);
|
||||
|
||||
int updateByExampleSelective(@Param("record") TestCaseFile record, @Param("example") TestCaseFileExample example);
|
||||
|
||||
int updateByExample(@Param("record") TestCaseFile record, @Param("example") TestCaseFileExample example);
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
<?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.TestCaseFileMapper">
|
||||
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.TestCaseFile">
|
||||
<result column="case_id" jdbcType="VARCHAR" property="caseId" />
|
||||
<result column="file_id" jdbcType="VARCHAR" property="fileId" />
|
||||
</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">
|
||||
case_id, file_id
|
||||
</sql>
|
||||
<select id="selectByExample" parameterType="io.metersphere.base.domain.TestCaseFileExample" resultMap="BaseResultMap">
|
||||
select
|
||||
<if test="distinct">
|
||||
distinct
|
||||
</if>
|
||||
<include refid="Base_Column_List" />
|
||||
from test_case_file
|
||||
<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.TestCaseFileExample">
|
||||
delete from test_case_file
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
</delete>
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.TestCaseFile">
|
||||
insert into test_case_file (case_id, file_id)
|
||||
values (#{caseId,jdbcType=VARCHAR}, #{fileId,jdbcType=VARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestCaseFile">
|
||||
insert into test_case_file
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="caseId != null">
|
||||
case_id,
|
||||
</if>
|
||||
<if test="fileId != null">
|
||||
file_id,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="caseId != null">
|
||||
#{caseId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="fileId != null">
|
||||
#{fileId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<select id="countByExample" parameterType="io.metersphere.base.domain.TestCaseFileExample" resultType="java.lang.Long">
|
||||
select count(*) from test_case_file
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
</select>
|
||||
<update id="updateByExampleSelective" parameterType="map">
|
||||
update test_case_file
|
||||
<set>
|
||||
<if test="record.caseId != null">
|
||||
case_id = #{record.caseId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.fileId != null">
|
||||
file_id = #{record.fileId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
<update id="updateByExample" parameterType="map">
|
||||
update test_case_file
|
||||
set case_id = #{record.caseId,jdbcType=VARCHAR},
|
||||
file_id = #{record.fileId,jdbcType=VARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
</mapper>
|
|
@ -1,7 +1,7 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum FileType {
|
||||
JMX(".jmx"), CSV(".csv"), JSON(".json");
|
||||
JMX(".jmx"), CSV(".csv"), JSON(".json"), PDF(".pdf"), JPG(".jpg"), PNG(".png");
|
||||
|
||||
// 保存后缀
|
||||
private String suffix;
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.metersphere.base.domain.*;
|
|||
import io.metersphere.base.mapper.FileContentMapper;
|
||||
import io.metersphere.base.mapper.FileMetadataMapper;
|
||||
import io.metersphere.base.mapper.LoadTestFileMapper;
|
||||
import io.metersphere.base.mapper.TestCaseFileMapper;
|
||||
import io.metersphere.commons.constants.FileType;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -24,6 +25,8 @@ public class FileService {
|
|||
private LoadTestFileMapper loadTestFileMapper;
|
||||
@Resource
|
||||
private FileContentMapper fileContentMapper;
|
||||
@Resource
|
||||
private TestCaseFileMapper testCaseFileMapper;
|
||||
|
||||
public byte[] loadFileAsBytes(String id) {
|
||||
FileContent fileContent = fileContentMapper.selectByPrimaryKey(id);
|
||||
|
@ -66,6 +69,19 @@ public class FileService {
|
|||
loadTestFileMapper.deleteByExample(example3);
|
||||
}
|
||||
|
||||
public void deleteFileRelatedByIds(List<String> ids) {
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
return;
|
||||
}
|
||||
FileMetadataExample example = new FileMetadataExample();
|
||||
example.createCriteria().andIdIn(ids);
|
||||
fileMetadataMapper.deleteByExample(example);
|
||||
|
||||
FileContentExample example2 = new FileContentExample();
|
||||
example2.createCriteria().andFileIdIn(ids);
|
||||
fileContentMapper.deleteByExample(example2);
|
||||
}
|
||||
|
||||
public FileMetadata saveFile(MultipartFile file) {
|
||||
final FileMetadata fileMetadata = new FileMetadata();
|
||||
fileMetadata.setId(UUID.randomUUID().toString());
|
||||
|
@ -109,4 +125,19 @@ public class FileService {
|
|||
String type = filename.substring(s);
|
||||
return FileType.valueOf(type.toUpperCase());
|
||||
}
|
||||
|
||||
public List<FileMetadata> getFileMetadataByCaseId(String caseId) {
|
||||
TestCaseFileExample testCaseFileExample = new TestCaseFileExample();
|
||||
testCaseFileExample.createCriteria().andCaseIdEqualTo(caseId);
|
||||
final List<TestCaseFile> testCaseFiles = testCaseFileMapper.selectByExample(testCaseFileExample);
|
||||
|
||||
if (CollectionUtils.isEmpty(testCaseFiles)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> fileIds = testCaseFiles.stream().map(TestCaseFile::getFileId).collect(Collectors.toList());
|
||||
FileMetadataExample example = new FileMetadataExample();
|
||||
example.createCriteria().andIdIn(fileIds);
|
||||
return fileMetadataMapper.selectByExample(example);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package io.metersphere.track.controller;
|
|||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.base.domain.FileMetadata;
|
||||
import io.metersphere.base.domain.Project;
|
||||
import io.metersphere.base.domain.TestCase;
|
||||
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
||||
|
@ -11,12 +12,18 @@ import io.metersphere.commons.utils.Pager;
|
|||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.excel.domain.ExcelResponse;
|
||||
import io.metersphere.service.CheckOwnerService;
|
||||
import io.metersphere.service.FileService;
|
||||
import io.metersphere.track.dto.TestCaseDTO;
|
||||
import io.metersphere.track.request.testcase.EditTestCaseRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
||||
import io.metersphere.track.request.testcase.TestCaseBatchRequest;
|
||||
import io.metersphere.track.request.testplan.FileOperationRequest;
|
||||
import io.metersphere.track.service.TestCaseService;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
@ -33,6 +40,8 @@ public class TestCaseController {
|
|||
TestCaseService testCaseService;
|
||||
@Resource
|
||||
private CheckOwnerService checkOwnerService;
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
|
||||
@PostMapping("/list/{goPage}/{pageSize}")
|
||||
public Pager<List<TestCaseDTO>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryTestCaseRequest request) {
|
||||
|
@ -94,16 +103,16 @@ public class TestCaseController {
|
|||
return testCaseService.getProjectByTestCaseId(testCaseId);
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@PostMapping(value = "/add", consumes = {"multipart/form-data"})
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public void addTestCase(@RequestBody TestCaseWithBLOBs testCase) {
|
||||
testCaseService.addTestCase(testCase);
|
||||
public void addTestCase(@RequestPart("request") TestCaseWithBLOBs testCase, @RequestPart(value = "file") List<MultipartFile> files) {
|
||||
testCaseService.save(testCase, files);
|
||||
}
|
||||
|
||||
@PostMapping("/edit")
|
||||
@PostMapping(value = "/edit", consumes = {"multipart/form-data"})
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public void editTestCase(@RequestBody TestCaseWithBLOBs testCase) {
|
||||
testCaseService.editTestCase(testCase);
|
||||
public void editTestCase(@RequestPart("request") EditTestCaseRequest request, @RequestPart(value = "file") List<MultipartFile> files) {
|
||||
testCaseService.edit(request, files);
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{testCaseId}")
|
||||
|
@ -150,4 +159,18 @@ public class TestCaseController {
|
|||
testCaseService.deleteTestCaseBath(request);
|
||||
}
|
||||
|
||||
@GetMapping("/file/metadata/{caseId}")
|
||||
public List<FileMetadata> getFileMetadata(@PathVariable String caseId) {
|
||||
return fileService.getFileMetadataByCaseId(caseId);
|
||||
}
|
||||
|
||||
@PostMapping("/file/download")
|
||||
public ResponseEntity<byte[]> downloadJmx(@RequestBody FileOperationRequest fileOperationRequest) {
|
||||
byte[] bytes = fileService.loadFileAsBytes(fileOperationRequest.getId());
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileOperationRequest.getName() + "\"")
|
||||
.body(bytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package io.metersphere.track.request.testcase;
|
||||
|
||||
import io.metersphere.base.domain.FileMetadata;
|
||||
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class EditTestCaseRequest extends TestCaseWithBLOBs {
|
||||
private List<FileMetadata> updatedFileList;
|
||||
}
|
|
@ -26,10 +26,13 @@ import io.metersphere.excel.listener.EasyExcelListener;
|
|||
import io.metersphere.excel.listener.TestCaseDataListener;
|
||||
import io.metersphere.excel.utils.EasyExcelExporter;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.service.FileService;
|
||||
import io.metersphere.track.dto.TestCaseDTO;
|
||||
import io.metersphere.track.request.testcase.EditTestCaseRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
||||
import io.metersphere.track.request.testcase.TestCaseBatchRequest;
|
||||
import io.metersphere.xmind.XmindCaseParser;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
|
@ -83,8 +86,12 @@ public class TestCaseService {
|
|||
TestCaseReviewTestCaseMapper testCaseReviewTestCaseMapper;
|
||||
@Resource
|
||||
TestCaseCommentService testCaseCommentService;
|
||||
@Resource
|
||||
FileService fileService;
|
||||
@Resource
|
||||
TestCaseFileMapper testCaseFileMapper;
|
||||
|
||||
public void addTestCase(TestCaseWithBLOBs testCase) {
|
||||
private TestCaseWithBLOBs addTestCase(TestCaseWithBLOBs testCase) {
|
||||
testCase.setName(testCase.getName());
|
||||
checkTestCaseExist(testCase);
|
||||
testCase.setId(UUID.randomUUID().toString());
|
||||
|
@ -93,6 +100,7 @@ public class TestCaseService {
|
|||
testCase.setNum(getNextNum(testCase.getProjectId()));
|
||||
testCase.setReviewStatus(TestCaseReviewStatus.Prepare.name());
|
||||
testCaseMapper.insert(testCase);
|
||||
return testCase;
|
||||
}
|
||||
|
||||
public List<TestCase> getTestCaseByNodeId(List<String> nodeIds) {
|
||||
|
@ -596,4 +604,55 @@ public class TestCaseService {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
public String save(TestCaseWithBLOBs testCase, List<MultipartFile> files) {
|
||||
if (files == null) {
|
||||
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
|
||||
}
|
||||
final TestCaseWithBLOBs testCaseWithBLOBs = addTestCase(testCase);
|
||||
files.forEach(file -> {
|
||||
final FileMetadata fileMetadata = fileService.saveFile(file);
|
||||
TestCaseFile testCaseFile = new TestCaseFile();
|
||||
testCaseFile.setCaseId(testCaseWithBLOBs.getId());
|
||||
testCaseFile.setFileId(fileMetadata.getId());
|
||||
testCaseFileMapper.insert(testCaseFile);
|
||||
});
|
||||
return testCaseWithBLOBs.getId();
|
||||
}
|
||||
|
||||
public String edit(EditTestCaseRequest request, List<MultipartFile> files) {
|
||||
TestCaseWithBLOBs testCaseWithBLOBs = testCaseMapper.selectByPrimaryKey(request.getId());
|
||||
if (testCaseWithBLOBs == null) {
|
||||
MSException.throwException(Translator.get("edit_load_test_not_found") + request.getId());
|
||||
}
|
||||
|
||||
// 新选择了一个文件,删除原来的文件
|
||||
List<FileMetadata> updatedFiles = request.getUpdatedFileList();
|
||||
List<FileMetadata> originFiles = fileService.getFileMetadataByCaseId(request.getId());
|
||||
List<String> updatedFileIds = updatedFiles.stream().map(FileMetadata::getId).collect(Collectors.toList());
|
||||
List<String> originFileIds = originFiles.stream().map(FileMetadata::getId).collect(Collectors.toList());
|
||||
// 相减
|
||||
List<String> deleteFileIds = ListUtils.subtract(originFileIds, updatedFileIds);
|
||||
fileService.deleteFileRelatedByIds(deleteFileIds);
|
||||
|
||||
if (!CollectionUtils.isEmpty(deleteFileIds)) {
|
||||
TestCaseFileExample testCaseFileExample = new TestCaseFileExample();
|
||||
testCaseFileExample.createCriteria().andFileIdIn(deleteFileIds);
|
||||
testCaseFileMapper.deleteByExample(testCaseFileExample);
|
||||
}
|
||||
|
||||
|
||||
if (files != null) {
|
||||
files.forEach(file -> {
|
||||
final FileMetadata fileMetadata = fileService.saveFile(file);
|
||||
TestCaseFile testCaseFile = new TestCaseFile();
|
||||
testCaseFile.setFileId(fileMetadata.getId());
|
||||
testCaseFile.setCaseId(request.getId());
|
||||
testCaseFileMapper.insert(testCaseFile);
|
||||
});
|
||||
}
|
||||
|
||||
editTestCase(request);
|
||||
return request.getId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit cf6b06526324326a563d933e07118fac014a63b4
|
||||
Subproject commit ee74568be0beba46da19616f5832e83f9164c688
|
|
@ -0,0 +1,7 @@
|
|||
create table if not exists test_case_file
|
||||
(
|
||||
case_id varchar(64) null,
|
||||
file_id varchar(64) null,
|
||||
constraint test_case_file_unique_key
|
||||
unique (case_id, file_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
|
@ -209,6 +209,59 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row style="margin-top: 15px;margin-bottom: 10px">
|
||||
<el-col :offset="2" :span="20">附件:
|
||||
<el-upload
|
||||
action=""
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="handleUpload"
|
||||
:on-exceed="handleExceed"
|
||||
multiple
|
||||
:limit="3"
|
||||
:file-list="fileList">
|
||||
<el-button icon="el-icon-plus" size="mini"></el-button>
|
||||
<!-- <span slot="tip" class="el-upload__tip"></span>-->
|
||||
</el-upload>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="20">
|
||||
<el-table class="basic-config" :data="tableData">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('load_test.file_name')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="size"
|
||||
:label="$t('load_test.file_size')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="type"
|
||||
:label="$t('load_test.file_type')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('load_test.last_modify_time')">
|
||||
<template v-slot:default="scope">
|
||||
<i class="el-icon-time"/>
|
||||
<span class="last-modified">{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<el-button @click="handleDownload(scope.row)" :disabled="!scope.row.id || readOnly" type="primary"
|
||||
icon="el-icon-download"
|
||||
size="mini" circle/>
|
||||
<el-button :disabled="readOnly" @click="handleDelete(scope.row, scope.$index)" type="danger"
|
||||
icon="el-icon-delete" size="mini"
|
||||
circle/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<template v-slot:footer>
|
||||
|
@ -234,6 +287,7 @@ import {TokenKey, WORKSPACE_ID} from '../../../../../common/js/constants';
|
|||
import MsDialogFooter from '../../../common/components/MsDialogFooter'
|
||||
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
|
||||
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
|
||||
import {Message} from "element-ui";
|
||||
|
||||
export default {
|
||||
name: "TestCaseEdit",
|
||||
|
@ -263,6 +317,9 @@ export default {
|
|||
maintainerOptions: [],
|
||||
testOptions: [],
|
||||
workspaceId: '',
|
||||
fileList: [],
|
||||
tableData: [],
|
||||
uploadList: [],
|
||||
rules: {
|
||||
name: [
|
||||
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
|
||||
|
@ -339,6 +396,7 @@ export default {
|
|||
tmp.steps = JSON.parse(testCase.steps);
|
||||
Object.assign(this.form, tmp);
|
||||
this.form.module = testCase.nodeId;
|
||||
this.getFileMetaData(testCase);
|
||||
} else {
|
||||
if (this.selectNode.data) {
|
||||
this.form.module = this.selectNode.data.id;
|
||||
|
@ -358,6 +416,24 @@ export default {
|
|||
this.reload();
|
||||
this.dialogFormVisible = true;
|
||||
},
|
||||
getFileMetaData(testCase) {
|
||||
this.fileList = [];
|
||||
this.tableData = [];
|
||||
this.uploadList = [];
|
||||
this.result = this.$get("test/case/file/metadata/" + testCase.id, response => {
|
||||
let files = response.data;
|
||||
|
||||
if (!files) {
|
||||
return;
|
||||
}
|
||||
// deep copy
|
||||
this.fileList = JSON.parse(JSON.stringify(files));
|
||||
this.tableData = JSON.parse(JSON.stringify(files));
|
||||
this.tableData.map(f => {
|
||||
f.size = f.size + ' Bytes';
|
||||
});
|
||||
})
|
||||
},
|
||||
handleAddStep(index, data) {
|
||||
let step = {};
|
||||
step.num = data.num + 1;
|
||||
|
@ -400,7 +476,8 @@ export default {
|
|||
if (valid) {
|
||||
let param = this.buildParam();
|
||||
if (this.validate(param)) {
|
||||
this.result = this.$post('/test/case/' + this.operationType, param, () => {
|
||||
let option = this.getOption(param);
|
||||
this.result = this.$request(option, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
if (this.operationType == 'add' && this.isCreateContinue) {
|
||||
this.form.name = '';
|
||||
|
@ -411,6 +488,7 @@ export default {
|
|||
result: ''
|
||||
}];
|
||||
this.form.remark = '';
|
||||
this.form.uploadList = [];
|
||||
this.$emit("refresh");
|
||||
return;
|
||||
}
|
||||
|
@ -444,6 +522,32 @@ export default {
|
|||
}
|
||||
return param;
|
||||
},
|
||||
getOption(param) {
|
||||
let formData = new FormData();
|
||||
let url = '/test/case/' + this.operationType;
|
||||
this.uploadList.forEach(f => {
|
||||
formData.append("file", f);
|
||||
});
|
||||
|
||||
param.updatedFileList = this.fileList;
|
||||
|
||||
let requestJson = JSON.stringify(param, function (key, value) {
|
||||
return key === "file" ? undefined : value
|
||||
});
|
||||
|
||||
formData.append('request', new Blob([requestJson], {
|
||||
type: "application/json"
|
||||
}));
|
||||
|
||||
return {
|
||||
method: 'POST',
|
||||
url: url,
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
validate(param) {
|
||||
for (let i = 0; i < param.steps.length; i++) {
|
||||
if ((param.steps[i].desc && param.steps[i].desc.length > 300) ||
|
||||
|
@ -527,7 +631,88 @@ export default {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
handleExceed() {
|
||||
this.$error(this.$t('load_test.file_size_limit'));
|
||||
},
|
||||
beforeUpload(file) {
|
||||
if (!this.fileValidator(file)) {
|
||||
/// todo: 显示错误信息
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.tableData.filter(f => f.name === file.name).length > 0) {
|
||||
this.$error(this.$t('load_test.delete_file'));
|
||||
return false;
|
||||
}
|
||||
|
||||
let type = file.name.substring(file.name.lastIndexOf(".") + 1);
|
||||
|
||||
this.tableData.push({
|
||||
name: file.name,
|
||||
size: file.size + ' Bytes', /// todo: 按照大小显示Byte、KB、MB等
|
||||
type: type.toUpperCase(),
|
||||
updateTime: file.lastModified,
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
handleUpload(uploadResources) {
|
||||
this.uploadList.push(uploadResources.file);
|
||||
},
|
||||
handleDownload(file) {
|
||||
let data = {
|
||||
name: file.name,
|
||||
id: file.id,
|
||||
};
|
||||
let config = {
|
||||
url: '/test/case/file/download',
|
||||
method: 'post',
|
||||
data: data,
|
||||
responseType: 'blob'
|
||||
};
|
||||
this.result = this.$request(config).then(response => {
|
||||
const content = response.data;
|
||||
const blob = new Blob([content]);
|
||||
if ("download" in document.createElement("a")) {
|
||||
// 非IE下载
|
||||
// chrome/firefox
|
||||
let aTag = document.createElement('a');
|
||||
aTag.download = file.name;
|
||||
aTag.href = URL.createObjectURL(blob);
|
||||
aTag.click();
|
||||
URL.revokeObjectURL(aTag.href)
|
||||
} else {
|
||||
// IE10+下载
|
||||
navigator.msSaveBlob(blob, this.filename)
|
||||
}
|
||||
}).catch(e => {
|
||||
Message.error({message: e.message, showClose: true});
|
||||
});
|
||||
},
|
||||
handleDelete(file, index) {
|
||||
this.$alert(this.$t('load_test.delete_file_confirm') + file.name + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this._handleDelete(file, index);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
_handleDelete(file, index) {
|
||||
this.fileList.splice(index, 1);
|
||||
this.tableData.splice(index, 1);
|
||||
let i = this.uploadList.findIndex(upLoadFile => upLoadFile.name === file.name);
|
||||
if (i > -1) {
|
||||
this.uploadList.splice(i, 1);
|
||||
}
|
||||
},
|
||||
fileValidator(file) {
|
||||
/// todo: 是否需要对文件内容和大小做限制
|
||||
return file.size > 0;
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
trigger="hover"
|
||||
>
|
||||
<test-case-detail :test-case="scope.row"/>
|
||||
<p slot="reference">{{ scope.row.name }}</p>
|
||||
<span slot="reference">{{ scope.row.name }}</span>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 06d935cd1d22ab36f09763745c2aff8ad3fb08c1
|
||||
Subproject commit cc38137a69a0f20fadece9c0f9f50a9468c4ace9
|
Loading…
Reference in New Issue