feat(缺陷管理): 补充缺陷导出及变更历史查询功能

This commit is contained in:
song-cc-rock 2024-01-23 19:12:48 +08:00 committed by 刘瑞斌
parent 4305aa9add
commit 3118d9d764
58 changed files with 1082 additions and 1501 deletions

View File

@ -83,6 +83,10 @@ public class Bug implements Serializable {
@NotNull(message = "{bug.deleted.not_blank}", groups = {Created.class}) @NotNull(message = "{bug.deleted.not_blank}", groups = {Created.class})
private Boolean deleted; private Boolean deleted;
@Schema(description = "自定义排序间隔5000", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{bug.pos.not_blank}", groups = {Created.class})
private Long pos;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public enum Column { public enum Column {
@ -103,7 +107,8 @@ public class Bug implements Serializable {
platformBugId("platform_bug_id", "platformBugId", "VARCHAR", false), platformBugId("platform_bug_id", "platformBugId", "VARCHAR", false),
deleteUser("delete_user", "deleteUser", "VARCHAR", false), deleteUser("delete_user", "deleteUser", "VARCHAR", false),
deleteTime("delete_time", "deleteTime", "BIGINT", false), deleteTime("delete_time", "deleteTime", "BIGINT", false),
deleted("deleted", "deleted", "BIT", false); deleted("deleted", "deleted", "BIT", false),
pos("pos", "pos", "BIGINT", false);
private static final String BEGINNING_DELIMITER = "`"; private static final String BEGINNING_DELIMITER = "`";

View File

@ -1347,6 +1347,66 @@ public class BugExample {
addCriterion("deleted not between", value1, value2, "deleted"); addCriterion("deleted not between", value1, value2, "deleted");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andPosIsNull() {
addCriterion("pos is null");
return (Criteria) this;
}
public Criteria andPosIsNotNull() {
addCriterion("pos is not null");
return (Criteria) this;
}
public Criteria andPosEqualTo(Long value) {
addCriterion("pos =", value, "pos");
return (Criteria) this;
}
public Criteria andPosNotEqualTo(Long value) {
addCriterion("pos <>", value, "pos");
return (Criteria) this;
}
public Criteria andPosGreaterThan(Long value) {
addCriterion("pos >", value, "pos");
return (Criteria) this;
}
public Criteria andPosGreaterThanOrEqualTo(Long value) {
addCriterion("pos >=", value, "pos");
return (Criteria) this;
}
public Criteria andPosLessThan(Long value) {
addCriterion("pos <", value, "pos");
return (Criteria) this;
}
public Criteria andPosLessThanOrEqualTo(Long value) {
addCriterion("pos <=", value, "pos");
return (Criteria) this;
}
public Criteria andPosIn(List<Long> values) {
addCriterion("pos in", values, "pos");
return (Criteria) this;
}
public Criteria andPosNotIn(List<Long> values) {
addCriterion("pos not in", values, "pos");
return (Criteria) this;
}
public Criteria andPosBetween(Long value1, Long value2) {
addCriterion("pos between", value1, value2, "pos");
return (Criteria) this;
}
public Criteria andPosNotBetween(Long value1, Long value2) {
addCriterion("pos not between", value1, value2, "pos");
return (Criteria) this;
}
} }
public static class Criteria extends GeneratedCriteria { public static class Criteria extends GeneratedCriteria {

View File

@ -1,125 +0,0 @@
package io.metersphere.bug.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 BugHistory implements Serializable {
@Schema(description = "变更记录ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug_history.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{bug_history.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(description = "所属缺陷ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug_history.bug_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{bug_history.bug_id.length_range}", groups = {Created.class, Updated.class})
private String bugId;
@Schema(description = "变更记录批次号")
private Integer num;
@Schema(description = "变更类型; IMPORT/EDIT/ROLLBACK")
private String type;
@Schema(description = "回退来源")
private String rollbackSourceId;
@Schema(description = "操作人")
private String createUser;
@Schema(description = "操作时间")
private Long createTime;
@Schema(description = "修改内容", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{bug_history.content.not_blank}", groups = {Created.class})
private byte[] content;
private static final long serialVersionUID = 1L;
public enum Column {
id("id", "id", "VARCHAR", false),
bugId("bug_id", "bugId", "VARCHAR", false),
num("num", "num", "INTEGER", false),
type("type", "type", "VARCHAR", true),
rollbackSourceId("rollback_source_id", "rollbackSourceId", "VARCHAR", false),
createUser("create_user", "createUser", "VARCHAR", false),
createTime("create_time", "createTime", "BIGINT", false),
content("content", "content", "LONGVARBINARY", 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

@ -1,670 +0,0 @@
package io.metersphere.bug.domain;
import java.util.ArrayList;
import java.util.List;
public class BugHistoryExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public BugHistoryExample() {
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 andBugIdIsNull() {
addCriterion("bug_id is null");
return (Criteria) this;
}
public Criteria andBugIdIsNotNull() {
addCriterion("bug_id is not null");
return (Criteria) this;
}
public Criteria andBugIdEqualTo(String value) {
addCriterion("bug_id =", value, "bugId");
return (Criteria) this;
}
public Criteria andBugIdNotEqualTo(String value) {
addCriterion("bug_id <>", value, "bugId");
return (Criteria) this;
}
public Criteria andBugIdGreaterThan(String value) {
addCriterion("bug_id >", value, "bugId");
return (Criteria) this;
}
public Criteria andBugIdGreaterThanOrEqualTo(String value) {
addCriterion("bug_id >=", value, "bugId");
return (Criteria) this;
}
public Criteria andBugIdLessThan(String value) {
addCriterion("bug_id <", value, "bugId");
return (Criteria) this;
}
public Criteria andBugIdLessThanOrEqualTo(String value) {
addCriterion("bug_id <=", value, "bugId");
return (Criteria) this;
}
public Criteria andBugIdLike(String value) {
addCriterion("bug_id like", value, "bugId");
return (Criteria) this;
}
public Criteria andBugIdNotLike(String value) {
addCriterion("bug_id not like", value, "bugId");
return (Criteria) this;
}
public Criteria andBugIdIn(List<String> values) {
addCriterion("bug_id in", values, "bugId");
return (Criteria) this;
}
public Criteria andBugIdNotIn(List<String> values) {
addCriterion("bug_id not in", values, "bugId");
return (Criteria) this;
}
public Criteria andBugIdBetween(String value1, String value2) {
addCriterion("bug_id between", value1, value2, "bugId");
return (Criteria) this;
}
public Criteria andBugIdNotBetween(String value1, String value2) {
addCriterion("bug_id not between", value1, value2, "bugId");
return (Criteria) this;
}
public Criteria andNumIsNull() {
addCriterion("num is null");
return (Criteria) this;
}
public Criteria andNumIsNotNull() {
addCriterion("num is not null");
return (Criteria) this;
}
public Criteria andNumEqualTo(Integer value) {
addCriterion("num =", value, "num");
return (Criteria) this;
}
public Criteria andNumNotEqualTo(Integer value) {
addCriterion("num <>", value, "num");
return (Criteria) this;
}
public Criteria andNumGreaterThan(Integer value) {
addCriterion("num >", value, "num");
return (Criteria) this;
}
public Criteria andNumGreaterThanOrEqualTo(Integer value) {
addCriterion("num >=", value, "num");
return (Criteria) this;
}
public Criteria andNumLessThan(Integer value) {
addCriterion("num <", value, "num");
return (Criteria) this;
}
public Criteria andNumLessThanOrEqualTo(Integer value) {
addCriterion("num <=", value, "num");
return (Criteria) this;
}
public Criteria andNumIn(List<Integer> values) {
addCriterion("num in", values, "num");
return (Criteria) this;
}
public Criteria andNumNotIn(List<Integer> values) {
addCriterion("num not in", values, "num");
return (Criteria) this;
}
public Criteria andNumBetween(Integer value1, Integer value2) {
addCriterion("num between", value1, value2, "num");
return (Criteria) this;
}
public Criteria andNumNotBetween(Integer value1, Integer value2) {
addCriterion("num not between", value1, value2, "num");
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 andRollbackSourceIdIsNull() {
addCriterion("rollback_source_id is null");
return (Criteria) this;
}
public Criteria andRollbackSourceIdIsNotNull() {
addCriterion("rollback_source_id is not null");
return (Criteria) this;
}
public Criteria andRollbackSourceIdEqualTo(String value) {
addCriterion("rollback_source_id =", value, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdNotEqualTo(String value) {
addCriterion("rollback_source_id <>", value, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdGreaterThan(String value) {
addCriterion("rollback_source_id >", value, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdGreaterThanOrEqualTo(String value) {
addCriterion("rollback_source_id >=", value, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdLessThan(String value) {
addCriterion("rollback_source_id <", value, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdLessThanOrEqualTo(String value) {
addCriterion("rollback_source_id <=", value, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdLike(String value) {
addCriterion("rollback_source_id like", value, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdNotLike(String value) {
addCriterion("rollback_source_id not like", value, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdIn(List<String> values) {
addCriterion("rollback_source_id in", values, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdNotIn(List<String> values) {
addCriterion("rollback_source_id not in", values, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdBetween(String value1, String value2) {
addCriterion("rollback_source_id between", value1, value2, "rollbackSourceId");
return (Criteria) this;
}
public Criteria andRollbackSourceIdNotBetween(String value1, String value2) {
addCriterion("rollback_source_id not between", value1, value2, "rollbackSourceId");
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

@ -1,41 +0,0 @@
package io.metersphere.bug.mapper;
import io.metersphere.bug.domain.BugHistory;
import io.metersphere.bug.domain.BugHistoryExample;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface BugHistoryMapper {
long countByExample(BugHistoryExample example);
int deleteByExample(BugHistoryExample example);
int deleteByPrimaryKey(String id);
int insert(BugHistory record);
int insertSelective(BugHistory record);
List<BugHistory> selectByExampleWithBLOBs(BugHistoryExample example);
List<BugHistory> selectByExample(BugHistoryExample example);
BugHistory selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") BugHistory record, @Param("example") BugHistoryExample example);
int updateByExampleWithBLOBs(@Param("record") BugHistory record, @Param("example") BugHistoryExample example);
int updateByExample(@Param("record") BugHistory record, @Param("example") BugHistoryExample example);
int updateByPrimaryKeySelective(BugHistory record);
int updateByPrimaryKeyWithBLOBs(BugHistory record);
int updateByPrimaryKey(BugHistory record);
int batchInsert(@Param("list") List<BugHistory> list);
int batchInsertSelective(@Param("list") List<BugHistory> list, @Param("selective") BugHistory.Column ... selective);
}

View File

@ -1,352 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.bug.mapper.BugHistoryMapper">
<resultMap id="BaseResultMap" type="io.metersphere.bug.domain.BugHistory">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="bug_id" jdbcType="VARCHAR" property="bugId" />
<result column="num" jdbcType="INTEGER" property="num" />
<result column="type" jdbcType="VARCHAR" property="type" />
<result column="rollback_source_id" jdbcType="VARCHAR" property="rollbackSourceId" />
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.bug.domain.BugHistory">
<result column="content" jdbcType="LONGVARBINARY" property="content" />
</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, bug_id, num, `type`, rollback_source_id, create_user, create_time
</sql>
<sql id="Blob_Column_List">
content
</sql>
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.bug.domain.BugHistoryExample" resultMap="ResultMapWithBLOBs">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from bug_history
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByExample" parameterType="io.metersphere.bug.domain.BugHistoryExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from bug_history
<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="ResultMapWithBLOBs">
select
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from bug_history
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from bug_history
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.bug.domain.BugHistoryExample">
delete from bug_history
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.bug.domain.BugHistory">
insert into bug_history (id, bug_id, num,
`type`, rollback_source_id, create_user,
create_time, content)
values (#{id,jdbcType=VARCHAR}, #{bugId,jdbcType=VARCHAR}, #{num,jdbcType=INTEGER},
#{type,jdbcType=VARCHAR}, #{rollbackSourceId,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR},
#{createTime,jdbcType=BIGINT}, #{content,jdbcType=LONGVARBINARY})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.bug.domain.BugHistory">
insert into bug_history
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="bugId != null">
bug_id,
</if>
<if test="num != null">
num,
</if>
<if test="type != null">
`type`,
</if>
<if test="rollbackSourceId != null">
rollback_source_id,
</if>
<if test="createUser != null">
create_user,
</if>
<if test="createTime != null">
create_time,
</if>
<if test="content != null">
content,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="bugId != null">
#{bugId,jdbcType=VARCHAR},
</if>
<if test="num != null">
#{num,jdbcType=INTEGER},
</if>
<if test="type != null">
#{type,jdbcType=VARCHAR},
</if>
<if test="rollbackSourceId != null">
#{rollbackSourceId,jdbcType=VARCHAR},
</if>
<if test="createUser != null">
#{createUser,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
#{createTime,jdbcType=BIGINT},
</if>
<if test="content != null">
#{content,jdbcType=LONGVARBINARY},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.bug.domain.BugHistoryExample" resultType="java.lang.Long">
select count(*) from bug_history
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update bug_history
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR},
</if>
<if test="record.bugId != null">
bug_id = #{record.bugId,jdbcType=VARCHAR},
</if>
<if test="record.num != null">
num = #{record.num,jdbcType=INTEGER},
</if>
<if test="record.type != null">
`type` = #{record.type,jdbcType=VARCHAR},
</if>
<if test="record.rollbackSourceId != null">
rollback_source_id = #{record.rollbackSourceId,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.content != null">
content = #{record.content,jdbcType=LONGVARBINARY},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExampleWithBLOBs" parameterType="map">
update bug_history
set id = #{record.id,jdbcType=VARCHAR},
bug_id = #{record.bugId,jdbcType=VARCHAR},
num = #{record.num,jdbcType=INTEGER},
`type` = #{record.type,jdbcType=VARCHAR},
rollback_source_id = #{record.rollbackSourceId,jdbcType=VARCHAR},
create_user = #{record.createUser,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
content = #{record.content,jdbcType=LONGVARBINARY}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update bug_history
set id = #{record.id,jdbcType=VARCHAR},
bug_id = #{record.bugId,jdbcType=VARCHAR},
num = #{record.num,jdbcType=INTEGER},
`type` = #{record.type,jdbcType=VARCHAR},
rollback_source_id = #{record.rollbackSourceId,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.bug.domain.BugHistory">
update bug_history
<set>
<if test="bugId != null">
bug_id = #{bugId,jdbcType=VARCHAR},
</if>
<if test="num != null">
num = #{num,jdbcType=INTEGER},
</if>
<if test="type != null">
`type` = #{type,jdbcType=VARCHAR},
</if>
<if test="rollbackSourceId != null">
rollback_source_id = #{rollbackSourceId,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="content != null">
content = #{content,jdbcType=LONGVARBINARY},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.bug.domain.BugHistory">
update bug_history
set bug_id = #{bugId,jdbcType=VARCHAR},
num = #{num,jdbcType=INTEGER},
`type` = #{type,jdbcType=VARCHAR},
rollback_source_id = #{rollbackSourceId,jdbcType=VARCHAR},
create_user = #{createUser,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
content = #{content,jdbcType=LONGVARBINARY}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.bug.domain.BugHistory">
update bug_history
set bug_id = #{bugId,jdbcType=VARCHAR},
num = #{num,jdbcType=INTEGER},
`type` = #{type,jdbcType=VARCHAR},
rollback_source_id = #{rollbackSourceId,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 bug_history
(id, bug_id, num, `type`, rollback_source_id, create_user, create_time, content)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.bugId,jdbcType=VARCHAR}, #{item.num,jdbcType=INTEGER},
#{item.type,jdbcType=VARCHAR}, #{item.rollbackSourceId,jdbcType=VARCHAR}, #{item.createUser,jdbcType=VARCHAR},
#{item.createTime,jdbcType=BIGINT}, #{item.content,jdbcType=LONGVARBINARY})
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
insert into bug_history (
<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="'bug_id'.toString() == column.value">
#{item.bugId,jdbcType=VARCHAR}
</if>
<if test="'num'.toString() == column.value">
#{item.num,jdbcType=INTEGER}
</if>
<if test="'type'.toString() == column.value">
#{item.type,jdbcType=VARCHAR}
</if>
<if test="'rollback_source_id'.toString() == column.value">
#{item.rollbackSourceId,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="'content'.toString() == column.value">
#{item.content,jdbcType=LONGVARBINARY}
</if>
</foreach>
)
</foreach>
</insert>
</mapper>

View File

@ -20,6 +20,7 @@
<result column="delete_user" jdbcType="VARCHAR" property="deleteUser" /> <result column="delete_user" jdbcType="VARCHAR" property="deleteUser" />
<result column="delete_time" jdbcType="BIGINT" property="deleteTime" /> <result column="delete_time" jdbcType="BIGINT" property="deleteTime" />
<result column="deleted" jdbcType="BIT" property="deleted" /> <result column="deleted" jdbcType="BIT" property="deleted" />
<result column="pos" jdbcType="BIGINT" property="pos" />
</resultMap> </resultMap>
<sql id="Example_Where_Clause"> <sql id="Example_Where_Clause">
<where> <where>
@ -120,7 +121,7 @@
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id, num, title, handle_users, handle_user, create_user, create_time, update_user, id, num, title, handle_users, handle_user, create_user, create_time, update_user,
update_time, project_id, template_id, platform, `status`, tags, platform_bug_id, update_time, project_id, template_id, platform, `status`, tags, platform_bug_id,
delete_user, delete_time, deleted delete_user, delete_time, deleted, pos
</sql> </sql>
<select id="selectByExample" parameterType="io.metersphere.bug.domain.BugExample" resultMap="BaseResultMap"> <select id="selectByExample" parameterType="io.metersphere.bug.domain.BugExample" resultMap="BaseResultMap">
select select
@ -159,14 +160,14 @@
project_id, template_id, platform, project_id, template_id, platform,
`status`, tags, `status`, tags,
platform_bug_id, delete_user, delete_time, platform_bug_id, delete_user, delete_time,
deleted) deleted, pos)
values (#{id,jdbcType=VARCHAR}, #{num,jdbcType=INTEGER}, #{title,jdbcType=VARCHAR}, values (#{id,jdbcType=VARCHAR}, #{num,jdbcType=INTEGER}, #{title,jdbcType=VARCHAR},
#{handleUsers,jdbcType=VARCHAR}, #{handleUser,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR}, #{handleUsers,jdbcType=VARCHAR}, #{handleUser,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR},
#{createTime,jdbcType=BIGINT}, #{updateUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT}, #{createTime,jdbcType=BIGINT}, #{updateUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT},
#{projectId,jdbcType=VARCHAR}, #{templateId,jdbcType=VARCHAR}, #{platform,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{templateId,jdbcType=VARCHAR}, #{platform,jdbcType=VARCHAR},
#{status,jdbcType=VARCHAR}, #{tags,jdbcType=VARCHAR,typeHandler=io.metersphere.handler.ListTypeHandler}, #{status,jdbcType=VARCHAR}, #{tags,jdbcType=VARCHAR,typeHandler=io.metersphere.handler.ListTypeHandler},
#{platformBugId,jdbcType=VARCHAR}, #{deleteUser,jdbcType=VARCHAR}, #{deleteTime,jdbcType=BIGINT}, #{platformBugId,jdbcType=VARCHAR}, #{deleteUser,jdbcType=VARCHAR}, #{deleteTime,jdbcType=BIGINT},
#{deleted,jdbcType=BIT}) #{deleted,jdbcType=BIT}, #{pos,jdbcType=BIGINT})
</insert> </insert>
<insert id="insertSelective" parameterType="io.metersphere.bug.domain.Bug"> <insert id="insertSelective" parameterType="io.metersphere.bug.domain.Bug">
insert into bug insert into bug
@ -225,6 +226,9 @@
<if test="deleted != null"> <if test="deleted != null">
deleted, deleted,
</if> </if>
<if test="pos != null">
pos,
</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null"> <if test="id != null">
@ -281,6 +285,9 @@
<if test="deleted != null"> <if test="deleted != null">
#{deleted,jdbcType=BIT}, #{deleted,jdbcType=BIT},
</if> </if>
<if test="pos != null">
#{pos,jdbcType=BIGINT},
</if>
</trim> </trim>
</insert> </insert>
<select id="countByExample" parameterType="io.metersphere.bug.domain.BugExample" resultType="java.lang.Long"> <select id="countByExample" parameterType="io.metersphere.bug.domain.BugExample" resultType="java.lang.Long">
@ -346,6 +353,9 @@
<if test="record.deleted != null"> <if test="record.deleted != null">
deleted = #{record.deleted,jdbcType=BIT}, deleted = #{record.deleted,jdbcType=BIT},
</if> </if>
<if test="record.pos != null">
pos = #{record.pos,jdbcType=BIGINT},
</if>
</set> </set>
<if test="_parameter != null"> <if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" /> <include refid="Update_By_Example_Where_Clause" />
@ -370,7 +380,8 @@
platform_bug_id = #{record.platformBugId,jdbcType=VARCHAR}, platform_bug_id = #{record.platformBugId,jdbcType=VARCHAR},
delete_user = #{record.deleteUser,jdbcType=VARCHAR}, delete_user = #{record.deleteUser,jdbcType=VARCHAR},
delete_time = #{record.deleteTime,jdbcType=BIGINT}, delete_time = #{record.deleteTime,jdbcType=BIGINT},
deleted = #{record.deleted,jdbcType=BIT} deleted = #{record.deleted,jdbcType=BIT},
pos = #{record.pos,jdbcType=BIGINT}
<if test="_parameter != null"> <if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" /> <include refid="Update_By_Example_Where_Clause" />
</if> </if>
@ -429,6 +440,9 @@
<if test="deleted != null"> <if test="deleted != null">
deleted = #{deleted,jdbcType=BIT}, deleted = #{deleted,jdbcType=BIT},
</if> </if>
<if test="pos != null">
pos = #{pos,jdbcType=BIGINT},
</if>
</set> </set>
where id = #{id,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR}
</update> </update>
@ -450,14 +464,15 @@
platform_bug_id = #{platformBugId,jdbcType=VARCHAR}, platform_bug_id = #{platformBugId,jdbcType=VARCHAR},
delete_user = #{deleteUser,jdbcType=VARCHAR}, delete_user = #{deleteUser,jdbcType=VARCHAR},
delete_time = #{deleteTime,jdbcType=BIGINT}, delete_time = #{deleteTime,jdbcType=BIGINT},
deleted = #{deleted,jdbcType=BIT} deleted = #{deleted,jdbcType=BIT},
pos = #{pos,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR}
</update> </update>
<insert id="batchInsert" parameterType="map"> <insert id="batchInsert" parameterType="map">
insert into bug insert into bug
(id, num, title, handle_users, handle_user, create_user, create_time, update_user, (id, num, title, handle_users, handle_user, create_user, create_time, update_user,
update_time, project_id, template_id, platform, `status`, tags, platform_bug_id, update_time, project_id, template_id, platform, `status`, tags, platform_bug_id,
delete_user, delete_time, deleted) delete_user, delete_time, deleted, pos)
values values
<foreach collection="list" item="item" separator=","> <foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.num,jdbcType=INTEGER}, #{item.title,jdbcType=VARCHAR}, (#{item.id,jdbcType=VARCHAR}, #{item.num,jdbcType=INTEGER}, #{item.title,jdbcType=VARCHAR},
@ -466,7 +481,7 @@
#{item.projectId,jdbcType=VARCHAR}, #{item.templateId,jdbcType=VARCHAR}, #{item.platform,jdbcType=VARCHAR}, #{item.projectId,jdbcType=VARCHAR}, #{item.templateId,jdbcType=VARCHAR}, #{item.platform,jdbcType=VARCHAR},
#{item.status,jdbcType=VARCHAR}, #{item.tags,jdbcType=VARCHAR,typeHandler=io.metersphere.handler.ListTypeHandler}, #{item.status,jdbcType=VARCHAR}, #{item.tags,jdbcType=VARCHAR,typeHandler=io.metersphere.handler.ListTypeHandler},
#{item.platformBugId,jdbcType=VARCHAR}, #{item.deleteUser,jdbcType=VARCHAR}, #{item.deleteTime,jdbcType=BIGINT}, #{item.platformBugId,jdbcType=VARCHAR}, #{item.deleteUser,jdbcType=VARCHAR}, #{item.deleteTime,jdbcType=BIGINT},
#{item.deleted,jdbcType=BIT}) #{item.deleted,jdbcType=BIT}, #{item.pos,jdbcType=BIGINT})
</foreach> </foreach>
</insert> </insert>
<insert id="batchInsertSelective" parameterType="map"> <insert id="batchInsertSelective" parameterType="map">
@ -533,6 +548,9 @@
<if test="'deleted'.toString() == column.value"> <if test="'deleted'.toString() == column.value">
#{item.deleted,jdbcType=BIT} #{item.deleted,jdbcType=BIT}
</if> </if>
<if test="'pos'.toString() == column.value">
#{item.pos,jdbcType=BIGINT}
</if>
</foreach> </foreach>
) )
</foreach> </foreach>

View File

@ -20,21 +20,23 @@ CREATE TABLE IF NOT EXISTS bug(
`delete_user` VARCHAR(50) NOT NULL COMMENT '删除人' , `delete_user` VARCHAR(50) NOT NULL COMMENT '删除人' ,
`delete_time` BIGINT NOT NULL COMMENT '删除时间' , `delete_time` BIGINT NOT NULL COMMENT '删除时间' ,
`deleted` BIT(1) NOT NULL COMMENT '删除状态' , `deleted` BIT(1) NOT NULL COMMENT '删除状态' ,
`pos` BIGINT NOT NULL COMMENT '自定义排序间隔5000' ,
PRIMARY KEY (id) PRIMARY KEY (id)
) COMMENT = '缺陷'; ) COMMENT = '缺陷';
CREATE INDEX idx_num ON bug(num); CREATE INDEX idx_num ON bug(num);
CREATE INDEX idx_title ON bug(title); CREATE INDEX idx_title ON bug(title);
CREATE INDEX idx_handle_user ON bug(handle_user); CREATE INDEX idx_assign_user ON bug(handle_user);
CREATE INDEX idx_create_user ON bug(create_user); CREATE INDEX idx_create_user ON bug(create_user);
CREATE INDEX idx_create_time ON bug(create_time desc); CREATE INDEX idx_create_time ON bug(create_time);
CREATE INDEX idx_update_user ON bug(update_user); CREATE INDEX idx_update_user ON bug(update_user);
CREATE INDEX idx_update_time ON bug(update_time desc); CREATE INDEX idx_update_time ON bug(update_time);
CREATE INDEX idx_project_id ON bug(project_id); CREATE INDEX idx_project_id ON bug(project_id);
CREATE INDEX idx_platform ON bug(platform); CREATE INDEX idx_platform ON bug(platform);
CREATE INDEX idx_status ON bug(status); CREATE INDEX idx_status ON bug(status);
CREATE INDEX idx_deleted ON bug(deleted); CREATE INDEX idx_deleted ON bug(deleted);
CREATE INDEX idx_pos ON bug(pos);
CREATE TABLE IF NOT EXISTS bug_content( CREATE TABLE IF NOT EXISTS bug_content(
@ -113,20 +115,5 @@ CREATE INDEX idx_plan_case_id ON bug_relation_case(test_plan_id,test_plan_case_i
CREATE INDEX idx_case_id ON bug_relation_case(case_id); CREATE INDEX idx_case_id ON bug_relation_case(case_id);
CREATE INDEX idx_case_type ON bug_relation_case(case_type); CREATE INDEX idx_case_type ON bug_relation_case(case_type);
CREATE TABLE IF NOT EXISTS bug_history(
`id` VARCHAR(50) NOT NULL COMMENT '变更记录ID' ,
`bug_id` VARCHAR(50) NOT NULL COMMENT '所属缺陷ID' ,
`num` INT NOT NULL COMMENT '变更记录批次号' ,
`type` VARCHAR(64) COMMENT '变更类型;IMPORT/EDIT/' ,
`rollback_source_id` VARCHAR(50) COMMENT '回退来源' ,
`content` BLOB NOT NULL COMMENT '修改内容' ,
`create_user` VARCHAR(50) NOT NULL COMMENT '操作人' ,
`create_time` BIGINT NOT NULL COMMENT '操作时间' ,
PRIMARY KEY (id)
) COMMENT = '缺陷变更记录';
CREATE INDEX idx_bug_id ON bug_history(bug_id);
-- set innodb lock wait timeout to default -- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT; SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -0,0 +1,16 @@
package io.metersphere.plugin.platform.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class PlatformCustomFieldMultiLevelOption implements Serializable {
private String value;
private String text;
private List<PlatformCustomFieldMultiLevelOption> children;
}

View File

@ -28,6 +28,7 @@ public interface Platform extends ExtensionPoint {
/** /**
* 校验用户配置 * 校验用户配置
* 个人中心-第三方平台点击时调用 * 个人中心-第三方平台点击时调用
* @param userConfig 用户配置
*/ */
void validateUserConfig(String userConfig); void validateUserConfig(String userConfig);
@ -72,8 +73,9 @@ public interface Platform extends ExtensionPoint {
* @param projectConfig 项目配置信息 * @param projectConfig 项目配置信息
* @param issueKey 缺陷ID * @param issueKey 缺陷ID
* @return 缺陷平台状态 * @return 缺陷平台状态
* @throws Exception 获取平台状态异常
*/ */
List<SelectOption> getStatusTransitions(String projectConfig, String issueKey); List<SelectOption> getStatusTransitions(String projectConfig, String issueKey) throws Exception;
/** /**
* 获取第三方平台关联需求列表 * 获取第三方平台关联需求列表

View File

@ -76,14 +76,6 @@ bug_relation_case.case_type.length_range=关联的用例类型长度必须在1-5
bug_relation_case.create_user.not_blank=创建人不能为空 bug_relation_case.create_user.not_blank=创建人不能为空
bug_relation_case.create_user.length_range=创建人长度必须在1-50之间 bug_relation_case.create_user.length_range=创建人长度必须在1-50之间
# bugHistory
bug_history.id.not_blank=变更记录ID不能为空
bug_history.id.length_range=变更记录ID长度必须在1-50之间
bug_history.bug_id.not_blank=所属缺陷ID不能为空
bug_history.bug_id.length_range=所属缺陷ID长度必须在1-50之间
bug_history.create_user.not_blank=操作人不能为空
bug_history.create_user.length_range=操作人长度必须在1-50之间
# error # error
bug_not_exist=缺陷不存在 bug_not_exist=缺陷不存在
not_local_bug_error=非本地缺陷,无法操作 not_local_bug_error=非本地缺陷,无法操作
@ -109,8 +101,8 @@ bug.tag=标签
bug.description=缺陷内容 bug.description=缺陷内容
# bug export # bug export
bug.system_columns.not_empty=系统字段不能为空 bug.system_columns.not_empty=系统字段不能为空
bug.export.system.columns.name=缺陷名称
bug.export.system.columns.id=ID bug.export.system.columns.id=ID
bug.export.system.columns.name=缺陷名称
bug.export.system.columns.content=缺陷内容 bug.export.system.columns.content=缺陷内容
bug.export.system.columns.status=缺陷状态 bug.export.system.columns.status=缺陷状态
bug.export.system.columns.handle_user=处理人 bug.export.system.columns.handle_user=处理人

View File

@ -76,14 +76,6 @@ bug_relation_case.case_type.length_range=caseType length must be between 1-50
bug_relation_case.create_user.not_blank=createUser cannot be empty bug_relation_case.create_user.not_blank=createUser cannot be empty
bug_relation_case.create_user.length_range=createUser length must be between 1-50 bug_relation_case.create_user.length_range=createUser length must be between 1-50
# bugHistory
bug_history.id.not_blank=id cannot be empty
bug_history.id.length_range=id length must be between 1-50
bug_history.bug_id.not_blank=bugId cannot be empty
bug_history.bug_id.length_range=bugId length must be between 1-50
bug_history.create_user.not_blank=createUser cannot be empty
bug_history.create_user.length_range=createUser length must be between 1-50
# error # error
bug_not_exist=Bug does not exist bug_not_exist=Bug does not exist
not_local_bug_error=Not local bug, error not_local_bug_error=Not local bug, error

View File

@ -76,14 +76,6 @@ bug_relation_case.case_type.length_range=关联的用例类型长度必须在1-5
bug_relation_case.create_user.not_blank=创建人不能为空 bug_relation_case.create_user.not_blank=创建人不能为空
bug_relation_case.create_user.length_range=创建人长度必须在1-50之间 bug_relation_case.create_user.length_range=创建人长度必须在1-50之间
# bugHistory
bug_history.id.not_blank=变更记录ID不能为空
bug_history.id.length_range=变更记录ID长度必须在1-50之间
bug_history.bug_id.not_blank=所属缺陷ID不能为空
bug_history.bug_id.length_range=所属缺陷ID长度必须在1-50之间
bug_history.create_user.not_blank=操作人不能为空
bug_history.create_user.length_range=操作人长度必须在1-50之间
# error # error
bug_not_exist=缺陷不存在 bug_not_exist=缺陷不存在
not_local_bug_error=非本地缺陷,无法操作 not_local_bug_error=非本地缺陷,无法操作

View File

@ -76,14 +76,6 @@ bug_relation_case.case_type.length_range=关联的用例类型長度必須在1-5
bug_relation_case.create_user.not_blank=创建人不能為空 bug_relation_case.create_user.not_blank=创建人不能為空
bug_relation_case.create_user.length_range=创建人長度必須在1-50之間 bug_relation_case.create_user.length_range=创建人長度必須在1-50之間
# bugHistory
bug_history.id.not_blank=变更记录ID不能為空
bug_history.id.length_range=变更记录ID長度必須在1-50之間
bug_history.bug_id.not_blank=所属缺陷ID不能為空
bug_history.bug_id.length_range=所属缺陷ID長度必須在1-50之間
bug_history.create_user.not_blank=操作人不能為空
bug_history.create_user.length_range=操作人長度必須在1-50之間
# error # error
bug_not_exist=缺陷不存在 bug_not_exist=缺陷不存在
not_local_bug_error=非本地缺陷,無法操作 not_local_bug_error=非本地缺陷,無法操作

View File

@ -1,7 +1,7 @@
package io.metersphere.bug.constants; package io.metersphere.bug.constants;
import io.metersphere.bug.dto.response.BugCustomFieldDTO;
import io.metersphere.sdk.util.Translator; import io.metersphere.sdk.util.Translator;
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
import lombok.Data; import lombok.Data;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -17,8 +17,8 @@ public class BugExportColumns {
private LinkedHashMap<String, String> customColumns = new LinkedHashMap<>(); private LinkedHashMap<String, String> customColumns = new LinkedHashMap<>();
public BugExportColumns() { public BugExportColumns() {
systemColumns.put("num", Translator.get("bug.export.system.columns.id"));
systemColumns.put("name", Translator.get("bug.export.system.columns.name")); systemColumns.put("name", Translator.get("bug.export.system.columns.name"));
systemColumns.put("id", Translator.get("bug.export.system.columns.id"));
systemColumns.put("content", Translator.get("bug.export.system.columns.content")); systemColumns.put("content", Translator.get("bug.export.system.columns.content"));
systemColumns.put("status", Translator.get("bug.export.system.columns.status")); systemColumns.put("status", Translator.get("bug.export.system.columns.status"));
systemColumns.put("handle_user", Translator.get("bug.export.system.columns.handle_user")); systemColumns.put("handle_user", Translator.get("bug.export.system.columns.handle_user"));
@ -31,9 +31,9 @@ public class BugExportColumns {
} }
public void initCustomColumns(List<BugCustomFieldDTO> customFieldList) { public void initCustomColumns(List<TemplateCustomFieldDTO> headerCustomFields) {
customFieldList.forEach(item -> { headerCustomFields.forEach(item -> {
customColumns.put(item.getId(), item.getName()); customColumns.put(item.getFieldId(), item.getFieldName());
}); });
} }
} }

View File

@ -0,0 +1,8 @@
package io.metersphere.bug.constants;
public class BugExportMultipleField {
public static final String MULTIPLE_FIELD = "multiple";
public static final String CASCADING_FIELD = "cascading";
public static final String CHECKBOX_FIELD = "checkbox";
}

View File

@ -28,7 +28,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.util.List; import java.util.List;
@Tag(name = "缺陷管理") @Tag(name = "缺陷管理-附件")
@RestController @RestController
@RequestMapping("/bug/attachment") @RequestMapping("/bug/attachment")
public class BugAttachmentController { public class BugAttachmentController {

View File

@ -3,9 +3,11 @@ package io.metersphere.bug.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.constants.BugExportColumns; import io.metersphere.bug.constants.BugExportColumns;
import io.metersphere.bug.domain.Bug;
import io.metersphere.bug.dto.BugSyncResult; import io.metersphere.bug.dto.BugSyncResult;
import io.metersphere.bug.dto.request.*; import io.metersphere.bug.dto.request.*;
import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.bug.dto.response.BugDTO;
import io.metersphere.bug.dto.response.BugDetailDTO;
import io.metersphere.bug.service.*; import io.metersphere.bug.service.*;
import io.metersphere.plugin.platform.dto.SelectOption; import io.metersphere.plugin.platform.dto.SelectOption;
import io.metersphere.project.dto.ProjectTemplateOptionDTO; import io.metersphere.project.dto.ProjectTemplateOptionDTO;
@ -14,6 +16,7 @@ import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.constants.TemplateScene; import io.metersphere.sdk.constants.TemplateScene;
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO; import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
import io.metersphere.system.dto.sdk.TemplateDTO; import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.dto.sdk.request.PosRequest;
import io.metersphere.system.log.annotation.Log; import io.metersphere.system.log.annotation.Log;
import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.notice.annotation.SendNotice; import io.metersphere.system.notice.annotation.SendNotice;
@ -83,7 +86,7 @@ public class BugController {
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public Pager<List<BugDTO>> page(@Validated @RequestBody BugPageRequest request) { public Pager<List<BugDTO>> page(@Validated @RequestBody BugPageRequest request) {
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(), Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "create_time desc"); StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "pos desc");
request.setUseTrash(false); request.setUseTrash(false);
return PageUtils.setPageInfo(page, bugService.list(request)); return PageUtils.setPageInfo(page, bugService.list(request));
} }
@ -94,9 +97,9 @@ public class BugController {
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request, #files)", msClass = BugLogService.class) @Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request, #files)", msClass = BugLogService.class)
@SendNotice(taskType = NoticeConstants.TaskType.BUG_TASK, event = NoticeConstants.Event.CREATE, target = "#targetClass.getNoticeByRequest(#request)", targetClass = BugNoticeService.class) @SendNotice(taskType = NoticeConstants.TaskType.BUG_TASK, event = NoticeConstants.Event.CREATE, target = "#targetClass.getNoticeByRequest(#request)", targetClass = BugNoticeService.class)
public void add(@Validated({Created.class}) @RequestPart(value = "request") BugEditRequest request, public Bug add(@Validated({Created.class}) @RequestPart(value = "request") BugEditRequest request,
@RequestPart(value = "file", required = false) List<MultipartFile> files) { @RequestPart(value = "file", required = false) List<MultipartFile> files) {
bugService.addOrUpdate(request, files, SessionUtils.getUserId(), SessionUtils.getCurrentOrganizationId(), false); return bugService.addOrUpdate(request, files, SessionUtils.getUserId(), SessionUtils.getCurrentOrganizationId(), false);
} }
@PostMapping("/update") @PostMapping("/update")
@ -105,16 +108,16 @@ public class BugController {
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request, #files)", msClass = BugLogService.class) @Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request, #files)", msClass = BugLogService.class)
@SendNotice(taskType = NoticeConstants.TaskType.BUG_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getNoticeByRequest(#request)", targetClass = BugNoticeService.class) @SendNotice(taskType = NoticeConstants.TaskType.BUG_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getNoticeByRequest(#request)", targetClass = BugNoticeService.class)
public void update(@Validated({Updated.class}) @RequestPart(value = "request") BugEditRequest request, public Bug update(@Validated({Updated.class}) @RequestPart(value = "request") BugEditRequest request,
@RequestPart(value = "file", required = false) List<MultipartFile> files) { @RequestPart(value = "file", required = false) List<MultipartFile> files) {
bugService.addOrUpdate(request, files, SessionUtils.getUserId(), SessionUtils.getCurrentOrganizationId(), true); return bugService.addOrUpdate(request, files, SessionUtils.getUserId(), SessionUtils.getCurrentOrganizationId(), true);
} }
@GetMapping("/get/{id}") @GetMapping("/get/{id}")
@Operation(summary = "缺陷管理-列表-详情&&编辑&&复制") @Operation(summary = "缺陷管理-列表-详情&&编辑&&复制")
@RequiresPermissions(PermissionConstants.PROJECT_BUG_READ) @RequiresPermissions(PermissionConstants.PROJECT_BUG_READ)
public void get(@PathVariable String id) { public BugDetailDTO get(@PathVariable String id) {
bugService.get(id); return bugService.get(id);
} }
@GetMapping("/delete/{id}") @GetMapping("/delete/{id}")
@ -163,6 +166,7 @@ public class BugController {
@RequiresPermissions(PermissionConstants.PROJECT_BUG_EXPORT) @RequiresPermissions(PermissionConstants.PROJECT_BUG_EXPORT)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public ResponseEntity<byte[]> export(@Validated @RequestBody BugExportRequest request) throws Exception { public ResponseEntity<byte[]> export(@Validated @RequestBody BugExportRequest request) throws Exception {
request.setUseTrash(false);
return bugService.export(request); return bugService.export(request);
} }
@ -184,6 +188,14 @@ public class BugController {
bugService.batchUpdate(request, SessionUtils.getUserId()); bugService.batchUpdate(request, SessionUtils.getUserId());
} }
@PostMapping("/edit/pos")
@Operation(summary = "缺陷管理-列表-拖拽排序")
@RequiresPermissions(PermissionConstants.PROJECT_BUG_UPDATE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public void editPos(@Validated @RequestBody PosRequest request) {
bugService.editPos(request);
}
@GetMapping("/template/option/{projectId}") @GetMapping("/template/option/{projectId}")
@Operation(summary = "缺陷管理-详情-获取当前项目模板选项") @Operation(summary = "缺陷管理-详情-获取当前项目模板选项")
@RequiresPermissions(PermissionConstants.PROJECT_BUG_READ) @RequiresPermissions(PermissionConstants.PROJECT_BUG_READ)

View File

@ -0,0 +1,42 @@
package io.metersphere.bug.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.bug.dto.request.BugHistoryPageRequest;
import io.metersphere.bug.service.BugHistoryService;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.dto.OperationHistoryDTO;
import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Tag(name = "缺陷管理-变更历史")
@RestController
@RequestMapping("/bug/history")
public class BugHistoryController {
@Resource
private BugHistoryService bugHistoryService;
@PostMapping("/page")
@Operation(summary = "缺陷管理-变更历史-列表")
@RequiresPermissions(PermissionConstants.PROJECT_BUG_READ)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public Pager<List<OperationHistoryDTO>> page(@Validated @RequestBody BugHistoryPageRequest request) {
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "id desc");
return PageUtils.setPageInfo(page, bugHistoryService.list(request));
}
}

View File

@ -1,38 +1,46 @@
package io.metersphere.bug.dto; package io.metersphere.bug.dto;
import io.metersphere.bug.constants.BugExportMultipleField;
import io.metersphere.bug.domain.BugContent; import io.metersphere.bug.domain.BugContent;
import io.metersphere.bug.dto.response.BugCommentDTO; import io.metersphere.bug.dto.response.BugCommentDTO;
import io.metersphere.bug.dto.response.BugCustomFieldDTO; import io.metersphere.bug.dto.response.BugCustomFieldDTO;
import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.bug.dto.response.BugDTO;
import io.metersphere.plugin.platform.dto.PlatformCustomFieldMultiLevelOption;
import io.metersphere.sdk.util.DateUtils; import io.metersphere.sdk.util.DateUtils;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.domain.CustomFieldOption;
import io.metersphere.system.dto.CommentUserInfo;
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
import lombok.Data; import lombok.Data;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList; import java.util.*;
import java.util.LinkedHashMap; import java.util.stream.Collectors;
import java.util.List;
import java.util.Map;
/** /**
* 缺陷导出数据结构模型 * 缺陷导出数据结构模型
*/ */
@Data @Data
public class BugExportExcelModel { public class BugExportExcelModel {
// <key,text>. 如果是自定义字段则是 <id,字段显示>
/**
* <key,text>. 如果是自定义字段则是 <id,字段显示>
*/
private LinkedHashMap<String, String> excelHeader; private LinkedHashMap<String, String> excelHeader;
// <key,value>. 如果是自定义字段则是 <id,数据> /**
* <key,value>. 如果是自定义字段则是 <id,数据>
*/
private List<LinkedHashMap<String, String>> excelRows; private List<LinkedHashMap<String, String>> excelRows;
public BugExportExcelModel(List<BugExportColumn> exportColumns, public BugExportExcelModel(BugExportHeaderModel headerModel,
List<BugDTO> bugList, List<BugDTO> bugList,
Map<String, List<BugCommentDTO>> bugComment, Map<String, List<BugCommentDTO>> bugComment,
Map<String, BugContent> bugContents, Map<String, BugContent> bugContents) {
Map<String, Long> bugCountMap) {
this.excelHeader = new LinkedHashMap<>(); this.excelHeader = new LinkedHashMap<>();
//注入表头 // 生成表头
for (BugExportColumn exportColumn : exportColumns) { for (BugExportColumn exportColumn : headerModel.getExportColumns()) {
this.excelHeader.put(exportColumn.getKey(), exportColumn.getText()); this.excelHeader.put(exportColumn.getKey(), exportColumn.getText());
} }
this.excelRows = new ArrayList<>(); this.excelRows = new ArrayList<>();
@ -40,40 +48,137 @@ public class BugExportExcelModel {
LinkedHashMap<String, String> excelRow = new LinkedHashMap<>(); LinkedHashMap<String, String> excelRow = new LinkedHashMap<>();
for (String key : excelHeader.keySet()) { for (String key : excelHeader.keySet()) {
switch (key) { switch (key) {
case "name" -> excelRow.put(key, bugDTO.getTitle()); case "name" :
case "id" -> excelRow.put(key, bugDTO.getId()); excelRow.put(key, bugDTO.getTitle());
case "content" -> excelRow.put(key, this.getBugContent(bugContents, bugDTO.getId())); break;
case "status" -> excelRow.put(key, bugDTO.getStatus()); case "num" :
case "handleUser" -> excelRow.put(key, bugDTO.getHandleUserName()); excelRow.put(key, bugDTO.getNum().toString());
case "createUser" -> excelRow.put(key, bugDTO.getCreateUserName()); break;
case "createTime" -> excelRow.put(key, DateUtils.getTimeString(bugDTO.getCreateTime())); case "content" :
case "caseCount" -> excelRow.put(key, this.getBugCaseCount(bugCountMap, bugDTO.getId())); excelRow.put(key, this.getBugContent(bugContents, bugDTO.getId()));
case "comment" -> excelRow.put(key, this.getBugComment(bugComment.get(bugDTO.getId()))); break;
case "platform" -> excelRow.put(key, bugDTO.getPlatform()); case "status" :
default -> excelRow.put(key, this.getCustomFieldValue(bugDTO.getCustomFields(), key)); excelRow.put(key, headerModel.getStatusMap().get(bugDTO.getStatus()));
break;
case "handle_user" :
excelRow.put(key, headerModel.getHandleUserMap().get(bugDTO.getHandleUser()));
break;
case "create_user" :
excelRow.put(key, bugDTO.getCreateUserName());
break;
case "create_time" :
excelRow.put(key, DateUtils.getTimeString(bugDTO.getCreateTime()));
break;
case "case_count" :
excelRow.put(key, String.valueOf(bugDTO.getRelationCaseCount()));
break;
case "comment" :
excelRow.put(key, this.getBugComment(bugComment.get(bugDTO.getId())));
break;
case "platform" :
excelRow.put(key, bugDTO.getPlatform());
break;
default :
excelRow.put(key, this.getCustomFieldValue(bugDTO.getCustomFields(), key, headerModel.getHeaderCustomFields()));
} }
} }
excelRows.add(excelRow); excelRows.add(excelRow);
} }
} }
private String getCustomFieldValue(List<BugCustomFieldDTO> customFields, String key) { private String getCustomFieldValue(List<BugCustomFieldDTO> customFields, String key, List<TemplateCustomFieldDTO> headerCustomFields) {
Map<String, TemplateCustomFieldDTO> fieldMap = headerCustomFields.stream().collect(Collectors.toMap(TemplateCustomFieldDTO::getFieldId, f -> f));
if (CollectionUtils.isNotEmpty(customFields)) { if (CollectionUtils.isNotEmpty(customFields)) {
for (BugCustomFieldDTO customField : customFields) { for (BugCustomFieldDTO customField : customFields) {
if (key.equals(customField.getId())) { if (key.equals(customField.getId())) {
return customField.getValue(); TemplateCustomFieldDTO templateField = fieldMap.get(key);
return parseOptionVal(templateField, customField.getValue());
} }
} }
} }
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
private String getBugCaseCount(Map<String, Long> bugCountMap, String id) { /**
long count = 0; * 解析自定义字段的值
if (bugCountMap.containsKey(id)) { * @param customField 模板字段(包含选项值)
count = bugCountMap.get(id); * @param val 字段值
* @return 字段文本
*/
private String parseOptionVal(TemplateCustomFieldDTO customField, String val) {
if (CollectionUtils.isEmpty(customField.getOptions()) && StringUtils.isBlank(customField.getPlatformOptionJson())) {
return val;
} }
return String.valueOf(count); if (CollectionUtils.isNotEmpty(customField.getOptions())) {
return filterOptionVal(customField.getOptions(), val, customField.getType());
}
if (StringUtils.isNotBlank(customField.getPlatformOptionJson())) {
List<PlatformCustomFieldMultiLevelOption> platformOptions = JSON.parseArray(customField.getPlatformOptionJson(), PlatformCustomFieldMultiLevelOption.class);
List<CustomFieldOption> options = new ArrayList<>();
buildOptionFromPlatformOption(platformOptions, options);
return filterOptionVal(options, val, customField.getType());
}
return StringUtils.EMPTY;
}
/**
* 递归解析平台选项
* @param platformOptions 平台选项
* @param options 选项值
*/
private void buildOptionFromPlatformOption(List<PlatformCustomFieldMultiLevelOption> platformOptions, List<CustomFieldOption> options) {
for (PlatformCustomFieldMultiLevelOption platformOption : platformOptions) {
CustomFieldOption option = new CustomFieldOption();
option.setValue(platformOption.getValue());
option.setText(platformOption.getText());
options.add(option);
if (CollectionUtils.isNotEmpty(platformOption.getChildren())) {
buildOptionFromPlatformOption(platformOption.getChildren(), options);
}
}
}
/**
* 解析自定义字段的值
* @param options 选项值
* @param val
* @param fieldType 字段类型
* @return 字段文本
*/
private String filterOptionVal(List<CustomFieldOption> options, String val, String fieldType) {
if (startsWithAnyMultipleField(fieldType)) {
// 多选字段
List<String> valList = JSON.parseArray(val, String.class);
List<String> textList = new ArrayList<>();
valList.forEach(value -> {
Optional<CustomFieldOption> targetOption = options.stream().filter(option -> StringUtils.equals(value, option.getValue())).findFirst();
targetOption.ifPresent(option -> textList.add(option.getText()));
});
if (CollectionUtils.isEmpty(textList)) {
return StringUtils.EMPTY;
} else {
return String.join(",", textList);
}
} else {
// 单选字段
Optional<CustomFieldOption> findOption = options.stream().filter(option -> StringUtils.equals(val, option.getValue())).findFirst();
if (findOption.isPresent()) {
return findOption.get().getText();
} else {
return StringUtils.EMPTY;
}
}
}
/**
* 是否是多选字段
* @param fieldType 字段类型
* @return 是否是多选字段
*/
private boolean startsWithAnyMultipleField(String fieldType) {
return StringUtils.startsWithIgnoreCase(fieldType, BugExportMultipleField.MULTIPLE_FIELD) ||
StringUtils.startsWithIgnoreCase(fieldType, BugExportMultipleField.CASCADING_FIELD) ||
StringUtils.startsWithIgnoreCase(fieldType, BugExportMultipleField.CHECKBOX_FIELD);
} }
private String getBugContent(Map<String, BugContent> bugContents, String id) { private String getBugContent(Map<String, BugContent> bugContents, String id) {
@ -90,23 +195,33 @@ public class BugExportExcelModel {
} else { } else {
StringBuilder commentBuilder = new StringBuilder(); StringBuilder commentBuilder = new StringBuilder();
for (BugCommentDTO bugCommentDTO : bugCommentList) { for (BugCommentDTO bugCommentDTO : bugCommentList) {
commentBuilder.append(bugCommentDTO.getCreateUser()); buildComment(bugCommentDTO, commentBuilder);
commentBuilder.append(StringUtils.SPACE);
commentBuilder.append(DateUtils.getTimeString(bugCommentDTO.getCreateTime()));
commentBuilder.append(StringUtils.LF);
commentBuilder.append(bugCommentDTO.getContent());
commentBuilder.append(StringUtils.LF);
} }
return commentBuilder.toString(); return commentBuilder.toString();
} }
} }
public List<String> getHeadTexts() { public void buildComment(BugCommentDTO bugCommentDTO, StringBuilder commentBuilder) {
return new ArrayList<>(excelHeader.values()); commentBuilder.append("");
commentBuilder.append(parseCommentUser(bugCommentDTO, bugCommentDTO.getCreateUser()));
commentBuilder.append(StringUtils.SPACE);
commentBuilder.append(DateUtils.getTimeString(bugCommentDTO.getCreateTime()));
commentBuilder.append(StringUtils.SPACE);
commentBuilder.append(StringUtils.isBlank(bugCommentDTO.getReplyUser()) ? "评论" :
"回复" + StringUtils.SPACE + parseCommentUser(bugCommentDTO, bugCommentDTO.getReplyUser()));
commentBuilder.append("");
commentBuilder.append(StringUtils.LF);
commentBuilder.append(bugCommentDTO.getContent());
commentBuilder.append(StringUtils.LF);
if (CollectionUtils.isNotEmpty(bugCommentDTO.getChildComments())) {
for (BugCommentDTO childComment : bugCommentDTO.getChildComments()) {
buildComment(childComment, commentBuilder);
}
}
} }
public List<String> getHeadKeys() { public List<String> getHeadTexts() {
return new ArrayList<>(excelHeader.keySet()); return new ArrayList<>(excelHeader.values());
} }
public List<List<String>> getData() { public List<List<String>> getData() {
@ -121,4 +236,19 @@ public class BugExportExcelModel {
} }
return returnList; return returnList;
} }
/**
* 用户ID -> 用户名
* @param commentDTO 评论DTO
* @param userId 用户ID
* @return 用户名
*/
private String parseCommentUser(BugCommentDTO commentDTO, String userId) {
Optional<CommentUserInfo> findUser = commentDTO.getCommentUserInfos().stream().filter(user -> StringUtils.equals(user.getId(), userId)).findFirst();
if (findUser.isPresent()) {
return findUser.get().getName();
} else {
return userId;
}
}
} }

View File

@ -0,0 +1,37 @@
package io.metersphere.bug.dto;
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BugExportHeaderModel {
/**
* 导出的表头列
*/
private List<BugExportColumn> exportColumns;
/**
* 导出的表头自定义字段(包含选项值)
*/
private List<TemplateCustomFieldDTO> headerCustomFields;
/**
* 导出的表头选项值集合(处理人)
*/
private Map<String, String> handleUserMap;
/**
* 导出的表头选项值集合(状态)
*/
private Map<String, String> statusMap;
}

View File

@ -0,0 +1,19 @@
package io.metersphere.bug.dto.request;
import io.metersphere.system.dto.sdk.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class BugHistoryPageRequest extends BasePageRequest {
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.project_id.not_blank}")
private String projectId;
@Schema(description = "缺陷ID", requiredMode = Schema.RequiredMode.REQUIRED)
private String bugId;
}

View File

@ -1,45 +0,0 @@
package io.metersphere.bug.dto.request;
import io.metersphere.bug.dto.response.BugCustomFieldDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
public class BugQuickEditRequest {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.id.not_blank}")
@Size(min = 1, max = 50, message = "{bug.id.length_range}")
private String id;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{bug.project_id.length_range}")
private String projectId;
@Schema(description = "模板ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.template_id.not_blank}")
@Size(min = 1, max = 50, message = "{bug.template_id.length_range}")
private String templateId;
@Schema(description = "处理人")
private String handleUser;
@Schema(description = "状态")
private String status;
@Schema(description = "标签")
private List<String> tags;
@Schema(description = "缺陷内容")
private String description;
@Schema(description = "自定义字段集合")
private List<BugCustomFieldDTO> customFields;
}

View File

@ -1,6 +1,10 @@
package io.metersphere.bug.dto.response; package io.metersphere.bug.dto.response;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -11,7 +15,38 @@ import java.util.List;
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class BugDetailDTO extends BugDTO { public class BugDetailDTO {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{bug.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(description = "缺陷标题")
@Size(min = 1, max = 300, message = "{bug.title.length_range}", groups = {Created.class, Updated.class})
private String title;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.project_id.not_blank}", groups = {Created.class, Updated.class})
@Size(min = 1, max = 50, message = "{bug.project_id.length_range}", groups = {Created.class, Updated.class})
private String projectId;
@Schema(description = "模板ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.template_id.not_blank}", groups = {Created.class, Updated.class})
@Size(min = 1, max = 50, message = "{bug.template_id.length_range}", groups = {Created.class, Updated.class})
private String templateId;
@Schema(description = "标签")
private List<String> tags;
@Schema(description = "缺陷内容")
private String description;
@Schema(description = "自定义字段集合")
private List<BugCustomFieldDTO> customFields;
@Schema(description = "是否平台默认模板")
private Boolean platformDefault;
@Schema(description = "附件集合") @Schema(description = "附件集合")
List<BugFileDTO> attachments; List<BugFileDTO> attachments;

View File

@ -61,6 +61,29 @@ public interface ExtBugMapper {
*/ */
void batchUpdate(@Param("request") BugBatchUpdateRequest request, @Param("ids") List<String> ids); void batchUpdate(@Param("request") BugBatchUpdateRequest request, @Param("ids") List<String> ids);
/**
* 获取前置排序位置
* @param projectId 项目ID
* @param basePos 目标位置
* @return 排序位置
*/
Long getPrePos(@Param("projectId") String projectId, @Param("basePos") Long basePos);
/**
* 获取后置排序位置
* @param projectId 项目ID
* @param basePos 目标位置
* @return 排序位置
*/
Long getLastPos(@Param("projectId") String projectId, @Param("basePos") Long basePos);
/**
* 获取当前项目下的最大排序位置
* @param projectId 项目ID
* @return 最大排序位置
*/
Long getMaxPos(@Param("projectId") String projectId);
List<BugProviderDTO> listByProviderRequest(@Param("table") String sourceType, @Param("sourceName") String sourceName, @Param("bugColumnName") String bugColumnName, @Param("request") BugPageProviderRequest bugPageProviderRequest, @Param("deleted") boolean deleted); List<BugProviderDTO> listByProviderRequest(@Param("table") String sourceType, @Param("sourceName") String sourceName, @Param("bugColumnName") String bugColumnName, @Param("request") BugPageProviderRequest bugPageProviderRequest, @Param("deleted") boolean deleted);
List<String> getIdsByProvider(@Param("request") AssociateBugRequest request, @Param("deleted") boolean deleted); List<String> getIdsByProvider(@Param("request") AssociateBugRequest request, @Param("deleted") boolean deleted);

View File

@ -38,10 +38,8 @@
b.template_id, b.template_id,
b.platform, b.platform,
b.status, b.status,
b.tags, b.tags
bc.description
from bug b from bug b
left join bug_content bc on b.id = bc.bug_id
WHERE b.id IN WHERE b.id IN
<foreach collection="ids" item="id" separator="," open="(" close=")"> <foreach collection="ids" item="id" separator="," open="(" close=")">
#{id} #{id}
@ -78,6 +76,27 @@
</foreach> </foreach>
</update> </update>
<select id="getPrePos" resultType="java.lang.Long">
select `pos` from bug where project_id = #{projectId}
<if test="basePos != null">
and `pos` &lt; #{basePos}
</if>
order by `pos` desc limit 1;
</select>
<select id="getLastPos" resultType="java.lang.Long">
select `pos` from bug where project_id = #{projectId}
<if test="basePos != null">
and `pos` &gt; #{basePos}
</if>
order by `pos` desc limit 1;
</select>
<select id="getMaxPos" resultType="java.lang.Long">
select max(pos) from bug where project_id = #{projectId}
</select>
<select id="listByProviderRequest" resultMap="BugProviderDTO"> <select id="listByProviderRequest" resultMap="BugProviderDTO">
SELECT SELECT
b.id as id, b.id as id,

View File

@ -53,6 +53,8 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -180,7 +182,7 @@ public class BugAttachmentService {
} else { } else {
// 删除本地上传的文件 // 删除本地上传的文件
List<SyncAttachmentToPlatformRequest> syncLocalFiles = List<SyncAttachmentToPlatformRequest> syncLocalFiles =
deleteLocalFile(bug.getId(), bug.getPlatformBugId(), request.getProjectId(), tempFileDir, request.getRefId(), bug.getPlatform(), true); deleteLocalFile(bug.getId(), bug.getPlatformBugId(), request.getProjectId(), tempFileDir, request.getRefId(), bug.getPlatform());
platformAttachments.addAll(syncLocalFiles); platformAttachments.addAll(syncLocalFiles);
} }
// 同步至第三方(异步调用) // 同步至第三方(异步调用)
@ -200,9 +202,10 @@ public class BugAttachmentService {
return ResponseEntity.ok().contentType(MediaType.parseMediaType("application/octet-stream")).body(null); return ResponseEntity.ok().contentType(MediaType.parseMediaType("application/octet-stream")).body(null);
} }
byte[] bytes = getLocalFileBytes(attachment, request.getProjectId(), request.getBugId()); byte[] bytes = getLocalFileBytes(attachment, request.getProjectId(), request.getBugId());
String name = URLEncoder.encode(attachment.getFileName(), StandardCharsets.UTF_8);
return ResponseEntity.ok() return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream")) .contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + attachment.getFileName() + "\"") .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + name + "\";" + "filename*=utf-8''"+ name)
.body(bytes); .body(bytes);
} }
@ -225,7 +228,7 @@ public class BugAttachmentService {
association.setModuleId(request.getModuleId()); association.setModuleId(request.getModuleId());
fileId = fileAssociationService.transferAndAssociation(association); fileId = fileAssociationService.transferAndAssociation(association);
// 删除本地上传的附件 // 删除本地上传的附件
deleteLocalFile(request.getBugId(), null, request.getProjectId(), null, attachment.getId(), null, false); deleteLocalFile(request.getBugId(), null, request.getProjectId(), null, attachment.getId(), null);
} catch (Exception e) { } catch (Exception e) {
throw new MSException(Translator.get("file.transfer.error")); throw new MSException(Translator.get("file.transfer.error"));
} }
@ -419,7 +422,7 @@ public class BugAttachmentService {
* @return 同步至平台的附件集合 * @return 同步至平台的附件集合
*/ */
private List<SyncAttachmentToPlatformRequest> deleteLocalFile(String bugId, String platformBugKey, String projectId, File tmpFileDir, private List<SyncAttachmentToPlatformRequest> deleteLocalFile(String bugId, String platformBugKey, String projectId, File tmpFileDir,
String refId, String platformName, boolean syncToPlatform) { String refId, String platformName) {
List<SyncAttachmentToPlatformRequest> syncLocalFiles = new ArrayList<>(); List<SyncAttachmentToPlatformRequest> syncLocalFiles = new ArrayList<>();
BugLocalAttachment localAttachment = bugLocalAttachmentMapper.selectByPrimaryKey(refId); BugLocalAttachment localAttachment = bugLocalAttachmentMapper.selectByPrimaryKey(refId);
// 删除本地上传的附件, BUG_LOCAL_ATTACHMENT表 // 删除本地上传的附件, BUG_LOCAL_ATTACHMENT表
@ -428,7 +431,7 @@ public class BugAttachmentService {
// 删除MINIO附件 // 删除MINIO附件
fileService.deleteFile(fileRequest); fileService.deleteFile(fileRequest);
// 删除的本地的附件同步至平台 // 删除的本地的附件同步至平台
if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName()) && syncToPlatform) { if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) {
File deleteTmpFile = new File(tmpFileDir, localAttachment.getFileName()); File deleteTmpFile = new File(tmpFileDir, localAttachment.getFileName());
syncLocalFiles.add(new SyncAttachmentToPlatformRequest(platformBugKey, deleteTmpFile, SyncAttachmentType.DELETE.syncOperateType())); syncLocalFiles.add(new SyncAttachmentToPlatformRequest(platformBugKey, deleteTmpFile, SyncAttachmentType.DELETE.syncOperateType()));
} }

View File

@ -72,7 +72,7 @@ public class BugCommentService {
commentUserInfos.add(userMap.get(bugComment.getReplyUser())); commentUserInfos.add(userMap.get(bugComment.getReplyUser()));
} }
commentUserInfos.addAll(getNotifyUserInfo(bugComment.getNotifier(), userMap)); commentUserInfos.addAll(getNotifyUserInfo(bugComment.getNotifier(), userMap));
commentDTO.setCommentUserInfos(commentUserInfos); commentDTO.setCommentUserInfos(commentUserInfos.stream().filter(Objects::nonNull).distinct().toList());
return commentDTO; return commentDTO;
}).toList(); }).toList();
@ -90,12 +90,12 @@ public class BugCommentService {
/** /**
* 批量获取缺陷ID * 批量获取缺陷评论
*/ */
public Map<String, List<BugCommentDTO>> getComments(@NotEmpty List<String> bugIds) { public Map<String, List<BugCommentDTO>> getComments(@NotEmpty List<String> bugIds) {
BugCommentExample example = new BugCommentExample(); BugCommentExample example = new BugCommentExample();
example.createCriteria().andBugIdIn(bugIds); example.createCriteria().andBugIdIn(bugIds);
List<BugComment> bugComments = bugCommentMapper.selectByExample(example); List<BugComment> bugComments = bugCommentMapper.selectByExampleWithBLOBs(example);
Map<String, List<BugComment>> bugCommentByBugId = bugComments.stream().collect(Collectors.groupingBy(BugComment::getBugId)); Map<String, List<BugComment>> bugCommentByBugId = bugComments.stream().collect(Collectors.groupingBy(BugComment::getBugId));
Map<String, List<BugCommentDTO>> returnMap = new HashMap<>(); Map<String, List<BugCommentDTO>> returnMap = new HashMap<>();

View File

@ -6,9 +6,12 @@ import io.metersphere.bug.domain.BugContent;
import io.metersphere.bug.domain.BugContentExample; import io.metersphere.bug.domain.BugContentExample;
import io.metersphere.bug.dto.BugExportColumn; import io.metersphere.bug.dto.BugExportColumn;
import io.metersphere.bug.dto.BugExportExcelModel; import io.metersphere.bug.dto.BugExportExcelModel;
import io.metersphere.bug.dto.BugExportHeaderModel;
import io.metersphere.bug.dto.response.BugCommentDTO; import io.metersphere.bug.dto.response.BugCommentDTO;
import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.bug.dto.response.BugDTO;
import io.metersphere.bug.mapper.BugContentMapper; import io.metersphere.bug.mapper.BugContentMapper;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
@ -29,55 +32,55 @@ public class BugExportService {
private static final int BATCH_PROCESS_QUANTITY = 2000; private static final int BATCH_PROCESS_QUANTITY = 2000;
private static final String EXPORT_TEMP_BASE_FOLDER = "/tmp/metersphere/export/bug/"; private static final String EXPORT_TEMP_BASE_FOLDER = "/tmp/metersphere/export/bug/";
@Resource
private BugContentMapper bugContentMapper;
@Resource @Resource
private BugCommentService bugCommentService; private BugCommentService bugCommentService;
@Resource
private BugContentMapper bugContentMapper;
/** /**
* @param list 缺陷数据 * @param list 缺陷数据
* @param exportColumns excel导出的列 * @param headerModel excel导出表头
* @return excel所在的文件夹 * @return excel所在的文件夹
* @throws Exception
*/ */
public String generateExcelFiles(List<BugDTO> list, List<BugExportColumn> exportColumns) { public String generateExcelFiles(List<BugDTO> list, BugExportHeaderModel headerModel) {
String filesFolder = EXPORT_TEMP_BASE_FOLDER + IDGenerator.nextStr(); String filesFolder = EXPORT_TEMP_BASE_FOLDER + IDGenerator.nextStr();
try { try {
FileUtils.forceMkdir(new File(filesFolder)); FileUtils.forceMkdir(new File(filesFolder));
int index = 1; int index = 1;
while (list.size() > 2000) { while (list.size() > BATCH_PROCESS_QUANTITY) {
List<BugDTO> excelBugList = list.subList(0, BATCH_PROCESS_QUANTITY); List<BugDTO> excelBugList = list.subList(0, BATCH_PROCESS_QUANTITY);
this.generateExcelFile(excelBugList, index, filesFolder, exportColumns); this.generateExcelFile(excelBugList, index, filesFolder, headerModel);
list.removeAll(excelBugList); list.removeAll(excelBugList);
index = 1; index += 1;
} }
this.generateExcelFile(list, index, filesFolder, exportColumns); this.generateExcelFile(list, index, filesFolder, headerModel);
} catch (Exception ignore) { } catch (Exception e) {
LogUtils.error(e.getMessage());
throw new MSException(e.getMessage());
} }
return filesFolder; return filesFolder;
} }
private void generateExcelFile(List<BugDTO> list, int fileIndex, String excelPath, List<BugExportColumn> exportColumns) throws Exception { private void generateExcelFile(List<BugDTO> list, int fileIndex, String excelPath, BugExportHeaderModel headerModel) throws Exception {
if (CollectionUtils.isNotEmpty(list)) { if (CollectionUtils.isNotEmpty(list)) {
boolean exportComment = this.exportComment(exportColumns); // 准备数据 {评论, 内容, 关联用例数}
boolean exportContent = this.exportContent(exportColumns); boolean exportComment = this.exportComment(headerModel.getExportColumns());
boolean exportContent = this.exportContent(headerModel.getExportColumns());
List<String> bugIdList = list.stream().map(BugDTO::getId).toList(); List<String> bugIdList = list.stream().map(BugDTO::getId).toList();
Map<String, List<BugCommentDTO>> bugCommentMap = new HashMap<>(); Map<String, List<BugCommentDTO>> bugCommentMap = new HashMap<>(16);
Map<String, BugContent> bugContentMap = new HashMap<>(); Map<String, BugContent> bugContentMap = new HashMap<>(16);
//todo 等昌昌需求确定再实现
Map<String, Long> bugCountMap = new HashMap<>();
if (exportContent) { if (exportContent) {
BugContentExample example = new BugContentExample(); BugContentExample example = new BugContentExample();
example.createCriteria().andBugIdIn(bugIdList); example.createCriteria().andBugIdIn(bugIdList);
bugContentMap = bugContentMapper.selectByExample(example).stream().collect(Collectors.toMap(BugContent::getBugId, bugContent -> bugContent)); bugContentMap = bugContentMapper.selectByExampleWithBLOBs(example).stream().collect(Collectors.toMap(BugContent::getBugId, bugContent -> bugContent));
} }
if (exportComment) { if (exportComment) {
bugCommentMap = bugCommentService.getComments(bugIdList); bugCommentMap = bugCommentService.getComments(bugIdList);
} }
//生成excel对象 //生成excel对象
BugExportExcelModel bugExportExcelModel = new BugExportExcelModel(exportColumns, list, bugCommentMap, bugContentMap, bugCountMap); BugExportExcelModel bugExportExcelModel = new BugExportExcelModel(headerModel, list, bugCommentMap, bugContentMap);
//生成excel文件 //生成excel文件
List<List<String>> data = bugExportExcelModel.getData(); List<List<String>> data = bugExportExcelModel.getData();
@ -88,7 +91,11 @@ public class BugExportService {
} }
} }
//是否包含缺陷评论 /**
* 是否包含缺陷评论
* @param exportColumns 导出列
* @return 是否包含评论列
*/
public boolean exportComment(List<BugExportColumn> exportColumns) { public boolean exportComment(List<BugExportColumn> exportColumns) {
for (BugExportColumn exportColumn : exportColumns) { for (BugExportColumn exportColumn : exportColumns) {
if ("comment".equals(exportColumn.getKey()) && "other".equals(exportColumn.getColumnType())) { if ("comment".equals(exportColumn.getKey()) && "other".equals(exportColumn.getColumnType())) {
@ -98,7 +105,11 @@ public class BugExportService {
return false; return false;
} }
//是否包含缺陷内容 /**
* 是否包含缺陷内容
* @param exportColumns 导出列
* @return 是否包含缺陷内容列
*/
public boolean exportContent(List<BugExportColumn> exportColumns) { public boolean exportContent(List<BugExportColumn> exportColumns) {
for (BugExportColumn exportColumn : exportColumns) { for (BugExportColumn exportColumn : exportColumns) {
if ("content".equals(exportColumn.getKey()) && "system".equals(exportColumn.getColumnType())) { if ("content".equals(exportColumn.getKey()) && "system".equals(exportColumn.getColumnType())) {

View File

@ -0,0 +1,22 @@
package io.metersphere.bug.service;
import io.metersphere.bug.dto.request.BugHistoryPageRequest;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.system.dto.OperationHistoryDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
public class BugHistoryService {
public List<OperationHistoryDTO> list(BugHistoryPageRequest request) {
XpackBugService bugService = CommonBeanFactory.getBean(XpackBugService.class);
if (bugService != null) {
return bugService.listHis(request);
}
return List.of();
}
}

View File

@ -2,6 +2,7 @@ package io.metersphere.bug.service;
import io.metersphere.bug.constants.BugExportColumns; import io.metersphere.bug.constants.BugExportColumns;
import io.metersphere.bug.domain.*; import io.metersphere.bug.domain.*;
import io.metersphere.bug.dto.BugExportHeaderModel;
import io.metersphere.bug.dto.BugTemplateInjectField; import io.metersphere.bug.dto.BugTemplateInjectField;
import io.metersphere.bug.dto.request.*; import io.metersphere.bug.dto.request.*;
import io.metersphere.bug.dto.response.*; import io.metersphere.bug.dto.response.*;
@ -32,12 +33,14 @@ import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileRequest; import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.*; import io.metersphere.sdk.util.*;
import io.metersphere.system.domain.CustomFieldOption;
import io.metersphere.system.domain.ServiceIntegration; import io.metersphere.system.domain.ServiceIntegration;
import io.metersphere.system.domain.Template; import io.metersphere.system.domain.Template;
import io.metersphere.system.domain.TemplateCustomField; import io.metersphere.system.domain.TemplateCustomField;
import io.metersphere.system.dto.sdk.OptionDTO; import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO; import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
import io.metersphere.system.dto.sdk.TemplateDTO; import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.dto.sdk.request.PosRequest;
import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.dto.LogDTO; import io.metersphere.system.log.dto.LogDTO;
@ -47,6 +50,7 @@ import io.metersphere.system.mapper.TemplateMapper;
import io.metersphere.system.service.*; import io.metersphere.system.service.*;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator; import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.ServiceUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jodd.util.StringUtil; import jodd.util.StringUtil;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
@ -69,6 +73,8 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -153,6 +159,8 @@ public class BugService {
@Resource @Resource
private BugAttachmentService bugAttachmentService; private BugAttachmentService bugAttachmentService;
public static final Long INTERVAL_POS = 5000L;
/** /**
* 缺陷列表查询 * 缺陷列表查询
* *
@ -175,8 +183,11 @@ public class BugService {
* @param request 缺陷请求参数 * @param request 缺陷请求参数
* @param files 附件集合 * @param files 附件集合
* @param currentUser 当前用户 * @param currentUser 当前用户
* @param currentOrgId 当前组织ID
* @param isUpdate 是否更新
* @return 缺陷
*/ */
public void addOrUpdate(BugEditRequest request, List<MultipartFile> files, String currentUser, String currentOrgId, boolean isUpdate) { public Bug addOrUpdate(BugEditRequest request, List<MultipartFile> files, String currentUser, String currentOrgId, boolean isUpdate) {
/* /*
* 缺陷创建或者修改逻辑: * 缺陷创建或者修改逻辑:
* 1. 判断所属项目是否关联第三方平台; * 1. 判断所属项目是否关联第三方平台;
@ -208,11 +219,12 @@ public class BugService {
} }
} }
// 处理基础字段 // 处理基础字段
handleAndSaveBug(request, currentUser, platformName, platformBug); Bug bug = handleAndSaveBug(request, currentUser, platformName, platformBug);
// 处理自定义字段 // 处理自定义字段
handleAndSaveCustomFields(request, isUpdate); handleAndSaveCustomFields(request, isUpdate);
// 处理附件 // 处理附件
handleAndSaveAttachments(request, files, currentUser, platformName, platformBug); handleAndSaveAttachments(request, files, currentUser, platformName, platformBug);
return bug;
} }
/** /**
@ -221,20 +233,52 @@ public class BugService {
* @return 缺陷详情 * @return 缺陷详情
*/ */
public BugDetailDTO get(String id) { public BugDetailDTO get(String id) {
BugDetailDTO bugDetail = new BugDetailDTO();
Bug bug = checkBugExist(id); Bug bug = checkBugExist(id);
BeanUtils.copyBean(bugDetail, bug); TemplateDTO template = getTemplate(bug.getTemplateId(), bug.getProjectId(), null, null);
// 缺陷内容 List<BugCustomFieldDTO> allCustomFields = extBugCustomFieldMapper.getBugAllCustomFields(List.of(id), bug.getProjectId());
BugContent bugContent = bugContentMapper.selectByPrimaryKey(id); BugDetailDTO detail = new BugDetailDTO();
if (bugContent != null) { detail.setId(id);
bugDetail.setDescription(bugContent.getDescription()); detail.setProjectId(bug.getProjectId());
detail.setTemplateId(template.getId());
detail.setPlatformDefault(template.getPlatformDefault());
if (!detail.getPlatformDefault()) {
// 非平台默认模板 {标题, 内容, 标签, 自定义字段: 处理人, 状态}
detail.setTitle(bug.getTitle());
BugContent bugContent = bugContentMapper.selectByPrimaryKey(id);
detail.setDescription(bugContent.getDescription());
detail.setTags(bug.getTags());
template.getCustomFields().forEach(field -> {
// 状态
if (StringUtils.equals(field.getFieldKey(), BugTemplateCustomField.STATUS.getId())) {
BugCustomFieldDTO status = new BugCustomFieldDTO();
status.setId(field.getFieldId());
status.setName(field.getFieldName());
status.setType(field.getType());
status.setValue(bug.getStatus());
allCustomFields.addFirst(status);
}
// 处理人
if (StringUtils.equals(field.getFieldKey(), BugTemplateCustomField.HANDLE_USER.getId())) {
BugCustomFieldDTO handleUser = new BugCustomFieldDTO();
handleUser.setId(field.getFieldId());
handleUser.setName(field.getFieldName());
handleUser.setType(field.getType());
handleUser.setValue(bug.getHandleUser());
allCustomFields.addFirst(handleUser);
}
});
} else {
// 平台默认模板
allCustomFields.forEach(field -> template.getCustomFields().stream().filter(templateField -> StringUtils.equals(templateField.getFieldId(), field.getId())).findFirst().ifPresent(templateField -> {
field.setName(templateField.getFieldName());
field.setType(templateField.getType());
}));
} }
// 缺陷自定义字段 // 缺陷自定义字段
List<BugCustomFieldDTO> customFields = extBugCustomFieldMapper.getBugAllCustomFields(List.of(id), bug.getProjectId()); detail.setCustomFields(allCustomFields);
bugDetail.setCustomFields(customFields);
// 缺陷附件信息 // 缺陷附件信息
bugDetail.setAttachments(bugAttachmentService.getAllBugFiles(id)); detail.setAttachments(bugAttachmentService.getAllBugFiles(id));
return bugDetail; return detail;
} }
/** /**
@ -386,6 +430,19 @@ public class BugService {
} }
} }
/**
* 拖拽缺陷位置
* @param request 请求参数
*/
public void editPos(PosRequest request) {
ServiceUtils.updatePosField(request,
Bug.class,
bugMapper::selectByPrimaryKey,
extBugMapper::getPrePos,
extBugMapper::getLastPos,
bugMapper::updateByPrimaryKeySelective);
}
/** /**
* 关注缺陷 * 关注缺陷
* @param id 缺陷ID * @param id 缺陷ID
@ -571,6 +628,7 @@ public class BugService {
// 状态字段 // 状态字段
attachTemplateStatusField(templateDTO, projectId, fromStatusId, platformBugKey); attachTemplateStatusField(templateDTO, projectId, fromStatusId, platformBugKey);
List<CustomFieldOption> handleUserOption = new ArrayList<>();
// 内置字段(处理人字段) // 内置字段(处理人字段)
if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) {
// 获取插件中自定义的注入字段(处理人) // 获取插件中自定义的注入字段(处理人)
@ -584,10 +642,22 @@ public class BugService {
BeanUtils.copyBean(templateCustomFieldDTO, injectField); BeanUtils.copyBean(templateCustomFieldDTO, injectField);
templateCustomFieldDTO.setFieldId(injectField.getId()); templateCustomFieldDTO.setFieldId(injectField.getId());
templateCustomFieldDTO.setFieldName(injectField.getName()); templateCustomFieldDTO.setFieldName(injectField.getName());
templateCustomFieldDTO.setFieldKey(injectField.getKey());
GetOptionRequest request = new GetOptionRequest(); GetOptionRequest request = new GetOptionRequest();
request.setOptionMethod(injectField.getOptionMethod()); request.setOptionMethod(injectField.getOptionMethod());
request.setProjectConfig(projectApplicationService.getProjectBugThirdPartConfig(projectId)); request.setProjectConfig(projectApplicationService.getProjectBugThirdPartConfig(projectId));
templateCustomFieldDTO.setPlatformOptionJson(JSON.toJSONString(platform.getFormOptions(request))); if (StringUtils.equals(injectField.getKey(), BugTemplateCustomField.HANDLE_USER.getId())) {
List<SelectOption> formOptions = platform.getFormOptions(request);
handleUserOption = formOptions.stream().map(user -> {
CustomFieldOption option = new CustomFieldOption();
option.setText(user.getText());
option.setValue(user.getValue());
return option;
}).toList();
templateCustomFieldDTO.setOptions(handleUserOption);
} else {
templateCustomFieldDTO.setPlatformOptionJson(JSON.toJSONString(platform.getFormOptions(request)));
}
templateDTO.getCustomFields().addFirst(templateCustomFieldDTO); templateDTO.getCustomFields().addFirst(templateCustomFieldDTO);
} }
} else { } else {
@ -595,12 +665,28 @@ public class BugService {
TemplateCustomFieldDTO handleUserField = new TemplateCustomFieldDTO(); TemplateCustomFieldDTO handleUserField = new TemplateCustomFieldDTO();
handleUserField.setFieldId(BugTemplateCustomField.HANDLE_USER.getId()); handleUserField.setFieldId(BugTemplateCustomField.HANDLE_USER.getId());
handleUserField.setFieldName(BugTemplateCustomField.HANDLE_USER.getName()); handleUserField.setFieldName(BugTemplateCustomField.HANDLE_USER.getName());
handleUserField.setFieldKey(BugTemplateCustomField.HANDLE_USER.getId());
handleUserField.setType(CustomFieldType.SELECT.getType()); handleUserField.setType(CustomFieldType.SELECT.getType());
handleUserField.setPlatformOptionJson(JSON.toJSONString(getLocalHandlerOption(projectId))); List<SelectOption> localHandlerOption = getLocalHandlerOption(projectId);
handleUserOption = localHandlerOption.stream().map(user -> {
CustomFieldOption option = new CustomFieldOption();
option.setText(user.getText());
option.setValue(user.getValue());
return option;
}).toList();
handleUserField.setOptions(handleUserOption);
handleUserField.setRequired(true); handleUserField.setRequired(true);
templateDTO.getCustomFields().addFirst(handleUserField); templateDTO.getCustomFields().addFirst(handleUserField);
} }
// 成员类型的自定义字段, 选项值与处理人选项保持一致
final List<CustomFieldOption> memberOption = handleUserOption;
templateDTO.getCustomFields().forEach(field -> {
if (StringUtils.equalsAny(field.getType(), CustomFieldType.MEMBER.getType(), CustomFieldType.MULTIPLE_MEMBER.getType())) {
field.setPlatformOptionJson(JSON.toJSONString(memberOption));
}
});
return templateDTO; return templateDTO;
} }
@ -619,8 +705,16 @@ public class BugService {
TemplateCustomFieldDTO statusField = new TemplateCustomFieldDTO(); TemplateCustomFieldDTO statusField = new TemplateCustomFieldDTO();
statusField.setFieldId(BugTemplateCustomField.STATUS.getId()); statusField.setFieldId(BugTemplateCustomField.STATUS.getId());
statusField.setFieldName(BugTemplateCustomField.STATUS.getName()); statusField.setFieldName(BugTemplateCustomField.STATUS.getName());
statusField.setFieldKey(BugTemplateCustomField.STATUS.getId());
statusField.setType(CustomFieldType.SELECT.getType()); statusField.setType(CustomFieldType.SELECT.getType());
statusField.setPlatformOptionJson(JSON.toJSONString(bugStatusService.getToStatusItemOption(projectId, fromStatusId, platformBugKey))); List<SelectOption> statusOption = bugStatusService.getToStatusItemOption(projectId, fromStatusId, platformBugKey);
List<CustomFieldOption> statusCustomOption = statusOption.stream().map(option -> {
CustomFieldOption customFieldOption = new CustomFieldOption();
customFieldOption.setText(option.getText());
customFieldOption.setValue(option.getValue());
return customFieldOption;
}).toList();
statusField.setOptions(statusCustomOption);
statusField.setRequired(true); statusField.setRequired(true);
templateDTO.getCustomFields().addFirst(statusField); templateDTO.getCustomFields().addFirst(statusField);
return templateDTO; return templateDTO;
@ -633,7 +727,7 @@ public class BugService {
* @param currentUser 当前用户ID * @param currentUser 当前用户ID
* @param platformName 第三方平台名称 * @param platformName 第三方平台名称
*/ */
private void handleAndSaveBug(BugEditRequest request, String currentUser, String platformName, PlatformBugUpdateDTO platformBug) { private Bug handleAndSaveBug(BugEditRequest request, String currentUser, String platformName, PlatformBugUpdateDTO platformBug) {
Bug bug = new Bug(); Bug bug = new Bug();
BeanUtils.copyBean(bug, request); BeanUtils.copyBean(bug, request);
bug.setPlatform(platformName); bug.setPlatform(platformName);
@ -687,11 +781,12 @@ public class BugService {
bug.setDeleteUser(currentUser); bug.setDeleteUser(currentUser);
bug.setDeleteTime(System.currentTimeMillis()); bug.setDeleteTime(System.currentTimeMillis());
bug.setDeleted(false); bug.setDeleted(false);
bug.setPos(getNextPos(request.getProjectId()));
bugMapper.insert(bug); bugMapper.insert(bug);
request.setId(bug.getId()); request.setId(bug.getId());
BugContent bugContent = new BugContent(); BugContent bugContent = new BugContent();
bugContent.setBugId(bug.getId()); bugContent.setBugId(bug.getId());
bugContent.setDescription(request.getDescription()); bugContent.setDescription(StringUtils.isEmpty(request.getDescription()) ? StringUtils.EMPTY : request.getDescription());
bugContentMapper.insert(bugContent); bugContentMapper.insert(bugContent);
} else { } else {
Bug orignalBug = checkBugExist(request.getId()); Bug orignalBug = checkBugExist(request.getId());
@ -702,16 +797,12 @@ public class BugService {
bug.setUpdateUser(currentUser); bug.setUpdateUser(currentUser);
bug.setUpdateTime(System.currentTimeMillis()); bug.setUpdateTime(System.currentTimeMillis());
bugMapper.updateByPrimaryKeySelective(bug); bugMapper.updateByPrimaryKeySelective(bug);
BugContent originalContent = bugContentMapper.selectByPrimaryKey(bug.getId());
BugContent bugContent = new BugContent(); BugContent bugContent = new BugContent();
bugContent.setBugId(bug.getId()); bugContent.setBugId(bug.getId());
bugContent.setDescription(request.getDescription()); bugContent.setDescription(StringUtils.isEmpty(request.getDescription()) ? StringUtils.EMPTY : request.getDescription());
if (originalContent == null) { bugContentMapper.updateByPrimaryKeySelective(bugContent);
bugContentMapper.insert(bugContent);
} else {
bugContentMapper.updateByPrimaryKeySelective(bugContent);
}
} }
return bug;
} }
/** /**
@ -906,7 +997,7 @@ public class BugService {
fileService.upload(file, fileRequest); fileService.upload(file, fileRequest);
// 同步新上传的附件至平台 // 同步新上传的附件至平台
if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) {
File uploadTmpFile = new File(tempFileDir, Objects.requireNonNull(file.getOriginalFilename())).toPath().normalize().toFile();; File uploadTmpFile = new File(tempFileDir, Objects.requireNonNull(file.getOriginalFilename())).toPath().normalize().toFile();
FileUtils.writeByteArrayToFile(uploadTmpFile, file.getBytes()); FileUtils.writeByteArrayToFile(uploadTmpFile, file.getBytes());
uploadPlatformAttachments.add(new SyncAttachmentToPlatformRequest(platformBug.getPlatformBugKey(), uploadTmpFile, SyncAttachmentType.UPLOAD.syncOperateType())); uploadPlatformAttachments.add(new SyncAttachmentToPlatformRequest(platformBug.getPlatformBugKey(), uploadTmpFile, SyncAttachmentType.UPLOAD.syncOperateType()));
} }
@ -981,7 +1072,7 @@ public class BugService {
* @param bugs 缺陷集合 * @param bugs 缺陷集合
* @return 缺陷DTO集合 * @return 缺陷DTO集合
*/ */
private List<BugDTO> buildExtraInfo(List<BugDTO> bugs) { public List<BugDTO> buildExtraInfo(List<BugDTO> bugs) {
// 获取用户集合 // 获取用户集合
List<String> userIds = new ArrayList<>(bugs.stream().map(BugDTO::getCreateUser).toList()); List<String> userIds = new ArrayList<>(bugs.stream().map(BugDTO::getCreateUser).toList());
userIds.addAll(bugs.stream().map(BugDTO::getUpdateUser).toList()); userIds.addAll(bugs.stream().map(BugDTO::getUpdateUser).toList());
@ -1169,15 +1260,31 @@ public class BugService {
* @throws Exception 异常 * @throws Exception 异常
*/ */
public ResponseEntity<byte[]> export(BugExportRequest request) throws Exception { public ResponseEntity<byte[]> export(BugExportRequest request) throws Exception {
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
// 准备导出缺陷, 自定义字段, 自定义字段选项值
List<BugDTO> bugs = this.getExportDataByBatchRequest(request); List<BugDTO> bugs = this.getExportDataByBatchRequest(request);
if (CollectionUtils.isEmpty(bugs)) { if (CollectionUtils.isEmpty(bugs)) {
throw new MSException(Translator.get("no_bug_select")); throw new MSException(Translator.get("no_bug_select"));
} }
ExportUtils exportUtils = new ExportUtils(bugs, request.getExportColumns()); // 缺陷自定义字段内容及补充内容
handleCustomField(bugs, request.getProjectId());
bugs = buildExtraInfo(bugs);
// 表头处理人选项
List<SelectOption> handleUserOption = getHeaderHandlerOption(request.getProjectId());
Map<String, String> handleUserMap = handleUserOption.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
// 表头状态选项
List<SelectOption> statusOption = bugStatusService.getHeaderStatusOption(request.getProjectId());
Map<String, String> statusMap = statusOption.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
// 表头自定义字段
List<TemplateCustomFieldDTO> headerCustomFields = getHeaderCustomFields(request.getProjectId());
ExportUtils exportUtils = new ExportUtils(bugs, BugExportHeaderModel.builder().exportColumns(request.getExportColumns()).headerCustomFields(headerCustomFields)
.handleUserMap(handleUserMap).statusMap(statusMap).build());
// 导出
byte[] bytes = exportUtils.exportToZipFile(bugExportService::generateExcelFiles); byte[] bytes = exportUtils.exportToZipFile(bugExportService::generateExcelFiles);
String zipName = "MeterSphere_bug_" + URLEncoder.encode(project.getName(), StandardCharsets.UTF_8) + ".zip";
return ResponseEntity.ok() return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream")) .contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"bug-export.zip\"") .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + zipName + "\";" + "filename*=utf-8''"+ zipName)
.body(bytes); .body(bytes);
} }
@ -1188,7 +1295,9 @@ public class BugService {
*/ */
public BugExportColumns getExportColumns(String projectId) { public BugExportColumns getExportColumns(String projectId) {
BugExportColumns bugExportColumns = new BugExportColumns(); BugExportColumns bugExportColumns = new BugExportColumns();
//todo 等待Scc提供自定义字段的查询方法 // 表头自定义字段
List<TemplateCustomFieldDTO> headerCustomFields = getHeaderCustomFields(projectId);
bugExportColumns.initCustomColumns(headerCustomFields);
return bugExportColumns; return bugExportColumns;
} }
@ -1253,7 +1362,7 @@ public class BugService {
String platformName = projectApplicationService.getPlatformName(projectId); String platformName = projectApplicationService.getPlatformName(projectId);
// 需要校验服务集成是否开启 // 需要校验服务集成是否开启
ServiceIntegration serviceIntegration = projectApplicationService.getPlatformServiceIntegrationWithSyncOrDemand(projectId, true); ServiceIntegration serviceIntegration = projectApplicationService.getPlatformServiceIntegrationWithSyncOrDemand(projectId, true);
if (StringUtils.equals(platformName, BugPlatform.LOCAL.getName()) || serviceIntegration == null) { if (StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) {
// Local处理人 // Local处理人
return getLocalHandlerOption(projectId); return getLocalHandlerOption(projectId);
} else { } else {
@ -1265,7 +1374,7 @@ public class BugService {
List<SelectOption> platformHandlerOption = new ArrayList<>(); List<SelectOption> platformHandlerOption = new ArrayList<>();
List<BugTemplateInjectField> platformInjectFields = getPlatformInjectFields(projectId); List<BugTemplateInjectField> platformInjectFields = getPlatformInjectFields(projectId);
for (BugTemplateInjectField injectField : platformInjectFields) { for (BugTemplateInjectField injectField : platformInjectFields) {
if (StringUtils.equals(injectField.getKey(), "assignee")) { if (StringUtils.equals(injectField.getKey(), BugTemplateCustomField.HANDLE_USER.getId())) {
GetOptionRequest request = new GetOptionRequest(); GetOptionRequest request = new GetOptionRequest();
request.setOptionMethod(injectField.getOptionMethod()); request.setOptionMethod(injectField.getOptionMethod());
request.setProjectConfig(projectApplicationService.getProjectBugThirdPartConfig(projectId)); request.setProjectConfig(projectApplicationService.getProjectBugThirdPartConfig(projectId));
@ -1317,6 +1426,19 @@ public class BugService {
// 本地模板 // 本地模板
List<Template> templates = projectTemplateService.getTemplates(projectId, TemplateScene.BUG.name()); List<Template> templates = projectTemplateService.getTemplates(projectId, TemplateScene.BUG.name());
templates.forEach(template -> headerCustomFields.addAll(baseTemplateService.getTemplateDTO(template).getCustomFields())); templates.forEach(template -> headerCustomFields.addAll(baseTemplateService.getTemplateDTO(template).getCustomFields()));
// 填充自定义字段成员类型的选项值
List<SelectOption> memberOption = getHeaderHandlerOption(projectId);
List<CustomFieldOption> memberCustomOption = memberOption.stream().map(option -> {
CustomFieldOption customFieldOption = new CustomFieldOption();
customFieldOption.setValue(option.getValue());
customFieldOption.setText(option.getText());
return customFieldOption;
}).toList();
headerCustomFields.forEach(field -> {
if (StringUtils.equalsAny(field.getType(), CustomFieldType.MEMBER.getType(), CustomFieldType.MULTIPLE_MEMBER.getType())) {
field.setOptions(memberCustomOption);
}
});
// 第三方平台模板 // 第三方平台模板
TemplateDTO pluginDefaultTemplate = getPluginBugDefaultTemplate(projectId, true); TemplateDTO pluginDefaultTemplate = getPluginBugDefaultTemplate(projectId, true);
if (pluginDefaultTemplate != null) { if (pluginDefaultTemplate != null) {
@ -1369,4 +1491,14 @@ public class BugService {
}); });
return logs; return logs;
} }
/**
* 获取下一个位置
* @param projectId 项目ID
* @return 位置
*/
private Long getNextPos(String projectId) {
Long pos = extBugMapper.getMaxPos(projectId);
return (pos == null ? 0 : pos) + INTERVAL_POS;
}
} }

View File

@ -1,23 +1,30 @@
package io.metersphere.bug.service; package io.metersphere.bug.service;
import io.metersphere.bug.domain.Bug;
import io.metersphere.bug.domain.BugExample;
import io.metersphere.bug.enums.BugPlatform; import io.metersphere.bug.enums.BugPlatform;
import io.metersphere.bug.mapper.BugMapper;
import io.metersphere.plugin.platform.dto.SelectOption; import io.metersphere.plugin.platform.dto.SelectOption;
import io.metersphere.plugin.platform.spi.Platform; import io.metersphere.plugin.platform.spi.Platform;
import io.metersphere.project.service.ProjectApplicationService; import io.metersphere.project.service.ProjectApplicationService;
import io.metersphere.sdk.constants.TemplateScene; import io.metersphere.sdk.constants.TemplateScene;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.service.BaseStatusFlowSettingService; import io.metersphere.system.service.BaseStatusFlowSettingService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public class BugStatusService { public class BugStatusService {
@Resource
private BugMapper bugMapper;
@Resource @Resource
private ProjectApplicationService projectApplicationService; private ProjectApplicationService projectApplicationService;
@Resource @Resource
@ -38,7 +45,13 @@ public class BugStatusService {
List<SelectOption> localStatusOption = getAllLocalStatusOptions(projectId); List<SelectOption> localStatusOption = getAllLocalStatusOptions(projectId);
Platform platform = projectApplicationService.getPlatform(projectId, true); Platform platform = projectApplicationService.getPlatform(projectId, true);
String projectConfig = projectApplicationService.getProjectBugThirdPartConfig(projectId); String projectConfig = projectApplicationService.getProjectBugThirdPartConfig(projectId);
List<SelectOption> platformStatusOption = platform.getStatusTransitions(projectConfig, null); // 获取一条最新的Jira默认模板缺陷Key
List<SelectOption> platformStatusOption = new ArrayList<>();
try {
platformStatusOption = platform.getStatusTransitions(projectConfig, getJiraPlatformBugKeyLatest(projectId));
} catch (Exception e) {
LogUtils.error("获取平台状态选项有误: " + e.getMessage());
}
return ListUtils.union(localStatusOption, platformStatusOption); return ListUtils.union(localStatusOption, platformStatusOption);
} }
} }
@ -60,7 +73,13 @@ public class BugStatusService {
// 获取配置平台, 获取第三方平台状态流 // 获取配置平台, 获取第三方平台状态流
Platform platform = projectApplicationService.getPlatform(projectId, true); Platform platform = projectApplicationService.getPlatform(projectId, true);
String projectConfig = projectApplicationService.getProjectBugThirdPartConfig(projectId); String projectConfig = projectApplicationService.getProjectBugThirdPartConfig(projectId);
return platform.getStatusTransitions(projectConfig, platformBugKey); List<SelectOption> platformOption = new ArrayList<>();
try {
platformOption = platform.getStatusTransitions(projectConfig, platformBugKey);
} catch (Exception e) {
LogUtils.error("获取平台状态选项有误: " + e.getMessage());
}
return platformOption;
} }
} }
@ -82,4 +101,16 @@ public class BugStatusService {
public List<SelectOption> getAllLocalStatusOptions(String projectId) { public List<SelectOption> getAllLocalStatusOptions(String projectId) {
return baseStatusFlowSettingService.getAllStatusOption(projectId, TemplateScene.BUG.name()); return baseStatusFlowSettingService.getAllStatusOption(projectId, TemplateScene.BUG.name());
} }
public String getJiraPlatformBugKeyLatest(String projectId) {
BugExample example = new BugExample();
example.createCriteria().andTemplateIdEqualTo("jira").andProjectIdEqualTo(projectId);
example.setOrderByClause("create_time desc");
List<Bug> bugs = bugMapper.selectByExample(example);
if (CollectionUtils.isNotEmpty(bugs)) {
return bugs.get(0).getPlatformBugId();
} else {
return StringUtils.EMPTY;
}
}
} }

View File

@ -1,19 +1,109 @@
//package io.metersphere.bug.service; package io.metersphere.bug.service;
//
//import io.metersphere.sdk.util.LogUtils; import io.metersphere.bug.domain.*;
//import io.metersphere.system.service.CleanupProjectResourceService; import io.metersphere.bug.mapper.*;
//import org.springframework.stereotype.Component; import io.metersphere.sdk.util.LogUtils;
// import io.metersphere.system.service.CleanupProjectResourceService;
//@Component import jakarta.annotation.Resource;
//public class CleanupBugResourceService implements CleanupProjectResourceService { import org.apache.commons.collections4.CollectionUtils;
// import org.springframework.scheduling.annotation.Async;
// @Override import org.springframework.stereotype.Component;
// public void deleteResources(String projectId) {
// LogUtils.info("删除当前项目[" + projectId + "]相关缺陷资源"); import java.util.List;
// }
// @Component
// @Override public class CleanupBugResourceService implements CleanupProjectResourceService {
// public void cleanReportResources(String projectId) {
// LogUtils.info("清理当前项目[" + projectId + "]相关缺陷报告资源"); @Resource
// } private BugMapper bugMapper;
//} @Resource
private BugContentMapper bugContentMapper;
@Resource
private BugFollowerMapper bugFollowerMapper;
@Resource
private BugLocalAttachmentMapper bugLocalAttachmentMapper;
@Resource
private BugCommentMapper bugCommentMapper;
@Resource
private BugCustomFieldMapper bugCustomFieldMapper;
@Resource
private BugRelationCaseMapper bugRelationCaseMapper;
@Async
@Override
public void deleteResources(String projectId) {
LogUtils.info("删除当前项目[" + projectId + "]相关缺陷测试资源");
List<String> deleteIds = getBugIds(projectId);
if (CollectionUtils.isNotEmpty(deleteIds)) {
// 清理缺陷
deleteBug(deleteIds);
// 清理缺陷内容
deleteBugContent(deleteIds);
// 清理缺陷关注人信息
deleteBugFollower(deleteIds);
// 清理缺陷本地附件
deleteBugLocalAttachment(deleteIds);
// 清理缺陷评论
deleteBugComment(deleteIds);
// 清理缺陷自定义字段
deleteBugCustomField(deleteIds);
// 清理缺陷关联用例
deleteBugRelateCase(deleteIds);
}
}
@Async
@Override
public void cleanReportResources(String projectId) {
LogUtils.info("清理当前项目[" + projectId + "]相关缺陷测试报告资源");
}
private List<String> getBugIds(String projectId) {
BugExample example = new BugExample();
example.createCriteria().andProjectIdEqualTo(projectId);
List<Bug> bugs = bugMapper.selectByExample(example);
return bugs.stream().map(Bug::getId).toList();
}
private void deleteBug(List<String> bugIds) {
BugExample example = new BugExample();
example.createCriteria().andIdIn(bugIds);
bugMapper.deleteByExample(example);
}
private void deleteBugContent(List<String> bugIds) {
BugContentExample example = new BugContentExample();
example.createCriteria().andBugIdIn(bugIds);
bugContentMapper.deleteByExample(example);
}
private void deleteBugFollower(List<String> bugIds) {
BugFollowerExample example = new BugFollowerExample();
example.createCriteria().andBugIdIn(bugIds);
bugFollowerMapper.deleteByExample(example);
}
private void deleteBugLocalAttachment(List<String> bugIds) {
BugLocalAttachmentExample example = new BugLocalAttachmentExample();
example.createCriteria().andBugIdIn(bugIds);
bugLocalAttachmentMapper.deleteByExample(example);
}
private void deleteBugComment(List<String> bugIds) {
BugCommentExample example = new BugCommentExample();
example.createCriteria().andBugIdIn(bugIds);
bugCommentMapper.deleteByExample(example);
}
private void deleteBugCustomField(List<String> bugIds) {
BugCustomFieldExample example = new BugCustomFieldExample();
example.createCriteria().andBugIdIn(bugIds);
bugCustomFieldMapper.deleteByExample(example);
}
private void deleteBugRelateCase(List<String> bugIds) {
BugRelationCaseExample example = new BugRelationCaseExample();
example.createCriteria().andBugIdIn(bugIds);
bugRelationCaseMapper.deleteByExample(example);
}
}

View File

@ -1,7 +1,11 @@
package io.metersphere.bug.service; package io.metersphere.bug.service;
import io.metersphere.bug.dto.request.BugHistoryPageRequest;
import io.metersphere.bug.dto.request.BugSyncRequest; import io.metersphere.bug.dto.request.BugSyncRequest;
import io.metersphere.project.domain.Project; import io.metersphere.project.domain.Project;
import io.metersphere.system.dto.OperationHistoryDTO;
import java.util.List;
/** /**
* 缺陷Xpack功能接口 (全量同步) * 缺陷Xpack功能接口 (全量同步)
@ -20,4 +24,11 @@ public interface XpackBugService {
* @param currentUser 当前用户 * @param currentUser 当前用户
*/ */
void syncPlatformBugs(Project project, BugSyncRequest request, String currentUser); void syncPlatformBugs(Project project, BugSyncRequest request, String currentUser);
/**
* 缺陷变更历史分页列表
* @param request 请求参数
* @return 变更历史集合
*/
List<OperationHistoryDTO> listHis(BugHistoryPageRequest request);
} }

View File

@ -1,6 +1,6 @@
package io.metersphere.bug.utils; package io.metersphere.bug.utils;
import io.metersphere.bug.dto.BugExportColumn; import io.metersphere.bug.dto.BugExportHeaderModel;
import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.bug.dto.response.BugDTO;
import io.metersphere.sdk.util.CompressUtils; import io.metersphere.sdk.util.CompressUtils;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
@ -12,23 +12,23 @@ import java.util.function.BiFunction;
public class ExportUtils { public class ExportUtils {
private List<BugDTO> bugs; private List<BugDTO> bugs;
private List<BugExportColumn> exportColumns; private BugExportHeaderModel headerModel;
public ExportUtils( public ExportUtils(
List<BugDTO> bugs, List<BugDTO> bugs,
List<BugExportColumn> exportColumns) { BugExportHeaderModel headerModel) {
this.bugs = bugs; this.bugs = bugs;
this.exportColumns = exportColumns; this.headerModel = headerModel;
} }
/* /**
1.生成包含excel文件目录 * 1.生成包含excel文件目录
2.压缩 * 2.压缩
3.删除该目录 * 3.删除该目录
*/ */
public byte[] exportToZipFile(BiFunction<List, List, String> generateExcelFilesFunction) throws Exception { public byte[] exportToZipFile(BiFunction<List, BugExportHeaderModel, String> generateExcelFilesFunction) throws Exception {
//生成包含excel文件目录 //生成包含excel文件目录
String folderPath = generateExcelFilesFunction.apply(bugs, exportColumns); String folderPath = generateExcelFilesFunction.apply(bugs, headerModel);
File excelFolder = new File(folderPath); File excelFolder = new File(folderPath);
//压缩文件 //压缩文件
File zipFile = CompressUtils.zipFiles(folderPath + File.separatorChar + "bug-export.zip", List.of(excelFolder.listFiles())); File zipFile = CompressUtils.zipFiles(folderPath + File.separatorChar + "bug-export.zip", List.of(excelFolder.listFiles()));

View File

@ -49,6 +49,7 @@ public class BugAttachmentControllerTests extends BaseTest {
public static final String BUG_ATTACHMENT_CHECK_UPDATE = "/bug/attachment/check-update"; public static final String BUG_ATTACHMENT_CHECK_UPDATE = "/bug/attachment/check-update";
public static final String BUG_ATTACHMENT_UPDATE = "/bug/attachment/update"; public static final String BUG_ATTACHMENT_UPDATE = "/bug/attachment/update";
public static final String BUG_ATTACHMENT_UPLOAD_MD = "/bug/attachment/upload/md/file"; public static final String BUG_ATTACHMENT_UPLOAD_MD = "/bug/attachment/upload/md/file";
public static final String BUG_ATTACHMENT_PREVIEW_MD = "/bug/attachment/preview/md/compressed";
@Test @Test
@Order(0) @Order(0)
@ -60,6 +61,12 @@ public class BugAttachmentControllerTests extends BaseTest {
// Mock minio save file exception // Mock minio save file exception
MockMultipartFile file = new MockMultipartFile("file", "test.txt", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes()); MockMultipartFile file = new MockMultipartFile("file", "test.txt", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes());
this.requestUploadFile(BUG_ATTACHMENT_UPLOAD_MD, file); this.requestUploadFile(BUG_ATTACHMENT_UPLOAD_MD, file);
BugFileSourceRequest request = new BugFileSourceRequest();
request.setBugId("default-attachment-bug-id");
request.setProjectId("default-project-for-attachment");
request.setAssociated(false);
request.setFileId("not-exist-file-id");
this.requestPostDownloadFile(BUG_ATTACHMENT_PREVIEW_MD, null, request);
} }
@Test @Test

View File

@ -35,6 +35,7 @@ import io.metersphere.system.domain.CustomFieldExample;
import io.metersphere.system.domain.ServiceIntegration; import io.metersphere.system.domain.ServiceIntegration;
import io.metersphere.system.dto.request.PluginUpdateRequest; import io.metersphere.system.dto.request.PluginUpdateRequest;
import io.metersphere.system.dto.sdk.TemplateDTO; import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.dto.sdk.request.PosRequest;
import io.metersphere.system.mapper.CustomFieldMapper; import io.metersphere.system.mapper.CustomFieldMapper;
import io.metersphere.system.mapper.ServiceIntegrationMapper; import io.metersphere.system.mapper.ServiceIntegrationMapper;
import io.metersphere.system.service.PluginService; import io.metersphere.system.service.PluginService;
@ -74,10 +75,10 @@ public class BugControllerTests extends BaseTest {
public static final String BUG_HEADER_STATUS_OPTION = "/bug/header/status-option"; public static final String BUG_HEADER_STATUS_OPTION = "/bug/header/status-option";
public static final String BUG_HEADER_HANDLER_OPTION = "/bug/header/handler-option"; public static final String BUG_HEADER_HANDLER_OPTION = "/bug/header/handler-option";
public static final String BUG_PAGE = "/bug/page"; public static final String BUG_PAGE = "/bug/page";
public static final String BUG_EDIT_POS = "/bug/edit/pos";
public static final String BUG_ADD = "/bug/add"; public static final String BUG_ADD = "/bug/add";
public static final String BUG_UPDATE = "/bug/update"; public static final String BUG_UPDATE = "/bug/update";
public static final String BUG_DETAIL = "/bug/get"; public static final String BUG_DETAIL = "/bug/get";
public static final String BUG_QUICK_UPDATE = "/bug/quick-update";
public static final String BUG_DELETE = "/bug/delete"; public static final String BUG_DELETE = "/bug/delete";
public static final String BUG_TEMPLATE_OPTION = "/bug/template/option"; public static final String BUG_TEMPLATE_OPTION = "/bug/template/option";
public static final String BUG_TEMPLATE_DETAIL = "/bug/template/detail"; public static final String BUG_TEMPLATE_DETAIL = "/bug/template/detail";
@ -130,7 +131,6 @@ public class BugControllerTests extends BaseTest {
fileService.upload(getMockFile(), fileRequest); fileService.upload(getMockFile(), fileRequest);
} }
@Test @Test
@Order(1) @Order(1)
void testBugPageSuccess() throws Exception { void testBugPageSuccess() throws Exception {
@ -174,6 +174,14 @@ public class BugControllerTests extends BaseTest {
// 返回值中取出第一条ID最大的数据, 并判断是否是default-bug // 返回值中取出第一条ID最大的数据, 并判断是否是default-bug
BugDTO maxBugDTO = JSON.parseArray(JSON.toJSONString(sortPageData.getList()), BugDTO.class).get(0); BugDTO maxBugDTO = JSON.parseArray(JSON.toJSONString(sortPageData.getList()), BugDTO.class).get(0);
Assertions.assertTrue(maxBugDTO.getId().contains("default")); Assertions.assertTrue(maxBugDTO.getId().contains("default"));
// 拖拽
PosRequest posRequest = new PosRequest();
posRequest.setProjectId("default-project-for-bug");
posRequest.setMoveId("default-bug-id");
posRequest.setMoveMode("AFTER");
posRequest.setTargetId("default-bug-id-tapd1");
this.requestPost(BUG_EDIT_POS, posRequest);
} }
@Test @Test
@ -225,6 +233,7 @@ public class BugControllerTests extends BaseTest {
@Order(4) @Order(4)
void testAddBugSuccess() throws Exception { void testAddBugSuccess() throws Exception {
BugEditRequest request = buildRequest(false); BugEditRequest request = buildRequest(false);
request.setDescription(null);
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.xlsx")).getPath(); String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.xlsx")).getPath();
File file = new File(filePath); File file = new File(filePath);
MultiValueMap<String, Object> paramMap = getDefaultMultiPartParam(request, file); MultiValueMap<String, Object> paramMap = getDefaultMultiPartParam(request, file);
@ -277,14 +286,7 @@ public class BugControllerTests extends BaseTest {
request.setDescription("1111"); request.setDescription("1111");
noFileParamMap.add("request", JSON.toJSONString(request)); noFileParamMap.add("request", JSON.toJSONString(request));
this.requestMultipartWithOkAndReturn(BUG_UPDATE, noFileParamMap); this.requestMultipartWithOkAndReturn(BUG_UPDATE, noFileParamMap);
// 获取缺陷详情
this.requestGetWithOk(BUG_DETAIL + "/default-bug-id");
this.requestGetWithOk(BUG_DETAIL + "/" + request.getId()); this.requestGetWithOk(BUG_DETAIL + "/" + request.getId());
// 更新部分
BugQuickEditRequest quickEditRequest = new BugQuickEditRequest();
quickEditRequest.setId(request.getId());
quickEditRequest.setTags(List.of("TEST"));
this.requestPost(BUG_QUICK_UPDATE, quickEditRequest, status().isOk());
} }
@Test @Test
@ -541,6 +543,8 @@ public class BugControllerTests extends BaseTest {
@Test @Test
@Order(96) @Order(96)
void coverPlatformBugSyncTests() throws Exception { void coverPlatformBugSyncTests() throws Exception {
// 获取默认模板缺陷详情
this.requestGetWithOk(BUG_DETAIL + "/default-bug-id-jira-sync");
// 表头字段, 状态选项, 处理人选项 (非Local平台) // 表头字段, 状态选项, 处理人选项 (非Local平台)
this.requestGetWithOk(BUG_HEADER_CUSTOM_FIELD + "/default-project-for-bug"); this.requestGetWithOk(BUG_HEADER_CUSTOM_FIELD + "/default-project-for-bug");
this.requestGetWithOk(BUG_HEADER_STATUS_OPTION + "/default-project-for-bug"); this.requestGetWithOk(BUG_HEADER_STATUS_OPTION + "/default-project-for-bug");
@ -640,7 +644,7 @@ public class BugControllerTests extends BaseTest {
@Test @Test
@Order(98) @Order(98)
void coverBugTests() throws Exception { void coverBugTests() {
BugCustomFieldDTO field = new BugCustomFieldDTO(); BugCustomFieldDTO field = new BugCustomFieldDTO();
field.setId("test_field"); field.setId("test_field");
field.setName("test"); field.setName("test");
@ -651,11 +655,6 @@ public class BugControllerTests extends BaseTest {
removeApiFieldTmp(); removeApiFieldTmp();
bugService.transferCustomToPlatformField("default-bug-template-id-not-exist", List.of(field), false); bugService.transferCustomToPlatformField("default-bug-template-id-not-exist", List.of(field), false);
rollBackApiField(); rollBackApiField();
// 覆盖导出相关的代码
BugExportRequest request = new BugExportRequest();
request.setSelectAll(true);
request.setExcludeIds(List.of("test-id"));
bugService.export(request);
} }
@Test @Test

View File

@ -0,0 +1,38 @@
package io.metersphere.bug.controller;
import io.metersphere.bug.dto.request.BugHistoryPageRequest;
import io.metersphere.system.base.BaseTest;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import java.util.Map;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BugHistoryControllerTests extends BaseTest {
public static final String BUG_PAGE = "/bug/history/page";
@Test
@Order(1)
@Sql(scripts = {"/dml/init_bug_history.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
void testBugHistoryPageSuccess() throws Exception {
BugHistoryPageRequest request = new BugHistoryPageRequest();
request.setBugId("bug-history-id");
request.setProjectId("100001100001");
request.setCurrent(1);
request.setPageSize(10);
this.requestPost(BUG_PAGE, request).andExpect(status().isOk());
request.setSort(Map.of("createTime", "asc"));
this.requestPost(BUG_PAGE, request).andExpect(status().isOk());
}
}

View File

@ -1,10 +1,14 @@
package io.metersphere.bug.mock; package io.metersphere.bug.mock;
import io.metersphere.bug.dto.request.BugHistoryPageRequest;
import io.metersphere.bug.dto.request.BugSyncRequest; import io.metersphere.bug.dto.request.BugSyncRequest;
import io.metersphere.bug.service.XpackBugService; import io.metersphere.bug.service.XpackBugService;
import io.metersphere.project.domain.Project; import io.metersphere.project.domain.Project;
import io.metersphere.system.dto.OperationHistoryDTO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
@Service @Service
public class XpackBugMockServiceImpl implements XpackBugService { public class XpackBugMockServiceImpl implements XpackBugService {
@ -17,4 +21,9 @@ public class XpackBugMockServiceImpl implements XpackBugService {
public void syncPlatformBugs(Project project, BugSyncRequest request, String currentUser) { public void syncPlatformBugs(Project project, BugSyncRequest request, String currentUser) {
} }
@Override
public List<OperationHistoryDTO> listHis(BugHistoryPageRequest request) {
return List.of();
}
} }

View File

@ -0,0 +1,39 @@
package io.metersphere.bug.service;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.invoker.ProjectServiceInvoker;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CleanBugResourceServiceTests extends BaseTest {
private final ProjectServiceInvoker serviceInvoker;
@Resource
private CleanupBugResourceService cleanupBugResourceService;
@Autowired
public CleanBugResourceServiceTests(ProjectServiceInvoker serviceInvoker) {
this.serviceInvoker = serviceInvoker;
}
@Test
@Order(1)
@Sql(scripts = {"/dml/init_bug_clean_resource.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
void test() {
serviceInvoker.invokeServices("default-project-for-clean-resource");
serviceInvoker.invokeServices("default-project-for-clean-resource-not-exist");
cleanupBugResourceService.cleanReportResources("default-project-for-clean-resource");
cleanupBugResourceService.cleanReportResources("default-project-for-clean-resource-not-exist");
}
}

View File

@ -3,21 +3,24 @@ INSERT INTO project (id, num, organization_id, name, description, create_user, u
INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUE INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUE
('default-project-for-bug', null, '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000), ('default-project-for-bug', null, '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000),
('no-status-project', null, '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000); ('no-status-project', null, '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000),
('default-project-for-bug-no-local', null, '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user) VALUES INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user) VALUES
(UUID(), 'admin', 'project_admin', 'default-project-for-bug', '100001', UNIX_TIMESTAMP() * 1000, 'admin'); (UUID(), 'admin', 'project_admin', 'default-project-for-bug', '100001', UNIX_TIMESTAMP() * 1000, 'admin');
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted) VALUES INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) VALUES
('default-bug-id', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0), ('default-bug-id', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 5000),
('default-bug-id-tapd1', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'default-bug-template-id', 'Tapd', 'open', '["default-tag"]', null, 0), ('default-bug-id-tapd1', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'default-bug-template-id', 'Tapd', 'open', '["default-tag"]', null, 0, 10000),
('default-bug-id-tapd2', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug-no-local', 'default-bug-template-id', 'Tapd', 'open', '["default-tag"]', null, 0), ('default-bug-id-tapd2', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug-no-local', 'default-bug-template-id', 'Tapd', 'open', '["default-tag"]', null, 0, 5000),
('default-bug-id-single', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug-single', 'default-bug-template-id', 'Tapd', 'open', '["default-tag"]', null, 0), ('default-bug-id-single', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug-single', 'default-bug-template-id', 'Tapd', 'open', '["default-tag"]', null, 0, 5000),
('default-bug-id-jira', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug-single', 'default-bug-template-id', 'Jira', 'open', '["default-tag"]', 'TES-TEST', 0), ('default-bug-id-jira', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug-single', 'default-bug-template-id', 'Jira', 'open', '["default-tag"]', 'TES-TEST', 0, 10000),
('default-bug-id-jira-sync', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'jira', 'Jira', 'open', '["default-tag"]', 'TES-TEST', 0), ('default-bug-id-jira-sync', 100000, '这是一段summary444', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'jira', 'Jira', 'open', '["default-tag"]', 'TES-TEST', 0, 15000),
('default-bug-id-jira-sync-1', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'jira-test', 'Jira', 'open', '["default-tag"]', 'TES-TEST', 0); ('default-bug-id-jira-sync-1', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'jira-test', 'Jira', 'open', '["default-tag"]', 'TES-TEST', 0, 20000);
INSERT INTO bug_content(bug_id, description) VALUE ('default-bug-id', 'oasis');
INSERT INTO bug_custom_field (bug_id, field_id, value) VALUE ('default-bug-id', 'test_field', '["default", "default-1"]'); INSERT INTO bug_custom_field (bug_id, field_id, value) VALUE ('default-bug-id', 'test_field', '["default", "default-1"]');
INSERT INTO bug_custom_field (bug_id, field_id, value) VALUE ('default-bug-id-jira-sync', 'summary', '这是一段summary444');
INSERT INTO custom_field (id, name, scene, type, remark, internal, scope_type, create_time, update_time, create_user, scope_id) VALUE INSERT INTO custom_field (id, name, scene, type, remark, internal, scope_type, create_time, update_time, create_user, scope_id) VALUE
('test_field', '测试字段', 'BUG', 'MULTIPLE_SELECT', '', 0, 'PROJECT', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'default-project-for-bug'), ('test_field', '测试字段', 'BUG', 'MULTIPLE_SELECT', '', 0, 'PROJECT', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'default-project-for-bug'),

View File

@ -4,6 +4,6 @@ INSERT INTO project (id, num, organization_id, name, description, create_user, u
INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time)
VALUE ('default-project-for-attachment', null, '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000); VALUE ('default-project-for-attachment', null, '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted) INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos)
VALUES ('default-attachment-bug-id', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-attachment', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 1), VALUES ('default-attachment-bug-id', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-attachment', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 1, 5000),
('default-bug-id-tapd', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-attachment', 'default-bug-template-id', 'Tapd', 'open', '["default-tag"]', null, 0); ('default-bug-id-tapd', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-attachment', 'default-bug-template-id', 'Tapd', 'open', '["default-tag"]', null, 0, 10000);

View File

@ -21,9 +21,9 @@ INSERT INTO functional_case_module(id, project_id, name, parent_id, pos, create_
VALUES ('init_module', 'default-project-for-bug', 'test_module_name', 'NONE', '1', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin'); VALUES ('init_module', 'default-project-for-bug', 'test_module_name', 'NONE', '1', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin');
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted) VALUES update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) VALUES
('default-relate-bug-id', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0), ('default-relate-bug-id', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 5000),
('default-relate-bug-id-1', 100001, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0); ('default-relate-bug-id-1', 100001, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 10000);
INSERT INTO bug_relation_case(id, case_id, bug_id, case_type, test_plan_id, test_plan_case_id, create_user, create_time, update_time) INSERT INTO bug_relation_case(id, case_id, bug_id, case_type, test_plan_id, test_plan_case_id, create_user, create_time, update_time)
VALUES ('bug-relate-case-default-id', 'bug_relate_case', 'default-relate-bug-id', 'FUNCTIONAL', 'test-plan-id', null, 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000), VALUES ('bug-relate-case-default-id', 'bug_relate_case', 'default-relate-bug-id', 'FUNCTIONAL', 'test-plan-id', null, 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000),

View File

@ -0,0 +1,20 @@
INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUE
('default-project-for-clean-resource-tmp', null, '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) VALUE
('bug-clean-resource-id', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'default-project-for-clean-resource', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 5000);
INSERT INTO bug_content (bug_id, description) VALUE ('bug-clean-resource-id', 'default-bug-description');
INSERT INTO bug_follower (bug_id, user_id) VALUE ('bug-clean-resource-id', 'admin');
INSERT INTO bug_local_attachment (id, bug_id, file_id, file_name, size, source, create_user, create_time) VALUE
('bug-clean-resource-attachment-id', 'bug-clean-resource-id', 'file-for-sync-extra', 'sync-extra-file-local-A.txt', 100, 'ATTACHMENT', 'admin', UNIX_TIMESTAMP() * 1000);
INSERT INTO bug_comment(id, bug_id, content, create_user, create_time, update_user, update_time) VALUE
('bug-clean-resource-comment-id', 'bug-clean-resource-id', 'This is a comment!', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000);
INSERT INTO bug_custom_field(bug_id, field_id, value) VALUE ('bug-clean-resource-id', 'custom-field-id', 'custom-field-value');
INSERT INTO bug_relation_case (id, case_id, bug_id, case_type, test_plan_id, test_plan_case_id, create_user, create_time, update_time) VALUE
('bug-clean-resource-relation-id', 'case-id', 'bug-clean-resource-id', 'case', 'test-plan-id', 'test-plan-case-id', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);

View File

@ -9,9 +9,9 @@ INSERT INTO user(id, name, email, password, create_time, update_time, language,
('oasis-user-id4', 'oasis4', 'oasis4@test.com', MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', false); ('oasis-user-id4', 'oasis4', 'oasis4@test.com', MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', false);
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted) VALUE update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) VALUE
('default-bug-id-for-comment', 100099, 'default-bug-for-comment', 'oasis', 'oasis', 'oasis-user-id', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0), ('default-bug-id-for-comment', 100099, 'default-bug-for-comment', 'oasis', 'oasis', 'oasis-user-id', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 5000),
('default-bug-id-for-comment1', 100099, 'default-bug-for-comment', 'oasis', 'oasis', 'oasis-user-id', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0); ('default-bug-id-for-comment1', 100099, 'default-bug-for-comment', 'oasis', 'oasis', 'oasis-user-id', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 10000);
INSERT INTO bug_comment (id, bug_id, reply_user, notifier, parent_id, content, create_user, create_time, update_user, update_time) VALUES INSERT INTO bug_comment (id, bug_id, reply_user, notifier, parent_id, content, create_user, create_time, update_user, update_time) VALUES
('default-bug-comment-id-1', 'default-bug-id-for-comment', null, null, null, 'This is a test comment!', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000), ('default-bug-comment-id-1', 'default-bug-id-for-comment', null, null, null, 'This is a test comment!', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000),

View File

@ -0,0 +1,10 @@
INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUE
('bug-his-project-tmp', null, '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) VALUES
('bug-history-id', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', 'Local', 'open', null, null, 1, 5000);
INSERT INTO operation_history (`id`, `project_id`, `create_time`, `create_user`, `source_id`, `type`, `module`, `ref_id`) VALUES
(1, '100001100001', 1706079964322, 'admin', 'bug-history-id', 'ADD', 'BUG_MANAGEMENT', NULL),
(2, '100001100001', 1706079964322, 'admin', 'bug-history-id', 'ADD', 'BUG_MANAGEMENT', NULL);

View File

@ -1,7 +1,7 @@
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted) VALUES update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) VALUES
('bug_id_1', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'project_wx_associate_test"', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0), ('bug_id_1', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'project_wx_associate_test"', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 5000),
('bug_id_2', 100001, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'project_wx_associate_test"', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0); ('bug_id_2', 100001, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'project_wx_associate_test"', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 10000);
INSERT INTO bug_relation_case(id, case_id, bug_id, case_type, test_plan_id, test_plan_case_id, create_user, create_time, update_time) INSERT INTO bug_relation_case(id, case_id, bug_id, case_type, test_plan_id, test_plan_case_id, create_user, create_time, update_time)
VALUES ('wx_test_id_1', 'wx_1', 'bug_id_1', 'FUNCTIONAL', null, null, 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000), VALUES ('wx_test_id_1', 'wx_1', 'bug_id_1', 'FUNCTIONAL', null, null, 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000),

View File

@ -4,8 +4,8 @@ INSERT INTO project (id, num, organization_id, name, description, create_user, u
INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUE INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUE
('project-for-sync-extra', null, '100001', '测试项目(缺陷同步)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000); ('project-for-sync-extra', null, '100001', '测试项目(缺陷同步)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted) VALUE INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) VALUE
('bug-for-sync-extra', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'project-for-sync-extra', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0); ('bug-for-sync-extra', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'project-for-sync-extra', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 5000);
INSERT INTO file_metadata (id, name, type, size, create_time, update_time, project_id, storage, create_user, update_user, INSERT INTO file_metadata (id, name, type, size, create_time, update_time, project_id, storage, create_user, update_user,
tags, description, module_id, path, latest, ref_id, file_version) VALUE tags, description, module_id, path, latest, ref_id, file_version) VALUE

View File

@ -5,10 +5,10 @@ INSERT INTO project (id, num, organization_id, name, description, create_user, u
('bug-trash-project', '100000043', '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000); ('bug-trash-project', '100000043', '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted) VALUES update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) VALUES
('trash-bug-1', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'bug-trash-project', 'bug-template-id', 'Local', 'open', null, null, 1), ('trash-bug-1', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'bug-trash-project', 'bug-template-id', 'Local', 'open', null, null, 1, 5000),
('trash-bug-2', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'bug-trash-project', 'default-bug-template-id', 'Local', 'open', null, null, 1), ('trash-bug-2', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'bug-trash-project', 'default-bug-template-id', 'Local', 'open', null, null, 1, 10000),
('trash-bug-3', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'bug-trash-project', 'default-bug-template-id', 'Local', 'open', null, null, 1), ('trash-bug-3', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'bug-trash-project', 'default-bug-template-id', 'Local', 'open', null, null, 1, 15000),
('trash-bug-4', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'bug-trash-project', 'default-bug-template-id', 'Local', 'open', null, null, 1), ('trash-bug-4', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'bug-trash-project', 'default-bug-template-id', 'Local', 'open', null, null, 1, 20000),
('trash-bug-5', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'bug-trash-project', 'default-bug-template-id', 'Jira', 'open', null, null, 1); ('trash-bug-5', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'bug-trash-project', 'default-bug-template-id', 'Jira', 'open', null, null, 1, 25000);

View File

@ -1,7 +1,7 @@
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted) VALUES update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) VALUES
('wx_bug_id_1', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'project_wx_associate_test"', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0), ('wx_bug_id_1', 100000, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'project_wx_associate_test"', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 5000),
('wx_bug_id_2', 100001, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'project_wx_associate_test"', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0); ('wx_bug_id_2', 100001, 'default-bug', 'oasis', 'oasis', 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'project_wx_associate_test"', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 10000);
INSERT INTO bug_relation_case(id, case_id, bug_id, case_type, test_plan_id, test_plan_case_id, create_user, create_time, update_time) INSERT INTO bug_relation_case(id, case_id, bug_id, case_type, test_plan_id, test_plan_case_id, create_user, create_time, update_time)
VALUES ('TEST', 'wx_1', 'wx_bug_id_1', 'FUNCTIONAL', null, null, 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000), VALUES ('TEST', 'wx_1', 'wx_bug_id_1', 'FUNCTIONAL', null, null, 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000),

View File

@ -595,6 +595,12 @@ public class ProjectApplicationService {
boolean isEnable = platformEnableConfig != null && Boolean.parseBoolean(platformEnableConfig.getTypeValue()) && platformKeyConfig != null; boolean isEnable = platformEnableConfig != null && Boolean.parseBoolean(platformEnableConfig.getTypeValue()) && platformKeyConfig != null;
if (!isEnable) { if (!isEnable) {
return "Local"; return "Local";
} else {
ServiceIntegration serviceIntegration = getPlatformServiceIntegrationWithSyncOrDemand(projectId, true);
if (serviceIntegration == null) {
// 项目未配置第三方平台
return "Local";
}
} }
return getPluginName(platformKeyConfig.getTypeValue()); return getPluginName(platformKeyConfig.getTypeValue());
} }

View File

@ -1,34 +1,34 @@
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags,
platform_bug_id, deleted) platform_bug_id, deleted, pos)
VALUES ('sty-file-association-bug-id-1', 100000, 'sty-default-bug-1', 'oasis', 'oasis', 'admin', VALUES ('sty-file-association-bug-id-1', 100000, 'sty-default-bug-1', 'oasis', 'oasis', 'admin',
UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000,
'admin', 'admin',
UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id',
'Local', 'open', '["default-tag"]', null, 0); 'Local', 'open', '["default-tag"]', null, 0, 5000);
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags,
platform_bug_id, deleted) platform_bug_id, deleted, pos)
VALUES ('sty-file-association-bug-id-2', 100001, 'sty-default-bug-2', 'oasis', 'oasis', 'admin', VALUES ('sty-file-association-bug-id-2', 100001, 'sty-default-bug-2', 'oasis', 'oasis', 'admin',
UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000,
'admin', 'admin',
UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id',
'Local', 'open', '["default-tag"]', null, 0); 'Local', 'open', '["default-tag"]', null, 0, 10000);
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags,
platform_bug_id, deleted) platform_bug_id, deleted, pos)
VALUES ('sty-file-association-bug-id-3', 100002, 'sty-default-bug-3', 'oasis', 'oasis', 'admin', VALUES ('sty-file-association-bug-id-3', 100002, 'sty-default-bug-3', 'oasis', 'oasis', 'admin',
UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000,
'admin', 'admin',
UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id',
'Local', 'open', '["default-tag"]', null, 0); 'Local', 'open', '["default-tag"]', null, 0, 15000);
INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time, INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,
update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags,
platform_bug_id, deleted) platform_bug_id, deleted, pos)
VALUES ('sty-file-association-bug-id-4', 100003, 'sty-default-bug-4', 'oasis', 'oasis', 'admin', VALUES ('sty-file-association-bug-id-4', 100003, 'sty-default-bug-4', 'oasis', 'oasis', 'admin',
UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000,
'admin', 'admin',
UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id',
'Local', 'open', '["default-tag"]', null, 0); 'Local', 'open', '["default-tag"]', null, 0, 20000);

View File

@ -11,7 +11,8 @@ replace INTO user(id, name, email, password, create_time, update_time, language,
replace INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user) VALUES (UUID_SHORT(), 'wx-test', 'project_admin', '100001100001', '100001', 1684747668375, 'admin'); replace INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user) VALUES (UUID_SHORT(), 'wx-test', 'project_admin', '100001100001', '100001', 1684747668375, 'admin');
INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUE INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUE
('default-project-for-application', null, '100002', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000); ('default-project-for-application', null, '100002', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000),
('project_application_test_id', null, '100003', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
-- 集成信息 -- 集成信息
INSERT INTO project_application (project_id, type, type_value) VALUES INSERT INTO project_application (project_id, type, type_value) VALUES

View File

@ -1,7 +1,6 @@
package io.metersphere.system.config.interceptor; package io.metersphere.system.config.interceptor;
import io.metersphere.bug.domain.BugContent; import io.metersphere.bug.domain.BugContent;
import io.metersphere.bug.domain.BugHistory;
import io.metersphere.sdk.util.CompressUtils; import io.metersphere.sdk.util.CompressUtils;
import io.metersphere.system.utils.MybatisInterceptorConfig; import io.metersphere.system.utils.MybatisInterceptorConfig;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -16,7 +15,6 @@ public class BugInterceptor {
public List<MybatisInterceptorConfig> bugCompressConfigs() { public List<MybatisInterceptorConfig> bugCompressConfigs() {
List<MybatisInterceptorConfig> configList = new ArrayList<>(); List<MybatisInterceptorConfig> configList = new ArrayList<>();
configList.add(new MybatisInterceptorConfig(BugContent.class, "description", CompressUtils.class, "zip", "unzip")); configList.add(new MybatisInterceptorConfig(BugContent.class, "description", CompressUtils.class, "zip", "unzip"));
configList.add(new MybatisInterceptorConfig(BugHistory.class, "content", CompressUtils.class, "zip", "unzip"));
return configList; return configList;
} }
} }

View File

@ -15,6 +15,9 @@ public class TemplateCustomFieldDTO {
@Schema(title = "字段名称", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(title = "字段名称", requiredMode = Schema.RequiredMode.REQUIRED)
private String fieldName; private String fieldName;
@Schema(title = "字段唯一Key, 处理人,状态字段需要, 其余自定义字段ID即可标识唯一")
private String fieldKey;
@Schema(title = "是否必填") @Schema(title = "是否必填")
private Boolean required; private Boolean required;