ソースを参照

增加事件可配置支持

zhou-hao 3 年 前
コミット
da89ab202f
12 ファイル変更508 行追加95 行削除
  1. 11 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/annotation/EnableEntityEvent.java
  2. 8 6
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/configuration/EasyormConfiguration.java
  3. 105 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/DefaultEntityEventListenerConfigure.java
  4. 185 89
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventListener.java
  5. 73 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventListenerConfigure.java
  6. 18 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventListenerCustomizer.java
  7. 9 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventPhase.java
  8. 8 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventType.java
  9. 21 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityPrepareCreateEvent.java
  10. 26 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityPrepareModifyEvent.java
  11. 21 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityPrepareSaveEvent.java
  12. 23 0
      hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/events/DefaultEntityEventListenerConfigureTest.java

+ 11 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/annotation/EnableEntityEvent.java

@@ -1,7 +1,11 @@
 package org.hswebframework.web.crud.annotation;
 
+import org.hswebframework.web.crud.events.EntityEventType;
+
 import java.lang.annotation.*;
 
+//import static org.hswebframework.web.crud.annotation.EnableEntityEvent.Feature.*;
+
 /**
  * 在实体类上添加此注解,表示开启实体操作事件,当实体类发生类修改,更新,删除等操作时,会触发事件。
  * 可以通过spring event监听事件:
@@ -31,4 +35,11 @@ import java.lang.annotation.*;
 @Documented
 public @interface EnableEntityEvent {
 
+    EntityEventType[] value() default {
+            EntityEventType.create,
+            EntityEventType.delete,
+            EntityEventType.modify,
+            EntityEventType.save
+    };
+
 }

+ 8 - 6
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/configuration/EasyormConfiguration.java

@@ -19,9 +19,7 @@ import org.hswebframework.web.api.crud.entity.EntityFactory;
 import org.hswebframework.web.crud.annotation.EnableEasyormRepository;
 import org.hswebframework.web.crud.entity.factory.EntityMappingCustomizer;
 import org.hswebframework.web.crud.entity.factory.MapperEntityFactory;
-import org.hswebframework.web.crud.events.CompositeEventListener;
-import org.hswebframework.web.crud.events.EntityEventListener;
-import org.hswebframework.web.crud.events.ValidateEventListener;
+import org.hswebframework.web.crud.events.*;
 import org.hswebframework.web.crud.generator.CurrentTimeGenerator;
 import org.hswebframework.web.crud.generator.DefaultIdGenerator;
 import org.hswebframework.web.crud.generator.MD5Generator;
@@ -33,6 +31,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -55,7 +54,7 @@ public class EasyormConfiguration {
     @Bean
     @ConditionalOnMissingBean
     public EntityFactory entityFactory(ObjectProvider<EntityMappingCustomizer> customizers) {
-        MapperEntityFactory factory= new MapperEntityFactory();
+        MapperEntityFactory factory = new MapperEntityFactory();
         for (EntityMappingCustomizer customizer : customizers) {
             customizer.custom(factory);
         }
@@ -149,8 +148,11 @@ public class EasyormConfiguration {
     }
 
     @Bean
-    public EntityEventListener entityEventListener() {
-        return new EntityEventListener();
+    public EntityEventListener entityEventListener(ApplicationEventPublisher eventPublisher,
+                                                   ObjectProvider<EntityEventListenerCustomizer> customizers) {
+        DefaultEntityEventListenerConfigure configure = new DefaultEntityEventListenerConfigure();
+        customizers.forEach(customizer -> customizer.customize(configure));
+        return new EntityEventListener(eventPublisher, configure);
     }
 
     @Bean

+ 105 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/DefaultEntityEventListenerConfigure.java

@@ -0,0 +1,105 @@
+package org.hswebframework.web.crud.events;
+
+import org.apache.commons.collections4.MapUtils;
+import org.hswebframework.web.api.crud.entity.Entity;
+import org.hswebframework.web.crud.annotation.EnableEntityEvent;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class DefaultEntityEventListenerConfigure implements EntityEventListenerConfigure {
+
+    private final Map<Class<? extends Entity>, Map<EntityEventType, Set<EntityEventPhase>>> enabledFeatures = new ConcurrentHashMap<>();
+    private final Map<Class<? extends Entity>, Map<EntityEventType, Set<EntityEventPhase>>> disabledFeatures = new ConcurrentHashMap<>();
+
+    @Override
+    public void enable(Class<? extends Entity> entityType) {
+        initByEntity(entityType, getOrCreateTypeMap(entityType, enabledFeatures), true);
+    }
+
+    @Override
+    public void disable(Class<? extends Entity> entityType) {
+        enabledFeatures.remove(entityType);
+        initByEntity(entityType, getOrCreateTypeMap(entityType, disabledFeatures), true);
+    }
+
+    @Override
+    public void enable(Class<? extends Entity> entityType, EntityEventType type, EntityEventPhase... feature) {
+        if (feature.length == 0) {
+            feature = EntityEventPhase.all;
+        }
+        getOrCreatePhaseSet(type, getOrCreateTypeMap(entityType, enabledFeatures))
+                .addAll(Arrays.asList(feature));
+
+        //删除disabled
+        Arrays.asList(feature)
+              .forEach(getOrCreatePhaseSet(type, getOrCreateTypeMap(entityType, disabledFeatures))::remove);
+    }
+
+    @Override
+    public void disable(Class<? extends Entity> entityType, EntityEventType type, EntityEventPhase... feature) {
+        if (feature.length == 0) {
+            feature = EntityEventPhase.all;
+        }
+        getOrCreatePhaseSet(type, getOrCreateTypeMap(entityType, disabledFeatures))
+                .addAll(Arrays.asList(feature));
+        //删除enabled
+        Arrays.asList(feature)
+              .forEach(getOrCreatePhaseSet(type, getOrCreateTypeMap(entityType, enabledFeatures))::remove);
+    }
+
+    protected Map<EntityEventType, Set<EntityEventPhase>> getOrCreateTypeMap(Class<? extends Entity> type,
+                                                                             Map<Class<? extends Entity>, Map<EntityEventType, Set<EntityEventPhase>>> map) {
+        return map.computeIfAbsent(type, ignore -> new EnumMap<>(EntityEventType.class));
+    }
+
+    protected Set<EntityEventPhase> getOrCreatePhaseSet(EntityEventType type,
+                                                        Map<EntityEventType, Set<EntityEventPhase>> map) {
+        return map.computeIfAbsent(type, ignore -> EnumSet.noneOf(EntityEventPhase.class));
+    }
+
+    protected void initByEntity(Class<? extends Entity> type,
+                                Map<EntityEventType, Set<EntityEventPhase>> typeSetMap,
+                                boolean all) {
+        EnableEntityEvent annotation = AnnotatedElementUtils.findMergedAnnotation(type, EnableEntityEvent.class);
+        EntityEventType[] types = annotation != null ? annotation.value() : all ? EntityEventType.values() : new EntityEventType[0];
+
+        for (EntityEventType entityEventType : types) {
+            Set<EntityEventPhase> phases = getOrCreatePhaseSet(entityEventType, typeSetMap);
+            phases.addAll(Arrays.asList(EntityEventPhase.values()));
+        }
+    }
+
+    @Override
+    public boolean isEnabled(Class<? extends Entity> entityType) {
+        if (!enabledFeatures.containsKey(entityType)) {
+            initByEntity(entityType, getOrCreateTypeMap(entityType, enabledFeatures), false);
+        }
+        return MapUtils.isNotEmpty(enabledFeatures.get(entityType));
+    }
+
+    @Override
+    public boolean isEnabled(Class<? extends Entity> entityType,
+                             EntityEventType type,
+                             EntityEventPhase phase) {
+        if (!enabledFeatures.containsKey(entityType)) {
+            initByEntity(entityType, getOrCreateTypeMap(entityType, enabledFeatures), false);
+        }
+        Map<EntityEventType, Set<EntityEventPhase>> enabled = enabledFeatures.get(entityType);
+        if (MapUtils.isEmpty(enabled)) {
+            return false;
+        }
+        Map<EntityEventType, Set<EntityEventPhase>> disabled = disabledFeatures.get(entityType);
+        Set<EntityEventPhase> phases = enabled.get(type);
+        if (phases != null && phases.contains(phase)) {
+            if (disabled != null) {
+                Set<EntityEventPhase> disabledPhases = disabled.get(type);
+                return disabledPhases == null || !disabledPhases.contains(phase);
+            }
+            return true;
+        }
+
+        return false;
+    }
+}

+ 185 - 89
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventListener.java

@@ -1,9 +1,11 @@
 package org.hswebframework.web.crud.events;
 
 
+import lombok.AllArgsConstructor;
 import org.apache.commons.collections.CollectionUtils;
 import org.hswebframework.ezorm.core.param.QueryParam;
 import org.hswebframework.ezorm.rdb.events.*;
+import org.hswebframework.ezorm.rdb.events.EventType;
 import org.hswebframework.ezorm.rdb.mapping.*;
 import org.hswebframework.ezorm.rdb.mapping.events.MappingContextKeys;
 import org.hswebframework.ezorm.rdb.mapping.events.MappingEventTypes;
@@ -31,10 +33,12 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiFunction;
 
 @SuppressWarnings("all")
+@AllArgsConstructor
 public class EntityEventListener implements EventListener {
 
-    @Autowired
-    ApplicationEventPublisher eventPublisher;
+    private final ApplicationEventPublisher eventPublisher;
+
+    private final EntityEventListenerConfigure listenerConfigure;
 
     @Override
     public String getId() {
@@ -53,36 +57,59 @@ public class EntityEventListener implements EventListener {
             return;
         }
         EntityColumnMapping mapping = context.get(MappingContextKeys.columnMapping).orElse(null);
+        Class<Entity> entityType;
+
         if (mapping == null ||
-                !Entity.class.isAssignableFrom(mapping.getEntityType()) ||
-                mapping.getEntityType().getAnnotation(EnableEntityEvent.class) == null) {
+                !Entity.class.isAssignableFrom(entityType = (Class) mapping.getEntityType()) ||
+                !listenerConfigure.isEnabled(entityType)) {
             return;
         }
+
         if (type == MappingEventTypes.select_before) {
             handleQueryBefore(mapping, context);
         }
         if (type == MappingEventTypes.insert_before) {
             boolean single = context.get(MappingContextKeys.type).map("single"::equals).orElse(false);
             if (single) {
-                handleSingleOperation(mapping.getEntityType(), context, EntityBeforeCreateEvent::new, EntityCreatedEvent::new);
+                handleSingleOperation(mapping.getEntityType(),
+                                      EntityEventType.create,
+                                      context,
+                                      EntityPrepareCreateEvent::new,
+                                      EntityBeforeCreateEvent::new,
+                                      EntityCreatedEvent::new);
             } else {
-                handleBatchOperation(mapping.getEntityType(), context, EntityBeforeCreateEvent::new, EntityCreatedEvent::new);
+                handleBatchOperation(mapping.getEntityType(),
+                                     EntityEventType.save,
+                                     context,
+                                     EntityPrepareSaveEvent::new,
+                                     EntityBeforeCreateEvent::new,
+                                     EntityCreatedEvent::new);
             }
         }
         if (type == MappingEventTypes.save_before) {
             boolean single = context.get(MappingContextKeys.type).map("single"::equals).orElse(false);
             if (single) {
-                handleSingleOperation(mapping.getEntityType(), context, EntityBeforeSaveEvent::new, EntitySavedEvent::new);
+                handleSingleOperation(mapping.getEntityType(),
+                                      EntityEventType.save,
+                                      context,
+
+                                      EntityPrepareSaveEvent::new,
+                                      EntityBeforeSaveEvent::new,
+                                      EntitySavedEvent::new);
             } else {
-                handleBatchOperation(mapping.getEntityType(), context, EntityBeforeSaveEvent::new, EntitySavedEvent::new);
+                handleBatchOperation(mapping.getEntityType(),
+                                     EntityEventType.save,
+                                     context,
+                                     EntityPrepareSaveEvent::new,
+                                     EntityBeforeSaveEvent::new,
+                                     EntitySavedEvent::new);
             }
         }
         if (type == MappingEventTypes.update_before) {
             handleUpdateBefore(context);
         }
-
         if (type == MappingEventTypes.delete_before) {
-            handleDeleteBefore(context);
+            handleDeleteBefore(entityType, context);
         }
     }
 
@@ -123,12 +150,13 @@ public class EntityEventListener implements EventListener {
                     .filter(Entity.class::isInstance)
                     .map(Entity.class::cast)
                     .orElseGet(() -> {
-                        return context.get(MappingContextKeys.updateColumnInstance)
-                                      .map(map -> {
-                                          return FastBeanCopier.copy(map, FastBeanCopier.copy(old, mapping.getEntityType()));
-                                      })
-                                      .map(Entity.class::cast)
-                                      .orElse(null);
+                        return context
+                                .get(MappingContextKeys.updateColumnInstance)
+                                .map(map -> {
+                                    return FastBeanCopier.copy(map, FastBeanCopier.copy(old, mapping.getEntityType()));
+                                })
+                                .map(Entity.class::cast)
+                                .orElse(null);
                     });
             if (newValue != null) {
                 FastBeanCopier.copy(old, newValue, FastBeanCopier.include(idColumn.getAlias()));
@@ -162,51 +190,81 @@ public class EntityEventListener implements EventListener {
         EntityColumnMapping mapping = context
                 .get(MappingContextKeys.columnMapping)
                 .orElseThrow(UnsupportedOperationException::new);
-
+        Class entityType = (Class) mapping.getEntityType();
         if (repo instanceof ReactiveRepository) {
 
             context.get(MappingContextKeys.reactiveResultHolder)
                    .ifPresent(holder -> {
                        AtomicReference<Tuple2<List<Object>, List<Object>>> updated = new AtomicReference<>();
+                       //prepare
+                       if (isEnabled(entityType,
+                                     EntityEventType.modify,
+                                     EntityEventPhase.prepare,
+                                     EntityEventPhase.before,
+                                     EntityEventPhase.after)) {
+                           holder.before(
+                                   ((ReactiveRepository<Object, ?>) repo)
+                                           .createQuery()
+                                           .setParam(update.toQueryParam())
+                                           .fetch()
+                                           .collectList()
+                                           .flatMap((list) -> {
+                                               List<Object> after = createAfterData(list, context);
+                                               updated.set(Tuples.of(list, after));
+                                               return sendUpdateEvent(list,
+                                                                      after,
+                                                                      entityType,
+                                                                      EntityPrepareModifyEvent::new);
 
-                       holder.after(v -> {
-                           return Mono.defer(() -> {
-                               Tuple2<List<Object>, List<Object>> _tmp = updated.getAndSet(null);
+                                           })
+                                           .then()
+                           );
+                       }
+                       //before
+                       if (isEnabled(entityType, EntityEventType.modify, EntityEventPhase.before)) {
+                           holder.invoke(Mono.defer(() -> {
+                               Tuple2<List<Object>, List<Object>> _tmp = updated.get();
                                if (_tmp != null) {
-                                   return sendUpdateEvent(_tmp.getT1(), _tmp.getT2(), (Class) mapping.getEntityType(), EntityModifyEvent::new);
+                                   return sendUpdateEvent(_tmp.getT1(),
+                                                          _tmp.getT2(),
+                                                          entityType,
+                                                          EntityBeforeModifyEvent::new);
                                }
                                return Mono.empty();
+                           }));
+                       }
+
+                       //after
+                       if (isEnabled(entityType, EntityEventType.modify, EntityEventPhase.after)) {
+                           holder.after(v -> {
+                               return Mono
+                                       .defer(() -> {
+                                           Tuple2<List<Object>, List<Object>> _tmp = updated.getAndSet(null);
+                                           if (_tmp != null) {
+                                               return sendUpdateEvent(_tmp.getT1(),
+                                                                      _tmp.getT2(),
+                                                                      entityType,
+                                                                      EntityModifyEvent::new);
+                                           }
+                                           return Mono.empty();
+                                       });
                            });
-                       });
-
-                       holder.before(
-                               ((ReactiveRepository<Object, ?>) repo)
-                                       .createQuery()
-                                       .setParam(update.toQueryParam())
-                                       .fetch()
-                                       .collectList()
-                                       .flatMap((list) -> {
-                                           List<Object> after = createAfterData(list, context);
-                                           updated.set(Tuples.of(list, after));
-                                           return sendUpdateEvent(list,
-                                                                  after,
-                                                                  (Class) mapping.getEntityType(),
-                                                                  EntityBeforeModifyEvent::new);
-
-                                       })
-                                       .then()
-                       );
+                       }
+
                    });
         } else if (repo instanceof SyncRepository) {
-            QueryParam param = update.toQueryParam();
-            SyncRepository<Object, ?> syncRepository = ((SyncRepository<Object, ?>) repo);
-            List<Object> list = syncRepository.createQuery()
-                                              .setParam(param)
-                                              .fetch();
-            sendUpdateEvent(list,
-                            createAfterData(list, context),
-                            (Class<Object>) mapping.getEntityType(),
-                            EntityBeforeModifyEvent::new).block();
+            if (isEnabled(entityType, EntityEventType.modify, EntityEventPhase.before)) {
+                QueryParam param = update.toQueryParam();
+                SyncRepository<Object, ?> syncRepository = ((SyncRepository<Object, ?>) repo);
+                List<Object> list = syncRepository.createQuery()
+                                                  .setParam(param)
+                                                  .fetch();
+                sendUpdateEvent(list,
+                                createAfterData(list, context),
+                                (Class<Object>) mapping.getEntityType(),
+                                EntityBeforeModifyEvent::new)
+                        .block();
+            }
         }
     }
 
@@ -218,7 +276,7 @@ public class EntityEventListener implements EventListener {
 
     }
 
-    protected void handleDeleteBefore(EventContext context) {
+    protected void handleDeleteBefore(Class<Entity> entityType, EventContext context) {
         EntityColumnMapping mapping = context
                 .get(MappingContextKeys.columnMapping)
                 .orElseThrow(UnsupportedOperationException::new);
@@ -229,28 +287,33 @@ public class EntityEventListener implements EventListener {
                        context.get(MappingContextKeys.reactiveResultHolder)
                               .ifPresent(holder -> {
                                   AtomicReference<List<Object>> deleted = new AtomicReference<>();
-                                  holder.after(v -> {
-                                      return Mono
-                                              .defer(() -> {
-                                                  List<Object> _tmp = deleted.getAndSet(null);
-                                                  if (CollectionUtils.isNotEmpty(_tmp)) {
-                                                      return sendDeleteEvent(_tmp, (Class) mapping.getEntityType(), EntityDeletedEvent::new);
-                                                  }
-                                                  return Mono.empty();
-                                              });
-                                  });
-                                  holder.before(((ReactiveRepository<Object, ?>) repo)
-                                                        .createQuery()
-                                                        .setParam(dslUpdate.toQueryParam())
-                                                        .fetch()
-                                                        .collectList()
-                                                        .filter(CollectionUtils::isNotEmpty)
-                                                        .flatMap(list -> {
-                                                            deleted.set(list);
-                                                            return this
-                                                                    .sendDeleteEvent(list, (Class) mapping.getEntityType(), EntityBeforeDeleteEvent::new);
-                                                        })
-                                  );
+                                  if (isEnabled(entityType, EntityEventType.delete, EntityEventPhase.before, EntityEventPhase.after)) {
+                                      holder.before(((ReactiveRepository<Object, ?>) repo)
+                                                            .createQuery()
+                                                            .setParam(dslUpdate.toQueryParam())
+                                                            .fetch()
+                                                            .collectList()
+                                                            .filter(CollectionUtils::isNotEmpty)
+                                                            .flatMap(list -> {
+                                                                deleted.set(list);
+                                                                return this
+                                                                        .sendDeleteEvent(list, (Class) mapping.getEntityType(), EntityBeforeDeleteEvent::new);
+                                                            })
+                                      );
+                                  }
+                                  if (isEnabled(entityType, EntityEventType.delete, EntityEventPhase.after)) {
+                                      holder.after(v -> {
+                                          return Mono
+                                                  .defer(() -> {
+                                                      List<Object> _tmp = deleted.getAndSet(null);
+                                                      if (CollectionUtils.isNotEmpty(_tmp)) {
+                                                          return sendDeleteEvent(_tmp, (Class) mapping.getEntityType(), EntityDeletedEvent::new);
+                                                      }
+                                                      return Mono.empty();
+                                                  });
+                                      });
+                                  }
+
                               });
                    } else if (repo instanceof SyncRepository) {
                        QueryParam param = dslUpdate.toQueryParam();
@@ -268,29 +331,41 @@ public class EntityEventListener implements EventListener {
 
     }
 
-    protected void handleBatchOperation(Class clazz, EventContext context,
+    protected void handleBatchOperation(Class clazz,
+                                        EntityEventType entityEventType,
+                                        EventContext context,
                                         BiFunction<List<?>, Class, AsyncEvent> before,
+                                        BiFunction<List<?>, Class, AsyncEvent> execute,
                                         BiFunction<List<?>, Class, AsyncEvent> after) {
 
         context.get(MappingContextKeys.instance)
                .filter(List.class::isInstance)
                .map(List.class::cast)
                .ifPresent(lst -> {
+                   AsyncEvent prepareEvent = before.apply(lst, clazz);
                    AsyncEvent afterEvent = after.apply(lst, clazz);
-                   AsyncEvent beforeEvent = before.apply(lst, clazz);
+                   AsyncEvent beforeEvent = execute.apply(lst, clazz);
                    Object repo = context.get(MappingContextKeys.repository).orElse(null);
                    if (repo instanceof ReactiveRepository) {
                        Optional<ReactiveResultHolder> resultHolder = context.get(MappingContextKeys.reactiveResultHolder);
                        if (resultHolder.isPresent()) {
-                           eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, beforeEvent, clazz));
                            ReactiveResultHolder holder = resultHolder.get();
-                           if (null != beforeEvent) {
-                               holder.before(beforeEvent.publish(eventPublisher));
+                           if (null != prepareEvent && isEnabled(clazz, entityEventType, EntityEventPhase.prepare)) {
+                               eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, prepareEvent, clazz));
+                               holder.before(prepareEvent.getAsync());
+                           }
+                           if (null != beforeEvent && isEnabled(clazz, entityEventType, EntityEventPhase.before)) {
+                               holder.invoke(Mono.defer(() -> {
+                                   eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, beforeEvent, clazz));
+                                   return beforeEvent.getAsync();
+                               }));
+                           }
+                           if (null != afterEvent && isEnabled(clazz, entityEventType, EntityEventPhase.after)) {
+                               holder.after(v -> {
+                                   eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, afterEvent, clazz));
+                                   return afterEvent.getAsync();
+                               });
                            }
-                           holder.after(v -> {
-                               eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, afterEvent, clazz));
-                               return afterEvent.getAsync();
-                           });
                            return;
                        }
                    }
@@ -300,29 +375,50 @@ public class EntityEventListener implements EventListener {
                });
     }
 
+    boolean isEnabled(Class clazz, EntityEventType entityEventType, EntityEventPhase... phase) {
+        for (EntityEventPhase entityEventPhase : phase) {
+            if (listenerConfigure.isEnabled(clazz, entityEventType, entityEventPhase)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     protected void handleSingleOperation(Class clazz,
+                                         EntityEventType entityEventType,
                                          EventContext context,
                                          BiFunction<List<?>, Class, AsyncEvent> before,
+                                         BiFunction<List<?>, Class, AsyncEvent> execute,
                                          BiFunction<List<?>, Class, AsyncEvent> after) {
         context.get(MappingContextKeys.instance)
                .filter(Entity.class::isInstance)
                .map(Entity.class::cast)
                .ifPresent(entity -> {
+                   AsyncEvent prepareEvent = before.apply(Collections.singletonList(entity), clazz);
+                   AsyncEvent beforeEvent = execute.apply(Collections.singletonList(entity), clazz);
                    AsyncEvent afterEvent = after.apply(Collections.singletonList(entity), clazz);
-                   AsyncEvent beforeEvent = before.apply(Collections.singletonList(entity), clazz);
+
                    Object repo = context.get(MappingContextKeys.repository).orElse(null);
                    if (repo instanceof ReactiveRepository) {
                        Optional<ReactiveResultHolder> resultHolder = context.get(MappingContextKeys.reactiveResultHolder);
                        if (resultHolder.isPresent()) {
-                           eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, beforeEvent, clazz));
                            ReactiveResultHolder holder = resultHolder.get();
-                           if (null != beforeEvent) {
-                               holder.before(beforeEvent.publish(eventPublisher));
+                           if (null != prepareEvent && isEnabled(clazz, entityEventType, EntityEventPhase.prepare)) {
+                               eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, prepareEvent, clazz));
+                               holder.before(prepareEvent.getAsync());
+                           }
+                           if (null != beforeEvent && isEnabled(clazz, entityEventType, EntityEventPhase.before)) {
+                               holder.invoke(Mono.defer(() -> {
+                                   eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, beforeEvent, clazz));
+                                   return beforeEvent.getAsync();
+                               }));
+                           }
+                           if (null != afterEvent && isEnabled(clazz, entityEventType, EntityEventPhase.after)) {
+                               holder.after(v -> {
+                                   eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, afterEvent, clazz));
+                                   return afterEvent.getAsync();
+                               });
                            }
-                           holder.after(v -> {
-                               eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, afterEvent, clazz));
-                               return afterEvent.getAsync();
-                           });
                            return;
                        }
                    }

+ 73 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventListenerConfigure.java

@@ -0,0 +1,73 @@
+package org.hswebframework.web.crud.events;
+
+import org.hswebframework.web.api.crud.entity.Entity;
+
+/**
+ * 实体事件监听器配置
+ * <pre>
+ *     configure.enable(MyEntity.class)//启用事件
+ *              //禁用某一类事件
+ *              .disable(MyEntity.class,EntityEventType.modify,EntityEventPhase.all)
+ * </pre>
+ *
+ * @author zhouhao
+ * @since 4.0.12
+ */
+public interface EntityEventListenerConfigure {
+
+    /**
+     * 启用实体类的事件
+     *
+     * @param entityType 实体类
+     * @see org.hswebframework.web.crud.annotation.EnableEntityEvent
+     */
+    void enable(Class<? extends Entity> entityType);
+
+    /**
+     * 禁用实体类事件
+     *
+     * @param entityType 实体类
+     */
+    void disable(Class<? extends Entity> entityType);
+
+    /**
+     * 启用指定类型的事件
+     *
+     * @param entityType 实体类型
+     * @param type       事件类型
+     * @param phases     事件阶段,如果不传则启用全部
+     */
+    void enable(Class<? extends Entity> entityType,
+                EntityEventType type,
+                EntityEventPhase... phases);
+
+    /**
+     * 禁用指定类型的事件
+     *
+     * @param entityType 实体类型
+     * @param type       事件类型
+     * @param phases     事件阶段,如果不传则禁用全部
+     */
+    void disable(Class<? extends Entity> entityType,
+                 EntityEventType type,
+                 EntityEventPhase... phases);
+
+    /**
+     * 判断实体类是否启用了事件
+     *
+     * @param entityType 实体类
+     * @return 是否启用
+     */
+    boolean isEnabled(Class<? extends Entity> entityType);
+
+    /**
+     * 判断实体类是否启用了指定类型的事件
+     *
+     * @param entityType 实体类
+     * @param type       事件类型
+     * @param phase      事件阶段
+     * @return 是否启用
+     */
+    boolean isEnabled(Class<? extends Entity> entityType, EntityEventType type, EntityEventPhase phase);
+
+}

