feat(测试计划): 计划报告内容支持富文本

This commit is contained in:
song-cc-rock 2024-05-20 20:18:05 +08:00 committed by Craftsman
parent c3f576d544
commit 98a580a427
16 changed files with 1455 additions and 22 deletions

View File

@ -0,0 +1,131 @@
package io.metersphere.plan.domain;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
@Data
public class TestPlanReportAttachment implements Serializable {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_report_attachment.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{test_plan_report_attachment.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(description = "报告ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_report_attachment.test_plan_report_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{test_plan_report_attachment.test_plan_report_id.length_range}", groups = {Created.class, Updated.class})
private String testPlanReportId;
@Schema(description = "文件ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_report_attachment.file_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{test_plan_report_attachment.file_id.length_range}", groups = {Created.class, Updated.class})
private String fileId;
@Schema(description = "文件名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_report_attachment.file_name.not_blank}", groups = {Created.class})
@Size(min = 1, max = 255, message = "{test_plan_report_attachment.file_name.length_range}", groups = {Created.class, Updated.class})
private String fileName;
@Schema(description = "文件大小", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{test_plan_report_attachment.size.not_blank}", groups = {Created.class})
private Long size;
@Schema(description = "文件来源", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan_report_attachment.source.not_blank}", groups = {Created.class})
@Size(min = 1, max = 255, message = "{test_plan_report_attachment.source.length_range}", groups = {Created.class, Updated.class})
private String source;
@Schema(description = "创建人")
private String createUser;
@Schema(description = "创建时间")
private Long createTime;
private static final long serialVersionUID = 1L;
public enum Column {
id("id", "id", "VARCHAR", false),
testPlanReportId("test_plan_report_id", "testPlanReportId", "VARCHAR", false),
fileId("file_id", "fileId", "VARCHAR", false),
fileName("file_name", "fileName", "VARCHAR", false),
size("size", "size", "BIGINT", true),
source("source", "source", "VARCHAR", true),
createUser("create_user", "createUser", "VARCHAR", false),
createTime("create_time", "createTime", "BIGINT", false);
private static final String BEGINNING_DELIMITER = "`";
private static final String ENDING_DELIMITER = "`";
private final String column;
private final boolean isColumnNameDelimited;
private final String javaProperty;
private final String jdbcType;
public String value() {
return this.column;
}
public String getValue() {
return this.column;
}
public String getJavaProperty() {
return this.javaProperty;
}
public String getJdbcType() {
return this.jdbcType;
}
Column(String column, String javaProperty, String jdbcType, boolean isColumnNameDelimited) {
this.column = column;
this.javaProperty = javaProperty;
this.jdbcType = jdbcType;
this.isColumnNameDelimited = isColumnNameDelimited;
}
public String desc() {
return this.getEscapedColumnName() + " DESC";
}
public String asc() {
return this.getEscapedColumnName() + " ASC";
}
public static Column[] excludes(Column ... excludes) {
ArrayList<Column> columns = new ArrayList<>(Arrays.asList(Column.values()));
if (excludes != null && excludes.length > 0) {
columns.removeAll(new ArrayList<>(Arrays.asList(excludes)));
}
return columns.toArray(new Column[]{});
}
public static Column[] all() {
return Column.values();
}
public String getEscapedColumnName() {
if (this.isColumnNameDelimited) {
return new StringBuilder().append(BEGINNING_DELIMITER).append(this.column).append(ENDING_DELIMITER).toString();
} else {
return this.column;
}
}
public String getAliasedEscapedColumnName() {
return this.getEscapedColumnName();
}
}
}

View File

@ -0,0 +1,740 @@
package io.metersphere.plan.domain;
import java.util.ArrayList;
import java.util.List;
public class TestPlanReportAttachmentExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public TestPlanReportAttachmentExample() {
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 andIdIsNull() {
addCriterion("id is null");
return (Criteria) this;
}
public Criteria andIdIsNotNull() {
addCriterion("id is not null");
return (Criteria) this;
}
public Criteria andIdEqualTo(String value) {
addCriterion("id =", value, "id");
return (Criteria) this;
}
public Criteria andIdNotEqualTo(String value) {
addCriterion("id <>", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThan(String value) {
addCriterion("id >", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThanOrEqualTo(String value) {
addCriterion("id >=", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThan(String value) {
addCriterion("id <", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThanOrEqualTo(String value) {
addCriterion("id <=", value, "id");
return (Criteria) this;
}
public Criteria andIdLike(String value) {
addCriterion("id like", value, "id");
return (Criteria) this;
}
public Criteria andIdNotLike(String value) {
addCriterion("id not like", value, "id");
return (Criteria) this;
}
public Criteria andIdIn(List<String> values) {
addCriterion("id in", values, "id");
return (Criteria) this;
}
public Criteria andIdNotIn(List<String> values) {
addCriterion("id not in", values, "id");
return (Criteria) this;
}
public Criteria andIdBetween(String value1, String value2) {
addCriterion("id between", value1, value2, "id");
return (Criteria) this;
}
public Criteria andIdNotBetween(String value1, String value2) {
addCriterion("id not between", value1, value2, "id");
return (Criteria) this;
}
public Criteria andTestPlanReportIdIsNull() {
addCriterion("test_plan_report_id is null");
return (Criteria) this;
}
public Criteria andTestPlanReportIdIsNotNull() {
addCriterion("test_plan_report_id is not null");
return (Criteria) this;
}
public Criteria andTestPlanReportIdEqualTo(String value) {
addCriterion("test_plan_report_id =", value, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdNotEqualTo(String value) {
addCriterion("test_plan_report_id <>", value, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdGreaterThan(String value) {
addCriterion("test_plan_report_id >", value, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdGreaterThanOrEqualTo(String value) {
addCriterion("test_plan_report_id >=", value, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdLessThan(String value) {
addCriterion("test_plan_report_id <", value, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdLessThanOrEqualTo(String value) {
addCriterion("test_plan_report_id <=", value, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdLike(String value) {
addCriterion("test_plan_report_id like", value, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdNotLike(String value) {
addCriterion("test_plan_report_id not like", value, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdIn(List<String> values) {
addCriterion("test_plan_report_id in", values, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdNotIn(List<String> values) {
addCriterion("test_plan_report_id not in", values, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdBetween(String value1, String value2) {
addCriterion("test_plan_report_id between", value1, value2, "testPlanReportId");
return (Criteria) this;
}
public Criteria andTestPlanReportIdNotBetween(String value1, String value2) {
addCriterion("test_plan_report_id not between", value1, value2, "testPlanReportId");
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 Criteria andFileNameIsNull() {
addCriterion("file_name is null");
return (Criteria) this;
}
public Criteria andFileNameIsNotNull() {
addCriterion("file_name is not null");
return (Criteria) this;
}
public Criteria andFileNameEqualTo(String value) {
addCriterion("file_name =", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameNotEqualTo(String value) {
addCriterion("file_name <>", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameGreaterThan(String value) {
addCriterion("file_name >", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameGreaterThanOrEqualTo(String value) {
addCriterion("file_name >=", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameLessThan(String value) {
addCriterion("file_name <", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameLessThanOrEqualTo(String value) {
addCriterion("file_name <=", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameLike(String value) {
addCriterion("file_name like", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameNotLike(String value) {
addCriterion("file_name not like", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameIn(List<String> values) {
addCriterion("file_name in", values, "fileName");
return (Criteria) this;
}
public Criteria andFileNameNotIn(List<String> values) {
addCriterion("file_name not in", values, "fileName");
return (Criteria) this;
}
public Criteria andFileNameBetween(String value1, String value2) {
addCriterion("file_name between", value1, value2, "fileName");
return (Criteria) this;
}
public Criteria andFileNameNotBetween(String value1, String value2) {
addCriterion("file_name not between", value1, value2, "fileName");
return (Criteria) this;
}
public Criteria andSizeIsNull() {
addCriterion("`size` is null");
return (Criteria) this;
}
public Criteria andSizeIsNotNull() {
addCriterion("`size` is not null");
return (Criteria) this;
}
public Criteria andSizeEqualTo(Long value) {
addCriterion("`size` =", value, "size");
return (Criteria) this;
}
public Criteria andSizeNotEqualTo(Long value) {
addCriterion("`size` <>", value, "size");
return (Criteria) this;
}
public Criteria andSizeGreaterThan(Long value) {
addCriterion("`size` >", value, "size");
return (Criteria) this;
}
public Criteria andSizeGreaterThanOrEqualTo(Long value) {
addCriterion("`size` >=", value, "size");
return (Criteria) this;
}
public Criteria andSizeLessThan(Long value) {
addCriterion("`size` <", value, "size");
return (Criteria) this;
}
public Criteria andSizeLessThanOrEqualTo(Long value) {
addCriterion("`size` <=", value, "size");
return (Criteria) this;
}
public Criteria andSizeIn(List<Long> values) {
addCriterion("`size` in", values, "size");
return (Criteria) this;
}
public Criteria andSizeNotIn(List<Long> values) {
addCriterion("`size` not in", values, "size");
return (Criteria) this;
}
public Criteria andSizeBetween(Long value1, Long value2) {
addCriterion("`size` between", value1, value2, "size");
return (Criteria) this;
}
public Criteria andSizeNotBetween(Long value1, Long value2) {
addCriterion("`size` not between", value1, value2, "size");
return (Criteria) this;
}
public Criteria andSourceIsNull() {
addCriterion("`source` is null");
return (Criteria) this;
}
public Criteria andSourceIsNotNull() {
addCriterion("`source` is not null");
return (Criteria) this;
}
public Criteria andSourceEqualTo(String value) {
addCriterion("`source` =", value, "source");
return (Criteria) this;
}
public Criteria andSourceNotEqualTo(String value) {
addCriterion("`source` <>", value, "source");
return (Criteria) this;
}
public Criteria andSourceGreaterThan(String value) {
addCriterion("`source` >", value, "source");
return (Criteria) this;
}
public Criteria andSourceGreaterThanOrEqualTo(String value) {
addCriterion("`source` >=", value, "source");
return (Criteria) this;
}
public Criteria andSourceLessThan(String value) {
addCriterion("`source` <", value, "source");
return (Criteria) this;
}
public Criteria andSourceLessThanOrEqualTo(String value) {
addCriterion("`source` <=", value, "source");
return (Criteria) this;
}
public Criteria andSourceLike(String value) {
addCriterion("`source` like", value, "source");
return (Criteria) this;
}
public Criteria andSourceNotLike(String value) {
addCriterion("`source` not like", value, "source");
return (Criteria) this;
}
public Criteria andSourceIn(List<String> values) {
addCriterion("`source` in", values, "source");
return (Criteria) this;
}
public Criteria andSourceNotIn(List<String> values) {
addCriterion("`source` not in", values, "source");
return (Criteria) this;
}
public Criteria andSourceBetween(String value1, String value2) {
addCriterion("`source` between", value1, value2, "source");
return (Criteria) this;
}
public Criteria andSourceNotBetween(String value1, String value2) {
addCriterion("`source` not between", value1, value2, "source");
return (Criteria) this;
}
public Criteria andCreateUserIsNull() {
addCriterion("create_user is null");
return (Criteria) this;
}
public Criteria andCreateUserIsNotNull() {
addCriterion("create_user is not null");
return (Criteria) this;
}
public Criteria andCreateUserEqualTo(String value) {
addCriterion("create_user =", value, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserNotEqualTo(String value) {
addCriterion("create_user <>", value, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserGreaterThan(String value) {
addCriterion("create_user >", value, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserGreaterThanOrEqualTo(String value) {
addCriterion("create_user >=", value, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserLessThan(String value) {
addCriterion("create_user <", value, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserLessThanOrEqualTo(String value) {
addCriterion("create_user <=", value, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserLike(String value) {
addCriterion("create_user like", value, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserNotLike(String value) {
addCriterion("create_user not like", value, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserIn(List<String> values) {
addCriterion("create_user in", values, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserNotIn(List<String> values) {
addCriterion("create_user not in", values, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserBetween(String value1, String value2) {
addCriterion("create_user between", value1, value2, "createUser");
return (Criteria) this;
}
public Criteria andCreateUserNotBetween(String value1, String value2) {
addCriterion("create_user not between", value1, value2, "createUser");
return (Criteria) this;
}
public Criteria andCreateTimeIsNull() {
addCriterion("create_time is null");
return (Criteria) this;
}
public Criteria andCreateTimeIsNotNull() {
addCriterion("create_time is not null");
return (Criteria) this;
}
public Criteria andCreateTimeEqualTo(Long value) {
addCriterion("create_time =", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotEqualTo(Long value) {
addCriterion("create_time <>", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeGreaterThan(Long value) {
addCriterion("create_time >", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) {
addCriterion("create_time >=", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeLessThan(Long value) {
addCriterion("create_time <", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeLessThanOrEqualTo(Long value) {
addCriterion("create_time <=", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeIn(List<Long> values) {
addCriterion("create_time in", values, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotIn(List<Long> values) {
addCriterion("create_time not in", values, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeBetween(Long value1, Long value2) {
addCriterion("create_time between", value1, value2, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotBetween(Long value1, Long value2) {
addCriterion("create_time not between", value1, value2, "createTime");
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,35 @@
package io.metersphere.plan.mapper;
import io.metersphere.plan.domain.TestPlanReportAttachment;
import io.metersphere.plan.domain.TestPlanReportAttachmentExample;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface TestPlanReportAttachmentMapper {
long countByExample(TestPlanReportAttachmentExample example);
int deleteByExample(TestPlanReportAttachmentExample example);
int deleteByPrimaryKey(String id);
int insert(TestPlanReportAttachment record);
int insertSelective(TestPlanReportAttachment record);
List<TestPlanReportAttachment> selectByExample(TestPlanReportAttachmentExample example);
TestPlanReportAttachment selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") TestPlanReportAttachment record, @Param("example") TestPlanReportAttachmentExample example);
int updateByExample(@Param("record") TestPlanReportAttachment record, @Param("example") TestPlanReportAttachmentExample example);
int updateByPrimaryKeySelective(TestPlanReportAttachment record);
int updateByPrimaryKey(TestPlanReportAttachment record);
int batchInsert(@Param("list") List<TestPlanReportAttachment> list);
int batchInsertSelective(@Param("list") List<TestPlanReportAttachment> list, @Param("selective") TestPlanReportAttachment.Column ... selective);
}

View File

@ -0,0 +1,307 @@
<?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.plan.mapper.TestPlanReportAttachmentMapper">
<resultMap id="BaseResultMap" type="io.metersphere.plan.domain.TestPlanReportAttachment">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="test_plan_report_id" jdbcType="VARCHAR" property="testPlanReportId" />
<result column="file_id" jdbcType="VARCHAR" property="fileId" />
<result column="file_name" jdbcType="VARCHAR" property="fileName" />
<result column="size" jdbcType="BIGINT" property="size" />
<result column="source" jdbcType="VARCHAR" property="source" />
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
id, test_plan_report_id, file_id, file_name, `size`, `source`, create_user, create_time
</sql>
<select id="selectByExample" parameterType="io.metersphere.plan.domain.TestPlanReportAttachmentExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from test_plan_report_attachment
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from test_plan_report_attachment
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from test_plan_report_attachment
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.plan.domain.TestPlanReportAttachmentExample">
delete from test_plan_report_attachment
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.plan.domain.TestPlanReportAttachment">
insert into test_plan_report_attachment (id, test_plan_report_id, file_id,
file_name, `size`, `source`,
create_user, create_time)
values (#{id,jdbcType=VARCHAR}, #{testPlanReportId,jdbcType=VARCHAR}, #{fileId,jdbcType=VARCHAR},
#{fileName,jdbcType=VARCHAR}, #{size,jdbcType=BIGINT}, #{source,jdbcType=VARCHAR},
#{createUser,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.plan.domain.TestPlanReportAttachment">
insert into test_plan_report_attachment
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="testPlanReportId != null">
test_plan_report_id,
</if>
<if test="fileId != null">
file_id,
</if>
<if test="fileName != null">
file_name,
</if>
<if test="size != null">
`size`,
</if>
<if test="source != null">
`source`,
</if>
<if test="createUser != null">
create_user,
</if>
<if test="createTime != null">
create_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="testPlanReportId != null">
#{testPlanReportId,jdbcType=VARCHAR},
</if>
<if test="fileId != null">
#{fileId,jdbcType=VARCHAR},
</if>
<if test="fileName != null">
#{fileName,jdbcType=VARCHAR},
</if>
<if test="size != null">
#{size,jdbcType=BIGINT},
</if>
<if test="source != null">
#{source,jdbcType=VARCHAR},
</if>
<if test="createUser != null">
#{createUser,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
#{createTime,jdbcType=BIGINT},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.plan.domain.TestPlanReportAttachmentExample" resultType="java.lang.Long">
select count(*) from test_plan_report_attachment
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update test_plan_report_attachment
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR},
</if>
<if test="record.testPlanReportId != null">
test_plan_report_id = #{record.testPlanReportId,jdbcType=VARCHAR},
</if>
<if test="record.fileId != null">
file_id = #{record.fileId,jdbcType=VARCHAR},
</if>
<if test="record.fileName != null">
file_name = #{record.fileName,jdbcType=VARCHAR},
</if>
<if test="record.size != null">
`size` = #{record.size,jdbcType=BIGINT},
</if>
<if test="record.source != null">
`source` = #{record.source,jdbcType=VARCHAR},
</if>
<if test="record.createUser != null">
create_user = #{record.createUser,jdbcType=VARCHAR},
</if>
<if test="record.createTime != null">
create_time = #{record.createTime,jdbcType=BIGINT},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update test_plan_report_attachment
set id = #{record.id,jdbcType=VARCHAR},
test_plan_report_id = #{record.testPlanReportId,jdbcType=VARCHAR},
file_id = #{record.fileId,jdbcType=VARCHAR},
file_name = #{record.fileName,jdbcType=VARCHAR},
`size` = #{record.size,jdbcType=BIGINT},
`source` = #{record.source,jdbcType=VARCHAR},
create_user = #{record.createUser,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.plan.domain.TestPlanReportAttachment">
update test_plan_report_attachment
<set>
<if test="testPlanReportId != null">
test_plan_report_id = #{testPlanReportId,jdbcType=VARCHAR},
</if>
<if test="fileId != null">
file_id = #{fileId,jdbcType=VARCHAR},
</if>
<if test="fileName != null">
file_name = #{fileName,jdbcType=VARCHAR},
</if>
<if test="size != null">
`size` = #{size,jdbcType=BIGINT},
</if>
<if test="source != null">
`source` = #{source,jdbcType=VARCHAR},
</if>
<if test="createUser != null">
create_user = #{createUser,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=BIGINT},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.plan.domain.TestPlanReportAttachment">
update test_plan_report_attachment
set test_plan_report_id = #{testPlanReportId,jdbcType=VARCHAR},
file_id = #{fileId,jdbcType=VARCHAR},
file_name = #{fileName,jdbcType=VARCHAR},
`size` = #{size,jdbcType=BIGINT},
`source` = #{source,jdbcType=VARCHAR},
create_user = #{createUser,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into test_plan_report_attachment
(id, test_plan_report_id, file_id, file_name, `size`, `source`, create_user, create_time
)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.testPlanReportId,jdbcType=VARCHAR}, #{item.fileId,jdbcType=VARCHAR},
#{item.fileName,jdbcType=VARCHAR}, #{item.size,jdbcType=BIGINT}, #{item.source,jdbcType=VARCHAR},
#{item.createUser,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT})
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
insert into test_plan_report_attachment (
<foreach collection="selective" item="column" separator=",">
${column.escapedColumnName}
</foreach>
)
values
<foreach collection="list" item="item" separator=",">
(
<foreach collection="selective" item="column" separator=",">
<if test="'id'.toString() == column.value">
#{item.id,jdbcType=VARCHAR}
</if>
<if test="'test_plan_report_id'.toString() == column.value">
#{item.testPlanReportId,jdbcType=VARCHAR}
</if>
<if test="'file_id'.toString() == column.value">
#{item.fileId,jdbcType=VARCHAR}
</if>
<if test="'file_name'.toString() == column.value">
#{item.fileName,jdbcType=VARCHAR}
</if>
<if test="'size'.toString() == column.value">
#{item.size,jdbcType=BIGINT}
</if>
<if test="'source'.toString() == column.value">
#{item.source,jdbcType=VARCHAR}
</if>
<if test="'create_user'.toString() == column.value">
#{item.createUser,jdbcType=VARCHAR}
</if>
<if test="'create_time'.toString() == column.value">
#{item.createTime,jdbcType=BIGINT}
</if>
</foreach>
)
</foreach>
</insert>
</mapper>

View File

@ -142,6 +142,22 @@ CREATE TABLE IF NOT EXISTS test_plan_report_bug(
CREATE INDEX idx_test_plan_report_id ON test_plan_report_bug(test_plan_report_id); CREATE INDEX idx_test_plan_report_id ON test_plan_report_bug(test_plan_report_id);
CREATE TABLE IF NOT EXISTS test_plan_report_attachment(
`id` VARCHAR(50) NOT NULL COMMENT 'ID' ,
`test_plan_report_id` VARCHAR(50) NOT NULL COMMENT '报告ID' ,
`file_id` VARCHAR(50) NOT NULL COMMENT '文件ID' ,
`file_name` VARCHAR(255) NOT NULL COMMENT '文件名称' ,
`size` BIGINT NOT NULL COMMENT '文件大小' ,
`source` VARCHAR(255) NOT NULL COMMENT '文件来源' ,
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
PRIMARY KEY (id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '测试计划报告附件关系表';
CREATE INDEX idx_report_id ON test_plan_report_attachment(test_plan_report_id);
CREATE INDEX idx_file_id ON test_plan_report_attachment(file_id);
CREATE INDEX idx_source ON test_plan_report_attachment(source);
-- 场景步骤 csv 表增加场景ID字段 -- 场景步骤 csv 表增加场景ID字段
ALTER TABLE api_scenario_csv_step ADD scenario_id varchar(50) NOT NULL COMMENT '场景ID'; ALTER TABLE api_scenario_csv_step ADD scenario_id varchar(50) NOT NULL COMMENT '场景ID';
CREATE INDEX idx_scenario_id USING BTREE ON api_scenario_csv_step (scenario_id); CREATE INDEX idx_scenario_id USING BTREE ON api_scenario_csv_step (scenario_id);

View File

@ -59,6 +59,7 @@ public class DefaultRepositoryDir {
private static final String PROJECT_API_SCENARIO_DIR = PROJECT_DIR + "/api-scenario/%s"; private static final String PROJECT_API_SCENARIO_DIR = PROJECT_DIR + "/api-scenario/%s";
private static final String PROJECT_API_SCENARIO_STEP_DIR = PROJECT_API_SCENARIO_DIR + "/step/%s"; private static final String PROJECT_API_SCENARIO_STEP_DIR = PROJECT_API_SCENARIO_DIR + "/step/%s";
private static final String PROJECT_BUG_DIR = PROJECT_DIR + "/bug/%s"; private static final String PROJECT_BUG_DIR = PROJECT_DIR + "/bug/%s";
private static final String PROJECT_PLAN_REPORT_DIR = PROJECT_DIR + "/plan-report/%s";
/** /**
* 接口定义相关文件的存储目录 * 接口定义相关文件的存储目录
@ -113,6 +114,10 @@ public class DefaultRepositoryDir {
return String.format(PROJECT_BUG_DIR, projectId, bugId); return String.format(PROJECT_BUG_DIR, projectId, bugId);
} }
public static String getPlanReportDir(String projectId, String reportId) {
return String.format(PROJECT_PLAN_REPORT_DIR, projectId, reportId);
}
public static String getApiDebugDir(String projectId, String apiDebugId) { public static String getApiDebugDir(String projectId, String apiDebugId) {
return String.format(PROJECT_API_DEBUG_DIR, projectId, apiDebugId); return String.format(PROJECT_API_DEBUG_DIR, projectId, apiDebugId);
} }

View File

@ -155,6 +155,7 @@ public class OperationLogModule {
//测试计划 //测试计划
public static final String TEST_PLAN = "TEST_PLAN"; public static final String TEST_PLAN = "TEST_PLAN";
public static final String TEST_PLAN_MODULE = "TEST_PLAN_MODULE"; public static final String TEST_PLAN_MODULE = "TEST_PLAN_MODULE";
public static final String TEST_PLAN_REPORT = "TEST_PLAN_REPORT";
// 个人信息-基本信息 // 个人信息-基本信息
public static final String PERSONAL_INFORMATION_BASE_INFO = "PERSONAL_INFORMATION_BASE_INFO"; public static final String PERSONAL_INFORMATION_BASE_INFO = "PERSONAL_INFORMATION_BASE_INFO";
@ -170,6 +171,4 @@ public class OperationLogModule {
public static final String SETTING_ORGANIZATION_TASK_CENTER = "SETTING_ORGANIZATION_TASK_CENTER"; public static final String SETTING_ORGANIZATION_TASK_CENTER = "SETTING_ORGANIZATION_TASK_CENTER";
//项目任务中心PROJECT_MANAGEMENT_TASK_CENTER //项目任务中心PROJECT_MANAGEMENT_TASK_CENTER
public static final String PROJECT_MANAGEMENT_TASK_CENTER = "PROJECT_MANAGEMENT_TASK_CENTER"; public static final String PROJECT_MANAGEMENT_TASK_CENTER = "PROJECT_MANAGEMENT_TASK_CENTER";
public static final String TEST_PLAN_REPORT = "TEST_PLAN_REPORT";
} }

View File

@ -3,6 +3,7 @@ package io.metersphere.plan.controller;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.bug.dto.response.BugDTO;
import io.metersphere.bug.service.BugAttachmentService;
import io.metersphere.plan.constants.TestPlanResourceConfig; import io.metersphere.plan.constants.TestPlanResourceConfig;
import io.metersphere.plan.domain.TestPlanReport; import io.metersphere.plan.domain.TestPlanReport;
import io.metersphere.plan.dto.ReportDetailCasePageDTO; import io.metersphere.plan.dto.ReportDetailCasePageDTO;
@ -29,6 +30,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -38,6 +40,8 @@ import java.util.List;
@Tag(name = "测试计划-报告") @Tag(name = "测试计划-报告")
public class TestPlanReportController { public class TestPlanReportController {
@Resource
private BugAttachmentService bugAttachmentService;
@Resource @Resource
private TestPlanManagementService testPlanManagementService; private TestPlanManagementService testPlanManagementService;
@Resource @Resource
@ -99,12 +103,20 @@ public class TestPlanReportController {
return testPlanReportService.getReport(reportId); return testPlanReportService.getReport(reportId);
} }
@PostMapping("/upload/md/file")
@Operation(summary = "测试计划-报告-详情-上传富文本(图片)")
@RequiresPermissions(PermissionConstants.TEST_PLAN_REPORT_READ_UPDATE)
public String upload(@RequestParam("file") MultipartFile file) {
return bugAttachmentService.uploadMdFile(file);
}
@PostMapping("/detail/edit") @PostMapping("/detail/edit")
@Operation(summary = "测试计划-报告-详情-报告内容更新") @Operation(summary = "测试计划-报告-详情-报告内容更新")
@RequiresPermissions(PermissionConstants.TEST_PLAN_REPORT_READ_UPDATE) @RequiresPermissions(PermissionConstants.TEST_PLAN_REPORT_READ_UPDATE)
@CheckOwner(resourceId = "#request.getId()", resourceType = "test_plan_report") @CheckOwner(resourceId = "#request.getId()", resourceType = "test_plan_report")
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateDetailLog(#request)", msClass = TestPlanReportLogService.class)
public TestPlanReportDetailResponse edit(@Validated @RequestBody TestPlanReportDetailEditRequest request) { public TestPlanReportDetailResponse edit(@Validated @RequestBody TestPlanReportDetailEditRequest request) {
return testPlanReportService.edit(request); return testPlanReportService.edit(request, SessionUtils.getUserId());
} }
@PostMapping("/detail/bug/page") @PostMapping("/detail/bug/page")

View File

@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import java.util.List;
@Data @Data
public class TestPlanReportDetailEditRequest { public class TestPlanReportDetailEditRequest {
@ -13,4 +15,7 @@ public class TestPlanReportDetailEditRequest {
@Schema(description = "报告内容") @Schema(description = "报告内容")
private String summary; private String summary;
@Schema(description = "富文本临时文件ID(图片)")
private List<String> richTextTmpFileIds;
} }

View File

@ -15,7 +15,7 @@ public class TestPlanReportDetailResponse {
@Schema(description = "报告执行开始时间") @Schema(description = "报告执行开始时间")
private Long executeTime; private Long executeTime;
@Schema(description = "报告结束(执行)时间") @Schema(description = "报告结束(执行)时间")
private Long ednTime; private Long endTime;
@Schema(description = "报告内容") @Schema(description = "报告内容")
private String summary; private String summary;

View File

@ -0,0 +1,9 @@
package io.metersphere.plan.enums;
public enum TestPlanReportAttachmentSourceType {
/**
* 富文本
*/
RICH_TEXT
}

View File

@ -46,6 +46,22 @@
tprfc.function_case_bug_count as bugCount tprfc.function_case_bug_count as bugCount
from test_plan_report_function_case tprfc from test_plan_report_function_case tprfc
where tprfc.test_plan_report_id = #{request.reportId} where tprfc.test_plan_report_id = #{request.reportId}
<include refid="filter"/>
</select> </select>
<sql id="filter">
<if test="request.filter != null and request.filter.size() > 0">
<foreach collection="request.filter.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<!-- 执行状态 -->
<when test="key == 'executeResult'">
and tprfc.function_case_execute_result in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
</choose>
</if>
</foreach>
</if>
</sql>
</mapper> </mapper>

View File

@ -1,6 +1,7 @@
package io.metersphere.plan.service; package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlanReport; import io.metersphere.plan.domain.TestPlanReport;
import io.metersphere.plan.dto.request.TestPlanReportDetailEditRequest;
import io.metersphere.plan.mapper.ExtTestPlanReportMapper; import io.metersphere.plan.mapper.ExtTestPlanReportMapper;
import io.metersphere.plan.mapper.TestPlanReportMapper; import io.metersphere.plan.mapper.TestPlanReportMapper;
import io.metersphere.project.domain.Project; import io.metersphere.project.domain.Project;
@ -67,6 +68,10 @@ public class TestPlanReportLogService {
return dto; return dto;
} }
public LogDTO updateDetailLog(TestPlanReportDetailEditRequest request) {
return updateLog(request.getId());
}
public void batchDeleteLog(List<String> ids, String userId, String projectId) { public void batchDeleteLog(List<String> ids, String userId, String projectId) {
Project project = projectMapper.selectByPrimaryKey(projectId); Project project = projectMapper.selectByPrimaryKey(projectId);
List<TestPlanReport> reports = extTestPlanReportMapper.selectReportByIds(ids); List<TestPlanReport> reports = extTestPlanReportMapper.selectReportByIds(ids);

View File

@ -10,16 +10,20 @@ import io.metersphere.plan.dto.TestPlanReportPostParam;
import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanReportDetailResponse; import io.metersphere.plan.dto.response.TestPlanReportDetailResponse;
import io.metersphere.plan.dto.response.TestPlanReportPageResponse; import io.metersphere.plan.dto.response.TestPlanReportPageResponse;
import io.metersphere.plan.enums.TestPlanReportAttachmentSourceType;
import io.metersphere.plan.mapper.*; import io.metersphere.plan.mapper.*;
import io.metersphere.plan.utils.RateCalculateUtils; import io.metersphere.plan.utils.RateCalculateUtils;
import io.metersphere.plugin.platform.dto.SelectOption; import io.metersphere.plugin.platform.dto.SelectOption;
import io.metersphere.sdk.constants.*; import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.util.DateUtils; import io.metersphere.sdk.file.FileCopyRequest;
import io.metersphere.sdk.util.SubListUtils; import io.metersphere.sdk.file.FileRepository;
import io.metersphere.sdk.util.Translator; import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.*;
import io.metersphere.system.domain.User; import io.metersphere.system.domain.User;
import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.mapper.BaseUserMapper;
import io.metersphere.system.mapper.UserMapper; import io.metersphere.system.mapper.UserMapper;
import io.metersphere.system.notice.constants.NoticeConstants; import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.service.UserService; import io.metersphere.system.service.UserService;
@ -34,6 +38,7 @@ import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -71,6 +76,10 @@ public class TestPlanReportService {
private TestPlanReportFunctionCaseMapper testPlanReportFunctionCaseMapper; private TestPlanReportFunctionCaseMapper testPlanReportFunctionCaseMapper;
@Resource @Resource
private TestPlanReportBugMapper testPlanReportBugMapper; private TestPlanReportBugMapper testPlanReportBugMapper;
@Resource
private TestPlanReportAttachmentMapper testPlanReportAttachmentMapper;
@Resource
private BaseUserMapper baseUserMapper;
/** /**
* 分页查询报告列表 * 分页查询报告列表
@ -321,8 +330,6 @@ public class TestPlanReportService {
// 计划的(执行)结果状态: 通过率 >= 阈值 ? 成功 : 失败 // 计划的(执行)结果状态: 通过率 >= 阈值 ? 成功 : 失败
planReport.setResultStatus(planReport.getPassRate() >= planReport.getPassThreshold() ? ReportStatus.SUCCESS.name() : ReportStatus.ERROR.name()); planReport.setResultStatus(planReport.getPassRate() >= planReport.getPassThreshold() ? ReportStatus.SUCCESS.name() : ReportStatus.ERROR.name());
planReport.setEndTime(System.currentTimeMillis());
testPlanReportMapper.updateByPrimaryKeySelective(planReport); testPlanReportMapper.updateByPrimaryKeySelective(planReport);
return planReport; return planReport;
} }
@ -354,12 +361,14 @@ public class TestPlanReportService {
* @param request 更新请求参数 * @param request 更新请求参数
* @return 报告详情 * @return 报告详情
*/ */
public TestPlanReportDetailResponse edit(TestPlanReportDetailEditRequest request) { public TestPlanReportDetailResponse edit(TestPlanReportDetailEditRequest request, String currentUser) {
TestPlanReport planReport = checkReport(request.getId()); TestPlanReport planReport = checkReport(request.getId());
TestPlanReportSummary reportSummary = new TestPlanReportSummary(); TestPlanReportSummary reportSummary = new TestPlanReportSummary();
reportSummary.setId(planReport.getId()); reportSummary.setId(planReport.getId());
reportSummary.setSummary(request.getSummary()); reportSummary.setSummary(request.getSummary());
testPlanReportSummaryMapper.updateByPrimaryKeySelective(reportSummary); testPlanReportSummaryMapper.updateByPrimaryKeySelective(reportSummary);
// 处理富文本文件
transferRichTextTmpFile(request.getId(), planReport.getProjectId(), request.getRichTextTmpFileIds(), currentUser, TestPlanReportAttachmentSourceType.RICH_TEXT.name());
return getReport(planReport.getId()); return getReport(planReport.getId());
} }
@ -373,15 +382,22 @@ public class TestPlanReportService {
} }
/** /**
* 分页查询报告详情-缺陷分页数据 * 分页查询报告详情-功能用例分页数据
* @param request 请求参数 * @param request 请求参数
* @return 缺陷分页数据 * @return 缺陷分页数据
*/ */
public List<ReportDetailCasePageDTO> listReportDetailFunctionalCases(TestPlanReportDetailPageRequest request) { public List<ReportDetailCasePageDTO> listReportDetailFunctionalCases(TestPlanReportDetailPageRequest request) {
return extTestPlanReportFunctionalCaseMapper.list(request); List<ReportDetailCasePageDTO> functionalCases = extTestPlanReportFunctionalCaseMapper.list(request);
if (CollectionUtils.isEmpty(functionalCases)) {
return new ArrayList<>();
}
List<String> distinctUserIds = functionalCases.stream().map(ReportDetailCasePageDTO::getExecuteUser).distinct().toList();
List<OptionDTO> userOptions = baseUserMapper.selectUserOptionByIds(distinctUserIds);
Map<String, String> userMap = userOptions.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName));
functionalCases.forEach(functionalCase -> functionalCase.setExecuteUser(userMap.getOrDefault(functionalCase.getExecuteUser(), functionalCase.getExecuteUser())));
return functionalCases;
} }
/** /**
* 统计用例执行数据 (目前只统计功能用例) * 统计用例执行数据 (目前只统计功能用例)
* @param reportDetail 用例详情 * @param reportDetail 用例详情
@ -459,4 +475,113 @@ public class TestPlanReportService {
} }
return testPlanReport; return testPlanReport;
} }
/**
* 转存报告内容富文本临时文件
* @param reportId 报告ID
* @param projectId 项目ID
* @param uploadFileIds 上传的文件ID集合
* @param userId 用户ID
* @param source 文件来源
*/
private void transferRichTextTmpFile(String reportId, String projectId, List<String> uploadFileIds, String userId, String source) {
if (CollectionUtils.isEmpty(uploadFileIds)) {
return;
}
//过滤已上传过的
TestPlanReportAttachmentExample example = new TestPlanReportAttachmentExample();
example.createCriteria().andTestPlanReportIdEqualTo(reportId).andFileIdIn(uploadFileIds).andSourceEqualTo(source);
List<TestPlanReportAttachment> existReportMdFiles = testPlanReportAttachmentMapper.selectByExample(example);
Map<String, TestPlanReportAttachment> existFileMap = existReportMdFiles.stream().collect(Collectors.toMap(TestPlanReportAttachment::getFileId, v -> v));
List<String> fileIds = uploadFileIds.stream().filter(t -> !existFileMap.containsKey(t) && StringUtils.isNotBlank(t)).toList();
if (CollectionUtils.isEmpty(fileIds)) {
return;
}
// 处理本地上传文件
FileRepository defaultRepository = FileCenter.getDefaultRepository();
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
// 添加文件与测试计划报告的关联关系
Map<String, String> addFileMap = new HashMap<>(fileIds.size());
LogUtils.info("开始上传副文本里的附件");
List<TestPlanReportAttachment> attachments = fileIds.stream().map(fileId -> {
TestPlanReportAttachment attachment = new TestPlanReportAttachment();
String fileName = getTempFileNameByFileId(fileId);
attachment.setId(IDGenerator.nextStr());
attachment.setTestPlanReportId(reportId);
attachment.setFileId(fileId);
attachment.setFileName(fileName);
attachment.setSource(source);
long fileSize;
try {
FileCopyRequest fileCopyRequest = new FileCopyRequest();
fileCopyRequest.setFolder(systemTempDir + "/" + fileId);
fileCopyRequest.setFileName(fileName);
fileSize = defaultRepository.getFileSize(fileCopyRequest);
} catch (Exception e) {
throw new MSException("读取富文本临时文件失败");
}
attachment.setSize(fileSize);
attachment.setCreateUser(userId);
attachment.setCreateTime(System.currentTimeMillis());
addFileMap.put(fileId, fileName);
return attachment;
}).toList();
testPlanReportAttachmentMapper.batchInsert(attachments);
// 上传文件到对象存储
LogUtils.info("upload to minio start");
uploadFileResource(DefaultRepositoryDir.getPlanReportDir(projectId, reportId), addFileMap);
LogUtils.info("upload to minio end");
}
/**
* 根据文件ID查询MINIO中对应目录下的文件名称
*/
private String getTempFileNameByFileId(String fileId) {
try {
FileRequest fileRequest = new FileRequest();
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
List<String> folderFileNames = FileCenter.getDefaultRepository().getFolderFileNames(fileRequest);
if (CollectionUtils.isEmpty(folderFileNames)) {
return null;
}
String[] pathSplit = folderFileNames.get(0).split("/");
return pathSplit[pathSplit.length - 1];
} catch (Exception e) {
LogUtils.error(e);
return null;
}
}
/**
* 上传文件到资源目录
* @param folder 文件夹
* @param addFileMap 文件ID与文件名映射
*/
private void uploadFileResource(String folder, Map<String, String> addFileMap) {
FileRepository defaultRepository = FileCenter.getDefaultRepository();
for (String fileId : addFileMap.keySet()) {
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
try {
String fileName = addFileMap.get(fileId);
if (StringUtils.isEmpty(fileName)) {
continue;
}
// 按ID建文件夹避免文件名重复
FileCopyRequest fileCopyRequest = new FileCopyRequest();
fileCopyRequest.setCopyFolder(systemTempDir + "/" + fileId);
fileCopyRequest.setCopyfileName(fileName);
fileCopyRequest.setFileName(fileName);
fileCopyRequest.setFolder(folder + "/" + fileId);
// 将文件从临时目录复制到资源目录
defaultRepository.copyFile(fileCopyRequest);
// 删除临时文件
fileCopyRequest.setFolder(systemTempDir + "/" + fileId);
fileCopyRequest.setFileName(fileName);
defaultRepository.delete(fileCopyRequest);
} catch (Exception e) {
LogUtils.error("上传副文本文件失败:{}",e);
throw new MSException(Translator.get("file_upload_fail"));
}
}
}
} }

View File

@ -84,6 +84,7 @@
<!-- <table tableName="test_plan_report_summary"/>--> <!-- <table tableName="test_plan_report_summary"/>-->
<!-- <table tableName="test_plan_report_function_case"/>--> <!-- <table tableName="test_plan_report_function_case"/>-->
<!-- <table tableName="test_plan_report_bug"/>--> <!-- <table tableName="test_plan_report_bug"/>-->
<!-- <table tableName="test_plan_report_attachment"/>-->
<!-- <table tableName="test_plan_config"/>--> <!-- <table tableName="test_plan_config"/>-->
<!-- 要忽略的字段--> <!-- 要忽略的字段-->
<!-- <table tableName="test_case"> <!-- <table tableName="test_case">

View File

@ -4,10 +4,8 @@ import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.TestPlanShareInfo; import io.metersphere.plan.dto.TestPlanShareInfo;
import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanReportPageResponse; import io.metersphere.plan.dto.response.TestPlanReportPageResponse;
import io.metersphere.plan.mapper.TestPlanReportBugMapper; import io.metersphere.plan.enums.TestPlanReportAttachmentSourceType;
import io.metersphere.plan.mapper.TestPlanReportFunctionCaseMapper; import io.metersphere.plan.mapper.*;
import io.metersphere.plan.mapper.TestPlanReportMapper;
import io.metersphere.plan.mapper.TestPlanReportSummaryMapper;
import io.metersphere.plan.service.CleanupTestPlanReportServiceImpl; import io.metersphere.plan.service.CleanupTestPlanReportServiceImpl;
import io.metersphere.plan.service.TestPlanReportService; import io.metersphere.plan.service.TestPlanReportService;
import io.metersphere.project.domain.ProjectApplicationExample; import io.metersphere.project.domain.ProjectApplicationExample;
@ -25,15 +23,14 @@ import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -48,6 +45,7 @@ public class TestPlanReportControllerTests extends BaseTest {
private static final String BATCH_DELETE_PLAN_REPORT = "/test-plan/report/batch-delete"; private static final String BATCH_DELETE_PLAN_REPORT = "/test-plan/report/batch-delete";
private static final String GEN_PLAN_REPORT = "/test-plan/report/gen"; private static final String GEN_PLAN_REPORT = "/test-plan/report/gen";
private static final String GET_PLAN_REPORT = "/test-plan/report/get"; private static final String GET_PLAN_REPORT = "/test-plan/report/get";
private static final String EDIT_PLAN_REPORT_AND_UPLOAD_PIC = "/test-plan/report/upload/md/file";
private static final String EDIT_PLAN_REPORT = "/test-plan/report/detail/edit"; private static final String EDIT_PLAN_REPORT = "/test-plan/report/detail/edit";
private static final String GET_PLAN_REPORT_DETAIL_BUG_PAGE = "/test-plan/report/detail/bug/page"; private static final String GET_PLAN_REPORT_DETAIL_BUG_PAGE = "/test-plan/report/detail/bug/page";
private static final String GET_PLAN_REPORT_DETAIL_FUNCTIONAL_PAGE = "/test-plan/report/detail/functional/case/page"; private static final String GET_PLAN_REPORT_DETAIL_FUNCTIONAL_PAGE = "/test-plan/report/detail/functional/case/page";
@ -64,6 +62,8 @@ public class TestPlanReportControllerTests extends BaseTest {
private TestPlanReportService testPlanReportService; private TestPlanReportService testPlanReportService;
@Resource @Resource
private ProjectApplicationMapper projectApplicationMapper; private ProjectApplicationMapper projectApplicationMapper;
@Resource
private TestPlanReportAttachmentMapper reportAttachmentMapper;
private static String GEN_REPORT_ID; private static String GEN_REPORT_ID;
private static String GEN_SHARE_ID; private static String GEN_SHARE_ID;
@ -291,6 +291,17 @@ public class TestPlanReportControllerTests extends BaseTest {
request.setId(GEN_REPORT_ID); request.setId(GEN_REPORT_ID);
request.setSummary("This is a summary for report detail"); request.setSummary("This is a summary for report detail");
this.requestPostWithOk(EDIT_PLAN_REPORT, request); this.requestPostWithOk(EDIT_PLAN_REPORT, request);
request.setRichTextTmpFileIds(List.of("rich-text-file-id-for-report"));
this.requestPost(EDIT_PLAN_REPORT, request, status().is5xxServerError());
generateReportMdFile();
this.requestPostWithOk(EDIT_PLAN_REPORT, request);
MockMultipartFile file = new MockMultipartFile("file", "test.txt", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes());
MvcResult mvcResult = this.requestUploadFileWithOkAndReturn(EDIT_PLAN_REPORT_AND_UPLOAD_PIC, file);
String sortData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(sortData, ResultHolder.class);
String fileId = resultHolder.getData().toString();
request.setRichTextTmpFileIds(List.of(fileId));
this.requestPostWithOk(EDIT_PLAN_REPORT, request);
} }
@Resource @Resource
@ -329,4 +340,20 @@ public class TestPlanReportControllerTests extends BaseTest {
Assertions.assertEquals(testPlanReportFunctionCaseMapper.countByExample(testPlanReportFunctionCaseExample), 0); Assertions.assertEquals(testPlanReportFunctionCaseMapper.countByExample(testPlanReportFunctionCaseExample), 0);
Assertions.assertEquals(testPlanReportBugMapper.countByExample(testPlanReportBugExample), 0); Assertions.assertEquals(testPlanReportBugMapper.countByExample(testPlanReportBugExample), 0);
} }
/**
* 生成报告附件的测试数据
*/
private void generateReportMdFile() {
TestPlanReportAttachment attachment = new TestPlanReportAttachment();
attachment.setId(UUID.randomUUID().toString());
attachment.setTestPlanReportId(GEN_REPORT_ID);
attachment.setFileId("rich-text-file-id-for-report");
attachment.setFileName("test-file");
attachment.setSize(111L);
attachment.setSource(TestPlanReportAttachmentSourceType.RICH_TEXT.name());
attachment.setCreateUser("admin");
attachment.setCreateTime(System.currentTimeMillis());
reportAttachmentMapper.insert(attachment);
}
} }