feat(项目设置): 缺陷列表基础功能

This commit is contained in:
song-cc-rock 2023-10-16 11:32:29 +08:00 committed by Craftsman
parent fc63fa5bc1
commit 3dab64d0f0
66 changed files with 2392 additions and 264 deletions

View File

@ -27,10 +27,13 @@ public class Bug implements Serializable {
@Size(min = 1, max = 300, message = "{bug.title.length_range}", groups = {Created.class, Updated.class})
private String title;
@Schema(description = "指派人", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.assign_user.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{bug.assign_user.length_range}", groups = {Created.class, Updated.class})
private String assignUser;
@Schema(description = "处理人集合;预留字段, 后续工作台统计可能需要")
private String handleUsers;
@Schema(description = "处理人", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.handle_user.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{bug.handle_user.length_range}", groups = {Created.class, Updated.class})
private String handleUser;
@Schema(description = "创建人")
private String createUser;
@ -55,7 +58,9 @@ public class Bug implements Serializable {
@Size(min = 1, max = 50, message = "{bug.project_id.length_range}", groups = {Created.class, Updated.class})
private String projectId;
@Schema(description = "模板ID")
@Schema(description = "模板ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.template_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{bug.template_id.length_range}", groups = {Created.class, Updated.class})
private String templateId;
@Schema(description = "缺陷平台", requiredMode = Schema.RequiredMode.REQUIRED)
@ -84,7 +89,8 @@ public class Bug implements Serializable {
id("id", "id", "VARCHAR", false),
num("num", "num", "INTEGER", false),
title("title", "title", "VARCHAR", false),
assignUser("assign_user", "assignUser", "VARCHAR", false),
handleUsers("handle_users", "handleUsers", "VARCHAR", false),
handleUser("handle_user", "handleUser", "VARCHAR", false),
createUser("create_user", "createUser", "VARCHAR", false),
createTime("create_time", "createTime", "BIGINT", false),
updateUser("update_user", "updateUser", "VARCHAR", false),

View File

@ -38,9 +38,9 @@ public class BugAttachment implements Serializable {
@NotNull(message = "{bug_attachment.size.not_blank}", groups = {Created.class})
private Long size;
@Schema(description = "是否关联", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{bug_attachment.association.not_blank}", groups = {Created.class})
private Boolean association;
@Schema(description = "是否本地", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{bug_attachment.local.not_blank}", groups = {Created.class})
private Boolean local;
@Schema(description = "创建人")
private String createUser;
@ -56,7 +56,7 @@ public class BugAttachment implements Serializable {
fileId("file_id", "fileId", "VARCHAR", false),
fileName("file_name", "fileName", "VARCHAR", false),
size("size", "size", "BIGINT", true),
association("association", "association", "BIT", false),
local("local", "local", "BIT", true),
createUser("create_user", "createUser", "VARCHAR", false),
createTime("create_time", "createTime", "BIGINT", false);

View File

@ -444,63 +444,63 @@ public class BugAttachmentExample {
return (Criteria) this;
}
public Criteria andAssociationIsNull() {
addCriterion("association is null");
public Criteria andLocalIsNull() {
addCriterion("`local` is null");
return (Criteria) this;
}
public Criteria andAssociationIsNotNull() {
addCriterion("association is not null");
public Criteria andLocalIsNotNull() {
addCriterion("`local` is not null");
return (Criteria) this;
}
public Criteria andAssociationEqualTo(Boolean value) {
addCriterion("association =", value, "association");
public Criteria andLocalEqualTo(Boolean value) {
addCriterion("`local` =", value, "local");
return (Criteria) this;
}
public Criteria andAssociationNotEqualTo(Boolean value) {
addCriterion("association <>", value, "association");
public Criteria andLocalNotEqualTo(Boolean value) {
addCriterion("`local` <>", value, "local");
return (Criteria) this;
}
public Criteria andAssociationGreaterThan(Boolean value) {
addCriterion("association >", value, "association");
public Criteria andLocalGreaterThan(Boolean value) {
addCriterion("`local` >", value, "local");
return (Criteria) this;
}
public Criteria andAssociationGreaterThanOrEqualTo(Boolean value) {
addCriterion("association >=", value, "association");
public Criteria andLocalGreaterThanOrEqualTo(Boolean value) {
addCriterion("`local` >=", value, "local");
return (Criteria) this;
}
public Criteria andAssociationLessThan(Boolean value) {
addCriterion("association <", value, "association");
public Criteria andLocalLessThan(Boolean value) {
addCriterion("`local` <", value, "local");
return (Criteria) this;
}
public Criteria andAssociationLessThanOrEqualTo(Boolean value) {
addCriterion("association <=", value, "association");
public Criteria andLocalLessThanOrEqualTo(Boolean value) {
addCriterion("`local` <=", value, "local");
return (Criteria) this;
}
public Criteria andAssociationIn(List<Boolean> values) {
addCriterion("association in", values, "association");
public Criteria andLocalIn(List<Boolean> values) {
addCriterion("`local` in", values, "local");
return (Criteria) this;
}
public Criteria andAssociationNotIn(List<Boolean> values) {
addCriterion("association not in", values, "association");
public Criteria andLocalNotIn(List<Boolean> values) {
addCriterion("`local` not in", values, "local");
return (Criteria) this;
}
public Criteria andAssociationBetween(Boolean value1, Boolean value2) {
addCriterion("association between", value1, value2, "association");
public Criteria andLocalBetween(Boolean value1, Boolean value2) {
addCriterion("`local` between", value1, value2, "local");
return (Criteria) this;
}
public Criteria andAssociationNotBetween(Boolean value1, Boolean value2) {
addCriterion("association not between", value1, value2, "association");
public Criteria andLocalNotBetween(Boolean value1, Boolean value2) {
addCriterion("`local` not between", value1, value2, "local");
return (Criteria) this;
}

View File

@ -45,9 +45,9 @@ public class BugComment implements Serializable {
private Long updateTime;
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug_comment.description.not_blank}", groups = {Created.class})
@Size(min = 1, max = 65535, message = "{bug_comment.description.length_range}", groups = {Created.class, Updated.class})
private String description;
@NotBlank(message = "{bug_comment.content.not_blank}", groups = {Created.class})
@Size(min = 1, max = 65535, message = "{bug_comment.content.length_range}", groups = {Created.class, Updated.class})
private String content;
private static final long serialVersionUID = 1L;
@ -61,7 +61,7 @@ public class BugComment implements Serializable {
createTime("create_time", "createTime", "BIGINT", false),
updateUser("update_user", "updateUser", "VARCHAR", false),
updateTime("update_time", "updateTime", "BIGINT", false),
description("description", "description", "LONGVARCHAR", false);
content("content", "content", "LONGVARCHAR", false);
private static final String BEGINNING_DELIMITER = "`";

View File

@ -304,73 +304,143 @@ public class BugExample {
return (Criteria) this;
}
public Criteria andAssignUserIsNull() {
addCriterion("assign_user is null");
public Criteria andHandleUsersIsNull() {
addCriterion("handle_users is null");
return (Criteria) this;
}
public Criteria andAssignUserIsNotNull() {
addCriterion("assign_user is not null");
public Criteria andHandleUsersIsNotNull() {
addCriterion("handle_users is not null");
return (Criteria) this;
}
public Criteria andAssignUserEqualTo(String value) {
addCriterion("assign_user =", value, "assignUser");
public Criteria andHandleUsersEqualTo(String value) {
addCriterion("handle_users =", value, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserNotEqualTo(String value) {
addCriterion("assign_user <>", value, "assignUser");
public Criteria andHandleUsersNotEqualTo(String value) {
addCriterion("handle_users <>", value, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserGreaterThan(String value) {
addCriterion("assign_user >", value, "assignUser");
public Criteria andHandleUsersGreaterThan(String value) {
addCriterion("handle_users >", value, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserGreaterThanOrEqualTo(String value) {
addCriterion("assign_user >=", value, "assignUser");
public Criteria andHandleUsersGreaterThanOrEqualTo(String value) {
addCriterion("handle_users >=", value, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserLessThan(String value) {
addCriterion("assign_user <", value, "assignUser");
public Criteria andHandleUsersLessThan(String value) {
addCriterion("handle_users <", value, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserLessThanOrEqualTo(String value) {
addCriterion("assign_user <=", value, "assignUser");
public Criteria andHandleUsersLessThanOrEqualTo(String value) {
addCriterion("handle_users <=", value, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserLike(String value) {
addCriterion("assign_user like", value, "assignUser");
public Criteria andHandleUsersLike(String value) {
addCriterion("handle_users like", value, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserNotLike(String value) {
addCriterion("assign_user not like", value, "assignUser");
public Criteria andHandleUsersNotLike(String value) {
addCriterion("handle_users not like", value, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserIn(List<String> values) {
addCriterion("assign_user in", values, "assignUser");
public Criteria andHandleUsersIn(List<String> values) {
addCriterion("handle_users in", values, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserNotIn(List<String> values) {
addCriterion("assign_user not in", values, "assignUser");
public Criteria andHandleUsersNotIn(List<String> values) {
addCriterion("handle_users not in", values, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserBetween(String value1, String value2) {
addCriterion("assign_user between", value1, value2, "assignUser");
public Criteria andHandleUsersBetween(String value1, String value2) {
addCriterion("handle_users between", value1, value2, "handleUsers");
return (Criteria) this;
}
public Criteria andAssignUserNotBetween(String value1, String value2) {
addCriterion("assign_user not between", value1, value2, "assignUser");
public Criteria andHandleUsersNotBetween(String value1, String value2) {
addCriterion("handle_users not between", value1, value2, "handleUsers");
return (Criteria) this;
}
public Criteria andHandleUserIsNull() {
addCriterion("handle_user is null");
return (Criteria) this;
}
public Criteria andHandleUserIsNotNull() {
addCriterion("handle_user is not null");
return (Criteria) this;
}
public Criteria andHandleUserEqualTo(String value) {
addCriterion("handle_user =", value, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserNotEqualTo(String value) {
addCriterion("handle_user <>", value, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserGreaterThan(String value) {
addCriterion("handle_user >", value, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserGreaterThanOrEqualTo(String value) {
addCriterion("handle_user >=", value, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserLessThan(String value) {
addCriterion("handle_user <", value, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserLessThanOrEqualTo(String value) {
addCriterion("handle_user <=", value, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserLike(String value) {
addCriterion("handle_user like", value, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserNotLike(String value) {
addCriterion("handle_user not like", value, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserIn(List<String> values) {
addCriterion("handle_user in", values, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserNotIn(List<String> values) {
addCriterion("handle_user not in", values, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserBetween(String value1, String value2) {
addCriterion("handle_user between", value1, value2, "handleUser");
return (Criteria) this;
}
public Criteria andHandleUserNotBetween(String value1, String value2) {
addCriterion("handle_user not between", value1, value2, "handleUser");
return (Criteria) this;
}

View File

@ -27,6 +27,12 @@ public class BugHistory implements Serializable {
@Schema(description = "变更记录批次号")
private Integer num;
@Schema(description = "变更类型; IMPORT/EDIT/ROLLBACK")
private String type;
@Schema(description = "回退来源")
private String rollbackSourceId;
@Schema(description = "操作人")
private String createUser;
@ -43,6 +49,8 @@ public class BugHistory implements Serializable {
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);

View File

@ -304,6 +304,146 @@ public class BugHistoryExample {
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;

View File

@ -18,9 +18,7 @@ public class BugRelationCase implements Serializable {
@Size(min = 1, max = 50, message = "{bug_relation_case.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(description = "关联功能用例ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug_relation_case.case_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{bug_relation_case.case_id.length_range}", groups = {Created.class, Updated.class})
@Schema(description = "关联功能用例ID")
private String caseId;
@Schema(description = "缺陷ID", requiredMode = Schema.RequiredMode.REQUIRED)

View File

@ -7,7 +7,7 @@
<result column="file_id" jdbcType="VARCHAR" property="fileId" />
<result column="file_name" jdbcType="VARCHAR" property="fileName" />
<result column="size" jdbcType="BIGINT" property="size" />
<result column="association" jdbcType="BIT" property="association" />
<result column="local" jdbcType="BIT" property="local" />
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
</resultMap>
@ -70,7 +70,7 @@
</where>
</sql>
<sql id="Base_Column_List">
id, bug_id, file_id, file_name, `size`, association, create_user, create_time
id, bug_id, file_id, file_name, `size`, `local`, create_user, create_time
</sql>
<select id="selectByExample" parameterType="io.metersphere.bug.domain.BugAttachmentExample" resultMap="BaseResultMap">
select
@ -104,11 +104,11 @@
</delete>
<insert id="insert" parameterType="io.metersphere.bug.domain.BugAttachment">
insert into bug_attachment (id, bug_id, file_id,
file_name, `size`, association,
create_user, create_time)
file_name, `size`, `local`, create_user,
create_time)
values (#{id,jdbcType=VARCHAR}, #{bugId,jdbcType=VARCHAR}, #{fileId,jdbcType=VARCHAR},
#{fileName,jdbcType=VARCHAR}, #{size,jdbcType=BIGINT}, #{association,jdbcType=BIT},
#{createUser,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT})
#{fileName,jdbcType=VARCHAR}, #{size,jdbcType=BIGINT}, #{local,jdbcType=BIT}, #{createUser,jdbcType=VARCHAR},
#{createTime,jdbcType=BIGINT})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.bug.domain.BugAttachment">
insert into bug_attachment
@ -128,8 +128,8 @@
<if test="size != null">
`size`,
</if>
<if test="association != null">
association,
<if test="local != null">
`local`,
</if>
<if test="createUser != null">
create_user,
@ -154,8 +154,8 @@
<if test="size != null">
#{size,jdbcType=BIGINT},
</if>
<if test="association != null">
#{association,jdbcType=BIT},
<if test="local != null">
#{local,jdbcType=BIT},
</if>
<if test="createUser != null">
#{createUser,jdbcType=VARCHAR},
@ -189,8 +189,8 @@
<if test="record.size != null">
`size` = #{record.size,jdbcType=BIGINT},
</if>
<if test="record.association != null">
association = #{record.association,jdbcType=BIT},
<if test="record.local != null">
`local` = #{record.local,jdbcType=BIT},
</if>
<if test="record.createUser != null">
create_user = #{record.createUser,jdbcType=VARCHAR},
@ -210,7 +210,7 @@
file_id = #{record.fileId,jdbcType=VARCHAR},
file_name = #{record.fileName,jdbcType=VARCHAR},
`size` = #{record.size,jdbcType=BIGINT},
association = #{record.association,jdbcType=BIT},
`local` = #{record.local,jdbcType=BIT},
create_user = #{record.createUser,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT}
<if test="_parameter != null">
@ -232,8 +232,8 @@
<if test="size != null">
`size` = #{size,jdbcType=BIGINT},
</if>
<if test="association != null">
association = #{association,jdbcType=BIT},
<if test="local != null">
`local` = #{local,jdbcType=BIT},
</if>
<if test="createUser != null">
create_user = #{createUser,jdbcType=VARCHAR},
@ -250,18 +250,18 @@
file_id = #{fileId,jdbcType=VARCHAR},
file_name = #{fileName,jdbcType=VARCHAR},
`size` = #{size,jdbcType=BIGINT},
association = #{association,jdbcType=BIT},
`local` = #{local,jdbcType=BIT},
create_user = #{createUser,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into bug_attachment
(id, bug_id, file_id, file_name, `size`, association, create_user, create_time)
(id, bug_id, file_id, file_name, `size`, `local`, create_user, create_time)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.bugId,jdbcType=VARCHAR}, #{item.fileId,jdbcType=VARCHAR},
#{item.fileName,jdbcType=VARCHAR}, #{item.size,jdbcType=BIGINT}, #{item.association,jdbcType=BIT},
#{item.fileName,jdbcType=VARCHAR}, #{item.size,jdbcType=BIGINT}, #{item.local,jdbcType=BIT},
#{item.createUser,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT})
</foreach>
</insert>
@ -290,8 +290,8 @@
<if test="'size'.toString() == column.value">
#{item.size,jdbcType=BIGINT}
</if>
<if test="'association'.toString() == column.value">
#{item.association,jdbcType=BIT}
<if test="'local'.toString() == column.value">
#{item.local,jdbcType=BIT}
</if>
<if test="'create_user'.toString() == column.value">
#{item.createUser,jdbcType=VARCHAR}

View File

@ -5,6 +5,7 @@
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="bug_id" jdbcType="VARCHAR" property="bugId" />
<result column="reply_user" jdbcType="VARCHAR" property="replyUser" />
<result column="notifier" jdbcType="VARCHAR" property="notifier" />
<result column="parent_id" jdbcType="VARCHAR" property="parentId" />
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
@ -12,7 +13,7 @@
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.bug.domain.BugComment">
<result column="description" jdbcType="LONGVARCHAR" property="description" />
<result column="content" jdbcType="LONGVARCHAR" property="content" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -73,10 +74,11 @@
</where>
</sql>
<sql id="Base_Column_List">
id, bug_id, reply_user, parent_id, create_user, create_time, update_user, update_time
id, bug_id, reply_user, notifier, parent_id, create_user, create_time, update_user,
update_time
</sql>
<sql id="Blob_Column_List">
description
content
</sql>
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.bug.domain.BugCommentExample" resultMap="ResultMapWithBLOBs">
select
@ -128,13 +130,13 @@
</delete>
<insert id="insert" parameterType="io.metersphere.bug.domain.BugComment">
insert into bug_comment (id, bug_id, reply_user,
parent_id, create_user, create_time,
update_user, update_time, description
)
notifier, parent_id, create_user,
create_time, update_user, update_time,
content)
values (#{id,jdbcType=VARCHAR}, #{bugId,jdbcType=VARCHAR}, #{replyUser,jdbcType=VARCHAR},
#{parentId,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT},
#{updateUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT}, #{description,jdbcType=LONGVARCHAR}
)
#{notifier,jdbcType=VARCHAR}, #{parentId,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR},
#{createTime,jdbcType=BIGINT}, #{updateUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT},
#{content,jdbcType=LONGVARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.bug.domain.BugComment">
insert into bug_comment
@ -148,6 +150,9 @@
<if test="replyUser != null">
reply_user,
</if>
<if test="notifier != null">
notifier,
</if>
<if test="parentId != null">
parent_id,
</if>
@ -163,8 +168,8 @@
<if test="updateTime != null">
update_time,
</if>
<if test="description != null">
description,
<if test="content != null">
content,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
@ -177,6 +182,9 @@
<if test="replyUser != null">
#{replyUser,jdbcType=VARCHAR},
</if>
<if test="notifier != null">
#{notifier,jdbcType=VARCHAR},
</if>
<if test="parentId != null">
#{parentId,jdbcType=VARCHAR},
</if>
@ -192,8 +200,8 @@
<if test="updateTime != null">
#{updateTime,jdbcType=BIGINT},
</if>
<if test="description != null">
#{description,jdbcType=LONGVARCHAR},
<if test="content != null">
#{content,jdbcType=LONGVARCHAR},
</if>
</trim>
</insert>
@ -215,6 +223,9 @@
<if test="record.replyUser != null">
reply_user = #{record.replyUser,jdbcType=VARCHAR},
</if>
<if test="record.notifier != null">
notifier = #{record.notifier,jdbcType=VARCHAR},
</if>
<if test="record.parentId != null">
parent_id = #{record.parentId,jdbcType=VARCHAR},
</if>
@ -230,8 +241,8 @@
<if test="record.updateTime != null">
update_time = #{record.updateTime,jdbcType=BIGINT},
</if>
<if test="record.description != null">
description = #{record.description,jdbcType=LONGVARCHAR},
<if test="record.content != null">
content = #{record.content,jdbcType=LONGVARCHAR},
</if>
</set>
<if test="_parameter != null">
@ -243,12 +254,13 @@
set id = #{record.id,jdbcType=VARCHAR},
bug_id = #{record.bugId,jdbcType=VARCHAR},
reply_user = #{record.replyUser,jdbcType=VARCHAR},
notifier = #{record.notifier,jdbcType=VARCHAR},
parent_id = #{record.parentId,jdbcType=VARCHAR},
create_user = #{record.createUser,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
update_user = #{record.updateUser,jdbcType=VARCHAR},
update_time = #{record.updateTime,jdbcType=BIGINT},
description = #{record.description,jdbcType=LONGVARCHAR}
content = #{record.content,jdbcType=LONGVARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -258,6 +270,7 @@
set id = #{record.id,jdbcType=VARCHAR},
bug_id = #{record.bugId,jdbcType=VARCHAR},
reply_user = #{record.replyUser,jdbcType=VARCHAR},
notifier = #{record.notifier,jdbcType=VARCHAR},
parent_id = #{record.parentId,jdbcType=VARCHAR},
create_user = #{record.createUser,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
@ -276,6 +289,9 @@
<if test="replyUser != null">
reply_user = #{replyUser,jdbcType=VARCHAR},
</if>
<if test="notifier != null">
notifier = #{notifier,jdbcType=VARCHAR},
</if>
<if test="parentId != null">
parent_id = #{parentId,jdbcType=VARCHAR},
</if>
@ -291,8 +307,8 @@
<if test="updateTime != null">
update_time = #{updateTime,jdbcType=BIGINT},
</if>
<if test="description != null">
description = #{description,jdbcType=LONGVARCHAR},
<if test="content != null">
content = #{content,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
@ -301,18 +317,20 @@
update bug_comment
set bug_id = #{bugId,jdbcType=VARCHAR},
reply_user = #{replyUser,jdbcType=VARCHAR},
notifier = #{notifier,jdbcType=VARCHAR},
parent_id = #{parentId,jdbcType=VARCHAR},
create_user = #{createUser,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
update_user = #{updateUser,jdbcType=VARCHAR},
update_time = #{updateTime,jdbcType=BIGINT},
description = #{description,jdbcType=LONGVARCHAR}
content = #{content,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.bug.domain.BugComment">
update bug_comment
set bug_id = #{bugId,jdbcType=VARCHAR},
reply_user = #{replyUser,jdbcType=VARCHAR},
notifier = #{notifier,jdbcType=VARCHAR},
parent_id = #{parentId,jdbcType=VARCHAR},
create_user = #{createUser,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
@ -322,14 +340,14 @@
</update>
<insert id="batchInsert" parameterType="map">
insert into bug_comment
(id, bug_id, reply_user, parent_id, create_user, create_time, update_user, update_time,
description)
(id, bug_id, reply_user, notifier, parent_id, create_user, create_time, update_user,
update_time, content)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.bugId,jdbcType=VARCHAR}, #{item.replyUser,jdbcType=VARCHAR},
#{item.parentId,jdbcType=VARCHAR}, #{item.createUser,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT},
#{item.updateUser,jdbcType=VARCHAR}, #{item.updateTime,jdbcType=BIGINT}, #{item.description,jdbcType=LONGVARCHAR}
)
#{item.notifier,jdbcType=VARCHAR}, #{item.parentId,jdbcType=VARCHAR}, #{item.createUser,jdbcType=VARCHAR},
#{item.createTime,jdbcType=BIGINT}, #{item.updateUser,jdbcType=VARCHAR}, #{item.updateTime,jdbcType=BIGINT},
#{item.content,jdbcType=LONGVARCHAR})
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
@ -351,6 +369,9 @@
<if test="'reply_user'.toString() == column.value">
#{item.replyUser,jdbcType=VARCHAR}
</if>
<if test="'notifier'.toString() == column.value">
#{item.notifier,jdbcType=VARCHAR}
</if>
<if test="'parent_id'.toString() == column.value">
#{item.parentId,jdbcType=VARCHAR}
</if>
@ -366,8 +387,8 @@
<if test="'update_time'.toString() == column.value">
#{item.updateTime,jdbcType=BIGINT}
</if>
<if test="'description'.toString() == column.value">
#{item.description,jdbcType=LONGVARCHAR}
<if test="'content'.toString() == column.value">
#{item.content,jdbcType=LONGVARCHAR}
</if>
</foreach>
)

View File

@ -5,6 +5,8 @@
<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>
@ -70,7 +72,7 @@
</where>
</sql>
<sql id="Base_Column_List">
id, bug_id, num, create_user, create_time
id, bug_id, num, `type`, rollback_source_id, create_user, create_time
</sql>
<sql id="Blob_Column_List">
content
@ -125,11 +127,11 @@
</delete>
<insert id="insert" parameterType="io.metersphere.bug.domain.BugHistory">
insert into bug_history (id, bug_id, num,
create_user, create_time, content
)
`type`, rollback_source_id, create_user,
create_time, content)
values (#{id,jdbcType=VARCHAR}, #{bugId,jdbcType=VARCHAR}, #{num,jdbcType=INTEGER},
#{createUser,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{content,jdbcType=LONGVARBINARY}
)
#{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
@ -143,6 +145,12 @@
<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>
@ -163,6 +171,12 @@
<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>
@ -192,6 +206,12 @@
<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>
@ -211,6 +231,8 @@
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}
@ -223,6 +245,8 @@
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">
@ -238,6 +262,12 @@
<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>
@ -254,6 +284,8 @@
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}
@ -263,18 +295,20 @@
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, create_user, create_time, content)
(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.createUser,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT}, #{item.content,jdbcType=LONGVARBINARY}
)
#{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">
@ -296,6 +330,12 @@
<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>

View File

@ -5,7 +5,8 @@
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="num" jdbcType="INTEGER" property="num" />
<result column="title" jdbcType="VARCHAR" property="title" />
<result column="assign_user" jdbcType="VARCHAR" property="assignUser" />
<result column="handle_users" jdbcType="VARCHAR" property="handleUsers" />
<result column="handle_user" jdbcType="VARCHAR" property="handleUser" />
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="update_user" jdbcType="VARCHAR" property="updateUser" />
@ -79,9 +80,9 @@
</where>
</sql>
<sql id="Base_Column_List">
id, num, title, assign_user, create_user, create_time, update_user, update_time,
delete_user, delete_time, project_id, template_id, platform, `status`, tag, platform_bug_id,
trash
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`,
tag, platform_bug_id, trash
</sql>
<select id="selectByExample" parameterType="io.metersphere.bug.domain.BugExample" resultMap="BaseResultMap">
select
@ -115,17 +116,19 @@
</delete>
<insert id="insert" parameterType="io.metersphere.bug.domain.Bug">
insert into bug (id, num, title,
assign_user, create_user, create_time,
update_user, update_time, delete_user,
delete_time, project_id, template_id,
platform, `status`, tag,
platform_bug_id, trash)
handle_users, handle_user, create_user,
create_time, update_user, update_time,
delete_user, delete_time, project_id,
template_id, platform, `status`,
tag, platform_bug_id, trash
)
values (#{id,jdbcType=VARCHAR}, #{num,jdbcType=INTEGER}, #{title,jdbcType=VARCHAR},
#{assignUser,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT},
#{updateUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT}, #{deleteUser,jdbcType=VARCHAR},
#{deleteTime,jdbcType=BIGINT}, #{projectId,jdbcType=VARCHAR}, #{templateId,jdbcType=VARCHAR},
#{platform,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{tag,jdbcType=VARCHAR},
#{platformBugId,jdbcType=VARCHAR}, #{trash,jdbcType=BIT})
#{handleUsers,jdbcType=VARCHAR}, #{handleUser,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR},
#{createTime,jdbcType=BIGINT}, #{updateUser,jdbcType=VARCHAR}, #{updateTime,jdbcType=BIGINT},
#{deleteUser,jdbcType=VARCHAR}, #{deleteTime,jdbcType=BIGINT}, #{projectId,jdbcType=VARCHAR},
#{templateId,jdbcType=VARCHAR}, #{platform,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR},
#{tag,jdbcType=VARCHAR}, #{platformBugId,jdbcType=VARCHAR}, #{trash,jdbcType=BIT}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.bug.domain.Bug">
insert into bug
@ -139,8 +142,11 @@
<if test="title != null">
title,
</if>
<if test="assignUser != null">
assign_user,
<if test="handleUsers != null">
handle_users,
</if>
<if test="handleUser != null">
handle_user,
</if>
<if test="createUser != null">
create_user,
@ -192,8 +198,11 @@
<if test="title != null">
#{title,jdbcType=VARCHAR},
</if>
<if test="assignUser != null">
#{assignUser,jdbcType=VARCHAR},
<if test="handleUsers != null">
#{handleUsers,jdbcType=VARCHAR},
</if>
<if test="handleUser != null">
#{handleUser,jdbcType=VARCHAR},
</if>
<if test="createUser != null">
#{createUser,jdbcType=VARCHAR},
@ -254,8 +263,11 @@
<if test="record.title != null">
title = #{record.title,jdbcType=VARCHAR},
</if>
<if test="record.assignUser != null">
assign_user = #{record.assignUser,jdbcType=VARCHAR},
<if test="record.handleUsers != null">
handle_users = #{record.handleUsers,jdbcType=VARCHAR},
</if>
<if test="record.handleUser != null">
handle_user = #{record.handleUser,jdbcType=VARCHAR},
</if>
<if test="record.createUser != null">
create_user = #{record.createUser,jdbcType=VARCHAR},
@ -306,7 +318,8 @@
set id = #{record.id,jdbcType=VARCHAR},
num = #{record.num,jdbcType=INTEGER},
title = #{record.title,jdbcType=VARCHAR},
assign_user = #{record.assignUser,jdbcType=VARCHAR},
handle_users = #{record.handleUsers,jdbcType=VARCHAR},
handle_user = #{record.handleUser,jdbcType=VARCHAR},
create_user = #{record.createUser,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
update_user = #{record.updateUser,jdbcType=VARCHAR},
@ -333,8 +346,11 @@
<if test="title != null">
title = #{title,jdbcType=VARCHAR},
</if>
<if test="assignUser != null">
assign_user = #{assignUser,jdbcType=VARCHAR},
<if test="handleUsers != null">
handle_users = #{handleUsers,jdbcType=VARCHAR},
</if>
<if test="handleUser != null">
handle_user = #{handleUser,jdbcType=VARCHAR},
</if>
<if test="createUser != null">
create_user = #{createUser,jdbcType=VARCHAR},
@ -382,7 +398,8 @@
update bug
set num = #{num,jdbcType=INTEGER},
title = #{title,jdbcType=VARCHAR},
assign_user = #{assignUser,jdbcType=VARCHAR},
handle_users = #{handleUsers,jdbcType=VARCHAR},
handle_user = #{handleUser,jdbcType=VARCHAR},
create_user = #{createUser,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
update_user = #{updateUser,jdbcType=VARCHAR},
@ -400,17 +417,18 @@
</update>
<insert id="batchInsert" parameterType="map">
insert into bug
(id, num, title, assign_user, create_user, create_time, update_user, update_time,
delete_user, delete_time, project_id, template_id, platform, `status`, tag, platform_bug_id,
trash)
(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`,
tag, platform_bug_id, trash)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.num,jdbcType=INTEGER}, #{item.title,jdbcType=VARCHAR},
#{item.assignUser,jdbcType=VARCHAR}, #{item.createUser,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT},
#{item.updateUser,jdbcType=VARCHAR}, #{item.updateTime,jdbcType=BIGINT}, #{item.deleteUser,jdbcType=VARCHAR},
#{item.deleteTime,jdbcType=BIGINT}, #{item.projectId,jdbcType=VARCHAR}, #{item.templateId,jdbcType=VARCHAR},
#{item.platform,jdbcType=VARCHAR}, #{item.status,jdbcType=VARCHAR}, #{item.tag,jdbcType=VARCHAR},
#{item.platformBugId,jdbcType=VARCHAR}, #{item.trash,jdbcType=BIT})
#{item.handleUsers,jdbcType=VARCHAR}, #{item.handleUser,jdbcType=VARCHAR}, #{item.createUser,jdbcType=VARCHAR},
#{item.createTime,jdbcType=BIGINT}, #{item.updateUser,jdbcType=VARCHAR}, #{item.updateTime,jdbcType=BIGINT},
#{item.deleteUser,jdbcType=VARCHAR}, #{item.deleteTime,jdbcType=BIGINT}, #{item.projectId,jdbcType=VARCHAR},
#{item.templateId,jdbcType=VARCHAR}, #{item.platform,jdbcType=VARCHAR}, #{item.status,jdbcType=VARCHAR},
#{item.tag,jdbcType=VARCHAR}, #{item.platformBugId,jdbcType=VARCHAR}, #{item.trash,jdbcType=BIT}
)
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
@ -432,8 +450,11 @@
<if test="'title'.toString() == column.value">
#{item.title,jdbcType=VARCHAR}
</if>
<if test="'assign_user'.toString() == column.value">
#{item.assignUser,jdbcType=VARCHAR}
<if test="'handle_users'.toString() == column.value">
#{item.handleUsers,jdbcType=VARCHAR}
</if>
<if test="'handle_user'.toString() == column.value">
#{item.handleUser,jdbcType=VARCHAR}
</if>
<if test="'create_user'.toString() == column.value">
#{item.createUser,jdbcType=VARCHAR}

View File

@ -1,12 +1,12 @@
-- set innodb lock wait timeout
SET SESSION innodb_lock_wait_timeout = 7200;
DROP TABLE IF EXISTS bug;
CREATE TABLE IF NOT EXISTS bug(
`id` VARCHAR(50) NOT NULL COMMENT 'ID' ,
`num` INT NOT NULL COMMENT '业务ID' ,
`title` VARCHAR(300) NOT NULL COMMENT '缺陷标题' ,
`assign_user` VARCHAR(50) NOT NULL COMMENT '指派人' ,
`handle_users` VARCHAR(1000) COMMENT '处理人集合; 预留字段, 后续工作台统计可能需要' ,
`handle_user` VARCHAR(50) NOT NULL COMMENT '处理人' ,
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
`update_user` VARCHAR(50) NOT NULL COMMENT '更新人' ,
@ -26,23 +26,24 @@ CREATE TABLE IF NOT EXISTS bug(
CREATE INDEX idx_num ON bug(num);
CREATE INDEX idx_title ON bug(title);
CREATE INDEX idx_assign_user ON bug(assign_user);
CREATE INDEX idx_handle_user ON bug(handle_user);
CREATE INDEX idx_create_user ON bug(create_user);
CREATE INDEX idx_create_time ON bug(create_time);
CREATE INDEX idx_update_user ON bug(update_user);
CREATE INDEX idx_update_time ON bug(update_time);
CREATE INDEX idx_project_id ON bug(project_id);
CREATE INDEX idx_platform ON bug(platform);
CREATE INDEX idx_status ON bug(status);
CREATE INDEX idx_trash ON bug(trash);
DROP TABLE IF EXISTS bug_content;
CREATE TABLE IF NOT EXISTS bug_content(
`bug_id` VARCHAR(50) NOT NULL COMMENT '缺陷ID' ,
`description` LONGTEXT COMMENT '缺陷描述' ,
PRIMARY KEY (bug_id)
) COMMENT = '缺陷内容';
DROP TABLE IF EXISTS bug_follower;
CREATE TABLE IF NOT EXISTS bug_follower(
`bug_id` VARCHAR(50) NOT NULL COMMENT '缺陷ID' ,
`user_id` VARCHAR(50) NOT NULL COMMENT '关注人ID' ,
@ -52,14 +53,25 @@ CREATE TABLE IF NOT EXISTS bug_follower(
CREATE INDEX idx_follow_id ON bug_follower(user_id);
DROP TABLE IF EXISTS bug_comment;
CREATE TABLE IF NOT EXISTS bug_attachment(
`id` VARCHAR(255) NOT NULL COMMENT 'ID' ,
`bug_id` VARCHAR(50) NOT NULL COMMENT '缺陷ID' ,
`file_id` VARCHAR(50) NOT NULL COMMENT '文件ID' ,
`file_name` VARCHAR(255) NOT NULL COMMENT '文件名称' ,
`size` BIGINT NOT NULL COMMENT '文件大小' ,
`local` BIT(1) NOT NULL COMMENT '是否本地' ,
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
PRIMARY KEY (id)
) COMMENT = '缺陷附件';
CREATE TABLE IF NOT EXISTS bug_comment(
`id` VARCHAR(50) NOT NULL COMMENT 'ID' ,
`bug_id` VARCHAR(50) NOT NULL COMMENT '缺陷ID' ,
`reply_user` VARCHAR(50) COMMENT '回复人' ,
`notifier` VARCHAR(1000) COMMENT '通知人' ,
`parent_id` VARCHAR(50) COMMENT '父评论ID' ,
`description` TEXT NOT NULL COMMENT '内容' ,
`content` TEXT NOT NULL COMMENT '内容' ,
`create_user` VARCHAR(50) NOT NULL COMMENT '评论人' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
`update_user` VARCHAR(50) NOT NULL COMMENT '更新人' ,
@ -71,31 +83,16 @@ CREATE TABLE IF NOT EXISTS bug_comment(
CREATE INDEX idx_bug_id ON bug_comment(bug_id);
CREATE INDEX idx_parent_id ON bug_comment(parent_id);
DROP TABLE IF EXISTS bug_attachment;
CREATE TABLE IF NOT EXISTS bug_attachment(
`id` VARCHAR(255) NOT NULL COMMENT 'ID' ,
`bug_id` VARCHAR(50) NOT NULL COMMENT '缺陷ID' ,
`file_id` VARCHAR(50) NOT NULL COMMENT '文件ID' ,
`file_name` VARCHAR(255) NOT NULL COMMENT '文件名称' ,
`size` BIGINT NOT NULL COMMENT '文件大小' ,
`association` BIT(1) NOT NULL COMMENT '是否关联' ,
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
PRIMARY KEY (id)
) COMMENT = '缺陷附件';
DROP TABLE IF EXISTS bug_custom_field;
CREATE TABLE IF NOT EXISTS bug_custom_field(
`bug_id` VARCHAR(50) NOT NULL COMMENT '缺陷ID' ,
`field_id` VARCHAR(50) NOT NULL COMMENT '字段ID' ,
`value` VARCHAR(1000) COMMENT '字段值' ,
PRIMARY KEY (bug_id)
PRIMARY KEY (bug_id, field_id)
) COMMENT = '缺陷自定义字段';
DROP TABLE IF EXISTS bug_relation_case;
CREATE TABLE IF NOT EXISTS bug_relation_case(
`id` VARCHAR(50) NOT NULL COMMENT 'ID' ,
`case_id` VARCHAR(50) NOT NULL COMMENT '关联功能用例ID' ,
`case_id` VARCHAR(50) COMMENT '关联功能用例ID' ,
`bug_id` VARCHAR(50) NOT NULL COMMENT '缺陷ID' ,
`case_type` VARCHAR(64) NOT NULL DEFAULT 'functional' COMMENT '关联的用例类型;functional/api/ui/performance' ,
`test_plan_id` VARCHAR(50) COMMENT '关联测试计划ID' ,
@ -112,11 +109,12 @@ 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_type ON bug_relation_case(case_type);
DROP TABLE IF EXISTS bug_history;
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 '操作时间' ,

View File

@ -120,6 +120,10 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_APPLICATION_WORKSTATION:UPDATE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_LOG:READ');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_LOG:UPDATE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'BUG:READ');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'BUG:READ+ADD');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'BUG:READ+UPDATE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'BUG:READ+DELETE');
-- 项目成员权限

View File

@ -0,0 +1,24 @@
package io.metersphere.plugin.platform.dto;
import lombok.Data;
import java.io.File;
@Data
public class SyncAttachmentToPlatformRequest {
/**
* 平台资源Key(需求或缺陷ID)
*/
private String platformKey;
/**
* 需要同步的附件
*/
private File file;
/**
* 同步类型是上传还是删除
* 参考 SyncAttachmentType
*/
private String syncType;
}

View File

@ -1,24 +0,0 @@
package io.metersphere.plugin.platform.dto;
import lombok.Getter;
import lombok.Setter;
import java.io.File;
@Getter
@Setter
public class SyncBugAttachmentRequest {
/**
* 平台 ID
*/
private String platformId;
/**
* 需要同步的附件
*/
private File file;
/**
* 操作类型是更新还是删除
* 参考 AttachmentSyncType
*/
private String syncType;
}

View File

@ -0,0 +1,24 @@
package io.metersphere.plugin.platform.enums;
public enum SyncAttachmentType {
/**
* 附件上传
*/
UPLOAD("upload"),
/**
* 附件删除
*/
DELETE("delete");
private final String syncOperateType;
SyncAttachmentType(String syncOperateType) {
this.syncOperateType = syncOperateType;
}
public String syncOperateType() {
return syncOperateType;
}
}

View File

@ -1,8 +1,11 @@
package io.metersphere.plugin.platform.spi;
import io.metersphere.plugin.platform.dto.PlatformBugUpdateRequest;
import io.metersphere.plugin.platform.dto.PlatformCustomFieldItemDTO;
import io.metersphere.plugin.platform.dto.PluginOptionsRequest;
import io.metersphere.plugin.platform.dto.SelectOption;
import io.metersphere.plugin.platform.dto.PlatformStatusDTO;
import io.metersphere.plugin.platform.dto.SyncAttachmentToPlatformRequest;
import org.pf4j.ExtensionPoint;
import java.util.List;
@ -22,19 +25,23 @@ public interface Platform extends ExtensionPoint {
/**
* 校验项目配置
* 项目设置成点击校验项目 key 时调用
* @param projectConfig 项目配置
*/
void validateProjectConfig(String projectConfig);
/**
* 插件是否支持第三方模板
* @return
* 平台是否支持第三方模板
*
* @return 是否支持第三方模板
*/
boolean isThirdPartTemplateSupport();
/**
* 获取第三方平台缺陷的自定义字段
* 需要 PluginMetaInfo isThirdPartTemplateSupport 返回 true
* @return
*
* @param projectConfig 项目配置信息
* @return 平台自定义字段集合
*/
List<PlatformCustomFieldItemDTO> getThirdPartCustomField(String projectConfig);
@ -44,4 +51,52 @@ public interface Platform extends ExtensionPoint {
* @return
*/
List<SelectOption> getPluginOptions(PluginOptionsRequest optionsRequest);
/**
* 新增平台缺陷
*
* @param request 平台缺陷参数
* @return 平台缺陷ID
*/
String addBug(PlatformBugUpdateRequest request);
/**
* 修改平台缺陷
*
* @param request 平台缺陷参数
*/
void updateBug(PlatformBugUpdateRequest request);
/**
* 删除平台缺陷
*
* @param platformBugId 平台缺陷ID
*/
void deleteBug(String platformBugId);
/**
* 平台是否支持附件同步
*
* @return 是否支持附件同步
*/
boolean isAttachmentUploadSupport();
/**
* 同步MS附件至第三方平台(isAttachmentUploadSupport返回true时执行同步附件的逻辑)
*
* @param request 同步附件参数
*/
void syncAttachmentToPlatform(SyncAttachmentToPlatformRequest request);
/**
* 同步部分缺陷
*/
void syncPartIssues();
/**
* 同步全量缺陷
*/
void syncAllIssues();
List<PlatformStatusDTO> getStatusList();
}

View File

@ -8,66 +8,73 @@ public enum CustomFieldType {
/**
* 输入框
*/
INPUT( false),
INPUT(false, "input"),
/**
* 文本框
*/
TEXTAREA(false),
TEXTAREA(false, "textarea"),
/**
* 单选下拉框框
*/
SELECT( true),
SELECT(true, "select"),
/**
* 多选下拉框框
*/
MULTIPLE_SELECT(true),
MULTIPLE_SELECT(true, "multipleSelect"),
/**
* 单选框
*/
RADIO(true),
RADIO(true, "radio"),
/**
* 复选框
*/
CHECKBOX(true),
CHECKBOX(true, "checkbox"),
/**
* 单选成员
*/
MEMBER(true),
MEMBER(true, "member"),
/**
* 多选成员
*/
MULTIPLE_MEMBER(true),
MULTIPLE_MEMBER(true, "multipleMember"),
/**
* 日期
*/
DATE(false),
DATE(false, "date"),
/**
* 日期时间
*/
DATETIME(false),
DATETIME(false, "datetime"),
/**
* 整型
*/
INT(false),
INT(false, "int"),
/**
* 浮点型
*/
FLOAT(false),
FLOAT(false, "float"),
/**
* 多值输入框标签输入框
*/
MULTIPLE_INPUT(false);
MULTIPLE_INPUT(false, "multipleInput");
private final Boolean hasOption;
CustomFieldType(Boolean hasOption) {
private final String type;
CustomFieldType(Boolean hasOption, String type) {
this.hasOption = hasOption;
this.type = type;
}
public Boolean getHasOption() {
return this.hasOption;
}
public String getType() {
return this.type;
}
public static Set<String> getHasOptionValueSet() {
return Arrays.stream(CustomFieldType.values())
.filter(CustomFieldType::getHasOption)

View File

@ -226,4 +226,11 @@ public class PermissionConstants {
public static final String PROJECT_API_DEBUG_READ_IMPORT = "PROJECT_API_DEBUG:READ+IMPORT";
public static final String PROJECT_API_DEBUG_READ_EXECUTE = "PROJECT_API_DEBUG:READ+EXECUTE";
/*------ end: API_DEBUG ------*/
/*------ start: BUG ------*/
public static final String BUG_READ = "BUG:READ";
public static final String BUG_ADD = "BUG:READ+ADD";
public static final String BUG_UPDATE = "BUG:READ+UPDATE";
public static final String BUG_DELETE = "BUG:READ+DELETE";
/*------ end: BUG ------*/
}

View File

@ -7,12 +7,12 @@ import java.io.File;
public class MsFileUtils {
public static final String DATE_ROOT_DIR = "/opt/metersphere/data/app";
public static final String DATA_ROOT_DIR = "/opt/metersphere/data/app";
public static final String PLUGIN_DIR_NAME = "plugins";
public static final String PLUGIN_DIR = DATE_ROOT_DIR + "/" + PLUGIN_DIR_NAME;
public static final String PLUGIN_DIR = DATA_ROOT_DIR + "/" + PLUGIN_DIR_NAME;
public static final String FUNCTIONAL_CASE_ATTACHMENT_DIR_NAME = "functionalCaseAttachment";
public static final String FUNCTIONAL_CASE_ATTACHMENT_DIR = DATE_ROOT_DIR + "/" + FUNCTIONAL_CASE_ATTACHMENT_DIR_NAME;
public static final String FUNCTIONAL_CASE_ATTACHMENT_DIR = DATA_ROOT_DIR + "/" + FUNCTIONAL_CASE_ATTACHMENT_DIR_NAME;
public static final String BUG_MANAGEMENT_DIR = "bug";
public static void validateFileName(String... fileNames) {
if (fileNames != null) {

View File

@ -13,6 +13,8 @@ bug.delete_user.not_blank=删除人不能为空
bug.delete_user.length_range=删除人长度必须在1-50之间
bug.project_id.not_blank=项目ID不能为空
bug.project_id.length_range=项目ID长度必须在1-50之间
bug.template_id.not_blank=模板ID不能为空
bug.template_id.length_range=模板ID长度必须在1-50之间
bug.platform.not_blank=缺陷平台不能为空
bug.platform.length_range=缺陷平台长度必须在1-50之间
bug.status.not_blank=平台状态不能为空
@ -49,8 +51,8 @@ bug_attachment.file_id.not_blank=文件ID不能为空
bug_attachment.file_id.length_range=文件ID长度必须在1-50之间
bug_attachment.file_name.not_blank=文件名称不能为空
bug_attachment.file_name.length_range=文件名称长度必须在1-50之间
bug_attachment.association.not_blank=是否关联不能为空
bug_attachment.association.length_range=是否关联长度必须在1-50之间
bug_attachment.local.not_blank=是否本地上传不能为空
bug_attachment.local.length_range=是否本地上传长度必须在1-50之间
bug_attachment.create_user.not_blank=创建人不能为空
bug_attachment.create_user.length_range=创建人长度必须在1-50之间
@ -78,4 +80,11 @@ 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之间
bug_history.create_user.length_range=操作人长度必须在1-50之间
# error
bug_not_exist=缺陷不存在
not_local_bug_error=非本地缺陷,无法操作
third_party_not_config=项目未配置第三方平台
bug_attachment_upload_error=缺陷附件上传失败
bug_attachment_delete_error=缺陷附件删除失败

View File

@ -13,6 +13,8 @@ bug.delete_user.not_blank=deleteUser cannot be empty
bug.delete_user.length_range=deleteUser length must be between 1-50
bug.project_id.not_blank=projectId cannot be empty
bug.project_id.length_range=projectId length must be between 1-50
bug.template_id.not_blank=templateId cannot be empty
bug.template_id.length_range=templateId length must be between 1-50
bug.platform.not_blank=platform cannot be empty
bug.platform.length_range=platform length must be between 1-50
bug.status.not_blank=status cannot be empty
@ -49,8 +51,8 @@ bug_attachment.file_id.not_blank=fileId cannot be empty
bug_attachment.file_id.length_range=fileId length must be between 1-50
bug_attachment.file_name.not_blank=fileName cannot be empty
bug_attachment.file_name.length_range=fileName length must be between 1-50
bug_attachment.association.not_blank=association cannot be empty
bug_attachment.association.length_range=association length must be between 1-50
bug_attachment.local.not_blank=local cannot be empty
bug_attachment.local.length_range=local length must be between 1-50
bug_attachment.create_user.not_blank=createUser cannot be empty
bug_attachment.create_user.length_range=createUser length must be between 1-50
@ -78,4 +80,11 @@ 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
bug_history.create_user.length_range=createUser length must be between 1-50
# error
bug_not_exist=Bug does not exist
not_local_bug_error=Not local bug, error
third_party_not_config=The project third-party platform not configured
bug_attachment_upload_error=Bug attachment upload error
bug_attachment_delete_error=Bug attachment delete error

View File

@ -13,6 +13,8 @@ bug.delete_user.not_blank=删除人不能为空
bug.delete_user.length_range=删除人长度必须在1-50之间
bug.project_id.not_blank=项目ID不能为空
bug.project_id.length_range=项目ID长度必须在1-50之间
bug.template_id.not_blank=模板ID不能为空
bug.template_id.length_range=模板ID长度必须在1-50之间
bug.platform.not_blank=缺陷平台不能为空
bug.platform.length_range=缺陷平台长度必须在1-50之间
bug.status.not_blank=平台状态不能为空
@ -49,8 +51,8 @@ bug_attachment.file_id.not_blank=文件ID不能为空
bug_attachment.file_id.length_range=文件ID长度必须在1-50之间
bug_attachment.file_name.not_blank=文件名称不能为空
bug_attachment.file_name.length_range=文件名称长度必须在1-50之间
bug_attachment.association.not_blank=是否关联不能为空
bug_attachment.association.length_range=是否关联长度必须在1-50之间
bug_attachment.local.not_blank=是否本地上传不能为空
bug_attachment.local.length_range=是否本地上传长度必须在1-50之间
bug_attachment.create_user.not_blank=创建人不能为空
bug_attachment.create_user.length_range=创建人长度必须在1-50之间
@ -78,4 +80,11 @@ 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之间
bug_history.create_user.length_range=操作人长度必须在1-50之间
# error
bug_not_exist=缺陷不存在
not_local_bug_error=非本地缺陷,无法操作
third_party_not_config=项目未配置第三方平台
bug_attachment_upload_error=缺陷附件上传失败
bug_attachment_delete_error=缺陷附件删除失败

View File

@ -13,6 +13,8 @@ bug.delete_user.not_blank=删除人不能為空
bug.delete_user.length_range=删除人長度必須在1-50之間
bug.project_id.not_blank=项目ID不能為空
bug.project_id.length_range=项目ID長度必須在1-50之間
bug.template_id.not_blank=模板ID不能為空
bug.template_id.length_range=模板ID長度必須在1-50之間
bug.platform.not_blank=缺陷平台不能為空
bug.platform.length_range=缺陷平台長度必須在1-50之間
bug.status.not_blank=平台状态不能為空
@ -49,8 +51,8 @@ bug_attachment.file_id.not_blank=文件ID不能為空
bug_attachment.file_id.length_range=文件ID長度必須在1-50之間
bug_attachment.file_name.not_blank=文件名称不能為空
bug_attachment.file_name.length_range=文件名称長度必須在1-50之間
bug_attachment.association.not_blank=是否关联不能為空
bug_attachment.association.length_range=是否关联長度必須在1-50之間
bug_attachment.local.not_blank=是否本地上傳不能為空
bug_attachment.local.length_range=是否本地上傳必須在1-50之間
bug_attachment.create_user.not_blank=创建人不能為空
bug_attachment.create_user.length_range=创建人長度必須在1-50之間
@ -80,4 +82,10 @@ bug_history.bug_id.length_range=所属缺陷ID長度必須在1-50之間
bug_history.create_user.not_blank=操作人不能為空
bug_history.create_user.length_range=操作人長度必須在1-50之間
# error
bug_not_exist=缺陷不存在
not_local_bug_error=非本地缺陷,無法操作
third_party_not_config=項目未配置第三方平台
bug_attachment_upload_error=缺陷附件上傳失敗
bug_attachment_delete_error=缺陷附件刪除失敗

View File

@ -471,3 +471,4 @@ file.name.cannot.be.empty=File name cannot be empty
project_template_permission_error=The project template is not turned on
third_part_config_is_null=Third party configuration cannot be empty
plugin_bug_template_remark=Templates are automatically obtained by the system and do not support editing and viewing
default_template=default template

View File

@ -470,3 +470,4 @@ project_template_permission_error=未开启项目模板
third_part_config_is_null=第三方平台配置信息不能为空
plugin_bug_template_remark=模板为系统自动获取,不支持编辑和查看
default_template=默认模板

View File

@ -470,4 +470,5 @@ file.name.cannot.be.empty=文件名稱不能為空
# template
project_template_permission_error=未開啟項目模板
third_part_config_is_null=第三方平臺配置信息不能爲空
plugin_bug_template_remark=模板為系統自動獲取,不支持編輯和查看
plugin_bug_template_remark=模板為系統自動獲取,不支持編輯和查看
default_template=默認模板

View File

@ -28,6 +28,14 @@
<artifactId>metersphere-project-management</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-system-setting</artifactId>
<version>${revision}</version>
<classifier>tests</classifier>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -0,0 +1,89 @@
package io.metersphere.bug.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.bug.dto.BugDTO;
import io.metersphere.bug.dto.request.BugEditRequest;
import io.metersphere.bug.dto.request.BugPageRequest;
import io.metersphere.bug.service.BugService;
import io.metersphere.project.dto.ProjectTemplateOptionDTO;
import io.metersphere.project.service.ProjectTemplateService;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.constants.TemplateScene;
import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager;
import io.metersphere.system.utils.SessionUtils;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
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.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* @author song-cc-rock
*/
@Tag(name = "缺陷管理")
@RestController
@RequestMapping("/bug")
public class BugController {
@Resource
private BugService bugService;
@Resource
private ProjectTemplateService projectTemplateService;
@PostMapping("/page")
@Operation(summary = "缺陷管理-获取缺陷列表")
@RequiresPermissions(PermissionConstants.BUG_READ)
public Pager<List<BugDTO>> page(@Validated @RequestBody BugPageRequest request) {
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "create_time desc");
request.setUseTrash(false);
return PageUtils.setPageInfo(page, bugService.list(request));
}
@PostMapping("/add")
@Operation(summary = "缺陷管理-创建缺陷")
@RequiresPermissions(PermissionConstants.BUG_ADD)
public void add(@Validated({Created.class}) @RequestPart(value = "request") BugEditRequest request,
@RequestPart(value = "file", required = false) List<MultipartFile> files) {
bugService.add(request, files, SessionUtils.getUserId());
}
@PostMapping("/update")
@Operation(summary = "缺陷管理-更新缺陷")
@RequiresPermissions(PermissionConstants.BUG_UPDATE)
public void update(@Validated({Updated.class}) @RequestPart(value = "request") BugEditRequest request,
@RequestPart(value = "file", required = false) List<MultipartFile> files) {
bugService.update(request, files, SessionUtils.getUserId());
}
@GetMapping("/delete/{id}")
@Operation(summary = "缺陷管理-删除缺陷")
@RequiresPermissions(PermissionConstants.BUG_DELETE)
public void delete(@PathVariable String id) {
bugService.delete(id);
}
@GetMapping("/template/option")
@Operation(summary = "缺陷管理-获取当前项目缺陷模板选项")
@RequiresPermissions(PermissionConstants.BUG_READ)
public List<ProjectTemplateOptionDTO> getTemplateOption(@RequestParam(value = "projectId") String projectId) {
return projectTemplateService.getOption(projectId, TemplateScene.BUG.name());
}
@GetMapping("/template/{id}")
@Operation(summary = "缺陷管理-获取模板内容")
@RequiresPermissions(PermissionConstants.BUG_READ)
public TemplateDTO getTemplateField(@PathVariable String id, @RequestParam(value = "projectId") String projectId) {
return bugService.getTemplate(id, projectId);
}
}

View File

@ -0,0 +1,4 @@
package io.metersphere.bug.controller;
public class BugHistoryController {
}

View File

@ -0,0 +1,18 @@
package io.metersphere.bug.dto;
import io.metersphere.system.domain.CustomField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author song-cc-rock
*/
@Data
public class BugCustomFieldDTO extends CustomField {
@Schema(description = "字段值")
private String value;
@Schema(description = "缺陷ID")
private String bugId;
}

View File

@ -0,0 +1,32 @@
package io.metersphere.bug.dto;
import io.metersphere.bug.domain.Bug;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* @author song-cc-rock
*/
@Data
public class BugDTO extends Bug {
@Schema(description = "缺陷内容")
private String description;
@Schema(description = "创建人名称")
private String createUserName;
@Schema(description = "指派人名称")
private String assignUserName;
@Schema(description = "状态名称")
private String statusName;
@Schema(description = "关联用例数量")
private Integer relationCaseCount;
@Schema(description = "自定义字段集合")
private List<BugCustomFieldDTO> customFields;
}

View File

@ -0,0 +1,24 @@
package io.metersphere.bug.dto;
import lombok.Data;
import java.util.List;
@Data
public class BugHistoryContentDTO extends BugDTO {
/**
* 缺陷自定义字段
*/
private List<BugCustomFieldDTO> customFields;
/**
* 缺陷内容
*/
private String description;
/**
* 附件名称集合
*/
private List<String> attachmentNames;
}

View File

@ -0,0 +1,17 @@
package io.metersphere.bug.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author song-cc-rock
*/
@Data
public class BugRelationCaseCountDTO {
@Schema(description = "缺陷ID")
private String bugId;
@Schema(description = "关联用例数量")
private Integer relationCaseCount;
}

View File

@ -0,0 +1,10 @@
package io.metersphere.bug.dto;
import io.metersphere.system.dto.sdk.OptionDTO;
import lombok.Data;
@Data
public class BugStatusOptionDTO extends OptionDTO {
// 状态流转信息
}

View File

@ -0,0 +1,66 @@
package io.metersphere.bug.dto.request;
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.Size;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @author song-cc-rock
*/
@Data
public class BugEditRequest {
@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 = "缺陷标题", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.title.not_blank}", groups = {Created.class, Updated.class})
@Size(min = 1, max = 300, message = "{bug.title.length_range}", groups = {Created.class, Updated.class})
private String title;
@Schema(description = "处理人", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.assign_user.not_blank}", groups = {Created.class, Updated.class})
@Size(min = 1, max = 50, message = "{bug.assign_user.length_range}", groups = {Created.class, Updated.class})
private String handleUser;
@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 = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{bug.status.not_blank}", groups = {Created.class, Updated.class})
@Size(min = 1, max = 50, message = "{bug.status.length_range}", groups = {Created.class, Updated.class})
private String status;
@Schema(description = "标签")
private String tag;
@Schema(description = "缺陷内容")
private String description;
@Schema(description = "自定义字段集合")
private Map<String, String> customFieldMap;
@Schema(description = "删除的本地附件集合")
private List<String> deleteLocalFileIds;
@Schema(description = "取消关联附件集合")
private List<String> unLinkFileIds;
@Schema(description = "关联附件集合")
private List<String> linkFileIds;
}

View File

@ -0,0 +1,17 @@
package io.metersphere.bug.dto.request;
import io.metersphere.system.dto.sdk.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class BugPageRequest extends BasePageRequest {
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
private String projectId;
@Schema(description = "是否回收站")
private boolean useTrash;
}

View File

@ -0,0 +1,39 @@
package io.metersphere.bug.enums;
import lombok.Getter;
/**
* @author song-cc-rock
*/
@Getter
public enum BugPlatform {
/**
* 本地
*/
LOCAL("Local"),
/**
* Jira
*/
JIRA("Jira"),
/**
* Tapd
*/
TAPD("Tapd"),
/**
* 禅道
*/
ZENDAO("Zentao"),
/**
* Azure
*/
AZURE("Azure");
private final String name;
BugPlatform(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,38 @@
package io.metersphere.bug.enums.result;
import io.metersphere.sdk.exception.IResultCode;
/**
* @author song-cc-rock
*/
public enum BugResultCode implements IResultCode {
/**
* 缺陷不存在
*/
BUG_NOT_EXIST(108001, "bug_not_exist"),
/**
* 非Local缺陷异常
*/
NOT_LOCAL_BUG_ERROR(108002, "not_local_bug_error");
private final int code;
private final String message;
BugResultCode(int code, String message) {
this.code = code;
this.message = message;
}
@Override
public int getCode() {
return code;
}
@Override
public String getMessage() {
return getTranslationMessage(this.message);
}
}

View File

@ -0,0 +1,15 @@
package io.metersphere.bug.mapper;
import io.metersphere.bug.domain.BugAttachment;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtBugAttachmentMapper {
/**
* 批量插入缺陷附件关系
* @param attachments 缺陷附件集合
*/
void batchInsert(@Param("list") List<BugAttachment> attachments);
}

View File

@ -0,0 +1,11 @@
<?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.ExtBugAttachmentMapper">
<insert id="batchInsert" parameterType="io.metersphere.bug.domain.BugAttachment">
insert into bug_attachment values
<foreach collection="list" item="attachment" separator=",">
(#{attachment.id}, #{attachment.bugId}, #{attachment.fileId}, #{attachment.fileName},
#{attachment.size}, #{attachment.local}, #{attachment.createUser}, #{attachment.createTime})
</foreach>
</insert>
</mapper>

View File

@ -0,0 +1,24 @@
package io.metersphere.bug.mapper;
import io.metersphere.bug.domain.BugCustomField;
import io.metersphere.bug.dto.BugCustomFieldDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtBugCustomFieldMapper {
/**
* 获取缺陷自定义字段值
* @param bugIds 缺陷集合
* @param projectId 项目ID
* @return 缺陷自定义字段值
*/
List<BugCustomFieldDTO> getBugCustomFields(@Param("ids") List<String> bugIds, @Param("projectId") String projectId);
/**
* 批量插入缺陷自定义字段值
* @param customFields 自定义字段值集合
*/
void batchInsert(@Param("list") List<BugCustomField> customFields);
}

View File

@ -0,0 +1,19 @@
<?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.ExtBugCustomFieldMapper">
<select id="getBugCustomFields" resultType="io.metersphere.bug.dto.BugCustomFieldDTO">
select cf.*, bcf.value, bcf.bug_id from bug_custom_field bcf join custom_field cf on bcf.field_id = cf.id
where cf.scene = 'BUG' and cf.scope_type = 'PROJECT' and scope_id = #{projectId}
and bug_id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
<insert id="batchInsert" parameterType="io.metersphere.bug.domain.BugCustomField">
insert into bug_custom_field (bug_id, field_id, value) values
<foreach collection="list" item="field" separator=",">
(#{field.bugId}, #{field.fieldId}, #{field.value})
</foreach>
</insert>
</mapper>

View File

@ -0,0 +1,26 @@
package io.metersphere.bug.mapper;
import io.metersphere.bug.dto.BugDTO;
import io.metersphere.bug.dto.request.BugPageRequest;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtBugMapper {
/**
* 缺陷列表查询
*
* @param request 请求查询参数
* @return 缺陷列表
*/
List<BugDTO> list(@Param("request") BugPageRequest request);
/**
* 获取缺陷业务ID
*
* @param projectId 项目ID
* @return 最大的业务ID
*/
Long getMaxNum(String projectId);
}

View File

@ -0,0 +1,143 @@
<?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.ExtBugMapper">
<select id="list" resultType="io.metersphere.bug.dto.BugDTO">
select b.id, b.num, b.title, b.handle_user, b.create_user, b.create_time, b.update_time, b.delete_time, b.delete_user,
b.project_id, b.template_id, b.platform, b.status, bc.description from bug b left join bug_content bc on b.id = bc.bug_id
<include refid="queryWhereCondition"/>
</select>
<select id="getMaxNum" resultType="java.lang.Long">
select max(num) from bug where project_id = #{projectId}
</select>
<sql id="queryWhereCondition">
<where>
<if test="request.useTrash">
b.trash = 1
</if>
<if test="!request.useTrash">
b.trash = 0
</if>
<if test="request.projectId">
and b.project_id = #{request.projectId}
</if>
<if test="request.keyword != null">
and (
b.title like concat('%', #{request.keyword},'%')
or b.num like concat('%', #{request.keyword},'%')
)
</if>
<include refid="filter"/>
<include refid="combine">
<property name="condition" value="request.combine"/>
</include>
</where>
</sql>
<sql id="filter">
<if test="request.filter != null and request.filter.size() > 0">
<foreach collection="request.filter.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key == 'handleUser'">
and b.handle_user in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
<when test="key == 'createUser'">
and b.create_user in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
<when test="key == 'platform'">
and b.platform in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
<when test="key == 'status'">
and b.status in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
<when test="key.startsWith('custom_single')">
and b.id in (
select bug_id from bug_custom_field where concat('custom_single_', field_id) = #{key}
and trim(both '"' from `value`) in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
)
</when>
<when test="key.startsWith('custom_multiple')">
and b.id in (
select bug_id from bug_custom_field where concat('custom_multiple_', field_id) = #{key}
and
<include refid="io.metersphere.system.mapper.BaseMapper.filterMultipleWrapper"/>
)
</when>
</choose>
</if>
</foreach>
</if>
</sql>
<sql id="combine">
<if test="request.combine != null">
<if test='${condition}.handleUser != null'>
and b.handle_user
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.assignUser"/>
</include>
</if>
<if test='${condition}.createUser != null'>
and b.create_user
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.createUser"/>
</include>
</if>
<if test='${condition}.platform != null'>
and b.platform
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.platform"/>
</include>
</if>
<if test='${condition}.status != null'>
and b.status
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.platform"/>
</include>
</if>
<if test="${condition}.customs != null and ${condition}.customs.size() > 0">
<foreach collection="${condition}.customs" item="custom" separator="" open="" close="">
<if test='custom.operator == "not like" or custom.operator == "not in"'>
and b.id not in (
</if>
<if test='custom.operator != "not like" and custom.operator != "not in"'>
and b.id in (
</if>
select bug_id from bug_custom_field where field_id = #{custom.id}
<choose>
<when test="custom.type == 'multipleMember' or custom.type == 'checkbox' or custom.type == 'multipleSelect'">
and ${custom.value}
</when>
<when test="custom.type == 'date' or custom.type == 'datetime'">
and left(replace(unix_timestamp(trim(both '"' from `value`)), '.', ''), 13)
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="custom"/>
</include>
</when>
<when test="custom.type == 'richText' or custom.type == 'textarea'">
and `value`
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="custom"/>
</include>
</when>
<otherwise>
and trim(both '"' from `value`)
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="custom"/>
</include>
</otherwise>
</choose>
)
</foreach>
</if>
</if>
</sql>
</mapper>

View File

@ -0,0 +1,19 @@
package io.metersphere.bug.mapper;
import io.metersphere.bug.dto.BugRelationCaseCountDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author song-cc-rock
*/
public interface ExtBugRelationCaseMapper {
/**
* 统计缺陷关联的用例数量
* @param bugIds 缺陷ID集合
* @return 缺陷关联DTO
*/
List<BugRelationCaseCountDTO> countRelationCases(@Param("ids") List<String> bugIds);
}

View File

@ -0,0 +1,12 @@
<?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.ExtBugRelationCaseMapper">
<select id="countRelationCases" resultType="io.metersphere.bug.dto.BugRelationCaseCountDTO">
select count(id) as relationCaseCount, bug_id as bugId from bug_relation_case brc
where brc.bug_id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
group by brc.bug_id
</select>
</mapper>

View File

@ -0,0 +1,29 @@
//package io.metersphere.bug.service;
//
//import io.metersphere.bug.mapper.BugAttachmentMapper;
//import io.metersphere.bug.mapper.BugMapper;
//import jakarta.annotation.Resource;
//import org.springframework.stereotype.Service;
//
//@Service
//public class BugHistoryService {
//
// @Resource
// private BugMapper bugMapper;
// @Resource
// private BugAttachmentMapper bugAttachmentMapper;
//
// public void save(String bugId) {
// /*
// * 变更历史内容: BUG信息(基础字段, 自定义字段, 缺陷内容大字段, 附件)
// */
// }
//
// public void compare() {
// // 对比变更历史
// }
//
// public void rollback() {
// // 回退变更历史
// }
//}

View File

@ -0,0 +1,519 @@
package io.metersphere.bug.service;
import io.metersphere.bug.domain.*;
import io.metersphere.bug.dto.BugCustomFieldDTO;
import io.metersphere.bug.dto.BugDTO;
import io.metersphere.bug.dto.BugRelationCaseCountDTO;
import io.metersphere.bug.dto.request.BugEditRequest;
import io.metersphere.bug.dto.request.BugPageRequest;
import io.metersphere.bug.enums.BugPlatform;
import io.metersphere.bug.mapper.*;
import io.metersphere.bug.utils.CustomFieldUtils;
import io.metersphere.project.domain.FileMetadata;
import io.metersphere.project.domain.FileMetadataExample;
import io.metersphere.project.dto.ProjectTemplateOptionDTO;
import io.metersphere.project.mapper.FileMetadataMapper;
import io.metersphere.project.service.FileService;
import io.metersphere.project.service.ProjectTemplateService;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.StorageType;
import io.metersphere.sdk.constants.TemplateScene;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.MsFileUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.domain.Template;
import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.file.FileRequest;
import io.metersphere.system.mapper.BaseUserMapper;
import io.metersphere.system.mapper.TemplateMapper;
import io.metersphere.system.service.BaseTemplateService;
import io.metersphere.system.service.PlatformPluginService;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.*;
import java.util.stream.Collectors;
import static io.metersphere.bug.enums.result.BugResultCode.BUG_NOT_EXIST;
/**
* @author song-cc-rock
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class BugService {
@Resource
private BugMapper bugMapper;
@Resource
private ExtBugMapper extBugMapper;
@Resource
private BugContentMapper bugContentMapper;
@Resource
private BaseUserMapper baseUserMapper;
@Resource
protected TemplateMapper templateMapper;
@Resource
private SqlSessionFactory sqlSessionFactory;
@Resource
private PlatformPluginService platformPluginService;
@Resource
private ProjectTemplateService projectTemplateService;
@Resource
private BugCustomFieldMapper bugCustomFieldMapper;
@Resource
private ExtBugCustomFieldMapper extBugCustomFieldMapper;
@Resource
private ExtBugRelationCaseMapper extBugRelationCaseMapper;
@Resource
private FileMetadataMapper fileMetadataMapper;
@Resource
private BugAttachmentMapper bugAttachmentMapper;
@Resource
private ExtBugAttachmentMapper extBugAttachmentMapper;
@Resource
private FileService fileService;
@Resource
private BaseTemplateService baseTemplateService;
/**
* 缺陷列表查询
*
* @param request 列表请求参数
* @return 缺陷列表
*/
public List<BugDTO> list(BugPageRequest request) {
CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(request);
List<BugDTO> bugs = extBugMapper.list(request);
if (CollectionUtils.isEmpty(bugs)) {
return new ArrayList<>();
}
// 处理自定义字段及状态字段
List<BugDTO> bugList = handleCustomFieldsAndStatus(bugs, request.getProjectId());
return buildExtraInfo(bugList);
}
/**
* 创建缺陷
*
* @param request 缺陷请求参数
* @param files 附件集合
* @param currentUser 当前用户
*/
public void add(BugEditRequest request, List<MultipartFile> files, String currentUser) {
/*
* 缺陷创建逻辑
* 1. 判断所属项目是否关联第三方平台;
* 2. 根据模板自定义字段保存缺陷, 附件一起保存;
* 3. 第三方平台需调用插件同步缺陷至其他平台(API字段, 附件需处理);
* 4. 变更历史, 操作记录;
*/
// TODO: 后续补充第三方平台同步逻辑(缺陷, 附件)
// String platformName = projectApplicationService.getPlatformName(request.getProjectId());
// request.setPlatform(platformName);
// if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) {
// // 关联第三方平台
// ServiceIntegration serviceIntegration = projectTemplateService.getServiceIntegration(request.getProjectId());
// if (serviceIntegration == null) {
// // 项目未配置第三方平台
// throw new MSException(Translator.get("third_party_not_config"));
// }
// // 获取配置平台, 插入平台缺陷
// Platform platform = platformPluginService.getPlatform(serviceIntegration.getPluginId(), serviceIntegration.getOrganizationId(),
// new String(serviceIntegration.getConfiguration()));
// PlatformBugUpdateRequest platformRequest = buildPlatformBugRequest(request);
// String platformBugId = platform.addBug(platformRequest);
// request.setPlatformBugId(platformBugId);
// }
// 缺陷基础字段
handleAndSaveBug(request, currentUser, BugPlatform.LOCAL.getName());
// 自定义字段
handleAndSaveCustomFields(request, false);
// 附件
handleAndSaveAttachments(request, files, currentUser);
}
/**
* 更新缺陷
*
* @param request 缺陷请求参数
* @param files 附件集合
* @param currentUser 当前用户
*/
public void update(BugEditRequest request, List<MultipartFile> files, String currentUser) {
/*
* 缺陷更新逻辑
* 1. 保存逻辑与创建(1, 2, 3, 4)一致.
* 2. 需校验状态流
*/
// TODO: 后续补充第三方平台同步逻辑(缺陷, 附件)
// 缺陷
handleAndSaveBug(request, currentUser, BugPlatform.LOCAL.getName());
// 自定义字段
handleAndSaveCustomFields(request, true);
// 附件
handleAndSaveAttachments(request, files, currentUser);
}
/**
* 删除缺陷
*
* @param id 缺陷ID
*/
public void delete(String id) {
Bug bug = bugMapper.selectByPrimaryKey(id);
if (bug == null) {
throw new MSException(BUG_NOT_EXIST);
}
if (StringUtils.equals(bug.getPlatform(), BugPlatform.LOCAL.getName())) {
Bug record = new Bug();
record.setId(id);
record.setTrash(true);
bugMapper.updateByPrimaryKeySelective(record);
} else {
bugMapper.deleteByPrimaryKey(id);
}
}
/**
* 获取缺陷模板详情
*
* @param templateId 模板ID
* @param projectId 项目ID
* @return 模板详情
*/
public TemplateDTO getTemplate(String templateId, String projectId) {
Template template = templateMapper.selectByPrimaryKey(templateId);
if (template != null) {
// 属于系统模板
return baseTemplateService.getTemplateDTO(template);
} else {
// 不属于系统模板
List<ProjectTemplateOptionDTO> option = projectTemplateService.getOption(projectId, TemplateScene.BUG.name());
Optional<ProjectTemplateOptionDTO> isThirdPartyDefaultTemplate = option.stream().filter(projectTemplateOptionDTO -> StringUtils.equals(projectTemplateOptionDTO.getId(), templateId)).findFirst();
if (isThirdPartyDefaultTemplate.isPresent()) {
// TODO: 获取第三方平台模板
return null;
} else {
// 不属于系统模板&&不属于第三方平台默认模板, 则该模板已被删除
return projectTemplateService.getDefaultTemplateDTO(projectId, TemplateScene.BUG.name());
}
}
}
/**
* 处理保存缺陷基础信息
*
* @param request 请求参数
* @param currentUser 当前用户ID
* @param platformName 第三方平台名称
*/
private void handleAndSaveBug(BugEditRequest request, String currentUser, String platformName) {
Bug bug = new Bug();
BeanUtils.copyBean(bug, request);
bug.setPlatform(platformName);
// TODO: 关于平台, 后续补充, 暂保留
// if (StringUtils.equalsIgnoreCase(BugPlatform.LOCAL.getName(), platformName)) {
// bug.setPlatformBugId(null);
// } else {
// bug.setPlatformBugId(platformBugId);
// }
if (StringUtils.isEmpty(bug.getId())) {
bug.setId(IDGenerator.nextStr());
// TODO: 业务ID生成规则, 暂保留, 后续补充
bug.setNum(Long.valueOf(NumGenerator.nextNum(request.getProjectId(), ApplicationNumScope.BUG_MANAGEMENT)).intValue());
bug.setHandleUsers(request.getHandleUser());
bug.setCreateUser(currentUser);
bug.setCreateTime(System.currentTimeMillis());
bug.setUpdateUser(currentUser);
bug.setUpdateTime(System.currentTimeMillis());
bug.setDeleteUser(currentUser);
bug.setDeleteTime(System.currentTimeMillis());
bug.setTrash(false);
bugMapper.insert(bug);
request.setId(bug.getId());
BugContent bugContent = new BugContent();
bugContent.setBugId(bug.getId());
bugContent.setDescription(request.getDescription());
bugContentMapper.insert(bugContent);
} else {
Bug orignalBug = checkBugExist(request.getId());
// 追加处理人
bug.setHandleUsers(orignalBug.getHandleUsers() + "," + request.getHandleUser());
bug.setUpdateUser(currentUser);
bug.setUpdateTime(System.currentTimeMillis());
bugMapper.updateByPrimaryKeySelective(bug);
BugContent bugContent = new BugContent();
bugContent.setBugId(bug.getId());
bugContent.setDescription(request.getDescription());
bugContentMapper.updateByPrimaryKeySelective(bugContent);
}
}
/**
* 校验缺陷是否存在
* @param id 缺陷ID
* @return 缺陷
*/
private Bug checkBugExist(String id) {
BugExample bugExample = new BugExample();
bugExample.createCriteria().andIdEqualTo(id).andTrashEqualTo(false);
List<Bug> bugs = bugMapper.selectByExample(bugExample);
if (CollectionUtils.isEmpty(bugs)) {
throw new MSException(BUG_NOT_EXIST);
}
return bugs.get(0);
}
/**
* 处理保存自定义字段信息
*
* @param request 请求参数
*/
private void handleAndSaveCustomFields(BugEditRequest request, boolean merge) {
Map<String, String> customFieldMap = request.getCustomFieldMap();
if (MapUtils.isEmpty(customFieldMap)) {
return;
}
List<BugCustomField> addFields = new ArrayList<>();
List<BugCustomField> updateFields = new ArrayList<>();
if (merge) {
// 编辑缺陷需合并原有自定义字段
List<BugCustomFieldDTO> originalFields = extBugCustomFieldMapper.getBugCustomFields(List.of(request.getId()), request.getProjectId());
Map<String, String> originalFieldMap = originalFields.stream().collect(Collectors.toMap(BugCustomFieldDTO::getId, BugCustomFieldDTO::getValue));
customFieldMap.keySet().forEach(fieldId -> {
BugCustomField bugCustomField = new BugCustomField();
if (!originalFieldMap.containsKey(fieldId)) {
// 新的缺陷字段关系
bugCustomField.setBugId(request.getId());
bugCustomField.setFieldId(fieldId);
bugCustomField.setValue(customFieldMap.get(fieldId));
addFields.add(bugCustomField);
} else {
// 已存在的缺陷字段关系
bugCustomField.setBugId(request.getId());
bugCustomField.setFieldId(fieldId);
bugCustomField.setValue(customFieldMap.get(fieldId));
updateFields.add(bugCustomField);
}
});
} else {
// 新增缺陷不需要合并自定义字段
customFieldMap.keySet().forEach(fieldId -> {
BugCustomField bugCustomField = new BugCustomField();
bugCustomField.setBugId(request.getId());
bugCustomField.setFieldId(fieldId);
bugCustomField.setValue(customFieldMap.get(fieldId));
addFields.add(bugCustomField);
});
}
if (CollectionUtils.isNotEmpty(addFields)) {
bugCustomFieldMapper.batchInsert(addFields);
}
if (CollectionUtils.isNotEmpty(updateFields)) {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
BugCustomFieldMapper bugCustomFieldMapper = sqlSession.getMapper(BugCustomFieldMapper.class);
for (BugCustomField bugCustomField : updateFields) {
BugCustomFieldExample bugCustomFieldExample = new BugCustomFieldExample();
bugCustomFieldExample.createCriteria().andBugIdEqualTo(bugCustomField.getBugId()).andFieldIdEqualTo(bugCustomField.getFieldId());
bugCustomFieldMapper.updateByExample(bugCustomField, bugCustomFieldExample);
}
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
}
/**
* 处理保存附件信息
* @param request 请求参数
* @param files 上传附件集合
*/
private void handleAndSaveAttachments(BugEditRequest request, List<MultipartFile> files, String currentUser) {
Map<String, MultipartFile> uploadMinioFiles = new HashMap<>(16);
List<BugAttachment> addFiles = new ArrayList<>();
// 处理删除的本地上传附件及取消关联的附件
List<String> deleteIds = new ArrayList<>();
if (CollectionUtils.isNotEmpty(request.getDeleteLocalFileIds())) {
deleteIds.addAll(request.getDeleteLocalFileIds());
request.getDeleteLocalFileIds().forEach(deleteFileId -> {
FileRequest fileRequest = new FileRequest();
fileRequest.setProjectId(request.getProjectId());
fileRequest.setFileName(deleteFileId);
fileRequest.setStorage(StorageType.MINIO.name());
try {
fileService.deleteFile(fileRequest);
} catch (Exception e) {
throw new MSException(Translator.get("bug_attachment_delete_error"));
}
});
}
if (CollectionUtils.isNotEmpty(request.getUnLinkFileIds())) {
deleteIds.addAll(request.getUnLinkFileIds());
}
if (CollectionUtils.isNotEmpty(deleteIds)) {
BugAttachmentExample example = new BugAttachmentExample();
example.createCriteria().andBugIdEqualTo(request.getId()).andFileIdIn(deleteIds);
bugAttachmentMapper.deleteByExample(example);
// TODO: 如果是第三方平台, 需调用平台插件同步删除附件
}
// 新本地上传的附件
if (CollectionUtils.isNotEmpty(files)) {
files.forEach(file -> {
BugAttachment bugAttachment = new BugAttachment();
bugAttachment.setId(IDGenerator.nextStr());
bugAttachment.setBugId(request.getId());
bugAttachment.setFileId(IDGenerator.nextStr());
bugAttachment.setFileName(file.getOriginalFilename());
bugAttachment.setSize(file.getSize());
bugAttachment.setLocal(true);
bugAttachment.setCreateTime(System.currentTimeMillis());
bugAttachment.setCreateUser(currentUser);
addFiles.add(bugAttachment);
uploadMinioFiles.put(bugAttachment.getFileId(), file);
});
}
// 新关联的附件
List<String> linkIds = request.getLinkFileIds();
if (CollectionUtils.isNotEmpty(linkIds)) {
FileMetadataExample example = new FileMetadataExample();
example.createCriteria().andIdIn(linkIds);
List<FileMetadata> linkFiles = fileMetadataMapper.selectByExample(example);
Map<String, FileMetadata> linkFileMap = linkFiles.stream().collect(Collectors.toMap(FileMetadata::getId, v -> v));
linkIds.forEach(fileId -> {
FileMetadata fileMetadata = linkFileMap.get(fileId);
if (fileMetadata == null) {
return;
}
BugAttachment bugAttachment = new BugAttachment();
bugAttachment.setId(IDGenerator.nextStr());
bugAttachment.setBugId(request.getId());
bugAttachment.setFileId(fileId);
bugAttachment.setFileName(fileMetadata.getName());
bugAttachment.setSize(fileMetadata.getSize());
bugAttachment.setLocal(false);
bugAttachment.setCreateTime(System.currentTimeMillis());
bugAttachment.setCreateUser(currentUser);
addFiles.add(bugAttachment);
});
}
extBugAttachmentMapper.batchInsert(addFiles);
// TODO: 如果是第三方平台, 需调用平台插件同步上传附件
uploadMinioFiles.forEach((fileId, file) -> {
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(file.getOriginalFilename());
fileRequest.setResourceId("/" + MsFileUtils.BUG_MANAGEMENT_DIR + "/" + request.getProjectId() + "/" + fileId);
fileRequest.setStorage(StorageType.MINIO.name());
try {
fileService.upload(file, fileRequest);
} catch (Exception e) {
throw new MSException(Translator.get("bug_attachment_upload_error"));
}
});
}
// /**
// * 封装缺陷平台请求参数
// * @param request 缺陷请求参数
// */
// private PlatformBugUpdateRequest buildPlatformBugRequest(BugEditRequest request) {
// /*
// * TODO: 封装缺陷平台请求参数
// * 如果是系统默认模板, 只需获取模板中有配置API的字段并处理
// * 如果是平台默认模板, 则处理参数中全部自定义字段
// */
// return new PlatformBugUpdateRequest();
// }
/**
* 封装缺陷其他字段
*
* @param bugs 缺陷集合
* @return 缺陷DTO集合
*/
private List<BugDTO> buildExtraInfo(List<BugDTO> bugs) {
// 获取用户集合
List<String> userIds = new ArrayList<>();
userIds.addAll(bugs.stream().map(BugDTO::getCreateUser).toList());
userIds.addAll(bugs.stream().map(BugDTO::getHandleUser).toList());
List<String> distinctUserIds = userIds.stream().distinct().toList();
List<OptionDTO> userOptions = baseUserMapper.selectUserOptionByIds(distinctUserIds);
Map<String, String> userMap = userOptions.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName));
// 根据缺陷ID获取关联用例数
List<String> ids = bugs.stream().map(BugDTO::getId).toList();
List<BugRelationCaseCountDTO> relationCaseCount = extBugRelationCaseMapper.countRelationCases(ids);
Map<String, Integer> countMap = relationCaseCount.stream().collect(Collectors.toMap(BugRelationCaseCountDTO::getBugId, BugRelationCaseCountDTO::getRelationCaseCount));
bugs.forEach(bug -> {
bug.setRelationCaseCount(countMap.get(bug.getId()));
bug.setCreateUserName(userMap.get(bug.getCreateUser()));
bug.setAssignUserName(userMap.get(bug.getHandleUser()));
});
return bugs;
}
/**
* 处理自定义字段及状态字段
*
* @param bugs 缺陷集合
* @return 缺陷DTO集合
*/
private List<BugDTO> handleCustomFieldsAndStatus(List<BugDTO> bugs, String projectId) {
List<String> ids = bugs.stream().map(BugDTO::getId).toList();
List<BugCustomFieldDTO> customFields = extBugCustomFieldMapper.getBugCustomFields(ids, projectId);
Map<String, List<BugCustomFieldDTO>> customFieldMap = customFields.stream().collect(Collectors.groupingBy(BugCustomFieldDTO::getBugId));
bugs.forEach(bug -> {
bug.setCustomFields(customFieldMap.get(bug.getId()));
// 缺陷状态选项值不存在时不展示 暂时保留, 项目插件同步配置开发后补充
// List<BugStatusOptionDTO> statusOption = bugStatusService.getProjectStatusOption(bug.getProjectId());
// if (CollectionUtils.isNotEmpty(statusOption)) {
// Optional<BugStatusOptionDTO> statusOpt = statusOption.stream().filter(optionDTO -> optionDTO.getId().equals(bug.getStatus())).findFirst();
// statusOpt.ifPresent(optionDTO -> bug.setStatusName(optionDTO.getName()));
// }
});
return bugs;
}
// /**
// * 获取第三方平台模板
// *
// * @param projectId 项目ID
// * @return 第三方平台模板
// */
// private TemplateDTO getPluginBugTemplate(String projectId) {
// ServiceIntegration serviceIntegration = projectTemplateService.getServiceIntegration(projectId);
// if (serviceIntegration == null) {
// return null;
// }
// TemplateDTO template = new TemplateDTO();
// Template pluginTemplate = projectTemplateService.getPluginBugTemplate(projectId);
// BeanUtils.copyBean(template, pluginTemplate);
// Platform platform = platformPluginService.getPlatform(serviceIntegration.getPluginId(), serviceIntegration.getOrganizationId(),
// new String(serviceIntegration.getConfiguration()));
// // TODO: 插件平台获取自定义字段, 并针对特殊字段单独处理
// List<PlatformCustomFieldItemDTO> platformCustomFields = platform.getThirdPartCustomField("");
// if (CollectionUtils.isNotEmpty(platformCustomFields)) {
// List<TemplateCustomFieldDTO> customFields = platformCustomFields.stream().map(platformCustomField -> {
// TemplateCustomFieldDTO customField = new TemplateCustomFieldDTO();
// customField.setFieldId(platformCustomField.getId());
// customField.setFieldName(platformCustomField.getName());
// customField.setDefaultValue(platformCustomField.getDefaultValue());
// customField.setRequired(platformCustomField.getRequired());
// return customField;
// }).toList();
// template.setCustomFields(customFields);
// }
// return template;
// }
}

View File

@ -0,0 +1,66 @@
//package io.metersphere.bug.service;
//
//import io.metersphere.bug.dto.BugStatusOptionDTO;
//import io.metersphere.bug.enums.BugPlatform;
//import io.metersphere.plugin.platform.spi.Platform;
//import io.metersphere.project.service.ProjectApplicationService;
//import io.metersphere.project.service.ProjectTemplateService;
//import io.metersphere.sdk.exception.MSException;
//import io.metersphere.sdk.util.Translator;
//import io.metersphere.system.domain.ServiceIntegration;
//import io.metersphere.system.service.PlatformPluginService;
//import jakarta.annotation.Resource;
//import org.apache.commons.lang3.StringUtils;
//import org.springframework.stereotype.Service;
//import org.springframework.transaction.annotation.Transactional;
//
//import java.util.List;
//
//@Service
//@Transactional(rollbackFor = Exception.class)
//public class BugStatusService {
//
// @Resource
// private ProjectApplicationService projectApplicationService;
// @Resource
// private ProjectTemplateService projectTemplateService;
// @Resource
// private PlatformPluginService platformPluginService;
//
// /**
// * 获取状态下拉选项
// * @param projectId 项目ID
// * @return 选项集合
// */
// public List<BugStatusOptionDTO> getProjectStatusOption(String projectId) {
// // TODO: 获取状态下拉选项 {Local平台: 直接取状态流中的选项, 第三方平台: 取第三方插件平台的状态和状态流中的选项}
// String platformName = projectApplicationService.getPlatformName(projectId);
// if (StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) {
// return getProjectStatusItemOption(projectId);
// } else {
// // 状态流 && 第三方平台状态流
// ServiceIntegration serviceIntegration = projectTemplateService.getServiceIntegration(projectId);
// if (serviceIntegration == null) {
// // 项目未配置第三方平台
// throw new MSException(Translator.get("third_party_not_config"));
// }
// // 获取配置平台, 获取第三方平台状态流
// Platform platform = platformPluginService.getPlatform(serviceIntegration.getPluginId(), serviceIntegration.getOrganizationId(),
// new String(serviceIntegration.getConfiguration()));
//// List<PlatformStatusDTO> statusList = platform.getStatusList();
// // 获取项目状态流
//// List<BugStatusOptionDTO> projectStatusItemOption = getProjectStatusItemOption(projectId);
// }
// return null;
// }
//
// /**
// * 获取项目状态流选项
// * @param projectId 项目ID
// * @return 选项集合
// */
// public List<BugStatusOptionDTO> getProjectStatusItemOption(String projectId) {
// // TODO: 获取项目状态流选项
// return null;
// }
//}

View File

@ -0,0 +1,19 @@
//package io.metersphere.bug.service;
//
//import io.metersphere.sdk.util.LogUtils;
//import io.metersphere.system.service.CleanupProjectResourceService;
//import org.springframework.stereotype.Component;
//
//@Component
//public class CleanupBugResourceService implements CleanupProjectResourceService {
//
// @Override
// public void deleteResources(String projectId) {
// LogUtils.info("删除当前项目[" + projectId + "]相关缺陷资源");
// }
//
// @Override
// public void cleanReportResources(String projectId) {
// LogUtils.info("清理当前项目[" + projectId + "]相关缺陷报告资源");
// }
//}

View File

@ -0,0 +1,63 @@
package io.metersphere.bug.utils;
import io.metersphere.sdk.constants.CustomFieldType;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.dto.sdk.BasePageRequest;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 字段处理工具类
* @author song-cc-rock
*/
public class CustomFieldUtils {
public static final String COMBINE_CUSTOM = "customs";
public static final String COMBINE_CUSTOM_FIELD_TYPE = "type";
public static final String COMBINE_CUSTOM_FIELD_VALUE = "value";
// public static final String COMBINE_CUSTOM_FIELD_OPERATOR = "operator";
// public static final String IS_CURRENT_USER = "current user";
public static final String CUSTOM_MULTIPLE_PREFIX = "custom_multiple";
/**
* 设置列表查询的多选字段参数
* @param request 请求参数
*/
public static void setBaseQueryRequestCustomMultipleFields(BasePageRequest request) {
// handle filter custom multiple fields
if (MapUtils.isNotEmpty(request.getFilter())) {
request.getFilter().entrySet().forEach(entry -> {
if (entry.getKey().startsWith(CUSTOM_MULTIPLE_PREFIX) && CollectionUtils.isNotEmpty(entry.getValue())) {
List<String> jsonValues = entry.getValue().stream().map(item -> "[\"".concat(item).concat("\"]")).collect(Collectors.toList());
entry.setValue(jsonValues);
}
});
}
// handle combine custom multiple fields
if (MapUtils.isNotEmpty(request.getCombine()) && ObjectUtils.isNotEmpty((request.getCombine().get(COMBINE_CUSTOM)))) {
//noinspection unchecked
List<Map<String, Object>> customs = (List<Map<String, Object>>) request.getCombine().get(COMBINE_CUSTOM);
customs.forEach(custom -> {
// when member select or member multipart select support current user, open it
// if(StringUtils.equalsIgnoreCase(custom.get(COMBINE_CUSTOM_FIELD_OPERATOR).toString(), IS_CURRENT_USER)){
// String userId = SessionUtils.getUserId();
// custom.put(COMBINE_CUSTOM_FIELD_VALUE, userId);
// }
if (StringUtils.equalsAny(custom.get(COMBINE_CUSTOM_FIELD_TYPE).toString(), CustomFieldType.MULTIPLE_MEMBER.getType(),
CustomFieldType.CHECKBOX.getType(), CustomFieldType.MULTIPLE_SELECT.getType())
&& StringUtils.isNotEmpty(custom.get(COMBINE_CUSTOM_FIELD_VALUE).toString())) {
List<String> customValues = JSON.parseArray(custom.get(COMBINE_CUSTOM_FIELD_VALUE).toString(), String.class);
List<String> jsonValues = customValues.stream().map(item -> "JSON_CONTAINS(`value`, '[\"".concat(item).concat("\"]')")).toList();
custom.put(COMBINE_CUSTOM_FIELD_VALUE, "(".concat(StringUtils.join(jsonValues, " OR ")).concat(")"));
}
});
}
}
}

View File

@ -71,13 +71,13 @@
<!--要生成的数据库表 -->
<table tableName="bug" />
<table tableName="bug_content" />
<table tableName="bug_follower" />
<table tableName="bug_comment" />
<table tableName="bug_attachment" />
<table tableName="bug_custom_field" />
<table tableName="bug_relation_case" />
<table tableName="bug_history" />
<!-- <table tableName="bug_content" />-->
<!-- <table tableName="bug_follower" />-->
<!-- <table tableName="bug_comment" />-->
<!-- <table tableName="bug_attachment" />-->
<!-- <table tableName="bug_custom_field" />-->
<!-- <table tableName="bug_relation_case" />-->
<!-- <table tableName="bug_history" />-->

View File

@ -0,0 +1,27 @@
[
{
"id": "BUG",
"name": "permission.bug.name",
"type": "BUG",
"children": [
{
"id": "BUG",
"name": "permission.bug.name",
"permissions": [
{
"id": "BUG:READ"
},
{
"id": "BUG:READ+ADD"
},
{
"id": "BUG:READ+UPDATE"
},
{
"id": "BUG:READ+DELETE"
}
]
}
]
}
]

View File

@ -19,7 +19,7 @@ import org.springframework.context.annotation.ComponentScan;
MinioProperties.class
})
@ServletComponentScan
@ComponentScan(basePackages = {"io.metersphere.sdk", "io.metersphere.bug", "io.metersphere.system"})
@ComponentScan(basePackages = {"io.metersphere.sdk", "io.metersphere.system", "io.metersphere.project", "io.metersphere.bug"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);

View File

@ -0,0 +1,289 @@
package io.metersphere.bug.controller;
import io.metersphere.bug.dto.BugDTO;
import io.metersphere.bug.dto.request.BugEditRequest;
import io.metersphere.bug.dto.request.BugPageRequest;
import io.metersphere.project.dto.ProjectTemplateOptionDTO;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.utils.Pager;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.*;
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 org.springframework.test.web.servlet.MvcResult;
import org.springframework.util.MultiValueMap;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BugControllerTests extends BaseTest {
public static final String BUG_PAGE = "/bug/page";
public static final String BUG_ADD = "/bug/add";
public static final String BUG_UPDATE = "/bug/update";
public static final String BUG_DELETE = "/bug/delete";
public static final String BUG_TEMPLATE_OPTION = "/bug/template/option";
public static final String BUG_TEMPLATE_DETAIL = "/bug/template";
@Test
@Order(0)
@Sql(scripts = {"/dml/init_bug.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void testBugPageSuccess() throws Exception {
BugPageRequest bugRequest = new BugPageRequest();
bugRequest.setCurrent(1);
bugRequest.setPageSize(10);
bugRequest.setKeyword("default");
bugRequest.setProjectId("default-project-for-bug");
bugRequest.setFilter(buildRequestFilter());
bugRequest.setCombine(buildRequestCombine());
MvcResult mvcResult = this.requestPostWithOkAndReturn(BUG_PAGE, bugRequest);
// 获取返回值
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(resultHolder);
Pager<?> pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class);
// 返回值不为空
Assertions.assertNotNull(pageData);
// 返回值的页码和当前页码相同
Assertions.assertEquals(pageData.getCurrent(), bugRequest.getCurrent());
// 返回的数据量不超过规定要返回的数据量相同
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= bugRequest.getPageSize());
// 返回值中取出第一条数据, 并判断是否包含关键字default
BugDTO bugDTO = JSON.parseArray(JSON.toJSONString(pageData.getList()), BugDTO.class).get(0);
Assertions.assertTrue(StringUtils.contains(bugDTO.getTitle(), bugRequest.getKeyword())
|| StringUtils.contains(bugDTO.getId(), bugRequest.getKeyword()));
// sort不为空
Map<String, String> sort = new HashMap<>();
sort.put("id", "desc");
bugRequest.setSort(sort);
MvcResult sortResult = this.requestPostWithOkAndReturn(BUG_PAGE, bugRequest);
String sortData = sortResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder sortHolder = JSON.parseObject(sortData, ResultHolder.class);
Pager<?> sortPageData = JSON.parseObject(JSON.toJSONString(sortHolder.getData()), Pager.class);
// 返回值中取出第一条ID最大的数据, 并判断是否是default-bug
BugDTO maxBugDTO = JSON.parseArray(JSON.toJSONString(sortPageData.getList()), BugDTO.class).get(0);
Assertions.assertTrue(maxBugDTO.getId().contains("default"));
}
@Test
@Order(1)
public void testBugPageEmptySuccess() throws Exception {
BugPageRequest bugPageRequest = new BugPageRequest();
bugPageRequest.setCurrent(1);
bugPageRequest.setPageSize(10);
bugPageRequest.setKeyword("default-x");
MvcResult mvcResult = this.requestPostWithOkAndReturn(BUG_PAGE, bugPageRequest);
// 获取返回值
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(resultHolder);
Pager<?> pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class);
// 返回值不为空
Assertions.assertNotNull(pageData);
// 返回值的页码和当前页码相同
Assertions.assertEquals(pageData.getCurrent(), bugPageRequest.getCurrent());
// 返回的数据量为0条
Assertions.assertEquals(0, pageData.getTotal());
}
@Test
@Order(2)
public void testBugPageError() throws Exception {
// 页码有误
BugPageRequest bugPageRequest = new BugPageRequest();
bugPageRequest.setCurrent(0);
bugPageRequest.setPageSize(10);
this.requestPost(BUG_PAGE, bugPageRequest, status().isBadRequest());
// 页数有误
bugPageRequest = new BugPageRequest();
bugPageRequest.setCurrent(1);
bugPageRequest.setPageSize(1);
this.requestPost(BUG_PAGE, bugPageRequest, status().isBadRequest());
}
@Test
@Order(3)
public void testAddBugSuccess() throws Exception {
BugEditRequest request = buildRequest(false);
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.xlsx")).getPath();
File file = new File(filePath);
MultiValueMap<String, Object> paramMap = getDefaultMultiPartParam(request, file);
this.requestMultipartWithOkAndReturn(BUG_ADD, paramMap);
}
@Test
@Order(4)
public void testAddBugError() throws Exception {
BugEditRequest request = buildRequest(false);
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.xlsx")).getPath();
File file = new File(filePath);
// 项目ID为空
request.setProjectId(null);
MultiValueMap<String, Object> paramMap = getDefaultMultiPartParam(request, file);
this.requestMultipart(BUG_ADD, paramMap).andExpect(status().isBadRequest());
// 标题为空
request.setProjectId("default-project-for-bug");
request.setTitle(null);
paramMap = getDefaultMultiPartParam(request, file);
this.requestMultipart(BUG_ADD, paramMap).andExpect(status().isBadRequest());
// 处理人为空
request.setTitle("default-bug-title");
request.setHandleUser(null);
paramMap = getDefaultMultiPartParam(request, file);
this.requestMultipart(BUG_ADD, paramMap).andExpect(status().isBadRequest());
// 模板为空
request.setHandleUser("admin");
request.setTemplateId(null);
paramMap = getDefaultMultiPartParam(request, file);
this.requestMultipart(BUG_ADD, paramMap).andExpect(status().isBadRequest());
// 状态为空
request.setTemplateId("default-bug-template");
request.setStatus(null);
paramMap = getDefaultMultiPartParam(request, file);
this.requestMultipart(BUG_ADD, paramMap).andExpect(status().isBadRequest());
}
@Test
@Order(5)
public void testUpdateBugSuccess() throws Exception {
BugEditRequest request = buildRequest(true);
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.xlsx")).getPath();
File file = new File(filePath);
MultiValueMap<String, Object> paramMap = getDefaultMultiPartParam(request, file);
this.requestMultipartWithOkAndReturn(BUG_UPDATE, paramMap);
}
@Test
@Order(6)
public void testUpdateBugError() throws Exception {
BugEditRequest request = buildRequest(true);
request.setId("default-bug-id-not-exist");
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.xlsx")).getPath();
File file = new File(filePath);
MultiValueMap<String, Object> paramMap = getDefaultMultiPartParam(request, file);
this.requestMultipart(BUG_UPDATE, paramMap).andExpect(status().is5xxServerError());
}
@Test
@Order(7)
public void testUpdateBugWithEmptyField() throws Exception {
BugEditRequest request = buildRequest(true);
request.setCustomFieldMap(null);
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.xlsx")).getPath();
File file = new File(filePath);
MultiValueMap<String, Object> paramMap = getDefaultMultiPartParam(request, file);
this.requestMultipartWithOkAndReturn(BUG_UPDATE, paramMap);
}
@Test
@Order(8)
public void testDeleteBugSuccess() throws Exception {
this.requestGet(BUG_DELETE + "/default-bug-id", status().isOk());
// 非Local缺陷
this.requestGet(BUG_DELETE + "/default-bug-id-tapd", status().isOk());
}
@Test
@Order(9)
public void testDeleteBugError() throws Exception {
this.requestGet(BUG_DELETE + "/default-bug-id-not-exist", status().is5xxServerError());
}
@Test
@Order(10)
public void testGetBugTemplateOption() throws Exception {
MvcResult mvcResult = this.requestGetWithOkAndReturn(BUG_TEMPLATE_OPTION + "?projectId=default-project-for-bug");
String sortData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(sortData, ResultHolder.class);
List<ProjectTemplateOptionDTO> templateOptionDTOS = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), ProjectTemplateOptionDTO.class);
// 默认模板断言
Assertions.assertTrue(templateOptionDTOS.stream().anyMatch(ProjectTemplateOptionDTO::getEnableDefault));
}
@Test
@Order(11)
public void testGetBugTemplateDetailSuccess() throws Exception {
MvcResult mvcResult = this.requestGetWithOkAndReturn(BUG_TEMPLATE_DETAIL + "/default-bug-template-id" + "?projectId=default-project-for-bug");
String sortData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(sortData, ResultHolder.class);
TemplateDTO templateDTO = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TemplateDTO.class);
Assertions.assertNotNull(templateDTO);
Assertions.assertEquals("default-bug-template-id", templateDTO.getId());
// 获取默认模板
MvcResult defaultResult = this.requestGetWithOkAndReturn(BUG_TEMPLATE_DETAIL + "/default-bug-template-id-not-exist" + "?projectId=default-project-for-bug");
String defaultData = defaultResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder defaultResultHolder = JSON.parseObject(defaultData, ResultHolder.class);
TemplateDTO defaultTemplate = JSON.parseObject(JSON.toJSONString(defaultResultHolder.getData()), TemplateDTO.class);
Assertions.assertNotNull(defaultTemplate);
Assertions.assertEquals("default-bug-template-id", defaultTemplate.getId());
}
/**
* 生成请求过滤参数
* @return filter param
*/
private Map<String, List<String>> buildRequestFilter() {
Map<String, List<String>> filter = new HashMap<>();
filter.put("custom_multiple_test_field", List.of("default", "default1"));
return filter;
}
/**
* 生成高级搜索参数
* @return combine param
*/
private Map<String, Object> buildRequestCombine() {
Map<String, Object> combine = new HashMap<>();
List<Map<String, Object>> customs = new ArrayList<>();
Map<String, Object> custom = new HashMap<>();
custom.put("id", "test_field");
custom.put("operator", "in");
custom.put("type", "multipleSelect");
custom.put("value", JSON.toJSONString(List.of("default", "default1")));
customs.add(custom);
combine.put("customs", customs);
return combine;
}
/**
* 生成请求参数
* @param isUpdate 是否更新操作
* @return 请求参数
*/
private BugEditRequest buildRequest(boolean isUpdate) {
BugEditRequest request = new BugEditRequest();
request.setProjectId("default-project-for-bug");
request.setTitle("default-bug-title");
request.setDescription("default-bug-description");
request.setHandleUser("admin");
request.setTemplateId("default-bug-template");
request.setStatus("prepare");
request.setTag(JSON.toJSONString(List.of("TAG", "DEFAULT-TAG")));
request.setLinkFileIds(List.of("default-bug-file-id-1", "default-bug-file-id-3"));
Map<String, String> customFieldMap = new HashMap<>();
customFieldMap.put("custom-field", "oasis");
if (isUpdate) {
request.setId("default-bug-id");
request.setUnLinkFileIds(List.of("default-bug-file-id-1"));
request.setDeleteLocalFileIds(List.of("default-bug-file-id"));
request.setLinkFileIds(List.of("default-bug-file-id-2"));
customFieldMap.put("test_field", JSON.toJSONString(List.of("test")));
}
request.setCustomFieldMap(customFieldMap);
return request;
}
}

View File

@ -6,14 +6,14 @@ server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css,text/javascript,image/jpeg
server.compression.min-response-size=2048
#
quartz.enabled=false
quartz.enabled=true
quartz.scheduler-name=msScheduler
quartz.thread-count=10
quartz.properties.org.quartz.jobStore.acquireTriggersWithinLock=true
#
logging.file.path=/opt/metersphere/logs/metersphere
# Hikari
spring.datasource.url=jdbc:mysql://${embedded.mysql.host}:${embedded.mysql.port}/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.url=jdbc:mysql://${embedded.mysql.host}:${embedded.mysql.port}/test?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true&useSSL=false&sessionVariables=sql_mode=%27STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION%27
spring.datasource.username=${embedded.mysql.user}
spring.datasource.password=${embedded.mysql.password}
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
@ -80,4 +80,4 @@ minio.secret-key=${embedded.minio.secretKey}
logging.level.org.springframework.jdbc.core=info
logging.level.io.metersphere.sdk.mapper=info
logging.level.io.metersphere.system.mapper=info
logging.level.io.metersphere.bug.mapper=info

View File

@ -0,0 +1,31 @@
INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time) VALUE
('default-project-for-bug-tmp', null, '100001', '测试项目(缺陷)', '系统默认创建的项目(缺陷)', 'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
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);
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, tag, platform_bug_id, trash) 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-tapd', 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);
INSERT INTO bug_custom_field (bug_id, field_id, value) VALUE ('default-bug-id', 'test_field', '["default", "default-1"]');
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');
INSERT INTO template (id, name, remark, internal, update_time, create_time, create_user, scope_type, scope_id, enable_third_part, scene) VALUES
('bug-template-id', 'bug-template', '', 0, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'PROJECT', 'default-project-for-bug', 0, 'BUG'),
('default-bug-template-id', 'bug-default-template', '', 0, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'PROJECT', 'default-project-for-bug', 0, 'BUG');
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
(UUID_SHORT(), UUID_SHORT(), 'default-bug-id', 'functional', null, null, 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
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) VALUES
('default-bug-file-id-1', 'test-file', 'xlsx', 100, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'MINIO', 'admin', 'admin',
'["default-tag"]', 'test-file', null, 'test-file', 1, 'default-bug-id', 1),
('default-bug-file-id-2', 'test-file', 'xlsx', 100, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'default-project-for-bug', 'MINIO', 'admin', 'admin',
'["default-tag"]', 'test-file', null, 'test-file', 1, 'default-bug-id', 1);
INSERT INTO project_application (project_id, type, type_value) VALUES ('default-project-for-bug', 'BUG_DEFAULT_TEMPLATE', 'default-bug-template-id');

View File

@ -24,7 +24,7 @@ public class CommandService {
public static String createFile(MultipartFile bodyFile) {
MsFileUtils.validateFileName(bodyFile.getOriginalFilename());
String dir = MsFileUtils.DATE_ROOT_DIR + "/body/environment/tmp";
String dir = MsFileUtils.DATA_ROOT_DIR + "/body/environment/tmp";
File fileDir = new File(dir);
if (!fileDir.exists()) {
fileDir.mkdirs();

View File

@ -18,13 +18,13 @@ import io.metersphere.project.utils.ModuleSortUtils;
import io.metersphere.sdk.constants.OperationLogConstants;
import io.metersphere.sdk.constants.ProjectApplicationType;
import io.metersphere.sdk.constants.ScheduleType;
import io.metersphere.system.log.dto.LogDTO;
import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.domain.*;
import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.dto.LogDTO;
import io.metersphere.system.mapper.PluginMapper;
import io.metersphere.system.mapper.ServiceIntegrationMapper;
import io.metersphere.system.sechedule.ScheduleService;
@ -568,4 +568,14 @@ public class ProjectApplicationService {
List<ProjectApplication> projectApplications = projectApplicationMapper.selectByExample(example);
return CollectionUtils.isEmpty(projectApplications) ? null : projectApplications.get(0);
}
// /**
// * 获取项目所属平台
// * @param projectId 项目ID
// * @return 项目所属平台
// */
// public String getPlatformName(String projectId) {
// // TODO 需调用项目平台配置接口, 获取项目所属平台名称
// return null;
// }
}

View File

@ -223,7 +223,7 @@ public class ProjectTemplateService extends BaseTemplateService {
* @param projectId
* @return
*/
private Template getPluginBugTemplate(String projectId) {
public Template getPluginBugTemplate(String projectId) {
ServiceIntegration serviceIntegration = getServiceIntegration(projectId);
if (serviceIntegration == null) {
return null;
@ -240,6 +240,7 @@ public class ProjectTemplateService extends BaseTemplateService {
PluginWrapper pluginWrapper = pluginLoadService.getPluginWrapper(pluginId);
Template template = new Template();
template.setId(pluginWrapper.getPluginId());
template.setName(((MsPlugin) pluginWrapper.getPlugin()).getName() + Translator.get("default_template"));
template.setCreateUser(InternalUser.ADMIN.getValue());
template.setScene(TemplateScene.BUG.name());
template.setEnableThirdPart(true);
@ -257,7 +258,7 @@ public class ProjectTemplateService extends BaseTemplateService {
* @param projectId
* @return
*/
private ServiceIntegration getServiceIntegration(String projectId) {
public ServiceIntegration getServiceIntegration(String projectId) {
// 判断项目是否开启集成缺陷
ProjectApplication syncEnableConfig = projectApplicationService.getByType(projectId, ProjectApplicationType.BUG_SYNC_CONFIG.SYNC_ENABLE.name());
boolean isSyncEnable = syncEnableConfig != null && Boolean.parseBoolean(syncEnableConfig.getTypeValue());

View File

@ -94,6 +94,6 @@ public class LocalFileRepository implements FileRepository {
private String getFileDir(FileRequest request) {
MsFileUtils.validateFileName(request.getProjectId(), request.getFileName());
return StringUtils.join(MsFileUtils.DATE_ROOT_DIR, "/", request.getProjectId());
return StringUtils.join(MsFileUtils.DATA_ROOT_DIR, "/", request.getProjectId());
}
}

View File

@ -47,4 +47,10 @@
#{value}
</foreach>
</sql>
<sql id="filterMultipleWrapper">
<foreach collection="values" item="value" separator="or" open="(" close=")">
JSON_CONTAINS(value, #{value})
</foreach>
</sql>
</mapper>

View File

@ -2,21 +2,22 @@ package io.metersphere.system.service;
import io.metersphere.sdk.constants.TemplateScene;
import io.metersphere.sdk.constants.TemplateScopeType;
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.dto.sdk.request.TemplateCustomFieldRequest;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.resolver.field.AbstractCustomFieldResolver;
import io.metersphere.system.resolver.field.CustomFieldResolverFactory;
import io.metersphere.system.utils.ServiceUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.domain.CustomField;
import io.metersphere.system.domain.Template;
import io.metersphere.system.domain.TemplateCustomField;
import io.metersphere.system.domain.TemplateExample;
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.dto.sdk.request.TemplateCustomFieldRequest;
import io.metersphere.system.mapper.TemplateMapper;
import io.metersphere.system.resolver.field.AbstractCustomFieldResolver;
import io.metersphere.system.resolver.field.CustomFieldResolverFactory;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.utils.ServiceUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@ -26,7 +27,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import io.metersphere.system.uid.IDGenerator;
import java.util.stream.Collectors;
import static io.metersphere.system.controller.handler.result.CommonResultCode.*;
@ -94,7 +94,7 @@ public class BaseTemplateService {
return templateMapper.selectByPrimaryKey(id);
}
protected TemplateDTO getTemplateDTO(Template template) {
public TemplateDTO getTemplateDTO(Template template) {
List<TemplateCustomField> templateCustomFields = baseTemplateCustomFieldService.getByTemplateId(template.getId());
// 查找字段名称