You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by zh...@apache.org on 2022/06/19 13:37:44 UTC

[incubator-shenyu] branch master updated: [ISSUE #3221]Feature/record log in user (#3586)

This is an automated email from the ASF dual-hosted git repository.

zhangzicheng 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 ff44a591c [ISSUE #3221]Feature/record log in user (#3586)
ff44a591c is described below

commit ff44a591cd06526eb3d68039e9a86a5f61405a83
Author: likeguo <33...@users.noreply.github.com>
AuthorDate: Sun Jun 19 21:37:37 2022 +0800

    [ISSUE #3221]Feature/record log in user (#3586)
    
    * fixbug/pg script error
    
    * feature/record log at user service
---
 .../admin/controller/DashboardUserController.java  |   3 +-
 .../shenyu/admin/mapper/DashboardUserMapper.java   |   9 ++
 .../shenyu/admin/model/enums/EventTypeEnum.java    |  64 +++++----
 .../model/event/user/BatchUserDeletedEvent.java    |  80 ++++++++++++
 .../admin/model/event/user/UserChangedEvent.java   |  92 +++++++++++++
 .../admin/model/event/user/UserCreatedEvent.java   |  39 ++++++
 .../admin/model/event/user/UserUpdatedEvent.java   |  40 ++++++
 .../shenyu/admin/service/DashboardUserService.java |  20 ++-
 .../service/impl/DashboardUserServiceImpl.java     | 144 ++++++++++++---------
 .../service/impl/DataPermissionServiceImpl.java    |  11 ++
 .../admin/service/publish/UserEventPublisher.java  |  72 +++++++++++
 .../resources/mappers/dashboard-user-sqlmap.xml    |  21 ++-
 .../admin/service/DashboardUserServiceTest.java    |  78 ++++++-----
 13 files changed, 545 insertions(+), 128 deletions(-)

diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/DashboardUserController.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/DashboardUserController.java
index 297525c3d..d0b93044e 100644
--- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/DashboardUserController.java
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/DashboardUserController.java
@@ -48,6 +48,7 @@ import javax.validation.Valid;
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
 
@@ -169,7 +170,7 @@ public class DashboardUserController {
     @DeleteMapping("/batch")
     @RequiresPermissions("system:manager:delete")
     public ShenyuAdminResult deleteDashboardUser(@RequestBody @NotEmpty final List<@NotBlank String> ids) {
-        Integer deleteCount = dashboardUserService.delete(ids);
+        Integer deleteCount = dashboardUserService.delete(new HashSet<>(ids));
         return ShenyuAdminResult.success(ShenyuResultMessage.DELETE_SUCCESS, deleteCount);
     }
 }
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/DashboardUserMapper.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/DashboardUserMapper.java
index 4604a9d0b..7b94a94b5 100644
--- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/DashboardUserMapper.java
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/DashboardUserMapper.java
@@ -25,6 +25,7 @@ import org.apache.shenyu.admin.validation.ExistProvider;
 
 import java.io.Serializable;
 import java.util.List;
+import java.util.Set;
 
 /**
  * DashboardUserMapper.
@@ -74,6 +75,14 @@ public interface DashboardUserMapper extends ExistProvider {
      */
     List<DashboardUserDO> selectByQuery(DashboardUserQuery dashboardUserQuery);
     
+    /**
+     * select by ids.
+     *
+     * @param ids ids.
+     * @return users
+     */
+    List<DashboardUserDO> selectByIds(@Param("ids") Set<String> ids);
+    
     /**
      * count dashboard user by query.
      *
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
index e1026e2d4..c9b913c8e 100644
--- 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
@@ -38,43 +38,47 @@ public enum EventTypeEnum {
     /**
      * plugin created event.
      */
-    PLUGIN_CREATE("CREATE:PLUGIN", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
+    PLUGIN_CREATE("CREATE:Plugin", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
     
     /**
      * plugin handle created event.
      */
-    PLUGIN_HANDLE_CREATE("CREATE:PLUGIN-HANDLE", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
+    PLUGIN_HANDLE_CREATE("CREATE:PluginHandle", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
     
     /**
      * selector created event.
      */
-    SELECTOR_CREATE("CREATE:SELECTOR", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
+    SELECTOR_CREATE("CREATE:Selector", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
     
     /**
      * rule created event.
      */
-    RULE_CREATE("CREATE:RULE", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
+    RULE_CREATE("CREATE:Rule", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
     
     /**
      * meta data created event.
      */
-    META_DATA_CREATE("CREATE:META_DATA", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
+    META_DATA_CREATE("CREATE:MetaData", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
     
     /**
      * resource created event.
      */
-    RESOURCE_CREATE("CREATE:RESOURCE", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
+    RESOURCE_CREATE("CREATE:Resource", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
     
     /**
      * dict created event.
      */
-    DICT_CREATE("CREATE:DICT", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
-    
+    DICT_CREATE("CREATE:Dict", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
     
     /**
      * role created event.
      */
-    ROLE_CREATE("CREATE:ROLE", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
+    ROLE_CREATE("CREATE:Role", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
+    
+    /**
+     * user created event.
+     */
+    USER_CREATE("CREATE:User", DataEventTypeEnum.CREATE, Color.CREATE_COLOR),
     
     // ============== delete ===================
     /**
@@ -90,43 +94,48 @@ public enum EventTypeEnum {
     /**
      * plugin deleted event.
      */
-    PLUGIN_DELETE("DELETE:PLUGIN", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
+    PLUGIN_DELETE("DELETE:Plugin", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
     
     
     /**
      * plugin handle deleted event.
      */
-    PLUGIN_HANDLE_DELETE("DELETE:PLUGIN-HANDLE", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
+    PLUGIN_HANDLE_DELETE("DELETE:Plugin-Handle", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
     
     /**
      * selector deleted event.
      */
-    SELECTOR_DELETE("DELETE:SELECTOR", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
+    SELECTOR_DELETE("DELETE:Selector", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
     
     /**
      * rule deleted event.
      */
-    RULE_DELETE("DELETE:RULE", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
+    RULE_DELETE("DELETE:Rule", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
     
     /**
      * meta data deleted event.
      */
-    META_DATA_DELETE("DELETE:META_DATA", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
+    META_DATA_DELETE("DELETE:Meta_Data", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
     
     /**
      * resource deleted event.
      */
-    RESOURCE_DELETE("DELETE:RESOURCE", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
+    RESOURCE_DELETE("DELETE:Resource", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
     
     /**
      * dict deleted event.
      */
-    DICT_DELETE("DELETE:DICT", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
+    DICT_DELETE("DELETE:Dict", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
     
     /**
      * role deleted event.
      */
-    ROLE_DELETE("DELETE:ROLE", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
+    ROLE_DELETE("DELETE:Role", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
+    
+    /**
+     * user deleted event.
+     */
+    USER_DELETE("DELETE:User", DataEventTypeEnum.DELETE, Color.DELETE_COLOR),
     
     // ============== update ===================
     
@@ -138,42 +147,47 @@ public enum EventTypeEnum {
     /**
      * plugin update.
      */
-    PLUGIN_UPDATE("UPDATE:PLUGIN", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
+    PLUGIN_UPDATE("UPDATE:Plugin", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
     
     /**
      * plugin handle update.
      */
-    PLUGIN_HANDLE_UPDATE("UPDATE:PLUGIN-HANDLE", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
+    PLUGIN_HANDLE_UPDATE("UPDATE:PluginHandle", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
     
     /**
      * selector update.
      */
-    SELECTOR_UPDATE("UPDATE:PLUGIN-HANDLE", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
+    SELECTOR_UPDATE("UPDATE:PluginHandle", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
     
     /**
      * rule update.
      */
-    RULE_UPDATE("UPDATE:RULE", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
+    RULE_UPDATE("UPDATE:Rule", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
     
     /**
      * meta data update.
      */
-    META_DATA_UPDATE("UPDATE:META_DATA", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
+    META_DATA_UPDATE("UPDATE:MetaData", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
     
     /**
      * resource update.
      */
-    RESOURCE_UPDATE("UPDATE:RESOURCE", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
+    RESOURCE_UPDATE("UPDATE:Resource", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
     
     /**
      * dict update.
      */
-    DICT_UPDATE("UPDATE:DICT", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
+    DICT_UPDATE("UPDATE:Dict", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
     
     /**
      * role update.
      */
-    ROLE_UPDATE("UPDATE:ROLE", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR);
+    ROLE_UPDATE("UPDATE:Role", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR),
+    
+    /**
+     * user update.
+     */
+    USER_UPDATE("UPDATE:User", DataEventTypeEnum.UPDATE, Color.UPDATE_COLOR);
     
     /**
      * type name.
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/BatchUserDeletedEvent.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/BatchUserDeletedEvent.java
new file mode 100644
index 000000000..1960906a8
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/BatchUserDeletedEvent.java
@@ -0,0 +1,80 @@
+/*
+ * 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.user;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.admin.model.entity.BaseDO;
+import org.apache.shenyu.admin.model.entity.DashboardUserDO;
+import org.apache.shenyu.admin.model.enums.EventTypeEnum;
+import org.apache.shenyu.admin.model.event.BatchChangedEvent;
+import org.apache.shenyu.admin.utils.ListUtil;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * BatchPluginDeletedEvent.
+ */
+public class BatchUserDeletedEvent extends BatchChangedEvent {
+    
+    private final List<String> deletedIds;
+    
+    
+    /**
+     * Create a new {@code BatchChangedEvent}.operator is unknown.
+     *
+     * @param source   Current plugin state
+     * @param operator operator
+     */
+    public BatchUserDeletedEvent(final Collection<DashboardUserDO> source, final String operator) {
+        super(source, null, EventTypeEnum.USER_DELETE, operator);
+        this.deletedIds = ListUtil.map(source, BaseDO::getId);
+
+    }
+    
+    @Override
+    public String buildContext() {
+        final String selector = ((Collection<?>) getSource())
+                .stream()
+                .map(s -> ((DashboardUserDO) s).getUserName())
+                .collect(Collectors.joining(","));
+        return String.format("the user[%s] is %s", selector, StringUtils.lowerCase(getType().getType().toString()));
+    }
+    
+    /**
+     * get users.
+     *
+     * @return list
+     */
+    public List<DashboardUserDO> getUsers() {
+        return ((Collection<?>) getSource())
+                .stream()
+                .map(DashboardUserDO.class::cast)
+                .collect(Collectors.toList());
+    }
+    
+    /**
+     * get deleted ids.
+     *
+     * @return ids.
+     */
+    public List<String> getDeletedIds() {
+        return deletedIds;
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/UserChangedEvent.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/UserChangedEvent.java
new file mode 100644
index 000000000..ce39fd77e
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/UserChangedEvent.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.model.event.user;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.admin.model.entity.DashboardUserDO;
+import org.apache.shenyu.admin.model.enums.EventTypeEnum;
+import org.apache.shenyu.admin.model.event.AdminDataModelChangedEvent;
+
+import java.util.Objects;
+
+/**
+ * UserChangedEvent.
+ */
+public class UserChangedEvent extends AdminDataModelChangedEvent {
+    
+    
+    /**
+     * Create a new {@code UserChangedEvent}.operator is unknown.
+     *
+     * @param source Current user state
+     * @param before Before the change user state
+     * @param type   event type
+     */
+    public UserChangedEvent(final DashboardUserDO source, final DashboardUserDO before, final EventTypeEnum type, final String operator) {
+        super(source, before, type, operator);
+    }
+    
+    @Override
+    public String buildContext() {
+        final DashboardUserDO after = (DashboardUserDO) getAfter();
+        if (Objects.isNull(getBefore())) {
+            return String.format("the selector [%s] is %s", after.getUserName(), StringUtils.lowerCase(getType().getType().toString()));
+        }
+        return String.format("the selector [%s] is %s : %s", after.getUserName(), StringUtils.lowerCase(getType().getType().toString()), contrast());
+        
+    }
+    
+    /**
+     * the changed user.
+     *
+     * @return user
+     */
+    public DashboardUserDO getChangedUser() {
+        return (DashboardUserDO) getSource();
+    }
+    
+    private String contrast() {
+        final DashboardUserDO before = (DashboardUserDO) getBefore();
+        Objects.requireNonNull(before);
+        final DashboardUserDO after = (DashboardUserDO) getAfter();
+        Objects.requireNonNull(after);
+        if (Objects.equals(before, after)) {
+            return "it no change";
+        }
+        final StringBuilder builder = new StringBuilder();
+        if (!Objects.equals(before.getUserName(), after.getUserName())) {
+            builder.append(String.format("name[%s => %s] ", before.getUserName(), after.getUserName()));
+        }
+        if (!Objects.equals(before.getPassword(), after.getPassword())) {
+            builder.append("password is changed...");
+        }
+        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()));
+        }
+
+        return builder.toString();
+    }
+    
+    @Override
+    public String eventName() {
+        return "user";
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/UserCreatedEvent.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/UserCreatedEvent.java
new file mode 100644
index 000000000..028671418
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/UserCreatedEvent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.user;
+
+import org.apache.shenyu.admin.model.entity.DashboardUserDO;
+import org.apache.shenyu.admin.model.enums.EventTypeEnum;
+
+/**
+ * UserCreatedEvent.
+ */
+public class UserCreatedEvent extends UserChangedEvent {
+    
+    
+    /**
+     * Create a new {@code UserCreatedEvent}.operator is unknown.
+     *
+     * @param source   Current user state
+     * @param operator operator
+     */
+    public UserCreatedEvent(final DashboardUserDO source, final String operator) {
+        super(source, null, EventTypeEnum.USER_CREATE, operator);
+    }
+    
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/UserUpdatedEvent.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/UserUpdatedEvent.java
new file mode 100644
index 000000000..241d81676
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/event/user/UserUpdatedEvent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.user;
+
+import org.apache.shenyu.admin.model.entity.DashboardUserDO;
+import org.apache.shenyu.admin.model.enums.EventTypeEnum;
+
+/**
+ * UserUpdatedEvent.
+ */
+public class UserUpdatedEvent extends UserChangedEvent {
+    
+    
+    /**
+     * Create a new {@code UserUpdatedEvent}.operator is unknown.
+     *
+     * @param source   Current user state
+     * @param before   before user state
+     * @param operator operator
+     */
+    public UserUpdatedEvent(final DashboardUserDO source, final DashboardUserDO before, final String operator) {
+        super(source, before, EventTypeEnum.USER_UPDATE, operator);
+    }
+    
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/DashboardUserService.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/DashboardUserService.java
index f1a4f11ed..c2475adcb 100644
--- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/DashboardUserService.java
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/DashboardUserService.java
@@ -24,7 +24,7 @@ import org.apache.shenyu.admin.model.vo.DashboardUserEditVO;
 import org.apache.shenyu.admin.model.vo.DashboardUserVO;
 import org.apache.shenyu.admin.model.vo.LoginDashboardUserVO;
 
-import java.util.List;
+import java.util.Set;
 
 /**
  * this is dashboard user service.
@@ -38,6 +38,22 @@ public interface DashboardUserService {
      * @return rows
      */
     int createOrUpdate(DashboardUserDTO dashboardUserDTO);
+    
+    /**
+     * create dashboard user.
+     *
+     * @param dashboardUserDTO {@linkplain DashboardUserDTO}
+     * @return rows
+     */
+    int create(DashboardUserDTO dashboardUserDTO);
+    
+    /**
+     *  update dashboard user.
+     *
+     * @param dashboardUserDTO {@linkplain DashboardUserDTO}
+     * @return rows
+     */
+    int update(DashboardUserDTO dashboardUserDTO);
 
     /**
      * delete dashboard users.
@@ -45,7 +61,7 @@ public interface DashboardUserService {
      * @param ids primary key.
      * @return rows
      */
-    int delete(List<String> ids);
+    int delete(Set<String> ids);
 
     /**
      * find dashboard user by id.
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DashboardUserServiceImpl.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DashboardUserServiceImpl.java
index a22ae2618..e4f9a93c4 100644
--- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DashboardUserServiceImpl.java
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DashboardUserServiceImpl.java
@@ -23,7 +23,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.shenyu.admin.config.properties.JwtProperties;
 import org.apache.shenyu.admin.config.properties.LdapProperties;
 import org.apache.shenyu.admin.mapper.DashboardUserMapper;
-import org.apache.shenyu.admin.mapper.DataPermissionMapper;
 import org.apache.shenyu.admin.mapper.RoleMapper;
 import org.apache.shenyu.admin.mapper.UserRoleMapper;
 import org.apache.shenyu.admin.model.dto.DashboardUserDTO;
@@ -39,8 +38,11 @@ import org.apache.shenyu.admin.model.vo.DashboardUserVO;
 import org.apache.shenyu.admin.model.vo.LoginDashboardUserVO;
 import org.apache.shenyu.admin.model.vo.RoleVO;
 import org.apache.shenyu.admin.service.DashboardUserService;
+import org.apache.shenyu.admin.service.publish.UserEventPublisher;
 import org.apache.shenyu.admin.transfer.DashboardUserTransfer;
+import org.apache.shenyu.admin.utils.Assert;
 import org.apache.shenyu.admin.utils.JwtUtils;
+import org.apache.shenyu.admin.utils.ListUtil;
 import org.apache.shenyu.common.constant.AdminConstants;
 import org.apache.shenyu.common.utils.ShaUtils;
 import org.slf4j.Logger;
@@ -52,7 +54,6 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Nullable;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -64,41 +65,41 @@ import java.util.stream.Collectors;
  */
 @Service
 public class DashboardUserServiceImpl implements DashboardUserService {
-
+    
     private static final Logger LOG = LoggerFactory.getLogger(DashboardUserServiceImpl.class);
-
+    
     private final DashboardUserMapper dashboardUserMapper;
-
+    
     private final UserRoleMapper userRoleMapper;
-
+    
     private final RoleMapper roleMapper;
-
-    private final DataPermissionMapper dataPermissionMapper;
-
+    
     @Nullable
     private final LdapProperties ldapProperties;
-
+    
     @Nullable
     private final LdapTemplate ldapTemplate;
-
+    
     private final JwtProperties jwtProperties;
-
+    
+    private final UserEventPublisher publisher;
+    
     public DashboardUserServiceImpl(final DashboardUserMapper dashboardUserMapper,
                                     final UserRoleMapper userRoleMapper,
                                     final RoleMapper roleMapper,
-                                    final DataPermissionMapper dataPermissionMapper,
                                     @Nullable final LdapProperties ldapProperties,
                                     @Nullable final LdapTemplate ldapTemplate,
-                                    final JwtProperties jwtProperties) {
+                                    final JwtProperties jwtProperties,
+                                    final UserEventPublisher publisher) {
         this.dashboardUserMapper = dashboardUserMapper;
         this.userRoleMapper = userRoleMapper;
         this.roleMapper = roleMapper;
-        this.dataPermissionMapper = dataPermissionMapper;
         this.ldapProperties = ldapProperties;
         this.ldapTemplate = ldapTemplate;
         this.jwtProperties = jwtProperties;
+        this.publisher = publisher;
     }
-
+    
     /**
      * create or update dashboard user.
      *
@@ -108,13 +109,24 @@ public class DashboardUserServiceImpl implements DashboardUserService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     public int createOrUpdate(final DashboardUserDTO dashboardUserDTO) {
+        return StringUtils.isBlank(dashboardUserDTO.getId()) ? create(dashboardUserDTO) : update(dashboardUserDTO);
+    }
+    
+    @Override
+    public int create(final DashboardUserDTO dashboardUserDTO) {
         DashboardUserDO dashboardUserDO = DashboardUserDO.buildDashboardUserDO(dashboardUserDTO);
         // create new user
-        if (StringUtils.isEmpty(dashboardUserDTO.getId())) {
-            bindUserRole(dashboardUserDO.getId(), dashboardUserDTO.getRoles());
-            return dashboardUserMapper.insertSelective(dashboardUserDO);
+        final int insertCount = dashboardUserMapper.insertSelective(dashboardUserDO);
+        bindUserRole(dashboardUserDO.getId(), dashboardUserDTO.getRoles());
+        if (insertCount > 0) {
+            publisher.onCreated(dashboardUserDO);
         }
-
+        return insertCount;
+    }
+    
+    @Override
+    public int update(final DashboardUserDTO dashboardUserDTO) {
+        DashboardUserDO dashboardUserDO = DashboardUserDO.buildDashboardUserDO(dashboardUserDTO);
         // update old user
         if (CollectionUtils.isNotEmpty(dashboardUserDTO.getRoles())) {
             if (!AdminConstants.ADMIN_NAME.equals(dashboardUserDTO.getUserName())) {
@@ -122,10 +134,14 @@ public class DashboardUserServiceImpl implements DashboardUserService {
             }
             bindUserRole(dashboardUserDTO.getId(), dashboardUserDTO.getRoles());
         }
-
-        return dashboardUserMapper.updateSelective(dashboardUserDO);
+        final DashboardUserDO before = dashboardUserMapper.selectById(dashboardUserDO.getId());
+        final int updateCount = dashboardUserMapper.updateSelective(dashboardUserDO);
+        if (updateCount > 0) {
+            publisher.onUpdated(dashboardUserDO, before);
+        }
+        return updateCount;
     }
-
+    
     /**
      * delete dashboard users.
      *
@@ -134,16 +150,22 @@ public class DashboardUserServiceImpl implements DashboardUserService {
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public int delete(final List<String> ids) {
-        List<String> idList = ids.stream().filter(StringUtils::isNotEmpty).distinct().collect(Collectors.toList());
-        Optional.ofNullable(dashboardUserMapper.selectByUserName(AdminConstants.ADMIN_NAME))
-                .ifPresent(userDO -> idList.remove(userDO.getId()));
-        int ret = dashboardUserMapper.deleteByIdList(idList);
-        userRoleMapper.deleteByUserIdList(idList);
-        dataPermissionMapper.deleteByUserIdList(idList);
-        return ret;
+    public int delete(final Set<String> ids) {
+        final List<DashboardUserDO> deletedUser = dashboardUserMapper.selectByIds(ids)
+                .stream()
+                // skip default admin user
+                .filter(u -> !Objects.equals(u.getUserName(), AdminConstants.ADMIN_NAME))
+                .collect(Collectors.toList());
+        final List<String> deletedIds = ListUtil.map(deletedUser, DashboardUserDO::getId);
+        int deleteCount = dashboardUserMapper.deleteByIdList(deletedIds);
+        if (deleteCount > 0) {
+            userRoleMapper.deleteByUserIdList(deletedIds);
+            publisher.onDeleted(deletedUser);
+            
+        }
+        return deleteCount;
     }
-
+    
     /**
      * find dashboard user by id.
      *
@@ -152,29 +174,25 @@ public class DashboardUserServiceImpl implements DashboardUserService {
      */
     @Override
     public DashboardUserEditVO findById(final String id) {
-
+        
         DashboardUserVO dashboardUserVO = DashboardUserVO.buildDashboardUserVO(dashboardUserMapper.selectById(id));
-
+        
         Set<String> roleIdSet = userRoleMapper.findByUserId(id)
                 .stream()
                 .map(UserRoleDO::getRoleId)
                 .collect(Collectors.toSet());
-
+        
         List<RoleDO> allRoleDOList = roleMapper.selectAll();
-        List<RoleVO> allRoles = allRoleDOList.stream()
-                .map(RoleVO::buildRoleVO).collect(Collectors.toList());
-
+        List<RoleVO> allRoles = ListUtil.map(allRoleDOList, RoleVO::buildRoleVO);
+        
         List<RoleDO> roleDOList = allRoleDOList.stream()
                 .filter(roleDO -> roleIdSet.contains(roleDO.getId()))
                 .collect(Collectors.toList());
-        List<RoleVO> roles = roleDOList.stream()
-                .map(RoleVO::buildRoleVO)
-                .filter(Objects::nonNull)
-                .collect(Collectors.toList());
-
+        List<RoleVO> roles = ListUtil.map(roleDOList, RoleVO::buildRoleVO);
+        
         return DashboardUserEditVO.buildDashboardUserEditVO(dashboardUserVO, roles, allRoles);
     }
-
+    
     /**
      * find dashboard user by query.
      *
@@ -186,7 +204,7 @@ public class DashboardUserServiceImpl implements DashboardUserService {
     public DashboardUserVO findByQuery(final String userName, final String password) {
         return DashboardUserVO.buildDashboardUserVO(dashboardUserMapper.findByQuery(userName, password));
     }
-
+    
     /**
      * find dashboard user by username.
      *
@@ -197,7 +215,7 @@ public class DashboardUserServiceImpl implements DashboardUserService {
     public DashboardUserVO findByUserName(final String userName) {
         return DashboardUserVO.buildDashboardUserVO(dashboardUserMapper.selectByUserName(userName));
     }
-
+    
     /**
      * find page of dashboard user by query.
      *
@@ -208,12 +226,9 @@ public class DashboardUserServiceImpl implements DashboardUserService {
     public CommonPager<DashboardUserVO> listByPage(final DashboardUserQuery dashboardUserQuery) {
         return PageResultUtils.result(dashboardUserQuery.getPageParameter(),
             () -> dashboardUserMapper.countByQuery(dashboardUserQuery),
-            () -> dashboardUserMapper.selectByQuery(dashboardUserQuery)
-                        .stream()
-                        .map(DashboardUserVO::buildDashboardUserVO)
-                        .collect(Collectors.toList()));
+            () -> ListUtil.map(dashboardUserMapper.selectByQuery(dashboardUserQuery), DashboardUserVO::buildDashboardUserVO));
     }
-
+    
     /**
      * To deal with the admin login.
      *
@@ -227,11 +242,11 @@ public class DashboardUserServiceImpl implements DashboardUserService {
         if (Objects.nonNull(ldapTemplate)) {
             dashboardUserVO = loginByLdap(userName, password);
         }
-
+        
         if (Objects.isNull(dashboardUserVO)) {
             dashboardUserVO = loginByDatabase(userName, password);
         }
-
+        
         final LoginDashboardUserVO loginDashboardUserVO = LoginDashboardUserVO.buildLoginDashboardUserVO(dashboardUserVO);
         final DashboardUserVO finalDashboardUserVO = dashboardUserVO;
         return Optional.ofNullable(loginDashboardUserVO).map(loginUser -> {
@@ -242,13 +257,14 @@ public class DashboardUserServiceImpl implements DashboardUserService {
                     jwtProperties.getExpiredSeconds())).setExpiredTime(jwtProperties.getExpiredSeconds());
         }).orElse(null);
     }
-
+    
     private DashboardUserVO loginByLdap(final String userName, final String password) {
+        Assert.notNull(ldapProperties, "ldap config is not enable");
         String searchBase = String.format("%s=%s,%s", ldapProperties.getLoginField(), LdapEncoder.nameEncode(userName), ldapProperties.getBaseDn());
         String filter = String.format("(objectClass=%s)", ldapProperties.getObjectClass());
         try {
             DashboardUserVO dashboardUserVO = null;
-            if (ldapTemplate.authenticate(searchBase, filter, password)) {
+            if (Objects.nonNull(ldapTemplate) && ldapTemplate.authenticate(searchBase, filter, password)) {
                 dashboardUserVO = findByUserName(userName);
                 if (Objects.isNull(dashboardUserVO)) {
                     RoleDO role = roleMapper.findByRoleName("default");
@@ -271,24 +287,26 @@ public class DashboardUserServiceImpl implements DashboardUserService {
             return null;
         }
     }
-
+    
     private DashboardUserVO loginByDatabase(final String userName, final String password) {
         return findByQuery(userName, ShaUtils.shaEncryption(password));
     }
-
+    
     /**
      * bind user and role id.
      *
-     * @param userId user id
+     * @param userId  user id
      * @param roleIds role ids.
      */
     private void bindUserRole(final String userId, final List<String> roleIds) {
-        List<UserRoleDO> userRoleDOList = Optional.ofNullable(roleIds).orElseGet(ArrayList::new)
-                        .stream().map(roleId -> UserRoleDO.buildUserRoleDO(UserRoleDTO.builder().userId(userId).roleId(roleId).build()))
-                        .collect(Collectors.toList());
-        if (CollectionUtils.isEmpty(userRoleDOList)) {
+        if (CollectionUtils.isEmpty(roleIds)) {
             return;
         }
-        userRoleMapper.insertBatch(userRoleDOList);
+        userRoleMapper.insertBatch(roleIds.stream()
+                .map(roleId -> UserRoleDO.buildUserRoleDO(UserRoleDTO.builder()
+                        .userId(userId)
+                        .roleId(roleId)
+                        .build()))
+                .collect(Collectors.toList()));
     }
 }
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DataPermissionServiceImpl.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DataPermissionServiceImpl.java
index 628bba398..b50b290bd 100644
--- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DataPermissionServiceImpl.java
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DataPermissionServiceImpl.java
@@ -28,6 +28,7 @@ import org.apache.shenyu.admin.model.entity.SelectorDO;
 import org.apache.shenyu.admin.model.event.rule.RuleCreatedEvent;
 import org.apache.shenyu.admin.model.event.selector.BatchSelectorDeletedEvent;
 import org.apache.shenyu.admin.model.event.selector.SelectorCreatedEvent;
+import org.apache.shenyu.admin.model.event.user.BatchUserDeletedEvent;
 import org.apache.shenyu.admin.model.page.CommonPager;
 import org.apache.shenyu.admin.model.page.PageResultUtils;
 import org.apache.shenyu.admin.model.query.RuleQuery;
@@ -292,4 +293,14 @@ public class DataPermissionServiceImpl implements DataPermissionService {
         // check selector delete
         dataPermissionMapper.deleteByDataIdList(event.getDeletedIds());
     }
+    
+    /**
+     * listen {@link BatchUserDeletedEvent} delete data permission.
+     *
+     * @param event event
+     */
+    @EventListener(BatchUserDeletedEvent.class)
+    public void onUserDeleted(final BatchUserDeletedEvent event) {
+        dataPermissionMapper.deleteByUserIdList(event.getDeletedIds());
+    }
 }
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/UserEventPublisher.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/UserEventPublisher.java
new file mode 100644
index 000000000..b128315c9
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/publish/UserEventPublisher.java
@@ -0,0 +1,72 @@
+/*
+ * 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.entity.DashboardUserDO;
+import org.apache.shenyu.admin.model.event.AdminDataModelChangedEvent;
+import org.apache.shenyu.admin.model.event.user.BatchUserDeletedEvent;
+import org.apache.shenyu.admin.model.event.user.UserCreatedEvent;
+import org.apache.shenyu.admin.model.event.user.UserUpdatedEvent;
+import org.apache.shenyu.admin.utils.SessionUtil;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+
+/**
+ * UserEventPublisher.
+ */
+@Component
+public class UserEventPublisher implements AdminDataModelChangedEventPublisher<DashboardUserDO> {
+    
+    private final ApplicationEventPublisher publisher;
+    
+    public UserEventPublisher(final ApplicationEventPublisher publisher) {
+        this.publisher = publisher;
+    }
+    
+    @Override
+    public void onCreated(final DashboardUserDO data) {
+        publish(new UserCreatedEvent(data, SessionUtil.visitorName()));
+    }
+    
+    @Override
+    public void onUpdated(final DashboardUserDO data, final DashboardUserDO before) {
+        publish(new UserUpdatedEvent(data, before, SessionUtil.visitorName()));
+    }
+    
+    /**
+     * on user deleted.
+     *
+     * @param users selectors
+     */
+    @Override
+    public void onDeleted(final Collection<DashboardUserDO> users) {
+        publish(new BatchUserDeletedEvent(users, SessionUtil.visitorName()));
+    }
+    
+    /**
+     * event.
+     *
+     * @param event event.
+     */
+    @Override
+    public void publish(final AdminDataModelChangedEvent event) {
+        publisher.publishEvent(event);
+    }
+}
diff --git a/shenyu-admin/src/main/resources/mappers/dashboard-user-sqlmap.xml b/shenyu-admin/src/main/resources/mappers/dashboard-user-sqlmap.xml
index b1b0bf316..15521aa8c 100644
--- a/shenyu-admin/src/main/resources/mappers/dashboard-user-sqlmap.xml
+++ b/shenyu-admin/src/main/resources/mappers/dashboard-user-sqlmap.xml
@@ -29,11 +29,12 @@
     </resultMap>
 
     <sql id="Base_Column_List">
-        id, 
-        date_created, 
-        date_updated, 
-        user_name, 
-        password, role, 
+        id,
+        date_created,
+        date_updated,
+        user_name,
+        password,
+        role,
         enabled
     </sql>
 
@@ -85,6 +86,16 @@
          LIMIT 1
     </select>
 
+    <select id="selectByIds" resultType="org.apache.shenyu.admin.model.entity.DashboardUserDO">
+        select
+            <include refid="Base_Column_List"/>
+            FROM dashboard_user
+        WHERE id IN
+        <foreach collection="ids" index="index" item="id" open="(" separator="," close=")">
+            #{id, jdbcType=VARCHAR}
+        </foreach>
+    </select>
+
     <sql id="likeUser">
         <if test="userName != null and userName != ''">
             <bind name="nameLike" value="('%' + userName + '%')"/>
diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DashboardUserServiceTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DashboardUserServiceTest.java
index 89022cfd4..6664bda68 100644
--- a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DashboardUserServiceTest.java
+++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DashboardUserServiceTest.java
@@ -20,7 +20,6 @@ package org.apache.shenyu.admin.service;
 import org.apache.shenyu.admin.config.properties.JwtProperties;
 import org.apache.shenyu.admin.config.properties.LdapProperties;
 import org.apache.shenyu.admin.mapper.DashboardUserMapper;
-import org.apache.shenyu.admin.mapper.DataPermissionMapper;
 import org.apache.shenyu.admin.mapper.RoleMapper;
 import org.apache.shenyu.admin.mapper.UserRoleMapper;
 import org.apache.shenyu.admin.model.dto.DashboardUserDTO;
@@ -33,6 +32,8 @@ import org.apache.shenyu.admin.model.query.DashboardUserQuery;
 import org.apache.shenyu.admin.model.vo.DashboardUserVO;
 import org.apache.shenyu.admin.model.vo.LoginDashboardUserVO;
 import org.apache.shenyu.admin.service.impl.DashboardUserServiceImpl;
+import org.apache.shenyu.admin.service.publish.UserEventPublisher;
+import org.apache.shenyu.admin.utils.ListUtil;
 import org.apache.shenyu.common.utils.ShaUtils;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -44,6 +45,7 @@ import org.springframework.test.util.ReflectionTestUtils;
 
 import java.sql.Timestamp;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -63,34 +65,34 @@ import static org.mockito.Mockito.when;
  */
 @ExtendWith(MockitoExtension.class)
 public final class DashboardUserServiceTest {
-
+    
     public static final String TEST_ID = "testId";
-
+    
     public static final String TEST_USER_NAME = "userName";
-
+    
     public static final String TEST_PASSWORD = "password";
-
+    
     @InjectMocks
     private DashboardUserServiceImpl dashboardUserService;
-
+    
     @Mock
     private DashboardUserMapper dashboardUserMapper;
-
+    
     @Mock
     private UserRoleMapper userRoleMapper;
-
+    
     @Mock
     private RoleMapper roleMapper;
-
+    
     @Mock
-    private DataPermissionMapper dataPermissionMapper;
-
+    private UserEventPublisher publisher;
+    
     @Mock
     private JwtProperties jwtProperties;
-
+    
     @Mock
     private LdapTemplate ldapTemplate;
-
+    
     @Test
     public void testCreateOrUpdate() {
         DashboardUserDTO dashboardUserDTO = DashboardUserDTO.builder()
@@ -99,67 +101,79 @@ public final class DashboardUserServiceTest {
         given(dashboardUserMapper.insertSelective(any(DashboardUserDO.class))).willReturn(1);
         assertEquals(1, dashboardUserService.createOrUpdate(dashboardUserDTO));
         verify(dashboardUserMapper).insertSelective(any(DashboardUserDO.class));
-
+        
         dashboardUserDTO.setId(TEST_ID);
         given(dashboardUserMapper.updateSelective(any(DashboardUserDO.class))).willReturn(2);
         assertEquals(2, dashboardUserService.createOrUpdate(dashboardUserDTO));
         verify(dashboardUserMapper).updateSelective(any(DashboardUserDO.class));
     }
-
+    
     @Test
     public void testDelete() {
         List<String> deleteIds = Stream.of("1", "2").collect(Collectors.toList());
         given(userRoleMapper.deleteByUserIdList(deleteIds)).willReturn(deleteIds.size());
-        given(dataPermissionMapper.deleteByUserIdList(deleteIds)).willReturn(deleteIds.size());
+//        given(dataPermissionMapper.deleteByUserIdList(deleteIds)).willReturn(deleteIds.size());
+        given(dashboardUserMapper.selectByIds(new HashSet<>(deleteIds))).willReturn(ListUtil.map(deleteIds, this::mockUserById));
         given(dashboardUserMapper.deleteByIdList(deleteIds)).willReturn(deleteIds.size());
-        assertEquals(deleteIds.size(), dashboardUserService.delete(deleteIds));
+        assertEquals(deleteIds.size(), dashboardUserService.delete(new HashSet<>(deleteIds)));
     }
-
+    
+    private DashboardUserDO mockUserById(final String id) {
+        return DashboardUserDO.builder()
+                .id(id)
+                .userName("mockUser" + id)
+                .enabled(true)
+                .role(1)
+                .dateCreated(new Timestamp(System.currentTimeMillis()))
+                .dateUpdated(new Timestamp(System.currentTimeMillis()))
+                .build();
+    }
+    
     @Test
     public void testFindById() {
         DashboardUserDO dashboardUserDO = createDashboardUserDO();
         given(dashboardUserMapper.selectById(eq(TEST_ID))).willReturn(dashboardUserDO);
-
+        
         DashboardUserVO dashboardUserVO = dashboardUserService.findById(TEST_ID);
         assertEquals(TEST_ID, dashboardUserVO.getId());
         verify(dashboardUserMapper).selectById(eq(TEST_ID));
     }
-
+    
     @Test
     public void testFindByQuery() {
         DashboardUserDO dashboardUserDO = createDashboardUserDO();
         given(dashboardUserMapper.findByQuery(eq(TEST_USER_NAME), eq(TEST_PASSWORD))).willReturn(dashboardUserDO);
-
+        
         DashboardUserVO dashboardUserVO = dashboardUserService.findByQuery(TEST_USER_NAME, TEST_PASSWORD);
         assertEquals(TEST_ID, dashboardUserVO.getId());
         assertEquals(TEST_USER_NAME, dashboardUserVO.getUserName());
         assertEquals(TEST_PASSWORD, dashboardUserVO.getPassword());
         verify(dashboardUserMapper).findByQuery(eq(TEST_USER_NAME), eq(TEST_PASSWORD));
     }
-
+    
     @Test
     public void testFindByUsername() {
         DashboardUserDO dashboardUserDO = createDashboardUserDO();
         given(dashboardUserMapper.selectByUserName(eq(TEST_USER_NAME))).willReturn(dashboardUserDO);
-
+        
         DashboardUserVO dashboardUserVO = dashboardUserService.findByUserName(TEST_USER_NAME);
         assertEquals(TEST_ID, dashboardUserVO.getId());
         assertEquals(TEST_USER_NAME, dashboardUserVO.getUserName());
         assertEquals(TEST_PASSWORD, dashboardUserVO.getPassword());
         verify(dashboardUserMapper).selectByUserName(eq(TEST_USER_NAME));
     }
-
+    
     @Test
     public void testListByPage() {
         DashboardUserQuery dashboardUserQuery = new DashboardUserQuery();
         dashboardUserQuery.setUserName(TEST_USER_NAME);
         PageParameter pageParameter = new PageParameter();
         dashboardUserQuery.setPageParameter(pageParameter);
-
+        
         given(dashboardUserMapper.countByQuery(eq(dashboardUserQuery))).willReturn(1);
         DashboardUserDO dashboardUserDO = createDashboardUserDO();
         given(dashboardUserMapper.selectByQuery(eq(dashboardUserQuery))).willReturn(Collections.singletonList(dashboardUserDO));
-
+        
         CommonPager<DashboardUserVO> commonPager = dashboardUserService.listByPage(dashboardUserQuery);
         assertThat(commonPager.getDataList()).isNotNull().isNotEmpty();
         assertEquals(1, commonPager.getDataList().size());
@@ -167,16 +181,16 @@ public final class DashboardUserServiceTest {
         verify(dashboardUserMapper).countByQuery(eq(dashboardUserQuery));
         verify(dashboardUserMapper).selectByQuery(eq(dashboardUserQuery));
     }
-
+    
     @Test
     public void testLogin() {
         ReflectionTestUtils.setField(dashboardUserService, "jwtProperties", jwtProperties);
         DashboardUserDO dashboardUserDO = createDashboardUserDO();
-
+        
         when(dashboardUserMapper.findByQuery(eq(TEST_USER_NAME), anyString())).thenReturn(dashboardUserDO);
         given(ldapTemplate.authenticate(anyString(), anyString(), anyString())).willReturn(true);
         given(roleMapper.findByRoleName("default")).willReturn(RoleDO.buildRoleDO(new RoleDTO("1", "test", null, null)));
-
+        
         // test loginByLdap
         LdapProperties ldapProperties = new LdapProperties();
         ldapProperties.setBaseDn("test");
@@ -185,7 +199,7 @@ public final class DashboardUserServiceTest {
         LoginDashboardUserVO loginDashboardUserVO = dashboardUserService.login(TEST_USER_NAME, TEST_PASSWORD);
         assertEquals(TEST_USER_NAME, loginDashboardUserVO.getUserName());
         assertEquals(ShaUtils.shaEncryption(TEST_PASSWORD), loginDashboardUserVO.getPassword());
-
+        
         // test loginByDatabase
         ReflectionTestUtils.setField(dashboardUserService, "ldapTemplate", null);
         assertLoginSuccessful(dashboardUserDO, dashboardUserService.login(TEST_USER_NAME, TEST_PASSWORD));
@@ -193,7 +207,7 @@ public final class DashboardUserServiceTest {
         assertLoginSuccessful(dashboardUserDO, dashboardUserService.login(TEST_USER_NAME, TEST_PASSWORD));
         verify(dashboardUserMapper, times(2)).findByQuery(eq(TEST_USER_NAME), anyString());
     }
-
+    
     private DashboardUserDO createDashboardUserDO() {
         return DashboardUserDO.builder()
                 .id(TEST_ID).userName(TEST_USER_NAME).password(TEST_PASSWORD)
@@ -201,7 +215,7 @@ public final class DashboardUserServiceTest {
                 .dateUpdated(new Timestamp(System.currentTimeMillis()))
                 .build();
     }
-
+    
     private void assertLoginSuccessful(final DashboardUserDO dashboardUserDO, final DashboardUserVO dashboardUserVO) {
         assertEquals(dashboardUserDO.getId(), dashboardUserVO.getId());
         assertEquals(dashboardUserDO.getUserName(), dashboardUserVO.getUserName());