+ 18 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventListenerCustomizer.java

@@ -0,0 +1,18 @@
+package org.hswebframework.web.crud.events;
+
+/**
+ * 实体事件监听器自定义接口,用于自定义实体事件
+ *
+ * @author zhouhao
+ * @see EntityEventListenerConfigure
+ * @since 4.0.12
+ */
+public interface EntityEventListenerCustomizer {
+
+    /**
+     * 执行自定义
+     * @param configure configure
+     */
+    void customize(EntityEventListenerConfigure configure);
+
+}

+ 9 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventPhase.java

@@ -0,0 +1,9 @@
+package org.hswebframework.web.crud.events;
+
+public enum EntityEventPhase {
+    prepare,
+    before,
+    after;
+
+    public static EntityEventPhase[] all = EntityEventPhase.values();
+}

+ 8 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityEventType.java

@@ -0,0 +1,8 @@
+package org.hswebframework.web.crud.events;
+
+public enum EntityEventType {
+    create,
+    delete,
+    modify,
+    save
+}

+ 21 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityPrepareCreateEvent.java

@@ -0,0 +1,21 @@
+package org.hswebframework.web.crud.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.hswebframework.web.event.DefaultAsyncEvent;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @see org.hswebframework.web.crud.annotation.EnableEntityEvent
+ * @param <E>
+ */
+@AllArgsConstructor
+@Getter
+public class EntityPrepareCreateEvent<E> extends DefaultAsyncEvent implements Serializable {
+
+    private final List<E> entity;
+
+    private final Class<E> entityType;
+}

