From 40a4e3b72ddf7029e6d38dc4a08257d52b3c3407 Mon Sep 17 00:00:00 2001 From: lan-yonghui Date: Tue, 9 Jan 2024 20:15:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=AE=9A=E4=B9=89=E5=8F=98=E6=9B=B4=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/domain/OperationHistory.java | 6 +- .../domain/OperationHistoryExample.java | 60 ++++++++++ .../system/mapper/OperationHistoryMapper.xml | 32 +++-- .../migration/3.0.0/ddl/V3.0.0_2__sdk_ddl.sql | 24 ++-- .../src/main/resources/i18n/system.properties | 9 ++ .../resources/i18n/system_en_US.properties | 10 +- .../resources/i18n/system_zh_CN.properties | 10 ++ .../resources/i18n/system_zh_TW.properties | 12 +- .../definition/ApiDefinitionController.java | 30 +++++ .../definition/ApiDefinitionVersionDTO.java | 5 +- .../api/mapper/ExtApiDefinitionMapper.java | 5 + .../api/mapper/ExtApiDefinitionMapper.xml | 38 ++++-- .../definition/ApiDefinitionLogService.java | 68 ++++++++--- .../definition/ApiDefinitionService.java | 94 +++++++++++++++ .../ApiDefinitionControllerTests.java | 111 +++++++++++++++++- .../system/dto/OperationHistoryDTO.java | 26 ++++ .../dto/request/OperationHistoryRequest.java | 33 ++++++ .../OperationHistoryVersionRequest.java | 39 ++++++ .../mapper/BaseOperationHistoryMapper.java | 4 + .../mapper/BaseOperationHistoryMapper.xml | 22 ++++ .../service/OperationHistoryService.java | 55 +++++++++ 21 files changed, 642 insertions(+), 51 deletions(-) create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/dto/OperationHistoryDTO.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/dto/request/OperationHistoryRequest.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/dto/request/OperationHistoryVersionRequest.java create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/service/OperationHistoryService.java diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/domain/OperationHistory.java b/backend/framework/domain/src/main/java/io/metersphere/system/domain/OperationHistory.java index 6db66b34ca..d3fd89a4da 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/domain/OperationHistory.java +++ b/backend/framework/domain/src/main/java/io/metersphere/system/domain/OperationHistory.java @@ -36,6 +36,9 @@ public class OperationHistory implements Serializable { @Schema(description = "操作模块/api/case/scenario/ui") private String module; + @Schema(description = "关联id(关联变更记录id来源)") + private Long refId; + private static final long serialVersionUID = 1L; public enum Column { @@ -45,7 +48,8 @@ public class OperationHistory implements Serializable { createUser("create_user", "createUser", "VARCHAR", false), sourceId("source_id", "sourceId", "VARCHAR", false), type("type", "type", "VARCHAR", true), - module("module", "module", "VARCHAR", true); + module("module", "module", "VARCHAR", true), + refId("ref_id", "refId", "BIGINT", false); private static final String BEGINNING_DELIMITER = "`"; diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/domain/OperationHistoryExample.java b/backend/framework/domain/src/main/java/io/metersphere/system/domain/OperationHistoryExample.java index 9b8d0daeae..842e01861b 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/domain/OperationHistoryExample.java +++ b/backend/framework/domain/src/main/java/io/metersphere/system/domain/OperationHistoryExample.java @@ -573,6 +573,66 @@ public class OperationHistoryExample { addCriterion("`module` not between", value1, value2, "module"); return (Criteria) this; } + + public Criteria andRefIdIsNull() { + addCriterion("ref_id is null"); + return (Criteria) this; + } + + public Criteria andRefIdIsNotNull() { + addCriterion("ref_id is not null"); + return (Criteria) this; + } + + public Criteria andRefIdEqualTo(Long value) { + addCriterion("ref_id =", value, "refId"); + return (Criteria) this; + } + + public Criteria andRefIdNotEqualTo(Long value) { + addCriterion("ref_id <>", value, "refId"); + return (Criteria) this; + } + + public Criteria andRefIdGreaterThan(Long value) { + addCriterion("ref_id >", value, "refId"); + return (Criteria) this; + } + + public Criteria andRefIdGreaterThanOrEqualTo(Long value) { + addCriterion("ref_id >=", value, "refId"); + return (Criteria) this; + } + + public Criteria andRefIdLessThan(Long value) { + addCriterion("ref_id <", value, "refId"); + return (Criteria) this; + } + + public Criteria andRefIdLessThanOrEqualTo(Long value) { + addCriterion("ref_id <=", value, "refId"); + return (Criteria) this; + } + + public Criteria andRefIdIn(List values) { + addCriterion("ref_id in", values, "refId"); + return (Criteria) this; + } + + public Criteria andRefIdNotIn(List values) { + addCriterion("ref_id not in", values, "refId"); + return (Criteria) this; + } + + public Criteria andRefIdBetween(Long value1, Long value2) { + addCriterion("ref_id between", value1, value2, "refId"); + return (Criteria) this; + } + + public Criteria andRefIdNotBetween(Long value1, Long value2) { + addCriterion("ref_id not between", value1, value2, "refId"); + return (Criteria) this; + } } public static class Criteria extends GeneratedCriteria { diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/mapper/OperationHistoryMapper.xml b/backend/framework/domain/src/main/java/io/metersphere/system/mapper/OperationHistoryMapper.xml index 84b6fe6ecb..2d88af3c61 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/mapper/OperationHistoryMapper.xml +++ b/backend/framework/domain/src/main/java/io/metersphere/system/mapper/OperationHistoryMapper.xml @@ -9,6 +9,7 @@ + @@ -69,7 +70,7 @@ - id, project_id, create_time, create_user, source_id, `type`, `module` + id, project_id, create_time, create_user, source_id, `type`, `module`, ref_id @@ -188,6 +195,9 @@ `module` = #{record.module,jdbcType=VARCHAR}, + + ref_id = #{record.refId,jdbcType=BIGINT}, + @@ -201,7 +211,8 @@ create_user = #{record.createUser,jdbcType=VARCHAR}, source_id = #{record.sourceId,jdbcType=VARCHAR}, `type` = #{record.type,jdbcType=VARCHAR}, - `module` = #{record.module,jdbcType=VARCHAR} + `module` = #{record.module,jdbcType=VARCHAR}, + ref_id = #{record.refId,jdbcType=BIGINT} @@ -227,6 +238,9 @@ `module` = #{module,jdbcType=VARCHAR}, + + ref_id = #{refId,jdbcType=BIGINT}, + where id = #{id,jdbcType=BIGINT} @@ -237,17 +251,18 @@ create_user = #{createUser,jdbcType=VARCHAR}, source_id = #{sourceId,jdbcType=VARCHAR}, `type` = #{type,jdbcType=VARCHAR}, - `module` = #{module,jdbcType=VARCHAR} + `module` = #{module,jdbcType=VARCHAR}, + ref_id = #{refId,jdbcType=BIGINT} where id = #{id,jdbcType=BIGINT} insert into operation_history - (id, project_id, create_time, create_user, source_id, `type`, `module`) + (id, project_id, create_time, create_user, source_id, `type`, `module`, ref_id) values (#{item.id,jdbcType=BIGINT}, #{item.projectId,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT}, #{item.createUser,jdbcType=VARCHAR}, #{item.sourceId,jdbcType=VARCHAR}, #{item.type,jdbcType=VARCHAR}, - #{item.module,jdbcType=VARCHAR}) + #{item.module,jdbcType=VARCHAR}, #{item.refId,jdbcType=BIGINT}) @@ -281,6 +296,9 @@ #{item.module,jdbcType=VARCHAR} + + #{item.refId,jdbcType=BIGINT} + ) diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_2__sdk_ddl.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_2__sdk_ddl.sql index 55f9cb378c..9c3550bcb5 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_2__sdk_ddl.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_2__sdk_ddl.sql @@ -134,15 +134,15 @@ CREATE TABLE IF NOT EXISTS worker_node DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'DB WorkerID Assigner for UID Generator'; -CREATE TABLE operation_history -( - `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键' , - `project_id` VARCHAR(50) NOT NULL DEFAULT 'NONE' COMMENT '项目id', - `create_time` BIGINT NOT NULL COMMENT '操作时间', - `create_user` VARCHAR(50) COMMENT '操作人', - `source_id` VARCHAR(50) COMMENT '资源id', - `type` VARCHAR(20) NOT NULL COMMENT '操作类型/add/update/delete', - `module` VARCHAR(50) COMMENT '操作模块/api/case/scenario/ui', +CREATE TABLE IF NOT EXISTS operation_history( + `id` BIGINT(50) NOT NULL AUTO_INCREMENT COMMENT '主键' , + `project_id` VARCHAR(50) NOT NULL DEFAULT 'NONE' COMMENT '项目id' , + `create_time` BIGINT NOT NULL COMMENT '操作时间' , + `create_user` VARCHAR(50) COMMENT '操作人' , + `source_id` VARCHAR(50) COMMENT '资源id' , + `type` VARCHAR(20) NOT NULL COMMENT '操作类型/add/update/delete' , + `module` VARCHAR(50) COMMENT '操作模块/api/case/scenario/ui' , + `ref_id` BIGINT(50) COMMENT '关联id(关联变更记录id来源)' , PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 @@ -154,9 +154,6 @@ CREATE INDEX idx_module ON operation_history (`module`); CREATE INDEX idx_project_id ON operation_history (`project_id`); CREATE INDEX idx_type ON operation_history (`type`); --- set innodb lock wait timeout to default -SET SESSION innodb_lock_wait_timeout = DEFAULT; - CREATE TABLE IF NOT EXISTS share_info ( `id` VARCHAR(50) NOT NULL COMMENT '分享ID' , @@ -170,3 +167,6 @@ CREATE TABLE IF NOT EXISTS share_info ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '分享'; + +-- set innodb lock wait timeout to default +SET SESSION innodb_lock_wait_timeout = DEFAULT; diff --git a/backend/framework/sdk/src/main/resources/i18n/system.properties b/backend/framework/sdk/src/main/resources/i18n/system.properties index e312865445..9bedfad6bf 100644 --- a/backend/framework/sdk/src/main/resources/i18n/system.properties +++ b/backend/framework/sdk/src/main/resources/i18n/system.properties @@ -299,6 +299,15 @@ user_local_config.type.not_blank=用户本地配置类型不能为空 user_local_config.type.length_range=用户本地配置类型长度必须在{min}和{max}之间 current_user_local_config_not_validate=当前用户本地配置不合法 +# operation_history +operation_history.id.not_blank=变更记录 ID 不能为空 +operation_history.project_id.not_blank=变更记录项目 ID 不能为空 +operation_history.project_id.length_range=变更记录项目 ID 长度必须在{min}和{max}之间 +operation_history.type.not_blank=变更记录操作类型不能为空 +operation_history.type.length_range=变更记录操作类型长度必须在{min}和{max}之间 +operation_history.source_id.not_blank=变更记录资源 ID 不能为空 +operation_history.version_id.not_blank=变更记录版本 ID 不能为空 +operation_history.version_id.length_range=变更记录版本 ID 长度必须在{min}和{max}之间 diff --git a/backend/framework/sdk/src/main/resources/i18n/system_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/system_en_US.properties index c5ada52e3e..e99de68178 100644 --- a/backend/framework/sdk/src/main/resources/i18n/system_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/system_en_US.properties @@ -301,5 +301,13 @@ user_local_config.type.not_blank=type cannot be empty user_local_config.type.length_range=type length must be between {min} and {max} current_user_local_config_not_validate=Current user local config not validate - +# operation_history +operation_history.id.not_blank=Operating history id cannot be empty +operation_history.project_id.not_blank=Operating history project id cannot be empty +operation_history.project_id.length_range=Operating history project id length must be between {min} and {max} +operation_history.type.not_blank=Operating history type cannot be empty +operation_history.type.length_range=Operating history type length must be between {min} and {max} +operation_history.source_id.not_blank=Operating history source id cannot be empty +operation_history.version_id.not_blank=Operating history version id cannot be empty +operation_history.version_id.length_range=Operating history version id length must be between {min} and {max} diff --git a/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties index 943ab7bb90..ba7b82f5af 100644 --- a/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties @@ -300,3 +300,13 @@ user_local_config.user_url.length_range=用户本地配置用户URL长度必须 user_local_config.type.not_blank=用户本地配置类型不能为空 user_local_config.type.length_range=用户本地配置类型长度必须在{min}和{max}之间 current_user_local_config_not_validate=当前用户本地配置不合法 + +# operation_history +operation_history.id.not_blank=变更记录 ID 不能为空 +operation_history.project_id.not_blank=变更记录项目 ID 不能为空 +operation_history.project_id.length_range=变更记录项目 ID 长度必须在{min}和{max}之间 +operation_history.type.not_blank=变更记录操作类型不能为空 +operation_history.type.length_range=变更记录操作类型长度必须在{min}和{max}之间 +operation_history.source_id.not_blank=变更记录资源 ID 不能为空 +operation_history.version_id.not_blank=变更记录版本 ID 不能为空 +operation_history.version_id.length_range=变更记录版本 ID 长度必须在{min}和{max}之间 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/system_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/system_zh_TW.properties index fe9bba85bd..d31bfd195f 100644 --- a/backend/framework/sdk/src/main/resources/i18n/system_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/system_zh_TW.properties @@ -298,4 +298,14 @@ user_local_config.user_url.not_blank=用户本地配置用户URL不能為空 user_local_config.user_url.length_range=用户本地配置用户URL長度必須在{min}和{max}之间 user_local_config.type.not_blank=用户本地配置类型不能為空 user_local_config.type.length_range=用户本地配置类型長度必須在{min}和{max}之间 -current_user_local_config_not_validate=当前用户本地配置不合法 \ No newline at end of file +current_user_local_config_not_validate=当前用户本地配置不合法 + +# operation_history +operation_history.id.not_blank=變更記錄 ID 不能為空 +operation_history.project_id.not_blank=變更記錄項目 ID 不能為空 +operation_history.project_id.length_range=變更記錄項目 ID 長度必須在{min}和{max}之間 +operation_history.type.not_blank=變更記錄操作類型不能為空 +operation_history.type.length_range=變更記錄操作類型長度必須在{min}和{max}之間 +operation_history.source_id.not_blank=變更記錄資源 ID 不能為空 +operation_history.version_id.not_blank=變更記錄版本 ID 不能為空 +operation_history.version_id.length_range=變更記錄版本 ID 長度必須在{min}和{max}之間 diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java index 5cfa8cc770..8f48e43901 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java @@ -9,6 +9,9 @@ import io.metersphere.api.dto.request.ImportRequest; import io.metersphere.api.service.definition.ApiDefinitionLogService; import io.metersphere.api.service.definition.ApiDefinitionService; import io.metersphere.sdk.constants.PermissionConstants; +import io.metersphere.system.dto.OperationHistoryDTO; +import io.metersphere.system.dto.request.OperationHistoryRequest; +import io.metersphere.system.dto.request.OperationHistoryVersionRequest; import io.metersphere.system.log.annotation.Log; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.security.CheckOwner; @@ -202,4 +205,31 @@ public class ApiDefinitionController { return apiDefinitionService.apiTestImport(file, request); } + @PostMapping("/operation-history") + @Operation(summary = "接口测试-接口管理-接口变更历史") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ) + @CheckOwner(resourceId = "#request.getSourceId()", resourceType = "api_definition") + public Pager> operationHistoryList(@Validated @RequestBody OperationHistoryRequest request) { + Page page = PageHelper.startPage(request.getCurrent(), request.getPageSize(), + StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "create_time desc"); + return PageUtils.setPageInfo(page, apiDefinitionService.list(request)); + } + + @PostMapping("/operation-history/recover") + @Operation(summary = "接口测试-接口管理-接口变更历史恢复") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_UPDATE) + @CheckOwner(resourceId = "#request.getId()", resourceType = "operation_history") + public void operationHistoryRecover(@Validated @RequestBody OperationHistoryVersionRequest request) { + apiDefinitionService.recoverOperationHistory(request); + } + + @PostMapping("/operation-history/save") + @Operation(summary = "接口测试-接口管理-另存变更历史为指定版本") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_UPDATE) + @CheckOwner(resourceId = "#request.getId()", resourceType = "operation_history") + public void saveOperationHistory(@Validated @RequestBody OperationHistoryVersionRequest request) { + apiDefinitionService.saveOperationHistory(request); + } + + } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionVersionDTO.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionVersionDTO.java index 6d6954c0be..d362ee3dc3 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionVersionDTO.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionVersionDTO.java @@ -20,12 +20,15 @@ public class ApiDefinitionVersionDTO implements Serializable { @Schema(description = "接口ID") private String id; - @Schema(description = "名称") + @Schema(description = "接口名称") private String name; @Schema(description = "版本id") private String versionId; + @Schema(description = "版本name") + private String versionName; + @Schema(description = "版本引用id") private String refId; diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.java b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.java index 6890d9fc62..40b59e624d 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.java @@ -3,6 +3,7 @@ package io.metersphere.api.mapper; import io.metersphere.api.domain.ApiDefinition; import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.definition.importdto.ApiDefinitionImportDTO; +import io.metersphere.system.dto.sdk.OptionDTO; import io.metersphere.system.dto.table.TableBatchProcessDTO; import org.apache.ibatis.annotations.Param; @@ -46,4 +47,8 @@ public interface ExtApiDefinitionMapper { List selectIdsByIdsAndDeleted(@Param("ids") List ids, @Param("deleted") boolean deleted); List selectByProjectId(@Param("projectId") String projectId); + + List selectVersionOptionByIds(@Param("apiIds") List apiIds); + + ApiDefinition selectApiDefinitionByVersion(@Param("refId") String refId, @Param("versionId") String versionId); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.xml index 31d4f64a28..d8fe8a74cb 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.xml @@ -118,13 +118,15 @@ + select + api_definition.id, project_version.name as name + from api_definition + LEFT JOIN project_version ON project_version.id = api_definition.version_id + where api_definition.ref_id in + + #{id} + + + + diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionLogService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionLogService.java index 2a67d1685c..12fa706b6d 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionLogService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionLogService.java @@ -59,7 +59,7 @@ public class ApiDefinitionLogService { OperationLogType.ADD.name(), OperationLogModule.API_DEFINITION, request.getName()); - dto.setHistory(true); + dto.setHistory(false); dto.setPath("/api/definition/add"); dto.setMethod(HttpMethodConstants.POST.name()); dto.setOriginalValue(JSON.toJSONBytes(request)); @@ -74,7 +74,7 @@ public class ApiDefinitionLogService { */ public LogDTO updateLog(ApiDefinitionUpdateRequest request) { ApiDefinitionDTO apiDefinition = getOriginalValue(request.getId()); - if(apiDefinition.getId() != null){ + if(apiDefinition.getId() != null) { LogDTO dto = new LogDTO( request.getProjectId(), null, @@ -100,7 +100,7 @@ public class ApiDefinitionLogService { */ public LogDTO delLog(ApiDefinitionDeleteRequest request) { ApiDefinitionDTO apiDefinition = getOriginalValue(request.getId()); - if(apiDefinition.getId() != null){ + if(apiDefinition.getId() != null) { LogDTO dto = new LogDTO( request.getProjectId(), null, @@ -109,7 +109,7 @@ public class ApiDefinitionLogService { OperationLogType.DELETE.name(), OperationLogModule.API_DEFINITION, apiDefinition.getName()); - dto.setHistory(true); + dto.setHistory(false); dto.setPath("/api/definition/delete"); dto.setMethod(HttpMethodConstants.POST.name()); dto.setOriginalValue(JSON.toJSONBytes(apiDefinition)); @@ -125,7 +125,7 @@ public class ApiDefinitionLogService { * @return */ public void batchDelLog(List ids, String userId, String projectId) { - saveBatchLog(projectId, ids, "/api/definition/batch-del", userId, OperationLogType.DELETE.name(), true); + saveBatchLog(projectId, ids, "/api/definition/batch-del", userId, OperationLogType.DELETE.name(), false); } /** @@ -139,7 +139,7 @@ public class ApiDefinitionLogService { public LogDTO copyLog(ApiDefinitionCopyRequest request) { ApiDefinitionDTO apiDefinition = getOriginalValue(request.getId()); - if(apiDefinition.getId() != null){ + if(apiDefinition.getId() != null) { LogDTO dto = new LogDTO( apiDefinition.getProjectId(), null, @@ -148,7 +148,7 @@ public class ApiDefinitionLogService { OperationLogType.UPDATE.name(), OperationLogModule.API_DEFINITION, apiDefinition.getName()); - dto.setHistory(true); + dto.setHistory(false); dto.setPath("/api/definition/copy"); dto.setMethod(HttpMethodConstants.POST.name()); dto.setOriginalValue(JSON.toJSONBytes(apiDefinition)); @@ -158,12 +158,12 @@ public class ApiDefinitionLogService { } public void batchMoveLog(List ids, String userId, String projectId) { - saveBatchLog(projectId, ids, "/api/definition/batch-move", userId, OperationLogType.UPDATE.name(), true); + saveBatchLog(projectId, ids, "/api/definition/batch-move", userId, OperationLogType.UPDATE.name(), false); } public LogDTO followLog(String id) { ApiDefinitionDTO apiDefinition = getOriginalValue(id); - if(apiDefinition.getId() != null){ + if(apiDefinition.getId() != null) { Project project = projectMapper.selectByPrimaryKey(apiDefinition.getProjectId()); LogDTO dto = new LogDTO( apiDefinition.getProjectId(), @@ -190,7 +190,7 @@ public class ApiDefinitionLogService { */ public LogDTO recoverLog(ApiDefinitionDeleteRequest request) { ApiDefinitionDTO apiDefinition = getOriginalValue(request.getId()); - if(apiDefinition.getId() != null){ + if(apiDefinition.getId() != null) { LogDTO dto = new LogDTO( request.getProjectId(), null, @@ -199,7 +199,7 @@ public class ApiDefinitionLogService { OperationLogType.RECOVER.name(), OperationLogModule.API_DEFINITION, apiDefinition.getName()); - dto.setHistory(true); + dto.setHistory(false); dto.setPath("/api/definition/recover"); dto.setMethod(HttpMethodConstants.POST.name()); dto.setOriginalValue(JSON.toJSONBytes(apiDefinition)); @@ -216,7 +216,7 @@ public class ApiDefinitionLogService { * @return */ public void batchRecoverLog(List ids, String userId, String projectId) { - saveBatchLog(projectId, ids, "/api/definition/batch-recover", userId, OperationLogType.RECOVER.name(), true); + saveBatchLog(projectId, ids, "/api/definition/batch-recover", userId, OperationLogType.RECOVER.name(), false); } @@ -225,7 +225,7 @@ public class ApiDefinitionLogService { */ public LogDTO trashDelLog(ApiDefinitionDeleteRequest request) { ApiDefinitionDTO apiDefinition = getOriginalValue(request.getId()); - if(apiDefinition.getId() != null){ + if(apiDefinition.getId() != null) { LogDTO dto = new LogDTO( request.getProjectId(), null, @@ -248,13 +248,13 @@ public class ApiDefinitionLogService { * 删除回收站接口定义接口日志 */ public void batchTrashDelLog(List ids, String userId, String projectId) { - saveBatchLog(projectId, ids, "/api/definition/batch-trash-del", userId, OperationLogType.DELETE.name(), true); + saveBatchLog(projectId, ids, "/api/definition/batch-trash-del", userId, OperationLogType.DELETE.name(), false); } - private ApiDefinitionDTO getOriginalValue(String id){ + private ApiDefinitionDTO getOriginalValue(String id) { ApiDefinitionDTO apiDefinitionDTO = new ApiDefinitionDTO(); ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(id); - if(null != apiDefinition){ + if(null != apiDefinition) { // 2. 使用Optional避免空指针异常 handleBlob(id, apiDefinitionDTO); BeanUtils.copyBean(apiDefinitionDTO, apiDefinition); @@ -303,5 +303,41 @@ public class ApiDefinitionLogService { } } + public Long saveOperationHistoryLog(ApiDefinitionDTO apiDefinitionDTO, String userId, String projectId) { + Project project = projectMapper.selectByPrimaryKey(projectId); + LogDTO dto = new LogDTO( + project.getId(), + project.getOrganizationId(), + apiDefinitionDTO.getId(), + userId, + OperationLogType.UPDATE.name(), + OperationLogModule.API_DEFINITION, + apiDefinitionDTO.getName()); + dto.setHistory(true); + dto.setPath("/api/definition/operation-history/save"); + dto.setMethod(HttpMethodConstants.POST.name()); + dto.setOriginalValue(JSON.toJSONBytes(apiDefinitionDTO)); + operationLogService.add(dto); + return dto.getId(); + } + + public Long recoverOperationHistoryLog(ApiDefinitionDTO apiDefinitionDTO, String userId, String projectId) { + Project project = projectMapper.selectByPrimaryKey(projectId); + LogDTO dto = new LogDTO( + project.getId(), + project.getOrganizationId(), + apiDefinitionDTO.getId(), + userId, + OperationLogType.RECOVER.name(), + OperationLogModule.API_DEFINITION, + apiDefinitionDTO.getName()); + dto.setHistory(true); + dto.setPath("/api/definition/operation-history/recover"); + dto.setMethod(HttpMethodConstants.POST.name()); + dto.setOriginalValue(JSON.toJSONBytes(apiDefinitionDTO)); + operationLogService.add(dto); + return dto.getId(); + } + } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java index 47904710d6..2bffa9b732 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java @@ -22,10 +22,17 @@ import io.metersphere.sdk.constants.ApiReportStatus; import io.metersphere.sdk.constants.ApplicationNumScope; import io.metersphere.sdk.constants.DefaultRepositoryDir; import io.metersphere.sdk.constants.ModuleConstants; +import io.metersphere.sdk.domain.OperationLogBlob; import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.mapper.OperationLogBlobMapper; import io.metersphere.sdk.util.*; +import io.metersphere.system.dto.OperationHistoryDTO; +import io.metersphere.system.dto.request.OperationHistoryRequest; +import io.metersphere.system.dto.request.OperationHistoryVersionRequest; +import io.metersphere.system.dto.sdk.OptionDTO; import io.metersphere.system.dto.table.TableBatchProcessDTO; import io.metersphere.system.log.constants.OperationLogModule; +import io.metersphere.system.service.OperationHistoryService; import io.metersphere.system.service.UserLoginService; import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.NumGenerator; @@ -42,6 +49,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -107,6 +115,12 @@ public class ApiDefinitionService { @Resource private ApiDefinitionMockService apiDefinitionMockService; + @Resource + private OperationHistoryService operationHistoryService; + + @Resource + private OperationLogBlobMapper operationLogBlobMapper; + public List getApiDefinitionPage(ApiDefinitionPageRequest request, String userId) { CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(request, userId); List list = extApiDefinitionMapper.list(request); @@ -910,4 +924,84 @@ public class ApiDefinitionService { } return apiImport; } + public List list(OperationHistoryRequest request) { + List operationHistoryList = operationHistoryService.list(request); + if (CollectionUtils.isNotEmpty(operationHistoryList)) { + List apiIds = operationHistoryList.stream() + .map(OperationHistoryDTO::getSourceId).toList(); + + Map apiMap = extApiDefinitionMapper.selectVersionOptionByIds(apiIds).stream() + .collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName)); + + operationHistoryList.forEach(item -> item.setVersionName(apiMap.getOrDefault(item.getSourceId(), StringUtils.EMPTY))); + } + + return operationHistoryList; + } + + /** + * 是否存在所选版本的接口定义,不存在则创建,同时创建日志记录 + */ + public void saveOperationHistory(OperationHistoryVersionRequest request) { + ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample(); + apiDefinitionExample.createCriteria().andRefIdEqualTo(request.getSourceId()).andVersionIdEqualTo(request.getVersionId()); + List matchingApiDefinitions = apiDefinitionMapper.selectByExample(apiDefinitionExample); + ApiDefinition apiDefinition = matchingApiDefinitions.stream().findFirst().orElseGet(ApiDefinition::new); + ApiDefinitionBlob copyApiDefinitionBlob = getApiDefinitionBlob(request.getSourceId()); + if (apiDefinition.getId() == null) { + ApiDefinition copyApiDefinition = apiDefinitionMapper.selectByPrimaryKey(request.getSourceId()); + BeanUtils.copyBean(apiDefinition, copyApiDefinition); + apiDefinition.setId(IDGenerator.nextStr()); + apiDefinition.setRefId(request.getSourceId()); + apiDefinition.setVersionId(request.getVersionId()); + apiDefinition.setCreateTime(System.currentTimeMillis()); + apiDefinition.setUpdateTime(System.currentTimeMillis()); + apiDefinitionMapper.insertSelective(apiDefinition); + + ApiDefinitionBlob apiDefinitionBlob = new ApiDefinitionBlob(); + if(copyApiDefinitionBlob != null) { + apiDefinitionBlob.setId(apiDefinition.getId()); + apiDefinitionBlob.setRequest(copyApiDefinitionBlob.getRequest()); + apiDefinitionBlob.setResponse(copyApiDefinitionBlob.getResponse()); + apiDefinitionBlobMapper.insertSelective(apiDefinitionBlob); + } + } + ApiDefinitionDTO apiDefinitionDTO = new ApiDefinitionDTO(); + BeanUtils.copyBean(apiDefinitionDTO, apiDefinition); + Optional.ofNullable(copyApiDefinitionBlob) + .map(blob -> new String(blob.getRequest())) + .ifPresent(requestBlob -> apiDefinitionDTO.setRequest(ApiDataUtils.parseObject(requestBlob, AbstractMsTestElement.class))); + + Optional.ofNullable(copyApiDefinitionBlob) + .map(blob -> new String(blob.getResponse())) + .ifPresent(responseBlob -> apiDefinitionDTO.setResponse(ApiDataUtils.parseArray(responseBlob, HttpResponse.class))); + + Long logId = apiDefinitionLogService.saveOperationHistoryLog(apiDefinitionDTO, apiDefinition.getCreateUser(), apiDefinition.getProjectId()); + operationHistoryService.associationRefId(request.getId(), logId); + } + + + + public void recoverOperationHistory(OperationHistoryVersionRequest request) { + OperationLogBlob operationLogBlob = operationLogBlobMapper.selectByPrimaryKey(request.getId()); + ApiDefinitionDTO apiDefinitionDTO = ApiDataUtils.parseObject(new String(operationLogBlob.getOriginalValue()), ApiDefinitionDTO.class); + Long logId = recoverApiDefinition(apiDefinitionDTO); + operationHistoryService.associationRefId(operationLogBlob.getId(), logId); + } + + public Long recoverApiDefinition(ApiDefinitionDTO apiDefinitionDTO) { + ApiDefinition apiDefinition = new ApiDefinition(); + BeanUtils.copyBean(apiDefinition, apiDefinitionDTO); + apiDefinition.setUpdateTime(System.currentTimeMillis()); + apiDefinitionMapper.updateByPrimaryKeySelective(apiDefinition); + + ApiDefinitionBlob apiDefinitionBlob = new ApiDefinitionBlob(); + apiDefinitionBlob.setId(apiDefinition.getId()); + apiDefinitionBlob.setRequest(ApiDataUtils.toJSONString(apiDefinitionDTO.getRequest()).getBytes(StandardCharsets.UTF_8)); + apiDefinitionBlob.setResponse(ApiDataUtils.toJSONString(apiDefinitionDTO.getResponse()).getBytes(StandardCharsets.UTF_8)); + apiDefinitionBlobMapper.updateByPrimaryKeySelective(apiDefinitionBlob); + + // 记录操作日志 + return apiDefinitionLogService.recoverOperationHistoryLog(apiDefinitionDTO, apiDefinition.getCreateUser(), apiDefinition.getProjectId()); + } } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java index 60e9378554..d9563d6e47 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionControllerTests.java @@ -23,12 +23,19 @@ import io.metersphere.sdk.constants.SessionConstants; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.file.FileCenter; import io.metersphere.sdk.file.FileRequest; +import io.metersphere.sdk.mapper.OperationLogBlobMapper; +import io.metersphere.sdk.mapper.OperationLogMapper; import io.metersphere.sdk.util.*; import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.controller.handler.result.MsHttpResultCode; +import io.metersphere.system.domain.OperationHistory; +import io.metersphere.system.domain.OperationHistoryExample; +import io.metersphere.system.dto.request.OperationHistoryRequest; +import io.metersphere.system.dto.request.OperationHistoryVersionRequest; import io.metersphere.system.dto.sdk.BaseCondition; import io.metersphere.system.log.constants.OperationLogType; +import io.metersphere.system.mapper.OperationHistoryMapper; import io.metersphere.system.utils.Pager; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; @@ -85,6 +92,9 @@ public class ApiDefinitionControllerTests extends BaseTest { private static final String GET = BASE_PATH + "get-detail/"; private static final String FOLLOW = BASE_PATH + "follow/"; private static final String VERSION = BASE_PATH + "version/"; + private static final String OPERATION_HISTORY = BASE_PATH + "operation-history"; + private static final String OPERATION_HISTORY_RECOVER = BASE_PATH + "operation-history/recover"; + private static final String OPERATION_HISTORY_SAVE = BASE_PATH + "operation-history/save"; private static final String UPLOAD_TEMP_FILE = BASE_PATH + "/upload/temp/file"; private static final String DEFAULT_MODULE_ID = "10001"; @@ -119,6 +129,15 @@ public class ApiDefinitionControllerTests extends BaseTest { @Resource private ExtApiDefinitionCustomFieldMapper extApiDefinitionCustomFieldMapper; + @Resource + private OperationHistoryMapper operationHistoryMapper; + + @Resource + private OperationLogMapper operationLogMapper; + + @Resource + private OperationLogBlobMapper operationLogBlobMapper; + @Resource private FileMetadataService fileMetadataService; private static String fileMetadataId; @@ -895,6 +914,88 @@ public class ApiDefinitionControllerTests extends BaseTest { @Test @Order(12) + public void testOperationHistoryList() throws Exception { + OperationHistoryRequest request = new OperationHistoryRequest(); + apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001"); + request.setSourceId(apiDefinition.getId()); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setCurrent(1); + request.setPageSize(10); + request.setSort(Map.of("createTime", "asc")); + + MvcResult mvcResult = this.requestPostWithOkAndReturn(OPERATION_HISTORY, 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()); + request.setSort(Map.of()); + this.requestPost(OPERATION_HISTORY, request); + + } + + @Test + @Order(12) + public void testOperationHistorySave() throws Exception { + OperationHistoryVersionRequest operationHistoryVersionRequest = new OperationHistoryVersionRequest(); + apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1002"); + OperationHistoryExample operationHistoryExample = new OperationHistoryExample(); + operationHistoryExample.createCriteria().andSourceIdEqualTo(apiDefinition.getId()); + operationHistoryExample.setOrderByClause("id DESC"); + OperationHistory operationHistory = operationHistoryMapper.selectByExample(operationHistoryExample).getFirst(); + operationHistoryVersionRequest.setId(operationHistory.getId()); + operationHistoryVersionRequest.setSourceId(apiDefinition.getId()); + operationHistoryVersionRequest.setVersionId(apiDefinition.getVersionId()); + this.requestPostWithOkAndReturn(OPERATION_HISTORY_SAVE, operationHistoryVersionRequest); + checkLogModelList.add(new CheckLogModel(apiDefinition.getId(), OperationLogType.UPDATE, OPERATION_HISTORY_SAVE)); + OperationHistoryExample comparisonExample = new OperationHistoryExample(); + comparisonExample.createCriteria().andSourceIdEqualTo(apiDefinition.getId()).andRefIdEqualTo(operationHistory.getId()).andTypeEqualTo(OperationLogType.UPDATE.name()); + comparisonExample.setOrderByClause("id DESC"); + OperationHistory comparison = operationHistoryMapper.selectByExample(operationHistoryExample).getFirst(); + Assertions.assertNotNull(comparison); + + operationHistoryVersionRequest.setId(operationHistory.getId()); + operationHistoryVersionRequest.setSourceId("1002"); + operationHistoryVersionRequest.setVersionId("1002002002"); + this.requestPostWithOkAndReturn(OPERATION_HISTORY_SAVE, operationHistoryVersionRequest); + checkLogModelList.add(new CheckLogModel(apiDefinition.getId(), OperationLogType.UPDATE, OPERATION_HISTORY_SAVE)); + OperationHistoryExample comparisonExampleNewVersion = new OperationHistoryExample(); + comparisonExampleNewVersion.createCriteria().andSourceIdEqualTo(apiDefinition.getId()).andRefIdEqualTo(operationHistory.getId()).andTypeEqualTo(OperationLogType.UPDATE.name()); + comparisonExampleNewVersion.setOrderByClause("id DESC"); + OperationHistory comparisonNewVersion = operationHistoryMapper.selectByExample(operationHistoryExample).getFirst(); + Assertions.assertNotNull(comparisonNewVersion); + } + + @Test + @Order(13) + public void testOperationHistoryRecover() throws Exception { + OperationHistoryVersionRequest operationHistoryVersionRequest = new OperationHistoryVersionRequest(); + apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1002"); + OperationHistoryExample operationHistoryExample = new OperationHistoryExample(); + operationHistoryExample.createCriteria().andSourceIdEqualTo(apiDefinition.getId()); + operationHistoryExample.setOrderByClause("id DESC"); + OperationHistory operationHistory = operationHistoryMapper.selectByExample(operationHistoryExample).getFirst(); + operationHistoryVersionRequest.setId(operationHistory.getId()); + operationHistoryVersionRequest.setSourceId(apiDefinition.getId()); + operationHistoryVersionRequest.setVersionId(apiDefinition.getVersionId()); + this.requestPostWithOkAndReturn(OPERATION_HISTORY_RECOVER, operationHistoryVersionRequest); + checkLogModelList.add(new CheckLogModel(apiDefinition.getId(), OperationLogType.RECOVER, OPERATION_HISTORY_RECOVER)); + OperationHistoryExample comparisonExample = new OperationHistoryExample(); + comparisonExample.createCriteria().andSourceIdEqualTo(apiDefinition.getId()).andRefIdEqualTo(operationHistory.getId()).andTypeEqualTo(OperationLogType.RECOVER.name()); + comparisonExample.setOrderByClause("id DESC"); + OperationHistory comparison = operationHistoryMapper.selectByExample(operationHistoryExample).getFirst(); + Assertions.assertNotNull(comparison); + } + + @Test + @Order(14) public void testDel() throws Exception { LogUtils.info("delete api test"); apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001"); @@ -968,7 +1069,7 @@ public class ApiDefinitionControllerTests extends BaseTest { } @Test - @Order(13) + @Order(15) public void testBatchDel() throws Exception { LogUtils.info("batch delete api test"); ApiDefinitionBatchRequest request = new ApiDefinitionBatchRequest(); @@ -1002,7 +1103,7 @@ public class ApiDefinitionControllerTests extends BaseTest { @Test - @Order(14) + @Order(16) public void testRecover() throws Exception { LogUtils.info("recover api test"); apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001"); @@ -1047,7 +1148,7 @@ public class ApiDefinitionControllerTests extends BaseTest { } @Test - @Order(15) + @Order(20) public void testBatchRecover() throws Exception { LogUtils.info("batch recover api test"); ApiDefinitionBatchRequest request = new ApiDefinitionBatchRequest(); @@ -1095,7 +1196,7 @@ public class ApiDefinitionControllerTests extends BaseTest { } @Test - @Order(16) + @Order(21) public void testTrashDel() throws Exception { LogUtils.info("trashDel api test"); apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001"); @@ -1137,7 +1238,7 @@ public class ApiDefinitionControllerTests extends BaseTest { } @Test - @Order(17) + @Order(25) public void testBatchTrashDel() throws Exception { LogUtils.info("batch trash delete api test"); ApiDefinitionBatchRequest request = new ApiDefinitionBatchRequest(); diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/OperationHistoryDTO.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/OperationHistoryDTO.java new file mode 100644 index 0000000000..401afdb0a4 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/OperationHistoryDTO.java @@ -0,0 +1,26 @@ +package io.metersphere.system.dto; + +import io.metersphere.system.domain.OperationHistory; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author: LAN + * @date: 2024/1/2 19:03 + * @version: 1.0 + */ +@Data +public class OperationHistoryDTO extends OperationHistory implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + // 操作人 + private String createUserName; + + // 版本 + private String versionName; + +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/request/OperationHistoryRequest.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/request/OperationHistoryRequest.java new file mode 100644 index 0000000000..07f3ca1da8 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/request/OperationHistoryRequest.java @@ -0,0 +1,33 @@ +package io.metersphere.system.dto.request; + +import io.metersphere.system.dto.sdk.BasePageRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = false) +public class OperationHistoryRequest extends BasePageRequest { + + @Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{operation_history.project_id.not_blank}") + @Size(min = 1, max = 50, message = "{operation_history.project_id.length_range}") + private String projectId; + + @Schema(description = "资源id", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{operation_history.source_id.not_blank}") + private String sourceId; + + @Schema(description = "操作人") + private String createUser; + + @Schema(description = "操作类型") + private String type; + + @Schema(description = "操作模块") + private String module; + + +} diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/request/OperationHistoryVersionRequest.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/request/OperationHistoryVersionRequest.java new file mode 100644 index 0000000000..329a95a9fd --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/request/OperationHistoryVersionRequest.java @@ -0,0 +1,39 @@ +package io.metersphere.system.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author: LAN + * @date: 2024/1/3 16:40 + * @version: 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class OperationHistoryVersionRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "变更记录id") + @NotNull(message = "{operation_history.id.not_blank}") + private Long id; + + @Schema(description = "资源id(当前变更记录的资源id)") + @NotBlank(message = "{operation_history.source_id.not_blank}") + private String sourceId; + + @Schema(description = "版本id") + @NotBlank(message = "{operation_history.version_id.not_blank}") + @Size(min = 1, max = 50, message = "{operation_history.version_id.length_range}") + private String versionId; + +} + diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/BaseOperationHistoryMapper.java b/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/BaseOperationHistoryMapper.java index 1e2728e0e7..3f3477314f 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/BaseOperationHistoryMapper.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/BaseOperationHistoryMapper.java @@ -1,6 +1,8 @@ package io.metersphere.system.mapper; +import io.metersphere.system.dto.OperationHistoryDTO; +import io.metersphere.system.dto.request.OperationHistoryRequest; import org.apache.ibatis.annotations.Param; import java.util.List; @@ -12,4 +14,6 @@ public interface BaseOperationHistoryMapper { List selectIdsBySourceId(@Param("sourceId") String sourceId, @Param("limit") int limit); void deleteByIds(@Param("sourceId") String sourceId, @Param("ids") List ids); + + List list(@Param("request") OperationHistoryRequest request); } \ No newline at end of file diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/BaseOperationHistoryMapper.xml b/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/BaseOperationHistoryMapper.xml index 87a65d4901..b968992091 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/BaseOperationHistoryMapper.xml +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/BaseOperationHistoryMapper.xml @@ -18,4 +18,26 @@ #{id} + + diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/OperationHistoryService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/OperationHistoryService.java new file mode 100644 index 0000000000..b3d53d3d35 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/OperationHistoryService.java @@ -0,0 +1,55 @@ +package io.metersphere.system.service; + +import io.metersphere.system.domain.OperationHistory; +import io.metersphere.system.dto.OperationHistoryDTO; +import io.metersphere.system.dto.request.OperationHistoryRequest; +import io.metersphere.system.dto.sdk.OptionDTO; +import io.metersphere.system.mapper.BaseOperationHistoryMapper; +import io.metersphere.system.mapper.BaseUserMapper; +import io.metersphere.system.mapper.OperationHistoryMapper; +import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class OperationHistoryService { + @Resource + private BaseOperationHistoryMapper baseOperationHistoryMapper; + + @Resource + private BaseUserMapper baseUserMapper; + + @Resource + private OperationHistoryMapper operationHistoryMapper; + + + public List list(OperationHistoryRequest request) { + List list = baseOperationHistoryMapper.list(request); + + if (CollectionUtils.isNotEmpty(list)) { + List userIds = list.stream().distinct() + .map(OperationHistoryDTO::getCreateUser).toList(); + + Map userMap = baseUserMapper.selectUserOptionByIds(userIds).stream() + .collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName)); + + list.forEach(item -> item.setCreateUserName(userMap.getOrDefault(item.getCreateUser(), StringUtils.EMPTY))); + + } + return list; + } + + public void associationRefId(Long refLogId, Long logId) { + OperationHistory operationHistory = operationHistoryMapper.selectByPrimaryKey(logId); + operationHistory.setRefId(refLogId); + operationHistoryMapper.updateByPrimaryKeySelective(operationHistory); + } + +}