From c9a35082784ded8d1c3c89e84d6322d4ae012eb4 Mon Sep 17 00:00:00 2001 From: guoyuqi Date: Fri, 1 Dec 2023 11:12:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=94=A8=E4=BE=8B=E8=AF=84=E5=AE=A1):=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=94=A8=E4=BE=8B=E8=AF=84=E5=AE=A1=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=9F=A5=E8=AF=A2=E5=92=8C=E5=A2=9E=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E4=BE=8B=E5=85=B3=E8=81=94=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../functional/domain/CaseReview.java | 15 + .../functional/domain/CaseReviewExample.java | 181 ++++++++++++ .../CaseReviewFunctionalCaseArchive.java | 4 +- .../CaseReviewFunctionalCaseArchiveMapper.xml | 14 +- .../functional/mapper/CaseReviewMapper.xml | 88 +++++- .../3.0.0/ddl/V3.0.0_10__functional_case.sql | 64 +++-- .../controller/CaseReviewController.java | 25 ++ .../functional/dto/CaseReviewDTO.java | 26 ++ .../functional/dto/CaseReviewUserDTO.java | 12 + .../mapper/ExtCaseReviewMapper.java | 5 + .../functional/mapper/ExtCaseReviewMapper.xml | 267 ++++++++++++++++++ .../mapper/ExtCaseReviewUserMapper.java | 17 ++ .../mapper/ExtCaseReviewUserMapper.xml | 18 ++ .../request/CaseReviewAssociateRequest.java | 37 +++ .../request/CaseReviewPageRequest.java | 21 ++ .../service/CaseReviewLogService.java | 42 +++ .../functional/service/CaseReviewService.java | 243 ++++++++++++++-- .../controller/CaseReviewControllerTests.java | 181 +++++++++++- .../CaseReviewModuleControllerTests.java | 1 + .../test/resources/dml/init_case_review.sql | 16 +- .../FunctionalCaseInterceptor.java | 4 +- .../system/log/aspect/OperationLogAspect.java | 2 +- .../log/constants/OperationLogType.java | 1 + .../src/test/resources/dml/init_aspect.sql | 5 +- 24 files changed, 1206 insertions(+), 83 deletions(-) create mode 100644 backend/services/case-management/src/main/java/io/metersphere/functional/dto/CaseReviewUserDTO.java create mode 100644 backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewUserMapper.java create mode 100644 backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewUserMapper.xml create mode 100644 backend/services/case-management/src/main/java/io/metersphere/functional/request/CaseReviewAssociateRequest.java create mode 100644 backend/services/case-management/src/main/java/io/metersphere/functional/request/CaseReviewPageRequest.java diff --git a/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReview.java b/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReview.java index 0987992258..6bc92e9c80 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReview.java +++ b/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReview.java @@ -4,6 +4,7 @@ import io.metersphere.validation.groups.*; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.*; import java.io.Serializable; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import lombok.Data; @@ -15,6 +16,9 @@ public class CaseReview implements Serializable { @Size(min = 1, max = 50, message = "{case_review.id.length_range}", groups = {Created.class, Updated.class}) private String id; + @Schema(description = "业务ID") + private Long num; + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank(message = "{case_review.name.not_blank}", groups = {Created.class}) @Size(min = 1, max = 255, message = "{case_review.name.length_range}", groups = {Created.class, Updated.class}) @@ -50,6 +54,14 @@ public class CaseReview implements Serializable { @Schema(description = "评审结束时间") private Long endTime; + @Schema(description = "用例数", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{case_review.case_count.not_blank}", groups = {Created.class}) + private Integer caseCount; + + @Schema(description = "通过率(保留两位小数)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "{case_review.pass_rate.not_blank}", groups = {Created.class}) + private BigDecimal passRate; + @Schema(description = "标签") private String tags; @@ -72,6 +84,7 @@ public class CaseReview implements Serializable { public enum Column { id("id", "id", "VARCHAR", false), + num("num", "num", "BIGINT", false), name("name", "name", "VARCHAR", true), moduleId("module_id", "moduleId", "VARCHAR", false), projectId("project_id", "projectId", "VARCHAR", false), @@ -80,6 +93,8 @@ public class CaseReview implements Serializable { pos("pos", "pos", "BIGINT", false), startTime("start_time", "startTime", "BIGINT", false), endTime("end_time", "endTime", "BIGINT", false), + caseCount("case_count", "caseCount", "INTEGER", false), + passRate("pass_rate", "passRate", "DECIMAL", false), tags("tags", "tags", "VARCHAR", false), description("description", "description", "VARCHAR", false), createTime("create_time", "createTime", "BIGINT", false), diff --git a/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReviewExample.java b/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReviewExample.java index 892c60b3c1..7c7bb65f26 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReviewExample.java +++ b/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReviewExample.java @@ -1,5 +1,6 @@ package io.metersphere.functional.domain; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -174,6 +175,66 @@ public class CaseReviewExample { return (Criteria) this; } + public Criteria andNumIsNull() { + addCriterion("num is null"); + return (Criteria) this; + } + + public Criteria andNumIsNotNull() { + addCriterion("num is not null"); + return (Criteria) this; + } + + public Criteria andNumEqualTo(Long value) { + addCriterion("num =", value, "num"); + return (Criteria) this; + } + + public Criteria andNumNotEqualTo(Long value) { + addCriterion("num <>", value, "num"); + return (Criteria) this; + } + + public Criteria andNumGreaterThan(Long value) { + addCriterion("num >", value, "num"); + return (Criteria) this; + } + + public Criteria andNumGreaterThanOrEqualTo(Long value) { + addCriterion("num >=", value, "num"); + return (Criteria) this; + } + + public Criteria andNumLessThan(Long value) { + addCriterion("num <", value, "num"); + return (Criteria) this; + } + + public Criteria andNumLessThanOrEqualTo(Long value) { + addCriterion("num <=", value, "num"); + return (Criteria) this; + } + + public Criteria andNumIn(List values) { + addCriterion("num in", values, "num"); + return (Criteria) this; + } + + public Criteria andNumNotIn(List values) { + addCriterion("num not in", values, "num"); + return (Criteria) this; + } + + public Criteria andNumBetween(Long value1, Long value2) { + addCriterion("num between", value1, value2, "num"); + return (Criteria) this; + } + + public Criteria andNumNotBetween(Long value1, Long value2) { + addCriterion("num not between", value1, value2, "num"); + return (Criteria) this; + } + public Criteria andNameIsNull() { addCriterion("`name` is null"); return (Criteria) this; @@ -704,6 +765,126 @@ public class CaseReviewExample { return (Criteria) this; } + public Criteria andCaseCountIsNull() { + addCriterion("case_count is null"); + return (Criteria) this; + } + + public Criteria andCaseCountIsNotNull() { + addCriterion("case_count is not null"); + return (Criteria) this; + } + + public Criteria andCaseCountEqualTo(Integer value) { + addCriterion("case_count =", value, "caseCount"); + return (Criteria) this; + } + + public Criteria andCaseCountNotEqualTo(Integer value) { + addCriterion("case_count <>", value, "caseCount"); + return (Criteria) this; + } + + public Criteria andCaseCountGreaterThan(Integer value) { + addCriterion("case_count >", value, "caseCount"); + return (Criteria) this; + } + + public Criteria andCaseCountGreaterThanOrEqualTo(Integer value) { + addCriterion("case_count >=", value, "caseCount"); + return (Criteria) this; + } + + public Criteria andCaseCountLessThan(Integer value) { + addCriterion("case_count <", value, "caseCount"); + return (Criteria) this; + } + + public Criteria andCaseCountLessThanOrEqualTo(Integer value) { + addCriterion("case_count <=", value, "caseCount"); + return (Criteria) this; + } + + public Criteria andCaseCountIn(List values) { + addCriterion("case_count in", values, "caseCount"); + return (Criteria) this; + } + + public Criteria andCaseCountNotIn(List values) { + addCriterion("case_count not in", values, "caseCount"); + return (Criteria) this; + } + + public Criteria andCaseCountBetween(Integer value1, Integer value2) { + addCriterion("case_count between", value1, value2, "caseCount"); + return (Criteria) this; + } + + public Criteria andCaseCountNotBetween(Integer value1, Integer value2) { + addCriterion("case_count not between", value1, value2, "caseCount"); + return (Criteria) this; + } + + public Criteria andPassRateIsNull() { + addCriterion("pass_rate is null"); + return (Criteria) this; + } + + public Criteria andPassRateIsNotNull() { + addCriterion("pass_rate is not null"); + return (Criteria) this; + } + + public Criteria andPassRateEqualTo(BigDecimal value) { + addCriterion("pass_rate =", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateNotEqualTo(BigDecimal value) { + addCriterion("pass_rate <>", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateGreaterThan(BigDecimal value) { + addCriterion("pass_rate >", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateGreaterThanOrEqualTo(BigDecimal value) { + addCriterion("pass_rate >=", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateLessThan(BigDecimal value) { + addCriterion("pass_rate <", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateLessThanOrEqualTo(BigDecimal value) { + addCriterion("pass_rate <=", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateIn(List values) { + addCriterion("pass_rate in", values, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateNotIn(List values) { + addCriterion("pass_rate not in", values, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateBetween(BigDecimal value1, BigDecimal value2) { + addCriterion("pass_rate between", value1, value2, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateNotBetween(BigDecimal value1, BigDecimal value2) { + addCriterion("pass_rate not between", value1, value2, "passRate"); + return (Criteria) this; + } + public Criteria andTagsIsNull() { addCriterion("tags is null"); return (Criteria) this; diff --git a/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReviewFunctionalCaseArchive.java b/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReviewFunctionalCaseArchive.java index f101c51b0a..8a39698d1c 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReviewFunctionalCaseArchive.java +++ b/backend/framework/domain/src/main/java/io/metersphere/functional/domain/CaseReviewFunctionalCaseArchive.java @@ -21,14 +21,14 @@ public class CaseReviewFunctionalCaseArchive implements Serializable { private String caseId; @Schema(description = "功能用例快照(JSON)") - private String content; + private byte[] content; private static final long serialVersionUID = 1L; public enum Column { reviewId("review_id", "reviewId", "VARCHAR", false), caseId("case_id", "caseId", "VARCHAR", false), - content("content", "content", "LONGVARCHAR", false); + content("content", "content", "LONGVARBINARY", false); private static final String BEGINNING_DELIMITER = "`"; diff --git a/backend/framework/domain/src/main/java/io/metersphere/functional/mapper/CaseReviewFunctionalCaseArchiveMapper.xml b/backend/framework/domain/src/main/java/io/metersphere/functional/mapper/CaseReviewFunctionalCaseArchiveMapper.xml index 4f0f91d407..96a51ca109 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/functional/mapper/CaseReviewFunctionalCaseArchiveMapper.xml +++ b/backend/framework/domain/src/main/java/io/metersphere/functional/mapper/CaseReviewFunctionalCaseArchiveMapper.xml @@ -6,7 +6,7 @@ - + @@ -111,7 +111,7 @@ insert into case_review_functional_case_archive (review_id, case_id, content ) - values (#{reviewId,jdbcType=VARCHAR}, #{caseId,jdbcType=VARCHAR}, #{content,jdbcType=LONGVARCHAR} + values (#{reviewId,jdbcType=VARCHAR}, #{caseId,jdbcType=VARCHAR}, #{content,jdbcType=LONGVARBINARY} ) @@ -135,7 +135,7 @@ #{caseId,jdbcType=VARCHAR}, - #{content,jdbcType=LONGVARCHAR}, + #{content,jdbcType=LONGVARBINARY}, @@ -155,7 +155,7 @@ case_id = #{record.caseId,jdbcType=VARCHAR}, - content = #{record.content,jdbcType=LONGVARCHAR}, + content = #{record.content,jdbcType=LONGVARBINARY}, @@ -166,7 +166,7 @@ update case_review_functional_case_archive set review_id = #{record.reviewId,jdbcType=VARCHAR}, case_id = #{record.caseId,jdbcType=VARCHAR}, - content = #{record.content,jdbcType=LONGVARCHAR} + content = #{record.content,jdbcType=LONGVARBINARY} @@ -184,7 +184,7 @@ (review_id, case_id, content) values - (#{item.reviewId,jdbcType=VARCHAR}, #{item.caseId,jdbcType=VARCHAR}, #{item.content,jdbcType=LONGVARCHAR} + (#{item.reviewId,jdbcType=VARCHAR}, #{item.caseId,jdbcType=VARCHAR}, #{item.content,jdbcType=LONGVARBINARY} ) @@ -205,7 +205,7 @@ #{item.caseId,jdbcType=VARCHAR} - #{item.content,jdbcType=LONGVARCHAR} + #{item.content,jdbcType=LONGVARBINARY} ) diff --git a/backend/framework/domain/src/main/java/io/metersphere/functional/mapper/CaseReviewMapper.xml b/backend/framework/domain/src/main/java/io/metersphere/functional/mapper/CaseReviewMapper.xml index 2404ae8c77..2210f61a43 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/functional/mapper/CaseReviewMapper.xml +++ b/backend/framework/domain/src/main/java/io/metersphere/functional/mapper/CaseReviewMapper.xml @@ -3,6 +3,7 @@ + @@ -11,6 +12,8 @@ + + @@ -77,8 +80,9 @@ - id, `name`, module_id, project_id, `status`, review_pass_rule, pos, start_time, end_time, - tags, description, create_time, create_user, update_time, update_user + id, num, `name`, module_id, project_id, `status`, review_pass_rule, pos, start_time, + end_time, case_count, pass_rate, tags, description, create_time, create_user, update_time, + update_user + + + + + case_review.module_id in + + #{moduleId} + + + + + + + ( + case_review.name like concat('%', #{request.keyword},'%') + or case_review.num like concat('%', #{request.keyword},'%') + ) + + + + + + + + + + + + + + + + + + + + AND + + + OR + + + + + + + + + + + case_review.status in + + + + + + + case_review.review_pass_rule in + + + + + + + case_review.create_user in + + + + + + + case_review.id in ( + select case_review_user.review_id from case_review_user where case_review_user.user_id in + in + + #{value} + + ) + + + + + + + + + + + + + + case_review.name + + + + + + + + + + case_review.num + + + + + + + + + + case_review.update_time + + + + + + + + + + case_review.create_time + + + + + + + + + + case_review.start_time + + + + + + + + + + case_review.end_time + + + + + + + + + + (api_definition.tags is null or api_definition.tags + + + + ) + + + + + + + api_definition.tags + + + + + + + + + case_review.case_count + + + + + + + + + case_review.pass_rate + + + + + + + + + case_review.id + + + + + + + + + case_review.id + + + + + + + + + case_review.module_id + + + + + + + + + + case_review.module_id + + + + + + + + + + + + + + + in ( select review_id from case_review_user where case_review_user.user_id in + + #{v} + + ) + + + not in + ( select review_id from case_review_user where case_review_user.user_id in + + #{v} + + ) + + + + + \ No newline at end of file diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewUserMapper.java b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewUserMapper.java new file mode 100644 index 0000000000..4d8b6cea14 --- /dev/null +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewUserMapper.java @@ -0,0 +1,17 @@ +package io.metersphere.functional.mapper; + +import io.metersphere.functional.dto.CaseReviewUserDTO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author guoyuqi + */ +public interface ExtCaseReviewUserMapper { + + List getReviewUser(@Param("reviewIds") List reviewIds); + + + +} diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewUserMapper.xml b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewUserMapper.xml new file mode 100644 index 0000000000..b458291791 --- /dev/null +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewUserMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/request/CaseReviewAssociateRequest.java b/backend/services/case-management/src/main/java/io/metersphere/functional/request/CaseReviewAssociateRequest.java new file mode 100644 index 0000000000..2561bed9b9 --- /dev/null +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/request/CaseReviewAssociateRequest.java @@ -0,0 +1,37 @@ +package io.metersphere.functional.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * @author guoyuqi + */ +@Data +public class CaseReviewAssociateRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "用例评审id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{case_review_associate_request.case_review_id.not_blank}") + private String reviewId; + + @Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{case_review_associate_request.project_id.not_blank}") + private String projectId; + + @Schema(description = "功能用例id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "{case_review_associate_request.case_ids.not_empty}") + private List caseIds; + + @Schema(description = "评审人") + @NotEmpty(message = "{case_review_associate_request.user_ids.not_empty}") + private List reviewers; + +} diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/request/CaseReviewPageRequest.java b/backend/services/case-management/src/main/java/io/metersphere/functional/request/CaseReviewPageRequest.java new file mode 100644 index 0000000000..a4c9c55536 --- /dev/null +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/request/CaseReviewPageRequest.java @@ -0,0 +1,21 @@ +package io.metersphere.functional.request; + +import io.metersphere.system.dto.sdk.BasePageRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class CaseReviewPageRequest extends BasePageRequest implements Serializable { + + @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{functional_case.project_id.not_blank}") + private String projectId; + + @Schema(description = "模块id") + private List moduleIds; + +} diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewLogService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewLogService.java index 8cc100025b..53ca5358a6 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewLogService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewLogService.java @@ -1,7 +1,11 @@ package io.metersphere.functional.service; import io.metersphere.functional.domain.CaseReview; +import io.metersphere.functional.domain.FunctionalCase; +import io.metersphere.functional.domain.FunctionalCaseExample; import io.metersphere.functional.mapper.CaseReviewMapper; +import io.metersphere.functional.mapper.FunctionalCaseMapper; +import io.metersphere.functional.request.CaseReviewAssociateRequest; import io.metersphere.functional.request.CaseReviewRequest; import io.metersphere.sdk.constants.HttpMethodConstants; import io.metersphere.sdk.util.JSON; @@ -9,9 +13,13 @@ import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.dto.LogDTO; import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; +import java.util.List; + /** * @author guoyuqi */ @@ -22,6 +30,9 @@ public class CaseReviewLogService { @Resource private CaseReviewMapper caseReviewMapper; + @Resource + private FunctionalCaseMapper functionalCaseMapper; + /** * 新增用例评审 日志 * @@ -69,4 +80,35 @@ public class CaseReviewLogService { dto.setOriginalValue(JSON.toJSONBytes(caseReview)); return dto; } + + public List associateCaseLog(CaseReviewAssociateRequest request){ + CaseReview caseReview = caseReviewMapper.selectByPrimaryKey(request.getReviewId()); + if (caseReview ==null) { + return null; + } + List dtoList = new ArrayList<>(); + List caseIds = request.getCaseIds(); + FunctionalCaseExample functionalCaseExample = new FunctionalCaseExample(); + functionalCaseExample.createCriteria().andIdIn(caseIds); + List functionalCases = functionalCaseMapper.selectByExample(functionalCaseExample); + if (CollectionUtils.isEmpty(functionalCases)) { + return null; + } + for (FunctionalCase functionalCase : functionalCases) { + LogDTO dto = new LogDTO( + caseReview.getProjectId(), + null, + caseReview.getId(), + caseReview.getCreateUser(), + OperationLogType.ASSOCIATE.name(), + OperationLogModule.CASE_REVIEW, + functionalCase.getName()); + + dto.setPath("/case/review/associate"); + dto.setMethod(HttpMethodConstants.POST.name()); + dto.setOriginalValue(JSON.toJSONBytes(functionalCase)); + } + + return dtoList; + } } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewService.java index b0e3906055..84b3d521e6 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewService.java @@ -4,17 +4,24 @@ package io.metersphere.functional.service; import io.metersphere.functional.constants.CaseReviewStatus; import io.metersphere.functional.constants.FunctionalCaseReviewStatus; import io.metersphere.functional.domain.*; +import io.metersphere.functional.dto.CaseReviewDTO; +import io.metersphere.functional.dto.CaseReviewUserDTO; import io.metersphere.functional.mapper.*; +import io.metersphere.functional.request.CaseReviewAssociateRequest; +import io.metersphere.functional.request.CaseReviewPageRequest; import io.metersphere.functional.request.CaseReviewRequest; import io.metersphere.functional.result.CaseManagementResultCode; +import io.metersphere.sdk.constants.ApplicationNumScope; import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.JSON; import io.metersphere.system.domain.User; import io.metersphere.system.mapper.ExtUserMapper; import io.metersphere.system.uid.IDGenerator; +import io.metersphere.system.uid.NumGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; @@ -22,7 +29,12 @@ import org.mybatis.spring.SqlSessionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * 用例评审表服务实现类 @@ -47,6 +59,119 @@ public class CaseReviewService { private CaseReviewUserMapper caseReviewUserMapper; @Resource private ExtUserMapper extUserMapper; + @Resource + private CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper; + @Resource + private ExtCaseReviewUserMapper extCaseReviewUserMapper; + + + /** + * 获取用例评审列表 + * + * @param request request + * @return CaseReviewDTO + */ + public List getCaseReviewPage(CaseReviewPageRequest request) { + List list = extCaseReviewMapper.list(request); + if (CollectionUtils.isEmpty(list)) { + return new ArrayList<>(); + } + List reviewIds = list.stream().map(CaseReview::getId).toList(); + Map> reviewCaseMap = getReviewCaseMap(reviewIds); + Map> reviewUserMap = getReviewUserMap(reviewIds); + for (CaseReviewDTO caseReviewDTO : list) { + buildCaseReviewDTO(caseReviewDTO, reviewCaseMap, reviewUserMap); + } + + return list; + } + + /** + * 通过 reviewCaseMap reviewUserMap 补充 用例评审的其他属性 + * + * @param caseReviewDTO caseReviewDTO + * @param reviewCaseMap 用例和评审的关系map + * @param reviewUserMap 评审和评审人的关系map + */ + private static void buildCaseReviewDTO(CaseReviewDTO caseReviewDTO, Map> reviewCaseMap, Map> reviewUserMap) { + List caseReviewFunctionalCaseList = reviewCaseMap.get(caseReviewDTO.getId()); + if (CollectionUtils.isEmpty(caseReviewFunctionalCaseList)) { + caseReviewDTO.setPassCount(0); + caseReviewDTO.setUnPassCount(0); + caseReviewDTO.setReReviewedCount(0); + caseReviewDTO.setUnderReviewedCount(0); + caseReviewDTO.setReviewedCount(0); + } else { + buildAboutCaseCount(caseReviewDTO, caseReviewFunctionalCaseList); + } + + List caseReviewUserDTOS = reviewUserMap.get(caseReviewDTO.getId()); + List userNames = caseReviewUserDTOS.stream().map(CaseReviewUserDTO::getUserName).toList(); + caseReviewDTO.setReviewers(userNames); + + caseReviewDTO.setStatusName(CaseReviewStatus.valueOf(caseReviewDTO.getStatus()).getName()); + } + + /** + * 构建用例相关的各种数量 + * @param caseReviewDTO 用例评审 + * @param caseReviewFunctionalCaseList 用例和评审相关联的集合 + */ + private static void buildAboutCaseCount(CaseReviewDTO caseReviewDTO, List caseReviewFunctionalCaseList) { + Map> statusCaseMap = caseReviewFunctionalCaseList.stream().collect(Collectors.groupingBy(CaseReviewFunctionalCase::getStatus)); + + List passList = statusCaseMap.get(FunctionalCaseReviewStatus.PASS.toString()); + if (passList == null) { + passList = new ArrayList<>(); + } + caseReviewDTO.setPassCount(passList.size()); + + List unPassList = statusCaseMap.get(FunctionalCaseReviewStatus.UN_PASS.toString()); + if (unPassList == null) { + unPassList = new ArrayList<>(); + } + caseReviewDTO.setUnPassCount(unPassList.size()); + + List reReviewedList = statusCaseMap.get(FunctionalCaseReviewStatus.RE_REVIEWED.toString()); + if (reReviewedList == null) { + reReviewedList = new ArrayList<>(); + } + caseReviewDTO.setReReviewedCount(reReviewedList.size()); + + List underReviewedList = statusCaseMap.get(FunctionalCaseReviewStatus.UNDER_REVIEWED.toString()); + if (underReviewedList == null) { + underReviewedList = new ArrayList<>(); + } + caseReviewDTO.setUnderReviewedCount(underReviewedList.size()); + + caseReviewDTO.setReviewedCount(caseReviewDTO.getPassCount() + caseReviewDTO.getUnPassCount()); + } + + /** + * 通过评审ids获取评审和评审人的关系map + * + * @param reviewIds 评审ids + * @return Map + */ + private Map> getReviewUserMap(List reviewIds) { + List reviewUser = extCaseReviewUserMapper.getReviewUser(reviewIds); + return reviewUser.stream().collect(Collectors.groupingBy(CaseReviewUserDTO::getReviewId)); + } + + + /** + * 通过评审ids获取用例和评审的关系map + * + * @param reviewIds 评审ids + * @return Map + */ + private Map> getReviewCaseMap(List reviewIds) { + CaseReviewFunctionalCaseExample caseReviewFunctionalCaseExample = new CaseReviewFunctionalCaseExample(); + caseReviewFunctionalCaseExample.createCriteria().andReviewIdIn(reviewIds); + List caseReviewFunctionalCases = caseReviewFunctionalCaseMapper.selectByExample(caseReviewFunctionalCaseExample); + return caseReviewFunctionalCases.stream().collect(Collectors.groupingBy(CaseReviewFunctionalCase::getReviewId)); + } + /** * 添加用例评审 @@ -60,21 +185,47 @@ public class CaseReviewService { SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); CaseReviewUserMapper mapper = sqlSession.getMapper(CaseReviewUserMapper.class); CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper = sqlSession.getMapper(CaseReviewFunctionalCaseMapper.class); + CaseReviewFunctionalCaseUserMapper caseReviewFunctionalCaseUserMapper = sqlSession.getMapper(CaseReviewFunctionalCaseUserMapper.class); try { //保存和评审人的关系 addCaseReviewUser(request, caseReviewId, mapper); //保存和用例的关系 - addCaseReviewFunctionalCase(request, userId, caseReviewId, caseReviewFunctionalCaseMapper); + addCaseReviewFunctionalCase(request.getCaseIds(), request.getProjectId(), userId, caseReviewId, caseReviewFunctionalCaseMapper); + //保存用例和用例评审人的关系 + addCaseReviewFunctionalCaseUser(request.getCaseIds(), request.getReviewers(), caseReviewId, caseReviewFunctionalCaseUserMapper); sqlSession.flushStatements(); } finally { SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); } } + /** + * 保存用例和用例评审人的关系 + * + * @param caseIds caseIds + * @param reviewers reviewers + * @param caseReviewId 当前用例评审id + * @param caseReviewFunctionalCaseUserMapper mapper + */ + private static void addCaseReviewFunctionalCaseUser(List caseIds, List reviewers, String caseReviewId, CaseReviewFunctionalCaseUserMapper caseReviewFunctionalCaseUserMapper) { + if (CollectionUtils.isNotEmpty(caseIds)) { + caseIds.forEach(caseId -> { + reviewers.forEach(reviewer -> { + CaseReviewFunctionalCaseUser caseReviewFunctionalCaseUser = new CaseReviewFunctionalCaseUser(); + caseReviewFunctionalCaseUser.setCaseId(caseId); + caseReviewFunctionalCaseUser.setUserId(reviewer); + caseReviewFunctionalCaseUser.setReviewId(caseReviewId); + caseReviewFunctionalCaseUserMapper.insert(caseReviewFunctionalCaseUser); + }); + }); + } + } + /** * 编辑用例评审 + * * @param request 页面参数 - * @param userId 当前操作人 + * @param userId 当前操作人 */ public void editCaseReview(CaseReviewRequest request, String userId) { String reviewId = request.getId(); @@ -129,24 +280,27 @@ public class CaseReviewService { /** * 获取具有评审权限的用户 + * * @param projectId projectId - * @param keyword 查询关键字,根据邮箱和用户名查询 + * @param keyword 查询关键字,根据邮箱和用户名查询 * @return List */ public List getReviewUserList(String projectId, String keyword) { - return extUserMapper.getUserByPermission(projectId, keyword, PermissionConstants.CASE_REVIEW_REVIEW); + return extUserMapper.getUserByPermission(projectId, keyword, PermissionConstants.CASE_REVIEW_REVIEW); } /** * 新增用例评审 - * @param request request - * @param userId 当前操作人 + * + * @param request request + * @param userId 当前操作人 * @param caseReviewId 用例评审id */ private void addCaseReview(CaseReviewRequest request, String userId, String caseReviewId) { CaseReview caseReview = new CaseReview(); caseReview.setId(caseReviewId); + caseReview.setNum(getNextNum(request.getProjectId())); caseReview.setProjectId(request.getProjectId()); caseReview.setName(request.getName()); caseReview.setModuleId(request.getModuleId()); @@ -156,6 +310,12 @@ public class CaseReviewService { if (CollectionUtils.isNotEmpty(request.getTags())) { caseReview.setTags(JSON.toJSONString(request.getTags())); } + caseReview.setPassRate(BigDecimal.valueOf(0.00)); + if (CollectionUtils.isEmpty(request.getCaseIds())) { + caseReview.setCaseCount(0); + } else { + caseReview.setCaseCount(request.getCaseIds().size()); + } caseReview.setStartTime(request.getStartTime()); caseReview.setEndTime(request.getEndTime()); caseReview.setCreateTime(System.currentTimeMillis()); @@ -170,17 +330,17 @@ public class CaseReviewService { * * @param caseReviewId 用例评审id */ - private void checkCaseReview(String caseReviewId) { + private CaseReview checkCaseReview(String caseReviewId) { CaseReviewExample caseReviewExample = new CaseReviewExample(); caseReviewExample.createCriteria().andIdEqualTo(caseReviewId); CaseReview caseReview = caseReviewMapper.selectByPrimaryKey(caseReviewId); if (caseReview == null) { throw new MSException(CaseManagementResultCode.CASE_REVIEW_NOT_FOUND); } + return caseReview; } /** - * * @param projectId 项目id * @return pos */ @@ -189,16 +349,26 @@ public class CaseReviewService { return (pos == null ? 0 : pos) + POS_STEP; } + /** + * @param projectId 项目id + * @return num + */ + public long getNextNum(String projectId) { + return NumGenerator.nextNum(projectId, ApplicationNumScope.CASE_MANAGEMENT); + } + /** * 保存用例评审和功能用例的关系 - * @param request request - * @param userId 当前操作人 - * @param caseReviewId 用例评审id + * + * @param caseIds 功能用例Ids + * @param projectId 项目ID + * @param userId 当前操作人 + * @param caseReviewId 用例评审id * @param caseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper */ - private void addCaseReviewFunctionalCase(CaseReviewRequest request, String userId, String caseReviewId, CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper) { - if (CollectionUtils.isNotEmpty(request.getCaseIds())) { - request.getReviewers().forEach(caseId -> { + private void addCaseReviewFunctionalCase(List caseIds, String projectId, String userId, String caseReviewId, CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper) { + if (CollectionUtils.isNotEmpty(caseIds)) { + caseIds.forEach(caseId -> { CaseReviewFunctionalCase caseReviewFunctionalCase = new CaseReviewFunctionalCase(); caseReviewFunctionalCase.setReviewId(caseReviewId); caseReviewFunctionalCase.setCaseId(caseId); @@ -207,7 +377,7 @@ public class CaseReviewService { caseReviewFunctionalCase.setCreateTime(System.currentTimeMillis()); caseReviewFunctionalCase.setUpdateTime(System.currentTimeMillis()); caseReviewFunctionalCase.setId(IDGenerator.nextStr()); - caseReviewFunctionalCase.setPos(getNextPos(request.getProjectId())); + caseReviewFunctionalCase.setPos(getNextPos(projectId)); caseReviewFunctionalCaseMapper.insert(caseReviewFunctionalCase); }); } @@ -215,8 +385,9 @@ public class CaseReviewService { /** * 保存用例评审和评审人的关系 - * @param request request - * @param caseReviewId 用例评审 + * + * @param request request + * @param caseReviewId 用例评审 * @param caseReviewUserMapper caseReviewUserMapper */ private static void addCaseReviewUser(CaseReviewRequest request, String caseReviewId, CaseReviewUserMapper caseReviewUserMapper) { @@ -228,4 +399,42 @@ public class CaseReviewService { }); } + + /** + * 关联用例 + * @param request 页面参数 + * @param userId 当前操作人 + */ + public void associateCase(CaseReviewAssociateRequest request, String userId) { + String caseReviewId = request.getReviewId(); + CaseReview caseReviewExist = checkCaseReview(caseReviewId); + CaseReviewFunctionalCaseExample caseReviewFunctionalCaseExample = new CaseReviewFunctionalCaseExample(); + caseReviewFunctionalCaseExample.createCriteria().andReviewIdEqualTo(caseReviewId); + List caseReviewFunctionalCases = caseReviewFunctionalCaseMapper.selectByExample(caseReviewFunctionalCaseExample); + List castIds = caseReviewFunctionalCases.stream().map(CaseReviewFunctionalCase::getCaseId).toList(); + List caseRealIds = request.getCaseIds().stream().filter(t -> !castIds.contains(t)).toList(); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper = sqlSession.getMapper(CaseReviewFunctionalCaseMapper.class); + CaseReviewFunctionalCaseUserMapper caseReviewFunctionalCaseUserMapper = sqlSession.getMapper(CaseReviewFunctionalCaseUserMapper.class); + try { + //保存和用例的关系 + addCaseReviewFunctionalCase(caseRealIds, request.getProjectId(), userId, caseReviewId, caseReviewFunctionalCaseMapper); + //保存用例和用例评审人的关系 + addCaseReviewFunctionalCaseUser(caseRealIds, request.getReviewers(), caseReviewId, caseReviewFunctionalCaseUserMapper); + sqlSession.flushStatements(); + } finally { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + List passList = caseReviewFunctionalCases.stream().filter(t -> StringUtils.equalsIgnoreCase(t.getStatus(), FunctionalCaseReviewStatus.PASS.toString())).toList(); + CaseReview caseReview = new CaseReview(); + caseReview.setId(caseReviewId); + //更新用例数量 + caseReview.setCaseCount(caseReviewExist.getCaseCount()+caseRealIds.size()); + //通过率 + BigDecimal passCount = BigDecimal.valueOf(passList.size()); + BigDecimal totalCount = BigDecimal.valueOf(caseReview.getCaseCount()); + BigDecimal passRate = passCount.divide(totalCount,2, RoundingMode.HALF_UP); + caseReview.setPassRate(passRate); + caseReviewMapper.updateByPrimaryKeySelective(caseReview); + } } \ No newline at end of file diff --git a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/CaseReviewControllerTests.java b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/CaseReviewControllerTests.java index 6729f6708d..3ba9848a69 100644 --- a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/CaseReviewControllerTests.java +++ b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/CaseReviewControllerTests.java @@ -1,12 +1,13 @@ package io.metersphere.functional.controller; import io.metersphere.functional.constants.CaseReviewPassRule; +import io.metersphere.functional.constants.FunctionalCaseReviewStatus; import io.metersphere.functional.domain.*; -import io.metersphere.functional.mapper.CaseReviewFollowerMapper; -import io.metersphere.functional.mapper.CaseReviewFunctionalCaseMapper; -import io.metersphere.functional.mapper.CaseReviewMapper; -import io.metersphere.functional.mapper.CaseReviewUserMapper; +import io.metersphere.functional.dto.CaseReviewDTO; +import io.metersphere.functional.mapper.*; +import io.metersphere.functional.request.CaseReviewAssociateRequest; import io.metersphere.functional.request.CaseReviewFollowerRequest; +import io.metersphere.functional.request.CaseReviewPageRequest; import io.metersphere.functional.request.CaseReviewRequest; import io.metersphere.project.domain.Notification; import io.metersphere.project.domain.NotificationExample; @@ -16,7 +17,9 @@ import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.domain.User; import io.metersphere.system.notice.constants.NoticeConstants; +import io.metersphere.system.utils.Pager; import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.*; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -28,8 +31,8 @@ import org.springframework.test.web.servlet.MvcResult; import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -45,6 +48,9 @@ public class CaseReviewControllerTests extends BaseTest { private static final String ADD_CASE_REVIEW = "/case/review/add"; private static final String EDIT_CASE_REVIEW = "/case/review/edit"; + private static final String PAGE_CASE_REVIEW = "/case/review/page"; + private static final String ASSOCIATE_CASE_REVIEW = "/case/review/associate"; + private static final String FOLLOW_CASE_REVIEW = "/case/review/edit/follower"; private static final String CASE_REVIEWER_LIST = "/case/review/user-option/"; @@ -59,6 +65,8 @@ public class CaseReviewControllerTests extends BaseTest { @Resource private CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper; @Resource + private CaseReviewFunctionalCaseUserMapper caseReviewFunctionalCaseUserMapper; + @Resource private NotificationMapper notificationMapper; @Test @@ -242,5 +250,166 @@ public class CaseReviewControllerTests extends BaseTest { Assertions.assertFalse(list.isEmpty()); } + @Test + @Order(10) + public void associateCaseSuccess() throws Exception { + List caseReviews = getCaseReviews("创建评审更新1"); + Assertions.assertEquals(1, caseReviews.size()); + String caseReviewId = caseReviews.get(0).getId(); + CaseReviewAssociateRequest caseReviewAssociateRequest = new CaseReviewAssociateRequest(); + caseReviewAssociateRequest.setProjectId(projectId); + caseReviewAssociateRequest.setReviewId(caseReviewId); + ListcaseIds = new ArrayList<>(); + caseIds.add("CASE_REVIEW_TEST_GYQ_ID2"); + caseIds.add("CASE_REVIEW_TEST_GYQ_ID3"); + caseIds.add("CASE_REVIEW_TEST_GYQ_ID4"); + caseIds.add("CASE_REVIEW_TEST_GYQ_ID5"); + caseIds.add("CASE_REVIEW_TEST_GYQ_ID6"); + caseReviewAssociateRequest.setCaseIds(caseIds); + ListuserIds = new ArrayList<>(); + userIds.add("gyq_review_test"); + userIds.add("gyq_review_test2"); + caseReviewAssociateRequest.setReviewers(userIds); + this.requestPostWithOk(ASSOCIATE_CASE_REVIEW, caseReviewAssociateRequest); + CaseReviewFunctionalCaseExample caseReviewFunctionalCaseExample = new CaseReviewFunctionalCaseExample(); + caseReviewFunctionalCaseExample.createCriteria().andReviewIdEqualTo(caseReviewId); + List caseReviewFunctionalCases = caseReviewFunctionalCaseMapper.selectByExample(caseReviewFunctionalCaseExample); + List castIds = caseReviewFunctionalCases.stream().map(CaseReviewFunctionalCase::getCaseId).toList(); + Assertions.assertTrue(CollectionUtils.isNotEmpty(castIds)); + CaseReviewFunctionalCaseUserExample caseReviewFunctionalCaseUserExample = new CaseReviewFunctionalCaseUserExample(); + caseReviewFunctionalCaseUserExample.createCriteria().andReviewIdEqualTo(caseReviewId); + List caseReviewFunctionalCaseUsers = caseReviewFunctionalCaseUserMapper.selectByExample(caseReviewFunctionalCaseUserExample); + List userIdList = caseReviewFunctionalCaseUsers.stream().map(CaseReviewFunctionalCaseUser::getUserId).toList(); + Assertions.assertTrue(userIdList.contains("gyq_review_test")); + Assertions.assertTrue(userIdList.contains("gyq_review_test2")); + } + @Test + @Order(11) + public void associateCaseFalse() throws Exception { + CaseReviewAssociateRequest caseReviewAssociateRequest = new CaseReviewAssociateRequest(); + caseReviewAssociateRequest.setProjectId(projectId); + caseReviewAssociateRequest.setReviewId("caseReviewIdXXXX"); + ListcaseIds = new ArrayList<>(); + caseIds.add("CASE_REVIEW_TEST_GYQ_ID2"); + caseReviewAssociateRequest.setCaseIds(caseIds); + ListuserIds = new ArrayList<>(); + userIds.add("gyq_review_test"); + userIds.add("gyq_review_test2"); + caseReviewAssociateRequest.setReviewers(userIds); + this.requestPost(ASSOCIATE_CASE_REVIEW, caseReviewAssociateRequest).andExpect(status().is5xxServerError()); + } + + @Test + @Order(12) + public void getPageSuccess() throws Exception { + List caseReviews = getCaseReviews("创建评审更新1"); + Assertions.assertEquals(1, caseReviews.size()); + Map caseReviewCombine = buildRequestCombine(); + CaseReviewPageRequest request = new CaseReviewPageRequest(); + Map> filters = new HashMap<>(); + filters.put("status", Arrays.asList("PREPARED", "UNDERWAY","COMPLETED", "ARCHIVED")); + request.setFilter(filters); + request.setCombine(caseReviewCombine); + request.setProjectId(projectId); + request.setKeyword("评审更新"); + request.setCurrent(1); + request.setPageSize(10); + MvcResult mvcResult = this.requestPostWithOkAndReturn(PAGE_CASE_REVIEW, request); + // 获取返回值 + String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); + // 返回请求正常 + Assertions.assertNotNull(resultHolder); + Pager pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class); + // 返回值不为空 + Assertions.assertNotNull(pageData); + // 返回值的页码和当前页码相同 + Assertions.assertEquals(pageData.getCurrent(), request.getCurrent()); + // 返回的数据量不超过规定要返回的数据量相同 + Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= request.getPageSize()); + + CaseReviewFunctionalCaseExample caseReviewFunctionalCaseExample = new CaseReviewFunctionalCaseExample(); + caseReviewFunctionalCaseExample.createCriteria().andReviewIdEqualTo(caseReviews.get(0).getId()); + List caseReviewFunctionalCases = caseReviewFunctionalCaseMapper.selectByExample(caseReviewFunctionalCaseExample); + Map caseReviewFunctionalCaseMap = caseReviewFunctionalCases.stream().collect(Collectors.toMap(CaseReviewFunctionalCase::getCaseId, t -> t)); + caseReviewFunctionalCaseMap.forEach((k,v)->{ + switch (k) { + case "CASE_REVIEW_TEST_GYQ_ID2" -> v.setStatus(FunctionalCaseReviewStatus.RE_REVIEWED.toString()); + case "CASE_REVIEW_TEST_GYQ_ID3" -> v.setStatus(FunctionalCaseReviewStatus.UNDER_REVIEWED.toString()); + case "CASE_REVIEW_TEST_GYQ_ID4" -> v.setStatus(FunctionalCaseReviewStatus.PASS.toString()); + case "CASE_REVIEW_TEST_GYQ_ID5" -> v.setStatus(FunctionalCaseReviewStatus.UN_PASS.toString()); + default -> v.setStatus(FunctionalCaseReviewStatus.UN_REVIEWED.toString()); + } + caseReviewFunctionalCaseMapper.updateByPrimaryKeySelective(v); + }); + + request = new CaseReviewPageRequest(); + filters = new HashMap<>(); + filters.put("status", Arrays.asList("PREPARED", "UNDERWAY","COMPLETED", "ARCHIVED")); + request.setFilter(filters); + request.setCombine(caseReviewCombine); + request.setProjectId(projectId); + request.setKeyword("评审更新"); + request.setCurrent(1); + request.setPageSize(10); + mvcResult = this.requestPostWithOkAndReturn(PAGE_CASE_REVIEW, request); + // 获取返回值 + returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + resultHolder = JSON.parseObject(returnData, ResultHolder.class); + // 返回请求正常 + Assertions.assertNotNull(resultHolder); + pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class); + // 返回值不为空 + Assertions.assertNotNull(pageData); + // 返回值的页码和当前页码相同 + Assertions.assertEquals(pageData.getCurrent(), request.getCurrent()); + // 返回的数据量不超过规定要返回的数据量相同 + Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= request.getPageSize()); + List caseReviewDTOS = JSON.parseArray(JSON.toJSONString(pageData.getList()), CaseReviewDTO.class); + List caseReviewOne = caseReviewDTOS.stream().filter(t -> StringUtils.equals(t.getName(), "创建评审更新1")).toList(); + Assertions.assertTrue(caseReviewOne.get(0).getPassCount()>0); + Assertions.assertTrue(caseReviewOne.get(0).getUnPassCount()>0); + Assertions.assertTrue(caseReviewOne.get(0).getUnderReviewedCount()>0); + Assertions.assertTrue(caseReviewOne.get(0).getReReviewedCount()>0); + Assertions.assertTrue(caseReviewOne.get(0).getReviewedCount()>0); + + request = new CaseReviewPageRequest(); + filters = new HashMap<>(); + filters.put("status", Arrays.asList("UNDERWAY","COMPLETED", "ARCHIVED")); + request.setFilter(filters); + request.setCombine(caseReviewCombine); + request.setProjectId(projectId); + request.setKeyword("评审更新"); + request.setCurrent(1); + request.setPageSize(10); + mvcResult = this.requestPostWithOkAndReturn(PAGE_CASE_REVIEW, request); + // 获取返回值 + returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + resultHolder = JSON.parseObject(returnData, ResultHolder.class); + // 返回请求正常 + Assertions.assertNotNull(resultHolder); + pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class); + // 返回值不为空 + Assertions.assertNotNull(pageData); + // 返回值的页码和当前页码相同 + Assertions.assertEquals(pageData.getCurrent(), request.getCurrent()); + // 返回的数据量不超过规定要返回的数据量相同 + Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= request.getPageSize()); + caseReviewDTOS = JSON.parseArray(JSON.toJSONString(pageData.getList()), CaseReviewDTO.class); + Assertions.assertTrue(CollectionUtils.isEmpty(caseReviewDTOS)); + + } + + + + /** + * 生成高级搜索参数 + * @return combine param + */ + private Map buildRequestCombine() { + Map map = new HashMap<>(); + map.put("reviewers", Map.of("operator", "in", "value", List.of("admin"))); + return map; + } } diff --git a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/CaseReviewModuleControllerTests.java b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/CaseReviewModuleControllerTests.java index 05deeb9992..1c88eacc43 100644 --- a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/CaseReviewModuleControllerTests.java +++ b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/CaseReviewModuleControllerTests.java @@ -688,6 +688,7 @@ public class CaseReviewModuleControllerTests extends BaseTest { CaseReview caseReview = new CaseReview(); caseReview.setId(IDGenerator.nextStr()); caseReview.setName(name); + caseReview.setNum(1001L); caseReview.setModuleId(a1a1Node.getId()); caseReview.setProjectId(project.getId()); caseReview.setStatus(CaseReviewStatus.PREPARED.toString()); diff --git a/backend/services/case-management/src/test/resources/dml/init_case_review.sql b/backend/services/case-management/src/test/resources/dml/init_case_review.sql index 896041e110..4c16d9b6de 100644 --- a/backend/services/case-management/src/test/resources/dml/init_case_review.sql +++ b/backend/services/case-management/src/test/resources/dml/init_case_review.sql @@ -9,10 +9,22 @@ INSERT INTO template (id, name, remark, internal, update_time, create_time, crea VALUES ('test_template_case_review_gyq_id', 'functional_case_review_gyq', '', b'0', 1696992836000, 1696992836000, 'admin', 'PROJECT', 'project-gyq-case-review-test', b'0', NULL, 'FUNCTIONAL'); INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time) -VALUES ('CASE_REVIEW_TEST_GYQ_ID', 1, 'CASE_REVIEW_TEST_MODULE_ID', 'project-gyq-case-review-test', '100001', '关联需求测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'CASE_REVIEW_TEST_GYQ_ID', 'UN_EXECUTED', true, b'0', b'0', 'gyq', 'gyq', '', 1698058347559, 1698058347559, NULL); +VALUES ('CASE_REVIEW_TEST_GYQ_ID', 1, 'CASE_REVIEW_TEST_MODULE_ID', 'project-gyq-case-review-test', '100001', '关联需求测试1', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'CASE_REVIEW_TEST_GYQ_ID', 'UN_EXECUTED', true, b'0', b'0', 'gyq', 'gyq', '', 1698058347559, 1698058347559, NULL); INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time) -VALUES ('CASE_REVIEW_TEST_GYQ_ID2', 1, 'CASE_REVIEW_TEST_MODULE_ID', 'project-gyq-case-review-test', '100001', '关联需求测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'CASE_REVIEW_TEST_GYQ_ID2', 'UN_EXECUTED', true, b'0', b'0', 'gyq', 'gyq', '', 1698058347559, 1698058347559, NULL); +VALUES ('CASE_REVIEW_TEST_GYQ_ID2', 1, 'CASE_REVIEW_TEST_MODULE_ID', 'project-gyq-case-review-test', '100001', '关联需求测试2', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'CASE_REVIEW_TEST_GYQ_ID2', 'UN_EXECUTED', true, b'0', b'0', 'gyq', 'gyq', '', 1698058347559, 1698058347559, NULL); + +INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time) +VALUES ('CASE_REVIEW_TEST_GYQ_ID3', 1, 'CASE_REVIEW_TEST_MODULE_ID', 'project-gyq-case-review-test', '100001', '关联需求测试3', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'CASE_REVIEW_TEST_GYQ_ID3', 'UN_EXECUTED', true, b'0', b'0', 'gyq', 'gyq', '', 1698058347559, 1698058347559, NULL); + +INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time) +VALUES ('CASE_REVIEW_TEST_GYQ_ID4', 1, 'CASE_REVIEW_TEST_MODULE_ID', 'project-gyq-case-review-test', '100001', '关联需求测试4', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'CASE_REVIEW_TEST_GYQ_ID4', 'UN_EXECUTED', true, b'0', b'0', 'gyq', 'gyq', '', 1698058347559, 1698058347559, NULL); + +INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time) +VALUES ('CASE_REVIEW_TEST_GYQ_ID5', 1, 'CASE_REVIEW_TEST_MODULE_ID', 'project-gyq-case-review-test', '100001', '关联需求测试5', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'CASE_REVIEW_TEST_GYQ_ID5', 'UN_EXECUTED', true, b'0', b'0', 'gyq', 'gyq', '', 1698058347559, 1698058347559, NULL); + +INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time) +VALUES ('CASE_REVIEW_TEST_GYQ_ID6', 1, 'CASE_REVIEW_TEST_MODULE_ID', 'project-gyq-case-review-test', '100001', '关联需求测试6', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'CASE_REVIEW_TEST_GYQ_ID6', 'UN_EXECUTED', true, b'0', b'0', 'gyq', 'gyq', '', 1698058347559, 1698058347559, NULL); INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('CASE_REVIEW_TEST_GYQ_ID', 'gyq_custom_id_review1', '22'); INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('CASE_REVIEW_TEST_GYQ_ID', 'gyq_custom_id_review2', '33'); diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/config/interceptor/FunctionalCaseInterceptor.java b/backend/services/system-setting/src/main/java/io/metersphere/system/config/interceptor/FunctionalCaseInterceptor.java index 64c8f811bd..6ae8dc8833 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/config/interceptor/FunctionalCaseInterceptor.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/config/interceptor/FunctionalCaseInterceptor.java @@ -1,5 +1,6 @@ package io.metersphere.system.config.interceptor; +import io.metersphere.functional.domain.CaseReviewFunctionalCaseArchive; import io.metersphere.functional.domain.FunctionalCaseHistory; import io.metersphere.sdk.util.CompressUtils; import io.metersphere.system.utils.MybatisInterceptorConfig; @@ -16,8 +17,7 @@ public class FunctionalCaseInterceptor { List configList = new ArrayList<>(); configList.add(new MybatisInterceptorConfig(FunctionalCaseHistory.class, "content", CompressUtils.class, "zip", "unzip")); - - + configList.add(new MybatisInterceptorConfig(CaseReviewFunctionalCaseArchive.class, "content", CompressUtils.class, "zip", "unzip")); return configList; } } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/log/aspect/OperationLogAspect.java b/backend/services/system-setting/src/main/java/io/metersphere/system/log/aspect/OperationLogAspect.java index 291e63562c..32037b6b47 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/log/aspect/OperationLogAspect.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/log/aspect/OperationLogAspect.java @@ -65,7 +65,7 @@ public class OperationLogAspect { // 此方法随时补充类型,需要在内容变更前执行的类型都可以加入 private final OperationLogType[] beforeMethodNames = new OperationLogType[]{OperationLogType.UPDATE, OperationLogType.DELETE - , OperationLogType.RECOVER, OperationLogType.DISASSOCIATE, OperationLogType.ARCHIVED}; + , OperationLogType.RECOVER, OperationLogType.DISASSOCIATE,OperationLogType.ASSOCIATE, OperationLogType.ARCHIVED}; // 需要后置执行合并内容的 private final OperationLogType[] postMethodNames = new OperationLogType[]{OperationLogType.ADD, OperationLogType.UPDATE}; diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogType.java b/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogType.java index ce12f12a27..06166200bc 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogType.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogType.java @@ -17,6 +17,7 @@ public enum OperationLogType { RECOVER, LOGOUT, DISASSOCIATE, + ASSOCIATE, ARCHIVED; public boolean contains(OperationLogType keyword) { diff --git a/backend/services/system-setting/src/test/resources/dml/init_aspect.sql b/backend/services/system-setting/src/test/resources/dml/init_aspect.sql index 5b8a29ff36..a1403fbd42 100644 --- a/backend/services/system-setting/src/test/resources/dml/init_aspect.sql +++ b/backend/services/system-setting/src/test/resources/dml/init_aspect.sql @@ -43,6 +43,7 @@ VALUES ('aspect_gyq_api_scenario_one', 'api_scenario', 'p1', 'test-api-status', INSERT INTO test_plan(id, project_id, parent_id, name, status, stage, tags, create_time, create_user, update_time, update_user, planned_start_time, planned_end_time, actual_start_time, actual_end_time, description) VALUES ('aspect_gyq_test_plan_one','100001100001', 'NONE', 'test_plan', 'test-api-status', 'Smock', null, UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 2000, UNIX_TIMESTAMP() * 3000 ,UNIX_TIMESTAMP() * 2000, UNIX_TIMESTAMP() * 3000,null); -INSERT INTO case_review(id, name, module_id, project_id, start_time, end_time, tags, description, create_time, create_user, update_time, update_user) -VALUES ('aspect_gyq_case_review_one','case_review','module_id', '100001100001', null, null, null, null, UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin'); +INSERT INTO case_review(id, num, name, module_id, project_id, status, review_pass_rule, pos, start_time, end_time, tags, description, create_time, create_user, update_time, update_user) +VALUES ('aspect_gyq_case_review_one','10001','case_review','module_id', '100001100001','PREPARED','SINGLE','0', null, null, null, null, UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, 'admin'); +