feat(用例管理): 新增停止导出

This commit is contained in:
guoyuqi 2024-08-06 16:42:27 +08:00 committed by 刘瑞斌
parent 6c40f93f0c
commit 6b921d4aa3
27 changed files with 2174 additions and 13 deletions

View File

@ -0,0 +1,126 @@
package io.metersphere.functional.domain;
import io.metersphere.validation.groups.*;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import lombok.Data;
@Data
public class ExportTask implements Serializable {
@Schema(description = "任务唯一ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{export_task.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{export_task.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(description = "名称")
private String name;
@Schema(description = "资源类型", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{export_task.type.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{export_task.type.length_range}", groups = {Created.class, Updated.class})
private String type;
@Schema(description = "文件id")
private String fileid;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{export_task.state.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{export_task.state.length_range}", groups = {Created.class, Updated.class})
private String state;
@Schema(description = "创建人")
private String createUser;
@Schema(description = "创建时间")
private Long createTime;
@Schema(description = "创建人")
private String updateUser;
@Schema(description = "创建时间")
private Long updateTime;
private static final long serialVersionUID = 1L;
public enum Column {
id("id", "id", "VARCHAR", false),
name("name", "name", "VARCHAR", true),
type("type", "type", "VARCHAR", true),
fileid("fileId", "fileid", "VARCHAR", false),
state("state", "state", "VARCHAR", true),
createUser("create_user", "createUser", "VARCHAR", false),
createTime("create_time", "createTime", "BIGINT", false),
updateUser("update_user", "updateUser", "VARCHAR", false),
updateTime("update_time", "updateTime", "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,810 @@
package io.metersphere.functional.domain;
import java.util.ArrayList;
import java.util.List;
public class ExportTaskExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public ExportTaskExample() {
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 andNameIsNull() {
addCriterion("`name` is null");
return (Criteria) this;
}
public Criteria andNameIsNotNull() {
addCriterion("`name` is not null");
return (Criteria) this;
}
public Criteria andNameEqualTo(String value) {
addCriterion("`name` =", value, "name");
return (Criteria) this;
}
public Criteria andNameNotEqualTo(String value) {
addCriterion("`name` <>", value, "name");
return (Criteria) this;
}
public Criteria andNameGreaterThan(String value) {
addCriterion("`name` >", value, "name");
return (Criteria) this;
}
public Criteria andNameGreaterThanOrEqualTo(String value) {
addCriterion("`name` >=", value, "name");
return (Criteria) this;
}
public Criteria andNameLessThan(String value) {
addCriterion("`name` <", value, "name");
return (Criteria) this;
}
public Criteria andNameLessThanOrEqualTo(String value) {
addCriterion("`name` <=", value, "name");
return (Criteria) this;
}
public Criteria andNameLike(String value) {
addCriterion("`name` like", value, "name");
return (Criteria) this;
}
public Criteria andNameNotLike(String value) {
addCriterion("`name` not like", value, "name");
return (Criteria) this;
}
public Criteria andNameIn(List<String> values) {
addCriterion("`name` in", values, "name");
return (Criteria) this;
}
public Criteria andNameNotIn(List<String> values) {
addCriterion("`name` not in", values, "name");
return (Criteria) this;
}
public Criteria andNameBetween(String value1, String value2) {
addCriterion("`name` between", value1, value2, "name");
return (Criteria) this;
}
public Criteria andNameNotBetween(String value1, String value2) {
addCriterion("`name` not between", value1, value2, "name");
return (Criteria) this;
}
public Criteria andTypeIsNull() {
addCriterion("`type` is null");
return (Criteria) this;
}
public Criteria andTypeIsNotNull() {
addCriterion("`type` is not null");
return (Criteria) this;
}
public Criteria andTypeEqualTo(String value) {
addCriterion("`type` =", value, "type");
return (Criteria) this;
}
public Criteria andTypeNotEqualTo(String value) {
addCriterion("`type` <>", value, "type");
return (Criteria) this;
}
public Criteria andTypeGreaterThan(String value) {
addCriterion("`type` >", value, "type");
return (Criteria) this;
}
public Criteria andTypeGreaterThanOrEqualTo(String value) {
addCriterion("`type` >=", value, "type");
return (Criteria) this;
}
public Criteria andTypeLessThan(String value) {
addCriterion("`type` <", value, "type");
return (Criteria) this;
}
public Criteria andTypeLessThanOrEqualTo(String value) {
addCriterion("`type` <=", value, "type");
return (Criteria) this;
}
public Criteria andTypeLike(String value) {
addCriterion("`type` like", value, "type");
return (Criteria) this;
}
public Criteria andTypeNotLike(String value) {
addCriterion("`type` not like", value, "type");
return (Criteria) this;
}
public Criteria andTypeIn(List<String> values) {
addCriterion("`type` in", values, "type");
return (Criteria) this;
}
public Criteria andTypeNotIn(List<String> values) {
addCriterion("`type` not in", values, "type");
return (Criteria) this;
}
public Criteria andTypeBetween(String value1, String value2) {
addCriterion("`type` between", value1, value2, "type");
return (Criteria) this;
}
public Criteria andTypeNotBetween(String value1, String value2) {
addCriterion("`type` not between", value1, value2, "type");
return (Criteria) this;
}
public Criteria andFileidIsNull() {
addCriterion("fileId is null");
return (Criteria) this;
}
public Criteria andFileidIsNotNull() {
addCriterion("fileId is not null");
return (Criteria) this;
}
public Criteria andFileidEqualTo(String value) {
addCriterion("fileId =", value, "fileid");
return (Criteria) this;
}
public Criteria andFileidNotEqualTo(String value) {
addCriterion("fileId <>", value, "fileid");
return (Criteria) this;
}
public Criteria andFileidGreaterThan(String value) {
addCriterion("fileId >", value, "fileid");
return (Criteria) this;
}
public Criteria andFileidGreaterThanOrEqualTo(String value) {
addCriterion("fileId >=", value, "fileid");
return (Criteria) this;
}
public Criteria andFileidLessThan(String value) {
addCriterion("fileId <", value, "fileid");
return (Criteria) this;
}
public Criteria andFileidLessThanOrEqualTo(String value) {
addCriterion("fileId <=", value, "fileid");
return (Criteria) this;
}
public Criteria andFileidLike(String value) {
addCriterion("fileId like", value, "fileid");
return (Criteria) this;
}
public Criteria andFileidNotLike(String value) {
addCriterion("fileId not like", value, "fileid");
return (Criteria) this;
}
public Criteria andFileidIn(List<String> values) {
addCriterion("fileId in", values, "fileid");
return (Criteria) this;
}
public Criteria andFileidNotIn(List<String> values) {
addCriterion("fileId not in", values, "fileid");
return (Criteria) this;
}
public Criteria andFileidBetween(String value1, String value2) {
addCriterion("fileId between", value1, value2, "fileid");
return (Criteria) this;
}
public Criteria andFileidNotBetween(String value1, String value2) {
addCriterion("fileId not between", value1, value2, "fileid");
return (Criteria) this;
}
public Criteria andStateIsNull() {
addCriterion("`state` is null");
return (Criteria) this;
}
public Criteria andStateIsNotNull() {
addCriterion("`state` is not null");
return (Criteria) this;
}
public Criteria andStateEqualTo(String value) {
addCriterion("`state` =", value, "state");
return (Criteria) this;
}
public Criteria andStateNotEqualTo(String value) {
addCriterion("`state` <>", value, "state");
return (Criteria) this;
}
public Criteria andStateGreaterThan(String value) {
addCriterion("`state` >", value, "state");
return (Criteria) this;
}
public Criteria andStateGreaterThanOrEqualTo(String value) {
addCriterion("`state` >=", value, "state");
return (Criteria) this;
}
public Criteria andStateLessThan(String value) {
addCriterion("`state` <", value, "state");
return (Criteria) this;
}
public Criteria andStateLessThanOrEqualTo(String value) {
addCriterion("`state` <=", value, "state");
return (Criteria) this;
}
public Criteria andStateLike(String value) {
addCriterion("`state` like", value, "state");
return (Criteria) this;
}
public Criteria andStateNotLike(String value) {
addCriterion("`state` not like", value, "state");
return (Criteria) this;
}
public Criteria andStateIn(List<String> values) {
addCriterion("`state` in", values, "state");
return (Criteria) this;
}
public Criteria andStateNotIn(List<String> values) {
addCriterion("`state` not in", values, "state");
return (Criteria) this;
}
public Criteria andStateBetween(String value1, String value2) {
addCriterion("`state` between", value1, value2, "state");
return (Criteria) this;
}
public Criteria andStateNotBetween(String value1, String value2) {
addCriterion("`state` not between", value1, value2, "state");
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 Criteria andUpdateUserIsNull() {
addCriterion("update_user is null");
return (Criteria) this;
}
public Criteria andUpdateUserIsNotNull() {
addCriterion("update_user is not null");
return (Criteria) this;
}
public Criteria andUpdateUserEqualTo(String value) {
addCriterion("update_user =", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotEqualTo(String value) {
addCriterion("update_user <>", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserGreaterThan(String value) {
addCriterion("update_user >", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserGreaterThanOrEqualTo(String value) {
addCriterion("update_user >=", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserLessThan(String value) {
addCriterion("update_user <", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserLessThanOrEqualTo(String value) {
addCriterion("update_user <=", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserLike(String value) {
addCriterion("update_user like", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotLike(String value) {
addCriterion("update_user not like", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserIn(List<String> values) {
addCriterion("update_user in", values, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotIn(List<String> values) {
addCriterion("update_user not in", values, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserBetween(String value1, String value2) {
addCriterion("update_user between", value1, value2, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotBetween(String value1, String value2) {
addCriterion("update_user not between", value1, value2, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateTimeIsNull() {
addCriterion("update_time is null");
return (Criteria) this;
}
public Criteria andUpdateTimeIsNotNull() {
addCriterion("update_time is not null");
return (Criteria) this;
}
public Criteria andUpdateTimeEqualTo(Long value) {
addCriterion("update_time =", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeNotEqualTo(Long value) {
addCriterion("update_time <>", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeGreaterThan(Long value) {
addCriterion("update_time >", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeGreaterThanOrEqualTo(Long value) {
addCriterion("update_time >=", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeLessThan(Long value) {
addCriterion("update_time <", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeLessThanOrEqualTo(Long value) {
addCriterion("update_time <=", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeIn(List<Long> values) {
addCriterion("update_time in", values, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeNotIn(List<Long> values) {
addCriterion("update_time not in", values, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeBetween(Long value1, Long value2) {
addCriterion("update_time between", value1, value2, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeNotBetween(Long value1, Long value2) {
addCriterion("update_time not between", value1, value2, "updateTime");
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,34 @@
package io.metersphere.functional.mapper;
import io.metersphere.functional.domain.ExportTask;
import io.metersphere.functional.domain.ExportTaskExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface ExportTaskMapper {
long countByExample(ExportTaskExample example);
int deleteByExample(ExportTaskExample example);
int deleteByPrimaryKey(String id);
int insert(ExportTask record);
int insertSelective(ExportTask record);
List<ExportTask> selectByExample(ExportTaskExample example);
ExportTask selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") ExportTask record, @Param("example") ExportTaskExample example);
int updateByExample(@Param("record") ExportTask record, @Param("example") ExportTaskExample example);
int updateByPrimaryKeySelective(ExportTask record);
int updateByPrimaryKey(ExportTask record);
int batchInsert(@Param("list") List<ExportTask> list);
int batchInsertSelective(@Param("list") List<ExportTask> list, @Param("selective") ExportTask.Column ... selective);
}

View File

@ -0,0 +1,328 @@
<?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.functional.mapper.ExportTaskMapper">
<resultMap id="BaseResultMap" type="io.metersphere.functional.domain.ExportTask">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="type" jdbcType="VARCHAR" property="type" />
<result column="fileId" jdbcType="VARCHAR" property="fileid" />
<result column="state" jdbcType="VARCHAR" property="state" />
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="update_user" jdbcType="VARCHAR" property="updateUser" />
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
</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, `name`, `type`, fileId, `state`, create_user, create_time, update_user, update_time
</sql>
<select id="selectByExample" parameterType="io.metersphere.functional.domain.ExportTaskExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from export_task
<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 export_task
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from export_task
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.functional.domain.ExportTaskExample">
delete from export_task
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.functional.domain.ExportTask">
insert into export_task (id, `name`, `type`,
fileId, `state`, create_user,
create_time, update_user, update_time
)
values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR},
#{fileid,jdbcType=VARCHAR}, #{state,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR},
#{createTime,jdbcType=BIGINT}, #{updateUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.functional.domain.ExportTask">
insert into export_task
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="name != null">
`name`,
</if>
<if test="type != null">
`type`,
</if>
<if test="fileid != null">
fileId,
</if>
<if test="state != null">
`state`,
</if>
<if test="createUser != null">
create_user,
</if>
<if test="createTime != null">
create_time,
</if>
<if test="updateUser != null">
update_user,
</if>
<if test="updateTime != null">
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="type != null">
#{type,jdbcType=VARCHAR},
</if>
<if test="fileid != null">
#{fileid,jdbcType=VARCHAR},
</if>
<if test="state != null">
#{state,jdbcType=VARCHAR},
</if>
<if test="createUser != null">
#{createUser,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
#{createTime,jdbcType=BIGINT},
</if>
<if test="updateUser != null">
#{updateUser,jdbcType=VARCHAR},
</if>
<if test="updateTime != null">
#{updateTime,jdbcType=BIGINT},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.functional.domain.ExportTaskExample" resultType="java.lang.Long">
select count(*) from export_task
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update export_task
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR},
</if>
<if test="record.name != null">
`name` = #{record.name,jdbcType=VARCHAR},
</if>
<if test="record.type != null">
`type` = #{record.type,jdbcType=VARCHAR},
</if>
<if test="record.fileid != null">
fileId = #{record.fileid,jdbcType=VARCHAR},
</if>
<if test="record.state != null">
`state` = #{record.state,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>
<if test="record.updateUser != null">
update_user = #{record.updateUser,jdbcType=VARCHAR},
</if>
<if test="record.updateTime != null">
update_time = #{record.updateTime,jdbcType=BIGINT},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update export_task
set id = #{record.id,jdbcType=VARCHAR},
`name` = #{record.name,jdbcType=VARCHAR},
`type` = #{record.type,jdbcType=VARCHAR},
fileId = #{record.fileid,jdbcType=VARCHAR},
`state` = #{record.state,jdbcType=VARCHAR},
create_user = #{record.createUser,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
update_user = #{record.updateUser,jdbcType=VARCHAR},
update_time = #{record.updateTime,jdbcType=BIGINT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.functional.domain.ExportTask">
update export_task
<set>
<if test="name != null">
`name` = #{name,jdbcType=VARCHAR},
</if>
<if test="type != null">
`type` = #{type,jdbcType=VARCHAR},
</if>
<if test="fileid != null">
fileId = #{fileid,jdbcType=VARCHAR},
</if>
<if test="state != null">
`state` = #{state,jdbcType=VARCHAR},
</if>
<if test="createUser != null">
create_user = #{createUser,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=BIGINT},
</if>
<if test="updateUser != null">
update_user = #{updateUser,jdbcType=VARCHAR},
</if>
<if test="updateTime != null">
update_time = #{updateTime,jdbcType=BIGINT},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.functional.domain.ExportTask">
update export_task
set `name` = #{name,jdbcType=VARCHAR},
`type` = #{type,jdbcType=VARCHAR},
fileId = #{fileid,jdbcType=VARCHAR},
`state` = #{state,jdbcType=VARCHAR},
create_user = #{createUser,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
update_user = #{updateUser,jdbcType=VARCHAR},
update_time = #{updateTime,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into export_task
(id, `name`, `type`, fileId, `state`, create_user, create_time, update_user, update_time
)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR}, #{item.type,jdbcType=VARCHAR},
#{item.fileid,jdbcType=VARCHAR}, #{item.state,jdbcType=VARCHAR}, #{item.createUser,jdbcType=VARCHAR},
#{item.createTime,jdbcType=BIGINT}, #{item.updateUser,jdbcType=VARCHAR}, #{item.updateTime,jdbcType=BIGINT}
)
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
insert into export_task (
<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="'name'.toString() == column.value">
#{item.name,jdbcType=VARCHAR}
</if>
<if test="'type'.toString() == column.value">
#{item.type,jdbcType=VARCHAR}
</if>
<if test="'fileId'.toString() == column.value">
#{item.fileid,jdbcType=VARCHAR}
</if>
<if test="'state'.toString() == column.value">
#{item.state,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>
<if test="'update_user'.toString() == column.value">
#{item.updateUser,jdbcType=VARCHAR}
</if>
<if test="'update_time'.toString() == column.value">
#{item.updateTime,jdbcType=BIGINT}
</if>
</foreach>
)
</foreach>
</insert>
</mapper>

View File

@ -7,6 +7,29 @@ ALTER TABLE api_test_case ADD ignore_api_diff BIT(1) DEFAULT 0 NOT NULL COMMENT
ALTER TABLE test_plan_report_bug MODIFY bug_handle_user VARCHAR(255) NULL COMMENT '缺陷处理人';
CREATE TABLE export_task(
`id` VARCHAR(50) NOT NULL COMMENT '任务唯一ID' ,
`name` VARCHAR(255) COMMENT '名称' ,
`type` VARCHAR(50) NOT NULL COMMENT '资源类型' ,
`fileId` VARCHAR(255) COMMENT '文件id' ,
`state` VARCHAR(50) NOT NULL COMMENT '状态' ,
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
`update_user` VARCHAR(50) NOT NULL COMMENT '创建人' ,
`update_time` BIGINT NOT NULL COMMENT '创建时间' ,
PRIMARY KEY (id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '导出任务';
CREATE INDEX idx_create_user ON export_task(`create_user`);
CREATE INDEX idx_state ON export_task(`state`);
CREATE INDEX idx_create_time ON export_task(`create_time`);
CREATE INDEX idx_type ON export_task(`type`);
CREATE INDEX idx_update_user ON export_task(`update_user`);
CREATE INDEX idx_update_time ON export_task(`update_time`);
-- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -2,6 +2,7 @@ package io.metersphere.sdk.constants;
public class KafkaTopicConstants {
public static final String PLUGIN = "PLUGIN";
public static final String EXPORT = "EXPORT";
// API TOPIC
public static final String API_REPORT_TOPIC = "API_REPORT_TOPIC";
public static final String API_REPORT_TASK_TOPIC = "API_REPORT_TASK_TOPIC";

View File

@ -3,8 +3,13 @@ package io.metersphere.sdk.util;
import io.metersphere.sdk.exception.MSException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
public class MsFileUtils {
public static void validateFileName(String... fileNames) {
@ -21,4 +26,38 @@ public class MsFileUtils {
File file = new File(path);
FileUtils.deleteDirectory(file);
}
/**
* 获取流文件
*/
private static void inputStreamToFile(InputStream ins, File file) {
try (OutputStream os = new FileOutputStream(file);) {
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
} catch (Exception e) {
LogUtils.error(e);
}
}
/**
* MultipartFile File
*
* @param file
= */
public static File multipartFileToFile(MultipartFile file) {
if (file != null && file.getSize() > 0) {
try (InputStream ins = file.getInputStream()) {
validateFileName(file.getOriginalFilename());
File toFile = new File(Objects.requireNonNull(file.getOriginalFilename()));
inputStreamToFile(ins, toFile);
return toFile;
} catch (Exception e) {
LogUtils.error(e);
}
}
return null;
}
}

View File

@ -1,11 +1,20 @@
package io.metersphere.sdk.util;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.StreamReadConstraints;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
@ -22,6 +31,29 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class XMLUtils {
private static final ObjectMapper objectMapper = JsonMapper.builder()
.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS)
.build();
public static final int DEFAULT_MAX_STRING_LEN = Integer.MAX_VALUE;
static {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 自动检测所有类的全部属性
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
// 如果一个对象中没有任何的属性那么在序列化的时候就会报错
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
// 使用BigDecimal来序列化
objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
// 设置JSON处理字符长度限制
objectMapper.getFactory()
.setStreamReadConstraints(StreamReadConstraints.builder().maxStringLength(DEFAULT_MAX_STRING_LEN).build());
// 处理时间格式
objectMapper.registerModule(new JavaTimeModule());
}
public static final boolean IS_TRANS = false;
public static Document getDocument(InputStream source) throws DocumentException {
@ -173,4 +205,91 @@ public class XMLUtils {
matcher.appendTail(result);
return result.toString();
}
public static String delXmlHeader(String xml) {
int begin = xml.indexOf("?>");
if (begin != -1) {
if (begin + 2 >= xml.length()) {
return null;
}
xml = xml.substring(begin + 2);
} // <?xml version="1.0" encoding="utf-8"?> 若存在则去除
String rgex = ">";
Pattern pattern = Pattern.compile(rgex);
Matcher m = pattern.matcher(xml);
xml = m.replaceAll("> ");
rgex = "\\s*</";
pattern = Pattern.compile(rgex);
m = pattern.matcher(xml);
xml = m.replaceAll(" </");
return xml;
}
// 传入完整的 xml 文本转换成 json 对象
public static JsonNode xmlConvertJson(String xml) {
if (StringUtils.isBlank(xml)) return null;
xml = delXmlHeader(xml);
if (xml == null) return null;
try {
if (stringToDocument(xml) == null) {
LogUtils.error("xml内容转换失败");
return null;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
Element node = null;
try {
node = stringToDocument(xml).getRootElement();
} catch (Exception e) {
throw new RuntimeException(e);
}
return getJsonObjectByDC(node);
}
private static JsonNode getJsonObjectByDC(Element node) {
ObjectNode result = objectMapper.createObjectNode();;
List<Element> listElement = node.elements();// 所有一级子节点的list
if (!listElement.isEmpty()) {
List<JsonNode> list = new LinkedList<>();
for (Element e : listElement) {// 遍历所有一级子节点
JsonNode jsonObject = getJsonObjectByDC(e);
//加xml标签上的属性 eg: <field length="2" scale="0" type="string">RB</field>
//这里添加 length scale type
if (!e.attributes().isEmpty()) {
ObjectNode attributeJson = objectMapper.createObjectNode();;
for (Attribute attribute : e.attributes()) {
try {
attributeJson.putIfAbsent(attribute.getName(), objectMapper.readTree(attribute.getValue()));
} catch (JsonProcessingException ex) {
throw new RuntimeException(ex);
}
}
ObjectNode jsonObjectNode = (ObjectNode) jsonObject;
jsonObjectNode.putIfAbsent("attribute", attributeJson);
}
list.add(jsonObject);
}
if (list.size() == 1) {
result.putIfAbsent(node.getName(), list.get(0));
} else {
try {
String s = objectMapper.writeValueAsString(list);
result.putIfAbsent(node.getName(), objectMapper.readTree(s));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
} else {
if (!StringUtils.isAllBlank(node.getName(), node.getText())) {
try {
result.putIfAbsent(node.getName(), objectMapper.readTree(node.getText()));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
return result;
}
}

View File

@ -173,5 +173,9 @@ case.minder.all.case=全部用例
case.minder.status.success=成功
case.minder.status.error=失败
case.minder.status.blocked=阻塞
#import
case.find_file_error=找不到该文件
export_case_task_stop=停止导出
export_case_task_existed=已有导出任务

View File

@ -257,6 +257,9 @@ case.execute.status.pending=Pending
functional_case_comment_template=【评论:%s%s】\n%s\n
functional_case_execute_comment_template=[Execute comment%s %s%s]\n%s\n
functional_case_review_comment_template=[Review comment%s %s%s]\n%s\n
#import
case.find_file_error=The file cannot be found
functional_case_xmind_template=Functional case xmind template
download_template_failed=Download template failed
functional_case=Functional case
@ -279,3 +282,6 @@ case.export.system.other.columns.create_user=Create user
case.export.system.other.columns.create_time=Create time
case.export.system.other.columns.update_user=Update user
case.export.system.other.columns.update_time=Update time
export_case_task_stop=Stop export
export_case_task_existed=Export task already exists

View File

@ -255,6 +255,8 @@ case.execute.status.pending=未执行
functional_case_comment_template=【评论:%s%s】\n%s\n
functional_case_execute_comment_template=【执行评论:%s %s%s】\n%s\n
functional_case_review_comment_template=【评审评论:%s %s%s】\n%s\n
#import
case.find_file_error=找不到该文件
functional_case_xmind_template=思维导图用例模版
download_template_failed=下载思维导图模版失败
functional_case=功能用例
@ -277,3 +279,6 @@ case.export.system.other.columns.create_user=创建人
case.export.system.other.columns.create_time=创建时间
case.export.system.other.columns.update_user=更新人
case.export.system.other.columns.update_time=更新时间
export_case_task_stop=停止导出
export_case_task_existed=已有导出任务

View File

@ -266,6 +266,11 @@ xmind_textDescription=文本描述
xmind_expectedResult=預期結果
xmind_step=用例步驟
xmind_stepDescription=步驟描述
#import
case.find_file_error=找不到該文件
# case export columns
case.export.system.columns.name=用例名稱
case.export.system.columns.id=ID
@ -278,3 +283,6 @@ case.export.system.other.columns.create_user=創建人
case.export.system.other.columns.create_time=創建時間
case.export.system.other.columns.update_user=更新人
case.export.system.other.columns.update_time=更新時間
export_case_task_stop=停止導出
export_case_task_existed=已有導出任務

View File

@ -255,7 +255,15 @@ public class FunctionalCaseController {
@Operation(summary = "用例管理-功能用例-excel导出")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_EXPORT)
public void testCaseExport(@Validated @RequestBody FunctionalCaseExportRequest request) {
functionalCaseFileService.exportFunctionalCaseZip(request);
functionalCaseFileService.export(SessionUtils.getUserId(), request);
}
@GetMapping("/stop/{projectId}")
@Operation(summary = "用例管理-功能用例-导出-停止导出")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_EXPORT)
@CheckOwner(resourceId = "#projectId", resourceType = "project")
public void caseStopExport(@PathVariable String projectId) {
functionalCaseFileService.stopExport(projectId, SessionUtils.getUserId());
}
@GetMapping("/download/xmind/template/{projectId}")

View File

@ -22,6 +22,7 @@ import io.metersphere.functional.excel.listener.FunctionalCaseImportEventListene
import io.metersphere.functional.excel.listener.FunctionalCasePretreatmentListener;
import io.metersphere.functional.excel.validate.AbstractCustomFieldValidator;
import io.metersphere.functional.excel.validate.CustomFieldValidatorFactory;
import io.metersphere.functional.mapper.ExportTaskMapper;
import io.metersphere.functional.mapper.ExtFunctionalCaseCommentMapper;
import io.metersphere.functional.request.FunctionalCaseExportRequest;
import io.metersphere.functional.request.FunctionalCaseImportRequest;
@ -36,6 +37,7 @@ import io.metersphere.sdk.dto.SocketMsgDTO;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.*;
import io.metersphere.system.constants.ExportConstants;
import io.metersphere.system.domain.CustomFieldOption;
import io.metersphere.system.domain.SystemParameter;
import io.metersphere.system.dto.sdk.BaseTreeNode;
@ -43,6 +45,7 @@ import io.metersphere.system.dto.sdk.SessionUser;
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.excel.utils.EasyExcelExporter;
import io.metersphere.system.manager.ExportTaskManager;
import io.metersphere.system.mapper.SystemParameterMapper;
import io.metersphere.system.service.FileService;
import io.metersphere.system.uid.IDGenerator;
@ -57,7 +60,6 @@ import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@ -103,6 +105,10 @@ public class FunctionalCaseFileService {
@Resource
private SystemParameterMapper systemParameterMapper;
private static final String EXPORT_FILE_NAME = "case_export";
@Resource
private ExportTaskManager exportTaskManager;
@Resource
private ExportTaskMapper exportTaskMapper;
/**
* 下载excel导入模板
@ -327,15 +333,28 @@ public class FunctionalCaseFileService {
}
}
public void export(String userId, FunctionalCaseExportRequest request){
try {
ExportTaskExample exportTaskExample = new ExportTaskExample();
exportTaskExample.createCriteria().andTypeEqualTo(ExportConstants.ExportType.CASE.toString()).andStateEqualTo(ExportConstants.ExportState.PREPARED.toString());
long preparedCount = exportTaskMapper.countByExample(exportTaskExample);
if (preparedCount>0) {
throw new MSException(Translator.get("export_case_task_existed"));
}
exportTaskManager.exportAsyncTask(userId, ExportConstants.ExportType.CASE.toString(), request, t->exportFunctionalCaseZip(request));
} catch (InterruptedException e) {
LogUtils.error("导出失败:"+e);
throw new MSException(e);
}
}
/**
* 导出excel
*
* @param request
* @param url
*/
@Async
public void exportFunctionalCaseZip(FunctionalCaseExportRequest request) {
public String exportFunctionalCaseZip(FunctionalCaseExportRequest request) {
File tmpDir = null;
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
try {
@ -356,14 +375,39 @@ public class FunctionalCaseFileService {
uploadFileToMinio(singeFile, request.getFileId());
}
functionalCaseLogService.exportExcelLog(request);
SocketMsgDTO socketMsgDTO = new SocketMsgDTO(request.getFileId(), "", MsgType.CONNECT.name(), MsgType.CONNECT.name());
List<ExportTask> exportTasks = getExportTasks();
String taskId;
if (CollectionUtils.isNotEmpty(exportTasks)) {
taskId = exportTasks.getFirst().getId();
updateExportTask(ExportConstants.ExportState.SUCCESS.toString(), taskId);
} else {
taskId = MsgType.CONNECT.name();
}
SocketMsgDTO socketMsgDTO = new SocketMsgDTO(request.getFileId(), "", MsgType.CONNECT.name(), taskId);
socketMsgDTO.setReportId(request.getFileId());
ExportWebSocketHandler.sendMessageSingle(socketMsgDTO);
} catch (Exception e) {
List<ExportTask> exportTasks = getExportTasks();
if (CollectionUtils.isNotEmpty(exportTasks)) {
updateExportTask(ExportConstants.ExportState.SUCCESS.toString(), exportTasks.getFirst().getId());
}
LogUtils.error(e);
throw new MSException(e);
}
return null;
}
private List<ExportTask> getExportTasks() {
ExportTaskExample exportTaskExample = new ExportTaskExample();
exportTaskExample.createCriteria().andTypeEqualTo(ExportConstants.ExportType.CASE.toString()).andStateEqualTo(ExportConstants.ExportState.PREPARED.toString());
return exportTaskMapper.selectByExample(exportTaskExample);
}
private void updateExportTask(String state, String taskId) {
ExportTask exportTask = new ExportTask();
exportTask.setState(state);
exportTask.setId(taskId);
exportTaskMapper.updateByPrimaryKey(exportTask);
}
private void uploadFileToMinio(File file, String fileId) {
@ -757,4 +801,8 @@ public class FunctionalCaseFileService {
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + "Metersphere_case_" + project.getName() + "\"")
.body(bytes);
}
public void stopExport(String projectId, String userId) {
exportTaskManager.sendStopMessage(projectId, userId);
}
}

View File

@ -0,0 +1,130 @@
package io.metersphere.functional.xmind.parser;
import com.fasterxml.jackson.databind.JsonNode;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.XMLUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class XMindLegacy {
/**
* 返回content.xml和comments.xml合并后的json
*
*/
public static List<String> getContent(String xmlContent, String xmlComments) throws IOException, DocumentException {
// 删除content.xml里面不能识别的字符串
xmlContent = xmlContent.replace("xmlns=\"urn:xmind:xmap:xmlns:content:2.0\"", StringUtils.EMPTY);
xmlContent = xmlContent.replace("xmlns:fo=\"http://www.w3.org/1999/XSL/Format\"", StringUtils.EMPTY);
try {
xmlContent = removeTopicsFromString(xmlContent);
} catch (Exception e) {
LogUtils.error("移除xml中的Topic出错", e);
}
// 去除title中svg:width属性
xmlContent = xmlContent.replaceAll("<title svg:width=\"[0-9]*\">", "<title>");
Document document = DocumentHelper.parseText(xmlContent);// 读取XML文件,获得document对象
Element root = document.getRootElement();
List<Node> topics = root.selectNodes("//topic");
if (xmlComments != null) {
// 删除comments.xml里面不能识别的字符串
xmlComments = xmlComments.replace("xmlns=\"urn:xmind:xmap:xmlns:comments:2.0\"", StringUtils.EMPTY);
// 添加评论到content中
Document commentDocument = DocumentHelper.parseText(xmlComments);
List<Node> commentsList = commentDocument.selectNodes("//comment");
for (Node topic : topics) {
for (Node commentNode : commentsList) {
Element commentElement = (Element) commentNode;
Element topicElement = (Element) topic;
if (topicElement.attribute("id").getValue()
.equals(commentElement.attribute("object-id").getValue())) {
Element comment = topicElement.addElement("comments");
comment.addAttribute("creationTime", commentElement.attribute("time").getValue());
comment.addAttribute("author", commentElement.attribute("author").getValue());
comment.addAttribute("content", commentElement.element("content").getText());
}
}
}
}
// 第一个topic转换为json中的rootTopic
List<Node> rootTopics = root.selectNodes("/xmap-content/sheet/topic");
for (Node rootTopic : rootTopics) {
rootTopic.setName("rootTopic");
// 将xml中topic节点转换为attached节点
List<Node> topicList = rootTopic.selectNodes("//topic");
for (Node node : topicList) {
node.setName("attached");
}
}
List<String> sheets = new ArrayList<>();
for (Element sheet : root.elements("sheet")) {
String res = sheet.asXML();
// 将xml转为json
JsonNode xmlJSONObj = XMLUtils.xmlConvertJson(res);
JsonNode jsonNode = xmlJSONObj.get("sheet");
sheets.add(jsonNode.toString());
}
// 设置缩进
return sheets;
}
/**
* 删除topics节点
*
*/
private static String removeTopicsFromString(String xmlContent) throws Exception {
Document doc = DocumentHelper.parseText(xmlContent);
if (doc != null) {
Element root = doc.getRootElement();
List<Element> childrenElement = root.elements();
for (Element child : childrenElement) {
removeTopicsFromElement(child);
}
xmlContent = doc.asXML();
}
return xmlContent;
}
/**
* 递归删除topics节点
*
*/
private static void removeTopicsFromElement(Element element) {
if (element != null) {
List<Element> childrenElement = element.elements();
List<Element> removeElements = new ArrayList<>();
List<Element> addElements = new ArrayList<>();
for (Element child : childrenElement) {
if (StringUtils.equalsIgnoreCase("topics", child.getName()) && StringUtils.equalsAnyIgnoreCase(child.attributeValue("type"), "attached", "detached")) {
removeElements.add(child);
addElements.addAll(child.elements());
}
}
removeElements.forEach(item -> {
item.getParent().remove(item);
});
addElements.forEach(item -> {
item.setParent(null);
element.add(item);
});
childrenElement = element.elements();
for (Element child : childrenElement) {
removeTopicsFromElement(child);
}
}
}
}

View File

@ -0,0 +1,125 @@
package io.metersphere.functional.xmind.parser;
import io.metersphere.functional.xmind.pojo.JsonRootBean;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.MsFileUtils;
import io.metersphere.sdk.util.Translator;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.io.FileUtils;
import org.dom4j.DocumentException;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @Description 解析主体
*/
public class XMindParser {
public static final String CONTENT_JSON = "content.json";
public static final String CONTENT_XML = "content.xml";
public static final String COMMENTS_XML = "comments.xml";
/**
* 解析脑图文件返回content整合后的内容
*
*/
public static List<String> parseJson(MultipartFile multipartFile) throws IOException {
File file = MsFileUtils.multipartFileToFile(multipartFile);
List<String> contents;
String res = null;
if (file == null || !file.exists()) {
throw new MSException (Translator.get("incorrect_format"));
}
try {
res = ZipUtils.extract(file);
if (isXMindZen(res)) {
contents = (getXMindZenContent(res));
} else {
contents = getXMindLegacyContent(res);
}
} catch (Exception e) {
throw new MSException (e.getMessage());
} finally {
// 删除生成的文件夹
if (res != null) {
File dir = new File(res);
FileUtils.deleteDirectory(dir);
}
// 删除零时文件
if (file != null) {
file.delete();
}
}
return contents;
}
public static List<JsonRootBean> parseObject(MultipartFile multipartFile) throws DocumentException, ArchiveException, IOException {
List<String> contents = parseJson(multipartFile);
int caseCount = 0;
List<JsonRootBean> jsonRootBeans = new ArrayList<>();
if (contents != null) {
for (String content : contents) {
caseCount += content.split("(case-:)").length;
JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class);
jsonRootBeans.add(jsonRootBean);
}
if (caseCount > 800) {
throw new MSException (Translator.get("import_xmind_count_error"));
}
}
return jsonRootBeans;
}
/**
* 解析xmind zen 格式的文件
* @param extractFileDir 解压后的文件夹名字
*/
public static List<String> getXMindZenContent(String extractFileDir)
throws IOException {
List<String> keys = new ArrayList<>();
keys.add(CONTENT_JSON);
Map<String, String> map = ZipUtils.getContents(keys, extractFileDir);
String content = map.get(CONTENT_JSON);
return XMindZen.getContent(content);
}
/**
* 解析正常xmind 格式的文件
* @param extractFileDir 解压后的文件夹名字
*/
public static List<String> getXMindLegacyContent(String extractFileDir)
throws IOException, DocumentException {
List<String> keys = new ArrayList<>();
keys.add(CONTENT_XML);
keys.add(COMMENTS_XML);
Map<String, String> map = ZipUtils.getContents(keys, extractFileDir);
String contentXml = map.get(CONTENT_XML);
String commentsXml = map.get(COMMENTS_XML);
return XMindLegacy.getContent(contentXml, commentsXml);
}
private static boolean isXMindZen(String res){
// 解压
File parent = new File(res);
if (parent.isDirectory()) {
String[] files = parent.list(new ZipUtils.FileFilter());
for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
if (files[i].equals(CONTENT_JSON)) {
return true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,66 @@
package io.metersphere.functional.xmind.parser;
import io.metersphere.sdk.util.JSON;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class XMindZen {
/**
* 返回content.json 解析后的的json
*/
public static List<String> getContent(String jsonContent) {
List jsonArray = JSON.parseArray(jsonContent);//.getJSONObject(0);
List<String> contents = new ArrayList<>();
for (Object object : jsonArray) {
Map<String, Map> jsonObject = (Map) object;
Map<String, Map> rootTopic = jsonObject.get("rootTopic");
transferNotes(rootTopic);
Map children = rootTopic.get("children");
recursionChildren(children);
contents.add(JSON.toJSONString(jsonObject));
}
return contents;
}
/**
* 递归转换children
*
*/
private static void recursionChildren(Map<String, List> children) {
if (children == null) {
return;
}
List<Map> attachedArray = children.get("attached");
if (attachedArray == null) {
return;
}
for (Object attached : attachedArray) {
Map<String, Map> attachedObject = (Map) attached;
transferNotes(attachedObject);
Map<String, List> childrenObject = attachedObject.get("children");
if (childrenObject == null) {
continue;
}
recursionChildren(childrenObject);
}
}
private static void transferNotes(Map object) {
Map notes = (Map) object.get("notes");
if (notes == null) {
return;
}
Map plain = (Map) notes.get("plain");
if (plain != null) {
String content = plain.get("content").toString();
notes.remove("plain");
notes.put("content", content);
} else {
notes.put("content", null);
}
}
}

View File

@ -0,0 +1,84 @@
package io.metersphere.functional.xmind.parser;
import io.metersphere.sdk.util.Translator;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.examples.Expander;
import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @Description zip解压工具
*/
public class ZipUtils {
private static final String CURRENT_PATH = System.getProperty("user.dir");
/**
* 找到压缩文件中匹配的子文件返回的为 getContents("comments.xml, unzip
*
*/
public static Map<String, String> getContents(List<String> subFileNames, String extractFileDir)
throws IOException {
Map<String, String> map = new HashMap<>(16);
File destFile = new File(extractFileDir);
if (destFile.isDirectory()) {
String[] res = destFile.list(new FileFilter());
for (int i = 0; i < Objects.requireNonNull(res).length; i++) {
if (subFileNames.contains(res[i])) {
String s = extractFileDir + File.separator + res[i];
String content = getFileContent(s);
map.put(res[i], content);
}
}
}
return map;
}
/**
* 返回解压后的文件夹名字
*
*/
public static String extract(File file) throws IOException, ArchiveException {
Expander expander = new Expander();
String destFileName = CURRENT_PATH + File.separator + "XMind" + System.currentTimeMillis();
expander.expand(file, new File(destFileName));
return destFileName;
}
/**
* 这是一个内部类过滤器,策略模式
*/
static class FileFilter implements FilenameFilter {
@Override
public boolean accept(File dir, String name) {
// String的 endsWith(String str)方法 筛选出以str结尾的字符串
return name.endsWith(".xml") || name.endsWith(".json");
}
}
public static String getFileContent(String fileName) throws IOException {
File file;
try {
file = new File(fileName);
} catch (Exception e) {
throw new RuntimeException(Translator.get("case.find_file_error"));
}
FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader);
StringBuilder stringBuffer = new StringBuilder();
while (bufferedReader.ready()) {
if(!stringBuffer.isEmpty()){
stringBuffer.append("\r\n");
}
stringBuffer.append(bufferedReader.readLine());
}
// 打开的文件需关闭在unix下可以删除否则在windows下不能删除file.delete())
bufferedReader.close();
fileReader.close();
return stringBuffer.toString();
}
}

View File

@ -0,0 +1,22 @@
package io.metersphere.functional.xmind.pojo;
import lombok.Data;
import java.util.List;
/**
* XMind 节点对象
*/
@Data
public class Attached {
private String id;
private String title;
private Notes notes;
private String path;
private Attached parent;
private List<Comments> comments;
private Children children;
}

View File

@ -0,0 +1,12 @@
package io.metersphere.functional.xmind.pojo;
import lombok.Data;
import java.util.List;
@Data
public class Children {
private List<Attached> attached;
}

View File

@ -0,0 +1,12 @@
package io.metersphere.functional.xmind.pojo;
import lombok.Data;
@Data
public class Comments {
private long creationTime;
private String author;
private String content;
}

View File

@ -0,0 +1,12 @@
package io.metersphere.functional.xmind.pojo;
import lombok.Data;
@Data
public class JsonRootBean {
private String id;
private String title;
private RootTopic rootTopic;
}

View File

@ -0,0 +1,10 @@
package io.metersphere.functional.xmind.pojo;
import lombok.Data;
@Data
public class Notes {
private String content;
}

View File

@ -0,0 +1,16 @@
package io.metersphere.functional.xmind.pojo;
import lombok.Data;
import java.util.List;
@Data
public class RootTopic {
private String id;
private String title;
private Notes notes;
private List<Comments> comments;
private Children children;
}

View File

@ -90,6 +90,7 @@ public class FunctionalCaseControllerTests extends BaseTest {
public static final String DOWNLOAD_XMIND_TEMPLATE_URL = "/functional/case/download/xmind/template/";
public static final String EXPORT_COLUMNS_URL = "/functional/case/export/columns/";
public static final String DOWNLOAD_FILE_URL = "/functional/case/download/file/";
public static final String STOP_EXPORT_URL = "/functional/case/stop/";
@Resource
private NotificationMapper notificationMapper;
@ -858,4 +859,10 @@ public class FunctionalCaseControllerTests extends BaseTest {
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken));
}
@Test
@Order(25)
public void stopExport() throws Exception {
this.requestGetExcel(STOP_EXPORT_URL + DEFAULT_PROJECT_ID);
}
}

View File

@ -0,0 +1,12 @@
package io.metersphere.system.constants;
public class ExportConstants {
public enum ExportType {
API, CASE
}
public enum ExportState {
PREPARED, STOP, SUCCESS, ERROR
}
}

View File

@ -0,0 +1,94 @@
package io.metersphere.system.manager;
import io.metersphere.functional.domain.ExportTask;
import io.metersphere.functional.mapper.ExportTaskMapper;
import io.metersphere.sdk.constants.KafkaTopicConstants;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.constants.ExportConstants;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Function;
@Service
public class ExportTaskManager {
@Resource
private KafkaTemplate<String, String> kafkaTemplate;
@Resource
private ExportTaskMapper exportTaskMapper;
public static Map<String, Future<?>> map = new ConcurrentHashMap<>();
public static final String EXPORT_CONSUME = "export_consume";
public <T> void exportAsyncTask(String userId, String type, T t, Function<Object, Object> selectListFunc) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<?> future = executorService.submit(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 线程任务逻辑
LogUtils.info("Thread has been start.");
selectListFunc.apply(t);
}
LogUtils.info("Thread has been interrupted.");
});
Thread.sleep(6000);
ExportTask exportTask = buildExportTask(userId, type);
map.put(exportTask.getId(), future);
}
private ExportTask buildExportTask(String userId, String type) {
ExportTask exportTask = new ExportTask();
exportTask.setId(IDGenerator.nextStr());
exportTask.setType(type);
exportTask.setCreateUser(userId);
exportTask.setCreateTime(System.currentTimeMillis());
exportTask.setState(ExportConstants.ExportState.PREPARED.toString());
exportTask.setUpdateUser(userId);
exportTask.setUpdateTime(System.currentTimeMillis());
exportTaskMapper.insert(exportTask);
return exportTask;
}
public void sendStopMessage(String id, String userId) {
ExportTask exportTask = new ExportTask();
exportTask.setId(id);
exportTask.setState(ExportConstants.ExportState.STOP.toString());
exportTask.setUpdateUser(userId);
exportTask.setUpdateTime(System.currentTimeMillis());
kafkaTemplate.send(KafkaTopicConstants.EXPORT, JSON.toJSONString(exportTask));
}
@KafkaListener(id=EXPORT_CONSUME, topics = KafkaTopicConstants.EXPORT, groupId = EXPORT_CONSUME + "_" + "${random.uuid}")
public void stop(ConsumerRecord<?, String> record) {
LogUtils.info("Service consume platform_plugin message: " + record.value());
ExportTask exportTask = JSON.parseObject(record.value(), ExportTask.class);
if (exportTask!=null && StringUtils.isNotBlank(exportTask.getId())) {
String id = exportTask.getId();
map.get(id).cancel(true);
map.remove(id);
exportTaskMapper.updateByPrimaryKey(exportTask);
}
}
@Scheduled(fixedDelay = 10000)
public void checkStop() {
for (String next : map.keySet()) {
if (map.get(next) != null && map.get(next).isDone()) {
map.remove(next);
}
}
}
}