diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/domain/StatusItem.java b/backend/framework/domain/src/main/java/io/metersphere/system/domain/StatusItem.java index b7289f3fbb..bea2059c0f 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/domain/StatusItem.java +++ b/backend/framework/domain/src/main/java/io/metersphere/system/domain/StatusItem.java @@ -45,6 +45,10 @@ public class StatusItem implements Serializable { @Size(min = 1, max = 50, message = "{status_item.scope_id.length_range}", groups = {Created.class, Updated.class}) private String scopeId; + @Schema(description = "排序字段", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{status_item.pos.not_blank}", groups = {Created.class}) + private Integer pos; + private static final long serialVersionUID = 1L; public enum Column { @@ -55,7 +59,8 @@ public class StatusItem implements Serializable { internal("internal", "internal", "BIT", false), scopeType("scope_type", "scopeType", "VARCHAR", false), refId("ref_id", "refId", "VARCHAR", false), - scopeId("scope_id", "scopeId", "VARCHAR", false); + scopeId("scope_id", "scopeId", "VARCHAR", false), + pos("pos", "pos", "INTEGER", false); private static final String BEGINNING_DELIMITER = "`"; diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/domain/StatusItemExample.java b/backend/framework/domain/src/main/java/io/metersphere/system/domain/StatusItemExample.java index b1b39711fb..9150816a74 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/domain/StatusItemExample.java +++ b/backend/framework/domain/src/main/java/io/metersphere/system/domain/StatusItemExample.java @@ -653,6 +653,66 @@ public class StatusItemExample { addCriterion("scope_id not between", value1, value2, "scopeId"); return (Criteria) this; } + + public Criteria andPosIsNull() { + addCriterion("pos is null"); + return (Criteria) this; + } + + public Criteria andPosIsNotNull() { + addCriterion("pos is not null"); + return (Criteria) this; + } + + public Criteria andPosEqualTo(Integer value) { + addCriterion("pos =", value, "pos"); + return (Criteria) this; + } + + public Criteria andPosNotEqualTo(Integer value) { + addCriterion("pos <>", value, "pos"); + return (Criteria) this; + } + + public Criteria andPosGreaterThan(Integer value) { + addCriterion("pos >", value, "pos"); + return (Criteria) this; + } + + public Criteria andPosGreaterThanOrEqualTo(Integer value) { + addCriterion("pos >=", value, "pos"); + return (Criteria) this; + } + + public Criteria andPosLessThan(Integer value) { + addCriterion("pos <", value, "pos"); + return (Criteria) this; + } + + public Criteria andPosLessThanOrEqualTo(Integer value) { + addCriterion("pos <=", value, "pos"); + return (Criteria) this; + } + + public Criteria andPosIn(List values) { + addCriterion("pos in", values, "pos"); + return (Criteria) this; + } + + public Criteria andPosNotIn(List values) { + addCriterion("pos not in", values, "pos"); + return (Criteria) this; + } + + public Criteria andPosBetween(Integer value1, Integer value2) { + addCriterion("pos between", value1, value2, "pos"); + return (Criteria) this; + } + + public Criteria andPosNotBetween(Integer value1, Integer value2) { + addCriterion("pos not between", value1, value2, "pos"); + return (Criteria) this; + } } public static class Criteria extends GeneratedCriteria { diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/mapper/StatusItemMapper.xml b/backend/framework/domain/src/main/java/io/metersphere/system/mapper/StatusItemMapper.xml index f677c2aad8..3c61d8bf1b 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/mapper/StatusItemMapper.xml +++ b/backend/framework/domain/src/main/java/io/metersphere/system/mapper/StatusItemMapper.xml @@ -10,6 +10,7 @@ + @@ -70,7 +71,7 @@ - id, `name`, scene, remark, internal, scope_type, ref_id, scope_id + id, `name`, scene, remark, internal, scope_type, ref_id, scope_id, pos @@ -198,6 +207,9 @@ scope_id = #{record.scopeId,jdbcType=VARCHAR}, + + pos = #{record.pos,jdbcType=INTEGER}, + @@ -212,7 +224,8 @@ internal = #{record.internal,jdbcType=BIT}, scope_type = #{record.scopeType,jdbcType=VARCHAR}, ref_id = #{record.refId,jdbcType=VARCHAR}, - scope_id = #{record.scopeId,jdbcType=VARCHAR} + scope_id = #{record.scopeId,jdbcType=VARCHAR}, + pos = #{record.pos,jdbcType=INTEGER} @@ -241,6 +254,9 @@ scope_id = #{scopeId,jdbcType=VARCHAR}, + + pos = #{pos,jdbcType=INTEGER}, + where id = #{id,jdbcType=VARCHAR} @@ -252,17 +268,19 @@ internal = #{internal,jdbcType=BIT}, scope_type = #{scopeType,jdbcType=VARCHAR}, ref_id = #{refId,jdbcType=VARCHAR}, - scope_id = #{scopeId,jdbcType=VARCHAR} + scope_id = #{scopeId,jdbcType=VARCHAR}, + pos = #{pos,jdbcType=INTEGER} where id = #{id,jdbcType=VARCHAR} insert into status_item - (id, `name`, scene, remark, internal, scope_type, ref_id, scope_id) + (id, `name`, scene, remark, internal, scope_type, ref_id, scope_id, pos) values (#{item.id,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR}, #{item.scene,jdbcType=VARCHAR}, #{item.remark,jdbcType=VARCHAR}, #{item.internal,jdbcType=BIT}, #{item.scopeType,jdbcType=VARCHAR}, - #{item.refId,jdbcType=VARCHAR}, #{item.scopeId,jdbcType=VARCHAR}) + #{item.refId,jdbcType=VARCHAR}, #{item.scopeId,jdbcType=VARCHAR}, #{item.pos,jdbcType=INTEGER} + ) @@ -299,6 +317,9 @@ #{item.scopeId,jdbcType=VARCHAR} + + #{item.pos,jdbcType=INTEGER} + ) diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_11__system_setting.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_11__system_setting.sql index 3d3e819acb..5243d0027b 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_11__system_setting.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_11__system_setting.sql @@ -417,10 +417,11 @@ CREATE TABLE IF NOT EXISTS status_item( `scope_type` VARCHAR(50) NOT NULL DEFAULT 0 COMMENT '组织或项目级别字段(PROJECT, ORGANIZATION)' , `ref_id` VARCHAR(50) COMMENT '项目状态所关联的组织状态ID' , `scope_id` VARCHAR(50) NOT NULL COMMENT '组织或项目ID' , + `pos` INT NOT NULL DEFAULT 0 COMMENT '排序字段' , PRIMARY KEY (id) ) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4 - COLLATE = utf8mb4_general_ci COMMENT = '状态流的状态项'; + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT = '状态流的状态项'; CREATE INDEX idx_scope_id ON status_item(scope_id); diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql index e90d79295d..61668a5a79 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql @@ -217,24 +217,24 @@ VALUES (UUID_SHORT(), 'test_plan_default', '', 1, UNIX_TIMESTAMP() * 1000, UNIX_ -- 初始化组织缺陷状态项 -- 新建 INSERT INTO status_item -(id, name, scene, remark, internal, scope_type, ref_id, scope_id) -VALUES(UUID_SHORT(), 'bug_new', 'BUG', NULL, 1, 'ORGANIZATION', NULL, '100001'); +(id, name, scene, remark, internal, scope_type, ref_id, scope_id, pos) +VALUES(UUID_SHORT(), 'bug_new', 'BUG', NULL, 1, 'ORGANIZATION', NULL, '100001', 0); -- 处理中 INSERT INTO status_item -(id, name, scene, remark, internal, scope_type, ref_id, scope_id) -VALUES(UUID_SHORT(), 'bug_in_process', 'BUG', NULL, 1, 'ORGANIZATION', NULL, '100001'); +(id, name, scene, remark, internal, scope_type, ref_id, scope_id, pos) +VALUES(UUID_SHORT(), 'bug_in_process', 'BUG', NULL, 1, 'ORGANIZATION', NULL, '100001', 1); -- 已关闭 INSERT INTO status_item -(id, name, scene, remark, internal, scope_type, ref_id, scope_id) -VALUES(UUID_SHORT(), 'bug_closed', 'BUG', NULL, 1, 'ORGANIZATION', NULL, '100001'); +(id, name, scene, remark, internal, scope_type, ref_id, scope_id, pos) +VALUES(UUID_SHORT(), 'bug_closed', 'BUG', NULL, 1, 'ORGANIZATION', NULL, '100001', 2); -- 已解决 INSERT INTO status_item -(id, name, scene, remark, internal, scope_type, ref_id, scope_id) -VALUES(UUID_SHORT(), 'bug_resolved', 'BUG', NULL, 1, 'ORGANIZATION', NULL, '100001'); +(id, name, scene, remark, internal, scope_type, ref_id, scope_id, pos) +VALUES(UUID_SHORT(), 'bug_resolved', 'BUG', NULL, 1, 'ORGANIZATION', NULL, '100001', 3); -- 已拒绝 INSERT INTO status_item -(id, name, scene, remark, internal, scope_type, ref_id, scope_id) -VALUES(UUID_SHORT(), 'bug_rejected', 'BUG', NULL, 1, 'ORGANIZATION', NULL, '100001'); +(id, name, scene, remark, internal, scope_type, ref_id, scope_id, pos) +VALUES(UUID_SHORT(), 'bug_rejected', 'BUG', NULL, 1, 'ORGANIZATION', NULL, '100001', 4); -- 初始化组织缺陷状态定义 INSERT INTO status_definition(status_id, definition_id) @@ -267,24 +267,24 @@ VALUES(UUID_SHORT(), (SELECT id FROM status_item where name = 'bug_rejected'), ( -- 初始化项目缺陷状态项 -- 新建 INSERT INTO status_item -(id, name, scene, remark, internal, scope_type, ref_id, scope_id) -VALUES(UUID_SHORT(), 'bug_new', 'BUG', NULL, 1, 'PROJECT', (SELECT id FROM (SELECT * FROM status_item) t where name = 'bug_new'), '100001100001'); +(id, name, scene, remark, internal, scope_type, ref_id, scope_id, pos) +VALUES(UUID_SHORT(), 'bug_new', 'BUG', NULL, 1, 'PROJECT', (SELECT id FROM (SELECT * FROM status_item) t where name = 'bug_new'), '100001100001', 0); -- 处理中 INSERT INTO status_item -(id, name, scene, remark, internal, scope_type, ref_id, scope_id) -VALUES(UUID_SHORT(), 'bug_in_process', 'BUG', NULL, 1, 'PROJECT', (SELECT id FROM (SELECT * FROM status_item) t where name = 'bug_in_process'), '100001100001'); +(id, name, scene, remark, internal, scope_type, ref_id, scope_id, pos) +VALUES(UUID_SHORT(), 'bug_in_process', 'BUG', NULL, 1, 'PROJECT', (SELECT id FROM (SELECT * FROM status_item) t where name = 'bug_in_process'), '100001100001', 1); -- 已解决 INSERT INTO status_item -(id, name, scene, remark, internal, scope_type, ref_id, scope_id) -VALUES(UUID_SHORT(), 'bug_resolved', 'BUG', NULL, 1, 'PROJECT', (SELECT id FROM (SELECT * FROM status_item) t where name = 'bug_resolved'), '100001100001'); +(id, name, scene, remark, internal, scope_type, ref_id, scope_id, pos) +VALUES(UUID_SHORT(), 'bug_resolved', 'BUG', NULL, 1, 'PROJECT', (SELECT id FROM (SELECT * FROM status_item) t where name = 'bug_resolved'), '100001100001', 2); -- 已关闭 INSERT INTO status_item -(id, name, scene, remark, internal, scope_type, ref_id, scope_id) -VALUES(UUID_SHORT(), 'bug_closed', 'BUG', NULL, 1, 'PROJECT', (SELECT id FROM (SELECT * FROM status_item) t where name = 'bug_closed'), '100001100001'); +(id, name, scene, remark, internal, scope_type, ref_id, scope_id, pos) +VALUES(UUID_SHORT(), 'bug_closed', 'BUG', NULL, 1, 'PROJECT', (SELECT id FROM (SELECT * FROM status_item) t where name = 'bug_closed'), '100001100001', 3); -- 已拒绝 INSERT INTO status_item -(id, name, scene, remark, internal, scope_type, ref_id, scope_id) -VALUES(UUID_SHORT(), 'bug_rejected', 'BUG', NULL, 1, 'PROJECT', (SELECT id FROM (SELECT * FROM status_item) t where name = 'bug_rejected'), '100001100001'); +(id, name, scene, remark, internal, scope_type, ref_id, scope_id, pos) +VALUES(UUID_SHORT(), 'bug_rejected', 'BUG', NULL, 1, 'PROJECT', (SELECT id FROM (SELECT * FROM status_item) t where name = 'bug_rejected'), '100001100001', 4); -- 初始化项目缺陷状态定义 INSERT INTO status_definition(status_id, definition_id) diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/request/StatusItemAddRequest.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/request/StatusItemAddRequest.java index b56f13846d..12bfee05b7 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/request/StatusItemAddRequest.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/request/StatusItemAddRequest.java @@ -35,4 +35,7 @@ public class StatusItemAddRequest implements Serializable { @Schema(description = "状态说明") private String remark; + + @Schema(description = "所有状态都可以流转到该状态") + private Boolean allTransferTo; } \ No newline at end of file diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectStatusFlowSettingController.java b/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectStatusFlowSettingController.java index 3c99e04cdc..2dba1c893a 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectStatusFlowSettingController.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectStatusFlowSettingController.java @@ -8,16 +8,19 @@ import io.metersphere.sdk.dto.request.StatusFlowUpdateRequest; import io.metersphere.sdk.dto.request.StatusItemAddRequest; import io.metersphere.sdk.dto.request.StatusItemUpdateRequest; import io.metersphere.system.domain.StatusItem; -import io.metersphere.system.dto.StatusFlowSettingDTO; +import io.metersphere.system.dto.StatusItemDTO; import io.metersphere.system.log.annotation.Log; import io.metersphere.system.log.constants.OperationLogType; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotEmpty; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.web.bind.annotation.*; +import java.util.List; + /** * @author jianxing * @date : 2023-10-9 @@ -33,9 +36,9 @@ public class ProjectStatusFlowSettingController { @GetMapping("/get/{projectId}/{scene}") @Operation(summary = "项目管理-模板-状态流设置-获取状态流设置") @RequiresPermissions(PermissionConstants.PROJECT_TEMPLATE_READ) - public StatusFlowSettingDTO getStatusFlowSetting(@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED) + public List getStatusFlowSetting(@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED) @PathVariable String projectId, - @Schema(description = "模板的使用场景(FUNCTIONAL,BUG,API,UI,TEST_PLAN)", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "模板的使用场景(FUNCTIONAL,BUG,API,UI,TEST_PLAN)", requiredMode = Schema.RequiredMode.REQUIRED) @PathVariable String scene) { return projectStatusFlowSettingService.getStatusFlowSetting(projectId, scene); } @@ -48,6 +51,17 @@ public class ProjectStatusFlowSettingController { projectStatusFlowSettingService.updateStatusDefinition(request); } + @PostMapping("/status/sort/{projectId}/{scene}") + @Operation(summary = "系统设置-组织-状态流设置-状态项排序") + @RequiresPermissions(PermissionConstants.PROJECT_TEMPLATE_UPDATE) + public void sortStatusItem(@PathVariable + String projectId, @PathVariable String scene, + @RequestBody + @NotEmpty + List statusIds) { + projectStatusFlowSettingService.sortStatusItem(projectId, scene, statusIds); + } + @PostMapping("/status/add") @Operation(summary = "项目管理-模板-状态流设置-添加状态项") @RequiresPermissions(PermissionConstants.PROJECT_TEMPLATE_UPDATE) diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectTemplateController.java b/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectTemplateController.java index daa2412773..4dfba7e713 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectTemplateController.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectTemplateController.java @@ -49,7 +49,7 @@ public class ProjectTemplateController { @Operation(summary = "获取模版详情") @RequiresPermissions(PermissionConstants.PROJECT_TEMPLATE_READ) public TemplateDTO get(@PathVariable String id) { - return projectTemplateservice.geTemplateDTOWithCheck(id); + return projectTemplateservice.getTemplateDTOWithCheck(id); } @PostMapping("/add") diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectStatusFlowSettingLogService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectStatusFlowSettingLogService.java index 36430a9459..7a6020b759 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectStatusFlowSettingLogService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectStatusFlowSettingLogService.java @@ -8,7 +8,7 @@ import io.metersphere.sdk.dto.request.StatusItemUpdateRequest; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.Translator; import io.metersphere.system.domain.StatusItem; -import io.metersphere.system.dto.StatusFlowSettingDTO; +import io.metersphere.system.dto.StatusItemDTO; import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.mapper.StatusItemMapper; @@ -16,6 +16,8 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + /** * @author jianxing * @date : 2023-10-16 @@ -54,7 +56,7 @@ public class ProjectStatusFlowSettingLogService { } public LogDTO updateStatusFlowSettingLog(String scopeId, String scene) { - StatusFlowSettingDTO statusFlowSetting = projectStatusFlowSettingService.getStatusFlowSetting(scopeId, scene); + List statusItemDTOS = projectStatusFlowSettingService.getStatusFlowSetting(scopeId, scene); LogDTO dto = new LogDTO( null, null, @@ -63,7 +65,7 @@ public class ProjectStatusFlowSettingLogService { OperationLogType.UPDATE.name(), OperationLogModule.SETTING_SYSTEM_ORGANIZATION_STATUS_FLOW_SETTING, Translator.get("status_flow.name")); - dto.setOriginalValue(JSON.toJSONBytes(statusFlowSetting)); + dto.setOriginalValue(JSON.toJSONBytes(statusItemDTOS)); return dto; } } \ No newline at end of file diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectStatusFlowSettingService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectStatusFlowSettingService.java index 53092d16d5..b85e055fb3 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectStatusFlowSettingService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectStatusFlowSettingService.java @@ -7,12 +7,15 @@ import io.metersphere.sdk.dto.request.StatusItemAddRequest; import io.metersphere.sdk.dto.request.StatusItemUpdateRequest; import io.metersphere.sdk.util.BeanUtils; import io.metersphere.system.domain.StatusItem; -import io.metersphere.system.dto.StatusFlowSettingDTO; +import io.metersphere.system.dto.StatusItemDTO; import io.metersphere.system.service.BaseStatusFlowSettingService; +import io.metersphere.system.service.OrganizationService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + /** * @author jianxing * @date : 2023-10-9 @@ -30,7 +33,7 @@ public class ProjectStatusFlowSettingService extends BaseStatusFlowSettingServic * @return */ @Override - public StatusFlowSettingDTO getStatusFlowSetting(String projectId, String scene) { + public List getStatusFlowSetting(String projectId, String scene) { ProjectService.checkResourceExist(projectId); return super.getStatusFlowSetting(projectId, scene); } @@ -59,7 +62,10 @@ public class ProjectStatusFlowSettingService extends BaseStatusFlowSettingServic StatusItem statusItem = new StatusItem(); BeanUtils.copyBean(statusItem, request); statusItem.setScopeType(TemplateScopeType.PROJECT.name()); - return baseStatusItemService.add(statusItem); + statusItem = baseStatusItemService.add(statusItem); + // 处理所有状态都可以流转到该状态 + super.handleAllTransferTo(request, statusItem.getId()); + return statusItem; } /** @@ -103,4 +109,11 @@ public class ProjectStatusFlowSettingService extends BaseStatusFlowSettingServic projectTemplateService.checkProjectTemplateEnable(request.getScopeId(), request.getScene()); super.updateStatusFlow(request); } + + @Override + public List sortStatusItem(String projectId, String scene, List statusIds) { + OrganizationService.checkResourceExist(projectId); + projectTemplateService.checkProjectTemplateEnable(projectId, scene); + return super.sortStatusItem(projectId, scene, statusIds); + } } \ No newline at end of file diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectTemplateService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectTemplateService.java index 2bd222522a..be21fd5f3e 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectTemplateService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectTemplateService.java @@ -137,7 +137,7 @@ public class ProjectTemplateService extends BaseTemplateService { template = getInternalTemplate(projectId, scene); } } - return geTemplateDTO(template); + return getTemplateDTO(template); } /** @@ -268,10 +268,10 @@ public class ProjectTemplateService extends BaseTemplateService { * @param id * @return */ - public TemplateDTO geTemplateDTOWithCheck(String id) { + public TemplateDTO getTemplateDTOWithCheck(String id) { Template template = super.getWithCheck(id); checkProjectResourceExist(template); - return super.geTemplateDTO(template); + return super.getTemplateDTO(template); } @Override diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectStatusFlowSettingControllerTest.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectStatusFlowSettingControllerTest.java index 12c2a86252..c95ce74f58 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectStatusFlowSettingControllerTest.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectStatusFlowSettingControllerTest.java @@ -6,14 +6,14 @@ import io.metersphere.sdk.dto.request.StatusFlowUpdateRequest; import io.metersphere.sdk.dto.request.StatusItemAddRequest; import io.metersphere.sdk.dto.request.StatusItemUpdateRequest; import io.metersphere.sdk.util.BeanUtils; -import io.metersphere.sdk.util.Translator; import io.metersphere.system.base.BaseTest; +import io.metersphere.system.controller.OrganizationStatusFlowSettingControllerTest; import io.metersphere.system.controller.param.StatusDefinitionUpdateRequestDefinition; import io.metersphere.system.controller.param.StatusFlowUpdateRequestDefinition; import io.metersphere.system.controller.param.StatusItemAddRequestDefinition; import io.metersphere.system.controller.param.StatusItemUpdateRequestDefinition; import io.metersphere.system.domain.*; -import io.metersphere.system.dto.StatusFlowSettingDTO; +import io.metersphere.system.dto.StatusItemDTO; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.mapper.OrganizationParameterMapper; import io.metersphere.system.mapper.StatusItemMapper; @@ -29,15 +29,13 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MvcResult; -import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; import static io.metersphere.project.enums.result.ProjectResultCode.PROJECT_TEMPLATE_PERMISSION; import static io.metersphere.system.controller.handler.result.CommonResultCode.STATUS_ITEM_EXIST; import static io.metersphere.system.controller.handler.result.CommonResultCode.STATUS_ITEM_NOT_EXIST; import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND; +import static io.metersphere.system.controller.result.SystemResultCode.ORGANIZATION_TEMPLATE_PERMISSION; /** * @author jianxing @@ -53,7 +51,8 @@ public class ProjectStatusFlowSettingControllerTest extends BaseTest { private static final String STATUS_DEFINITION_UPDATE = "status/definition/update"; private static final String STATUS_ADD = "status/add"; private static final String STATUS_UPDATE = "status/update"; - private static final String STATUS_DELETE = "status/delete/{id}"; + private static final String STATUS_SORT = "/status/sort/{0}/{1}"; + private static final String STATUS_DELETE = "status/delete/{0}"; private static final String STATUS_FLOW_UPDATE = "status/flow/update"; private static StatusItem addStatusItem; @@ -79,44 +78,8 @@ public class ProjectStatusFlowSettingControllerTest extends BaseTest { public void getStatusFlowSetting() throws Exception { // @@校验没有数据的情况 MvcResult mvcResult = this.requestGetWithOkAndReturn(GET, DEFAULT_PROJECT_ID, TemplateScene.BUG.name()); - StatusFlowSettingDTO statusFlowSetting = getResultData(mvcResult, StatusFlowSettingDTO.class); - List statusItems = statusFlowSetting.getStatusItems(); - List statusDefinitions = statusFlowSetting.getStatusDefinitions(); - List statusFlows = statusFlowSetting.getStatusFlows(); - List statusDefinitionTypes = statusFlowSetting.getStatusDefinitionTypes(); - Map nameIdMap = baseStatusItemService.getByScopeIdAndScene(DEFAULT_PROJECT_ID, TemplateScene.BUG.name()).stream() - .collect(Collectors.toMap(StatusItem::getId, StatusItem::getName)); - - // 校验状态定义是否正确 - Assertions.assertEquals(statusDefinitionTypes, Arrays.stream(BugStatusDefinitionType.values()).map(Enum::name).toList()); - - // 校验默认的状态定义是否初始化正确 - Assertions.assertEquals(statusItems.size(), DefaultBugStatusItem.values().length); - for (DefaultBugStatusItem defaultBugStatusItem : DefaultBugStatusItem.values()) { - StatusItem statusItem = statusItems.stream() - .filter(item -> item.getName().equals(Translator.get("status_item." + defaultBugStatusItem.getName()))) - .findFirst() - .get(); - - // 校验默认的状态定义是否初始化正确 - List defaultDefinitionTypes = defaultBugStatusItem.getDefinitionTypes() - .stream() - .map(BugStatusDefinitionType::name) - .toList(); - List definitionTypes = statusDefinitions.stream() - .filter(item -> item.getStatusId().equals(statusItem.getId())) - .map(StatusDefinition::getDefinitionId) - .toList(); - Assertions.assertEquals(defaultDefinitionTypes, definitionTypes); - - // 校验默认的状态流是否初始化正确 - List defaultFlowTargets = defaultBugStatusItem.getStatusFlowTargets(); - List flowTargets = statusFlows.stream() - .filter(item -> item.getFromId().equals(statusItem.getId())) - .map(item -> nameIdMap.get(item.getToId())) - .toList(); - Assertions.assertEquals(defaultFlowTargets, flowTargets); - } + List statusItemDTOS = getResultDataArray(mvcResult, StatusItemDTO.class); + OrganizationStatusFlowSettingControllerTest.assertDefaultStatusFlowSettingInit(statusItemDTOS); // @@校验权限 requestGetPermissionTest(PermissionConstants.PROJECT_TEMPLATE_READ, GET, DEFAULT_PROJECT_ID, TemplateScene.BUG.name()); } @@ -141,6 +104,7 @@ public class ProjectStatusFlowSettingControllerTest extends BaseTest { requestStatusItem.setId(statusItem.getId()); requestStatusItem.setScopeType(statusItem.getScopeType()); requestStatusItem.setInternal(statusItem.getInternal()); + requestStatusItem.setPos(baseStatusItemService.getByScopeIdAndScene(DEFAULT_PROJECT_ID, TemplateScene.BUG.name()).size()); Assertions.assertEquals(statusItem.getScopeType(), TemplateScopeType.PROJECT.name()); Assertions.assertEquals(statusItem.getInternal(), false); Assertions.assertEquals(requestStatusItem, statusItem); @@ -155,6 +119,12 @@ public class ProjectStatusFlowSettingControllerTest extends BaseTest { assertErrorCode(this.requestPost(STATUS_ADD, request), PROJECT_TEMPLATE_PERMISSION); changeOrgTemplateEnable(false); + request.setName("test3"); + request.setAllTransferTo(true); + mvcResult = this.requestPostWithOkAndReturn(STATUS_ADD, request); + statusItemId = getResultData(mvcResult, StatusItem.class).getId(); + OrganizationStatusFlowSettingControllerTest.assertAllTransferTo(statusItemId, request.getScopeId()); + // @@校验组织是否存在 request.setScopeId("1111"); assertErrorCode(this.requestPost(STATUS_ADD, request), NOT_FOUND); @@ -329,7 +299,28 @@ public class ProjectStatusFlowSettingControllerTest extends BaseTest { requestGetPermissionTest(PermissionConstants.PROJECT_TEMPLATE_UPDATE, STATUS_DELETE, addStatusItem.getId()); } + @Order(6) + public void sortStatusItem() throws Exception { + List statusItems = baseStatusItemService.getByScopeIdAndScene(DEFAULT_PROJECT_ID, TemplateScene.BUG.name()); + List statusIds = statusItems.stream().map(StatusItem::getId).toList().reversed(); + // @@校验请求成功 + this.requestPostWithOkAndReturn(STATUS_SORT, statusIds, DEFAULT_PROJECT_ID, TemplateScene.BUG.name()); + OrganizationStatusFlowSettingControllerTest.assertSortStatusItem(DEFAULT_PROJECT_ID, statusIds); + // @校验是否开启组织模板 + changeOrgTemplateEnable(false); + assertErrorCode(this.requestPost(STATUS_SORT, statusIds, DEFAULT_PROJECT_ID, TemplateScene.BUG.name()), ORGANIZATION_TEMPLATE_PERMISSION); + changeOrgTemplateEnable(true); + + // @@状态不存在 + assertErrorCode(this.requestPost(STATUS_SORT, List.of("1111"), DEFAULT_PROJECT_ID, TemplateScene.BUG.name()), STATUS_ITEM_NOT_EXIST); + + // @@校验组织是否存在 + assertErrorCode(this.requestPost(STATUS_SORT, statusIds, "111", TemplateScene.BUG.name()), NOT_FOUND); + + // @@校验权限 + requestPostPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_UPDATE, STATUS_SORT, List.of(), DEFAULT_PROJECT_ID, TemplateScene.BUG.name()); + } private void changeOrgTemplateEnable(boolean enable) { if (enable) { diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/controller/OrganizationStatusFlowSettingController.java b/backend/services/system-setting/src/main/java/io/metersphere/system/controller/OrganizationStatusFlowSettingController.java index 57278e1f76..4eaf9a13c0 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/controller/OrganizationStatusFlowSettingController.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/controller/OrganizationStatusFlowSettingController.java @@ -6,7 +6,7 @@ import io.metersphere.sdk.dto.request.StatusFlowUpdateRequest; import io.metersphere.sdk.dto.request.StatusItemAddRequest; import io.metersphere.sdk.dto.request.StatusItemUpdateRequest; import io.metersphere.system.domain.StatusItem; -import io.metersphere.system.dto.StatusFlowSettingDTO; +import io.metersphere.system.dto.StatusItemDTO; import io.metersphere.system.log.annotation.Log; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.service.OrganizationStatusFlowSettingLogService; @@ -15,9 +15,12 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotEmpty; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.web.bind.annotation.*; +import java.util.List; + /** * @author jianxing * @date : 2023-10-9 @@ -33,10 +36,10 @@ public class OrganizationStatusFlowSettingController { @GetMapping("/get/{organizationId}/{scene}") @Operation(summary = "系统设置-组织-状态流设置-获取状态流设置") @RequiresPermissions(PermissionConstants.ORGANIZATION_TEMPLATE_READ) - public StatusFlowSettingDTO getStatusFlowSetting(@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED) - @PathVariable String organizationId, - @Schema(description = "模板的使用场景(FUNCTIONAL,BUG,API,UI,TEST_PLAN)", requiredMode = Schema.RequiredMode.REQUIRED) - @PathVariable String scene) { + public List getStatusFlowSetting(@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED) + @PathVariable String organizationId, + @Schema(description = "模板的使用场景(FUNCTIONAL,BUG,API,UI,TEST_PLAN)", requiredMode = Schema.RequiredMode.REQUIRED) + @PathVariable String scene) { return organizationStatusFlowSettingService.getStatusFlowSetting(organizationId, scene); } @@ -53,7 +56,7 @@ public class OrganizationStatusFlowSettingController { @RequiresPermissions(PermissionConstants.ORGANIZATION_TEMPLATE_UPDATE) @Log(type = OperationLogType.UPDATE, expression = "#msClass.addStatusItemLog(#request)", msClass = OrganizationStatusFlowSettingLogService.class) public StatusItem addStatusItem(@RequestBody StatusItemAddRequest request) { - return organizationStatusFlowSettingService.addStatusItem(request); + return organizationStatusFlowSettingService.addStatusItem(request); } @PostMapping("/status/update") @@ -64,6 +67,17 @@ public class OrganizationStatusFlowSettingController { return organizationStatusFlowSettingService.updateStatusItem(request); } + @PostMapping("/status/sort/{organizationId}/{scene}") + @Operation(summary = "系统设置-组织-状态流设置-状态项排序") + @RequiresPermissions(PermissionConstants.ORGANIZATION_TEMPLATE_UPDATE) + public void sortStatusItem(@PathVariable + String organizationId, @PathVariable String scene, + @RequestBody + @NotEmpty + List statusIds) { + organizationStatusFlowSettingService.sortStatusItem(organizationId, scene, statusIds); + } + @GetMapping("/status/delete/{id}") @Operation(summary = "系统设置-组织-状态流设置-删除状态项") @RequiresPermissions(PermissionConstants.ORGANIZATION_TEMPLATE_UPDATE) diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/StatusFlowSettingDTO.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/StatusFlowSettingDTO.java deleted file mode 100644 index dea922efd8..0000000000 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/StatusFlowSettingDTO.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.metersphere.system.dto; - -import io.metersphere.system.domain.StatusDefinition; -import io.metersphere.system.domain.StatusFlow; -import io.metersphere.system.domain.StatusItem; -import lombok.Data; - -import java.io.Serializable; -import java.util.List; - -@Data -public class StatusFlowSettingDTO implements Serializable { - private static final long serialVersionUID = 1L; - - private List statusItems; - private List statusDefinitionTypes; - private List statusFlows; - private List statusDefinitions; -} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/StatusItemDTO.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/StatusItemDTO.java new file mode 100644 index 0000000000..86ef6f86f1 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/StatusItemDTO.java @@ -0,0 +1,17 @@ +package io.metersphere.system.dto; + +import io.metersphere.system.domain.StatusItem; +import lombok.Data; + +import java.util.List; + + +/** + * @author song-cc-rock + */ +@Data +public class StatusItemDTO extends StatusItem { + + private List statusDefinitions; + private List statusFlowTargets; +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusFlowService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusFlowService.java index 4383273c07..2e76be243a 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusFlowService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusFlowService.java @@ -11,8 +11,10 @@ import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * @author jianxing @@ -32,6 +34,9 @@ public class BaseStatusFlowService { StatusFlowExample example = new StatusFlowExample(); example.createCriteria() .andFromIdIn(statusIds); + StatusFlowExample.Criteria criteria = example.createCriteria() + .andToIdIn(statusIds); + example.or(criteria); return statusFlowMapper.selectByExample(example); } @@ -55,12 +60,12 @@ public class BaseStatusFlowService { } public static List getStatusIds(List statusFlows) { - List statusIds = new ArrayList<>(); + Set statusIds = new HashSet<>(); statusFlows.forEach(statusFlow -> { statusIds.add(statusFlow.getFromId()); statusIds.add(statusFlow.getToId()); }); - return statusIds; + return statusIds.stream().collect(Collectors.toList()); } public void deleteByStatusId(String statusId) { diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusFlowSettingService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusFlowSettingService.java index 8ae33926c4..a6d441c242 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusFlowSettingService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusFlowSettingService.java @@ -6,23 +6,27 @@ import io.metersphere.sdk.constants.TemplateScene; import io.metersphere.sdk.constants.TemplateScopeType; import io.metersphere.sdk.dto.request.StatusDefinitionUpdateRequest; import io.metersphere.sdk.dto.request.StatusFlowUpdateRequest; +import io.metersphere.sdk.dto.request.StatusItemAddRequest; import io.metersphere.sdk.util.BeanUtils; import io.metersphere.system.domain.StatusDefinition; import io.metersphere.system.domain.StatusDefinitionExample; import io.metersphere.system.domain.StatusFlow; import io.metersphere.system.domain.StatusItem; -import io.metersphere.system.dto.StatusFlowSettingDTO; +import io.metersphere.system.dto.StatusItemDTO; import io.metersphere.system.mapper.StatusDefinitionMapper; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; /** @@ -44,29 +48,51 @@ public class BaseStatusFlowSettingService { /** * 查询状态流设置 + * * @param scopeId * @param scene * @return */ - public StatusFlowSettingDTO getStatusFlowSetting(String scopeId, String scene) { - StatusFlowSettingDTO statusFlowSetting = new StatusFlowSettingDTO(); + public List getStatusFlowSetting(String scopeId, String scene) { List statusItems = baseStatusItemService.getStatusItems(scopeId, scene); statusItems = baseStatusItemService.translateInternalStatusItem(statusItems); List statusIds = statusItems.stream().map(StatusItem::getId).toList(); - statusFlowSetting.setStatusDefinitionTypes( - Arrays.stream(BugStatusDefinitionType.values()) - .map(BugStatusDefinitionType::name).toList() - ); - statusFlowSetting.setStatusItems(statusItems); - statusFlowSetting.setStatusDefinitions(baseStatusDefinitionService.getStatusDefinitions(statusIds)); - statusFlowSetting.setStatusFlows(baseStatusFlowService.getStatusFlows(statusIds)); - return statusFlowSetting; + // 获取状态定义 + Map> statusDefinitionMap = baseStatusDefinitionService.getStatusDefinitions(statusIds) + .stream() + .collect(Collectors.groupingBy(StatusDefinition::getStatusId)); + + // 获取状态流 + Map> statusFlowMap = baseStatusFlowService.getStatusFlows(statusIds).stream() + .collect(Collectors.groupingBy(StatusFlow::getFromId)); + + return statusItems.stream().map(statusItem -> { + StatusItemDTO statusItemDTO = BeanUtils.copyBean(new StatusItemDTO(), statusItem); + List statusDefinitions = statusDefinitionMap.get(statusItem.getId()); + List statusDefinitionIds = statusDefinitions == null ? new ArrayList<>() : statusDefinitions + .stream() + .map(StatusDefinition::getDefinitionId) + .collect(Collectors.toList()); + + List statusFlows = statusFlowMap.get(statusItem.getId()); + List statusFlowTargets = statusFlows == null ? new ArrayList<>() : statusFlows + .stream() + .map(StatusFlow::getToId) + .collect(Collectors.toList()); + + statusItemDTO.setStatusFlowTargets(statusFlowTargets); + statusItemDTO.setStatusDefinitions(statusDefinitionIds); + return statusItemDTO; + }).sorted(Comparator.comparing(StatusItemDTO::getPos)) + .collect(Collectors.toList()); + } /** * 设置状态定义 * 比如设置成项目 + * * @param request */ public void updateStatusDefinition(StatusDefinitionUpdateRequest request) { @@ -88,6 +114,7 @@ public class BaseStatusFlowSettingService { /** * 设置状态定义 * 比如设置成项目 + * * @param scopeStatusItemIds * @param statusDefinitions */ @@ -112,9 +139,10 @@ public class BaseStatusFlowSettingService { /** * 更新状态流配置 + * * @param request */ - protected void updateStatusFlow( StatusFlowUpdateRequest request) { + protected void updateStatusFlow(StatusFlowUpdateRequest request) { List statusFlows = request.getStatusFlows(); List statusIds = baseStatusFlowService.getStatusIds(statusFlows); baseStatusItemService.checkStatusScope(request.getScopeId(), statusIds); @@ -132,6 +160,7 @@ public class BaseStatusFlowSettingService { /** * 初始化组织或者项目的默认状态流配置 + * * @param projectId * @param scopeType */ @@ -181,6 +210,7 @@ public class BaseStatusFlowSettingService { /** * 删除组织或项目下状态流 + * * @param scopeId */ public void deleteByScopeId(String scopeId) { @@ -192,4 +222,47 @@ public class BaseStatusFlowSettingService { baseStatusDefinitionService.deleteByStatusIds(statusItemIds); baseStatusItemService.deleteByScopeId(scopeId); } + + /** + * 当勾选了所有状态都可以流转到该状态 + * 添加对应的状态流转 + * @param request + * @param addStatusItemId + */ + protected void handleAllTransferTo(StatusItemAddRequest request, String addStatusItemId) { + if (BooleanUtils.isTrue(request.getAllTransferTo())) { + List statusItems = baseStatusItemService.getByScopeIdAndScene(request.getScopeId(), request.getScene()); + List statusFlows = statusItems.stream() + // 过滤自己 + .filter(item -> !StringUtils.equals(item.getId(), addStatusItemId)) + .map(item -> { + StatusFlowUpdateRequest.StatusFlowRequest statusFlow = new StatusFlowUpdateRequest.StatusFlowRequest(); + statusFlow.setFromId(item.getId()); + statusFlow.setToId(addStatusItemId); + return statusFlow; + }).toList(); + StatusFlowUpdateRequest statusFlowUpdateRequest = new StatusFlowUpdateRequest(); + statusFlowUpdateRequest.setScopeId(request.getScopeId()); + statusFlowUpdateRequest.setScene(request.getScene()); + statusFlowUpdateRequest.setStatusFlows(statusFlows); + updateStatusFlow(statusFlowUpdateRequest); + } + } + + protected List sortStatusItem(String scopeId, String scene, List statusIds) { + baseStatusItemService.checkStatusScope(scopeId, statusIds); + List statusItems = baseStatusItemService.getByScopeIdAndScene(scopeId, scene); + statusItems.sort(Comparator.comparingInt(item -> statusIds.indexOf(item.getId()))); + AtomicInteger pos = new AtomicInteger(); + statusItems = statusItems.stream().map(item -> { + StatusItem statusItem = new StatusItem(); + statusItem.setId(item.getId()); + statusItem.setPos(pos.getAndIncrement()); + return statusItem; + }).toList(); + for (StatusItem statusItem : statusItems) { + baseStatusItemService.update(statusItem); + } + return statusItems; + } } \ No newline at end of file diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusItemService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusItemService.java index 4511e20ab1..c922187721 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusItemService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseStatusItemService.java @@ -49,7 +49,7 @@ public class BaseStatusItemService { return statusItems; } - public String translateInternalStatusItem(String statusName) { + public static String translateInternalStatusItem(String statusName) { return Translator.get("status_item." + statusName); } @@ -85,6 +85,10 @@ public class BaseStatusItemService { public StatusItem add(StatusItem statusItem) { checkAddExist(statusItem); statusItem.setInternal(false); + if (statusItem.getPos() == null) { + // 如果没有指定排序,就放到最后 + statusItem.setPos(getByScopeIdAndScene(statusItem.getScopeId(), statusItem.getScene()).size() + 1); + } return baseAdd(statusItem); } @@ -92,7 +96,12 @@ public class BaseStatusItemService { if (CollectionUtils.isEmpty(statusItems)) { return List.of(); } - statusItems.forEach(statusItem -> statusItem.setId(IDGenerator.nextStr())); + int pos = getByScopeIdAndScene(statusItems.get(0).getScopeId(), statusItems.get(0).getScene()).size(); + for (StatusItem statusItem : statusItems) { + statusItem.setId(IDGenerator.nextStr()); + // 设置排序 + statusItem.setPos(pos++); + } statusItemMapper.batchInsert(statusItems); return statusItems; } @@ -181,4 +190,10 @@ public class BaseStatusItemService { example.createCriteria().andScopeIdEqualTo(scopeId); statusItemMapper.deleteByExample(example); } + + public void updateByRefId(StatusItem copyStatusItem, String refId) { + StatusItemExample example = new StatusItemExample(); + example.createCriteria().andRefIdEqualTo(refId); + statusItemMapper.updateByExampleSelective(copyStatusItem, example); + } } \ No newline at end of file diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateService.java index 294bf15fa5..e468cac980 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/BaseTemplateService.java @@ -94,7 +94,7 @@ public class BaseTemplateService { return templateMapper.selectByPrimaryKey(id); } - protected TemplateDTO geTemplateDTO(Template template) { + protected TemplateDTO getTemplateDTO(Template template) { List templateCustomFields = baseTemplateCustomFieldService.getByTemplateId(template.getId()); // 查找字段名称 diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationStatusFlowSettingLogService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationStatusFlowSettingLogService.java index 9be455a7dc..38e4d60789 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationStatusFlowSettingLogService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationStatusFlowSettingLogService.java @@ -9,7 +9,7 @@ import io.metersphere.sdk.dto.request.StatusItemUpdateRequest; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.Translator; import io.metersphere.system.domain.StatusItem; -import io.metersphere.system.dto.StatusFlowSettingDTO; +import io.metersphere.system.dto.StatusItemDTO; import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.mapper.StatusItemMapper; @@ -17,6 +17,8 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + /** * @author jianxing * @date : 2023-10-16 @@ -54,7 +56,7 @@ public class OrganizationStatusFlowSettingLogService { } public LogDTO updateStatusFlowSettingLog(String scopeId, String scene) { - StatusFlowSettingDTO statusFlowSetting = organizationStatusFlowSettingService.getStatusFlowSetting(scopeId, scene); + List statusFlowSetting = organizationStatusFlowSettingService.getStatusFlowSetting(scopeId, scene); LogDTO dto = new LogDTO( OperationLogConstants.ORGANIZATION, null, diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationStatusFlowSettingService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationStatusFlowSettingService.java index 6cfc2d1c85..e68bd7914e 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationStatusFlowSettingService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationStatusFlowSettingService.java @@ -10,7 +10,7 @@ import io.metersphere.sdk.util.SubListUtils; import io.metersphere.system.domain.StatusDefinition; import io.metersphere.system.domain.StatusFlow; import io.metersphere.system.domain.StatusItem; -import io.metersphere.system.dto.StatusFlowSettingDTO; +import io.metersphere.system.dto.StatusItemDTO; import io.metersphere.system.mapper.BaseProjectMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -33,14 +33,16 @@ public class OrganizationStatusFlowSettingService extends BaseStatusFlowSettingS private OrganizationTemplateService organizationTemplateService; @Resource private BaseProjectMapper baseProjectMapper; + /** * 查询状态流设置 + * * @param organizationId * @param scene * @return */ @Override - public StatusFlowSettingDTO getStatusFlowSetting(String organizationId, String scene) { + public List getStatusFlowSetting(String organizationId, String scene) { OrganizationService.checkResourceExist(organizationId); return super.getStatusFlowSetting(organizationId, scene); } @@ -48,6 +50,7 @@ public class OrganizationStatusFlowSettingService extends BaseStatusFlowSettingS /** * 设置状态定义 * 比如设置成项目 + * * @param request */ @Override @@ -121,6 +124,8 @@ public class OrganizationStatusFlowSettingService extends BaseStatusFlowSettingS statusItem = baseStatusItemService.add(statusItem); // 同步添加项目级别状态项 addRefProjectStatusItem(request.getScopeId(), statusItem); + // 处理所有状态都可以流转到该状态 + super.handleAllTransferTo(request, statusItem.getId()); return statusItem; } @@ -212,6 +217,7 @@ public class OrganizationStatusFlowSettingService extends BaseStatusFlowSettingS /** * 更新状态流配置 + * * @param request */ public void updateStatusFlow(StatusFlowUpdateRequest request) { @@ -269,4 +275,18 @@ public class OrganizationStatusFlowSettingService extends BaseStatusFlowSettingS baseStatusFlowService.batchAdd(statusFlows); }); } + + @Override + public List sortStatusItem(String organizationId, String scene, List statusIds) { + OrganizationService.checkResourceExist(organizationId); + organizationTemplateService.checkOrganizationTemplateEnable(organizationId, scene); + List statusItems = super.sortStatusItem(organizationId, scene, statusIds); + // 同步更新项目级别状态项 + for (StatusItem statusItem : statusItems) { + StatusItem copyStatusItem = new StatusItem(); + copyStatusItem.setPos(statusItem.getPos()); + baseStatusItemService.updateByRefId(copyStatusItem, statusItem.getId()); + } + return statusItems; + } } \ No newline at end of file diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationTemplateService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationTemplateService.java index a6dc0e80f1..a458c3f14b 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationTemplateService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/OrganizationTemplateService.java @@ -45,7 +45,7 @@ public class OrganizationTemplateService extends BaseTemplateService { public TemplateDTO geDTOWithCheck(String id) { Template template = super.getWithCheck(id); checkOrgResourceExist(template); - return super.geTemplateDTO(template); + return super.getTemplateDTO(template); } @Override diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/OrganizationStatusFlowSettingControllerTest.java b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/OrganizationStatusFlowSettingControllerTest.java index 0867fdfc74..e3dd288bc5 100644 --- a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/OrganizationStatusFlowSettingControllerTest.java +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/OrganizationStatusFlowSettingControllerTest.java @@ -6,6 +6,7 @@ import io.metersphere.sdk.dto.request.StatusFlowUpdateRequest; import io.metersphere.sdk.dto.request.StatusItemAddRequest; import io.metersphere.sdk.dto.request.StatusItemUpdateRequest; import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.CommonBeanFactory; import io.metersphere.sdk.util.Translator; import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.param.StatusDefinitionUpdateRequestDefinition; @@ -13,7 +14,7 @@ import io.metersphere.system.controller.param.StatusFlowUpdateRequestDefinition; import io.metersphere.system.controller.param.StatusItemAddRequestDefinition; import io.metersphere.system.controller.param.StatusItemUpdateRequestDefinition; import io.metersphere.system.domain.*; -import io.metersphere.system.dto.StatusFlowSettingDTO; +import io.metersphere.system.dto.StatusItemDTO; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.mapper.OrganizationParameterMapper; import io.metersphere.system.mapper.StatusItemMapper; @@ -29,7 +30,6 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MvcResult; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -53,7 +53,8 @@ public class OrganizationStatusFlowSettingControllerTest extends BaseTest { private static final String STATUS_DEFINITION_UPDATE = "status/definition/update"; private static final String STATUS_ADD = "status/add"; private static final String STATUS_UPDATE = "status/update"; - private static final String STATUS_DELETE = "status/delete/{id}"; + private static final String STATUS_SORT = "/status/sort/{0}/{1}"; + private static final String STATUS_DELETE = "status/delete/{0}"; private static final String STATUS_FLOW_UPDATE = "status/flow/update"; private static StatusItem addStatusItem; @@ -79,21 +80,20 @@ public class OrganizationStatusFlowSettingControllerTest extends BaseTest { public void getStatusFlowSetting() throws Exception { // @@校验没有数据的情况 MvcResult mvcResult = this.requestGetWithOkAndReturn(GET, DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()); - StatusFlowSettingDTO statusFlowSetting = getResultData(mvcResult, StatusFlowSettingDTO.class); - List statusItems = statusFlowSetting.getStatusItems(); - List statusDefinitions = statusFlowSetting.getStatusDefinitions(); - List statusFlows = statusFlowSetting.getStatusFlows(); - List statusDefinitionTypes = statusFlowSetting.getStatusDefinitionTypes(); - Map nameIdMap = baseStatusItemService.getByScopeIdAndScene(DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()).stream() + List statusItemDTOS = getResultDataArray(mvcResult, StatusItemDTO.class); + + assertDefaultStatusFlowSettingInit(statusItemDTOS); + // @@校验权限 + requestGetPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_READ, GET, DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()); + } + + public static void assertDefaultStatusFlowSettingInit( List statusItemDTOS) { + Map nameIdMap = statusItemDTOS.stream() .collect(Collectors.toMap(StatusItem::getId, StatusItem::getName)); - - // 校验状态定义是否正确 - Assertions.assertEquals(statusDefinitionTypes, Arrays.stream(BugStatusDefinitionType.values()).map(Enum::name).toList()); - // 校验默认的状态定义是否初始化正确 - Assertions.assertEquals(statusItems.size(), DefaultBugStatusItem.values().length); + Assertions.assertEquals(statusItemDTOS.size(), DefaultBugStatusItem.values().length); for (DefaultBugStatusItem defaultBugStatusItem : DefaultBugStatusItem.values()) { - StatusItem statusItem = statusItems.stream() + StatusItemDTO statusItemDTO = statusItemDTOS.stream() .filter(item -> item.getName().equals(Translator.get("status_item." + defaultBugStatusItem.getName()))) .findFirst() .get(); @@ -103,25 +103,20 @@ public class OrganizationStatusFlowSettingControllerTest extends BaseTest { .stream() .map(BugStatusDefinitionType::name) .toList(); - List definitionTypes = statusDefinitions.stream() - .filter(item -> item.getStatusId().equals(statusItem.getId())) - .map(StatusDefinition::getDefinitionId) - .toList(); + List definitionTypes = statusItemDTO.getStatusDefinitions(); Assertions.assertEquals(defaultDefinitionTypes, definitionTypes); // 校验默认的状态流是否初始化正确 List defaultFlowTargets = defaultBugStatusItem.getStatusFlowTargets(); - List flowTargets = statusFlows.stream() - .filter(item -> item.getFromId().equals(statusItem.getId())) - .map(item -> nameIdMap.get(item.getToId())) + List flowTargets = statusItemDTO.getStatusFlowTargets() + .stream() + .map(nameIdMap::get) .toList(); - Assertions.assertEquals(defaultFlowTargets, flowTargets); + Assertions.assertEquals(defaultFlowTargets.stream().map(i -> BaseStatusItemService.translateInternalStatusItem(i)).toList(), + flowTargets); } - // @@校验权限 - requestGetPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_READ, GET, DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()); } - @Test @Order(1) public void addStatusItem() throws Exception { @@ -139,6 +134,7 @@ public class OrganizationStatusFlowSettingControllerTest extends BaseTest { requestStatusItem.setId(statusItem.getId()); requestStatusItem.setScopeType(statusItem.getScopeType()); requestStatusItem.setInternal(statusItem.getInternal()); + requestStatusItem.setPos(baseStatusItemService.getByScopeIdAndScene(DEFAULT_PROJECT_ID, TemplateScene.BUG.name()).size()); Assertions.assertEquals(statusItem.getScopeType(), TemplateScopeType.ORGANIZATION.name()); Assertions.assertEquals(statusItem.getInternal(), false); Assertions.assertEquals(requestStatusItem, statusItem); @@ -154,6 +150,14 @@ public class OrganizationStatusFlowSettingControllerTest extends BaseTest { assertErrorCode(this.requestPost(STATUS_ADD, request), ORGANIZATION_TEMPLATE_PERMISSION); changeOrgTemplateEnable(true); + // @校验 allTransferTo + request.setName("test2"); + request.setAllTransferTo(true); + mvcResult = this.requestPostWithOkAndReturn(STATUS_ADD, request); + statusItemId = getResultData(mvcResult, StatusItem.class).getId(); + assertAllTransferTo(statusItemId, request.getScopeId()); + assertRefAllTransferTo(statusItemId); + // @@校验组织是否存在 request.setScopeId("1111"); assertErrorCode(this.requestPost(STATUS_ADD, request), NOT_FOUND); @@ -166,6 +170,38 @@ public class OrganizationStatusFlowSettingControllerTest extends BaseTest { requestPostPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_UPDATE, STATUS_ADD, request); } + /** + * 校验变更组织状态项时,有没有同步变更项目状态项 + * + * @param orgStatusItemId + */ + private void assertRefAllTransferTo(String orgStatusItemId) { + List refStatusItems = baseStatusItemService.getByRefId(orgStatusItemId); + refStatusItems.forEach(refStatusItem -> { + assertAllTransferTo(refStatusItem.getId(), refStatusItem.getScopeId()); + }); + } + + /** + * 校验 allTransferTo + * @param statusItemId + */ + public static void assertAllTransferTo(String statusItemId, String scopeId) { + BaseStatusFlowService baseStatusFlowService = CommonBeanFactory.getBean(BaseStatusFlowService.class); + BaseStatusItemService baseStatusItemService = CommonBeanFactory.getBean(BaseStatusItemService.class); + List statusFlows = baseStatusFlowService.getStatusFlows(List.of(statusItemId)); + Map> formMap = statusFlows.stream().collect(Collectors.groupingBy(StatusFlow::getToId)); + Assertions.assertEquals(1, formMap.size()); + Assertions.assertTrue(formMap.containsKey(statusItemId)); + List statusItems = baseStatusItemService.getByScopeIdAndScene(scopeId, TemplateScene.BUG.name()); + Assertions.assertEquals(statusFlows.size() + 1, statusItems.size()); + for (StatusItem item : statusItems) { + if (!StringUtils.equals(item.getId(), statusItemId)) { + Assertions.assertEquals(statusFlows.stream().filter(i -> StringUtils.equals(i.getFromId(), item.getId())).count(), 1); + } + } + } + /** * 校验变更组织状态项时,有没有同步变更项目状态项 * @@ -353,7 +389,54 @@ public class OrganizationStatusFlowSettingControllerTest extends BaseTest { requestGetPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_UPDATE, STATUS_DELETE, addStatusItem.getId()); } + @Test + @Order(6) + public void sortStatusItem() throws Exception { + List statusItems = baseStatusItemService.getByScopeIdAndScene(DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()); + List statusIds = statusItems.stream().map(StatusItem::getId).toList().reversed(); + // @@校验请求成功 + this.requestPostWithOkAndReturn(STATUS_SORT, statusIds, DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()); + assertSortStatusItem(DEFAULT_ORGANIZATION_ID, statusIds); + // 校验同步更新项目状态 + assertSortStatusItem(); + // @校验是否开启组织模板 + changeOrgTemplateEnable(false); + assertErrorCode(this.requestPost(STATUS_SORT, statusIds, DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()), ORGANIZATION_TEMPLATE_PERMISSION); + changeOrgTemplateEnable(true); + + // @@状态不存在 + assertErrorCode(this.requestPost(STATUS_SORT, List.of("1111"), DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()), STATUS_ITEM_NOT_EXIST); + + // @@校验组织是否存在 + assertErrorCode(this.requestPost(STATUS_SORT, statusIds, "111", TemplateScene.BUG.name()), NOT_FOUND); + + // @@校验权限 + requestPostPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_UPDATE, STATUS_SORT, List.of(), DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()); + } + + private void assertSortStatusItem() { + List statusItems = baseStatusItemService.getByScopeIdAndScene(DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()); + List projectStatusItems = baseStatusItemService.getByScopeIdAndScene(DEFAULT_PROJECT_ID, TemplateScene.BUG.name()); + for (int i = 0; i < projectStatusItems.size(); i++) { + StatusItem projectStatusItem = projectStatusItems.get(i); + StatusItem statusItem = statusItems.stream().filter(item -> StringUtils.equals(item.getId(), projectStatusItem.getRefId())) + .findFirst().orElse(null); + Assertions.assertEquals(statusItem.getPos(), projectStatusItem.getPos()); + } + } + + public static void assertSortStatusItem(String scopeId, List statusIds) { + BaseStatusItemService baseStatusItemService = CommonBeanFactory.getBean(BaseStatusItemService.class); + List statusItems = baseStatusItemService.getByScopeId(scopeId); + for (int i = 0; i < statusIds.size(); i++) { + String statusId = statusIds.get(i); + // 根据statusId 查询 + StatusItem statusItem = statusItems.stream().filter(item -> StringUtils.equals(item.getId(), statusId)) + .findFirst().orElse(null); + Assertions.assertEquals(statusItem.getPos(), i); + } + } private void changeOrgTemplateEnable(boolean enable) { if (enable) { diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/SystemProjectControllerTests.java b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/SystemProjectControllerTests.java index cbbd384b14..d3b696f85f 100644 --- a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/SystemProjectControllerTests.java +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/SystemProjectControllerTests.java @@ -11,7 +11,6 @@ import io.metersphere.sdk.dto.UserExtend; import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.Pager; -import io.metersphere.sdk.util.Translator; import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.domain.*; @@ -249,7 +248,8 @@ public class SystemProjectControllerTests extends BaseTest { Assertions.assertNull(currentProject.getModuleSetting()); // 项目模板开启时,校验是否初始化了项目模板 assertionsInitTemplate(projectId); - assertionsInitStatusFlowSetting(projectId); + List statusItemDTOS = baseStatusFlowSettingService.getStatusFlowSetting(projectId, TemplateScene.BUG.name()); + OrganizationStatusFlowSettingControllerTest.assertDefaultStatusFlowSettingInit(statusItemDTOS); //设置了模块模版 List moduleIds = new ArrayList<>(); @@ -417,47 +417,6 @@ public class SystemProjectControllerTests extends BaseTest { } } - public void assertionsInitStatusFlowSetting(String projectId) { - StatusFlowSettingDTO statusFlowSetting = baseStatusFlowSettingService.getStatusFlowSetting(projectId, TemplateScene.BUG.name()); - List statusItems = statusFlowSetting.getStatusItems(); - List statusDefinitions = statusFlowSetting.getStatusDefinitions(); - List statusFlows = statusFlowSetting.getStatusFlows(); - List statusDefinitionTypes = statusFlowSetting.getStatusDefinitionTypes(); - Map nameIdMap = baseStatusItemService.getByScopeIdAndScene(projectId, TemplateScene.BUG.name()).stream() - .collect(Collectors.toMap(StatusItem::getId, StatusItem::getName)); - - // 校验状态定义是否正确 - Assertions.assertEquals(statusDefinitionTypes, Arrays.stream(BugStatusDefinitionType.values()).map(Enum::name).toList()); - - // 校验默认的状态定义是否初始化正确 - Assertions.assertEquals(statusItems.size(), DefaultBugStatusItem.values().length); - for (DefaultBugStatusItem defaultBugStatusItem : DefaultBugStatusItem.values()) { - StatusItem statusItem = statusItems.stream() - .filter(item -> item.getName().equals(Translator.get("status_item." + defaultBugStatusItem.getName()))) - .findFirst() - .get(); - - // 校验默认的状态定义是否初始化正确 - List defaultDefinitionTypes = defaultBugStatusItem.getDefinitionTypes() - .stream() - .map(BugStatusDefinitionType::name) - .toList(); - List definitionTypes = statusDefinitions.stream() - .filter(item -> item.getStatusId().equals(statusItem.getId())) - .map(StatusDefinition::getDefinitionId) - .toList(); - Assertions.assertEquals(defaultDefinitionTypes, definitionTypes); - - // 校验默认的状态流是否初始化正确 - List defaultFlowTargets = defaultBugStatusItem.getStatusFlowTargets(); - List flowTargets = statusFlows.stream() - .filter(item -> item.getFromId().equals(statusItem.getId())) - .map(item -> nameIdMap.get(item.getToId())) - .toList(); - Assertions.assertEquals(defaultFlowTargets, flowTargets); - } - } - private void changeOrgTemplateEnable(boolean enable) { changeOrgTemplateEnable(enable, OrganizationParameterConstants.ORGANIZATION_FUNCTIONAL_TEMPLATE_ENABLE_KEY); changeOrgTemplateEnable(enable, OrganizationParameterConstants.ORGANIZATION_BUG_TEMPLATE_ENABLE_KEY);