+ 26 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityPrepareModifyEvent.java

@@ -0,0 +1,26 @@
+package org.hswebframework.web.crud.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.hswebframework.web.event.DefaultAsyncEvent;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @see org.hswebframework.web.crud.annotation.EnableEntityEvent
+ * @param <E>
+ */
+@AllArgsConstructor
+@Getter
+public class EntityPrepareModifyEvent<E> extends DefaultAsyncEvent implements Serializable{
+
+    private static final long serialVersionUID = -7158901204884303777L;
+
+    private final List<E> before;
+
+    private final List<E> after;
+
+    private final Class<E> entityType;
+
+}

+ 21 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/events/EntityPrepareSaveEvent.java

@@ -0,0 +1,21 @@
+package org.hswebframework.web.crud.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.hswebframework.web.event.DefaultAsyncEvent;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @see org.hswebframework.web.crud.annotation.EnableEntityEvent
+ * @param <E>
+ */
+@AllArgsConstructor
+@Getter
+public class EntityPrepareSaveEvent<E> extends DefaultAsyncEvent implements Serializable {
+
+    private final List<E> entity;
+
+    private final Class<E> entityType;
+}

+ 23 - 0
hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/events/DefaultEntityEventListenerConfigureTest.java

@@ -0,0 +1,23 @@
+package org.hswebframework.web.crud.events;
+
+import org.hswebframework.web.crud.entity.EventTestEntity;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class DefaultEntityEventListenerConfigureTest {
+
+    @Test
+    public void test() {
+        DefaultEntityEventListenerConfigure configure = new DefaultEntityEventListenerConfigure();
+        configure.enable(EventTestEntity.class);
+        configure.disable(EventTestEntity.class, EntityEventType.create, EntityEventPhase.after);
+
+
+        assertTrue(configure.isEnabled(EventTestEntity.class));
+        assertTrue(configure.isEnabled(EventTestEntity.class, EntityEventType.create, EntityEventPhase.before));
+
+        assertFalse(configure.isEnabled(EventTestEntity.class, EntityEventType.create, EntityEventPhase.after));
+
+    }
+}