refactor(项目设置): 补充状态流相关接口

This commit is contained in:
AgAngle 2023-10-24 22:24:18 +08:00 committed by Craftsman
parent a7538c1fad
commit e9a7ebe733
24 changed files with 484 additions and 205 deletions

View File

@ -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 = "`";

View File

@ -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<Integer> values) {
addCriterion("pos in", values, "pos");
return (Criteria) this;
}
public Criteria andPosNotIn(List<Integer> 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 {

View File

@ -10,6 +10,7 @@
<result column="scope_type" jdbcType="VARCHAR" property="scopeType" />
<result column="ref_id" jdbcType="VARCHAR" property="refId" />
<result column="scope_id" jdbcType="VARCHAR" property="scopeId" />
<result column="pos" jdbcType="INTEGER" property="pos" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -70,7 +71,7 @@
</where>
</sql>
<sql id="Base_Column_List">
id, `name`, scene, remark, internal, scope_type, ref_id, scope_id
id, `name`, scene, remark, internal, scope_type, ref_id, scope_id, pos
</sql>
<select id="selectByExample" parameterType="io.metersphere.system.domain.StatusItemExample" resultMap="BaseResultMap">
select
@ -105,10 +106,12 @@
<insert id="insert" parameterType="io.metersphere.system.domain.StatusItem">
insert into status_item (id, `name`, scene,
remark, internal, scope_type,
ref_id, scope_id)
ref_id, scope_id, pos
)
values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{scene,jdbcType=VARCHAR},
#{remark,jdbcType=VARCHAR}, #{internal,jdbcType=BIT}, #{scopeType,jdbcType=VARCHAR},
#{refId,jdbcType=VARCHAR}, #{scopeId,jdbcType=VARCHAR})
#{refId,jdbcType=VARCHAR}, #{scopeId,jdbcType=VARCHAR}, #{pos,jdbcType=INTEGER}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.system.domain.StatusItem">
insert into status_item
@ -137,6 +140,9 @@
<if test="scopeId != null">
scope_id,
</if>
<if test="pos != null">
pos,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -163,6 +169,9 @@
<if test="scopeId != null">
#{scopeId,jdbcType=VARCHAR},
</if>
<if test="pos != null">
#{pos,jdbcType=INTEGER},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.system.domain.StatusItemExample" resultType="java.lang.Long">
@ -198,6 +207,9 @@
<if test="record.scopeId != null">
scope_id = #{record.scopeId,jdbcType=VARCHAR},
</if>
<if test="record.pos != null">
pos = #{record.pos,jdbcType=INTEGER},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -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}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -241,6 +254,9 @@
<if test="scopeId != null">
scope_id = #{scopeId,jdbcType=VARCHAR},
</if>
<if test="pos != null">
pos = #{pos,jdbcType=INTEGER},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
@ -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}
</update>
<insert id="batchInsert" parameterType="map">
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
<foreach collection="list" item="item" separator=",">
(#{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}
)
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
@ -299,6 +317,9 @@
<if test="'scope_id'.toString() == column.value">
#{item.scopeId,jdbcType=VARCHAR}
</if>
<if test="'pos'.toString() == column.value">
#{item.pos,jdbcType=INTEGER}
</if>
</foreach>
)
</foreach>

View File

@ -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);

View File

@ -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)

View File

@ -35,4 +35,7 @@ public class StatusItemAddRequest implements Serializable {
@Schema(description = "状态说明")
private String remark;
@Schema(description = "所有状态都可以流转到该状态")
private Boolean allTransferTo;
}

View File

@ -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<StatusItemDTO> 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<String> statusIds) {
projectStatusFlowSettingService.sortStatusItem(projectId, scene, statusIds);
}
@PostMapping("/status/add")
@Operation(summary = "项目管理-模板-状态流设置-添加状态项")
@RequiresPermissions(PermissionConstants.PROJECT_TEMPLATE_UPDATE)

View File

@ -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")

View File

@ -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<StatusItemDTO> 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;
}
}

View File

@ -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<StatusItemDTO> 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<StatusItem> sortStatusItem(String projectId, String scene, List<String> statusIds) {
OrganizationService.checkResourceExist(projectId);
projectTemplateService.checkProjectTemplateEnable(projectId, scene);
return super.sortStatusItem(projectId, scene, statusIds);
}
}

View File

@ -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

View File

