This is an automated email from the ASF dual-hosted git repository.
xiaoyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 7ce81a132 [ISSUE #3221] record operation logs in admin (#3328)
7ce81a132 is described below
commit 7ce81a13257a9685b388924ab60a963fd3bcc49e
Author: likeguo <[email protected]>
AuthorDate: Fri Apr 29 14:55:39 2022 +0800
[ISSUE #3221] record operation logs in admin (#3328)
* rule and selector page list api
* rule and selector page list api
* feature/record-log
* feature/record-log
* feature/record-log
* feature/record-log:add upgrade and pg support
* feature/record-log:add upgrade and pg support
* fixbug/pg script error
* fixbug/pg script error
---
script/2.4.3-upgrade-2.5.0-mysql.sql | 16 +-
script/2.4.3-upgrade-2.5.0-pg.sql | 26 ++-
...-debug-operation-record-log-controller-api.http | 25 +++
.../config/properties/DashboardProperties.java | 54 ++++++
.../controller/OperationRecordLogController.java | 54 ++++++
.../RecordLogDataChangedAdapterListener.java | 53 ++++++
.../admin/mapper/OperationRecordLogMapper.java | 47 ++++++
.../admin/model/entity/OperationRecordLog.java | 179 ++++++++++++++++++++
.../shenyu/admin/model/enums/EventTypeEnum.java | 124 ++++++++++++++
.../model/event/AdminDataModelChangedEvent.java | 188 +++++++++++++++++++++
.../admin/model/event/BatchChangedEvent.java | 68 ++++++++
.../admin/model/event/BatchPluginChangedEvent.java | 57 +++++++
.../admin/model/event/PluginChangedEvent.java | 84 +++++++++
.../admin/service/OperationRecordLogService.java | 35 ++++
.../impl/OperationRecordLogServiceImpl.java | 48 ++++++
.../admin/service/impl/PluginServiceImpl.java | 38 ++---
.../AdminDataModelChangedEventPublisher.java | 95 +++++++++++
.../service/publish/PluginEventPublisher.java | 119 +++++++++++++
.../org/apache/shenyu/admin/utils/SessionUtil.java | 92 ++++++++++
.../mappers/operation-record-log-sqlmap.xml | 57 +++++++
.../src/main/resources/sql-script/h2/schema.sql | 12 ++
.../src/main/resources/sql-script/mysql/schema.sql | 12 ++
.../src/main/resources/sql-script/pg/schema.sql | 29 ++++
.../shenyu/admin/service/PluginServiceTest.java | 8 +-
24 files changed, 1493 insertions(+), 27 deletions(-)
diff --git a/script/2.4.3-upgrade-2.5.0-mysql.sql
b/script/2.4.3-upgrade-2.5.0-mysql.sql
index 7a3fadb3c..e4b59ba2b 100644
--- a/script/2.4.3-upgrade-2.5.0-mysql.sql
+++ b/script/2.4.3-upgrade-2.5.0-mysql.sql
@@ -37,4 +37,18 @@ INSERT IGNORE INTO shenyu_dict (`id`, `type`, `dict_code`,
`dict_name`, `dict_va
INSERT IGNORE INTO shenyu_dict (`id`, `type`, `dict_code`, `dict_name`,
`dict_value`, `desc`, `sort`, `enabled`) VALUES ('1516043495265869824',
'operator', 'OPERATOR', 'endsWith', 'endsWith', 'endsWith', 8, 1);
-- refactor logging name
-UPDATE plugin SET name = 'LoggingConsole' WHERE name = 'logging';
\ No newline at end of file
+UPDATE plugin SET name = 'LoggingConsole' WHERE name = 'logging';
+
+-- new table operation_record_log
+-- ----------------------------
+-- Table structure for operation_record_log
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `operation_record_log`
+(
+ `id` bigint auto_increment not null comment 'id'
primary key,
+ `color` varchar(20) not null comment 'log color',
+ `context` text not null comment 'log
context',
+ `operator` varchar(200) not null comment 'operator
[user or app]]',
+ `operation_time` datetime default now() not null comment 'operation
time',
+ `operation_type` varchar(60) default 'update' not null comment 'operation
type:create/update/delete/register...'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
ROW_FORMAT=DYNAMIC comment 'operation record log';
diff --git a/script/2.4.3-upgrade-2.5.0-pg.sql
b/script/2.4.3-upgrade-2.5.0-pg.sql
index 14eb23462..4997f1088 100644
--- a/script/2.4.3-upgrade-2.5.0-pg.sql
+++ b/script/2.4.3-upgrade-2.5.0-pg.sql
@@ -37,4 +37,28 @@ INSERT INTO shenyu_dict ("id", "type", "dict_code",
"dict_name", "dict_value", "
INSERT INTO shenyu_dict ("id", "type", "dict_code", "dict_name", "dict_value",
"desc", "sort", "enabled") VALUES ('1516043495265869824', 'operator',
'OPERATOR', 'endsWith', 'endsWith', 'endsWith', 8, 1);
-- refactor logging name
-UPDATE plugin SET name = 'LoggingConsole' WHERE name = 'logging';
+UPDATE plugin SET name = "LoggingConsole" WHERE name = "logging";
+
+-- new table operation_record_log
+-- ----------------------------
+-- Table structure for operation_record_log
+-- ----------------------------
+CREATE TABLE "operation_record_log"
+(
+ "id" int8 NOT NULL,
+ "color" varchar(20) COLLATE "pg_catalog"."default" NOT NULL,
+ "context" text COLLATE "pg_catalog"."default" NOT NULL,
+ "operator" varchar(200) COLLATE "pg_catalog"."default" NOT NULL,
+ "operation_time" timestamp(6) NOT NULL,
+ "operation_type" varchar(60) COLLATE "pg_catalog"."default" NOT NULL,
+ CONSTRAINT "operation_record_log_pkey" PRIMARY KEY ("id")
+)
+;
+
+COMMENT ON COLUMN "operation_record_log"."id" IS 'id';
+COMMENT ON COLUMN "operation_record_log"."color" IS 'log color';
+COMMENT ON COLUMN "operation_record_log"."context" IS 'log context';
+COMMENT ON COLUMN "operation_record_log"."operator" IS 'operator [user or
app]]';
+COMMENT ON COLUMN "operation_record_log"."operation_time" IS 'operation time';
+COMMENT ON COLUMN "operation_record_log"."operation_type" IS 'operation
type:create/update/delete/register...';
+COMMENT ON TABLE "operation_record_log" IS 'operation record log';
diff --git
a/shenyu-admin/src/http/http-debug-operation-record-log-controller-api.http
b/shenyu-admin/src/http/http-debug-operation-record-log-controller-api.http
new file mode 100644
index 000000000..9ea6e181f
--- /dev/null
+++ b/shenyu-admin/src/http/http-debug-operation-record-log-controller-api.http
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# if you debug api, replace your own token
+
+### list
+GET http://localhost:9095/operation-record/log/list
+Accept: application/json
+Content-Type: application/json
+X-Access-Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjUwNzg3NTY4fQ.YdxPSutcRClyuj76nYhwHJWkkkMzFVZeBfv5V04ybYA
+
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/DashboardProperties.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/DashboardProperties.java
new file mode 100644
index 000000000..13386868b
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/DashboardProperties.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.config.properties;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * admin dashboard properties.
+ */
+@Configuration
+@ConfigurationProperties(prefix = "shenyu.dashboard.core")
+public class DashboardProperties {
+
+ /**
+ * record log limit.
+ */
+ @Value("${shenyu.dashboard.core.record-log-limit:12}")
+ private Integer recordLogLimit;
+
+ /**
+ * get recordLogLimit.
+ *
+ * @return limit
+ */
+ public Integer getRecordLogLimit() {
+ return recordLogLimit;
+ }
+
+ /**
+ * set recordLogLimit.
+ *
+ * @param recordLogLimit limit
+ */
+ public void setRecordLogLimit(final Integer recordLogLimit) {
+ this.recordLogLimit = recordLogLimit;
+ }
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/OperationRecordLogController.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/OperationRecordLogController.java
new file mode 100644
index 000000000..cfd411550
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/OperationRecordLogController.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.controller;
+
+import org.apache.shenyu.admin.model.entity.OperationRecordLog;
+import org.apache.shenyu.admin.model.result.AdminResult;
+import org.apache.shenyu.admin.service.OperationRecordLogService;
+import org.apache.shenyu.admin.utils.ResultUtil;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * OperationRecordLogController.
+ */
+@Validated
+@RestController
+@RequestMapping("/operation-record/log")
+public class OperationRecordLogController {
+
+ private final OperationRecordLogService recordLogService;
+
+ public OperationRecordLogController(final OperationRecordLogService
recordLogService) {
+ this.recordLogService = recordLogService;
+ }
+
+ /**
+ * list.
+ *
+ * @return list
+ */
+ @GetMapping("/list")
+ public AdminResult<List<OperationRecordLog>> list() {
+ return ResultUtil.ok(recordLogService.list());
+ }
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/listener/RecordLogDataChangedAdapterListener.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/listener/RecordLogDataChangedAdapterListener.java
new file mode 100644
index 000000000..5c4196795
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/listener/RecordLogDataChangedAdapterListener.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.listener;
+
+import org.apache.shenyu.admin.mapper.OperationRecordLogMapper;
+import org.apache.shenyu.admin.model.entity.OperationRecordLog;
+import org.apache.shenyu.admin.model.event.AdminDataModelChangedEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * RecordLogDataChangedAdapterListener.
+ */
+@Component
+public class RecordLogDataChangedAdapterListener implements
DataChangedListener, ApplicationListener<AdminDataModelChangedEvent> {
+
+ private final OperationRecordLogMapper logMapper;
+
+ public RecordLogDataChangedAdapterListener(final OperationRecordLogMapper
logMapper) {
+ this.logMapper = logMapper;
+ }
+
+ @Override
+ public void onApplicationEvent(final AdminDataModelChangedEvent event) {
+ if (event.isConsumed()) {
+ return;
+ }
+ final OperationRecordLog log = new OperationRecordLog();
+ log.setColor(event.getType().getColor());
+ log.setContext(event.buildContext());
+ log.setOperationTime(event.getDate());
+ log.setOperationType(event.getType().getTypeName());
+ log.setOperator(event.getOperator());
+ logMapper.insert(log);
+ event.consumed();
+ }
+
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/OperationRecordLogMapper.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/OperationRecordLogMapper.java
new file mode 100644
index 000000000..26b30de38
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/OperationRecordLogMapper.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.shenyu.admin.model.entity.OperationRecordLog;
+
+import java.util.List;
+
+/**
+ * OperationRecordLogMapper.
+ */
+@Mapper
+public interface OperationRecordLogMapper {
+
+ /**
+ * select limit.
+ *
+ * @param limit limit
+ * @return list
+ */
+ List<OperationRecordLog> selectLimit(@Param("limit") Integer limit);
+
+ /**
+ * insert.
+ *
+ * @param recordLog log
+ * @return count change
+ */
+ int insert(OperationRecordLog recordLog);
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/OperationRecordLog.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/OperationRecordLog.java
new file mode 100644
index 000000000..c3955e97a
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/OperationRecordLog.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.model.entity;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.util.Date;
+
+/**
+ * operation_record_log.
+ */
+public class OperationRecordLog {
+
+ /**
+ * id.
+ */
+ private Long id;
+
+ /**
+ * color.
+ */
+ private String color;
+
+ /**
+ * context.
+ */
+ private String context;
+
+ /**
+ * operator.
+ */
+ private String operator;
+
+ /**
+ * operation time.
+ */
+ @JsonFormat(pattern = "MM-dd hh:mm:ss")
+ private Date operationTime;
+
+ /**
+ * operation type.
+ */
+ private String operationType;
+
+ /**
+ * get id.
+ *
+ * @return id
+ */
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * set id.
+ *
+ * @param id id
+ */
+ public void setId(final Long id) {
+ this.id = id;
+ }
+
+ /**
+ * get color.
+ *
+ * @return color
+ */
+ public String getColor() {
+ return color;
+ }
+
+ /**
+ * set color.
+ *
+ * @param color color
+ */
+ public void setColor(final String color) {
+ this.color = color;
+ }
+
+ /**
+ * get context.
+ *
+ * @return context
+ */
+ public String getContext() {
+ return context;
+ }
+
+ /**
+ * set context.
+ *
+ * @param context context
+ */
+ public void setContext(final String context) {
+ this.context = context;
+ }
+
+ /**
+ * get operator.
+ *
+ * @return operator
+ */
+ public String getOperator() {
+ return operator;
+ }
+
+ /**
+ * set operator.
+ *
+ * @param operator operator
+ */
+ public void setOperator(final String operator) {
+ this.operator = operator;
+ }
+
+ /**
+ * get operationTime.
+ *
+ * @return operationTime
+ */
+ public Date getOperationTime() {
+ return operationTime;
+ }
+
+ /**
+ * set operationTime.
+ *
+ * @param operationTime operationTime
+ */
+ public void setOperationTime(final Date operationTime) {
+ this.operationTime = operationTime;
+ }
+
+ /**
+ * get operationType.
+ *
+ * @return operationType
+ */
+ public String getOperationType() {
+ return operationType;
+ }
+
+ /**
+ * set operationType.
+ *
+ * @param operationType operationType
+ */
+ public void setOperationType(final String operationType) {
+ this.operationType = operationType;
+ }
+
+ @Override
+ public String toString() {
+ return "OperationRecordLog{"
+ + "id=" + id
+ + ", color='" + color + '\''
+ + ", context='" + context + '\''
+ + ", operator='" + operator + '\''
+ + ", operationTime=" + operationTime
+ + ", operationType='" + operationType + '\''
+ + '}';
+ }
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/enums/EventTypeEnum.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/enums/EventTypeEnum.java
new file mode 100644
index 000000000..58771af5c
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/enums/EventTypeEnum.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.model.enums;
+
+import org.apache.shenyu.common.enums.DataEventTypeEnum;
+
+/**
+ * EventTypeEnum.
+ */
+public enum EventTypeEnum {
+
+ // ============== created ===================
+ /**
+ * created event.
+ */
+ CREATE(DataEventTypeEnum.CREATE, "green"),
+
+ /**
+ * register event.
+ */
+ REGISTER("REGISTER", DataEventTypeEnum.CREATE, "#1f640a"),
+
+ /**
+ * plugin created event.
+ */
+ PLUGIN_CREATE("CREATE:PLUGIN", DataEventTypeEnum.CREATE, "green"),
+
+ // ============== delete ===================
+ /**
+ * deleted event.
+ */
+ DELETE(DataEventTypeEnum.DELETE, "red"),
+
+ /**
+ * clean event.
+ */
+ CLEAN(DataEventTypeEnum.DELETE, "#e42c64"),
+
+ /**
+ * plugin deleted event.
+ */
+ PLUGIN_DELETE("DELETE:PLUGIN", DataEventTypeEnum.DELETE, "red"),
+
+ // ============== update ===================
+
+ /**
+ * update event.
+ */
+ UPDATE(DataEventTypeEnum.UPDATE, "yellow"),
+
+ /**
+ * plugin update.
+ */
+ PLUGIN_UPDATE("UPDATE:PLUGIN", DataEventTypeEnum.UPDATE, "yellow");
+
+ /**
+ * type name.
+ */
+ private final String typeName;
+
+ /**
+ * type.
+ */
+ private final DataEventTypeEnum type;
+
+ /**
+ * color.
+ */
+ private final String color;
+
+ EventTypeEnum(final DataEventTypeEnum type, final String color) {
+ this(type.toString(), type, color);
+ }
+
+ EventTypeEnum(final String typeName, final DataEventTypeEnum type, final
String color) {
+ this.typeName = typeName;
+ this.type = type;
+ this.color = color;
+ }
+
+ /**
+ * get typeName.
+ *
+ * @return type
+ */
+ public String getTypeName() {
+ return typeName;
+ }
+
+ /**
+ * get type.
+ *
+ * @return DataEventTypeEnum
+ */
+ public DataEventTypeEnum getType() {
+ return type;
+ }
+
+
+ /**
+ * get color.
+ *
+ * @return color
+ */
+ public String getColor() {
+ return color;
+ }
+
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/AdminDataModelChangedEvent.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/AdminDataModelChangedEvent.java
new file mode 100644
index 000000000..f0286acd2
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/AdminDataModelChangedEvent.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.model.event;
+
+import org.apache.shenyu.admin.model.enums.EventTypeEnum;
+import org.springframework.context.ApplicationEvent;
+
+import java.util.Date;
+import java.util.Objects;
+
+/**
+ * AdminDataModelChangedEvent.
+ */
+public class AdminDataModelChangedEvent extends ApplicationEvent {
+
+ /**
+ * action type.
+ */
+ private final EventTypeEnum type;
+
+ /**
+ * before data.
+ */
+ private final Object before;
+
+ /**
+ * after data.
+ */
+ private final Object after;
+
+ /**
+ * operator:is user or app.
+ */
+ private final String operator;
+
+ /**
+ * consumed.
+ */
+ private boolean consumed;
+
+ /**
+ * event date.
+ */
+ private final Date date;
+
+ /**
+ * Create a new {@code ApplicationEvent}.operator is unknown.
+ *
+ * @param source Current data state
+ * @param before Before the change data state
+ * @param type event type
+ */
+ public AdminDataModelChangedEvent(final Object source, final Object
before, final EventTypeEnum type) {
+ this(source, before, type, null);
+ }
+
+ /**
+ * Create a new {@code ApplicationEvent}.
+ *
+ * @param source Current data state
+ * @param before Before the change data state
+ * @param type event type
+ * @param operator operator,default is unknown
+ */
+ public AdminDataModelChangedEvent(final Object source, final Object
before, final EventTypeEnum type, final String operator) {
+ super(source);
+ this.type = type;
+ this.before = before;
+ this.after = source;
+ this.operator = Objects.isNull(operator) ? "unknown" : operator;
+ this.consumed = false;
+ this.date = new Date();
+ }
+
+
+ /**
+ * get type.
+ *
+ * @return type
+ */
+ public EventTypeEnum getType() {
+ return type;
+ }
+
+ /**
+ * get before.
+ *
+ * @return before data
+ */
+ public Object getBefore() {
+ return before;
+ }
+
+ /**
+ * get after.
+ *
+ * @return after data
+ */
+ public Object getAfter() {
+ return after;
+ }
+
+ /**
+ * get operator.
+ *
+ * @return operator
+ */
+ public String getOperator() {
+ return operator;
+ }
+
+ /**
+ * consumed.
+ */
+ public void consumed() {
+ this.consumed = true;
+ }
+
+ /**
+ * is consumed.
+ *
+ * @return is consumed
+ */
+ public boolean isConsumed() {
+ return consumed;
+ }
+
+
+ /**
+ * get date.
+ *
+ * @return event date
+ */
+ public Date getDate() {
+ return date;
+ }
+
+ /**
+ * before data snapshot.
+ *
+ * @return snapshot
+ */
+ public String beforeSnapshot() {
+ return Objects.toString(before, "before unknown");
+ }
+
+ /**
+ * after data snapshot.
+ *
+ * @return snapshot
+ */
+ public String afterSnapshot() {
+ return Objects.toString(after, "after unknown");
+ }
+
+ /**
+ * build event context.
+ *
+ * @return event context
+ */
+ public String buildContext() {
+ return String.format("%s changed(%s)[%s = > %s]", eventName(),
type.getTypeName(), beforeSnapshot(), afterSnapshot());
+ }
+
+ /**
+ * event name.
+ *
+ * @return name
+ */
+ public String eventName() {
+ return "data";
+ }
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/BatchChangedEvent.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/BatchChangedEvent.java
new file mode 100644
index 000000000..41eebd34e
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/BatchChangedEvent.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.model.event;
+
+import org.apache.shenyu.admin.model.enums.EventTypeEnum;
+
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * BatchChangedEvent.
+ */
+public class BatchChangedEvent extends AdminDataModelChangedEvent {
+
+
+ /**
+ * Create a new {@code PluginChangedEvent}.operator is unknown.
+ *
+ * @param source Current plugin state
+ * @param before Before the change plugin state
+ * @param type event type
+ */
+ public BatchChangedEvent(final Collection<?> source, final Collection<?>
before, final EventTypeEnum type, final String operator) {
+ super(source, before, type, operator);
+ }
+
+ /**
+ * before plguin snapshot.
+ *
+ * @return snapshot
+ */
+ @Override
+ public String beforeSnapshot() {
+ // format plugin data
+ return Objects.toString(getBefore(), "before plugin unknown");
+ }
+
+ /**
+ * after plugin snapshot.
+ *
+ * @return snapshot
+ */
+ @Override
+ public String afterSnapshot() {
+ // format plugin data
+ return Objects.toString(getAfter(), "after plugin unknown");
+ }
+
+ @Override
+ public String eventName() {
+ return "plugin";
+ }
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/BatchPluginChangedEvent.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/BatchPluginChangedEvent.java
new file mode 100644
index 000000000..da552eb76
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/BatchPluginChangedEvent.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.model.event;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.admin.model.entity.PluginDO;
+import org.apache.shenyu.admin.model.enums.EventTypeEnum;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+/**
+ * BatchPluginChangedEvent.
+ */
+public class BatchPluginChangedEvent extends BatchChangedEvent {
+
+
+ /**
+ * Create a new {@code PluginChangedEvent}.operator is unknown.
+ *
+ * @param source Current plugin state
+ * @param before Before the change plugin state
+ * @param type event type
+ */
+ public BatchPluginChangedEvent(final Collection<PluginDO> source, final
Collection<PluginDO> before, final EventTypeEnum type, final String operator) {
+ super(source, before, type, operator);
+ }
+
+ @Override
+ public String buildContext() {
+ final String plugins = ((Collection<?>) getSource())
+ .stream()
+ .map(s -> ((PluginDO) s).getName())
+ .collect(Collectors.joining(","));
+ return String.format("the plugins[%s] is %s", plugins,
StringUtils.lowerCase(getType().getType().toString()));
+ }
+
+ @Override
+ public String eventName() {
+ return "plugin";
+ }
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/PluginChangedEvent.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/PluginChangedEvent.java
new file mode 100644
index 000000000..9bacf6a06
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/PluginChangedEvent.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.model.event;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.admin.model.entity.PluginDO;
+import org.apache.shenyu.admin.model.enums.EventTypeEnum;
+
+import java.util.Objects;
+
+/**
+ * AdminDataModelChangedEvent.
+ */
+public class PluginChangedEvent extends AdminDataModelChangedEvent {
+
+
+ /**
+ * Create a new {@code PluginChangedEvent}.operator is unknown.
+ *
+ * @param source Current plugin state
+ * @param before Before the change plugiin state
+ * @param type event type
+ */
+ public PluginChangedEvent(final PluginDO source, final PluginDO before,
final EventTypeEnum type, final String operator) {
+ super(source, before, type, operator);
+ }
+
+ @Override
+ public String buildContext() {
+ final PluginDO after = (PluginDO) getAfter();
+ if (Objects.isNull(getBefore())) {
+ return String.format("the plugin [%s] is %s", after.getName(),
StringUtils.lowerCase(getType().getType().toString()));
+ }
+ return String.format("the plugin [%s] is %s : %s", after.getName(),
StringUtils.lowerCase(getType().getType().toString()), contrast());
+
+ }
+
+ private String contrast() {
+ final PluginDO before = (PluginDO) getBefore();
+ Objects.requireNonNull(before);
+ final PluginDO after = (PluginDO) getAfter();
+ Objects.requireNonNull(after);
+ if (Objects.equals(before, after)) {
+ return "it no change";
+ }
+ final StringBuilder builder = new StringBuilder();
+ if (!Objects.equals(before.getName(), after.getName())) {
+ builder.append(String.format("name[%s => %s] ", before.getName(),
after.getName()));
+ }
+ if (!Objects.equals(before.getConfig(), after.getConfig())) {
+ builder.append(String.format("config[%s => %s] ",
before.getConfig(), after.getConfig()));
+ }
+ if (!Objects.equals(before.getRole(), after.getRole())) {
+ builder.append(String.format("role[%s => %s] ", before.getRole(),
after.getRole()));
+ }
+ if (!Objects.equals(before.getEnabled(), after.getEnabled())) {
+ builder.append(String.format("enable[%s => %s] ",
before.getEnabled(), after.getEnabled()));
+ }
+ if (!Objects.equals(before.getSort(), after.getSort())) {
+ builder.append(String.format("sort[%s => %s] ", before.getSort(),
after.getSort()));
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public String eventName() {
+ return "plugin";
+ }
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/OperationRecordLogService.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/OperationRecordLogService.java
new file mode 100644
index 000000000..e4bb319a5
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/OperationRecordLogService.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.service;
+
+import org.apache.shenyu.admin.model.entity.OperationRecordLog;
+
+import java.util.List;
+
+/**
+ * OperationRecordLogService.
+ */
+public interface OperationRecordLogService {
+
+ /**
+ * list.
+ *
+ * @return list
+ */
+ List<OperationRecordLog> list();
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/OperationRecordLogServiceImpl.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/OperationRecordLogServiceImpl.java
new file mode 100644
index 000000000..854f32e79
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/OperationRecordLogServiceImpl.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.service.impl;
+
+import org.apache.shenyu.admin.config.properties.DashboardProperties;
+import org.apache.shenyu.admin.mapper.OperationRecordLogMapper;
+import org.apache.shenyu.admin.model.entity.OperationRecordLog;
+import org.apache.shenyu.admin.service.OperationRecordLogService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * OperationRecordLogServiceImpl.
+ */
+@Service
+public class OperationRecordLogServiceImpl implements
OperationRecordLogService {
+
+ private final OperationRecordLogMapper recordLogMapper;
+
+ private final DashboardProperties dashboardProperties;
+
+ public OperationRecordLogServiceImpl(final OperationRecordLogMapper
recordLogMapper,
+ final DashboardProperties
dashboardProperties) {
+ this.recordLogMapper = recordLogMapper;
+ this.dashboardProperties = dashboardProperties;
+ }
+
+ @Override
+ public List<OperationRecordLog> list() {
+ return
recordLogMapper.selectLimit(dashboardProperties.getRecordLogLimit());
+ }
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/PluginServiceImpl.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/PluginServiceImpl.java
index aa7b3d16a..94385a694 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/PluginServiceImpl.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/PluginServiceImpl.java
@@ -20,7 +20,6 @@ package org.apache.shenyu.admin.service.impl;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shenyu.admin.aspect.annotation.Pageable;
-import org.apache.shenyu.admin.listener.DataChangedEvent;
import org.apache.shenyu.admin.mapper.PluginHandleMapper;
import org.apache.shenyu.admin.mapper.PluginMapper;
import org.apache.shenyu.admin.mapper.RuleConditionMapper;
@@ -42,6 +41,7 @@ import org.apache.shenyu.admin.model.vo.PluginVO;
import org.apache.shenyu.admin.model.vo.ResourceVO;
import org.apache.shenyu.admin.service.PluginService;
import org.apache.shenyu.admin.service.ResourceService;
+import org.apache.shenyu.admin.service.publish.PluginEventPublisher;
import org.apache.shenyu.admin.transfer.PluginTransfer;
import org.apache.shenyu.admin.utils.Assert;
import org.apache.shenyu.admin.utils.ShenyuResultMessage;
@@ -50,8 +50,6 @@ import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.enums.AdminPluginOperateEnum;
import org.apache.shenyu.common.enums.AdminResourceEnum;
import org.apache.shenyu.common.enums.ConfigGroupEnum;
-import org.apache.shenyu.common.enums.DataEventTypeEnum;
-import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -79,26 +77,26 @@ public class PluginServiceImpl implements PluginService {
private final RuleConditionMapper ruleConditionMapper;
- private final ApplicationEventPublisher eventPublisher;
-
private final ResourceService resourceService;
+ private final PluginEventPublisher modelDataEventPublisher;
+
public PluginServiceImpl(final PluginMapper pluginMapper,
final PluginHandleMapper pluginHandleMapper,
final SelectorMapper selectorMapper,
final SelectorConditionMapper
selectorConditionMapper,
final RuleMapper ruleMapper,
final RuleConditionMapper ruleConditionMapper,
- final ApplicationEventPublisher eventPublisher,
- final ResourceService resourceService) {
+ final ResourceService resourceService,
+ final PluginEventPublisher
modelDataEventPublisher) {
this.pluginMapper = pluginMapper;
this.pluginHandleMapper = pluginHandleMapper;
this.selectorMapper = selectorMapper;
this.selectorConditionMapper = selectorConditionMapper;
this.ruleMapper = ruleMapper;
this.ruleConditionMapper = ruleConditionMapper;
- this.eventPublisher = eventPublisher;
this.resourceService = resourceService;
+ this.modelDataEventPublisher = modelDataEventPublisher;
}
@Override
@@ -134,9 +132,11 @@ public class PluginServiceImpl implements PluginService {
}
final List<String> pluginIds = plugins.stream()
.map(PluginDO::getId).collect(Collectors.toList());
-
+
// 2. delete plugins.
this.pluginMapper.deleteByIds(pluginIds);
+
+ // TODO move to plugin handle service, listen plugin delete event
execute
// 3. delete plugin handle.
this.pluginHandleMapper.deleteByPluginIds(pluginIds);
@@ -170,8 +170,7 @@ public class PluginServiceImpl implements PluginService {
}
// 6. publish change event.
- eventPublisher.publishEvent(new
DataChangedEvent(ConfigGroupEnum.PLUGIN, DataEventTypeEnum.DELETE,
-
plugins.stream().map(PluginTransfer.INSTANCE::mapToData).collect(Collectors.toList())));
+ modelDataEventPublisher.onDeleted(plugins);
return StringUtils.EMPTY;
}
@@ -192,8 +191,7 @@ public class PluginServiceImpl implements PluginService {
pluginMapper.updateEnableByIdList(ids, enabled);
// publish change event.
if (CollectionUtils.isNotEmpty(plugins)) {
- eventPublisher.publishEvent(new
DataChangedEvent(ConfigGroupEnum.PLUGIN, DataEventTypeEnum.UPDATE,
-
plugins.stream().map(PluginTransfer.INSTANCE::mapToData).collect(Collectors.toList())));
+ modelDataEventPublisher.onEnabled(plugins);
}
return StringUtils.EMPTY;
}
@@ -335,7 +333,7 @@ public class PluginServiceImpl implements PluginService {
insertPluginMenuResource(resourceDO);
}
-
+
/**
* create plugin.<br>
* insert plugin and insert plugin data.
@@ -348,13 +346,11 @@ public class PluginServiceImpl implements PluginService {
PluginDO pluginDO = PluginDO.buildPluginDO(pluginDTO);
insertPluginDataToResource(pluginDTO);
pluginMapper.insertSelective(pluginDO);
-
// publish create event.
- eventPublisher.publishEvent(new
DataChangedEvent(ConfigGroupEnum.PLUGIN, DataEventTypeEnum.CREATE,
-
Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO))));
+ modelDataEventPublisher.onCreated(pluginDO);
return ShenyuResultMessage.CREATE_SUCCESS;
}
-
+
/**
* update plugin.<br>
*
@@ -363,12 +359,12 @@ public class PluginServiceImpl implements PluginService {
*/
private String update(final PluginDTO pluginDTO) {
Assert.isNull(pluginMapper.nameExistedExclude(pluginDTO.getName(),
Collections.singletonList(pluginDTO.getId())),
AdminConstants.PLUGIN_NAME_IS_EXIST);
+ final PluginDO before = pluginMapper.selectById(pluginDTO.getId());
PluginDO pluginDO = PluginDO.buildPluginDO(pluginDTO);
pluginMapper.updateSelective(pluginDO);
-
+
// publish update event.
- eventPublisher.publishEvent(new
DataChangedEvent(ConfigGroupEnum.PLUGIN, DataEventTypeEnum.UPDATE,
-
Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO))));
+ modelDataEventPublisher.onUpdated(pluginDO, before);
return ShenyuResultMessage.UPDATE_SUCCESS;
}
}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/AdminDataModelChangedEventPublisher.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/AdminDataModelChangedEventPublisher.java
new file mode 100644
index 000000000..b6dc616bf
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/AdminDataModelChangedEventPublisher.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.service.publish;
+
+import org.apache.shenyu.admin.model.enums.EventTypeEnum;
+import org.apache.shenyu.admin.model.event.AdminDataModelChangedEvent;
+import org.apache.shenyu.admin.model.event.BatchChangedEvent;
+import org.apache.shenyu.admin.utils.SessionUtil;
+
+import java.util.Collection;
+
+/**
+ * ModelDataEventPublisher.
+ */
+public interface AdminDataModelChangedEventPublisher<T> {
+
+ /**
+ * on created.
+ *
+ * @param data data
+ */
+ default void onCreated(final T data) {
+ publish(new AdminDataModelChangedEvent(data, null,
EventTypeEnum.CREATE, SessionUtil.visitorName()));
+ }
+
+ /**
+ * on created.
+ *
+ * @param data data
+ */
+ default void onCreated(final Collection<T> data) {
+ publish(new BatchChangedEvent(data, null, EventTypeEnum.CREATE,
SessionUtil.visitorName()));
+ }
+
+ /**
+ * on data updated.
+ *
+ * @param data data
+ * @param before before data
+ */
+ default void onUpdated(final T data, final T before) {
+ publish(new AdminDataModelChangedEvent(data, before,
EventTypeEnum.UPDATE, SessionUtil.visitorName()));
+ }
+
+ /**
+ * on data updated.
+ *
+ * @param data data
+ * @param before before data
+ */
+ default void onUpdated(final Collection<T> data, final Collection<T>
before) {
+ publish(new BatchChangedEvent(data, before, EventTypeEnum.UPDATE,
SessionUtil.visitorName()));
+ }
+
+ /**
+ * on data deleted.
+ *
+ * @param data data
+ */
+ default void onDeleted(final T data) {
+ publish(new AdminDataModelChangedEvent(data, null,
EventTypeEnum.DELETE, SessionUtil.visitorName()));
+ }
+
+
+ /**
+ * on data deleted.
+ *
+ * @param data data
+ */
+ default void onDeleted(final Collection<T> data) {
+ publish(new BatchChangedEvent(data, null, EventTypeEnum.DELETE,
SessionUtil.visitorName()));
+ }
+
+ /**
+ * event.
+ *
+ * @param event event.
+ */
+ void publish(AdminDataModelChangedEvent event);
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/PluginEventPublisher.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/PluginEventPublisher.java
new file mode 100644
index 000000000..b029e9afc
--- /dev/null
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/PluginEventPublisher.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.service.publish;
+
+import org.apache.shenyu.admin.listener.DataChangedEvent;
+import org.apache.shenyu.admin.model.entity.PluginDO;
+import org.apache.shenyu.admin.model.enums.EventTypeEnum;
+import org.apache.shenyu.admin.model.event.AdminDataModelChangedEvent;
+import org.apache.shenyu.admin.model.event.BatchPluginChangedEvent;
+import org.apache.shenyu.admin.model.event.PluginChangedEvent;
+import org.apache.shenyu.admin.transfer.PluginTransfer;
+import org.apache.shenyu.admin.utils.SessionUtil;
+import org.apache.shenyu.common.enums.ConfigGroupEnum;
+import org.apache.shenyu.common.enums.DataEventTypeEnum;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * ModelDataEventPublisher.
+ */
+@Component
+public class PluginEventPublisher implements
AdminDataModelChangedEventPublisher<PluginDO> {
+
+ private final ApplicationEventPublisher publisher;
+
+ public PluginEventPublisher(final ApplicationEventPublisher publisher) {
+ this.publisher = publisher;
+ }
+
+ /**
+ * on plugin created.
+ *
+ * @param plugin plugin
+ */
+ @Override
+ public void onCreated(final PluginDO plugin) {
+ publisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN,
DataEventTypeEnum.CREATE,
+
Collections.singletonList(PluginTransfer.INSTANCE.mapToData(plugin))));
+ publish(new PluginChangedEvent(plugin, null,
EventTypeEnum.PLUGIN_CREATE, SessionUtil.visitorName()));
+ }
+
+ /**
+ * on plugin updated.
+ *
+ * @param plugin plugin
+ * @param before before plugin
+ */
+ @Override
+ public void onUpdated(final PluginDO plugin, final PluginDO before) {
+ publisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN,
DataEventTypeEnum.UPDATE,
+
Collections.singletonList(PluginTransfer.INSTANCE.mapToData(plugin))));
+ publish(new PluginChangedEvent(plugin, before,
EventTypeEnum.PLUGIN_UPDATE, SessionUtil.visitorName()));
+ }
+
+ /**
+ * on plugin deleted.
+ *
+ * @param plugin plugin
+ */
+ @Override
+ public void onDeleted(final PluginDO plugin) {
+ publish(new PluginChangedEvent(plugin, null,
EventTypeEnum.PLUGIN_DELETE, SessionUtil.visitorName()));
+ publisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN,
DataEventTypeEnum.DELETE,
+
Stream.of(plugin).map(PluginTransfer.INSTANCE::mapToData).collect(Collectors.toList())));
+ }
+
+ /**
+ * on plugin deleted.
+ *
+ * @param plugins plugins
+ */
+ @Override
+ public void onDeleted(final Collection<PluginDO> plugins) {
+ publish(new BatchPluginChangedEvent(plugins, null,
EventTypeEnum.PLUGIN_DELETE, SessionUtil.visitorName()));
+ publisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN,
DataEventTypeEnum.DELETE,
+
plugins.stream().map(PluginTransfer.INSTANCE::mapToData).collect(Collectors.toList())));
+ }
+
+ /**
+ * on plugin batch enabled.
+ *
+ * @param plugins plugins
+ */
+ public void onEnabled(final Collection<PluginDO> plugins) {
+ publish(new BatchPluginChangedEvent(plugins, null,
EventTypeEnum.PLUGIN_UPDATE, SessionUtil.visitorName()));
+ publisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN,
DataEventTypeEnum.UPDATE,
+
plugins.stream().map(PluginTransfer.INSTANCE::mapToData).collect(Collectors.toList())));
+ }
+
+ /**
+ * event.
+ *
+ * @param event event.
+ */
+ @Override
+ public void publish(final AdminDataModelChangedEvent event) {
+ publisher.publishEvent(event);
+ }
+}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/SessionUtil.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/SessionUtil.java
new file mode 100644
index 000000000..922b09e1c
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/SessionUtil.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.utils;
+
+import org.apache.shenyu.admin.model.custom.UserInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+
+/**
+ * the session is a request content.
+ * Suitable for retrieving user context information from a variety of sources。
+ */
+public final class SessionUtil {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(SessionUtil.class);
+
+ private static final ThreadLocal<UserInfo> LOCAL_VISITOR = new
InheritableThreadLocal<>();
+
+ private SessionUtil() {
+ }
+
+
+ /**
+ * visitor is login user[admin or other] / app /bootstrap.
+ *
+ * @return default is unknown
+ */
+ public static String visitorName() {
+ return visitor().getUserName();
+ }
+
+ /**
+ * visitor is login user[admin or other] / app /bootstrap.
+ *
+ * @return default is unknown
+ */
+ public static UserInfo visitor() {
+ try {
+ final UserInfo userInfo = LOCAL_VISITOR.get();
+ if (Objects.isNull(userInfo)) {
+ // try get from auth
+ setLocalVisitorFromAuth();
+ }
+ return LOCAL_VISITOR.get();
+ } catch (Exception e) {
+ LOG.warn("get user info error ,not found, used default user ,it
unknown");
+ }
+ return UserInfo.builder().userId("-1").userName("unknown").build();
+ }
+
+ /**
+ * set visitor user.
+ *
+ * @param userInfo user info
+ */
+ public static void setLocalVisitor(final UserInfo userInfo) {
+ LOCAL_VISITOR.set(userInfo);
+ }
+
+ /**
+ * set visitor user.
+ */
+ public static void setLocalVisitorFromAuth() {
+ // featureToDo:Adapting app access
+ LOCAL_VISITOR.set(JwtUtils.getUserInfo());
+ }
+
+ /**
+ * clean current session.
+ */
+ public static void clean() {
+ LOCAL_VISITOR.remove();
+ }
+}
+
diff --git
a/shenyu-admin/src/main/resources/mappers/operation-record-log-sqlmap.xml
b/shenyu-admin/src/main/resources/mappers/operation-record-log-sqlmap.xml
new file mode 100644
index 000000000..a171e9352
--- /dev/null
+++ b/shenyu-admin/src/main/resources/mappers/operation-record-log-sqlmap.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.apache.shenyu.admin.mapper.OperationRecordLogMapper">
+ <resultMap id="BaseResultMap"
type="org.apache.shenyu.admin.model.entity.OperationRecordLog">
+ <id column="id" jdbcType="VARCHAR" property="id"/>
+ <result column="id" jdbcType="BIGINT" property="id"/>
+ <result column="color" jdbcType="VARCHAR" property="color"/>
+ <result column="context" jdbcType="VARCHAR" property="context"/>
+ <result column="operator" jdbcType="VARCHAR" property="operator"/>
+ <result column="operation_time" jdbcType="VARCHAR"
property="operationTime"/>
+ <result column="operation_type" jdbcType="VARCHAR"
property="operationType"/>
+ </resultMap>
+
+ <sql id="Base_Column_List">
+ id,
+ color,
+ context,
+ operator,
+ operation_time,
+ operation_type
+ </sql>
+
+ <select id="selectLimit" parameterType="java.lang.Integer"
resultMap="BaseResultMap">
+ SELECT
+ <include refid="Base_Column_List"/>
+ FROM operation_record_log
+ order by operation_time desc
+ </select>
+
+ <insert id="insert"
parameterType="org.apache.shenyu.admin.model.entity.OperationRecordLog"
useGeneratedKeys="true" keyProperty="id">
+ insert into operation_record_log(id, color, context, operator,
operation_time, operation_type)
+ values (#{id},
+ #{color},
+ #{context},
+ #{operator},
+ #{operationTime},
+ #{operationType}
+ )
+ </insert>
+</mapper>
diff --git a/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
b/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
index c12aa48fe..51794b245 100644
--- a/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
+++ b/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
@@ -242,6 +242,18 @@ CREATE TABLE IF NOT EXISTS `data_permission` (
`date_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP COMMENT 'update time',
PRIMARY KEY (`id`)
);
+-- ----------------------------
+-- Table structure for operation_record_log
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `operation_record_log`
+(
+ `id` bigint NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT
'id',
+ `color` varchar(20) NOT NULL COMMENT 'log color',
+ `context` text NOT NULL COMMENT 'log context',
+ `operator` varchar(200) NOT NULL COMMENT 'operator [user or app]]',
+ `operation_time` datetime NOT NULL DEFAULT now() COMMENT 'operation
time',
+ `operation_type` varchar(60) NOT NULL DEFAULT 'update' COMMENT 'operation
type:create/update/delete/register...'
+);
/**default admin user**/
INSERT IGNORE INTO `dashboard_user` (`id`, `user_name`, `password`, `role`,
`enabled`) VALUES
('1','admin','ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413',
'1', '1');
diff --git a/shenyu-admin/src/main/resources/sql-script/mysql/schema.sql
b/shenyu-admin/src/main/resources/sql-script/mysql/schema.sql
index f00704143..13687aa68 100644
--- a/shenyu-admin/src/main/resources/sql-script/mysql/schema.sql
+++ b/shenyu-admin/src/main/resources/sql-script/mysql/schema.sql
@@ -248,6 +248,18 @@ CREATE TABLE IF NOT EXISTS `data_permission` (
`date_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP COMMENT 'update time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
ROW_FORMAT=DYNAMIC COMMENT='data permission table';
+-- ----------------------------
+-- Table structure for operation_record_log
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `operation_record_log`
+(
+ `id` bigint auto_increment not null comment 'id'
primary key,
+ `color` varchar(20) not null comment 'log color',
+ `context` text not null comment 'log
context',
+ `operator` varchar(200) not null comment 'operator
[user or app]]',
+ `operation_time` datetime default now() not null comment 'operation
time',
+ `operation_type` varchar(60) default 'update' not null comment 'operation
type:create/update/delete/register...'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
ROW_FORMAT=DYNAMIC comment 'operation record log';
/**default admin user**/
INSERT IGNORE INTO `dashboard_user` (`id`, `user_name`, `password`, `role`,
`enabled`) VALUES
('1','admin','ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413',
'1', '1');
diff --git a/shenyu-admin/src/main/resources/sql-script/pg/schema.sql
b/shenyu-admin/src/main/resources/sql-script/pg/schema.sql
index 55b721179..5924953a7 100644
--- a/shenyu-admin/src/main/resources/sql-script/pg/schema.sql
+++ b/shenyu-admin/src/main/resources/sql-script/pg/schema.sql
@@ -1092,6 +1092,35 @@ ELSE
PERFORM public.dblink_exec('init_conn', 'COMMIT');
END IF;
+
+-- ----------------------------------------
+-- create table operation_record_log if not exist ---
+-- ---------------------------------------
+IF (SELECT * FROM dblink('host=localhost user=' || _user || ' password=' ||
_password || ' dbname=' ||_db,'SELECT COUNT(1) FROM pg_class WHERE relname =
''' ||'operation_record_log' || '''')AS t(count BIGINT) )> 0 THEN
+ RAISE NOTICE 'operation_record_log already exists';
+ELSE
+ PERFORM public.dblink_exec('init_conn', 'BEGIN');
+ PERFORM public.dblink_exec('init_conn', 'CREATE TABLE
"operation_record_log" (
+ "id" int8 NOT NULL,
+ "color" varchar(20) COLLATE "pg_catalog"."default" NOT NULL,
+ "context" text COLLATE "pg_catalog"."default" NOT NULL,
+ "operator" varchar(200) COLLATE "pg_catalog"."default" NOT NULL,
+ "operation_time" timestamp(6) NOT NULL,
+ "operation_type" varchar(60) COLLATE "pg_catalog"."default" NOT NULL,
+ CONSTRAINT "operation_record_log_pkey" PRIMARY KEY ("id")
+ )');
+
+ PERFORM public.dblink_exec('init_conn','COMMENT ON COLUMN
"operation_record_log"."id" IS ''' || 'id' || '''');
+ PERFORM public.dblink_exec('init_conn','COMMENT ON COLUMN
"operation_record_log"."color" IS ''' || 'log color' || '''');
+ PERFORM public.dblink_exec('init_conn','COMMENT ON COLUMN
"operation_record_log"."context" IS ''' || 'log context' || '''');
+ PERFORM public.dblink_exec('init_conn','COMMENT ON COLUMN
"operation_record_log"."operator" IS ''' || 'operator [user or app]]' || '''');
+ PERFORM public.dblink_exec('init_conn','COMMENT ON COLUMN
"operation_record_log"."operation_time" IS ''' || 'operation time' || '''');
+ PERFORM public.dblink_exec('init_conn','COMMENT ON COLUMN
"operation_record_log"."operation_type" IS ''' || 'operation
type:create/update/delete/register...' || '''');
+ PERFORM public.dblink_exec('init_conn','COMMENT ON TABLE
"operation_record_log" IS ''' || 'operation record log' || '''');
+
+ PERFORM public.dblink_exec('init_conn', 'COMMIT');
+END IF;
+
PERFORM public.dblink_disconnect('init_conn');
END
$do$;
diff --git
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/PluginServiceTest.java
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/PluginServiceTest.java
index 2b527e0d9..167dd58f4 100644
---
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/PluginServiceTest.java
+++
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/PluginServiceTest.java
@@ -33,6 +33,7 @@ import org.apache.shenyu.admin.model.page.PageParameter;
import org.apache.shenyu.admin.model.query.PluginQuery;
import org.apache.shenyu.admin.model.vo.PluginVO;
import org.apache.shenyu.admin.service.impl.PluginServiceImpl;
+import org.apache.shenyu.admin.service.publish.PluginEventPublisher;
import org.apache.shenyu.admin.utils.ShenyuResultMessage;
import org.apache.shenyu.common.constant.AdminConstants;
import org.apache.shenyu.common.dto.PluginData;
@@ -45,7 +46,6 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
-import org.springframework.context.ApplicationEventPublisher;
import java.sql.Timestamp;
import java.time.LocalDateTime;
@@ -91,15 +91,15 @@ public final class PluginServiceTest {
private SelectorConditionMapper selectorConditionMapper;
@Mock
- private ApplicationEventPublisher eventPublisher;
+ private ResourceService resourceService;
@Mock
- private ResourceService resourceService;
+ private PluginEventPublisher modelDataEventPublisher;
@BeforeEach
public void setUp() {
pluginService = new PluginServiceImpl(pluginMapper,
pluginHandleMapper, selectorMapper, selectorConditionMapper,
- ruleMapper, ruleConditionMapper, eventPublisher,
resourceService);
+ ruleMapper, ruleConditionMapper, resourceService,
modelDataEventPublisher);
}
@Test