@ -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<StatusItem> statusItems = statusFlowSetting.getStatusItems();
List<StatusDefinition> statusDefinitions = statusFlowSetting.getStatusDefinitions();
List<StatusFlow> statusFlows = statusFlowSetting.getStatusFlows();
List<String> statusDefinitionTypes = statusFlowSetting.getStatusDefinitionTypes();
Map<String, String> 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<String> defaultDefinitionTypes = defaultBugStatusItem.getDefinitionTypes()
.stream()
.map(BugStatusDefinitionType::name)
.toList();
List<String> definitionTypes = statusDefinitions.stream()
.filter(item -> item.getStatusId().equals(statusItem.getId()))
.map(StatusDefinition::getDefinitionId)
.toList();
Assertions.assertEquals(defaultDefinitionTypes, definitionTypes);
// 校验默认的状态流是否初始化正确
List<String> defaultFlowTargets = defaultBugStatusItem.getStatusFlowTargets();
List<String> flowTargets = statusFlows.stream()
.filter(item -> item.getFromId().equals(statusItem.getId()))
.map(item -> nameIdMap.get(item.getToId()))
.toList();
Assertions.assertEquals(defaultFlowTargets, flowTargets);
}
List<StatusItemDTO> 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<StatusItem> statusItems = baseStatusItemService.getByScopeIdAndScene(DEFAULT_PROJECT_ID, TemplateScene.BUG.name());
List<String> 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) {

View File

@ -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<StatusItemDTO> 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<String> statusIds) {
organizationStatusFlowSettingService.sortStatusItem(organizationId, scene, statusIds);
}
@GetMapping("/status/delete/{id}")
@Operation(summary = "系统设置-组织-状态流设置-删除状态项")
@RequiresPermissions(PermissionConstants.ORGANIZATION_TEMPLATE_UPDATE)

View File

@ -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<StatusItem> statusItems;
private List<String> statusDefinitionTypes;
private List<StatusFlow> statusFlows;
private List<StatusDefinition> statusDefinitions;
}

View File

@ -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<String> statusDefinitions;
private List<String> statusFlowTargets;
}

View File

@ -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<String> getStatusIds(List<StatusFlowUpdateRequest.StatusFlowRequest> statusFlows) {
List<String> statusIds = new ArrayList<>();
Set<String> 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) {

View File

@ -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<StatusItemDTO> getStatusFlowSetting(String scopeId, String scene) {
List<StatusItem> statusItems = baseStatusItemService.getStatusItems(scopeId, scene);
statusItems = baseStatusItemService.translateInternalStatusItem(statusItems);
List<String> 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<String, List<StatusDefinition>> statusDefinitionMap = baseStatusDefinitionService.getStatusDefinitions(statusIds)
.stream()
.collect(Collectors.groupingBy(StatusDefinition::getStatusId));
// 获取状态流
Map<String, List<StatusFlow>> statusFlowMap = baseStatusFlowService.getStatusFlows(statusIds).stream()
.collect(Collectors.groupingBy(StatusFlow::getFromId));
return statusItems.stream().map(statusItem -> {
StatusItemDTO statusItemDTO = BeanUtils.copyBean(new StatusItemDTO(), statusItem);
List<StatusDefinition> statusDefinitions = statusDefinitionMap.get(statusItem.getId());
List<String> statusDefinitionIds = statusDefinitions == null ? new ArrayList<>() : statusDefinitions
.stream()
.map(StatusDefinition::getDefinitionId)
.collect(Collectors.toList());
List<StatusFlow> statusFlows = statusFlowMap.get(statusItem.getId());
List<String> 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<StatusFlowUpdateRequest.StatusFlowRequest> statusFlows = request.getStatusFlows();
List<String> 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<StatusItem> statusItems = baseStatusItemService.getByScopeIdAndScene(request.getScopeId(), request.getScene());
List<StatusFlowUpdateRequest.StatusFlowRequest> 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<StatusItem> sortStatusItem(String scopeId, String scene, List<String> statusIds) {
baseStatusItemService.checkStatusScope(scopeId, statusIds);
List<StatusItem> 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;
}
}

View File

@ -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);
}
}

View File

@ -94,7 +94,7 @@ public class BaseTemplateService {
return templateMapper.selectByPrimaryKey(id);
}
protected TemplateDTO geTemplateDTO(Template template) {
protected TemplateDTO getTemplateDTO(Template template) {
List<TemplateCustomField> templateCustomFields = baseTemplateCustomFieldService.getByTemplateId(template.getId());
// 查找字段名称

View File

@ -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<StatusItemDTO> statusFlowSetting = organizationStatusFlowSettingService.getStatusFlowSetting(scopeId, scene);
LogDTO dto = new LogDTO(
OperationLogConstants.ORGANIZATION,
null,

View File

@ -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<StatusItemDTO> 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<StatusItem> sortStatusItem(String organizationId, String scene, List<String> statusIds) {
OrganizationService.checkResourceExist(organizationId);
organizationTemplateService.checkOrganizationTemplateEnable(organizationId, scene);
List<StatusItem> 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;
}
}

View File

@ -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

View File

@ -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<StatusItem> statusItems = statusFlowSetting.getStatusItems();
List<StatusDefinition> statusDefinitions = statusFlowSetting.getStatusDefinitions();
List<StatusFlow> statusFlows = statusFlowSetting.getStatusFlows();
List<String> statusDefinitionTypes = statusFlowSetting.getStatusDefinitionTypes();
Map<String, String> nameIdMap = baseStatusItemService.getByScopeIdAndScene(DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name()).stream()
List<StatusItemDTO> statusItemDTOS = getResultDataArray(mvcResult, StatusItemDTO.class);
assertDefaultStatusFlowSettingInit(statusItemDTOS);
// @@校验权限
requestGetPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_READ, GET, DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name());
}
public static void assertDefaultStatusFlowSettingInit( List<StatusItemDTO> statusItemDTOS) {
Map<String, String> 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<String> definitionTypes = statusDefinitions.stream()
.filter(item -> item.getStatusId().equals(statusItem.getId()))
.map(StatusDefinition::getDefinitionId)
.toList();
List<String> definitionTypes = statusItemDTO.getStatusDefinitions();
Assertions.assertEquals(defaultDefinitionTypes, definitionTypes);
// 校验默认的状态流是否初始化正确
List<String> defaultFlowTargets = defaultBugStatusItem.getStatusFlowTargets();
List<String> flowTargets = statusFlows.stream()
.filter(item -> item.getFromId().equals(statusItem.getId()))
.map(item -> nameIdMap.get(item.getToId()))
List<String> 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<StatusItem> 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<StatusFlow> statusFlows = baseStatusFlowService.getStatusFlows(List.of(statusItemId));
Map<String, List<StatusFlow>> formMap = statusFlows.stream().collect(Collectors.groupingBy(StatusFlow::getToId));
Assertions.assertEquals(1, formMap.size());
Assertions.assertTrue(formMap.containsKey(statusItemId));
List<StatusItem> 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<StatusItem> statusItems = baseStatusItemService.getByScopeIdAndScene(DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name());
List<String> 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<StatusItem> statusItems = baseStatusItemService.getByScopeIdAndScene(DEFAULT_ORGANIZATION_ID, TemplateScene.BUG.name());
List<StatusItem> 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<String> statusIds) {
BaseStatusItemService baseStatusItemService = CommonBeanFactory.getBean(BaseStatusItemService.class);
List<StatusItem> 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) {

View File

@ -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<StatusItemDTO> statusItemDTOS = baseStatusFlowSettingService.getStatusFlowSetting(projectId, TemplateScene.BUG.name());
OrganizationStatusFlowSettingControllerTest.assertDefaultStatusFlowSettingInit(statusItemDTOS);
//设置了模块模版
List<String> 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<StatusItem> statusItems = statusFlowSetting.getStatusItems();
List<StatusDefinition> statusDefinitions = statusFlowSetting.getStatusDefinitions();
List<StatusFlow> statusFlows = statusFlowSetting.getStatusFlows();
List<String> statusDefinitionTypes = statusFlowSetting.getStatusDefinitionTypes();
Map<String, String> 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<String> defaultDefinitionTypes = defaultBugStatusItem.getDefinitionTypes()
.stream()
.map(BugStatusDefinitionType::name)
.toList();
List<String> definitionTypes = statusDefinitions.stream()
.filter(item -> item.getStatusId().equals(statusItem.getId()))
.map(StatusDefinition::getDefinitionId)
.toList();
Assertions.assertEquals(defaultDefinitionTypes, definitionTypes);
// 校验默认的状态流是否初始化正确
List<String> defaultFlowTargets = defaultBugStatusItem.getStatusFlowTargets();
List<String> 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);