From d302037bc281a2a5523dcd7a5e5f8d9d1ea38177 Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 22 Aug 2024 18:19:30 +0800 Subject: [PATCH 01/62] =?UTF-8?q?feature:=E8=87=AA=E5=8A=A8=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E6=96=B9=E6=B3=95=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?,=E7=AE=80=E5=8C=96=E9=83=A8=E5=88=86=E6=8A=BD=E8=B1=A1?= =?UTF-8?q?=E7=B1=BB=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/share/ClassUtils.java | 36 +++++++++++++++ .../event/RocketMqDomainEventSubscriber.java | 12 ++++- .../repo/AbstractJpaPersistListener.java | 45 ++++++++++++++++++- .../domain/repo/AbstractJpaSpecification.java | 11 ++++- 4 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ClassUtils.java diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ClassUtils.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ClassUtils.java new file mode 100644 index 0000000..9972c7d --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ClassUtils.java @@ -0,0 +1,36 @@ +package org.netcorepal.cap4j.ddd.share; + +import java.lang.reflect.Method; +import java.util.Objects; +import java.util.function.Predicate; + +/** + * 类型工具类 + * + * @author binking338 + */ +public class ClassUtils { + + /** + * 查找方法 + * @param clazz 查找基于类型 + * @param name 方法名称 + * @param methodPredicate + * @return + */ + public static Method findMethod(Class clazz, String name, Predicate methodPredicate){ + Method[] methods = clazz.getMethods(); + for (Method method : methods) { + if (Objects.equals(method.getName(), name)) { + if(methodPredicate!=null){ + if (methodPredicate.test(method)){ + return method; + } + } else { + return method; + } + } + } + return null; + } +} diff --git a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java index 8a41346..46766fc 100644 --- a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java +++ b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java @@ -1,5 +1,8 @@ package org.netcorepal.cap4j.ddd.domain.event; +import org.netcorepal.cap4j.ddd.share.ClassUtils; +import org.springframework.core.ResolvableType; + /** * 基于RocketMq的领域事件订阅抽象类 * @@ -9,12 +12,19 @@ public abstract class RocketMqDomainEventSubscriber implements DomainEventSubscriber { /** * 监听的领域事件类型 + * * @return */ - public abstract Class forDomainEventClass(); + public Class forDomainEventClass() { + return ((Class) ClassUtils.findMethod( + this.getClass(), + "onEvent", + m -> m.getParameterCount() == 1).getParameters()[0].getType()); + } /** * 领域事件消费逻辑 + * * @param event */ @Override diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java index b6e1ac0..0fc01d8 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java @@ -1,5 +1,6 @@ package org.netcorepal.cap4j.ddd.domain.repo; +import org.netcorepal.cap4j.ddd.share.ClassUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -23,9 +24,51 @@ public T getBean(String name, Class clazz){ } - public abstract Class forEntityClass(); + public Class forEntityClass(){ + return ((Class) ClassUtils.findMethod( + this.getClass(), + "onChange", + m -> m.getParameterCount() == 1).getParameters()[0].getType()); + } public boolean throwOnException() { return true; } + + + /** + * 持久化变更 + * @param entity + */ + @Override + public void onChange(Entity entity){ + + } + + /** + * 新增实体时 + * @param entity + */ + @Override + public void onCreate(Entity entity){ + + } + + /** + * 更新实体时 + * @param entity + */ + @Override + public void onUpdate(Entity entity){ + + } + + /** + * 删除实体时 + * @param entity + */ + @Override + public void onDelete(Entity entity){ + + } } diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java index 415a264..28f2555 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java @@ -1,5 +1,7 @@ package org.netcorepal.cap4j.ddd.domain.repo; +import org.netcorepal.cap4j.ddd.share.ClassUtils; + /** * 基于Jpa的实体规约抽象类 * @@ -7,9 +9,16 @@ * @date 2023/8/13 */ public abstract class AbstractJpaSpecification implements Specification { - public abstract Class forEntityClass(); + public Class forEntityClass(){ + return ((Class) ClassUtils.findMethod( + this.getClass(), + "specify", + m -> m.getParameterCount() == 1).getParameters()[0].getType()); + } + public boolean forceBeforeTransaction(){ return false; } + public abstract Result specify(Entity entity); } From 2a2c743854ad03e4475904d16e5f80405a14bd2e Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 22 Aug 2024 23:43:48 +0800 Subject: [PATCH 02/62] =?UTF-8?q?feature:@DomainEvent=E4=BD=BF=E7=94=A8int?= =?UTF-8?q?ergration=E4=BD=9C=E4=B8=BA=E9=9B=86=E6=88=90=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E5=AF=B9=E5=A4=96=E5=90=8D=E7=A7=B0=EF=BC=8C=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++++++-------- .../domain/event/annotation/DomainEvent.java | 22 ++++++++++++++----- .../ddd/domain/event/persistence/Event.java | 2 +- .../RocketMqDomainEventSubscriberAdapter.java | 6 ++--- .../cap4j/ddd/domain/repo/JpaUnitOfWork.java | 2 +- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 42df6cb..f93ef1c 100644 --- a/README.md +++ b/README.md @@ -878,15 +878,15 @@ public class OrderPlacedDomainEvent { ``` > 注解属性详解 -> - `value()` value字段非空,则事件会被识别为集成事件,意味着该事件将通过消息队列适配,通知到分布式系统中的其他服务进程。 +> - `intergration()` intergration 字段非空,则事件会被识别为集成事件,意味着该事件将通过消息队列适配,通知到分布式系统中的其他服务进程。 > - `subscriber()` 集成事件订阅场景,必须定义该字段,通常该字段的值将会被适配的消息队列应用到消费分组配置中。 > - `persist()` 控制事件发布记录持久化。集成事件发布场景,该字段无意义。非集成事件发布场景(仅在本服务进程内部有订阅需求),可以通过`persist=true`控制事件进入发件箱表,并脱离事件发布上下文事务中。以避免订阅逻辑异常影响发布事务的完成。 > > 应用场景例子说明 -> - `基于MQ发送方` DomainEvent(value="event-name-used-for-mq-topic") -> - `基于MQ订阅方` DomainEvent(subscriber="consumer-group") -> - `消费方与订阅方事务隔离` DomainEvent(persist=true) -> - `消费方与订阅方同一事务` DomainEvent +> - `基于MQ发送方` @DomainEvent(intergration="event-name-used-for-mq-topic") +> - `基于MQ订阅方` @DomainEvent(subscriber="subscriber-name-used-for-mq-consumer-group") +> - `消费方与订阅方事务隔离` @DomainEvent(persist=true) +> - `消费方与订阅方同一事务` @DomainEvent > > 关于领域事件与集成事件 > @@ -899,11 +899,9 @@ public class OrderPlacedDomainEvent { 通常应在实体行为中,发布领域事件。 接口[DomainEventSupervisor.java](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java) -> `即时发送` DefaultDomainEventSupervisor.instance.attach(Object eventPayload, Object entity) -> -> `延时发送` DefaultDomainEventSupervisor.instance.attach(Object eventPayload, Object entity, Duration delay) -> -> `定时发送` DefaultDomainEventSupervisor.instance.attach(Object eventPayload, Object entity, LocalDateTime schedule) +> - `即时发送` DefaultDomainEventSupervisor.instance.attach(Object eventPayload, Object entity) +> - `延时发送` DefaultDomainEventSupervisor.instance.attach(Object eventPayload, Object entity, Duration delay) +> - `定时发送` DefaultDomainEventSupervisor.instance.attach(Object eventPayload, Object entity, LocalDateTime schedule) ```java import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java index 4576714..f3034c6 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java @@ -1,5 +1,7 @@ package org.netcorepal.cap4j.ddd.domain.event.annotation; +import org.springframework.core.annotation.AliasFor; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -17,13 +19,21 @@ public static final String NONE_SUBSCRIBER = "[none]"; /** - * 领域事件名称 - * 集成事件需要定义领域事件名称(通常作为MQ topic名称) - * + * 集成事件名称 * @return */ + @AliasFor("intergration") String value() default ""; + /** + * 集成事件名称 + * 该字段非空即为集成事件 + * (通常作为MQ topic名称) + * @return + */ + @AliasFor("value") + String intergration() default ""; + /** * 订阅者 * (通常作为MQ consumer group名称) @@ -32,9 +42,9 @@ String subscriber() default NONE_SUBSCRIBER; /** - * 事件记录持久化 - * 如果持久化,则事件发送失败或消费失败将会有重试机制 - * + * 事件记录是否持久化 + * 普通领域事件选择记录持久化,则事件发送失败将会有重试机制。 + * 集成事件必然持久化,该字段值无效。 * @return */ boolean persist() default false; diff --git a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java index 9820c65..6b6db3a 100644 --- a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java +++ b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java @@ -163,7 +163,7 @@ private void loadPayload(Object payload) { ? null : payload.getClass().getAnnotation(DomainEvent.class); if (domainEvent != null) { - this.eventType = domainEvent.value(); + this.eventType = domainEvent.intergration(); } Retry retry = payload == null ? null diff --git a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberAdapter.java b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberAdapter.java index b7c593a..9895178 100644 --- a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberAdapter.java +++ b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberAdapter.java @@ -47,7 +47,7 @@ public void init() { Set> classes = ScanUtils.scanClass(scanPath, true); classes.stream().filter(cls -> { DomainEvent domainEvent = cls.getAnnotation(DomainEvent.class); - if (!Objects.isNull(domainEvent) && StringUtils.isNotEmpty(domainEvent.value()) + if (!Objects.isNull(domainEvent) && StringUtils.isNotEmpty(domainEvent.intergration()) & !DomainEvent.NONE_SUBSCRIBER.equalsIgnoreCase(domainEvent.subscriber())) { return true; } else { @@ -90,7 +90,7 @@ public void shutdown() { public DefaultMQPushConsumer createDefaultConsumer(Class domainEventClass) { DomainEvent domainEvent = (DomainEvent) domainEventClass.getAnnotation(DomainEvent.class); - if (Objects.isNull(domainEvent) || StringUtils.isBlank(domainEvent.value()) + if (Objects.isNull(domainEvent) || StringUtils.isBlank(domainEvent.intergration()) || DomainEvent.NONE_SUBSCRIBER.equalsIgnoreCase(domainEvent.subscriber())) { // 不是集成事件, 或显式标明无订阅 return null; @@ -99,7 +99,7 @@ public DefaultMQPushConsumer createDefaultConsumer(Class domainEventClass) { // // 不存在订阅 // return null; // } - String target = domainEvent.value(); + String target = domainEvent.intergration(); target = TextUtils.resolvePlaceholderWithCache(target, environment); String topic = target.lastIndexOf(':') > 0 ? target.substring(0, target.lastIndexOf(':')) : target; String tag = target.lastIndexOf(':') > 0 ? target.substring(target.lastIndexOf(':') + 1) : ""; diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java index 8ba4f61..b31410c 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java @@ -524,7 +524,7 @@ public boolean isDomainEventPersist(Object payload) { ? null : payload.getClass().getAnnotation(DomainEvent.class); if (domainEvent != null) { - return domainEvent.persist() || (domainEvent.value() != null && !domainEvent.value().trim().isEmpty()); + return domainEvent.persist() || (domainEvent.intergration() != null && !domainEvent.intergration().trim().isEmpty()); } else { return false; } From bb158009973fa4572a1fea61e4afba667b526b54 Mon Sep 17 00:00:00 2001 From: binking338 Date: Fri, 23 Aug 2024 12:51:18 +0800 Subject: [PATCH 03/62] =?UTF-8?q?optimize:=E5=A2=9E=E5=8A=A0archTemplateEn?= =?UTF-8?q?coding=E9=85=8D=E7=BD=AE=E9=A1=B9=E6=8C=87=E5=AE=9AarchTemplate?= =?UTF-8?q?=E7=BC=96=E7=A0=81=EF=BC=8C=E9=81=BF=E5=85=8D=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E4=B9=B1=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenArchMojo.java | 14 +++++++--- .../cap4j/ddd/codegen/GenEntityMojo.java | 28 +++++++++++++++++++ .../cap4j/ddd/codegen/GenRepositoryMojo.java | 8 ++++++ .../cap4j/ddd/codegen/HelpMojo.java | 8 ++++-- .../cap4j/ddd/codegen/MyAbstractMojo.java | 10 ++++++- .../ddd/codegen/misc/SourceFileUtils.java | 12 ++++---- cap4j-ddd-codegen-template.json | 2 +- 7 files changed, 66 insertions(+), 16 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index 021b81a..1ecc553 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -11,6 +11,7 @@ import java.io.File; import java.io.IOException; +import java.nio.charset.Charset; import java.util.List; import java.util.Map; @@ -61,18 +62,22 @@ public static class PathNode { @Override public void execute() throws MojoExecutionException, MojoFailureException { + getLog().info("当前默认编码:" + Charset.defaultCharset().name()); + getLog().info("archTemplate设置编码:" + Charset.defaultCharset().name()); + String templateContent = ""; try { if (null == archTemplate || archTemplate.isEmpty()) { -// templateContent = SourceFileUtils.loadResourceFileContent("template.json"); +// templateContent = SourceFileUtils.loadResourceFileContent("template.json", archTemplateEncoding); getLog().error("请设置archTemplate参数"); return; } else { - templateContent = SourceFileUtils.loadFileContent(archTemplate); + templateContent = SourceFileUtils.loadFileContent(archTemplate, archTemplateEncoding); } } catch (IOException e) { throw new RuntimeException(e); } + getLog().debug(templateContent); if (basePackage == null || basePackage.isEmpty()) { getLog().warn("请设置basePackage参数"); return; @@ -148,7 +153,7 @@ public void renderFile(PathNode pathNode, String parentPath) { break; case "url": try { - content = SourceFileUtils.loadFileContent(pathNode.data); + content = SourceFileUtils.loadFileContent(pathNode.data, archTemplateEncoding); } catch (IOException ex) { getLog().error("获取模板源文件异常", ex); } @@ -172,7 +177,7 @@ public void renderFile(PathNode pathNode, String parentPath) { try { FileUtils.fileDelete(path); - FileUtils.fileWrite(path, "utf-8", content); + FileUtils.fileWrite(path, content); } catch (IOException e) { getLog().error("写入模板文件异常", e); } @@ -191,6 +196,7 @@ public String escapeContent(String content) { content = content.replace("${artifactId}", projectArtifactId); content = content.replace("${version}", projectVersion); content = content.replace("${archTemplate}", archTemplate); + content = content.replace("${archTemplateEncoding}", archTemplateEncoding); content = content.replace("${basePackage}", basePackage); content = content.replace("${multiModule}", multiModule ? "true" : "false"); content = content.replace("${moduleNameSuffix4Adapter}", moduleNameSuffix4Adapter); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index aca21ba..71e4161 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -13,6 +13,7 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.nio.charset.Charset; import java.sql.*; import java.util.*; import java.util.stream.Collectors; @@ -35,6 +36,31 @@ public class GenEntityMojo extends MyAbstractMojo { @Override public void execute() throws MojoExecutionException, MojoFailureException { + getLog().info("当前默认编码:" + Charset.defaultCharset().name()); + getLog().info("数据库连接:" + connectionString); + getLog().info("数据库账号:" + user); + getLog().info("数据库密码:" + pwd); + getLog().info("数据库名称:" + schema); + getLog().info("包含表:" + table); + getLog().info("忽略表:" + ignoreTable); + getLog().info("主键字段:" + idField); + getLog().info("乐观锁字段:" + versionField); + getLog().info("软删字段:" + deletedField); + getLog().info("只读字段:" +readonlyFields ); + getLog().info("忽略字段:" + ignoreFields); + getLog().info(""); + getLog().info("主键ID生成器:" + idGenerator); + getLog().info("聚合根主键映射Java类型:" + aggregateIdentityClass); + getLog().info("日期类型映射Java包:" + datePackage4Java); + getLog().info("枚举值Java字段名称:" + enumValueField); + getLog().info("枚举名Java字段名称:" + enumNameField); + getLog().info("枚举类型Jpa类型映射转换期不匹配是否抛出异常:" + enumUnmatchedThrowException); + getLog().info("类型强制映射规则:"); + for (Map.Entry entry : + typeRemapping.entrySet()) { + getLog().info(" " + entry.getKey() + " <-> " + entry.getValue()); + } + getLog().info(""); this.getLog().info("开始生成实体代码"); MysqlSchemaUtils.mojo = this; @@ -70,6 +96,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("应用层目录:" + applicationModulePath); getLog().info("领域层目录:" + domainModulePath); getLog().info("基础包名:" + basePackage); + getLog().info("实体基类:" + entityBaseClass); + getLog().info("聚合根标注注解:" + aggregateRootAnnotation); if (StringUtils.isBlank(entityMetaInfoClassOutputPackage)) { entityMetaInfoClassOutputPackage = "domain._share.meta"; diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java index e7dabe8..405a411 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java @@ -8,6 +8,7 @@ import org.codehaus.plexus.util.StringUtils; import java.io.*; +import java.nio.charset.Charset; import java.util.*; import static org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils.writeLine; @@ -21,6 +22,13 @@ public class GenRepositoryMojo extends MyAbstractMojo { @Override public void execute() throws MojoExecutionException, MojoFailureException { + getLog().info("当前默认编码:" + Charset.defaultCharset().name()); + getLog().info("聚合根标注注解:" + aggregateRootAnnotation); + getLog().info("聚合根基类:" + aggregateRepositoryBaseClass); + getLog().info("跳过生成仓储的聚合根:" + ignoreAggregateRoots); + getLog().info("聚合仓储自定义代码:"); + getLog().info(aggregateRepositoryCustomerCode); + getLog().info(""); this.getLog().info("开始生成仓储代码"); // 项目结构解析 diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java index 66bea24..b51aa17 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java @@ -28,6 +28,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { " \n" + " \n" + " https://raw.githubusercontent.com/netcorepal/cap4j/main/cap4j-ddd-codegen-template.json\n" + + " \n" + + " UTF-8\n" + " \n" + " org.netcorepal.cap4j.ddd.example\n" + " \n" + @@ -100,13 +102,13 @@ public void execute() throws MojoExecutionException, MojoFailureException { " Long\n" + " \n" + " \n" + + " \n" + + " \n" + + " \n" + " \n" + " \n" + " \n" + " \n" + - " \n" + - " \n" + - " \n" + " "); getLog().info(""); getLog().info(""); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java index 9f20e9f..2e1cdf3 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java @@ -22,6 +22,14 @@ public abstract class MyAbstractMojo extends AbstractMojo { @Parameter(property = "archTemplate", defaultValue = "") String archTemplate = ""; + /** + * 代码模板配置文件编码,默认UFT-8 + * + * @parameter expression="${archTemplateEncoding}" + */ + @Parameter(property = "archTemplateEncoding", defaultValue = "UTF-8") + String archTemplateEncoding = "UTF-8"; + /** * 基础包路径 * @@ -284,7 +292,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { String aggregateRepositoryCustomerCode = ""; /** - * 忽略聚合根 + * 跳过生成仓储的聚合根 * 逗号','或分号';'分割 * * @parameter expression="${ignoreAggregateRoots}" diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SourceFileUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SourceFileUtils.java index 3f8fc09..7483380 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SourceFileUtils.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SourceFileUtils.java @@ -6,8 +6,6 @@ import java.io.*; import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.*; /** @@ -54,12 +52,12 @@ public static List loadFiles(String baseDir) { return result; } - public static String loadFileContent(String location) throws IOException { + public static String loadFileContent(String location, String charsetName) throws IOException { String content = ""; if (location.startsWith("http://") || location.startsWith("https://")) { try { URL url = new URL(location); - BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); + BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), charsetName)); String line; while ((line = reader.readLine()) != null) { content += line; @@ -70,7 +68,7 @@ public static String loadFileContent(String location) throws IOException { } } else { try { - content = new String(Files.readAllBytes(Paths.get(location))); + content = FileUtils.fileRead(location, charsetName); } catch (IOException ex){ throw ex; } @@ -78,9 +76,9 @@ public static String loadFileContent(String location) throws IOException { return content; } - public static String loadResourceFileContent(String path) throws IOException { + public static String loadResourceFileContent(String path, String charsetName) throws IOException { InputStream in = SourceFileUtils.class.getClassLoader().getResourceAsStream(path); - InputStreamReader reader = new InputStreamReader(in); + InputStreamReader reader = new InputStreamReader(in, charsetName); BufferedReader bufferedReader = new BufferedReader(reader); StringBuilder stringBuilder = new StringBuilder(); bufferedReader.lines().forEachOrdered(line -> { diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index abee6fe..f40bcae 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -388,7 +388,7 @@ "name": "pom.xml", "format": "raw", "conflict": "overwrite", - "data": "\n 4.0.0\n\n ${groupId}\n ${artifactId}\n ${version}\n jar\n\n ddd-sample\n\n \n UTF-8\n UTF-8\n 1.8\n 1.0.0-alpha-1\n\n 1.18.16\n\n 31.1-jre\n 3.12.0\n 4.4\n 1.2.79\n\n 5.1.46\n 1.2.8\n\n 2.7.6\n 2021.0.5\n \n \n\n 1.6.14\n 3.0.3\n 2.2.7\n \n\n \n \n \n org.springframework.boot\n spring-boot-dependencies\n ${spring-boot.version}\n pom\n import\n \n \n org.springframework.cloud\n spring-cloud-dependencies\n ${spring-cloud.version}\n pom\n import\n \n \n \n\n \n \n org.projectlombok\n lombok\n true\n ${org.projectlombok.version}\n \n\n \n io.github.netcorepal\n cap4j-ddd-starter-jpa-rocketmq\n ${cap4j.version}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n provided\n \n\n \n \n org.springframework.boot\n spring-boot-starter-web\n \n \n org.springframework.boot\n spring-boot-starter-actuator\n \n \n org.springframework.boot\n spring-boot-starter-validation\n \n\n \n \n org.apache.commons\n commons-lang3\n ${commons.lang3.version}\n \n \n org.apache.commons\n commons-collections4\n ${commons.collections4.version}\n \n \n com.google.guava\n guava\n ${guava.version}\n \n \n com.alibaba\n fastjson\n ${fastjson.version}\n \n\n \n \n com.ctrip.framework.apollo\n apollo-client\n 1.7.0\n \n \n \n com.githup.liuyanggithup\n xxl-job-core\n 1.2.1\n \n \n \n org.springframework.cloud\n spring-cloud-starter-openfeign\n \n \n io.github.openfeign\n feign-okhttp\n 11.10\n \n\n \n \n org.springframework.boot\n spring-boot-starter-data-jpa\n \n \n \n org.mybatis.spring.boot\n mybatis-spring-boot-starter\n 2.1.3\n \n \n \n com.alibaba\n druid-spring-boot-starter\n ${spring-boot-druid.version}\n \n \n \n mysql\n mysql-connector-java\n ${mysql-connector.version}\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n org.springdoc\n springdoc-openapi-ui\n ${springdoc.version}\n \n \n com.github.xiaoymin\n knife4j-springdoc-ui\n ${knife4j.version}\n \n \n io.swagger.core.v3\n swagger-annotations\n ${swagger3.version}\n \n\n \n net.logstash.logback\n logstash-logback-encoder\n 6.6\n \n \n junit\n junit\n 3.8.1\n test\n \n \n \n ${project.artifactId}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n \n ${archTemplate}\n ${basePackage}\n ${multiModule}\n ${moduleNameSuffix4Adapter}\n ${moduleNameSuffix4Domain}\n ${moduleNameSuffix4Application}\n \n \n \n ${user}\n ${pwd}\n ${schema}\n ${table}
\n ${ignoreTable}\n ${idField}\n ${versionField}\n ${deletedField}\n ${readonlyFields}\n ${ignoreFields}\n ${entityBaseClass}\n ${entityMetaInfoClassOutputMode}\n ${entityMetaInfoClassOutputPackage}\n ${fetchMode}\n ${fetchType}\n ${idGenerator}\n ${enumValueField}\n ${enumNameField}\n ${enumUnmatchedThrowException}\n ${datePackage4Java}\n ${typeRemapping}\n ${generateDefault}\n ${generateDbType}\n ${generateSchema}\n ${generateBuild}\n ${aggregateIdentityClass}\n ${aggregateRootAnnotation}\n ${aggregateRepositoryBaseClass}\n ${aggregateRepositoryCustomerCode}\n ${ignoreAggregateRoots}\n
\n
\n \n org.springframework.boot\n spring-boot-maven-plugin\n 2.6.3\n \n \n \n \n repackage\n \n repackage\n \n \n ${basePackage}.StartApplication1\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 1.8\n 1.8\n UTF-8\n \n \n \n org.apache.maven.plugins\n maven-archetype-plugin\n 3.2.0\n \n\n
\n
\n
" + "data": "\n 4.0.0\n\n ${groupId}\n ${artifactId}\n ${version}\n jar\n\n ddd-sample\n\n \n UTF-8\n UTF-8\n 1.8\n 1.0.0-alpha-1\n\n 1.18.16\n\n 31.1-jre\n 3.12.0\n 4.4\n 1.2.79\n\n 5.1.46\n 1.2.8\n\n 2.7.6\n 2021.0.5\n \n \n\n 1.6.14\n 3.0.3\n 2.2.7\n \n\n \n \n \n org.springframework.boot\n spring-boot-dependencies\n ${spring-boot.version}\n pom\n import\n \n \n org.springframework.cloud\n spring-cloud-dependencies\n ${spring-cloud.version}\n pom\n import\n \n \n \n\n \n \n org.projectlombok\n lombok\n true\n ${org.projectlombok.version}\n \n\n \n io.github.netcorepal\n cap4j-ddd-starter-jpa-rocketmq\n ${cap4j.version}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n provided\n \n\n \n \n org.springframework.boot\n spring-boot-starter-web\n \n \n org.springframework.boot\n spring-boot-starter-actuator\n \n \n org.springframework.boot\n spring-boot-starter-validation\n \n\n \n \n org.apache.commons\n commons-lang3\n ${commons.lang3.version}\n \n \n org.apache.commons\n commons-collections4\n ${commons.collections4.version}\n \n \n com.google.guava\n guava\n ${guava.version}\n \n \n com.alibaba\n fastjson\n ${fastjson.version}\n \n\n \n \n com.ctrip.framework.apollo\n apollo-client\n 1.7.0\n \n \n \n com.githup.liuyanggithup\n xxl-job-core\n 1.2.1\n \n \n \n org.springframework.cloud\n spring-cloud-starter-openfeign\n \n \n io.github.openfeign\n feign-okhttp\n 11.10\n \n\n \n \n org.springframework.boot\n spring-boot-starter-data-jpa\n \n \n \n org.mybatis.spring.boot\n mybatis-spring-boot-starter\n 2.1.3\n \n \n \n com.alibaba\n druid-spring-boot-starter\n ${spring-boot-druid.version}\n \n \n \n mysql\n mysql-connector-java\n ${mysql-connector.version}\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n org.springdoc\n springdoc-openapi-ui\n ${springdoc.version}\n \n \n com.github.xiaoymin\n knife4j-springdoc-ui\n ${knife4j.version}\n \n \n io.swagger.core.v3\n swagger-annotations\n ${swagger3.version}\n \n\n \n net.logstash.logback\n logstash-logback-encoder\n 6.6\n \n \n junit\n junit\n 3.8.1\n test\n \n \n \n ${project.artifactId}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n \n ${archTemplate}\n ${archTemplateEncoding}\n ${basePackage}\n ${multiModule}\n ${moduleNameSuffix4Adapter}\n ${moduleNameSuffix4Domain}\n ${moduleNameSuffix4Application}\n \n \n \n ${user}\n ${pwd}\n ${schema}\n ${table}
\n ${ignoreTable}\n ${idField}\n ${versionField}\n ${deletedField}\n ${readonlyFields}\n ${ignoreFields}\n ${entityBaseClass}\n ${entityMetaInfoClassOutputMode}\n ${entityMetaInfoClassOutputPackage}\n ${fetchMode}\n ${fetchType}\n ${idGenerator}\n ${enumValueField}\n ${enumNameField}\n ${enumUnmatchedThrowException}\n ${datePackage4Java}\n ${typeRemapping}\n ${generateDefault}\n ${generateDbType}\n ${generateSchema}\n ${generateBuild}\n ${aggregateIdentityClass}\n ${aggregateRootAnnotation}\n ${aggregateRepositoryBaseClass}\n ${aggregateRepositoryCustomerCode}\n ${ignoreAggregateRoots}\n
\n
\n \n org.springframework.boot\n spring-boot-maven-plugin\n 2.6.3\n \n \n \n \n repackage\n \n repackage\n \n \n ${basePackage}.StartApplication1\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 1.8\n 1.8\n UTF-8\n \n \n \n org.apache.maven.plugins\n maven-archetype-plugin\n 3.2.0\n \n\n
\n
\n
" } ] } \ No newline at end of file From bd644ef4dd184c11ba454191a48d37ba4da19c6b Mon Sep 17 00:00:00 2001 From: binking338 Date: Fri, 23 Aug 2024 14:21:21 +0800 Subject: [PATCH 04/62] =?UTF-8?q?optimize=EF=BC=9Agen-arch=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E6=96=87=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenArchMojo.java | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index 1ecc553..9de6683 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -100,7 +100,6 @@ public void execute() throws MojoExecutionException, MojoFailureException { public void render(PathNode pathNode, String parentPath) { String path = parentPath + File.separator + pathNode.name; - getLog().info("创建 " + escapePath(path)); switch (pathNode.type) { case "file": renderFile(pathNode, parentPath); @@ -125,7 +124,7 @@ public void render(PathNode pathNode, String parentPath) { public void renderDir(PathNode pathNode, String parentPath) { if (!"dir".equalsIgnoreCase(pathNode.type)) { - throw new RuntimeException("节点类型必须是文件"); + throw new RuntimeException("节点类型必须是目录"); } if (pathNode.name == null || pathNode.name.isEmpty()) { throw new RuntimeException("模板节点配置 name 不得为空 parentPath = " + parentPath); @@ -133,7 +132,29 @@ public void renderDir(PathNode pathNode, String parentPath) { String path = parentPath + File.separator + pathNode.name; path = escapePath(path); - new File(path).mkdirs(); + if (FileUtils.fileExists(path)) { + + switch (pathNode.conflict) { + case "warn": + getLog().warn("目录存在:" + path); + return; + case "overwrite": + getLog().info("目录覆盖:" + path); + break; + case "skip": + getLog().info("目录存在:" + path); + return; + default: + getLog().info("目录创建:" + path); + break; + } + } + try { + FileUtils.deleteDirectory(path); + } catch (IOException ex) { + getLog().error("获取模板源文件异常", ex); + } + FileUtils.mkdir(path); } public void renderFile(PathNode pathNode, String parentPath) { @@ -163,15 +184,17 @@ public void renderFile(PathNode pathNode, String parentPath) { if (FileUtils.fileExists(path)) { switch (pathNode.conflict) { case "warn": - getLog().warn("文件已存在:" + path); + getLog().warn("文件存在:" + path); return; case "overwrite": - getLog().info("文件将覆盖:" + path); + getLog().info("文件覆盖:" + path); break; case "skip": - default: - getLog().info("文件已存在:" + path); + getLog().info("文件存在:" + path); return; + default: + getLog().info("文件创建:" + path); + break; } } From 5b3129dd55fe59d144e2625630814e87d667d538 Mon Sep 17 00:00:00 2001 From: binking338 Date: Sat, 24 Aug 2024 11:00:29 +0800 Subject: [PATCH 05/62] =?UTF-8?q?optimize=EF=BC=9A=E5=AE=9E=E4=BD=93?= =?UTF-8?q?=E7=94=9F=E6=88=90=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99import?= =?UTF-8?q?=EF=BC=9B=E5=8A=A0=E5=85=A5@Aggregate=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E4=BE=BF=E4=BA=8E=E5=90=8E=E7=BB=AD=E5=AF=BC=E5=87=BA=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=EF=BC=9B=E4=BB=93=E5=82=A8=E8=AF=86=E5=88=AB=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=95=88=E7=8E=87=E4=BC=98=E5=8C=96=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 + cap4j-ddd-codegen-maven-plugin/pom.xml | 6 - .../cap4j/ddd/codegen/GenArchMojo.java | 13 ++ .../cap4j/ddd/codegen/GenEntityMojo.java | 130 +++++++++++------- .../cap4j/ddd/codegen/GenRepositoryMojo.java | 118 +++++++++------- .../cap4j/ddd/codegen/MyAbstractMojo.java | 70 +++++++++- .../ddd/codegen/misc/MysqlSchemaUtils.java | 18 +-- .../ddd/codegen/misc/SourceFileUtils.java | 34 +++-- cap4j-ddd-codegen-template.json | 2 +- .../aggregate/annotation/Aggregate.java | 55 ++++++++ 10 files changed, 315 insertions(+), 134 deletions(-) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java diff --git a/README.md b/README.md index f93ef1c..d8b7aae 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,9 @@ db_created_at,db_updated_at + + + ref domain._share.meta SUBSELECT diff --git a/cap4j-ddd-codegen-maven-plugin/pom.xml b/cap4j-ddd-codegen-maven-plugin/pom.xml index b314917..c2457cd 100644 --- a/cap4j-ddd-codegen-maven-plugin/pom.xml +++ b/cap4j-ddd-codegen-maven-plugin/pom.xml @@ -24,18 +24,12 @@ lombok 1.18.16 - - org.apache.maven - maven-plugin-api - 3.8.1 - org.apache.maven maven-core 3.8.1 - org.apache.maven.plugin-tools maven-plugin-annotations diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index 9de6683..4df54e7 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -237,6 +237,7 @@ public String escapeContent(String content) { content = content.replace("${readonlyFields}", readonlyFields); content = content.replace("${ignoreFields}", ignoreFields); content = content.replace("${entityBaseClass}", entityBaseClass); + content = content.replace("${entityClassExtraImports}", stringfyEntityClassImportPackages()); content = content.replace("${entityMetaInfoClassOutputPackage}", entityMetaInfoClassOutputPackage); content = content.replace("${entityMetaInfoClassOutputMode}", entityMetaInfoClassOutputMode); content = content.replace("${idGenerator}", idGenerator); @@ -273,4 +274,16 @@ private String stringfyTypeRemapping() { } return result; } + + private String stringfyEntityClassImportPackages() { + if (entityClassExtraImports == null || entityClassExtraImports.isEmpty()) { + return ""; + } + String result = "\n"; + for (String entityClassExtraImport : entityClassExtraImports) { + result += " " + entityClassExtraImport + "\n"; + } + result += " \n"; + return result; + } } diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index 71e4161..a860ae1 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -1,18 +1,15 @@ package org.netcorepal.cap4j.ddd.codegen; import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.StringUtils; import org.netcorepal.cap4j.ddd.codegen.misc.Inflector; import org.netcorepal.cap4j.ddd.codegen.misc.MysqlSchemaUtils; import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; -import org.apache.commons.lang3.StringUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; +import java.io.*; import java.nio.charset.Charset; import java.sql.*; import java.util.*; @@ -23,6 +20,8 @@ import static org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils.writeLine; /** + * 生成实体 + * * @author binking338 * @date 2022-02-16 */ @@ -46,7 +45,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("主键字段:" + idField); getLog().info("乐观锁字段:" + versionField); getLog().info("软删字段:" + deletedField); - getLog().info("只读字段:" +readonlyFields ); + getLog().info("只读字段:" + readonlyFields); getLog().info("忽略字段:" + ignoreFields); getLog().info(""); getLog().info("主键ID生成器:" + idGenerator); @@ -89,7 +88,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { } String basePackage = StringUtils.isNotBlank(this.basePackage) ? this.basePackage - : SourceFileUtils.resolveBasePackage(domainModulePath); + : SourceFileUtils.resolveDefaultBasePackage(domainModulePath); getLog().info(multiModule ? "多模块项目" : "单模块项目"); getLog().info("项目目录:" + projectDir); getLog().info("适配层目录:" + adapterModulePath); @@ -97,7 +96,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("领域层目录:" + domainModulePath); getLog().info("基础包名:" + basePackage); getLog().info("实体基类:" + entityBaseClass); - getLog().info("聚合根标注注解:" + aggregateRootAnnotation); + getLog().info("聚合根标注注解:" + getAggregateRootAnnotation()); if (StringUtils.isBlank(entityMetaInfoClassOutputPackage)) { entityMetaInfoClassOutputPackage = "domain._share.meta"; @@ -639,10 +638,10 @@ public boolean readCustomerSourceFile(String filePath, List importLines, startClassLine = i; } else if ((line.trim().startsWith("@") || annotationLines.size() > 0) && startClassLine == 0) { annotationLines.add(line); - getLog().debug("a " + line); + getLog().debug("[annotation] " + line); } else if ((annotationLines.size() == 0 && startClassLine == 0)) { importLines.add(line); - getLog().debug("i " + line); + getLog().debug("[import] " + line); } else if (startClassLine > 0 && (startMapperLine == 0 || endMapperLine > 0) ) { @@ -660,7 +659,7 @@ public boolean readCustomerSourceFile(String filePath, List importLines, } customerLines.remove(i); } - customerLines.forEach(l -> getLog().debug("c " + l)); + customerLines.forEach(l -> getLog().debug("[customer] " + l)); if (startMapperLine == 0 || endMapperLine == 0) { return false; } @@ -669,32 +668,21 @@ public boolean readCustomerSourceFile(String filePath, List importLines, return true; } - public void processImportLines(Map table, String basePackage, List importLines) { + public void processImportLines(Map table, String basePackage, List importLines, String content) { boolean importEmpty = importLines.size() == 0; if (importEmpty) { importLines.add(""); } - List importNamespaces = Arrays.asList( - "lombok.AllArgsConstructor", - "lombok.Builder", - "lombok.Getter", - "lombok.NoArgsConstructor", - "org.hibernate.annotations.GenericGenerator", - "org.hibernate.annotations.DynamicInsert", - "org.hibernate.annotations.DynamicUpdate", - "org.hibernate.annotations.Fetch", - "org.hibernate.annotations.FetchMode", - "org.hibernate.annotations.SQLDelete", - "org.hibernate.annotations.Where", - "javax.persistence.*" - ); + List entityClassExtraImports = getEntityClassExtraImports(); if (importEmpty) { - for (String importNamespace : importNamespaces) { - if (importNamespace.equalsIgnoreCase("javax.persistence.*")) { + boolean breakLine = false; + for (String entityClassExtraImport : entityClassExtraImports) { + if (entityClassExtraImport.startsWith("javax") && !breakLine) { + breakLine = true; importLines.add(""); } - importLines.add("import " + importNamespace + ";"); + importLines.add("import " + entityClassExtraImport + ";"); } importLines.add(""); importLines.add("/**"); @@ -710,35 +698,57 @@ public void processImportLines(Map table, String basePackage, Li importLines.add(" * 警告:请勿手工修改该文件的字段声明,重新生成会覆盖字段声明"); importLines.add(" */"); } else { - for (String importNamespace : importNamespaces) { - SourceFileUtils.addSortedIfNone(importLines, - "\\s*import\\s+" + importNamespace + for (String entityClassExtraImport : entityClassExtraImports) { + SourceFileUtils.addIfNone(importLines, + "\\s*import\\s+" + (entityClassExtraImport .replace(".", "\\.") - .replace("*", "\\*") + "\\s*;", - "import " + importNamespace + ";"); + .replace("*", "\\*")) + "\\s*;", + "import " + entityClassExtraImport + ";", + (list, line) -> { + Optional firstLargeLine = list.stream().filter(l -> !l.isEmpty() && l.compareTo(line) > 0).findFirst(); + if (firstLargeLine.isPresent()) { + return list.indexOf(firstLargeLine.get()); + } + List imports = list.stream().filter(l -> !l.isEmpty() && !l.contains(" java") && l.startsWith("import")).collect(Collectors.toList()); + return list.indexOf(imports.get(imports.size() - 1)) + 1; + }); + } + } + for (int i = 0; i < importLines.size(); i++) { + String importLine = importLines.get(i); + if (importLine.contains(" org.hibernate.annotations.") + && !importLine.contains("*")) { + String hibernateAnnotation = importLine.substring(importLine.lastIndexOf('.') + 1).replace(";", "").trim(); + if (!content.contains(hibernateAnnotation)) { + importLines.remove(importLine); + i--; + } } } } public void processAnnotationLines(Map table, List> columns, List annotationLines) { String tableName = MysqlSchemaUtils.getTableName(table); + String simpleClassName = getEntityJavaType(tableName); boolean annotationEmpty = annotationLines.size() == 0; - SourceFileUtils.addIfNone(annotationLines, "@Entity(\\(.*\\))?", "@Entity"); - if (StringUtils.isNotBlank(aggregateRootAnnotation)) { - String aggregateRootAnnotationSimpleClassName = aggregateRootAnnotation.substring(aggregateRootAnnotation.contains(".") ? aggregateRootAnnotation.lastIndexOf(".") + 1 : 0); + SourceFileUtils.removeText(annotationLines, "@Aggregate\\(.*\\)"); + SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(entity = \"" + simpleClassName + "\", root = " + (MysqlSchemaUtils.isAggregateRoot(table) ? "true" : "false") + ", aggregate = \"" + simpleClassName + "\", description = \"" + MysqlSchemaUtils.getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); + if (StringUtils.isNotBlank(getAggregateRootAnnotation())) { if (MysqlSchemaUtils.isAggregateRoot(table)) { - SourceFileUtils.addIfNone(annotationLines, "@[\\w\\.]*" + aggregateRootAnnotationSimpleClassName + "(\\(.*\\))?", "@" + aggregateRootAnnotation); + SourceFileUtils.addIfNone(annotationLines, getAggregateRootAnnotation() + "(\\(.*\\))?", getAggregateRootAnnotation()); } else { - SourceFileUtils.removeText(annotationLines, "@" + aggregateRootAnnotation + "(\\(.*\\))?"); - SourceFileUtils.removeText(annotationLines, "@" + aggregateRootAnnotationSimpleClassName + "(\\(.*\\))?"); - } - } else { - if (MysqlSchemaUtils.isAggregateRoot(table)) { - SourceFileUtils.addIfNone(annotationLines, "\\/\\* @AggregateRoot(\\(.*\\))? \\*\\/", "/* @AggregateRoot */"); - } else { - SourceFileUtils.removeText(annotationLines, "\\/\\* @AggregateRoot(\\(.*\\))? \\*\\/"); + SourceFileUtils.removeText(annotationLines, getAggregateRootAnnotation() + "(\\(.*\\))?"); + SourceFileUtils.removeText(annotationLines, "@AggregateRoot(\\(.*\\))?"); } } +// else { +// if (MysqlSchemaUtils.isAggregateRoot(table)) { +// SourceFileUtils.addIfNone(annotationLines, "\\/\\* @AggregateRoot(\\(.*\\))? \\*\\/", "/* @AggregateRoot */"); +// } else { +// SourceFileUtils.removeText(annotationLines, "\\/\\* @AggregateRoot(\\(.*\\))? \\*\\/"); +// } +// } + SourceFileUtils.addIfNone(annotationLines, "@Entity(\\(.*\\))?", "@Entity"); SourceFileUtils.addIfNone(annotationLines, "@Table(\\(.*\\))?", "@Table(name = \"`" + tableName + "`\")"); SourceFileUtils.addIfNone(annotationLines, "@DynamicInsert(\\(.*\\))?", "@DynamicInsert"); SourceFileUtils.addIfNone(annotationLines, "@DynamicUpdate(\\(.*\\))?", "@DynamicUpdate"); @@ -807,13 +817,27 @@ public void writeEntitySourceFile(Map table, List writeLine(out, line)); + writeLine(out, mainSource); + out.close(); + if (generateSchema) { + writeSchemaSourceFile(table, columns, tablePackageMap, relations, basePackage, baseDir.replace("-domain", "-application")); + } + } + + private String generateEntityClassMainSource(Map table, List> columns, Map tablePackageMap, Map> relations, List enums, List annotationLines, List customerLines) throws IOException { + String tableName = MysqlSchemaUtils.getTableName(table); + String simpleClassName = getEntityJavaType(tableName); + StringWriter stringWriter = new StringWriter(); + BufferedWriter out = new BufferedWriter(stringWriter); annotationLines.forEach(line -> writeLine(out, line)); writeLine(out, "public class " + simpleClassName + (StringUtils.isNotBlank(entityBaseClass) ? " extends " + entityBaseClass : "") + " {"); if (customerLines.size() > 0) { @@ -866,10 +890,9 @@ public void writeEntitySourceFile(Map table, List table, Map column, Map> relations) { @@ -1040,6 +1063,7 @@ public void writeColumnProperty(BufferedWriter out, Map table, M } public void writeColumnComment(BufferedWriter out, Map column) { + String columnName = MysqlSchemaUtils.getColumnName(column); String columnJavaType = getColumnJavaType(column); writeLine(out, " /**"); for (String comment : MysqlSchemaUtils.getComment(column).split("[\\r\\n]")) { @@ -1048,7 +1072,7 @@ public void writeColumnComment(BufferedWriter out, Map column) { } writeLine(out, " * " + comment); if (MysqlSchemaUtils.hasEnum(column)) { - getLog().info("获取枚举java类型=" + columnJavaType); + getLog().info("获取枚举java类型:"+columnName + " -> " + columnJavaType); Map enumMap = EnumConfigMap.get(columnJavaType); if (enumMap == null) { enumMap = EnumConfigMap.get(MysqlSchemaUtils.getType(column)); @@ -1211,7 +1235,7 @@ public void writeEnumSourceFile(Map enumConfigs, String enumT writeLine(out, "public enum " + enumType + " {"); writeLine(out, ""); for (Map.Entry entry : enumConfigs.entrySet()) { - getLog().info(entry.getValue()[0] + " = " + entry.getKey() + " : " + entry.getValue()[1]); + getLog().info(" " + entry.getValue()[1] + " : " + entry.getValue()[0] + " = " + entry.getKey()); writeLine(out, " /**"); writeLine(out, " * " + entry.getValue()[1]); writeLine(out, " */"); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java index 405a411..cd744c8 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java @@ -10,10 +10,13 @@ import java.io.*; import java.nio.charset.Charset; import java.util.*; +import java.util.stream.Collectors; import static org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils.writeLine; /** + * 生成仓储 + * * @author binking338 * @date 2022-02-14 */ @@ -23,11 +26,10 @@ public class GenRepositoryMojo extends MyAbstractMojo { @Override public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("当前默认编码:" + Charset.defaultCharset().name()); - getLog().info("聚合根标注注解:" + aggregateRootAnnotation); - getLog().info("聚合根基类:" + aggregateRepositoryBaseClass); + getLog().info("聚合根标注注解:" + getAggregateRootAnnotation()); + getLog().info("聚合根基类:" + getAggregateRepositoryBaseClass()); getLog().info("跳过生成仓储的聚合根:" + ignoreAggregateRoots); - getLog().info("聚合仓储自定义代码:"); - getLog().info(aggregateRepositoryCustomerCode); + getLog().info("聚合仓储自定义代码:" + getAggregateRepositoryCustomerCode()); getLog().info(""); this.getLog().info("开始生成仓储代码"); @@ -54,9 +56,9 @@ public void execute() throws MojoExecutionException, MojoFailureException { applicationModulePath = absoluteCurrentDir; adapterModulePath = absoluteCurrentDir; } - String basePackage = org.apache.commons.lang3.StringUtils.isNotBlank(this.basePackage) + String basePackage = StringUtils.isNotBlank(this.basePackage) ? this.basePackage - : SourceFileUtils.resolveBasePackage(domainModulePath); + : SourceFileUtils.resolveDefaultBasePackage(domainModulePath); getLog().info(multiModule ? "多模块项目" : "单模块项目"); getLog().info("项目目录:" + projectDir); getLog().info("适配层目录:" + adapterModulePath); @@ -64,51 +66,40 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("领域层目录:" + domainModulePath); getLog().info("基础包名:" + basePackage); - // - - if (StringUtils.isBlank(aggregateRepositoryBaseClass)) { - // 默认聚合仓储基类 - aggregateRepositoryBaseClass = "org.netcorepal.cap4j.ddd.domain.repo.AggregateRepository<${EntityType}, ${IdentityType}>"; - } - if(StringUtils.isBlank(aggregateRepositoryCustomerCode)){ - aggregateRepositoryCustomerCode = - "@org.springframework.stereotype.Component\n" + - " public static class ${EntityType}JpaRepositoryAdapter extends org.netcorepal.cap4j.ddd.domain.repo.AbstractJpaRepository<${EntityType}, ${IdentityType}>\n" + - " {\n" + - " public ${EntityType}JpaRepositoryAdapter(org.springframework.data.jpa.repository.JpaSpecificationExecutor<${EntityType}> jpaSpecificationExecutor, org.springframework.data.jpa.repository.JpaRepository<${EntityType}, ${IdentityType}> jpaRepository) {\n" + - " super(jpaSpecificationExecutor, jpaRepository);\n" + - " }\n" + - " }" + - ""; - } try { //renameAggregateRepositorySourceFiles(basePackage, adapterModulePath); List files = SourceFileUtils.loadFiles(domainModulePath); - files.forEach(file -> { - String content = ""; - try { - content = FileUtils.fileRead(file); - } catch (IOException e) { - e.printStackTrace(); - } - boolean isAggregateRoot = Arrays.stream(content.replace("\r\n", "\n").split("\n")) - .anyMatch(line -> line.matches("^\\s*(@(\\s*[A-Za-z_][A-Za-z0-9_]*\\s*\\.?)+)+$") && line.matches("^\\s*@([^.]*\\.)*AggregateRoot\\s*\\s*$") - || line.matches("^\\s*\\/\\*\\s*@AggregateRoot\\s*\\*\\/\\s*$")); - if (isAggregateRoot) { - this.getLog().info("发现聚合根: " + SourceFileUtils.resolveClassName(file.getAbsolutePath())); - - String simpleClassName = SourceFileUtils.resolveSimpleClassName(file.getAbsolutePath()); - if (Arrays.stream(ignoreAggregateRoots.split("[\\,\\;]")) - .anyMatch(i -> i.equalsIgnoreCase(simpleClassName))) { - return; - } - try { - writeAggregateRepositorySourceFile(file.getAbsolutePath(), basePackage, adapterModulePath); - } catch (IOException e) { - e.printStackTrace(); - } - } + files = files.stream() + .filter(file -> "java".equalsIgnoreCase(FileUtils.extension(file.getName()))) + .collect(Collectors.toList()); + files.forEach(file->{ + this.getLog().debug("发现Java文件: " + SourceFileUtils.resolveClassName(file.getAbsolutePath())); }); + getLog().info("发现java文件数量:" + files.size()); + files.forEach(file -> { + this.getLog().debug("解析Java文件: " + SourceFileUtils.resolveClassName(file.getAbsolutePath())); + String content = ""; + try { + content = FileUtils.fileRead(file); + } catch (IOException e) { + e.printStackTrace(); + } + boolean isAggregateRoot = isAggregateRoot(content); + if (isAggregateRoot) { + this.getLog().info("发现聚合根: " + SourceFileUtils.resolveClassName(file.getAbsolutePath())); + + String simpleClassName = SourceFileUtils.resolveSimpleClassName(file.getAbsolutePath()); + if (Arrays.stream(ignoreAggregateRoots.split("[\\,\\;]")) + .anyMatch(i -> i.equalsIgnoreCase(simpleClassName))) { + return; + } + try { + writeAggregateRepositorySourceFile(file.getAbsolutePath(), basePackage, adapterModulePath); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); } catch (Exception e) { this.getLog().error("发生异常,", e); } @@ -116,6 +107,35 @@ public void execute() throws MojoExecutionException, MojoFailureException { this.getLog().info("结束生成仓储代码"); } + private boolean isAggregateRoot(String content) { + return Arrays.stream(content.split("(\r)|(\n)|(\r\n)")) + .filter(line -> line.trim().startsWith("@") || line.replace("\\s", "").equals("/*@AggregateRoot*/")) + .anyMatch(line -> { + boolean hasAggregateRoot = false; + if (StringUtils.isBlank(getAggregateRootAnnotation())) { + boolean oldAggregateRoot1 = line.matches("@AggregateRoot(\\(.*\\))?"); + boolean oldAggregateRoot2 = line.matches("^\\s*\\/\\*\\s*@AggregateRoot\\s*\\*\\/\\s*$"); + hasAggregateRoot = oldAggregateRoot1 || oldAggregateRoot2; + } else { + boolean isAggregateRootAnnotation = line.matches("@AggregateRoot(\\(.*\\))?"); + boolean isAggregateRootAnnotationFullName = line.matches(getAggregateRootAnnotation() + "(\\(.*\\))?"); + hasAggregateRoot = (isAggregateRootAnnotationFullName || isAggregateRootAnnotation); + } + if(hasAggregateRoot){ + return hasAggregateRoot; + } + + boolean hasAggregate = line.matches("@Aggregate\\s*\\(.*root\\s*=\\s*true.*\\)"); + boolean aggregateRoot = hasAggregate; + + getLog().debug("annotationline: " + line); + getLog().debug("hasAggregateRoot=" + hasAggregateRoot); + getLog().debug("hasAggregate=" + hasAggregate); + return aggregateRoot; + } + ); + } + /** * 返回能否从新生成实体 * @@ -228,7 +248,7 @@ public void writeAggregateRepositorySourceFile(String entitySourceFilePath, Stri writeLine(out, " * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); writeLine(out, " */"); } - writeLine(out, "public interface " + simpleClassName + "Repository extends " + aggregateRepositoryBaseClass.replace("${EntityType}", simpleClassName).replace("${IdentityType}", aggregateIdentityClass) + " {"); + writeLine(out, "public interface " + simpleClassName + "Repository extends " + getAggregateRepositoryBaseClass().replace("${EntityType}", simpleClassName).replace("${IdentityType}", aggregateIdentityClass) + " {"); if (customerLines.size() > 0) { for (String line : customerLines) { writeLine(out, line); @@ -236,7 +256,7 @@ public void writeAggregateRepositorySourceFile(String entitySourceFilePath, Stri } else { writeLine(out, " // 【自定义代码开始】本段落之外代码由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动"); writeLine(out, ""); - writeLine(out, " " + aggregateRepositoryCustomerCode.replace("${EntityType}", simpleClassName).replace("${IdentityType}", aggregateIdentityClass)); + writeLine(out, " " + getAggregateRepositoryCustomerCode().replace("${EntityType}", simpleClassName).replace("${IdentityType}", aggregateIdentityClass)); writeLine(out, ""); writeLine(out, " // 【自定义代码结束】本段落之外代码由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动"); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java index 2e1cdf3..85a9f11 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java @@ -2,9 +2,10 @@ import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugins.annotations.Parameter; +import org.codehaus.plexus.util.StringUtils; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; /** * 基Mojo @@ -156,6 +157,36 @@ public abstract class MyAbstractMojo extends AbstractMojo { */ @Parameter(property = "entityBaseClass", defaultValue = "") String entityBaseClass = ""; + + /** + * 实体类附加导入包 + * + * @parameter expression="${entityClassExtraImports}" + */ + @Parameter(property = "entityClassExtraImports", defaultValue = "") + List entityClassExtraImports = new ArrayList<>(); + + public List getEntityClassExtraImports() { + List importList = Arrays.asList( + "lombok.AllArgsConstructor", + "lombok.Builder", + "lombok.Getter", + "lombok.NoArgsConstructor", + "org.hibernate.annotations.GenericGenerator", + "org.hibernate.annotations.DynamicInsert", + "org.hibernate.annotations.DynamicUpdate", + "org.hibernate.annotations.Fetch", + "org.hibernate.annotations.FetchMode", + "org.hibernate.annotations.SQLDelete", + "org.hibernate.annotations.Where", + "org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate", + "javax.persistence.*" + ); + List imports = new ArrayList<>(importList); + imports.addAll(entityClassExtraImports); + return imports.stream().distinct().collect(Collectors.toList()); + } + /** * 实体辅助类输出模式,绝对路径或相对路径,abs|ref * @@ -163,6 +194,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { */ @Parameter(property = "entityMetaInfoClassOutputMode", defaultValue = "") String entityMetaInfoClassOutputMode = "abs"; + /** * 实体辅助类输出包 * @@ -185,6 +217,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { */ @Parameter(property = "fetchMode", defaultValue = "SUBSELECT") String fetchMode = "SUBSELECT"; + /** * 主键生成器 默认自增策略 * @@ -275,6 +308,16 @@ public abstract class MyAbstractMojo extends AbstractMojo { @Parameter(property = "aggregateRootAnnotation", defaultValue = "") String aggregateRootAnnotation = ""; + public String getAggregateRootAnnotation() { + if (StringUtils.isNotEmpty(aggregateRootAnnotation)) { + aggregateRootAnnotation = aggregateRootAnnotation.trim(); + if (!aggregateRootAnnotation.startsWith("@")) { + aggregateRootAnnotation = "@" + aggregateRootAnnotation; + } + } + return aggregateRootAnnotation; + } + /** * 聚合仓储基类型 * @@ -283,6 +326,14 @@ public abstract class MyAbstractMojo extends AbstractMojo { @Parameter(property = "aggregateRepositoryBaseClass", defaultValue = "") String aggregateRepositoryBaseClass = ""; + public String getAggregateRepositoryBaseClass() { + if (StringUtils.isBlank(aggregateRepositoryBaseClass)) { + // 默认聚合仓储基类 + aggregateRepositoryBaseClass = "org.netcorepal.cap4j.ddd.domain.repo.AggregateRepository<${EntityType}, ${IdentityType}>"; + } + return aggregateRepositoryBaseClass; + } + /** * 聚合仓储自定义代码 * @@ -291,6 +342,21 @@ public abstract class MyAbstractMojo extends AbstractMojo { @Parameter(property = "aggregateRepositoryCustomerCode", defaultValue = "") String aggregateRepositoryCustomerCode = ""; + public String getAggregateRepositoryCustomerCode(){ + if (StringUtils.isBlank(aggregateRepositoryCustomerCode)) { + aggregateRepositoryCustomerCode = + "@org.springframework.stereotype.Component\n" + + " public static class ${EntityType}JpaRepositoryAdapter extends org.netcorepal.cap4j.ddd.domain.repo.AbstractJpaRepository<${EntityType}, ${IdentityType}>\n" + + " {\n" + + " public ${EntityType}JpaRepositoryAdapter(org.springframework.data.jpa.repository.JpaSpecificationExecutor<${EntityType}> jpaSpecificationExecutor, org.springframework.data.jpa.repository.JpaRepository<${EntityType}, ${IdentityType}> jpaRepository) {\n" + + " super(jpaSpecificationExecutor, jpaRepository);\n" + + " }\n" + + " }" + + ""; + } + return aggregateRepositoryCustomerCode; + } + /** * 跳过生成仓储的聚合根 * 逗号','或分号';'分割 diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/MysqlSchemaUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/MysqlSchemaUtils.java index 0961b4c..10929a4 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/MysqlSchemaUtils.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/MysqlSchemaUtils.java @@ -1,8 +1,8 @@ package org.netcorepal.cap4j.ddd.codegen.misc; -import org.apache.commons.lang3.StringUtils; import org.apache.maven.plugin.Mojo; import org.apache.maven.plugin.logging.Log; +import org.codehaus.plexus.util.StringUtils; import java.util.Arrays; import java.util.HashMap; @@ -129,22 +129,22 @@ public static boolean hasLazy(Map table) { return hasAnyAnnotation(table, Arrays.asList("Lazy", "L")); } - public static boolean isLazy(Map table){ + public static boolean isLazy(Map table) { return isLazy(table, false); } public static boolean isLazy(Map table, boolean defaultLazy) { String val = getAnyAnnotation(table, Arrays.asList("Lazy", "L")); - if(defaultLazy) { - return StringUtils.compareIgnoreCase(val, "false") == 0 || StringUtils.compareIgnoreCase(val, "0") == 0 ? false : true; + if (defaultLazy) { + return "false".equalsIgnoreCase(val) || "0".equalsIgnoreCase(val) ? false : true; } else { - return StringUtils.compareIgnoreCase(val, "true") == 0 || StringUtils.compareIgnoreCase(val, "1") == 0 ? true : false; + return "true".equalsIgnoreCase(val) || "1".equalsIgnoreCase(val) ? true : false; } } public static boolean countIsOne(Map table) { String val = getAnyAnnotation(table, Arrays.asList("Count", "C")); - return StringUtils.compareIgnoreCase(val, "One") == 0 || StringUtils.compareIgnoreCase(val, "1") == 0 ? true : false; + return "One".equalsIgnoreCase(val) || "1".equalsIgnoreCase(val) ? true : false; } /** @@ -337,9 +337,9 @@ public static Map getEnum(Map column) { getLog().debug(enumConfig); List pair = Arrays.stream(enumConfig.split("\\:")) .map(c -> c.trim() - .replace("\n","") - .replace("\r","") - .replace("\t","")) + .replace("\n", "") + .replace("\r", "") + .replace("\t", "")) .collect(Collectors.toList()); if (pair.size() == 0) { continue; diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SourceFileUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SourceFileUtils.java index 7483380..f50e806 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SourceFileUtils.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SourceFileUtils.java @@ -1,12 +1,13 @@ package org.netcorepal.cap4j.ddd.codegen.misc; import com.sun.org.apache.xml.internal.serialize.LineSeparator; -import org.apache.commons.lang3.StringUtils; import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.StringUtils; import java.io.*; import java.net.URL; import java.util.*; +import java.util.function.BiFunction; /** * @author binking338 @@ -15,7 +16,8 @@ public class SourceFileUtils { private static Map> Cache = new HashMap<>(); - private final static String JAVA_SRC_DIRS = "src.main.java."; + private final static String SRC_MAIN_JAVA = "src.main.java."; + private final static String SRC_TEST_JAVA = "src.test.java."; public static List loadFiles(String baseDir) { if (Cache.containsKey(baseDir)) { @@ -91,7 +93,7 @@ public static String loadResourceFileContent(String path, String charsetName) th public static String resolveDirectory(String baseDir, String packageName) { String dir = null; try { - dir = new File(baseDir).getCanonicalPath() + File.separator + (JAVA_SRC_DIRS + packageName).replace(".", File.separator); + dir = new File(baseDir).getCanonicalPath() + File.separator + (SRC_MAIN_JAVA + packageName).replace(".", File.separator); } catch (IOException e) { e.printStackTrace(); } @@ -101,7 +103,7 @@ public static String resolveDirectory(String baseDir, String packageName) { public static String resolveSourceFile(String baseDir, String packageName, String className){ String file = null; try { - file = new File(baseDir).getCanonicalPath() + File.separator + (JAVA_SRC_DIRS + packageName).replace(".", File.separator) + File.separator + className + ".java"; + file = new File(baseDir).getCanonicalPath() + File.separator + (SRC_MAIN_JAVA + packageName).replace(".", File.separator) + File.separator + className + ".java"; } catch (IOException e) { e.printStackTrace(); } @@ -114,7 +116,16 @@ public static Optional findJavaFileBySimpleClassName(String baseDir, Strin public static String resolveClassName(String filePath) { String className = filePath.replace(File.separator, ".").replaceAll("\\.java$", ""); - className = className.substring(className.lastIndexOf(JAVA_SRC_DIRS) + JAVA_SRC_DIRS.length()); + int idx = -1; + + if(className.lastIndexOf(SRC_MAIN_JAVA) >= 0) { + idx = className.lastIndexOf(SRC_MAIN_JAVA) + SRC_MAIN_JAVA.length(); + } else if(className.lastIndexOf(SRC_TEST_JAVA) >= 0) { + idx = className.lastIndexOf(SRC_TEST_JAVA) + SRC_TEST_JAVA.length(); + } else { + return ""; + } + className = className.substring(idx); return className; } @@ -130,9 +141,9 @@ public static String resolveSimpleClassName(String filePath) { return simpleClassName; } - public static String resolveBasePackage(String baseDir) { + public static String resolveDefaultBasePackage(String baseDir) { try { - Optional javaFile = loadFiles(new File(baseDir).getCanonicalPath() + File.separator + JAVA_SRC_DIRS.replace(".", File.separator)) + Optional javaFile = loadFiles(new File(baseDir).getCanonicalPath() + File.separator + SRC_MAIN_JAVA.replace(".", File.separator)) .stream().filter(file -> FileUtils.getExtension(file.getAbsolutePath()).contains("java")).findFirst(); if (javaFile.isPresent()) { String packageName = resolvePackage(javaFile.get().getCanonicalPath()); @@ -201,14 +212,9 @@ public static void addIfNone(List list, String regex, String line) { } } - public static void addSortedIfNone(List list, String regex, String line) { + public static void addIfNone(List list, String regex, String line, BiFunction, String, Integer> idx) { if (!hasLine(list, regex)) { - Optional first = list.stream().filter(l -> l.compareToIgnoreCase(line) > 0).findFirst(); - if (first.isPresent()) { - list.add(list.indexOf(first.get()), line); - } else { - list.add(0, line); - } + list.add(idx.apply(list, line).intValue(), line); } } diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index a3f27b1..d665807 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -388,7 +388,7 @@ "name": "pom.xml", "format": "raw", "conflict": "overwrite", - "data": "\n 4.0.0\n\n ${groupId}\n ${artifactId}\n ${version}\n jar\n\n ddd-sample\n\n \n UTF-8\n UTF-8\n 1.8\n 1.0.0-alpha-1\n\n 1.18.16\n\n 31.1-jre\n 3.12.0\n 4.4\n 1.2.79\n\n 5.1.46\n 1.2.8\n\n 2.7.6\n 2021.0.5\n \n \n\n 1.6.14\n 3.0.3\n 2.2.7\n \n\n \n \n \n org.springframework.boot\n spring-boot-dependencies\n ${spring-boot.version}\n pom\n import\n \n \n org.springframework.cloud\n spring-cloud-dependencies\n ${spring-cloud.version}\n pom\n import\n \n \n \n\n \n \n org.projectlombok\n lombok\n true\n ${org.projectlombok.version}\n \n\n \n io.github.netcorepal\n cap4j-ddd-starter-jpa-rocketmq\n ${cap4j.version}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n provided\n \n\n \n \n org.springframework.boot\n spring-boot-starter-web\n \n \n org.springframework.boot\n spring-boot-starter-actuator\n \n \n org.springframework.boot\n spring-boot-starter-validation\n \n\n \n \n org.apache.commons\n commons-lang3\n ${commons.lang3.version}\n \n \n org.apache.commons\n commons-collections4\n ${commons.collections4.version}\n \n \n com.google.guava\n guava\n ${guava.version}\n \n \n com.alibaba\n fastjson\n ${fastjson.version}\n \n\n \n \n com.ctrip.framework.apollo\n apollo-client\n 1.7.0\n \n \n \n com.githup.liuyanggithup\n xxl-job-core\n 1.2.1\n \n \n \n org.springframework.cloud\n spring-cloud-starter-openfeign\n \n \n io.github.openfeign\n feign-okhttp\n 11.10\n \n\n \n \n org.springframework.boot\n spring-boot-starter-data-jpa\n \n \n \n org.mybatis.spring.boot\n mybatis-spring-boot-starter\n 2.1.3\n \n \n \n com.alibaba\n druid-spring-boot-starter\n ${spring-boot-druid.version}\n \n \n \n mysql\n mysql-connector-java\n ${mysql-connector.version}\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n org.springdoc\n springdoc-openapi-ui\n ${springdoc.version}\n \n \n com.github.xiaoymin\n knife4j-springdoc-ui\n ${knife4j.version}\n \n \n io.swagger.core.v3\n swagger-annotations\n ${swagger3.version}\n \n\n \n net.logstash.logback\n logstash-logback-encoder\n 6.6\n \n \n junit\n junit\n 3.8.1\n test\n \n \n \n ${project.artifactId}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n \n ${archTemplate}\n ${archTemplateEncoding}\n ${basePackage}\n ${multiModule}\n ${moduleNameSuffix4Adapter}\n ${moduleNameSuffix4Domain}\n ${moduleNameSuffix4Application}\n \n \n \n ${user}\n ${pwd}\n ${schema}\n ${table}
\n ${ignoreTable}\n ${idField}\n ${versionField}\n ${deletedField}\n ${readonlyFields}\n ${ignoreFields}\n ${entityBaseClass}\n ${entityMetaInfoClassOutputMode}\n ${entityMetaInfoClassOutputPackage}\n ${fetchMode}\n ${fetchType}\n ${idGenerator}\n ${enumValueField}\n ${enumNameField}\n ${enumUnmatchedThrowException}\n ${datePackage4Java}\n ${typeRemapping}\n ${generateDefault}\n ${generateDbType}\n ${generateSchema}\n ${generateBuild}\n ${aggregateIdentityClass}\n ${aggregateRootAnnotation}\n ${aggregateRepositoryBaseClass}\n ${aggregateRepositoryCustomerCode}\n ${ignoreAggregateRoots}\n
\n
\n \n org.springframework.boot\n spring-boot-maven-plugin\n 2.6.3\n \n \n \n \n repackage\n \n repackage\n \n \n ${basePackage}.StartApplication\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 1.8\n 1.8\n UTF-8\n \n \n \n org.apache.maven.plugins\n maven-archetype-plugin\n 3.2.0\n \n\n
\n
\n
" + "data": "\n 4.0.0\n\n ${groupId}\n ${artifactId}\n ${version}\n jar\n\n ddd-sample\n\n \n UTF-8\n UTF-8\n 1.8\n 1.0.0-alpha-1\n\n 1.18.16\n\n 31.1-jre\n 3.12.0\n 4.4\n 1.2.79\n\n 5.1.46\n 1.2.8\n\n 2.7.6\n 2021.0.5\n \n \n\n 1.6.14\n 3.0.3\n 2.2.7\n \n\n \n \n \n org.springframework.boot\n spring-boot-dependencies\n ${spring-boot.version}\n pom\n import\n \n \n org.springframework.cloud\n spring-cloud-dependencies\n ${spring-cloud.version}\n pom\n import\n \n \n \n\n \n \n org.projectlombok\n lombok\n true\n ${org.projectlombok.version}\n \n\n \n io.github.netcorepal\n cap4j-ddd-starter-jpa-rocketmq\n ${cap4j.version}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n provided\n \n\n \n \n org.springframework.boot\n spring-boot-starter-web\n \n \n org.springframework.boot\n spring-boot-starter-actuator\n \n \n org.springframework.boot\n spring-boot-starter-validation\n \n\n \n \n org.apache.commons\n commons-lang3\n ${commons.lang3.version}\n \n \n org.apache.commons\n commons-collections4\n ${commons.collections4.version}\n \n \n com.google.guava\n guava\n ${guava.version}\n \n \n com.alibaba\n fastjson\n ${fastjson.version}\n \n\n \n \n com.ctrip.framework.apollo\n apollo-client\n 1.7.0\n \n \n \n com.githup.liuyanggithup\n xxl-job-core\n 1.2.1\n \n \n \n org.springframework.cloud\n spring-cloud-starter-openfeign\n \n \n io.github.openfeign\n feign-okhttp\n 11.10\n \n\n \n \n org.springframework.boot\n spring-boot-starter-data-jpa\n \n \n \n org.mybatis.spring.boot\n mybatis-spring-boot-starter\n 2.1.3\n \n \n \n com.alibaba\n druid-spring-boot-starter\n ${spring-boot-druid.version}\n \n \n \n mysql\n mysql-connector-java\n ${mysql-connector.version}\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n org.springdoc\n springdoc-openapi-ui\n ${springdoc.version}\n \n \n com.github.xiaoymin\n knife4j-springdoc-ui\n ${knife4j.version}\n \n \n io.swagger.core.v3\n swagger-annotations\n ${swagger3.version}\n \n\n \n net.logstash.logback\n logstash-logback-encoder\n 6.6\n \n \n junit\n junit\n 3.8.1\n test\n \n \n \n ${project.artifactId}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n \n ${archTemplate}\n ${archTemplateEncoding}\n ${basePackage}\n ${multiModule}\n ${moduleNameSuffix4Adapter}\n ${moduleNameSuffix4Domain}\n ${moduleNameSuffix4Application}\n \n \n \n ${user}\n ${pwd}\n ${schema}\n ${table}
\n ${ignoreTable}\n ${idField}\n ${versionField}\n ${deletedField}\n ${readonlyFields}\n ${ignoreFields}\n ${entityBaseClass}\n ${entityClassExtraImports}\n ${entityMetaInfoClassOutputMode}\n ${entityMetaInfoClassOutputPackage}\n ${fetchMode}\n ${fetchType}\n ${idGenerator}\n ${enumValueField}\n ${enumNameField}\n ${enumUnmatchedThrowException}\n ${datePackage4Java}\n ${typeRemapping}\n ${generateDefault}\n ${generateDbType}\n ${generateSchema}\n ${generateBuild}\n ${aggregateIdentityClass}\n ${aggregateRootAnnotation}\n ${aggregateRepositoryBaseClass}\n ${aggregateRepositoryCustomerCode}\n ${ignoreAggregateRoots}\n
\n
\n \n org.springframework.boot\n spring-boot-maven-plugin\n 2.6.3\n \n \n \n \n repackage\n \n repackage\n \n \n ${basePackage}.StartApplication\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 1.8\n 1.8\n UTF-8\n \n \n \n org.apache.maven.plugins\n maven-archetype-plugin\n 3.2.0\n \n\n
\n
\n
" } ] } \ No newline at end of file diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java new file mode 100644 index 0000000..c64746c --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java @@ -0,0 +1,55 @@ +package org.netcorepal.cap4j.ddd.domain.aggregate.annotation; + +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 聚合信息 + * @author binking338 + * @date 2024/8/23 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Aggregate { + /** + * 实体名称 + * @return + */ + @AliasFor("entity") + String value() default ""; + + /** + * 实体名称 + * @return + */ + @AliasFor("value") + String entity() default ""; + + /** + * 是否聚合根 + * @return + */ + boolean root() default false; + + /** + * 实体描述 + * @return + */ + String description() default ""; + + /** + * 所属聚合 + * @return + */ + String aggregate() default ""; + + /** + * 归属实体名称 + * @return + */ + String parentEntity() default ""; +} From 881588b6008cea54f4baabc7e3ba0f9919e6024d Mon Sep 17 00:00:00 2001 From: binking338 Date: Sat, 24 Aug 2024 11:43:06 +0800 Subject: [PATCH 06/62] bugfix:https://github.com/netcorepal/cap4j/issues/21 --- README.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 42df6cb..fa8f08e 100644 --- a/README.md +++ b/README.md @@ -1144,7 +1144,7 @@ public class ${NAME} extends PageParam { root -> root.id().gt(param.id) ), JpaPageUtils.toSpringData(param)); - return JpaPageUtils.fromSpringData(page, p -> UserPageDto.builder() + return JpaPageUtils.fromSpringData(page, p -> ${NAME}Dto.builder() .id(p.getId()) .build()); } @@ -1204,9 +1204,9 @@ $Qry$.Handler $qry$Handler; @Schema(description = "接口说明") @GetMapping("/$qry$") public ResponseData<$Qry$.$Qry$Dto> $qry$(@Valid $Qry$ param) { - $Qry$.$Qry$Dto result = $qry$Handler.exec(param); - return ResponseData.success(result); - } + $Qry$.$Qry$Dto result = $qry$Handler.exec(param); + return ResponseData.success(result); +} ``` `aqryl` 适配mvc透出查询列表 ```java @@ -1217,9 +1217,9 @@ $Qry$.Handler $qry$Handler; @Schema(description = "接口说明") @GetMapping("/$qry$") public ResponseData> $qry$(@Valid $Qry$ param) { - List<$Qry$.$Qry$Dto> result = $qry$Handler.exec(param); - return ResponseData.success(result); - } + List<$Qry$.$Qry$Dto> result = $qry$Handler.exec(param); + return ResponseData.success(result); +} ``` `aqryp` 适配mvc透出查询分页列表 ```java @@ -1230,9 +1230,8 @@ $Qry$.Handler $qry$Handler; @Schema(description = "接口说明") @PostMapping("/$qry$") public ResponseData> $qry$(@RequestBody @Valid $Qry$ param) { - PageData<$Qry$.$Qry$Dto> result = $qry$Handler.exec(param); - return ResponseData.success(result); - }esponseData.success(result); - } + PageData<$Qry$.$Qry$Dto> result = $qry$Handler.exec(param); + return ResponseData.success(result); +} ``` ### have a nice trip! \ No newline at end of file From bb72081d279ce7e7132d3c20312496358fb4f697 Mon Sep 17 00:00:00 2001 From: binking338 Date: Sat, 24 Aug 2024 23:05:40 +0800 Subject: [PATCH 07/62] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=B8=AD=E4=BB=8B?= =?UTF-8?q?=E8=80=85=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MediatorAutoConfiguration.java | 34 +++++++ .../event/RocketMqEventAutoConfiguration.java | 10 +- .../repo/JpaRepositoryAutoConfiguration.java | 2 + .../main/resources/META-INF/spring.factories | 1 + .../cap4j/ddd/application/Mediator.java | 98 +++++++++++++++++++ .../application/MediatorConfiguration.java | 16 +++ .../cap4j/ddd/application/RequestHandler.java | 20 ++++ .../ddd/application/RequestSupervisor.java | 49 ++++++++++ .../RequestSupervisorConfiguration.java | 22 +++++ .../ddd/application/command/Command.java | 8 +- .../ddd/application/impl/DefaultMediator.java | 61 ++++++++++++ .../impl/DefaultRequestSupervisor.java | 57 +++++++++++ .../cap4j/ddd/application/query/Query.java | 8 +- .../domain/event/DomainEventSupervisor.java | 36 ++++--- .../DomainEventSupervisorConfiguration.java | 21 ++++ .../impl/DefaultDomainEventSupervisor.java | 1 - .../event/RocketMqDomainEventSubscriber.java | 5 +- .../event/RocketMqDomainEventSupervisor.java | 13 --- .../repo/AbstractJpaPersistListener.java | 3 +- .../domain/repo/AbstractJpaSpecification.java | 3 +- .../repo/JpaPersistListenerManager.java | 35 ++++--- .../domain/repo/JpaSpecificationManager.java | 33 ++++--- 22 files changed, 468 insertions(+), 68 deletions(-) create mode 100644 cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorConfiguration.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorConfiguration.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java delete mode 100644 ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSupervisor.java diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java new file mode 100644 index 0000000..e11a281 --- /dev/null +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java @@ -0,0 +1,34 @@ +package org.netcorepal.cap4j.ddd.application; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.impl.DefaultMediator; +import org.netcorepal.cap4j.ddd.application.impl.DefaultRequestSupervisor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +/** + * CQS自动配置类 + * + * @author binking338 + * @date 2024/8/24 + */ +@Configuration +@RequiredArgsConstructor +public class MediatorAutoConfiguration { + @Bean + public DefaultRequestSupervisor defaultRequestSupervisor(List requestHandlers){ + DefaultRequestSupervisor defaultRequestSupervisor = new DefaultRequestSupervisor(requestHandlers); + defaultRequestSupervisor.init(); + RequestSupervisorConfiguration.configure(defaultRequestSupervisor); + return defaultRequestSupervisor; + } + + @Bean + public DefaultMediator defaultMediator(){ + DefaultMediator defaultMediator = new DefaultMediator(); + MediatorConfiguration.configure(defaultMediator); + return defaultMediator; + } +} diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java index eb191f8..fb55b52 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java @@ -5,6 +5,7 @@ import org.netcorepal.cap4j.ddd.application.distributed.Locker; import org.netcorepal.cap4j.ddd.domain.event.configure.EventProperties; import org.netcorepal.cap4j.ddd.domain.event.configure.EventScheduleProperties; +import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; import org.netcorepal.cap4j.ddd.domain.event.persistence.ArchivedEventJpaRepository; import org.netcorepal.cap4j.ddd.domain.event.persistence.EventRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -59,7 +60,9 @@ public JpaEventRecordRepository jpaEventRecordRepository() { @Bean public RocketMqDomainEventSubscriberManager rocketMqDomainEventSubscriberManager() { - RocketMqDomainEventSubscriberManager domainEventSubscriberManager = new RocketMqDomainEventSubscriberManager(subscribers, applicationEventPublisher); + RocketMqDomainEventSubscriberManager domainEventSubscriberManager = new RocketMqDomainEventSubscriberManager( + subscribers, + applicationEventPublisher); return domainEventSubscriberManager; } @@ -78,9 +81,8 @@ public RocketMqDomainEventPublisher rocketMqDomainEventPublisher( } @Bean - public RocketMqDomainEventSupervisor rocketMqDomainEventSupervisor() { - RocketMqDomainEventSupervisor rocketMqDomainEventSupervisor = new RocketMqDomainEventSupervisor(); - return rocketMqDomainEventSupervisor; + public DefaultDomainEventSupervisor defaultDomainEventSupervisor() { + return (DefaultDomainEventSupervisor) DomainEventSupervisor.getInstance(); } @Bean diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java index ee4f3b9..90c7d05 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java @@ -35,12 +35,14 @@ public class JpaRepositoryAutoConfiguration { @Bean public JpaPersistListenerManager jpaPersistListenerManager(List persistListeners) { JpaPersistListenerManager persistListenerManager = new JpaPersistListenerManager(persistListeners); + persistListenerManager.init(); return persistListenerManager; } @Bean public JpaSpecificationManager jpaSpecificationManager(List specifications) { JpaSpecificationManager specificationManager = new JpaSpecificationManager(specifications); + specificationManager.init(); return specificationManager; } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories b/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories index c5017cc..152ecac 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + org.netcorepal.cap4j.ddd.application.MediatorAutoConfiguration,\ org.netcorepal.cap4j.ddd.application.distributed.configure.JdbcLockerProperties,\ org.netcorepal.cap4j.ddd.application.distributed.configure.SnowflakeProperties,\ org.netcorepal.cap4j.ddd.application.distributed.JdbcLockerAutoConfiguration,\ diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java new file mode 100644 index 0000000..95cec02 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java @@ -0,0 +1,98 @@ +package org.netcorepal.cap4j.ddd.application; + +import java.time.Duration; +import java.time.LocalDateTime; + +/** + * 中介者 + * + * @author binking338 + * @date 2024/8/24 + */ +public interface Mediator { + static Mediator getInstance() + { + return MediatorConfiguration.instance; + } + + /** + * 执行请求 + * + * @param request 请求参数 + * @param 请求参数类型 + */ + Object request(REQUEST request); + + /** + * 执行请求 + * + * @param request 请求参数 + * @param resultClass 返回结果类型 + * @param 请求参数类型 + * @param 返回结果类型 + */ + RESPONSE request(REQUEST request, Class resultClass); + + /** + * 执行请求 + * + * @param request 请求参数 + * @param paramClass 请求参数类型 + * @param resultClass 返回结果类型 + * @return 请求结果 + * @param 请求参数类型 + * @param 返回结果类型 + */ + RESPONSE request(REQUEST request, Class paramClass, Class resultClass); + + /** + * 通知事件 + * + * @param eventPayload 事件消息体 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload); + + /** + * 延迟通知事件 + * @param eventPayload 事件消息体 + * @param delay 延迟时长 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload, Duration delay); + + /** + * 定时通知事件 + * @param eventPayload 事件消息体 + * @param schedule 定时时间 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload, LocalDateTime schedule); + + /** + * 通知事件 + * @param eventPayload 事件消息体 + * @param entity 事件关联实体 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload, Object entity); + + /** + * 延迟通知事件 + * @param eventPayload 事件消息体 + * @param entity 事件关联实体 + * @param delay 延迟时长 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload, Object entity, Duration delay); + + /** + * 定时通知事件 + * @param eventPayload 事件消息体 + * @param entity 事件关联实体 + * @param schedule 定时时间 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload, Object entity, LocalDateTime schedule); + +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorConfiguration.java new file mode 100644 index 0000000..8cab324 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorConfiguration.java @@ -0,0 +1,16 @@ +package org.netcorepal.cap4j.ddd.application; + +/** + * todo: 类描述 + * + * @author binking338 + * @date 2024/8/24 + */ +public class MediatorConfiguration { + static Mediator instance = null; + + public static void configure(Mediator mediator) + { + instance = mediator; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java new file mode 100644 index 0000000..80c3727 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java @@ -0,0 +1,20 @@ +package org.netcorepal.cap4j.ddd.application; + +/** + * 请求接口 + * + * @param 请求参数 + * @param 返回结果 + * + * @author binking338 + * @date 2024/8/24 + */ +public interface RequestHandler { + + /** + * 执行请求 + * @param request 请求参数 + * @return 返回结果 + */ + RESPONSE exec(REQUEST request); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java new file mode 100644 index 0000000..d58d15b --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java @@ -0,0 +1,49 @@ +package org.netcorepal.cap4j.ddd.application; + +/** + * 请求管理器 + * + * @author binking338 + * @date 2024/8/24 + */ +public interface RequestSupervisor { + + /** + * 获取请求管理器 + * + * @return 请求管理器 + */ + static RequestSupervisor getInstance(){ + return RequestSupervisorConfiguration.instance; + } + + /** + * 执行请求 + * + * @param request 请求参数 + * @param 请求参数类型 + */ + Object request(REQUEST request); + + /** + * 执行请求 + * + * @param request 请求参数 + * @param resultClass 返回结果类型 + * @param 请求参数类型 + * @param 返回结果类型 + */ + RESPONSE request(REQUEST request, Class resultClass); + + /** + * 执行请求 + * + * @param request 请求参数 + * @param paramClass 请求参数类型 + * @param resultClass 返回结果类型 + * @return 请求结果 + * @param 请求参数类型 + * @param 返回结果类型 + */ + RESPONSE request(REQUEST request, Class paramClass, Class resultClass); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorConfiguration.java new file mode 100644 index 0000000..c5f76cf --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorConfiguration.java @@ -0,0 +1,22 @@ +package org.netcorepal.cap4j.ddd.application; + +import org.netcorepal.cap4j.ddd.application.impl.DefaultRequestSupervisor; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisor; + +/** + * 请求管理器配置 + * + * @author binking338 + * @date 2024/8/24 + */ +public class RequestSupervisorConfiguration { + static RequestSupervisor instance = null; + + /** + * 配置请求管理器 + * @param requestSupervisor {@link RequestSupervisor} + */ + public static void configure(RequestSupervisor requestSupervisor) { + instance = requestSupervisor; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java index 8a773b2..fb1e4c9 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java @@ -1,11 +1,17 @@ package org.netcorepal.cap4j.ddd.application.command; +import org.netcorepal.cap4j.ddd.application.RequestHandler; + /** * 命令接口 * * @author binking338 * @date + * + * @param + * @param */ -public interface Command { +public interface Command extends RequestHandler { + @Override RESULT exec(PARAM param); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java new file mode 100644 index 0000000..39a3f54 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java @@ -0,0 +1,61 @@ +package org.netcorepal.cap4j.ddd.application.impl; + +import org.netcorepal.cap4j.ddd.application.Mediator; +import org.netcorepal.cap4j.ddd.application.RequestSupervisor; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisor; + +import java.time.Duration; +import java.time.LocalDateTime; + +/** + * 默认中介者 + * + * @author binking338 + * @date 2024/8/24 + */ +public class DefaultMediator implements Mediator { + @Override + public Object request(REQUEST request) { + return RequestSupervisor.getInstance().request(request); + } + + @Override + public RESPONSE request(REQUEST request, Class resultClass) { + return RequestSupervisor.getInstance().request(request, resultClass); + } + + @Override + public RESPONSE request(REQUEST request, Class paramClass, Class resultClass) { + return RequestSupervisor.getInstance().request(request, paramClass, resultClass); + } + + @Override + public void notify(EVENT eventPayload) { + DomainEventSupervisor.getInstance().attach(eventPayload); + } + + @Override + public void notify(EVENT eventPayload, Duration delay) { + DomainEventSupervisor.getInstance().attach(eventPayload, delay); + } + + @Override + public void notify(EVENT eventPayload, LocalDateTime schedule) { + DomainEventSupervisor.getInstance().attach(eventPayload, schedule); + } + + @Override + public void notify(EVENT eventPayload, Object entity) { + DomainEventSupervisor.getInstance().attach(eventPayload, entity); + } + + @Override + public void notify(EVENT eventPayload, Object entity, Duration delay) { + DomainEventSupervisor.getInstance().attach(eventPayload, entity, delay); + } + + @Override + public void notify(EVENT eventPayload, Object entity, LocalDateTime schedule) { + DomainEventSupervisor.getInstance().attach(eventPayload, entity, schedule); + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java new file mode 100644 index 0000000..71fc22a --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java @@ -0,0 +1,57 @@ +package org.netcorepal.cap4j.ddd.application.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.RequestHandler; +import org.netcorepal.cap4j.ddd.application.RequestSupervisor; +import org.netcorepal.cap4j.ddd.share.ClassUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 默认请求管理器 + * + * @author binking338 + * @date 2024/8/24 + */ +@RequiredArgsConstructor +public class DefaultRequestSupervisor implements RequestSupervisor { + private final List requestHandlers; + + private Map, RequestHandler> requestHandlerMap = null; + + public void init() { + if(requestHandlerMap != null){ + return; + } + synchronized (DefaultRequestSupervisor.class){ + if(requestHandlerMap != null){ + return; + } + } + requestHandlerMap = new HashMap<>(); + for (RequestHandler requestHandler : requestHandlers) { + Class requestPayloadClass = ClassUtils.findMethod( + requestHandler.getClass(), + "exec", + m -> m.getParameterCount() == 1 + ).getParameters()[0].getType(); + requestHandlerMap.put(requestPayloadClass, requestHandler); + } + } + @Override + public Object request(PARAM param){ + return request(param, (Class)param.getClass(), Object.class); + } + @Override + public RESULT request(PARAM param, Class resultClass) { + return request(param, (Class)param.getClass(), resultClass); + } + + @Override + public RESULT request(PARAM param, Class paramClass, Class resultClass) { + init(); + return (RESULT) requestHandlerMap.get(paramClass).exec(param); + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/Query.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/Query.java index 76a4a71..53ce71d 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/Query.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/Query.java @@ -1,11 +1,17 @@ package org.netcorepal.cap4j.ddd.application.query; +import org.netcorepal.cap4j.ddd.application.RequestHandler; + /** * 查询接口 * * @author binking338 * @date + * + * @param 查询参数 + * @param 查询结果 */ -public interface Query { +public interface Query extends RequestHandler { + @Override RESULT exec(PARAM param); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java index d17e5e6..bae5eb4 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java @@ -11,36 +11,44 @@ * @date 2023/8/12 */ public interface DomainEventSupervisor { + /** + * 获取领域事件管理器 + * @return 领域事件管理器 + */ + static DomainEventSupervisor getInstance() { + return DomainEventSupervisorConfiguration.instance; + } + /** * 附加事件 - * @param eventPayload + * @param eventPayload 事件对象 */ void attach(Object eventPayload); /** * 附加事件 - * @param eventPayload + * @param eventPayload 事件对象 * @param delay 延迟发送 */ void attach(Object eventPayload, Duration delay); /** * 附加事件 - * @param eventPayload + * @param eventPayload 事件对象 * @param schedule 指定时间发送 */ void attach(Object eventPayload, LocalDateTime schedule); /** * 附加事件 - * @param eventPayload + * @param eventPayload 事件对象 * @param entity 绑定实体,该实体对象进入持久化上下文才会触发事件分发 */ void attach(Object eventPayload, Object entity); /** * 附加事件 - * @param eventPayload + * @param eventPayload 事件对象 * @param entity 绑定实体,该实体对象进入持久化上下文才会触发事件分发 * @param delay 延迟发送 */ @@ -48,7 +56,7 @@ public interface DomainEventSupervisor { /** * 附加事件 - * @param eventPayload + * @param eventPayload 事件对象 * @param entity 绑定实体,该实体对象进入持久化上下文才会触发事件分发 * @param schedule 指定时间发送 */ @@ -56,13 +64,13 @@ public interface DomainEventSupervisor { /** * 剥离事件 - * @param eventPayload + * @param eventPayload 事件对象 */ void detach(Object eventPayload); /** * 剥离事件 - * @param eventPayload - * @param entity + * @param eventPayload 事件对象 + * @param entity 关联实体 */ void detach(Object eventPayload, Object entity); /** @@ -72,21 +80,21 @@ public interface DomainEventSupervisor { /** * 弹出事件列表 - * @return + * @return 事件列表 */ Set popEvents(); /** * 弹出实体绑定的事件列表 - * @param entity - * @return + * @param entity 关联实体 + * @return 事件列表 */ public Set popEvents(Object entity); /** * 获取发送事件 - * @param eventPayload - * @return + * @param eventPayload 事件对象 + * @return 发送时间 */ LocalDateTime getDeliverTime(Object eventPayload); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java new file mode 100644 index 0000000..821695b --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java @@ -0,0 +1,21 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; + +/** + * 领域事件管理器配置 + * + * @author binking338 + * @date 2024/8/24 + */ +public class DomainEventSupervisorConfiguration { + static DomainEventSupervisor instance = new DefaultDomainEventSupervisor(); + + /** + * 配置领域事件管理器 + * @param domainEventSupervisor {@link DomainEventSupervisor} + */ + public static void configure(DomainEventSupervisor domainEventSupervisor) { + instance = domainEventSupervisor; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java index ecf6472..5252419 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java @@ -13,7 +13,6 @@ * @date 2023/8/13 */ public class DefaultDomainEventSupervisor implements DomainEventSupervisor { - public static DomainEventSupervisor instance = new DefaultDomainEventSupervisor(); private static final ThreadLocal> TL_EVENT_PAYLOADS = new ThreadLocal>(); private static final ThreadLocal>> TL_ENTITY_EVENT_PAYLOADS = new ThreadLocal>>(); private static final ThreadLocal> TL_EVENT_SCHEDULE_MAP = new ThreadLocal>(); diff --git a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java index 46766fc..b5008f7 100644 --- a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java +++ b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java @@ -16,10 +16,11 @@ public abstract class RocketMqDomainEventSubscriber implements DomainEven * @return */ public Class forDomainEventClass() { - return ((Class) ClassUtils.findMethod( + return (Class) ClassUtils.findMethod( this.getClass(), "onEvent", - m -> m.getParameterCount() == 1).getParameters()[0].getType()); + m -> m.getParameterCount() == 1 + ).getParameters()[0].getType(); } /** diff --git a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSupervisor.java b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSupervisor.java deleted file mode 100644 index 6d4de05..0000000 --- a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSupervisor.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.event; - -import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; - -/** - * 基于RocketMq的领域事件管理器 - * - * @author binking338 - * @date 2023/8/13 - */ -public class RocketMqDomainEventSupervisor extends DefaultDomainEventSupervisor { - -} diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java index 0fc01d8..6df4393 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java @@ -28,7 +28,8 @@ public Class forEntityClass(){ return ((Class) ClassUtils.findMethod( this.getClass(), "onChange", - m -> m.getParameterCount() == 1).getParameters()[0].getType()); + m -> m.getParameterCount() == 1 + ).getParameters()[0].getType()); } public boolean throwOnException() { diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java index 28f2555..b75f480 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java @@ -13,7 +13,8 @@ public Class forEntityClass(){ return ((Class) ClassUtils.findMethod( this.getClass(), "specify", - m -> m.getParameterCount() == 1).getParameters()[0].getType()); + m -> m.getParameterCount() == 1 + ).getParameters()[0].getType()); } public boolean forceBeforeTransaction(){ diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java index f2f5b3e..b0cb715 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java @@ -22,26 +22,31 @@ public class JpaPersistListenerManager implements PersistListenerManager { private Map> persistListenersMap; - private void init() { - if (persistListenersMap == null) { - synchronized (this) { - if (persistListenersMap == null) { - persistListenersMap = new HashMap<>(); - persistListeners.sort((a, b) -> - OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE) - OrderUtils.getOrder(b.getClass(), Ordered.LOWEST_PRECEDENCE) - ); - for (AbstractJpaPersistListener persistListener : persistListeners) { - if (!persistListenersMap.containsKey(persistListener.forEntityClass())) { - persistListenersMap.put(persistListener.forEntityClass(), new java.util.ArrayList()); - } - List persistListenerList = persistListenersMap.get(persistListener.forEntityClass()); - persistListenerList.add(persistListener); - } + public void init() { + if (persistListenersMap != null) { + return; + } + synchronized (this) { + if (persistListenersMap != null) { + return; + } + persistListenersMap = new HashMap<>(); + persistListeners.sort((a, b) -> + OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE) - OrderUtils.getOrder(b.getClass(), Ordered.LOWEST_PRECEDENCE) + ); + for (AbstractJpaPersistListener persistListener : persistListeners) { + if (!persistListenersMap.containsKey(persistListener.forEntityClass())) { + persistListenersMap.put(persistListener.forEntityClass(), new java.util.ArrayList()); } + List persistListenerList = persistListenersMap.get(persistListener.forEntityClass()); + persistListenerList.add(persistListener); } } } + + + /** * onCreate & onUpdate & onDelete * @param entity diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java index 76b7051..9096e5d 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java @@ -20,22 +20,25 @@ public class JpaSpecificationManager implements SpecificationManager { private final List specifications; private Map> specificationMap; - private void init(){ - if(specificationMap == null){ - synchronized (this){ - if(specificationMap == null){ - specificationMap = new java.util.HashMap>(); - specifications.sort((a,b)-> - OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE) - OrderUtils.getOrder(b.getClass(), Ordered.LOWEST_PRECEDENCE) - ); - for (AbstractJpaSpecification specification : specifications) { - if(!specificationMap.containsKey(specification.forEntityClass())){ - specificationMap.put(specification.forEntityClass(), new java.util.ArrayList()); - } - List specificationList = specificationMap.get(specification.forEntityClass()); - specificationList.add(specification); - } + public void init(){ + if(specificationMap != null){ + return; + } + synchronized (this){ + + if(specificationMap != null){ + return; + } + specificationMap = new java.util.HashMap>(); + specifications.sort((a,b)-> + OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE) - OrderUtils.getOrder(b.getClass(), Ordered.LOWEST_PRECEDENCE) + ); + for (AbstractJpaSpecification specification : specifications) { + if(!specificationMap.containsKey(specification.forEntityClass())){ + specificationMap.put(specification.forEntityClass(), new java.util.ArrayList()); } + List specificationList = specificationMap.get(specification.forEntityClass()); + specificationList.add(specification); } } } From 62ea79fc7351433d04078aa40c1c79e7c0c38b15 Mon Sep 17 00:00:00 2001 From: binking338 Date: Mon, 26 Aug 2024 13:53:20 +0800 Subject: [PATCH 08/62] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=B8=AD=E4=BB=8B?= =?UTF-8?q?=E8=80=85=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../event/RocketMqEventAutoConfiguration.java | 5 +- .../repo/JpaRepositoryAutoConfiguration.java | 7 +- .../cap4j/ddd/application/Mediator.java | 86 +-------------- .../ddd/application/impl/DefaultMediator.java | 14 +-- .../domain/event/DomainEventSupervisor.java | 2 +- .../DomainEventSupervisorConfiguration.java | 14 ++- .../ddd/domain/event/EventSupervisor.java | 67 ++++++++++++ .../impl/DefaultDomainEventSupervisor.java | 34 +++++- .../cap4j/ddd/domain/repo/Repository.java | 30 +++--- .../ddd/domain/repo/RepositorySupervisor.java | 101 ++++++++++++++++++ .../RepositorySupervisorConfiguration.java | 15 +++ .../cap4j/ddd/domain/repo/UnitOfWork.java | 12 ++- .../domain/repo/UnitOfWorkConfiguration.java | 21 ++++ .../repo/AbstractJpaPersistListener.java | 2 +- .../domain/repo/AbstractJpaRepository.java | 32 ++++-- .../domain/repo/AbstractJpaSpecification.java | 2 +- .../repo/JpaPersistListenerManager.java | 4 +- .../domain/repo/JpaSpecificationManager.java | 4 +- .../cap4j/ddd/domain/repo/JpaUnitOfWork.java | 83 ++++++++++++-- 20 files changed, 400 insertions(+), 137 deletions(-) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSupervisor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisorConfiguration.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWorkConfiguration.java diff --git a/README.md b/README.md index b9af82e..ce77158 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Maven Central Version](https://img.shields.io/maven-central/v/io.github.netcorepal/cap4j)](https://central.sonatype.com/artifact/io.github.netcorepal/cap4j) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/netcorepal/cap4j/blob/main/LICENSE) -本项目是 [CAP](https://github.com/dotnetcore/CAP) 项目的 Java 实现,基于[整洁架构](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)、`领域模型`、`Outbox`模式、`CQS`模式以及`UoW`模式等理念,cap4j期望解决如何`实现领域驱动设计`的问题。 +本项目是 [CAP](https://github.com/dotnetcore/CAP) 项目的 Java 实现,基于[整洁架构](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)、[Outbox](https://www.kamilgrzybek.com/blog/posts/the-outbox-pattern)模式、[CQS](https://martinfowler.com/bliki/CommandQuerySeparation.html)模式以及[UoW](https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/june/the-unit-of-work-pattern-and-persistence-ignorance)模式等理念,cap4j期望解决基于`领域模型`如何`实现领域驱动设计`的问题。 如果对以上架构理念有充分了解,那么cap4j的使用将会非常顺手。另一方面,通过cap4j来构建你的服务,你将学会一种实现领域驱动设计的完整落地方法。 diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java index fb55b52..86df2a9 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java @@ -82,7 +82,10 @@ public RocketMqDomainEventPublisher rocketMqDomainEventPublisher( @Bean public DefaultDomainEventSupervisor defaultDomainEventSupervisor() { - return (DefaultDomainEventSupervisor) DomainEventSupervisor.getInstance(); + DefaultDomainEventSupervisor defaultDomainEventSupervisor = new DefaultDomainEventSupervisor(); + DomainEventSupervisorConfiguration.configure((DomainEventSupervisor)defaultDomainEventSupervisor); + DomainEventSupervisorConfiguration.configure((EventSupervisor)defaultDomainEventSupervisor); + return defaultDomainEventSupervisor; } @Bean diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java index 90c7d05..00df349 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java @@ -48,6 +48,7 @@ public JpaSpecificationManager jpaSpecificationManager(List abstractJpaRepository, JpaSpecificationManager jpaSpecificationManager, JpaPersistListenerManager jpaPersistListenerManager, @Autowired(required = false) @@ -58,6 +59,7 @@ public JpaUnitOfWork jpaUnitOfWork( String svcName ) { JpaUnitOfWork unitOfWork = new JpaUnitOfWork( + abstractJpaRepository, applicationEventPublisher, domainEventSupervisor, domainEventPublisher, @@ -70,13 +72,16 @@ public JpaUnitOfWork jpaUnitOfWork( jpaUnitOfWorkProperties.getEntityGetIdMethod(), jpaUnitOfWorkProperties.getRetrieveCountWarnThreshold(), eventScheduleProperties.getCompenseIntervalSeconds()); + unitOfWork.init(); + UnitOfWorkConfiguration.configure(unitOfWork); return unitOfWork; } + @Configuration private static class JpaLoader { public JpaLoader(@Autowired(required = false) JpaUnitOfWork jpaUnitOfWork) { - JpaUnitOfWork.instance = jpaUnitOfWork; + JpaUnitOfWork.fixJpaAopWrapper(jpaUnitOfWork); } } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java index 95cec02..30c0ca2 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java @@ -1,5 +1,9 @@ package org.netcorepal.cap4j.ddd.application; +import org.netcorepal.cap4j.ddd.domain.event.EventSupervisor; +import org.netcorepal.cap4j.ddd.domain.repo.RepositorySupervisor; +import org.netcorepal.cap4j.ddd.domain.repo.UnitOfWork; + import java.time.Duration; import java.time.LocalDateTime; @@ -9,90 +13,10 @@ * @author binking338 * @date 2024/8/24 */ -public interface Mediator { +public interface Mediator extends RequestSupervisor, EventSupervisor { static Mediator getInstance() { return MediatorConfiguration.instance; } - /** - * 执行请求 - * - * @param request 请求参数 - * @param 请求参数类型 - */ - Object request(REQUEST request); - - /** - * 执行请求 - * - * @param request 请求参数 - * @param resultClass 返回结果类型 - * @param 请求参数类型 - * @param 返回结果类型 - */ - RESPONSE request(REQUEST request, Class resultClass); - - /** - * 执行请求 - * - * @param request 请求参数 - * @param paramClass 请求参数类型 - * @param resultClass 返回结果类型 - * @return 请求结果 - * @param 请求参数类型 - * @param 返回结果类型 - */ - RESPONSE request(REQUEST request, Class paramClass, Class resultClass); - - /** - * 通知事件 - * - * @param eventPayload 事件消息体 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload); - - /** - * 延迟通知事件 - * @param eventPayload 事件消息体 - * @param delay 延迟时长 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload, Duration delay); - - /** - * 定时通知事件 - * @param eventPayload 事件消息体 - * @param schedule 定时时间 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload, LocalDateTime schedule); - - /** - * 通知事件 - * @param eventPayload 事件消息体 - * @param entity 事件关联实体 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload, Object entity); - - /** - * 延迟通知事件 - * @param eventPayload 事件消息体 - * @param entity 事件关联实体 - * @param delay 延迟时长 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload, Object entity, Duration delay); - - /** - * 定时通知事件 - * @param eventPayload 事件消息体 - * @param entity 事件关联实体 - * @param schedule 定时时间 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload, Object entity, LocalDateTime schedule); - } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java index 39a3f54..1cdd953 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java @@ -2,7 +2,7 @@ import org.netcorepal.cap4j.ddd.application.Mediator; import org.netcorepal.cap4j.ddd.application.RequestSupervisor; -import org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisor; +import org.netcorepal.cap4j.ddd.domain.event.EventSupervisor; import java.time.Duration; import java.time.LocalDateTime; @@ -31,31 +31,31 @@ public RESPONSE request(REQUEST request, Class para @Override public void notify(EVENT eventPayload) { - DomainEventSupervisor.getInstance().attach(eventPayload); + EventSupervisor.getInstance().notify(eventPayload); } @Override public void notify(EVENT eventPayload, Duration delay) { - DomainEventSupervisor.getInstance().attach(eventPayload, delay); + EventSupervisor.getInstance().notify(eventPayload, delay); } @Override public void notify(EVENT eventPayload, LocalDateTime schedule) { - DomainEventSupervisor.getInstance().attach(eventPayload, schedule); + EventSupervisor.getInstance().notify(eventPayload, schedule); } @Override public void notify(EVENT eventPayload, Object entity) { - DomainEventSupervisor.getInstance().attach(eventPayload, entity); + EventSupervisor.getInstance().notify(eventPayload, entity); } @Override public void notify(EVENT eventPayload, Object entity, Duration delay) { - DomainEventSupervisor.getInstance().attach(eventPayload, entity, delay); + EventSupervisor.getInstance().notify(eventPayload, entity, delay); } @Override public void notify(EVENT eventPayload, Object entity, LocalDateTime schedule) { - DomainEventSupervisor.getInstance().attach(eventPayload, entity, schedule); + EventSupervisor.getInstance().notify(eventPayload, entity, schedule); } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java index bae5eb4..4a9e2ef 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java @@ -16,7 +16,7 @@ public interface DomainEventSupervisor { * @return 领域事件管理器 */ static DomainEventSupervisor getInstance() { - return DomainEventSupervisorConfiguration.instance; + return DomainEventSupervisorConfiguration.domainEventSupervisor; } /** diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java index 821695b..d6a6671 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java @@ -9,13 +9,23 @@ * @date 2024/8/24 */ public class DomainEventSupervisorConfiguration { - static DomainEventSupervisor instance = new DefaultDomainEventSupervisor(); + static DomainEventSupervisor domainEventSupervisor = null; + + static EventSupervisor eventSupervisor = null; /** * 配置领域事件管理器 * @param domainEventSupervisor {@link DomainEventSupervisor} */ public static void configure(DomainEventSupervisor domainEventSupervisor) { - instance = domainEventSupervisor; + DomainEventSupervisorConfiguration.domainEventSupervisor = domainEventSupervisor; + } + + /** + * 配置事件管理器 + * @param eventSupervisor {@link EventSupervisor} + */ + public static void configure(EventSupervisor eventSupervisor) { + DomainEventSupervisorConfiguration.eventSupervisor = eventSupervisor; } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSupervisor.java new file mode 100644 index 0000000..6ea35e3 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSupervisor.java @@ -0,0 +1,67 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import java.time.Duration; +import java.time.LocalDateTime; + +/** + * todo: 类描述 + * + * @author binking338 + * @date 2024/8/25 + */ +public interface EventSupervisor { + + static EventSupervisor getInstance() { + return DomainEventSupervisorConfiguration.eventSupervisor; + } + + /** + * 通知事件 + * + * @param eventPayload 事件消息体 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload); + + /** + * 延迟通知事件 + * @param eventPayload 事件消息体 + * @param delay 延迟时长 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload, Duration delay); + + /** + * 定时通知事件 + * @param eventPayload 事件消息体 + * @param schedule 定时时间 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload, LocalDateTime schedule); + + /** + * 通知事件 + * @param eventPayload 事件消息体 + * @param entity 事件关联实体 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload, Object entity); + + /** + * 延迟通知事件 + * @param eventPayload 事件消息体 + * @param entity 事件关联实体 + * @param delay 延迟时长 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload, Object entity, Duration delay); + + /** + * 定时通知事件 + * @param eventPayload 事件消息体 + * @param entity 事件关联实体 + * @param schedule 定时时间 + * @param 事件消息类型 + */ + void notify(EVENT eventPayload, Object entity, LocalDateTime schedule); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java index 5252419..bb2835d 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java @@ -1,6 +1,7 @@ package org.netcorepal.cap4j.ddd.domain.event.impl; import org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisor; +import org.netcorepal.cap4j.ddd.domain.event.EventSupervisor; import java.time.Duration; import java.time.LocalDateTime; @@ -12,7 +13,7 @@ * @author binking338 * @date 2023/8/13 */ -public class DefaultDomainEventSupervisor implements DomainEventSupervisor { +public class DefaultDomainEventSupervisor implements DomainEventSupervisor, EventSupervisor { private static final ThreadLocal> TL_EVENT_PAYLOADS = new ThreadLocal>(); private static final ThreadLocal>> TL_ENTITY_EVENT_PAYLOADS = new ThreadLocal>>(); private static final ThreadLocal> TL_EVENT_SCHEDULE_MAP = new ThreadLocal>(); @@ -130,4 +131,35 @@ public LocalDateTime getDeliverTime(Object eventPayload) { return LocalDateTime.now(); } } + + + @Override + public void notify(EVENT eventPayload) { + attach(eventPayload); + } + + @Override + public void notify(EVENT eventPayload, Duration delay) { + attach(eventPayload, delay); + } + + @Override + public void notify(EVENT eventPayload, LocalDateTime schedule) { + attach(eventPayload, schedule); + } + + @Override + public void notify(EVENT eventPayload, Object entity) { + attach(eventPayload, entity); + } + + @Override + public void notify(EVENT eventPayload, Object entity, Duration delay) { + attach(eventPayload, entity, delay); + } + + @Override + public void notify(EVENT eventPayload, Object entity, LocalDateTime schedule) { + attach(eventPayload, entity, schedule); + } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java index ce86aeb..c1f67e7 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java @@ -16,45 +16,38 @@ public interface Repository { /** - * 通过ID判断实体是否存在 - * @param id + * 根据条件获取实体列表 + * @param condition + * @param orders * @return */ - boolean existsById(Object id); + List find(Object condition, List orders); /** * 通过ID获取实体 * @param id * @return */ - Optional getById(Object id); + Optional findById(Object id); /** * 通过ID获取实体 * @param ids * @return */ - List listByIds(Iterable ids); - + List findByIds(Iterable ids); /** * 根据条件获取实体 * @param condition * @return */ - Optional getBy(Object condition); - /** - * 根据条件获取实体列表 - * @param condition - * @param orders - * @return - */ - List listBy(Object condition, List orders); + Optional findOne(Object condition); /** * 根据条件获取实体分页列表 * @param condition * @param pageParam * @return */ - PageData pageBy(Object condition, PageParam pageParam); + PageData findPage(Object condition, PageParam pageParam); /** * 根据条件获取实体计数 @@ -69,4 +62,11 @@ public interface Repository { * @return */ boolean exists(Object condition); + + /** + * 通过ID判断实体是否存在 + * @param id + * @return + */ + boolean existsById(Object id); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java new file mode 100644 index 0000000..de9486a --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java @@ -0,0 +1,101 @@ +package org.netcorepal.cap4j.ddd.domain.repo; + +import org.netcorepal.cap4j.ddd.share.OrderInfo; +import org.netcorepal.cap4j.ddd.share.PageData; +import org.netcorepal.cap4j.ddd.share.PageParam; + +import java.util.List; +import java.util.Optional; + +/** + * 仓储管理器 + * + * @author binking338 + * @date 2024/8/25 + */ +public interface RepositorySupervisor { + static RepositorySupervisor getInstance(){ + return RepositorySupervisorConfiguration.instance; + } + + /** + * 获取仓储 + * @param entityClass 实体类型 + * @return {@link Repository} + * @param 实体类型 + */ + Repository repo(Class entityClass); + + /** + * 根据条件获取实体列表 + * @param entityClass + * @param condition + * @param orders + * @return + * @param + */ + List find(Class entityClass, Object condition, List orders); + + /** + * 根据条件获取单个实体 + * @param entityClass + * @param condition + * @return + * @param + */ + Optional findOne(Class entityClass, Object condition); + /** + * 根据条件获取实体分页列表 + * @param entityClass + * @param condition + * @param pageParam + * @return + * @param + */ + PageData findPage(Class entityClass, Object condition, PageParam pageParam); + + /** + * 根据ID获取实体 + * @param entityClass + * @param id + * @return + * @param + */ + Optional findById(Class entityClass, Object id); + /** + * 根据ID获取实体列表 + * @param entityClass + * @param ids + * @return + * @param + */ + List findByIds(Class entityClass, Iterable ids); + + /** + * 根据条件获取实体计数 + * @param entityClass + * @param condition + * @return + * @param + */ + long count(Class entityClass, Object condition); + + /** + * 根据条件判断实体是否存在 + * @param entityClass + * @param condition + * @return + * @param + */ + boolean exists(Class entityClass, Object condition); + + /** + * 通过ID判断实体是否存在 + * @param entityClass + * @param id + * @return + * @param + */ + boolean existsById(Class entityClass, Object id); + +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisorConfiguration.java new file mode 100644 index 0000000..d0fd565 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisorConfiguration.java @@ -0,0 +1,15 @@ +package org.netcorepal.cap4j.ddd.domain.repo; + +/** + * 仓储管理器配置 + * + * @author binking338 + * @date 2024/8/25 + */ +public class RepositorySupervisorConfiguration { + static RepositorySupervisor instance = null; + + public static void configure(RepositorySupervisor repositorySupervisor) { + instance = repositorySupervisor; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWork.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWork.java index 7915ff1..060d149 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWork.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWork.java @@ -8,16 +8,20 @@ * @author binking338 * @date 2023/8/5 */ -public interface UnitOfWork { +public interface UnitOfWork extends RepositorySupervisor { + static UnitOfWork getInstance(){ + return UnitOfWorkConfiguration.instance; + } + /** * 新增或更新持久化记录 - * @param entity + * @param entity 实体对象 */ void persist(Object entity); /** * 移除持久化记录 - * @param entity + * @param entity 实体对象 */ void remove(Object entity); @@ -28,7 +32,7 @@ public interface UnitOfWork { /** * 提交事务 - * @param propagation + * @param propagation 事务传播特性 */ void save(Propagation propagation); diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWorkConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWorkConfiguration.java new file mode 100644 index 0000000..39333c2 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWorkConfiguration.java @@ -0,0 +1,21 @@ +package org.netcorepal.cap4j.ddd.domain.repo; + +/** + * 工作单元配置 + * + * @author binking338 + * @date 2024/8/25 + */ +public class UnitOfWorkConfiguration { + static UnitOfWork instance = null; + + /** + * 配置工作单元 + * + * @param unitOfWork 工作单元 + */ + public static void configure(UnitOfWork unitOfWork) + { + instance = unitOfWork; + } +} diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java index 6df4393..c4e828d 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java @@ -24,7 +24,7 @@ public T getBean(String name, Class clazz){ } - public Class forEntityClass(){ + Class forEntityClass(){ return ((Class) ClassUtils.findMethod( this.getClass(), "onChange", diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java index e716788..94de95d 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java @@ -1,9 +1,11 @@ package org.netcorepal.cap4j.ddd.domain.repo; import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.share.ClassUtils; import org.netcorepal.cap4j.ddd.share.OrderInfo; import org.netcorepal.cap4j.ddd.share.PageData; import org.netcorepal.cap4j.ddd.share.PageParam; +import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -11,6 +13,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -23,18 +27,25 @@ * @date 2023/8/13 */ @RequiredArgsConstructor -public abstract class AbstractJpaRepository implements Repository { +public class AbstractJpaRepository implements Repository { private final JpaSpecificationExecutor jpaSpecificationExecutor; private final JpaRepository jpaRepository; - public Optional getById(Object id) { + Class forEntityClass(){ + return (Class) ResolvableType.forType( + ((ParameterizedType) this.getClass().getGenericSuperclass()) + .getActualTypeArguments()[0] + ).toClass(); + } + + public Optional findById(Object id) { List ids = new ArrayList<>(1); ids.add((ID) id); Optional entity = jpaRepository.findAllById(ids).stream().findFirst(); return entity; } - public List listByIds(Iterable ids){ + public List findByIds(Iterable ids){ List entities = jpaRepository.findAllById((Iterable) ids); return entities; } @@ -43,11 +54,17 @@ public boolean existsById(Object id) { return jpaRepository.existsById((ID) id); } - public Optional getBy(Object condition) { + public Optional findOne(Object condition) { return jpaSpecificationExecutor.findOne((org.springframework.data.jpa.domain.Specification) condition); } - public List listBy(Object condition, List orders) { + @Override + public PageData findPage(Object condition, PageParam pageParam) { + Page page = jpaSpecificationExecutor.findAll((org.springframework.data.jpa.domain.Specification) condition, convertPageable(pageParam)); + return convertPageData(page); + } + + public List find(Object condition, List orders) { Sort sort = Sort.unsorted(); if (orders != null && !orders.isEmpty()) { sort = convertSort(orders); @@ -56,11 +73,6 @@ public List listBy(Object condition, List orders) { return entities; } - public PageData pageBy(Object condition, PageParam pageParam) { - Page page = jpaSpecificationExecutor.findAll((org.springframework.data.jpa.domain.Specification) condition, convertPageable(pageParam)); - return convertPageData(page); - } - public long count(Object condition) { long result = jpaSpecificationExecutor.count((org.springframework.data.jpa.domain.Specification) condition); return result; diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java index b75f480..f4f4d49 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java @@ -9,7 +9,7 @@ * @date 2023/8/13 */ public abstract class AbstractJpaSpecification implements Specification { - public Class forEntityClass(){ + Class forEntityClass(){ return ((Class) ClassUtils.findMethod( this.getClass(), "specify", diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java index b0cb715..ea63a95 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java @@ -18,9 +18,9 @@ @RequiredArgsConstructor @Slf4j public class JpaPersistListenerManager implements PersistListenerManager { - private final List persistListeners; + protected final List persistListeners; - private Map> persistListenersMap; + Map> persistListenersMap; public void init() { if (persistListenersMap != null) { diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java index 9096e5d..f2e9d63 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java @@ -17,8 +17,8 @@ @RequiredArgsConstructor @Slf4j public class JpaSpecificationManager implements SpecificationManager { - private final List specifications; - private Map> specificationMap; + protected final List specifications; + protected Map> specificationMap; public void init(){ if(specificationMap != null){ diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java index b31410c..db405d0 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java @@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.netcorepal.cap4j.ddd.domain.event.*; import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; -import org.netcorepal.cap4j.ddd.share.DomainException; +import org.netcorepal.cap4j.ddd.share.*; import org.hibernate.engine.spi.SessionImplementor; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; @@ -38,7 +38,7 @@ @RequiredArgsConstructor @Slf4j public class JpaUnitOfWork implements UnitOfWork { - + private final List repositories; private final ApplicationEventPublisher applicationEventPublisher; private final DomainEventSupervisor domainEventSupervisor; private final DomainEventPublisher domainEventPublisher; @@ -48,6 +48,14 @@ public class JpaUnitOfWork implements UnitOfWork { private final JpaPersistListenerManager jpaPersistListenerManager; private final DomainEventMessageInterceptor domainEventMessageInterceptor; + @Getter + @PersistenceContext + protected EntityManager entityManager; + protected static JpaUnitOfWork instance; + public static void fixJpaAopWrapper(JpaUnitOfWork unitOfWork) { + instance = unitOfWork; + } + private final String svcName; private final String entityGetIdMethod; private final int retrieveCountWarnThreshold; @@ -57,6 +65,72 @@ public class JpaUnitOfWork implements UnitOfWork { private ThreadLocal> removedEntitiesThreadLocal = new ThreadLocal<>(); private ThreadLocal entityPersisttedEventThreadLocal = ThreadLocal.withInitial(() -> new EntityPersisttedEvent(instance, new HashSet<>(), new HashSet<>(), new HashSet<>())); + protected Map repositoryMap = null; + public void init(){ + if(repositoryMap != null){ + return; + } + synchronized (this){ + if(repositoryMap != null){ + return; + } + } + repositoryMap = new HashMap<>(); + repositories.forEach(repository -> { + repositoryMap.put(repository.forEntityClass(), repository); + }); + } + + @Override + public Repository repo(Class entityClass) { + init(); + if(!repositoryMap.containsKey(entityClass)){ + throw new DomainException("仓储不存在:" + entityClass.getTypeName()); + } + return repositoryMap.get(entityClass); + } + + @Override + public List find(Class entityClass, Object condition, List orders) { + return repo(entityClass).find(condition, orders); + } + + @Override + public Optional findOne(Class entityClass, Object condition) { + return repo(entityClass).findOne(condition); + } + + @Override + public PageData findPage(Class entityClass, Object condition, PageParam pageParam) { + return repo(entityClass).findPage(condition, pageParam); + } + + @Override + public Optional findById(Class entityClass, Object id) { + return repo(entityClass).findById(id); + } + + @Override + public List findByIds(Class entityClass, Iterable ids) { + return repo(entityClass).findByIds(ids); + } + + @Override + public long count(Class entityClass, Object condition) { + return repo(entityClass).count(condition); + } + + @Override + public boolean exists(Class entityClass, Object condition) { + return repo(entityClass).exists(condition); + } + + @Override + public boolean existsById(Class entityClass, Object id) { + return repo(entityClass).existsById(id); + } + + public void persist(Object entity) { if (persistedEntitiesThreadLocal.get() == null) { persistedEntitiesThreadLocal.set(new HashSet<>()); @@ -166,11 +240,6 @@ public void reset() { entityPersisttedEventThreadLocal.get().reset(); } - @Getter - @PersistenceContext - protected EntityManager entityManager; - protected static JpaUnitOfWork instance; - public interface QueryBuilder { void build(CriteriaBuilder cb, CriteriaQuery cq, Root root); } From 3ed570fe9825c6d0fc9310fee194dc948ef1d865 Mon Sep 17 00:00:00 2001 From: binking338 Date: Mon, 26 Aug 2024 13:56:57 +0800 Subject: [PATCH 09/62] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7=201.0.0-alpha-2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- cap4j-ddd-codegen-maven-plugin/pom.xml | 4 ++-- cap4j-ddd-codegen-template.json | 2 +- cap4j-ddd-starter-jpa-rocketmq/pom.xml | 4 ++-- ddd-core/pom.xml | 2 +- ddd-distributed-idgenerator-snowflake/pom.xml | 2 +- ddd-distributed-locker-jdbc/pom.xml | 2 +- ddd-domain-event-jpa/pom.xml | 2 +- ddd-domain-event-rocketmq/pom.xml | 2 +- ddd-domain-repo-jpa/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ce77158..c3573ea 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ io.github.netcorepal cap4j-ddd-codegen-maven-plugin - 1.0.0-alpha-1 + 1.0.0-alpha-2 provided @@ -39,7 +39,7 @@ io.github.netcorepal cap4j-ddd-codegen-maven-plugin - 1.0.0-alpha-1 + 1.0.0-alpha-2 https://raw.githubusercontent.com/netcorepal/cap4j/main/cap4j-ddd-codegen-template.json org.netcorepal.cap4j.ddd.example @@ -340,7 +340,7 @@ CREATE TABLE `order_item` ( > > 如果想要对这套语法有个详细完整的了解,可以通过如下maven指令获取语法帮助。 > ```shell -> mvn io.github.netcorepal:cap4j-ddd-codegen-maven-plugin:1.0.0-alpha-1:help +> mvn io.github.netcorepal:cap4j-ddd-codegen-maven-plugin:1.0.0-alpha-2:help > # or > mvn cap4j-ddd-codegen:help > ``` diff --git a/cap4j-ddd-codegen-maven-plugin/pom.xml b/cap4j-ddd-codegen-maven-plugin/pom.xml index c2457cd..78e2e69 100644 --- a/cap4j-ddd-codegen-maven-plugin/pom.xml +++ b/cap4j-ddd-codegen-maven-plugin/pom.xml @@ -6,13 +6,13 @@ io.github.netcorepal cap4j - 1.0.0-alpha-1 + 1.0.0-alpha-2 ../pom.xml io.github.netcorepal cap4j-ddd-codegen-maven-plugin - 1.0.0-alpha-1 + 1.0.0-alpha-2 maven-plugin diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index d665807..1c6aee3 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -388,7 +388,7 @@ "name": "pom.xml", "format": "raw", "conflict": "overwrite", - "data": "\n 4.0.0\n\n ${groupId}\n ${artifactId}\n ${version}\n jar\n\n ddd-sample\n\n \n UTF-8\n UTF-8\n 1.8\n 1.0.0-alpha-1\n\n 1.18.16\n\n 31.1-jre\n 3.12.0\n 4.4\n 1.2.79\n\n 5.1.46\n 1.2.8\n\n 2.7.6\n 2021.0.5\n \n \n\n 1.6.14\n 3.0.3\n 2.2.7\n \n\n \n \n \n org.springframework.boot\n spring-boot-dependencies\n ${spring-boot.version}\n pom\n import\n \n \n org.springframework.cloud\n spring-cloud-dependencies\n ${spring-cloud.version}\n pom\n import\n \n \n \n\n \n \n org.projectlombok\n lombok\n true\n ${org.projectlombok.version}\n \n\n \n io.github.netcorepal\n cap4j-ddd-starter-jpa-rocketmq\n ${cap4j.version}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n provided\n \n\n \n \n org.springframework.boot\n spring-boot-starter-web\n \n \n org.springframework.boot\n spring-boot-starter-actuator\n \n \n org.springframework.boot\n spring-boot-starter-validation\n \n\n \n \n org.apache.commons\n commons-lang3\n ${commons.lang3.version}\n \n \n org.apache.commons\n commons-collections4\n ${commons.collections4.version}\n \n \n com.google.guava\n guava\n ${guava.version}\n \n \n com.alibaba\n fastjson\n ${fastjson.version}\n \n\n \n \n com.ctrip.framework.apollo\n apollo-client\n 1.7.0\n \n \n \n com.githup.liuyanggithup\n xxl-job-core\n 1.2.1\n \n \n \n org.springframework.cloud\n spring-cloud-starter-openfeign\n \n \n io.github.openfeign\n feign-okhttp\n 11.10\n \n\n \n \n org.springframework.boot\n spring-boot-starter-data-jpa\n \n \n \n org.mybatis.spring.boot\n mybatis-spring-boot-starter\n 2.1.3\n \n \n \n com.alibaba\n druid-spring-boot-starter\n ${spring-boot-druid.version}\n \n \n \n mysql\n mysql-connector-java\n ${mysql-connector.version}\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n org.springdoc\n springdoc-openapi-ui\n ${springdoc.version}\n \n \n com.github.xiaoymin\n knife4j-springdoc-ui\n ${knife4j.version}\n \n \n io.swagger.core.v3\n swagger-annotations\n ${swagger3.version}\n \n\n \n net.logstash.logback\n logstash-logback-encoder\n 6.6\n \n \n junit\n junit\n 3.8.1\n test\n \n \n \n ${project.artifactId}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n \n ${archTemplate}\n ${archTemplateEncoding}\n ${basePackage}\n ${multiModule}\n ${moduleNameSuffix4Adapter}\n ${moduleNameSuffix4Domain}\n ${moduleNameSuffix4Application}\n \n \n \n ${user}\n ${pwd}\n ${schema}\n ${table}
\n ${ignoreTable}\n ${idField}\n ${versionField}\n ${deletedField}\n ${readonlyFields}\n ${ignoreFields}\n ${entityBaseClass}\n ${entityClassExtraImports}\n ${entityMetaInfoClassOutputMode}\n ${entityMetaInfoClassOutputPackage}\n ${fetchMode}\n ${fetchType}\n ${idGenerator}\n ${enumValueField}\n ${enumNameField}\n ${enumUnmatchedThrowException}\n ${datePackage4Java}\n ${typeRemapping}\n ${generateDefault}\n ${generateDbType}\n ${generateSchema}\n ${generateBuild}\n ${aggregateIdentityClass}\n ${aggregateRootAnnotation}\n ${aggregateRepositoryBaseClass}\n ${aggregateRepositoryCustomerCode}\n ${ignoreAggregateRoots}\n
\n
\n \n org.springframework.boot\n spring-boot-maven-plugin\n 2.6.3\n \n \n \n \n repackage\n \n repackage\n \n \n ${basePackage}.StartApplication\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 1.8\n 1.8\n UTF-8\n \n \n \n org.apache.maven.plugins\n maven-archetype-plugin\n 3.2.0\n \n\n
\n
\n
" + "data": "\n 4.0.0\n\n ${groupId}\n ${artifactId}\n ${version}\n jar\n\n ddd-sample\n\n \n UTF-8\n UTF-8\n 1.8\n 1.0.0-alpha-2\n\n 1.18.16\n\n 31.1-jre\n 3.12.0\n 4.4\n 1.2.79\n\n 5.1.46\n 1.2.8\n\n 2.7.6\n 2021.0.5\n \n \n\n 1.6.14\n 3.0.3\n 2.2.7\n \n\n \n \n \n org.springframework.boot\n spring-boot-dependencies\n ${spring-boot.version}\n pom\n import\n \n \n org.springframework.cloud\n spring-cloud-dependencies\n ${spring-cloud.version}\n pom\n import\n \n \n \n\n \n \n org.projectlombok\n lombok\n true\n ${org.projectlombok.version}\n \n\n \n io.github.netcorepal\n cap4j-ddd-starter-jpa-rocketmq\n ${cap4j.version}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n provided\n \n\n \n \n org.springframework.boot\n spring-boot-starter-web\n \n \n org.springframework.boot\n spring-boot-starter-actuator\n \n \n org.springframework.boot\n spring-boot-starter-validation\n \n\n \n \n org.apache.commons\n commons-lang3\n ${commons.lang3.version}\n \n \n org.apache.commons\n commons-collections4\n ${commons.collections4.version}\n \n \n com.google.guava\n guava\n ${guava.version}\n \n \n com.alibaba\n fastjson\n ${fastjson.version}\n \n\n \n \n com.ctrip.framework.apollo\n apollo-client\n 1.7.0\n \n \n \n com.githup.liuyanggithup\n xxl-job-core\n 1.2.1\n \n \n \n org.springframework.cloud\n spring-cloud-starter-openfeign\n \n \n io.github.openfeign\n feign-okhttp\n 11.10\n \n\n \n \n org.springframework.boot\n spring-boot-starter-data-jpa\n \n \n \n org.mybatis.spring.boot\n mybatis-spring-boot-starter\n 2.1.3\n \n \n \n com.alibaba\n druid-spring-boot-starter\n ${spring-boot-druid.version}\n \n \n \n mysql\n mysql-connector-java\n ${mysql-connector.version}\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n org.springdoc\n springdoc-openapi-ui\n ${springdoc.version}\n \n \n com.github.xiaoymin\n knife4j-springdoc-ui\n ${knife4j.version}\n \n \n io.swagger.core.v3\n swagger-annotations\n ${swagger3.version}\n \n\n \n net.logstash.logback\n logstash-logback-encoder\n 6.6\n \n \n junit\n junit\n 3.8.1\n test\n \n \n \n ${project.artifactId}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n \n ${archTemplate}\n ${archTemplateEncoding}\n ${basePackage}\n ${multiModule}\n ${moduleNameSuffix4Adapter}\n ${moduleNameSuffix4Domain}\n ${moduleNameSuffix4Application}\n \n \n \n ${user}\n ${pwd}\n ${schema}\n ${table}
\n ${ignoreTable}\n ${idField}\n ${versionField}\n ${deletedField}\n ${readonlyFields}\n ${ignoreFields}\n ${entityBaseClass}\n ${entityClassExtraImports}\n ${entityMetaInfoClassOutputMode}\n ${entityMetaInfoClassOutputPackage}\n ${fetchMode}\n ${fetchType}\n ${idGenerator}\n ${enumValueField}\n ${enumNameField}\n ${enumUnmatchedThrowException}\n ${datePackage4Java}\n ${typeRemapping}\n ${generateDefault}\n ${generateDbType}\n ${generateSchema}\n ${generateBuild}\n ${aggregateIdentityClass}\n ${aggregateRootAnnotation}\n ${aggregateRepositoryBaseClass}\n ${aggregateRepositoryCustomerCode}\n ${ignoreAggregateRoots}\n
\n
\n \n org.springframework.boot\n spring-boot-maven-plugin\n 2.6.3\n \n \n \n \n repackage\n \n repackage\n \n \n ${basePackage}.StartApplication\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 1.8\n 1.8\n UTF-8\n \n \n \n org.apache.maven.plugins\n maven-archetype-plugin\n 3.2.0\n \n\n
\n
\n
" } ] } \ No newline at end of file diff --git a/cap4j-ddd-starter-jpa-rocketmq/pom.xml b/cap4j-ddd-starter-jpa-rocketmq/pom.xml index c66d885..ba82d6e 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/pom.xml +++ b/cap4j-ddd-starter-jpa-rocketmq/pom.xml @@ -4,7 +4,7 @@ io.github.netcorepal cap4j - 1.0.0-alpha-1 + 1.0.0-alpha-2 ../pom.xml @@ -72,7 +72,7 @@ io.github.netcorepal ddd-distributed-idgenerator-snowflake - 1.0.0-alpha-1 + 1.0.0-alpha-2 compile diff --git a/ddd-core/pom.xml b/ddd-core/pom.xml index b9e2de4..fd131ca 100644 --- a/ddd-core/pom.xml +++ b/ddd-core/pom.xml @@ -4,7 +4,7 @@ io.github.netcorepal cap4j - 1.0.0-alpha-1 + 1.0.0-alpha-2 ddd-core diff --git a/ddd-distributed-idgenerator-snowflake/pom.xml b/ddd-distributed-idgenerator-snowflake/pom.xml index 5ba532b..a2c3d51 100644 --- a/ddd-distributed-idgenerator-snowflake/pom.xml +++ b/ddd-distributed-idgenerator-snowflake/pom.xml @@ -4,7 +4,7 @@ io.github.netcorepal cap4j - 1.0.0-alpha-1 + 1.0.0-alpha-2 io.github.netcorepal diff --git a/ddd-distributed-locker-jdbc/pom.xml b/ddd-distributed-locker-jdbc/pom.xml index 3982b7c..0cf4e02 100644 --- a/ddd-distributed-locker-jdbc/pom.xml +++ b/ddd-distributed-locker-jdbc/pom.xml @@ -4,7 +4,7 @@ io.github.netcorepal cap4j - 1.0.0-alpha-1 + 1.0.0-alpha-2 ddd-distributed-locker-jdbc diff --git a/ddd-domain-event-jpa/pom.xml b/ddd-domain-event-jpa/pom.xml index dc24447..62ea029 100644 --- a/ddd-domain-event-jpa/pom.xml +++ b/ddd-domain-event-jpa/pom.xml @@ -4,7 +4,7 @@ io.github.netcorepal cap4j - 1.0.0-alpha-1 + 1.0.0-alpha-2 ddd-domain-event-jpa diff --git a/ddd-domain-event-rocketmq/pom.xml b/ddd-domain-event-rocketmq/pom.xml index 0bfee4a..e6e7ddf 100644 --- a/ddd-domain-event-rocketmq/pom.xml +++ b/ddd-domain-event-rocketmq/pom.xml @@ -4,7 +4,7 @@ io.github.netcorepal cap4j - 1.0.0-alpha-1 + 1.0.0-alpha-2 ddd-domain-event-rocketmq diff --git a/ddd-domain-repo-jpa/pom.xml b/ddd-domain-repo-jpa/pom.xml index 3afdf67..b0233cc 100644 --- a/ddd-domain-repo-jpa/pom.xml +++ b/ddd-domain-repo-jpa/pom.xml @@ -4,7 +4,7 @@ io.github.netcorepal cap4j - 1.0.0-alpha-1 + 1.0.0-alpha-2 ddd-domain-repo-jpa diff --git a/pom.xml b/pom.xml index c8c9d03..2850825 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ io.github.netcorepal cap4j - 1.0.0-alpha-1 + 1.0.0-alpha-2 pom https://github.com/netcorepal/cap4j cap4j,更方便地实现领域驱动设计 From 8bf9dbea70f050192e4dabec978b639fac72debf Mon Sep 17 00:00:00 2001 From: binking338 Date: Mon, 26 Aug 2024 13:58:35 +0800 Subject: [PATCH 10/62] =?UTF-8?q?=E9=85=8D=E7=BD=AERepositorySupervisor.ge?= =?UTF-8?q?tInstance()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java | 1 + 1 file changed, 1 insertion(+) diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java index 00df349..77491bf 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java @@ -74,6 +74,7 @@ public JpaUnitOfWork jpaUnitOfWork( eventScheduleProperties.getCompenseIntervalSeconds()); unitOfWork.init(); UnitOfWorkConfiguration.configure(unitOfWork); + RepositorySupervisorConfiguration.configure(unitOfWork); return unitOfWork; } From 3d0ab4f80f992cbdd5db33550b0819cdb4cac3db Mon Sep 17 00:00:00 2001 From: binking338 Date: Mon, 26 Aug 2024 19:25:51 +0800 Subject: [PATCH 11/62] issue:https://github.com/netcorepal/cap4j/issues/22 --- .../distributed/configure/JdbcLockerProperties.java | 10 +++++----- .../cap4j/ddd/application/distributed/JdbcLocker.java | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/JdbcLockerProperties.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/JdbcLockerProperties.java index 5368aab..8704b1c 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/JdbcLockerProperties.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/JdbcLockerProperties.java @@ -17,21 +17,21 @@ public class JdbcLockerProperties { /** * 锁表名 */ - String table = "__locker"; + String table = "`__locker`"; /** * 锁名称字段名 */ - String fieldName = "name"; + String fieldName = "`name`"; /** * 锁密码字段名 */ - String fieldPwd = "pwd"; + String fieldPwd = "`pwd`"; /** * 锁获取时间字段名 */ - String fieldLockAt = "lock_at"; + String fieldLockAt = "`lock_at`"; /** * 锁释放时间字段名 */ - String fieldUnlockAt = "unlock_at"; + String fieldUnlockAt = "`unlock_at`"; } diff --git a/ddd-distributed-locker-jdbc/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/JdbcLocker.java b/ddd-distributed-locker-jdbc/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/JdbcLocker.java index 1ec48dc..baf56cf 100644 --- a/ddd-distributed-locker-jdbc/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/JdbcLocker.java +++ b/ddd-distributed-locker-jdbc/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/JdbcLocker.java @@ -45,7 +45,7 @@ public boolean acquire(String key, String pwd, Duration expireDuration) { } } } - String sql = String.format("select count(*) from `%s` where `%s` = ? ", table, fieldName); + String sql = String.format("select count(*) from %s where %s = ? ", table, fieldName); if (showSql) { log.debug(sql); log.debug(String.format("binding parameters: [%s]", key)); @@ -53,7 +53,7 @@ public boolean acquire(String key, String pwd, Duration expireDuration) { Integer exists = jdbcTemplate.queryForObject(sql, Integer.class, key); if (exists == null || exists == 0) { try { - sql = String.format("insert into `%s`(`%s`, `%s`, `%s`, `%s`) values(?, ?, ?, ?)", table, fieldName, fieldPwd, fieldLockAt, fieldUnlockAt); + sql = String.format("insert into %s(%s, %s, %s, %s) values(?, ?, ?, ?)", table, fieldName, fieldPwd, fieldLockAt, fieldUnlockAt); if (showSql) { log.debug(sql); log.debug(String.format("binding parameters: [%s, %s, %s, %s]", key, pwd, now, now.plusSeconds(expireDuration.getSeconds()))); @@ -67,7 +67,7 @@ public boolean acquire(String key, String pwd, Duration expireDuration) { } } else { try { - sql = String.format("update `%s` set `%s` = ?, `%s` = ?, `%s` = ? where `%s` = ? and (`%s` < ? or `%s` = ?)", table, fieldPwd, fieldLockAt, fieldUnlockAt, fieldName, fieldUnlockAt, fieldPwd); + sql = String.format("update %s set %s = ?, %s = ?, %s = ? where %s = ? and (%s < ? or %s = ?)", table, fieldPwd, fieldLockAt, fieldUnlockAt, fieldName, fieldUnlockAt, fieldPwd); if (showSql) { log.debug(sql); log.debug(String.format("binding parameters: [%s, %s, %s, %s, %s, %s]", pwd, now, now.plusSeconds(expireDuration.getSeconds()), key, now, pwd)); @@ -92,7 +92,7 @@ public boolean release(String key, String pwd) { return false; } } - String sql = String.format("select count(*) from `%s` where `%s` = ? and `%s` = ?", table, fieldName, fieldPwd); + String sql = String.format("select count(*) from %s where %s = ? and %s = ?", table, fieldName, fieldPwd); if (showSql) { log.debug(sql); log.debug(String.format("binding parameters: [%s, %s]", key, pwd)); @@ -101,7 +101,7 @@ public boolean release(String key, String pwd) { if (count == null || count == 0) { return false; } - sql = String.format("update `%s` set `%s` = ? where `%s` = ? and `%s` = ? and `%s` > ?", table, fieldUnlockAt, fieldName, fieldPwd, fieldUnlockAt); + sql = String.format("update %s set %s = ? where %s = ? and %s = ? and %s > ?", table, fieldUnlockAt, fieldName, fieldPwd, fieldUnlockAt); if (showSql) { log.debug(sql); log.debug(String.format("binding parameters: [%s, %s, %s, %s]", now, key, pwd, now)); From 660b21704bf25de6e90d6b18367ea9e7d9d8a05b Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 5 Sep 2024 09:52:36 +0800 Subject: [PATCH 12/62] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=AD=E4=BB=8B?= =?UTF-8?q?=E8=80=85=E6=A8=A1=E5=BC=8F=EF=BC=8C=E6=95=B4=E7=90=86=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E5=A2=9E=E5=8A=A0=E7=BC=96=E7=A0=81=E4=BE=BF?= =?UTF-8?q?=E5=88=A9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 112 +------ cap4j-ddd-codegen-maven-plugin/pom.xml | 28 +- .../cap4j/ddd/codegen/GenArchMojo.java | 4 +- .../cap4j/ddd/codegen/GenEntityMojo.java | 46 ++- .../cap4j/ddd/codegen/GenRepositoryMojo.java | 90 ++++-- .../cap4j/ddd/codegen/MyAbstractMojo.java | 3 +- cap4j-ddd-codegen-template.json | 54 +++- .../MediatorAutoConfiguration.java | 19 +- .../JdbcLockerAutoConfiguration.java | 4 +- .../event/RocketMqEventAutoConfiguration.java | 95 ++++++ .../SnowflakeAutoConfiguration.java | 49 +-- .../event/DomainEventAutoConfiguration.java | 179 +++++++++++ .../event/RocketMqEventAutoConfiguration.java | 174 ----------- .../event/configure/EventProperties.java | 6 +- .../repo/JpaRepositoryAutoConfiguration.java | 124 +++++--- .../configure/JpaUnitOfWorkProperties.java | 4 - .../DomainServiceAutoConfiguration.java | 34 ++ .../web/ClearDomainContextInterceptor.java | 10 +- .../main/resources/META-INF/spring.factories | 10 +- ddd-core/pom.xml | 5 + .../org/netcorepal/cap4j/ddd/Mediator.java | 128 ++++++++ .../netcorepal/cap4j/ddd/MediatorSupport.java | 23 ++ .../cap4j/ddd/application/Mediator.java | 22 -- .../application/MediatorConfiguration.java | 16 - .../cap4j/ddd/application/RequestHandler.java | 4 +- .../ddd/application/RequestInterceptor.java | 24 ++ .../ddd/application/RequestSupervisor.java | 26 +- ...ion.java => RequestSupervisorSupport.java} | 6 +- .../repo => application}/UnitOfWork.java | 16 +- .../UnitOfWorkSupport.java} | 7 +- .../ddd/application/command/Command.java | 5 +- .../command/CommandNoneParamAndResult.java | 2 +- .../ddd/application/distributed/Locker.java | 2 + ...ventAttachedTransactionCommittedEvent.java | 30 ++ .../event/IntegrationEventInterceptor.java | 22 ++ .../event/IntegrationEventPublisher.java | 37 +++ .../event/IntegrationEventSupervisor.java | 43 +++ .../IntegrationEventSupervisorSupport.java | 21 ++ .../event/annotation/AutoNotify.java | 40 +++ .../event/annotation/AutoRequest.java | 33 ++ .../event/annotation/IntegrationEvent.java | 34 ++ .../DefaultIntegrationEventSupervisor.java | 105 +++++++ .../ddd/application/impl/DefaultMediator.java | 61 ---- .../impl/DefaultRequestSupervisor.java | 63 ++-- .../domain/aggregate/AggregateFactory.java | 31 ++ .../aggregate/AggregateFactorySupervisor.java | 32 ++ .../AggregateFactorySupervisorSupport.java | 15 + .../{repo => aggregate}/Specification.java | 11 +- .../SpecificationManager.java | 11 +- .../aggregate/annotation/Aggregate.java | 38 ++- .../aggregate/impl/AbstractSpecification.java | 14 + .../DefaultAggregateFactorySupervisor.java | 74 +++++ .../impl/DefaultSpecificationManager.java | 85 +++++ ...ventAttachedTransactionCommitingEvent.java | 28 ++ ...ventAttachedTransactionCommittedEvent.java | 28 ++ .../domain/event/DomainEventInterceptor.java | 28 ++ .../domain/event/DomainEventPublisher.java | 19 -- .../event/DomainEventSubscriberManager.java | 13 - .../domain/event/DomainEventSupervisor.java | 80 ++--- .../DomainEventSupervisorConfiguration.java | 31 -- .../event/DomainEventSupervisorSupport.java | 28 ++ ...ptor.java => EventMessageInterceptor.java} | 32 +- .../ddd/domain/event/EventPublisher.java | 19 ++ .../cap4j/ddd/domain/event/EventRecord.java | 32 +- .../domain/event/EventRecordRepository.java | 1 + ...ntSubscriber.java => EventSubscriber.java} | 2 +- .../domain/event/EventSubscriberManager.java | 34 ++ .../ddd/domain/event/EventSupervisor.java | 67 ---- .../domain/event/annotation/AutoAttach.java | 53 ++++ .../domain/event/annotation/DomainEvent.java | 24 +- .../event/impl/AbstractEventSubscriber.java | 20 ++ .../impl/DefaultDomainEventSupervisor.java | 172 ++++++++--- .../event/impl/DefaultEventPublisher.java | 234 ++++++++++++++ .../impl/DefaultEventSubscriberManager.java | 127 ++++++++ .../ddd/domain/repo/PersistListener.java | 21 +- .../domain/repo/PersistListenerManager.java | 8 +- .../cap4j/ddd/domain/repo/PersistType.java | 13 + .../ddd/domain/repo/RepositorySupervisor.java | 31 +- ....java => RepositorySupervisorSupport.java} | 2 +- .../repo/impl/AbstractPersistListener.java | 24 ++ .../impl/DefaultPersistListenerManager.java | 111 +++++++ .../service/DomainServiceSupervisor.java | 22 ++ .../DomainServiceSupervisorConfiguration.java | 15 + .../service/annotation/DomainService.java | 23 ++ .../impl/DefaultDomainServiceSupervisor.java | 26 ++ .../cap4j/ddd/impl/DefaultMediator.java | 154 ++++++++++ .../cap4j/ddd/share/ClassUtils.java | 36 --- .../netcorepal/cap4j/ddd/share/Constants.java | 8 + .../cap4j/ddd/share/DomainException.java | 3 +- .../cap4j/ddd/share/EventInterceptor.java | 48 +++ .../cap4j/ddd/share/misc/ClassUtils.java | 106 +++++++ .../cap4j/ddd/share/{ => misc}/ScanUtils.java | 44 ++- .../cap4j/ddd/share/{ => misc}/TextUtils.java | 2 +- .../SnowflakeIdentifierGenerator.java | 12 +- .../DefaultSnowflakeWorkerIdDispatcher.java | 8 +- .../snowflake/SnowflakeIdGenerator.java | 2 +- .../SnowflakeWorkerIdDispatcher.java | 10 +- .../ddd/domain/event/EventRecordImpl.java | 52 +++- .../event/JpaEventRecordRepository.java | 25 +- .../domain/event/JpaEventScheduleService.java | 45 +-- .../event/persistence/ArchivedEvent.java | 3 +- .../ddd/domain/event/persistence/Event.java | 20 +- ...epository.java => EventJpaRepository.java} | 2 +- .../event/RocketMqDomainEventPublisher.java | 145 --------- .../event/RocketMqDomainEventSubscriber.java | 33 -- .../RocketMqDomainEventSubscriberAdapter.java | 96 +++--- .../RocketMqDomainEventSubscriberManager.java | 69 ----- .../RocketMqIntegrationEventPublisher.java | 91 ++++++ .../impl}/JpaUnitOfWork.java | 290 +++--------------- .../repo/AbstractJpaPersistListener.java | 75 ----- .../domain/repo/AbstractJpaRepository.java | 13 +- .../domain/repo/AbstractJpaSpecification.java | 25 -- .../cap4j/ddd/domain/repo/JpaPageUtils.java | 5 +- .../repo/JpaPersistListenerManager.java | 132 -------- .../domain/repo/JpaSpecificationManager.java | 77 ----- .../impl/DefaultRepositorySupervisor.java | 143 +++++++++ 116 files changed, 3425 insertions(+), 1848 deletions(-) create mode 100644 cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java rename cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/{application => domain}/distributed/SnowflakeAutoConfiguration.java (78%) create mode 100644 cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java delete mode 100644 cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java create mode 100644 cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/Mediator.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/MediatorSupport.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorConfiguration.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestInterceptor.java rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/{RequestSupervisorConfiguration.java => RequestSupervisorSupport.java} (67%) rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/{domain/repo => application}/UnitOfWork.java (68%) rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/{domain/repo/UnitOfWorkConfiguration.java => application/UnitOfWorkSupport.java} (62%) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventAttachedTransactionCommittedEvent.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventPublisher.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisorSupport.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequest.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/IntegrationEvent.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactory.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisorSupport.java rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/{repo => aggregate}/Specification.java (80%) rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/{repo => aggregate}/SpecificationManager.java (77%) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/AbstractSpecification.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultSpecificationManager.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommitingEvent.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittedEvent.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptor.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventPublisher.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSubscriberManager.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorSupport.java rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/{DomainEventMessageInterceptor.java => EventMessageInterceptor.java} (76%) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventPublisher.java rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/{DomainEventSubscriber.java => EventSubscriber.java} (83%) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSubscriberManager.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSupervisor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/AutoAttach.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/AbstractEventSubscriber.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistType.java rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/{RepositorySupervisorConfiguration.java => RepositorySupervisorSupport.java} (86%) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/AbstractPersistListener.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisorConfiguration.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/annotation/DomainService.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/impl/DefaultDomainServiceSupervisor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ClassUtils.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/EventInterceptor.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/{ => misc}/ScanUtils.java (50%) rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/{ => misc}/TextUtils.java (93%) rename ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/{application => domain}/distributed/SnowflakeIdentifierGenerator.java (56%) rename ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/{application => domain}/distributed/snowflake/DefaultSnowflakeWorkerIdDispatcher.java (81%) rename ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/{application => domain}/distributed/snowflake/SnowflakeIdGenerator.java (98%) rename ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/{application => domain}/distributed/snowflake/SnowflakeWorkerIdDispatcher.java (65%) rename ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/{EventRepository.java => EventJpaRepository.java} (71%) delete mode 100644 ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventPublisher.java delete mode 100644 ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java delete mode 100644 ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberManager.java create mode 100644 ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqIntegrationEventPublisher.java rename ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/{domain/repo => application/impl}/JpaUnitOfWork.java (60%) delete mode 100644 ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java delete mode 100644 ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java delete mode 100644 ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java delete mode 100644 ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java create mode 100644 ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java diff --git a/README.md b/README.md index c3573ea..eeeae12 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ -domain -application - + root 123456 @@ -62,13 +62,13 @@ - + static org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisorSupport.events ref domain._share.meta SUBSELECT EAGER - org.netcorepal.cap4j.ddd.application.distributed.SnowflakeIdentifierGenerator + org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator code name true @@ -162,7 +162,7 @@ mvn cap4j-ddd-codegen:gen-arch    ├── domain (领域层接口实现)    │   ├── _share    │   │   └── configure -    │   │   └── MyDomainEventMessageInterceptor.java (集成事件消息拦截器) +    │   │   └── MyEventMessageInterceptor.java (集成事件消息拦截器)    │   └── repositories (实现聚合仓储接口)    ├── infra (基础设施适配接口实现)    │   ├── _share @@ -232,7 +232,7 @@ mvn cap4j-ddd-codegen:gen-arch │   │   │   ├── domain │   │   │   │   ├── _share │   │   │   │   │   └── configure - │   │   │   │   │   └── MyDomainEventMessageInterceptor.java + │   │   │   │   │   └── MyEventMessageInterceptor.java │   │   │   │   └── repositories │   │   │   ├── infra │   │   │   │   ├── _share @@ -400,8 +400,8 @@ public class Order { // 【字段映射开始】本段落由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 @Id - @GeneratedValue(generator = "org.netcorepal.cap4j.ddd.application.distributed.SnowflakeIdentifierGenerator") - @GenericGenerator(name = "org.netcorepal.cap4j.ddd.application.distributed.SnowflakeIdentifierGenerator", strategy = "org.netcorepal.cap4j.ddd.application.distributed.SnowflakeIdentifierGenerator") + @GeneratedValue(generator = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") + @GenericGenerator(name = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator", strategy = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") @Column(name = "`id`") Long id; @@ -493,8 +493,8 @@ public class OrderItem { // 【字段映射开始】本段落由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 @Id - @GeneratedValue(generator = "org.netcorepal.cap4j.ddd.application.distributed.SnowflakeIdentifierGenerator") - @GenericGenerator(name = "org.netcorepal.cap4j.ddd.application.distributed.SnowflakeIdentifierGenerator", strategy = "org.netcorepal.cap4j.ddd.application.distributed.SnowflakeIdentifierGenerator") + @GeneratedValue(generator = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") + @GenericGenerator(name = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator", strategy = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") @Column(name = "`id`") Long id; @@ -674,7 +674,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.netcorepal.cap4j.ddd.application.command.Command; import org.netcorepal.cap4j.ddd.domain.repo.AggregateRepository; -import org.netcorepal.cap4j.ddd.domain.repo.UnitOfWork; +import org.netcorepal.cap4j.ddd.application.UnitOfWork; import org.netcorepal.cap4j.ddd.example.domain.aggregates.Order; import org.netcorepal.cap4j.ddd.example.domain.aggregates.OrderItem; import org.springframework.stereotype.Service; @@ -746,91 +746,7 @@ public class PlaceOrderCmd { ##### 事件定义、订阅、发布 **创建发件箱表** -为了实现`Outbox`模式,cap4j需要在业务库中创建发件箱表。脚手架初始化后,项目内`resources/ddl.sql`包含完整的发件箱表建表语句 -```sql --- Create syntax for TABLE '__event' -CREATE TABLE `__event` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `event_uuid` varchar(64) NOT NULL DEFAULT '' COMMENT '事件uuid', - `svc_name` varchar(255) NOT NULL DEFAULT '' COMMENT '服务', - `event_type` varchar(255) NOT NULL DEFAULT '' COMMENT '事件类型', - `data` text COMMENT '事件数据', - `data_type` varchar(255) NOT NULL DEFAULT '' COMMENT '事件数据类型', - `exception` text COMMENT '事件发送异常', - `expire_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '过期时间', - `create_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `event_state` int(11) NOT NULL DEFAULT '0' COMMENT '分发状态', - `last_try_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上次尝试时间', - `next_try_time` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '下次尝试时间', - `tried_times` int(11) NOT NULL DEFAULT '0' COMMENT '已尝试次数', - `try_times` int(11) NOT NULL DEFAULT '0' COMMENT '尝试次数', - `version` int(11) NOT NULL DEFAULT '0', - `db_created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `db_updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id` -# , `db_created_at` - ), - KEY `idx_db_created_at` (`db_created_at`), - KEY `idx_db_updated_at` (`db_updated_at`), - KEY `idx_event_uuid` (`event_uuid`), - KEY `idx_event_type` (`event_type`,`svc_name`), - KEY `idx_create_at` (`create_at`), - KEY `idx_expire_at` (`expire_at`), - KEY `idx_next_try_time` (`next_try_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='事件发件箱 support by cap4j\n@I;' -# partition by range(to_days(db_created_at)) -# (partition p202201 values less than (to_days('2022-02-01')) ENGINE=InnoDB) -; --- Create syntax for TABLE '__achrived_event' -CREATE TABLE `__achrived_event` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `event_uuid` varchar(64) NOT NULL DEFAULT '' COMMENT '事件uuid', - `svc_name` varchar(255) NOT NULL DEFAULT '' COMMENT '服务', - `event_type` varchar(255) NOT NULL DEFAULT '' COMMENT '事件类型', - `data` text COMMENT '事件数据', - `data_type` varchar(255) NOT NULL DEFAULT '' COMMENT '事件数据类型', - `exception` text COMMENT '事件发送异常', - `expire_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '过期时间', - `create_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `event_state` int(11) NOT NULL DEFAULT '0' COMMENT '分发状态', - `last_try_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上次尝试时间', - `next_try_time` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '下次尝试时间', - `tried_times` int(11) NOT NULL DEFAULT '0' COMMENT '已尝试次数', - `try_times` int(11) NOT NULL DEFAULT '0' COMMENT '尝试次数', - `version` int(11) NOT NULL DEFAULT '0', - `db_created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `db_updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id` -# , `db_created_at` - ), - KEY `idx_db_created_at` (`db_created_at`), - KEY `idx_db_updated_at` (`db_updated_at`), - KEY `idx_event_uuid` (`event_uuid`), - KEY `idx_event_type` (`event_type`,`svc_name`), - KEY `idx_create_at` (`create_at`), - KEY `idx_expire_at` (`expire_at`), - KEY `idx_next_try_time` (`next_try_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='事件发件箱存档 support by cap4j\n@I;' -# partition by range(to_days(db_created_at)) -# (partition p202201 values less than (to_days('2022-02-01')) ENGINE=InnoDB) -; - -CREATE TABLE `__locker` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(100) NOT NULL DEFAULT '' COMMENT '锁名称', - `pwd` varchar(100) NOT NULL DEFAULT '' COMMENT '锁密码', - `lock_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT '锁获取时间', - `unlock_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT '锁释放时间', - `version` bigint(20) unsigned NOT NULL DEFAULT '0', - `db_created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `db_updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `idx_db_created_at` (`db_created_at`), - KEY `idx_db_updated_at` (`db_updated_at`), - UNIQUE `uniq_name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='锁 support by cap4j\n@I;'; - -``` +为了实现`Outbox`模式,cap4j需要在业务库中创建发件箱表。脚手架初始化后,使用项目内`resources/ddl.sql`包含的完整的发件箱表建表语句初始化数据库。 **领域事件定义** @@ -838,7 +754,7 @@ CREATE TABLE `__locker` ( 通过[`DomainEvent`](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java)注解的类,cap4j将会识别成领域事件。 后续即可通过[`DefaultDomainEventSupervisor`](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java).`instance`.`attach`方法来向当前线程上线文附加领域事件。 -一旦 [`UnitOfWork`](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWork.java).save() 顺利提交事务。则cap4j将会保障事件被提交到具体适配好的消息队列(比如当前cap4j实现的RocketMQ)中。 +一旦 [`UnitOfWork`](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java).save() 顺利提交事务。则cap4j将会保障事件被提交到具体适配好的消息队列(比如当前cap4j实现的RocketMQ)中。 ```java package org.netcorepal.cap4j.ddd.example.domain.aggregates.events; @@ -881,12 +797,12 @@ public class OrderPlacedDomainEvent { ``` > 注解属性详解 -> - `intergration()` intergration 字段非空,则事件会被识别为集成事件,意味着该事件将通过消息队列适配,通知到分布式系统中的其他服务进程。 +> - `integration()` integration 字段非空,则事件会被识别为集成事件,意味着该事件将通过消息队列适配,通知到分布式系统中的其他服务进程。 > - `subscriber()` 集成事件订阅场景,必须定义该字段,通常该字段的值将会被适配的消息队列应用到消费分组配置中。 > - `persist()` 控制事件发布记录持久化。集成事件发布场景,该字段无意义。非集成事件发布场景(仅在本服务进程内部有订阅需求),可以通过`persist=true`控制事件进入发件箱表,并脱离事件发布上下文事务中。以避免订阅逻辑异常影响发布事务的完成。 > > 应用场景例子说明 -> - `基于MQ发送方` @DomainEvent(intergration="event-name-used-for-mq-topic") +> - `基于MQ发送方` @DomainEvent(integration="event-name-used-for-mq-topic") > - `基于MQ订阅方` @DomainEvent(subscriber="subscriber-name-used-for-mq-consumer-group") > - `消费方与订阅方事务隔离` @DomainEvent(persist=true) > - `消费方与订阅方同一事务` @DomainEvent diff --git a/cap4j-ddd-codegen-maven-plugin/pom.xml b/cap4j-ddd-codegen-maven-plugin/pom.xml index 78e2e69..14cb0e6 100644 --- a/cap4j-ddd-codegen-maven-plugin/pom.xml +++ b/cap4j-ddd-codegen-maven-plugin/pom.xml @@ -10,7 +10,6 @@ ../pom.xml - io.github.netcorepal cap4j-ddd-codegen-maven-plugin 1.0.0-alpha-2 @@ -24,11 +23,18 @@ lombok 1.18.16 + - org.apache.maven - maven-core - 3.8.1 + mysql + mysql-connector-java + 5.1.46 + + com.alibaba + fastjson + 1.2.79 + + org.apache.maven.plugin-tools @@ -36,16 +42,16 @@ 3.8.1 provided - - mysql - mysql-connector-java - 5.1.46 + org.apache.maven.plugins + maven-plugin-plugin + 3.8.1 + provided - com.alibaba - fastjson - 1.2.79 + org.apache.maven + maven-core + 3.8.1 diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index 4df54e7..60820c8 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -281,9 +281,9 @@ private String stringfyEntityClassImportPackages() { } String result = "\n"; for (String entityClassExtraImport : entityClassExtraImports) { - result += " " + entityClassExtraImport + "\n"; + result += " " + entityClassExtraImport + "\n"; } - result += " \n"; + result += " "; return result; } } diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index a860ae1..1d55288 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -1,13 +1,13 @@ package org.netcorepal.cap4j.ddd.codegen; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; import org.netcorepal.cap4j.ddd.codegen.misc.Inflector; import org.netcorepal.cap4j.ddd.codegen.misc.MysqlSchemaUtils; import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Mojo; import java.io.*; import java.nio.charset.Charset; @@ -32,6 +32,7 @@ public class GenEntityMojo extends MyAbstractMojo { private Map>> ColumnsMap = new HashMap<>(); private Map> EnumConfigMap = new HashMap<>(); private Map EnumPackageMap = new HashMap<>(); + private Map EnumTableNameMap = new HashMap<>(); @Override public void execute() throws MojoExecutionException, MojoFailureException { @@ -170,6 +171,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { if (enumConfig.size() > 0) { EnumConfigMap.put(MysqlSchemaUtils.getType(column), enumConfig); EnumPackageMap.put(MysqlSchemaUtils.getType(column), basePackage + "." + getEntityPackage(MysqlSchemaUtils.getTableName(table)) + ".enums"); + EnumTableNameMap.put(MysqlSchemaUtils.getType(column), MysqlSchemaUtils.getTableName(table)); } } } @@ -183,7 +185,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info(""); for (Map.Entry> entry : EnumConfigMap.entrySet()) { try { - writeEnumSourceFile(entry.getValue(), entry.getKey(), EnumPackageMap.get(entry.getKey()), domainModulePath, enumValueField, enumNameField); + writeEnumSourceFile(entry.getValue(), entry.getKey(), EnumPackageMap.get(entry.getKey()), getAggregate(EnumTableNameMap.get(entry.getKey())), domainModulePath, enumValueField, enumNameField); } catch (IOException e) { e.printStackTrace(); getLog().error(e); @@ -291,13 +293,20 @@ public String getModule(String tableName) { public String getAggregate(String tableName) { Map table = TableMap.get(tableName); String aggregate = MysqlSchemaUtils.getAggregate(table); - getLog().info("尝试解析聚合:" + MysqlSchemaUtils.getTableName(table) + " " + aggregate); + getLog().info("尝试解析聚合:" + MysqlSchemaUtils.getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); while (!MysqlSchemaUtils.isAggregateRoot(table) && StringUtils.isBlank(aggregate)) { - table = TableMap.get(MysqlSchemaUtils.getParent(table)); + String parent = MysqlSchemaUtils.getParent(table); + if (StringUtils.isBlank(parent)) { + break; + } + table = TableMap.get(parent); aggregate = MysqlSchemaUtils.getAggregate(table); - getLog().info("尝试父表聚合:" + MysqlSchemaUtils.getTableName(table) + " " + aggregate); + getLog().info("尝试父表聚合:" + MysqlSchemaUtils.getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); } - getLog().info("聚合解析结果:" + tableName + " " + aggregate); + if (StringUtils.isBlank(aggregate)) { + aggregate = getEntityJavaType(MysqlSchemaUtils.getTableName(table)); + } + getLog().info("聚合解析结果:" + tableName + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); return aggregate; } @@ -337,7 +346,7 @@ public String getEntityPackage(String tableName) { String aggregate = getAggregate(tableName); String packageName = ("domain.aggregates" + (StringUtils.isNotBlank(module) ? "." + module : "") - + (StringUtils.isNotBlank(aggregate) ? "." + aggregate : "") + + (StringUtils.isNotBlank(aggregate) ? "." + aggregate.toLowerCase() : "") ); return packageName; } @@ -729,10 +738,13 @@ public void processImportLines(Map table, String basePackage, Li public void processAnnotationLines(Map table, List> columns, List annotationLines) { String tableName = MysqlSchemaUtils.getTableName(table); - String simpleClassName = getEntityJavaType(tableName); boolean annotationEmpty = annotationLines.size() == 0; SourceFileUtils.removeText(annotationLines, "@Aggregate\\(.*\\)"); - SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(entity = \"" + simpleClassName + "\", root = " + (MysqlSchemaUtils.isAggregateRoot(table) ? "true" : "false") + ", aggregate = \"" + simpleClassName + "\", description = \"" + MysqlSchemaUtils.getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); + if (MysqlSchemaUtils.isAggregateRoot(table)) { + SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(aggregate = \"" + getAggregate(tableName) + "\", name = \"" + getEntityJavaType(tableName) + "\", type = \"root\", description = \"" + MysqlSchemaUtils.getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); + } else { + SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(aggregate = \"" + getAggregate(tableName) + "\", name = \"" + getEntityJavaType(tableName) + "\", type = \"entity\", relevant = { \"" + getEntityJavaType(MysqlSchemaUtils.getParent(table)) + "\" }, description = \"" + MysqlSchemaUtils.getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); + } if (StringUtils.isNotBlank(getAggregateRootAnnotation())) { if (MysqlSchemaUtils.isAggregateRoot(table)) { SourceFileUtils.addIfNone(annotationLines, getAggregateRootAnnotation() + "(\\(.*\\))?", getAggregateRootAnnotation()); @@ -807,6 +819,12 @@ public void writeEntitySourceFile(Map table, List enums = new ArrayList<>(); @@ -1072,7 +1090,7 @@ public void writeColumnComment(BufferedWriter out, Map column) { } writeLine(out, " * " + comment); if (MysqlSchemaUtils.hasEnum(column)) { - getLog().info("获取枚举java类型:"+columnName + " -> " + columnJavaType); + getLog().info("获取枚举java类型:" + columnName + " -> " + columnJavaType); Map enumMap = EnumConfigMap.get(columnJavaType); if (enumMap == null) { enumMap = EnumConfigMap.get(MysqlSchemaUtils.getType(column)); @@ -1213,7 +1231,7 @@ public void writeRelationProperty(BufferedWriter out, Map table, } } - public void writeEnumSourceFile(Map enumConfigs, String enumType, String enumPackage, String baseDir, String enumValueField, String enumNameField) throws IOException { + public void writeEnumSourceFile(Map enumConfigs, String enumType, String enumPackage, String belongAggregate, String baseDir, String enumValueField, String enumNameField) throws IOException { new File(SourceFileUtils.resolveDirectory(baseDir, enumPackage)).mkdirs(); String filePath = SourceFileUtils.resolveSourceFile(baseDir, enumPackage, enumType); @@ -1223,6 +1241,7 @@ public void writeEnumSourceFile(Map enumConfigs, String enumT writeLine(out, "package " + enumPackage + ";"); writeLine(out, ""); writeLine(out, "import lombok.Getter;"); + writeLine(out, "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;"); writeLine(out, ""); writeLine(out, "import javax.persistence.*;"); writeLine(out, "import java.util.HashMap;"); @@ -1232,6 +1251,7 @@ public void writeEnumSourceFile(Map enumConfigs, String enumT writeLine(out, " * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); writeLine(out, " * 警告:请勿手工修改该文件,重新生成会覆盖该文件"); writeLine(out, " */"); + writeLine(out, "@Aggregate(aggregate = \"" + belongAggregate + "\", name = \"" + enumType + "\", type = \"enum\", description = \"\")"); writeLine(out, "public enum " + enumType + " {"); writeLine(out, ""); for (Map.Entry entry : enumConfigs.entrySet()) { diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java index cd744c8..fe37a20 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java @@ -1,15 +1,20 @@ package org.netcorepal.cap4j.ddd.codegen; -import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; +import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; -import java.io.*; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.nio.charset.Charset; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils.writeLine; @@ -23,6 +28,8 @@ @Mojo(name = "gen-repository") public class GenRepositoryMojo extends MyAbstractMojo { + Map AggregateRoot2AggregateNameMap = new HashMap<>(); + @Override public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("当前默认编码:" + Charset.defaultCharset().name()); @@ -72,34 +79,35 @@ public void execute() throws MojoExecutionException, MojoFailureException { files = files.stream() .filter(file -> "java".equalsIgnoreCase(FileUtils.extension(file.getName()))) .collect(Collectors.toList()); - files.forEach(file->{ + files.forEach(file -> { this.getLog().debug("发现Java文件: " + SourceFileUtils.resolveClassName(file.getAbsolutePath())); }); getLog().info("发现java文件数量:" + files.size()); files.forEach(file -> { - this.getLog().debug("解析Java文件: " + SourceFileUtils.resolveClassName(file.getAbsolutePath())); - String content = ""; - try { - content = FileUtils.fileRead(file); - } catch (IOException e) { - e.printStackTrace(); - } - boolean isAggregateRoot = isAggregateRoot(content); - if (isAggregateRoot) { - this.getLog().info("发现聚合根: " + SourceFileUtils.resolveClassName(file.getAbsolutePath())); + String className = SourceFileUtils.resolveClassName(file.getAbsolutePath()); + this.getLog().debug("解析Java文件: " + className); + String content = ""; + try { + content = FileUtils.fileRead(file); + } catch (IOException e) { + e.printStackTrace(); + } + boolean isAggregateRoot = isAggregateRoot(content, className); + if (isAggregateRoot) { + this.getLog().info("发现聚合根: " + className); - String simpleClassName = SourceFileUtils.resolveSimpleClassName(file.getAbsolutePath()); - if (Arrays.stream(ignoreAggregateRoots.split("[\\,\\;]")) - .anyMatch(i -> i.equalsIgnoreCase(simpleClassName))) { - return; - } - try { - writeAggregateRepositorySourceFile(file.getAbsolutePath(), basePackage, adapterModulePath); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); + String simpleClassName = SourceFileUtils.resolveSimpleClassName(file.getAbsolutePath()); + if (Arrays.stream(ignoreAggregateRoots.split("[\\,\\;]")) + .anyMatch(i -> i.equalsIgnoreCase(simpleClassName))) { + return; + } + try { + writeAggregateRepositorySourceFile(file.getAbsolutePath(), basePackage, adapterModulePath); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); } catch (Exception e) { this.getLog().error("发生异常,", e); } @@ -107,7 +115,9 @@ public void execute() throws MojoExecutionException, MojoFailureException { this.getLog().info("结束生成仓储代码"); } - private boolean isAggregateRoot(String content) { + Pattern AGGREGATE_PATTERN = Pattern.compile("\\((aggregate\\s*=\\s*){1}\\\"(){1}\\\"\\)"); + + private boolean isAggregateRoot(String content, String className) { return Arrays.stream(content.split("(\r)|(\n)|(\r\n)")) .filter(line -> line.trim().startsWith("@") || line.replace("\\s", "").equals("/*@AggregateRoot*/")) .anyMatch(line -> { @@ -121,11 +131,17 @@ private boolean isAggregateRoot(String content) { boolean isAggregateRootAnnotationFullName = line.matches(getAggregateRootAnnotation() + "(\\(.*\\))?"); hasAggregateRoot = (isAggregateRootAnnotationFullName || isAggregateRootAnnotation); } - if(hasAggregateRoot){ + if (hasAggregateRoot) { return hasAggregateRoot; } - boolean hasAggregate = line.matches("@Aggregate\\s*\\(.*root\\s*=\\s*true.*\\)"); + boolean hasAggregate = line.matches("@Aggregate\\s*\\(.*type\\s*=\\s*\\\"root\\\".*\\)"); + if (hasAggregate) { + Matcher matcher = AGGREGATE_PATTERN.matcher(line); + if (matcher.find() && matcher.groupCount() > 1) { + AggregateRoot2AggregateNameMap.put(className, matcher.group(2)); + } + } boolean aggregateRoot = hasAggregate; getLog().debug("annotationline: " + line); @@ -214,6 +230,8 @@ public void writeAggregateRepositorySourceFile(String entitySourceFilePath, Stri String packageName = basePackage + ".adapter.domain.repositories"; String simpleClassName = SourceFileUtils.resolveSimpleClassName(entitySourceFilePath); + String className = SourceFileUtils.resolveClassName(entitySourceFilePath); + String aggregate = AggregateRoot2AggregateNameMap.getOrDefault(className, simpleClassName); new File(SourceFileUtils.resolveDirectory(baseDir, packageName)).mkdirs(); String filePath = SourceFileUtils.resolveSourceFile(baseDir, packageName, simpleClassName + "Repository"); @@ -231,7 +249,6 @@ public void writeAggregateRepositorySourceFile(String entitySourceFilePath, Stri // importLines.removeIf(l -> l.contains(".convention.AggregateRepository;")); importLines.removeIf(l -> l.contains("." + simpleClassName + ";")); - String className = SourceFileUtils.resolveClassName(entitySourceFilePath); BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); writeLine(out, "package " + packageName + ";"); writeLine(out, ""); @@ -248,7 +265,7 @@ public void writeAggregateRepositorySourceFile(String entitySourceFilePath, Stri writeLine(out, " * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); writeLine(out, " */"); } - writeLine(out, "public interface " + simpleClassName + "Repository extends " + getAggregateRepositoryBaseClass().replace("${EntityType}", simpleClassName).replace("${IdentityType}", aggregateIdentityClass) + " {"); + writeLine(out, "public interface " + simpleClassName + "Repository extends " + replacePlaceholder(getAggregateRepositoryBaseClass(), simpleClassName, aggregateIdentityClass, aggregate) + " {"); if (customerLines.size() > 0) { for (String line : customerLines) { writeLine(out, line); @@ -256,7 +273,7 @@ public void writeAggregateRepositorySourceFile(String entitySourceFilePath, Stri } else { writeLine(out, " // 【自定义代码开始】本段落之外代码由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动"); writeLine(out, ""); - writeLine(out, " " + getAggregateRepositoryCustomerCode().replace("${EntityType}", simpleClassName).replace("${IdentityType}", aggregateIdentityClass)); + writeLine(out, " " + replacePlaceholder(getAggregateRepositoryCustomerCode(), simpleClassName, aggregateIdentityClass, aggregate)); writeLine(out, ""); writeLine(out, " // 【自定义代码结束】本段落之外代码由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动"); @@ -264,4 +281,15 @@ public void writeAggregateRepositorySourceFile(String entitySourceFilePath, Stri writeLine(out, "}"); out.close(); } + + private String replacePlaceholder( + String template, + String entityType, + String identityType, + String aggregate + ) { + return template.replace("${EntityType}", entityType) + .replace("${IdentityType}", identityType) + .replace("${Aggregate}", aggregate); + } } diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java index 85a9f11..13940ed 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java @@ -342,10 +342,11 @@ public String getAggregateRepositoryBaseClass() { @Parameter(property = "aggregateRepositoryCustomerCode", defaultValue = "") String aggregateRepositoryCustomerCode = ""; - public String getAggregateRepositoryCustomerCode(){ + public String getAggregateRepositoryCustomerCode() { if (StringUtils.isBlank(aggregateRepositoryCustomerCode)) { aggregateRepositoryCustomerCode = "@org.springframework.stereotype.Component\n" + + " @org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate(aggregate = \"${Aggregate}\", name = \"${EntityType}\", type = \"repository\", description = \"\")\n" + " public static class ${EntityType}JpaRepositoryAdapter extends org.netcorepal.cap4j.ddd.domain.repo.AbstractJpaRepository<${EntityType}, ${IdentityType}>\n" + " {\n" + " public ${EntityType}JpaRepositoryAdapter(org.springframework.data.jpa.repository.JpaSpecificationExecutor<${EntityType}> jpaSpecificationExecutor, org.springframework.data.jpa.repository.JpaRepository<${EntityType}, ${IdentityType}> jpaRepository) {\n" + diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index 1c6aee3..997836c 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -87,7 +87,21 @@ "children": [ { "type": "dir", - "name": "_share" + "name": "_share", + "children": [ + { + "type": "dir", + "name": "configure", + "children": [ + { + "type": "file", + "name": "MyIntegrationEventInterceptor.java", + "format": "raw", + "data": "package ${basePackage}.adapter.application._share.configure;\n\nimport org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor;\nimport org.netcorepal.cap4j.ddd.domain.event.EventRecord;\nimport org.springframework.stereotype.Service;\n\nimport java.time.LocalDateTime;\n\n/**\n * 集成事件拦截器\n *\n * @author binking338\n * @date 2024/9/4\n */\n@Service\npublic class MyIntegrationEventInterceptor implements IntegrationEventInterceptor {\n @Override\n public void onNotify(Object eventPayload, LocalDateTime schedule) {\n\n }\n\n @Override\n public void prePersist(EventRecord event) {\n\n }\n\n @Override\n public void postPersist(EventRecord event) {\n\n }\n\n @Override\n public void preRelease(EventRecord event) {\n\n }\n\n @Override\n public void postRelease(EventRecord event) {\n\n }\n\n @Override\n public void onException(Throwable throwable, EventRecord event) {\n\n }\n}\n" + } + ] + } + ] }, { "type": "dir", @@ -109,9 +123,15 @@ "children": [ { "type": "file", - "name": "MyDomainEventMessageInterceptor.java", + "name": "MyDomainEventInterceptor.java", + "format": "raw", + "data": "package ${basePackage}.adapter.domain._share.configure;\n\nimport org.netcorepal.cap4j.ddd.domain.event.DomainEventInterceptor;\nimport org.netcorepal.cap4j.ddd.domain.event.EventRecord;\nimport org.springframework.stereotype.Service;\n\nimport java.time.LocalDateTime;\n\n/**\n * 领域事件拦截器\n *\n * @author binking338\n * @date 2024/9/4\n */\n@Service\npublic class MyDomainEventInterceptor implements DomainEventInterceptor {\n @Override\n public void onAttach(Object eventPayload, Object entity, LocalDateTime schedule) {\n\n }\n\n @Override\n public void onDetach(Object eventPayload, Object entity) {\n\n }\n\n @Override\n public void prePersist(EventRecord event) {\n\n }\n\n @Override\n public void postPersist(EventRecord event) {\n\n }\n\n @Override\n public void preRelease(EventRecord event) {\n\n }\n\n @Override\n public void postRelease(EventRecord event) {\n\n }\n\n @Override\n public void onException(Throwable throwable, EventRecord event) {\n\n }\n}\n" + }, + { + "type": "file", + "name": "MyEventMessageInterceptor.java", "format": "raw", - "data": "package ${basePackage}.adapter.domain._share.configure;\n\nimport org.netcorepal.cap4j.ddd.domain.event.DomainEventMessageInterceptor;\nimport org.springframework.messaging.Message;\nimport org.springframework.stereotype.Service;\n\n/**\n * 领域事件消息拦截器\n *\n * @author binking338\n */\n@Service\npublic class MyDomainEventMessageInterceptor implements DomainEventMessageInterceptor {\n @Override\n public Message beforePublish(Message message) {\n return message;\n }\n\n @Override\n public Message beforeSubscribe(Message message) {\n return message;\n }\n}\n" + "data": "package ${basePackage}.adapter.domain._share.configure;\n\nimport org.netcorepal.cap4j.ddd.domain.event.EventMessageInterceptor;\nimport org.springframework.messaging.Message;\nimport org.springframework.stereotype.Service;\n\n/**\n * 领域事件消息拦截器\n *\n * @author binking338\n */\n@Service\npublic class MyEventMessageInterceptor implements EventMessageInterceptor {\n\n @Override\n public void initPublish(Message message) {\n\n }\n\n @Override\n public void prePublish(Message message) {\n\n }\n\n @Override\n public void postPublish(Message message) {\n\n }\n\n @Override\n public void preSubscribe(Message message) {\n\n }\n\n @Override\n public void postSubscribe(Message message) {\n\n }\n}\n" } ] } @@ -265,24 +285,34 @@ "type": "dir", "name": "_share", "children": [ - { - "type": "dir", - "name": "clients" - }, { "type": "dir", "name": "enums" - }, - { - "type": "dir", - "name": "events" } ] + },, + { + "type": "dir", + "name": "clients" }, { "type": "dir", "name": "commands" }, + { + "type": "dir", + "name": "distributed", + "children": [ + { + "type": "dir", + "name": "events" + }, + { + "type": "dir", + "name": "sagas" + } + ] + }, { "type": "dir", "name": "queries" @@ -333,7 +363,7 @@ "type": "file", "name": "application.properties", "format": "raw", - "data": "spring.application.name=${artifactId}\n\nlogging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE\nlogging.level.java.sql.PreparedStatement=DEBUG\nlogging.level.org.hibernate.engine.jdbc.batch.internal.BatchingBatch=OFF\n\ncap4j.ddd.distributed.idgenerator.snowflake.enable=true\n\ncap4j.ddd.domain.event.subscriber-scan-package=${basePackage}\ncap4j.ddd.domain.event.schedule.add-partition-enable=false\n\n# server\nserver.port=8081\nserver.tomcat.uri-encoding=UTF-8\nserver.tomcat.max-threads=400\nserver.servlet.context-path=/${artifactId}\nspring.servlet.multipart.max-request-size=50MB\nspring.servlet.multipart.max-file-size = 50MB\nmanagement.endpoint.shutdown.enabled=true\nmanagement.endpoints.web.exposure.include=health,info,prometheus,metrics,shutdown\nmanagement.metrics.tags.application = ${spring.application.name}\nmanagement.health.redis.enabled=false\nspring.jackson.date-format=yyyy-MM-dd HH:mm:ss\nspring.jackson.time-zone=GMT+8\nspring.jackson.default-property-inclusion=non_null\nspring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false\nspring.mvc.log-request-details=true\n\n# api doc\n#springdoc.api-docs.path=/v3/api-docs\nspringdoc.swagger-ui.path=/swagger-ui.html\nspringdoc.swagger-ui.disable-swagger-default-url=true\n\n# feign\nfeign.okhttp.enabled = true\nfeign.hystrix.enabled = true\nhystrix.shareSecurityContext = true\nhystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 3000\nhystrix.command.default.circuitBreaker.requestVolumeThreshold = 200\nhystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests = 50\nhystrix.threadpool.default.coreSize = 50\nribbon.okhttp.enabled = true\nribbon.ServerListRefreshInterval = 3000\nribbon.ConnectTimeout = 2000\nribbon.ReadTimeout = 20000\nribbon.MaxAutoRetries = 0\nribbon.MaxAutoRetriesNextServer = 0\nribbon.OkToRetryOnAllOperations = false\n\n# mysql\nspring.datasource.url=${connectionString}\nspring.datasource.type=com.alibaba.druid.pool.DruidDataSource\nspring.datasource.username=${user}\nspring.datasource.password=${pwd}\nspring.datasource.driver-class-name=com.mysql.jdbc.Driver\nspring.datasource.druid.initial-size=30\nspring.datasource.druid.max-active=100\nspring.datasource.druid.min-idle=30\nspring.datasource.druid.max-wait=1000\nspring.datasource.druid.use-unfair-lock=true\nspring.datasource.druid.pool-prepared-statements = false\nspring.datasource.druid.validation-query = select 1\nspring.datasource.druid.validation-query-timeout = 2\nspring.datasource.druid.keep-alive=true\nspring.datasource.druid.test-on-borraw = false\nspring.datasource.druid.test-on-return = false\nspring.datasource.druid.test-while-idle = true\nspring.datasource.druid.min-evictable-idle-time-millis = 43200000\nspring.datasource.druid.max-evictable-idle-time-millis = 86400000\n#spring.datasource.druid.filters=stat,wall\n#spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.showSqlMillis=500\n#spring.datasource.druid.web-stat-filter.enabled=true\n#spring.datasource.druid.web-stat-filter.url-pattern=/*\n#spring.datasource.druid.stat-view-servlet.enabled=true\n#spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*\n#spring.datasource.druid.stat-view-servlet.reset-enable=false\n#spring.datasource.druid.stat-view-servlet.login-username=admin\n#spring.datasource.druid.stat-view-servlet.login-password=123\n\n# jpa\nspring.jpa.open-in-view=false\nspring.jpa.hibernate.ddl-auto=none\nspring.jpa.show-sql=true\nspring.jpa.properties.hibernate.dialect.storage_engine=innodb\nspring.jpa.properties.hibernate.jdbc.batch_size=5000\nspring.jpa.properties.hibernate.jdbc.batch_versioned_data=true\nspring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect\n\n# mybatis\nmybatis.mapper-locations=classpath:mapper/*.xml\nmybatis.configuration.map-underscore-to-camel-case=true\nmybatis.configuration.default-enum-type-handler=${basePackage}.adapter.infra.mybatis._share.MyEnumTypeHandler\n\n# rocketmq\nrocketmq.name-server=localhost:9876\nrocketmq.producer.group=${spring.application.name}\n\n## redis\n#spring.redis.host = localhost\n#spring.redis.port = 6379\n#spring.redis.password =\n#spring.redis.database = 0\n#spring.redis.jedis.pool.max-active = 50\n#spring.redis.jedis.pool.max-idle = 10\n#spring.redis.jedis.pool.max-wait = -1ms\n#spring.redis.jedis.pool.min-idle = 5\n#spring.redis.timeout = 5000ms\n#spring.cache.redis.key-prefix = ${spring.application.name}:local\n\n# elasticsearch\n#management.health.elasticsearch.enabled=false\n#spring.elasticsearch.uris = http://localhost:9200\n#spring.elasticsearch.username =\n#spring.elasticsearch.password =" + "data": "spring.application.name=${artifactId}\n\nlogging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE\nlogging.level.java.sql.PreparedStatement=DEBUG\nlogging.level.org.hibernate.engine.jdbc.batch.internal.BatchingBatch=OFF\n\ncap4j.ddd.distributed.idgenerator.snowflake.enable=true\ncap4j.ddd.domain.event.event-scan-package=${basePackage}\ncap4j.ddd.domain.event.schedule.add-partition-enable=false\n\n# server\nserver.port=8081\nserver.tomcat.uri-encoding=UTF-8\nserver.tomcat.threads.max=400\nserver.servlet.context-path=/${artifactId}\nspring.servlet.multipart.max-request-size=50MB\nspring.servlet.multipart.max-file-size = 50MB\nmanagement.endpoint.shutdown.enabled=true\nmanagement.endpoints.web.exposure.include=health,info,prometheus,metrics,shutdown\nmanagement.metrics.tags.application = ${spring.application.name}\nmanagement.health.redis.enabled=false\nspring.jackson.date-format=yyyy-MM-dd HH:mm:ss\nspring.jackson.time-zone=GMT+8\nspring.jackson.default-property-inclusion=non_null\nspring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false\nspring.mvc.log-request-details=true\n\n# api doc\n#springdoc.api-docs.path=/v3/api-docs\nspringdoc.swagger-ui.path=/swagger-ui.html\nspringdoc.swagger-ui.disable-swagger-default-url=true\n\n# feign\nfeign.okhttp.enabled = true\n#feign.hystrix.enabled = true\n#hystrix.shareSecurityContext = true\n#hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 3000\n#hystrix.command.default.circuitBreaker.requestVolumeThreshold = 200\n#hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests = 50\n#hystrix.threadpool.default.coreSize = 50\n#ribbon.okhttp.enabled = true\n#ribbon.ServerListRefreshInterval = 3000\n#ribbon.ConnectTimeout = 2000\n#ribbon.ReadTimeout = 20000\n#ribbon.MaxAutoRetries = 0\n#ribbon.MaxAutoRetriesNextServer = 0\n#ribbon.OkToRetryOnAllOperations = false\n\n# mysql\nspring.datasource.url=${connectionString}\nspring.datasource.type=com.alibaba.druid.pool.DruidDataSource\nspring.datasource.username=${user}\nspring.datasource.password=${pwd}\nspring.datasource.driver-class-name=com.mysql.jdbc.Driver\nspring.datasource.druid.initial-size=30\nspring.datasource.druid.max-active=100\nspring.datasource.druid.min-idle=30\nspring.datasource.druid.max-wait=1000\nspring.datasource.druid.use-unfair-lock=true\nspring.datasource.druid.pool-prepared-statements = false\nspring.datasource.druid.validation-query = select 1\nspring.datasource.druid.validation-query-timeout = 2\nspring.datasource.druid.keep-alive=true\nspring.datasource.druid.test-on-borrow = false\nspring.datasource.druid.test-on-return = false\nspring.datasource.druid.test-while-idle = true\nspring.datasource.druid.min-evictable-idle-time-millis = 43200000\nspring.datasource.druid.max-evictable-idle-time-millis = 86400000\n#spring.datasource.druid.filters=stat,wall\n#spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.showSqlMillis=500\n#spring.datasource.druid.web-stat-filter.enabled=true\n#spring.datasource.druid.web-stat-filter.url-pattern=/*\n#spring.datasource.druid.stat-view-servlet.enabled=true\n#spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*\n#spring.datasource.druid.stat-view-servlet.reset-enable=false\n#spring.datasource.druid.stat-view-servlet.login-username=admin\n#spring.datasource.druid.stat-view-servlet.login-password=123\n\n# jpa\nspring.jpa.open-in-view=false\nspring.jpa.hibernate.ddl-auto=none\nspring.jpa.show-sql=true\nspring.jpa.properties.hibernate.dialect.storage_engine=innodb\nspring.jpa.properties.hibernate.jdbc.batch_size=5000\nspring.jpa.properties.hibernate.jdbc.batch_versioned_data=true\nspring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect\n\n# mybatis\nmybatis.mapper-locations=classpath:mapper/*.xml\nmybatis.configuration.map-underscore-to-camel-case=true\nmybatis.configuration.default-enum-type-handler=${basePackage}.adapter.infra.mybatis._share.MyEnumTypeHandler\n\n# rocketmq\nrocketmq.name-server=localhost:9876\nrocketmq.producer.group=${spring.application.name}\n\n## redis\n#spring.redis.host = localhost\n#spring.redis.port = 6379\n#spring.redis.password =\n#spring.redis.database = 0\n#spring.redis.jedis.pool.max-active = 50\n#spring.redis.jedis.pool.max-idle = 10\n#spring.redis.jedis.pool.max-wait = -1ms\n#spring.redis.jedis.pool.min-idle = 5\n#spring.redis.timeout = 5000ms\n#spring.cache.redis.key-prefix = ${spring.application.name}:local\n\n# elasticsearch\n#management.health.elasticsearch.enabled=false\n#spring.elasticsearch.uris = http://localhost:9200\n#spring.elasticsearch.username =\n#spring.elasticsearch.password =" }, { "type": "file", diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java index e11a281..6481619 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java @@ -1,10 +1,14 @@ package org.netcorepal.cap4j.ddd.application; import lombok.RequiredArgsConstructor; -import org.netcorepal.cap4j.ddd.application.impl.DefaultMediator; +import org.netcorepal.cap4j.ddd.Mediator; +import org.netcorepal.cap4j.ddd.MediatorSupport; +import org.netcorepal.cap4j.ddd.impl.DefaultMediator; import org.netcorepal.cap4j.ddd.application.impl.DefaultRequestSupervisor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import java.util.List; @@ -18,17 +22,22 @@ @RequiredArgsConstructor public class MediatorAutoConfiguration { @Bean - public DefaultRequestSupervisor defaultRequestSupervisor(List requestHandlers){ - DefaultRequestSupervisor defaultRequestSupervisor = new DefaultRequestSupervisor(requestHandlers); + @Primary + public RequestSupervisor defaultRequestSupervisor( + List> requestHandlers, + List> requestInterceptors + ){ + DefaultRequestSupervisor defaultRequestSupervisor = new DefaultRequestSupervisor(requestHandlers, requestInterceptors); defaultRequestSupervisor.init(); - RequestSupervisorConfiguration.configure(defaultRequestSupervisor); + RequestSupervisorSupport.configure(defaultRequestSupervisor); return defaultRequestSupervisor; } @Bean + @ConditionalOnMissingBean(Mediator.class) public DefaultMediator defaultMediator(){ DefaultMediator defaultMediator = new DefaultMediator(); - MediatorConfiguration.configure(defaultMediator); + MediatorSupport.configure(defaultMediator); return defaultMediator; } } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/JdbcLockerAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/JdbcLockerAutoConfiguration.java index 6aa0f2c..4e84c29 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/JdbcLockerAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/JdbcLockerAutoConfiguration.java @@ -19,11 +19,13 @@ public class JdbcLockerAutoConfiguration { private final JdbcTemplate jdbcTemplate; + private static final String CONFIG_KEY_4_JPA_SHOW_SQL = "${spring.jpa.show-sql:${spring.jpa.showSql:false}}"; + @Bean @ConditionalOnMissingBean(value = Locker.class) public JdbcLocker jdbcLocker( JdbcLockerProperties properties, - @Value("${spring.jpa.show-sql:${spring.jpa.showSql:false}}") + @Value(CONFIG_KEY_4_JPA_SHOW_SQL) boolean showSql ) { JdbcLocker jdbcLocker = new JdbcLocker( diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java new file mode 100644 index 0000000..b810870 --- /dev/null +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java @@ -0,0 +1,95 @@ +package org.netcorepal.cap4j.ddd.application.event; + +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.netcorepal.cap4j.ddd.application.event.impl.DefaultIntegrationEventSupervisor; +import org.netcorepal.cap4j.ddd.domain.event.*; +import org.netcorepal.cap4j.ddd.domain.event.configure.EventProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.Environment; + +import java.util.List; + +import static org.netcorepal.cap4j.ddd.share.Constants.*; + +/** + * 基于RocketMq的领域事件(集成事件)实现自动配置类 + * + * + * + * @author binking338 + * @date 2023/9/10 + */ +@Configuration +@ConditionalOnProperty(name = "rocketmq.name-server") +@RequiredArgsConstructor +public class RocketMqEventAutoConfiguration { + + @Bean + @Primary + public DefaultIntegrationEventSupervisor defaultIntegrationEventSupervisor( + EventPublisher eventPublisher, + EventRecordRepository eventRecordRepository, + List integrationEventInterceptors, + ApplicationEventPublisher applicationEventPublisher, + @Value(CONFIG_KEY_4_SVC_NAME) + String svcName + ){ + DefaultIntegrationEventSupervisor defaultIntegrationEventSupervisor = new DefaultIntegrationEventSupervisor( + eventPublisher, + eventRecordRepository, + integrationEventInterceptors, + applicationEventPublisher, + svcName + ); + + IntegrationEventSupervisorSupport.configure(defaultIntegrationEventSupervisor); + return defaultIntegrationEventSupervisor; + } + + @Bean + public RocketMqIntegrationEventPublisher rocketMqIntegrationEventPublisher( + RocketMQTemplate rocketMQTemplate, + Environment environment + ) { + RocketMqIntegrationEventPublisher rocketMqDomainEventPublisher = new RocketMqIntegrationEventPublisher( + rocketMQTemplate, + environment); + return rocketMqDomainEventPublisher; + } + + @Bean + public RocketMqDomainEventSubscriberAdapter rocketMqDomainEventSubscriberAdapter( + EventSubscriberManager eventSubscriberManager, + List eventMessageInterceptors, + @Autowired(required = false) + MQConsumerConfigure mqConsumerConfigure, + Environment environment, + EventProperties eventProperties, + @Value(CONFIG_KEY_4_SVC_NAME) + String svcName, + @Value(CONFIG_KEY_4_ROCKETMQ_NAME_SERVER) + String defaultNameSrv, + @Value(CONFIG_KEY_4_ROCKETMQ_MSG_CHARSET) + String msgCharset + ) { + RocketMqDomainEventSubscriberAdapter rocketMqDomainEventSubscriberAdapter = new RocketMqDomainEventSubscriberAdapter( + eventSubscriberManager, + eventMessageInterceptors, + mqConsumerConfigure, + environment, + eventProperties.getEventScanPackage(), + svcName, + defaultNameSrv, + msgCharset + ); + rocketMqDomainEventSubscriberAdapter.init(); + return rocketMqDomainEventSubscriberAdapter; + } +} diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/SnowflakeAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/SnowflakeAutoConfiguration.java similarity index 78% rename from cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/SnowflakeAutoConfiguration.java rename to cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/SnowflakeAutoConfiguration.java index fd2d604..c91e11a 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/SnowflakeAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/SnowflakeAutoConfiguration.java @@ -1,11 +1,12 @@ -package org.netcorepal.cap4j.ddd.application.distributed; +package org.netcorepal.cap4j.ddd.domain.distributed; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.netcorepal.cap4j.ddd.application.distributed.configure.SnowflakeProperties; -import org.netcorepal.cap4j.ddd.application.distributed.snowflake.DefaultSnowflakeWorkerIdDispatcher; -import org.netcorepal.cap4j.ddd.application.distributed.snowflake.SnowflakeIdGenerator; -import org.netcorepal.cap4j.ddd.application.distributed.snowflake.SnowflakeWorkerIdDispatcher; +import org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator; +import org.netcorepal.cap4j.ddd.domain.distributed.snowflake.DefaultSnowflakeWorkerIdDispatcher; +import org.netcorepal.cap4j.ddd.domain.distributed.snowflake.SnowflakeIdGenerator; +import org.netcorepal.cap4j.ddd.domain.distributed.snowflake.SnowflakeWorkerIdDispatcher; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; @@ -33,18 +34,37 @@ public class SnowflakeAutoConfiguration { int pongContinuousErrorCount = 0; SnowflakeWorkerIdDispatcher snowflakeWorkerIdDispatcher; + public void shutdown() { + snowflakeWorkerIdDispatcher.release(); + } + + @Scheduled(cron = "0 */1 * * * ?") + public void pong() { + if (snowflakeWorkerIdDispatcher.pong()) { + log.info("SnowflakeWorkerIdDispatcher 心跳上报成功"); + pongContinuousErrorCount = 0; + } else { + log.error("SnowflakeWorkerIdDispatcher 心跳上报失败"); + pongContinuousErrorCount ++; + if(pongContinuousErrorCount > properties.getMaxPongContinuousErrorCount()){ + snowflakeWorkerIdDispatcher.remind(); + } + } + } + @Bean + @ConditionalOnMissingBean(SnowflakeIdGenerator.class) public SnowflakeIdGenerator snowflakeIdGenerator(SnowflakeWorkerIdDispatcher snowflakeWorkerIdDispatcher, SnowflakeProperties properties) { long workerId = snowflakeWorkerIdDispatcher.acquire(properties.getWorkerId(), properties.getDatacenterId()); SnowflakeIdGenerator snowflakeIdGenerator = new SnowflakeIdGenerator(workerId % (1 << SnowflakeIdGenerator.workerIdBits), workerId >> 5L); - SnowflakeIdentifierGenerator.snowflakeIdGenerator = snowflakeIdGenerator; + SnowflakeIdentifierGenerator.configure(snowflakeIdGenerator); return snowflakeIdGenerator; } @Bean @ConditionalOnMissingBean(SnowflakeWorkerIdDispatcher.class) public DefaultSnowflakeWorkerIdDispatcher defaultSnowflakeWorkerIdDispatcher(SnowflakeProperties properties) { - log.warn("注意!!!默认调度器需通过手工配置完成WorkerId、DatacenterId分发,有重复分配风险,请根据项目实际情况自行实现SnowflakeWorkerIdDispatcher。"); + log.warn("注意!!!默认调度器通过手工配置完成WorkerId、DatacenterId分发,有重复分配风险,请根据项目实际情况自行实现SnowflakeWorkerIdDispatcher。"); DefaultSnowflakeWorkerIdDispatcher dispatcher = new DefaultSnowflakeWorkerIdDispatcher( properties.getWorkerId() == null ? 0 : properties.getWorkerId().longValue(), properties.getDatacenterId() == null ? 0 : properties.getDatacenterId().longValue() @@ -53,21 +73,4 @@ public DefaultSnowflakeWorkerIdDispatcher defaultSnowflakeWorkerIdDispatcher(Sno return dispatcher; } - @Scheduled(cron = "0 */1 * * * ?") - public void pong() { - if (snowflakeWorkerIdDispatcher.pong()) { - log.info("SnowflakeWorkerIdDispatcher 心跳上报成功"); - pongContinuousErrorCount = 0; - } else { - log.error("SnowflakeWorkerIdDispatcher 心跳上报失败"); - pongContinuousErrorCount ++; - if(pongContinuousErrorCount > properties.getMaxPongContinuousErrorCount()){ - snowflakeWorkerIdDispatcher.remind(); - } - } - } - - public void shutdown() { - snowflakeWorkerIdDispatcher.release(); - } } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java new file mode 100644 index 0000000..47629a0 --- /dev/null +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java @@ -0,0 +1,179 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.distributed.Locker; +import org.netcorepal.cap4j.ddd.application.event.*; +import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultEventPublisher; +import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultEventSubscriberManager; +import org.netcorepal.cap4j.ddd.domain.event.configure.EventProperties; +import org.netcorepal.cap4j.ddd.domain.event.configure.EventScheduleProperties; +import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; +import org.netcorepal.cap4j.ddd.domain.event.persistence.ArchivedEventJpaRepository; +import org.netcorepal.cap4j.ddd.domain.event.persistence.EventJpaRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +import java.time.Duration; +import java.util.List; + +import static org.netcorepal.cap4j.ddd.share.Constants.*; + +/** + * 基于RocketMq的领域事件(集成事件)实现自动配置类 + * + * + * + * @author binking338 + * @date 2023/9/10 + */ +@Configuration +@RequiredArgsConstructor +@EnableJpaRepositories(basePackages = { + "org.netcorepal.cap4j.ddd.domain.event.persistence" +}) +@EntityScan(basePackages = { + "org.netcorepal.cap4j.ddd.domain.event.persistence" +}) +@EnableScheduling +public class DomainEventAutoConfiguration { + public static final String CONFIG_KEY_4_EVENT_COMPENSE_LOCKER_KEY = "event_compense[" + CONFIG_KEY_4_SVC_NAME + "]"; + public static final String CONFIG_KEY_4_EVENT_ARCHIVE_LOCKER_KEY = "event_archive[" + CONFIG_KEY_4_SVC_NAME + "]"; + + + private static final String CONFIG_KEY_4_COMPENSE_CRON = "${cap4j.ddd.domain.event.schedule.compenseCron:${cap4j.ddd.domain.event.schedule.compense-cron:0 */1 * * * ?}}"; + private static final String CONFIG_KEY_4_ARCHIVE_CRON = "${cap4j.ddd.domain.event.schedule.archiveCron:${cap4j.ddd.domain.event.schedule.archive-cron:0 0 2 * * ?}}"; + private static final String CONFIG_KEY_4_ADD_PARTITION_CRON = "${cap4j.ddd.domain.event.schedule.addPartitionCron:${cap4j.ddd.domain.event.schedule.add-partition-cron:0 0 0 * * ?}}"; + + + private final EventScheduleProperties eventScheduleProperties; + private JpaEventScheduleService scheduleService = null; + + @Scheduled(cron = CONFIG_KEY_4_COMPENSE_CRON) + public void compensation() { + if (scheduleService == null) return; + scheduleService.compense( + eventScheduleProperties.getCompenseBatchSize(), + eventScheduleProperties.getCompenseMaxConcurrency(), + Duration.ofSeconds(eventScheduleProperties.getCompenseIntervalSeconds()), + Duration.ofSeconds(eventScheduleProperties.getCompenseMaxLockSeconds()) + ); + } + + @Scheduled(cron = CONFIG_KEY_4_ARCHIVE_CRON) + public void archive() { + if (scheduleService == null) return; + scheduleService.archive( + eventScheduleProperties.getArchiveExpireDays(), + eventScheduleProperties.getArchiveBatchSize(), + Duration.ofSeconds(eventScheduleProperties.getArchiveMaxLockSeconds()) + ); + } + + @Scheduled(cron = CONFIG_KEY_4_ADD_PARTITION_CRON) + public void addTablePartition() { + scheduleService.addPartition(); + } + + @Bean + public JpaEventScheduleService eventScheduleService( + EventPublisher eventPublisher, + EventRecordRepository eventRecordRepository, + EventJpaRepository eventJpaRepository, + ArchivedEventJpaRepository archivedEventJpaRepository, + Locker locker, + @Value(CONFIG_KEY_4_SVC_NAME) + String svcName, + @Value(CONFIG_KEY_4_EVENT_COMPENSE_LOCKER_KEY) + String compensationLockerKey, + @Value(CONFIG_KEY_4_EVENT_ARCHIVE_LOCKER_KEY) + String archiveLockerKey, + EventScheduleProperties eventScheduleProperties, + JdbcTemplate jdbcTemplate) { + scheduleService = new JpaEventScheduleService( + eventPublisher, + eventRecordRepository, + eventJpaRepository, + archivedEventJpaRepository, + locker, + svcName, + compensationLockerKey, + archiveLockerKey, + eventScheduleProperties.isAddPartitionEnable(), + jdbcTemplate); + scheduleService.init(); + return scheduleService; + } + + @Bean + @ConditionalOnMissingBean(EventPublisher.class) + public EventPublisher defaultEventPublisher( + EventSubscriberManager eventSubscriberManager, + List integrationEventPublisheres, + EventRecordRepository eventRecordRepository, + List eventMessageInterceptors, + List domainEventInterceptors, + List integrationEventInterceptors, + EventProperties eventProperties + ){ + DefaultEventPublisher defaultEventPublisher = new DefaultEventPublisher( + eventSubscriberManager, + integrationEventPublisheres, + eventRecordRepository, + eventMessageInterceptors, + domainEventInterceptors, + integrationEventInterceptors, + eventProperties.getPublisherThreadPoolSize() + ); + defaultEventPublisher.init(); + return defaultEventPublisher; + } + + @Bean + @ConditionalOnMissingBean(EventSubscriberManager.class) + public EventSubscriberManager defaultEventSubscriberManager( + List> subscribers, + ApplicationEventPublisher applicationEventPublisher, + EventProperties eventProperties + ) { + DefaultEventSubscriberManager domainEventSubscriberManager = + new DefaultEventSubscriberManager( + subscribers, + applicationEventPublisher, + eventProperties.getEventScanPackage() + ); + domainEventSubscriberManager.init(); + return domainEventSubscriberManager; + } + + @Bean + @Primary + public DomainEventSupervisor defaultDomainEventSupervisor( + EventRecordRepository eventRecordRepository, + List domainEventInterceptors, + EventPublisher eventPublisher, + ApplicationEventPublisher applicationEventPublisher, + @Value(CONFIG_KEY_4_SVC_NAME) + String svcName + ) { + DefaultDomainEventSupervisor defaultDomainEventSupervisor = new DefaultDomainEventSupervisor( + eventRecordRepository, + domainEventInterceptors, + eventPublisher, + applicationEventPublisher, + svcName + ); + + DomainEventSupervisorSupport.configure(defaultDomainEventSupervisor); + return defaultDomainEventSupervisor; + } + +} \ No newline at end of file diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java deleted file mode 100644 index 86df2a9..0000000 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqEventAutoConfiguration.java +++ /dev/null @@ -1,174 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.event; - -import lombok.RequiredArgsConstructor; -import org.apache.rocketmq.spring.core.RocketMQTemplate; -import org.netcorepal.cap4j.ddd.application.distributed.Locker; -import org.netcorepal.cap4j.ddd.domain.event.configure.EventProperties; -import org.netcorepal.cap4j.ddd.domain.event.configure.EventScheduleProperties; -import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; -import org.netcorepal.cap4j.ddd.domain.event.persistence.ArchivedEventJpaRepository; -import org.netcorepal.cap4j.ddd.domain.event.persistence.EventRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; - -import java.time.Duration; -import java.util.List; - -import static org.netcorepal.cap4j.ddd.share.Constants.*; - -/** - * 基于RocketMq的领域事件(集成事件)实现自动配置类 - * - * @author binking338 - * @date 2023/9/10 - */ -@Configuration -@ConditionalOnProperty(name = "rocketmq.name-server") -@RequiredArgsConstructor -@EnableJpaRepositories(basePackages = {"org.netcorepal.cap4j.ddd.domain.event.persistence"}) -@EntityScan(basePackages = {"org.netcorepal.cap4j.ddd.domain.event.persistence"}) -@EnableScheduling -public class RocketMqEventAutoConfiguration { - private final Locker locker; - private final EventRepository eventRepository; - private final ArchivedEventJpaRepository archivedEventJpaRepository; - private final RocketMQTemplate rocketMQTemplate; - private final ApplicationEventPublisher applicationEventPublisher; - private final List subscribers; - private final JdbcTemplate jdbcTemplate; - private final Environment environment; - - private final EventScheduleProperties eventScheduleProperties; - - @Bean - @ConditionalOnMissingBean(EventRecordRepository.class) - public JpaEventRecordRepository jpaEventRecordRepository() { - JpaEventRecordRepository eventRecordRepository = new JpaEventRecordRepository(eventRepository); - return eventRecordRepository; - } - - @Bean - public RocketMqDomainEventSubscriberManager rocketMqDomainEventSubscriberManager() { - RocketMqDomainEventSubscriberManager domainEventSubscriberManager = new RocketMqDomainEventSubscriberManager( - subscribers, - applicationEventPublisher); - return domainEventSubscriberManager; - } - - @Bean - public RocketMqDomainEventPublisher rocketMqDomainEventPublisher( - RocketMqDomainEventSubscriberManager rocketMqDomainEventSubscriberManager, - JpaEventRecordRepository jpaEventRecordRepository, - EventProperties eventProperties) { - RocketMqDomainEventPublisher rocketMqDomainEventPublisher = new RocketMqDomainEventPublisher( - rocketMqDomainEventSubscriberManager, - rocketMQTemplate, - jpaEventRecordRepository, - eventProperties.getPublisherThreadPoolSize(), - environment); - return rocketMqDomainEventPublisher; - } - - @Bean - public DefaultDomainEventSupervisor defaultDomainEventSupervisor() { - DefaultDomainEventSupervisor defaultDomainEventSupervisor = new DefaultDomainEventSupervisor(); - DomainEventSupervisorConfiguration.configure((DomainEventSupervisor)defaultDomainEventSupervisor); - DomainEventSupervisorConfiguration.configure((EventSupervisor)defaultDomainEventSupervisor); - return defaultDomainEventSupervisor; - } - - @Bean - public RocketMqDomainEventSubscriberAdapter rocketMqDomainEventSubscriberAdapter( - RocketMqDomainEventSubscriberManager rocketMqDomainEventSubscriberManager, - @Autowired(required = false) - DomainEventMessageInterceptor domainEventMessageInterceptor, - @Autowired(required = false) - MQConsumerConfigure mqConsumerConfigure, - EventProperties eventProperties, - @Value(CONFIG_KEY_4_SVC_NAME) - String applicationName, - @Value("${rocketmq.name-server:}") - String defaultNameSrv, - @Value("${rocketmq.msg-charset:UTF-8}") - String msgCharset - ) { - RocketMqDomainEventSubscriberAdapter rocketMqDomainEventSubscriberAdapter = new RocketMqDomainEventSubscriberAdapter( - rocketMqDomainEventSubscriberManager, - environment, - domainEventMessageInterceptor, - mqConsumerConfigure, - applicationName, - defaultNameSrv, - msgCharset, - eventProperties.getSubscriberScanPackage() - ); - return rocketMqDomainEventSubscriberAdapter; - } - - @Bean - public JpaEventScheduleService eventScheduleService( - DomainEventPublisher domainEventPublisher, - JpaEventRecordRepository jpaEventRecordRepository, - @Autowired(required = false) - DomainEventMessageInterceptor domainEventMessageInterceptor, - @Value(CONFIG_KEY_4_SVC_NAME) - String svcName, - @Value("event_compense[" + CONFIG_KEY_4_SVC_NAME + "]") - String compensationLockerKey, - @Value("event_archive[" + CONFIG_KEY_4_SVC_NAME + "]") - String archiveLockerKey) { - scheduleService = new JpaEventScheduleService( - locker, - domainEventPublisher, - domainEventMessageInterceptor, - jpaEventRecordRepository, - eventRepository, - archivedEventJpaRepository, - svcName, - compensationLockerKey, - archiveLockerKey, - eventScheduleProperties.isAddPartitionEnable(), - jdbcTemplate); - return scheduleService; - } - - private JpaEventScheduleService scheduleService = null; - - @Scheduled(cron = "${cap4j.ddd.domain.event.schedule.compenseCron:0 */1 * * * ?}") - public void compensation() { - if (scheduleService == null) return; - scheduleService.compense( - eventScheduleProperties.getCompenseBatchSize(), - eventScheduleProperties.getCompenseMaxConcurrency(), - Duration.ofSeconds(eventScheduleProperties.getCompenseIntervalSeconds()), - Duration.ofSeconds(eventScheduleProperties.getCompenseMaxLockSeconds()) - ); - } - - @Scheduled(cron = "${cap4j.ddd.domain.event.schedule.archiveCron:0 0 2 * * ?}") - public void archive() { - if (scheduleService == null) return; - scheduleService.archive( - eventScheduleProperties.getArchiveExpireDays(), - eventScheduleProperties.getArchiveBatchSize(), - Duration.ofSeconds(eventScheduleProperties.getArchiveMaxLockSeconds()) - ); - } - - @Scheduled(cron = "${cap4j.ddd.domain.event.schedule.addPartitionCron:0 0 0 * * ?}") - public void addTablePartition() { - scheduleService.addPartition(); - } - -} diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/configure/EventProperties.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/configure/EventProperties.java index fa6e101..12c1089 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/configure/EventProperties.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/configure/EventProperties.java @@ -15,12 +15,14 @@ @ConfigurationProperties("cap4j.ddd.domain.event") public class EventProperties { /** - * 订阅器扫描包范围 + * 事件扫描包范围 + * 领域事件 & 集成事件 */ - String subscriberScanPackage = ""; + String eventScanPackage = ""; /** * 发布器线程池大小 + * 用于实现延迟发送 */ int publisherThreadPoolSize = 4; } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java index 77491bf..9de3c62 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java @@ -2,22 +2,37 @@ import lombok.RequiredArgsConstructor; import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.netcorepal.cap4j.ddd.application.UnitOfWork; +import org.netcorepal.cap4j.ddd.application.impl.JpaUnitOfWork; +import org.netcorepal.cap4j.ddd.application.UnitOfWorkSupport; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactorySupervisor; +import org.netcorepal.cap4j.ddd.domain.aggregate.Specification; +import org.netcorepal.cap4j.ddd.domain.aggregate.SpecificationManager; +import org.netcorepal.cap4j.ddd.domain.aggregate.impl.DefaultAggregateFactorySupervisor; import org.netcorepal.cap4j.ddd.domain.event.*; -import org.netcorepal.cap4j.ddd.domain.event.configure.EventScheduleProperties; +import org.netcorepal.cap4j.ddd.domain.event.configure.EventProperties; +import org.netcorepal.cap4j.ddd.domain.event.persistence.EventJpaRepository; +import org.netcorepal.cap4j.ddd.domain.repo.impl.DefaultPersistListenerManager; +import org.netcorepal.cap4j.ddd.domain.aggregate.impl.DefaultSpecificationManager; import org.netcorepal.cap4j.ddd.domain.repo.configure.JpaUnitOfWorkProperties; +import org.netcorepal.cap4j.ddd.domain.repo.impl.DefaultRepositorySupervisor; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import java.util.List; -import static org.netcorepal.cap4j.ddd.share.Constants.*; - /** * 基于Jpa的仓储实现自动配置类 + *

+ * DefaultPersistListenerManager + * DefaultSpecificationManager + * JpaUnitOfWork * * @author binking338 * @date 2023/9/10 @@ -26,63 +41,92 @@ @ConditionalOnBean(RocketMQTemplate.class) @RequiredArgsConstructor public class JpaRepositoryAutoConfiguration { - private final ApplicationEventPublisher applicationEventPublisher; - private final DomainEventSupervisor domainEventSupervisor; - private final DomainEventPublisher domainEventPublisher; - private final DomainEventSubscriberManager domainEventSubscriberManager; - private final EventRecordRepository eventRecordRepository; @Bean - public JpaPersistListenerManager jpaPersistListenerManager(List persistListeners) { - JpaPersistListenerManager persistListenerManager = new JpaPersistListenerManager(persistListeners); + @ConditionalOnMissingBean(PersistListenerManager.class) + public PersistListenerManager defaultPersistListenerManager( + List> persistListeners, + EventProperties eventProperties + ) { + DefaultPersistListenerManager persistListenerManager = new DefaultPersistListenerManager( + persistListeners, + eventProperties.getEventScanPackage()); persistListenerManager.init(); return persistListenerManager; } @Bean - public JpaSpecificationManager jpaSpecificationManager(List specifications) { - JpaSpecificationManager specificationManager = new JpaSpecificationManager(specifications); + @ConditionalOnMissingBean(SpecificationManager.class) + public SpecificationManager defaultSpecificationManager(List> specifications) { + DefaultSpecificationManager specificationManager = new DefaultSpecificationManager(specifications); specificationManager.init(); return specificationManager; } @Bean + @Primary public JpaUnitOfWork jpaUnitOfWork( - List abstractJpaRepository, - JpaSpecificationManager jpaSpecificationManager, - JpaPersistListenerManager jpaPersistListenerManager, - @Autowired(required = false) - DomainEventMessageInterceptor domainEventMessageInterceptor, - JpaUnitOfWorkProperties jpaUnitOfWorkProperties, - EventScheduleProperties eventScheduleProperties, - @Value(CONFIG_KEY_4_SVC_NAME) - String svcName + DomainEventSupervisor domainEventSupervisor, + ApplicationEventPublisher applicationEventPublisher, + SpecificationManager specificationManager, + PersistListenerManager persistListenerManager, + JpaUnitOfWorkProperties jpaUnitOfWorkProperties ) { JpaUnitOfWork unitOfWork = new JpaUnitOfWork( - abstractJpaRepository, - applicationEventPublisher, domainEventSupervisor, - domainEventPublisher, - domainEventSubscriberManager, - eventRecordRepository, - jpaSpecificationManager, - jpaPersistListenerManager, - domainEventMessageInterceptor, - svcName, - jpaUnitOfWorkProperties.getEntityGetIdMethod(), - jpaUnitOfWorkProperties.getRetrieveCountWarnThreshold(), - eventScheduleProperties.getCompenseIntervalSeconds()); - unitOfWork.init(); - UnitOfWorkConfiguration.configure(unitOfWork); - RepositorySupervisorConfiguration.configure(unitOfWork); + specificationManager, + persistListenerManager, + applicationEventPublisher, + jpaUnitOfWorkProperties.getRetrieveCountWarnThreshold()); + UnitOfWorkSupport.configure(unitOfWork); return unitOfWork; } + @Bean + @Primary + public RepositorySupervisor defaultRepositorySupervisor( + List> repositories, + UnitOfWork unitOfWork + ){ + DefaultRepositorySupervisor repositorySupervisor = new DefaultRepositorySupervisor(repositories, unitOfWork); + repositorySupervisor.init(); + RepositorySupervisorSupport.configure(repositorySupervisor); + return repositorySupervisor; + } + + @Bean + @Primary + public AggregateFactorySupervisor defaultAggregateFactorySupervisor( + List> factories + ){ + DefaultAggregateFactorySupervisor aggregateFactorySupervisor = new DefaultAggregateFactorySupervisor( + factories + ); + aggregateFactorySupervisor.init(); + return aggregateFactorySupervisor; + } + @Configuration - private static class JpaLoader { - public JpaLoader(@Autowired(required = false) JpaUnitOfWork jpaUnitOfWork) { - JpaUnitOfWork.fixJpaAopWrapper(jpaUnitOfWork); + private static class __JpaUnitOfWorkLoader { + public __JpaUnitOfWorkLoader( + @Autowired(required = false) + JpaUnitOfWork jpaUnitOfWork + ) { + JpaUnitOfWork.fixAopWrapper(jpaUnitOfWork); } } + + + @Bean + @ConditionalOnMissingBean(EventRecordRepository.class) + public JpaEventRecordRepository jpaEventRecordRepository( + EventJpaRepository eventJpaRepository + ) { + JpaEventRecordRepository eventRecordRepository = + new JpaEventRecordRepository( + eventJpaRepository + ); + return eventRecordRepository; + } } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/configure/JpaUnitOfWorkProperties.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/configure/JpaUnitOfWorkProperties.java index c69950f..2d48a62 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/configure/JpaUnitOfWorkProperties.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/configure/JpaUnitOfWorkProperties.java @@ -14,10 +14,6 @@ @Configuration @ConfigurationProperties("cap4j.ddd.domain.jpa-uow") public class JpaUnitOfWorkProperties { - /** - * 实体获取Id方法名称 - */ - String entityGetIdMethod = "getId"; /** * 单次获取记录数 */ diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java new file mode 100644 index 0000000..43e2313 --- /dev/null +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java @@ -0,0 +1,34 @@ +package org.netcorepal.cap4j.ddd.domain.service; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.domain.service.impl.DefaultDomainServiceSupervisor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 领域服务自动配置 + * + * @author binking338 + * @date 2024/9/4 + */ +@Configuration +@RequiredArgsConstructor +public class DomainServiceAutoConfiguration { + + /** + * 默认领域服务管理器 + * + * @param applicationContext + * @return + */ + @Bean + @ConditionalOnMissingBean(DomainServiceSupervisor.class) + public DomainServiceSupervisor defaultDomainServiceSupervisor(ApplicationContext applicationContext) { + DefaultDomainServiceSupervisor domainServiceSupervisor = new DefaultDomainServiceSupervisor( + applicationContext + ); + return domainServiceSupervisor; + } +} diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/web/ClearDomainContextInterceptor.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/web/ClearDomainContextInterceptor.java index 4cefbc8..3163917 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/web/ClearDomainContextInterceptor.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/web/ClearDomainContextInterceptor.java @@ -1,8 +1,8 @@ package org.netcorepal.cap4j.ddd.domain.web; import lombok.RequiredArgsConstructor; -import org.netcorepal.cap4j.ddd.domain.repo.UnitOfWork; -import org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisor; +import org.netcorepal.cap4j.ddd.application.impl.JpaUnitOfWork; +import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.HandlerInterceptor; @@ -20,12 +20,10 @@ @RequiredArgsConstructor @ConditionalOnWebApplication public class ClearDomainContextInterceptor implements HandlerInterceptor { - private final UnitOfWork unitOfWork; - private final DomainEventSupervisor domainEventSupervisor; @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { - unitOfWork.reset(); - domainEventSupervisor.reset(); + JpaUnitOfWork.reset(); + DefaultDomainEventSupervisor.reset(); } } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories b/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories index 152ecac..9d011cf 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories @@ -1,12 +1,14 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.netcorepal.cap4j.ddd.application.MediatorAutoConfiguration,\ - org.netcorepal.cap4j.ddd.application.distributed.configure.JdbcLockerProperties,\ org.netcorepal.cap4j.ddd.application.distributed.configure.SnowflakeProperties,\ + org.netcorepal.cap4j.ddd.application.distributed.configure.JdbcLockerProperties,\ org.netcorepal.cap4j.ddd.application.distributed.JdbcLockerAutoConfiguration,\ - org.netcorepal.cap4j.ddd.application.distributed.SnowflakeAutoConfiguration,\ + org.netcorepal.cap4j.ddd.application.event.RocketMqEventAutoConfiguration,\ + org.netcorepal.cap4j.ddd.application.MediatorAutoConfiguration,\ + org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeAutoConfiguration,\ org.netcorepal.cap4j.ddd.domain.event.configure.EventProperties,\ org.netcorepal.cap4j.ddd.domain.event.configure.EventScheduleProperties,\ + org.netcorepal.cap4j.ddd.domain.event.DomainEventAutoConfiguration,\ org.netcorepal.cap4j.ddd.domain.repo.configure.JpaUnitOfWorkProperties,\ org.netcorepal.cap4j.ddd.domain.repo.JpaRepositoryAutoConfiguration,\ - org.netcorepal.cap4j.ddd.domain.event.RocketMqEventAutoConfiguration,\ + org.netcorepal.cap4j.ddd.domain.service.DomainServiceAutoConfiguration,\ org.netcorepal.cap4j.ddd.domain.web.ClearDomainContextInterceptor \ No newline at end of file diff --git a/ddd-core/pom.xml b/ddd-core/pom.xml index fd131ca..1c72f74 100644 --- a/ddd-core/pom.xml +++ b/ddd-core/pom.xml @@ -22,6 +22,11 @@ spring-tx provided + + org.springframework + spring-context + provided + org.springframework spring-messaging diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/Mediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/Mediator.java new file mode 100644 index 0000000..004d80b --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/Mediator.java @@ -0,0 +1,128 @@ +package org.netcorepal.cap4j.ddd; + +import org.netcorepal.cap4j.ddd.application.RequestSupervisor; +import org.netcorepal.cap4j.ddd.application.UnitOfWork; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventSupervisor; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactorySupervisor; +import org.netcorepal.cap4j.ddd.domain.repo.RepositorySupervisor; +import org.netcorepal.cap4j.ddd.domain.service.DomainServiceSupervisor; +import org.springframework.context.ApplicationContext; + +/** + * 中介者 + * + * @author binking338 + * @date 2024/8/24 + */ +public interface Mediator extends AggregateFactorySupervisor, RepositorySupervisor, DomainServiceSupervisor, UnitOfWork, IntegrationEventSupervisor, RequestSupervisor { + static Mediator getInstance() { + return MediatorSupport.instance; + } + + /** + * 获取IOC容器 + * + * @return + */ + static ApplicationContext ioc() { + return MediatorSupport.ioc; + } + + /** + * 获取聚合工厂管理器 + * + * @return + */ + static AggregateFactorySupervisor factories() { + return AggregateFactorySupervisor.getInstance(); + } + + /** + * 获取聚合仓储管理器 + * + * @return + */ + static RepositorySupervisor repositories() { + return RepositorySupervisor.getInstance(); + } + + /** + * 获取领域服务管理器 + * + * @return + */ + static DomainServiceSupervisor services() { + return DomainServiceSupervisor.getInstance(); + } + + /** + * 获取单元工作单元 + * + * @return + */ + static UnitOfWork uow() { + return UnitOfWork.getInstance(); + } + + /** + * 获取集成事件管理器 + * + * @return + */ + static IntegrationEventSupervisor events() { + return IntegrationEventSupervisor.getInstance(); + } + + + /** + * 获取请求管理器 + * 兼容 cmd() qry(),当前三者实现一致。 + * + * @return + */ + static RequestSupervisor requests() { + return RequestSupervisor.getInstance(); + } + + /** + * 获取命令管理器 + * + * @return + */ + static RequestSupervisor commands() { + return RequestSupervisor.getInstance(); + } + + /** + * 获取查询管理器 + * + * @return + */ + static RequestSupervisor queries() { + return RequestSupervisor.getInstance(); + } + + default ApplicationContext getApplicationContext() { + return MediatorSupport.ioc; + } + + default AggregateFactorySupervisor getAggregateFactorySupervisor() { + return AggregateFactorySupervisor.getInstance(); + } + + default RepositorySupervisor getRepositorySupervisor() { + return RepositorySupervisor.getInstance(); + } + + default UnitOfWork getUnitOfWork() { + return UnitOfWork.getInstance(); + } + + default IntegrationEventSupervisor getIntegrationEventSupervisor() { + return IntegrationEventSupervisor.getInstance(); + } + + default RequestSupervisor getRequestSupervisor() { + return RequestSupervisor.getInstance(); + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/MediatorSupport.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/MediatorSupport.java new file mode 100644 index 0000000..3c71132 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/MediatorSupport.java @@ -0,0 +1,23 @@ +package org.netcorepal.cap4j.ddd; + +import org.springframework.context.ApplicationContext; + +/** + * 终结者配置 + * + * @author binking338 + * @date 2024/8/24 + */ +public class MediatorSupport { + static Mediator instance = null; + + static ApplicationContext ioc = null; + + public static void configure(Mediator mediator) { + instance = mediator; + } + + public static void configure(ApplicationContext applicationContext) { + ioc = applicationContext; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java deleted file mode 100644 index 30c0ca2..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/Mediator.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.netcorepal.cap4j.ddd.application; - -import org.netcorepal.cap4j.ddd.domain.event.EventSupervisor; -import org.netcorepal.cap4j.ddd.domain.repo.RepositorySupervisor; -import org.netcorepal.cap4j.ddd.domain.repo.UnitOfWork; - -import java.time.Duration; -import java.time.LocalDateTime; - -/** - * 中介者 - * - * @author binking338 - * @date 2024/8/24 - */ -public interface Mediator extends RequestSupervisor, EventSupervisor { - static Mediator getInstance() - { - return MediatorConfiguration.instance; - } - -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorConfiguration.java deleted file mode 100644 index 8cab324..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorConfiguration.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.netcorepal.cap4j.ddd.application; - -/** - * todo: 类描述 - * - * @author binking338 - * @date 2024/8/24 - */ -public class MediatorConfiguration { - static Mediator instance = null; - - public static void configure(Mediator mediator) - { - instance = mediator; - } -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java index 80c3727..e59bc57 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java @@ -3,9 +3,8 @@ /** * 请求接口 * - * @param 请求参数 + * @param 请求参数 * @param 返回结果 - * * @author binking338 * @date 2024/8/24 */ @@ -13,6 +12,7 @@ public interface RequestHandler { /** * 执行请求 + * * @param request 请求参数 * @return 返回结果 */ diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestInterceptor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestInterceptor.java new file mode 100644 index 0000000..4d467ec --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestInterceptor.java @@ -0,0 +1,24 @@ +package org.netcorepal.cap4j.ddd.application; + +/** + * 请求拦截器 + * + * @author binking338 + * @date 2024/9/1 + */ +public interface RequestInterceptor { + /** + * 请求前 + * + * @param request + */ + void preRequest(REQUEST request); + + /** + * 请求后 + * + * @param request + * @param response + */ + void postRequest(REQUEST request, RESPONSE response); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java index d58d15b..5bf22a8 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java @@ -13,37 +13,37 @@ public interface RequestSupervisor { * * @return 请求管理器 */ - static RequestSupervisor getInstance(){ - return RequestSupervisorConfiguration.instance; + static RequestSupervisor getInstance() { + return RequestSupervisorSupport.instance; } /** * 执行请求 * - * @param request 请求参数 + * @param request 请求参数 * @param 请求参数类型 */ - Object request(REQUEST request); + Object send(REQUEST request); /** * 执行请求 * - * @param request 请求参数 + * @param request 请求参数 * @param resultClass 返回结果类型 - * @param 请求参数类型 - * @param 返回结果类型 + * @param 请求参数类型 + * @param 返回结果类型 */ - RESPONSE request(REQUEST request, Class resultClass); + RESPONSE send(REQUEST request, Class resultClass); /** * 执行请求 * - * @param request 请求参数 - * @param paramClass 请求参数类型 + * @param request 请求参数 + * @param paramClass 请求参数类型 * @param resultClass 返回结果类型 + * @param 请求参数类型 + * @param 返回结果类型 * @return 请求结果 - * @param 请求参数类型 - * @param 返回结果类型 */ - RESPONSE request(REQUEST request, Class paramClass, Class resultClass); + RESPONSE send(REQUEST request, Class paramClass, Class resultClass); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorSupport.java similarity index 67% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorConfiguration.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorSupport.java index c5f76cf..7c691a9 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorConfiguration.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorSupport.java @@ -1,19 +1,17 @@ package org.netcorepal.cap4j.ddd.application; -import org.netcorepal.cap4j.ddd.application.impl.DefaultRequestSupervisor; -import org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisor; - /** * 请求管理器配置 * * @author binking338 * @date 2024/8/24 */ -public class RequestSupervisorConfiguration { +public class RequestSupervisorSupport { static RequestSupervisor instance = null; /** * 配置请求管理器 + * * @param requestSupervisor {@link RequestSupervisor} */ public static void configure(RequestSupervisor requestSupervisor) { diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWork.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java similarity index 68% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWork.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java index 060d149..f8165bd 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWork.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java @@ -1,4 +1,4 @@ -package org.netcorepal.cap4j.ddd.domain.repo; +package org.netcorepal.cap4j.ddd.application; import org.springframework.transaction.annotation.Propagation; @@ -8,19 +8,21 @@ * @author binking338 * @date 2023/8/5 */ -public interface UnitOfWork extends RepositorySupervisor { - static UnitOfWork getInstance(){ - return UnitOfWorkConfiguration.instance; +public interface UnitOfWork { + static UnitOfWork getInstance() { + return UnitOfWorkSupport.instance; } /** * 新增或更新持久化记录 + * * @param entity 实体对象 */ void persist(Object entity); /** * 移除持久化记录 + * * @param entity 实体对象 */ void remove(Object entity); @@ -32,12 +34,8 @@ static UnitOfWork getInstance(){ /** * 提交事务 + * * @param propagation 事务传播特性 */ void save(Propagation propagation); - - /** - * 重置上下文 - */ - void reset(); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWorkConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWorkSupport.java similarity index 62% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWorkConfiguration.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWorkSupport.java index 39333c2..36844c8 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/UnitOfWorkConfiguration.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWorkSupport.java @@ -1,4 +1,4 @@ -package org.netcorepal.cap4j.ddd.domain.repo; +package org.netcorepal.cap4j.ddd.application; /** * 工作单元配置 @@ -6,7 +6,7 @@ * @author binking338 * @date 2024/8/25 */ -public class UnitOfWorkConfiguration { +public class UnitOfWorkSupport { static UnitOfWork instance = null; /** @@ -14,8 +14,7 @@ public class UnitOfWorkConfiguration { * * @param unitOfWork 工作单元 */ - public static void configure(UnitOfWork unitOfWork) - { + public static void configure(UnitOfWork unitOfWork) { instance = unitOfWork; } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java index fb1e4c9..c9fc3c8 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java @@ -5,11 +5,10 @@ /** * 命令接口 * - * @author binking338 - * @date - * * @param * @param + * @author binking338 + * @date */ public interface Command extends RequestHandler { @Override diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParamAndResult.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParamAndResult.java index 916544c..9810c0f 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParamAndResult.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParamAndResult.java @@ -6,7 +6,7 @@ * @author binking338 * @date */ -public abstract class CommandNoneParamAndResult implements Command{ +public abstract class CommandNoneParamAndResult implements Command { @Override public Void exec(Void param) { exec(); diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/Locker.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/Locker.java index 67c971e..ea9ed22 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/Locker.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/Locker.java @@ -11,6 +11,7 @@ public interface Locker { /** * 获取锁 + * * @param key * @param pwd * @param expireDuration @@ -20,6 +21,7 @@ public interface Locker { /** * 释放锁 + * * @param key * @param pwd * @return diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventAttachedTransactionCommittedEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventAttachedTransactionCommittedEvent.java new file mode 100644 index 0000000..c83e4f2 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventAttachedTransactionCommittedEvent.java @@ -0,0 +1,30 @@ +package org.netcorepal.cap4j.ddd.application.event; + +import lombok.Getter; +import org.netcorepal.cap4j.ddd.domain.event.EventRecord; +import org.springframework.context.ApplicationEvent; + +import java.util.List; + +/** + * 集成事件所在事务成功提交事件 + * + * @author binking338 + * @date 2024/8/28 + */ +public class IntegrationEventAttachedTransactionCommittedEvent extends ApplicationEvent { + @Getter + List events; + + /** + * Create a new {@code ApplicationEvent}. + * + * @param source the object on which the event initially occurred or with + * which the event is associated (never {@code null}) + */ + public IntegrationEventAttachedTransactionCommittedEvent(Object source, List events) { + super(source); + this.events = events; + } +} + diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptor.java new file mode 100644 index 0000000..f224055 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptor.java @@ -0,0 +1,22 @@ +package org.netcorepal.cap4j.ddd.application.event; + +import org.netcorepal.cap4j.ddd.share.EventInterceptor; + +import java.time.LocalDateTime; + +/** + * 集成事件拦截器 + * + * @author binking338 + * @date 2024/8/29 + */ +public interface IntegrationEventInterceptor extends EventInterceptor { + + /** + * 调用通知时 + * + * @param eventPayload + * @param schedule + */ + void onNotify(Object eventPayload, LocalDateTime schedule); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventPublisher.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventPublisher.java new file mode 100644 index 0000000..1a47cf4 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventPublisher.java @@ -0,0 +1,37 @@ +package org.netcorepal.cap4j.ddd.application.event; + +import org.netcorepal.cap4j.ddd.domain.event.EventRecord; + +/** + * 集成事件发布器 + * + * @author binking338 + * @date 2024/8/29 + */ +public interface IntegrationEventPublisher { + + + /** + * 发布事件 + * + * @param event + * @param publishCallback + */ + void publish(EventRecord event, PublishCallback publishCallback); + + public interface PublishCallback { + /** + * 发布成功 + * + * @param event + */ + void onSuccess(EventRecord event); + + /** + * @param event + * @param throwable + */ + void onException(EventRecord event, Throwable throwable); + } + +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisor.java new file mode 100644 index 0000000..5509436 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisor.java @@ -0,0 +1,43 @@ +package org.netcorepal.cap4j.ddd.application.event; + +import java.time.Duration; +import java.time.LocalDateTime; + +/** + * 集成事件管理器 + * + * @author binking338 + * @date 2024/8/25 + */ +public interface IntegrationEventSupervisor { + + static IntegrationEventSupervisor getInstance() { + return IntegrationEventSupervisorSupport.instance; + } + + /** + * 通知集成事件 + * + * @param integrationEventPayload 集成事件消息体 + * @param 事件消息类型 + */ + void notify(INTEGRATION_EVENT integrationEventPayload); + + /** + * 延迟通知集成事件 + * + * @param integrationEventPayload 集成事件消息体 + * @param delay 延迟时长 + * @param 事件消息类型 + */ + void notify(INTEGRATION_EVENT integrationEventPayload, Duration delay); + + /** + * 定时通知集成事件 + * + * @param integrationEventPayload 集成事件消息体 + * @param schedule 定时时间 + * @param 事件消息类型 + */ + void notify(INTEGRATION_EVENT integrationEventPayload, LocalDateTime schedule); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisorSupport.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisorSupport.java new file mode 100644 index 0000000..f256f76 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisorSupport.java @@ -0,0 +1,21 @@ +package org.netcorepal.cap4j.ddd.application.event; + +/** + * 事件管理器配置 + * + * @author binking338 + * @date 2024/8/26 + */ +public class IntegrationEventSupervisorSupport { + static IntegrationEventSupervisor instance = null; + + + /** + * 配置事件管理器 + * + * @param integrationEventSupervisor {@link IntegrationEventSupervisor} + */ + public static void configure(IntegrationEventSupervisor integrationEventSupervisor) { + IntegrationEventSupervisorSupport.instance = integrationEventSupervisor; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java new file mode 100644 index 0000000..25c93d7 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java @@ -0,0 +1,40 @@ +package org.netcorepal.cap4j.ddd.application.event.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自动发布 + * 领域事件 -> 集成事件 + * + * @author binking338 + * @date 2024/8/29 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AutoNotify { + + /** + * 源领域事件类型 + * + * @return + */ + Class sourceDomainEventClass() default Void.class; + + /** + * 延迟发布(秒) + * + * @return + */ + int delayInSeconds() default 0; + + /** + * 领域事件 -> 集成事件 转换器 + * {@link org.springframework.core.convert.converter.Converter} + * + * @return {@link Class} + */ + Class converterClass() default Void.class; +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequest.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequest.java new file mode 100644 index 0000000..9f170e5 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequest.java @@ -0,0 +1,33 @@ +package org.netcorepal.cap4j.ddd.application.event.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自动触发请求 + * 事件(领域\集成)-> 命令 + * + * @author binking338 + * @date 2024/9/1 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AutoRequest { + + /** + * 目标请求 + * + * @return + */ + Class targetRequestClass() default Void.class; + + /** + * 事件 -> 请求 转换器 + * {@link org.springframework.core.convert.converter.Converter} + * + * @return {@link Class} + */ + Class converterClass() default Void.class; +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/IntegrationEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/IntegrationEvent.java new file mode 100644 index 0000000..1700d63 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/IntegrationEvent.java @@ -0,0 +1,34 @@ +package org.netcorepal.cap4j.ddd.application.event.annotation; + +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author binking338 + * @date 2024/8/27 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface IntegrationEvent { + public static final String NONE_SUBSCRIBER = "[none]"; + + /** + * 集成事件名称 + * 通常作为MQ topic名称 + * + * @return + */ + String value() default ""; + + /** + * 订阅者 + * 通常作为MQ consumer group名称 + * + * @return + */ + String subscriber() default NONE_SUBSCRIBER; +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java new file mode 100644 index 0000000..b4220c1 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java @@ -0,0 +1,105 @@ +package org.netcorepal.cap4j.ddd.application.event.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.domain.event.EventPublisher; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventAttachedTransactionCommittedEvent; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventSupervisor; +import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; +import org.netcorepal.cap4j.ddd.domain.event.EventRecord; +import org.netcorepal.cap4j.ddd.domain.event.EventRecordRepository; +import org.netcorepal.cap4j.ddd.share.DomainException; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.OrderUtils; +import org.springframework.transaction.event.TransactionalEventListener; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * 默认事件管理器 + * + * @author binking338 + * @date 2024/8/28 + */ +@RequiredArgsConstructor +public class DefaultIntegrationEventSupervisor implements IntegrationEventSupervisor { + private final EventPublisher eventPublisher; + private final EventRecordRepository eventRecordRepository; + private final List integrationEventInterceptors; + private final ApplicationEventPublisher applicationEventPublisher; + private final String svcName; + + private List sortedIntegrationEventInterceptors = null; + + /** + * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 + * + * @return + */ + protected List getOrderedIntegrationEventInterceptors() { + if (sortedIntegrationEventInterceptors == null) { + sortedIntegrationEventInterceptors = new ArrayList<>(integrationEventInterceptors); + sortedIntegrationEventInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); + } + return sortedIntegrationEventInterceptors; + } + + /** + * 默认事件过期时间(分钟) + * 一天 60*24 = 1440 + */ + private static final int DEFAULT_IntegrationEvent_EXPIRE_MINUTES = 1440; + /** + * 默认事件重试次数 + */ + private static final int DEFAULT_IntegrationEvent_RETRY_TIMES = 200; + + @Override + public void notify(INTEGRATION_EVENT eventPayload, LocalDateTime schedule) { + // 判断集成事件,仅支持集成事件。 + if (eventPayload == null || !eventPayload.getClass().isAnnotationPresent(IntegrationEvent.class)) { + throw new DomainException("事件类型必须为领域事件"); + } + + getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onNotify(eventPayload, schedule)); + + EventRecord event = eventRecordRepository.create(); + event.init(eventPayload, this.svcName, schedule, Duration.ofMinutes(DEFAULT_IntegrationEvent_EXPIRE_MINUTES), DEFAULT_IntegrationEvent_RETRY_TIMES); + getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.prePersist(event)); + eventRecordRepository.save(event); + getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.postPersist(event)); + List events = new ArrayList<>(); + events.add(event); + + IntegrationEventAttachedTransactionCommittedEvent integrationEventAttachedTransactionCommittedEvent + = new IntegrationEventAttachedTransactionCommittedEvent(this, events); + applicationEventPublisher.publishEvent(integrationEventAttachedTransactionCommittedEvent); + } + + @Override + public void notify(INTEGRATION_EVENT eventPayload) { + notify(eventPayload, LocalDateTime.now()); + } + + @Override + public void notify(INTEGRATION_EVENT eventPayload, Duration delay) { + notify(eventPayload, LocalDateTime.now().plus(delay)); + } + + @TransactionalEventListener(fallbackExecution = true, classes = IntegrationEventAttachedTransactionCommittedEvent.class) + public void onTransactionCommitted(IntegrationEventAttachedTransactionCommittedEvent transactionCommittedEvent) { + List events = transactionCommittedEvent.getEvents(); + if (events != null && !events.isEmpty()) { + LocalDateTime now = LocalDateTime.now(); + events.forEach(event -> { + event.markPersist(true); + eventPublisher.publish(event); + }); + } + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java deleted file mode 100644 index 1cdd953..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultMediator.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.netcorepal.cap4j.ddd.application.impl; - -import org.netcorepal.cap4j.ddd.application.Mediator; -import org.netcorepal.cap4j.ddd.application.RequestSupervisor; -import org.netcorepal.cap4j.ddd.domain.event.EventSupervisor; - -import java.time.Duration; -import java.time.LocalDateTime; - -/** - * 默认中介者 - * - * @author binking338 - * @date 2024/8/24 - */ -public class DefaultMediator implements Mediator { - @Override - public Object request(REQUEST request) { - return RequestSupervisor.getInstance().request(request); - } - - @Override - public RESPONSE request(REQUEST request, Class resultClass) { - return RequestSupervisor.getInstance().request(request, resultClass); - } - - @Override - public RESPONSE request(REQUEST request, Class paramClass, Class resultClass) { - return RequestSupervisor.getInstance().request(request, paramClass, resultClass); - } - - @Override - public void notify(EVENT eventPayload) { - EventSupervisor.getInstance().notify(eventPayload); - } - - @Override - public void notify(EVENT eventPayload, Duration delay) { - EventSupervisor.getInstance().notify(eventPayload, delay); - } - - @Override - public void notify(EVENT eventPayload, LocalDateTime schedule) { - EventSupervisor.getInstance().notify(eventPayload, schedule); - } - - @Override - public void notify(EVENT eventPayload, Object entity) { - EventSupervisor.getInstance().notify(eventPayload, entity); - } - - @Override - public void notify(EVENT eventPayload, Object entity, Duration delay) { - EventSupervisor.getInstance().notify(eventPayload, entity, delay); - } - - @Override - public void notify(EVENT eventPayload, Object entity, LocalDateTime schedule) { - EventSupervisor.getInstance().notify(eventPayload, entity, schedule); - } -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java index 71fc22a..e472867 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java @@ -2,12 +2,16 @@ import lombok.RequiredArgsConstructor; import org.netcorepal.cap4j.ddd.application.RequestHandler; +import org.netcorepal.cap4j.ddd.application.RequestInterceptor; import org.netcorepal.cap4j.ddd.application.RequestSupervisor; -import org.netcorepal.cap4j.ddd.share.ClassUtils; +import org.netcorepal.cap4j.ddd.application.command.Command; +import org.netcorepal.cap4j.ddd.application.command.CommandNoneParam; +import org.netcorepal.cap4j.ddd.application.command.CommandNoneParamAndResult; +import org.netcorepal.cap4j.ddd.application.command.CommandNoneResult; +import org.netcorepal.cap4j.ddd.application.query.*; +import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * 默认请求管理器 @@ -17,41 +21,58 @@ */ @RequiredArgsConstructor public class DefaultRequestSupervisor implements RequestSupervisor { - private final List requestHandlers; + private final List> requestHandlers; + private final List> requestInterceptors; - private Map, RequestHandler> requestHandlerMap = null; + private Map, RequestHandler> requestHandlerMap = null; + private Map, List>> requestInterceptorMap = null; public void init() { - if(requestHandlerMap != null){ + if (null != requestHandlerMap) { return; } - synchronized (DefaultRequestSupervisor.class){ - if(requestHandlerMap != null){ + synchronized (DefaultRequestSupervisor.class) { + if (null != requestHandlerMap) { return; } } requestHandlerMap = new HashMap<>(); - for (RequestHandler requestHandler : requestHandlers) { - Class requestPayloadClass = ClassUtils.findMethod( - requestHandler.getClass(), - "exec", - m -> m.getParameterCount() == 1 - ).getParameters()[0].getType(); + requestInterceptorMap = new HashMap<>(); + for (RequestHandler requestHandler : requestHandlers) { + Class requestPayloadClass = ClassUtils.resolveGenericTypeClass( + requestHandler.getClass(), 0, + RequestHandler.class, + Command.class, CommandNoneParam.class, CommandNoneResult.class, CommandNoneParamAndResult.class, + Query.class, QueryNoArgs.class, ListQuery.class, ListQueryNoArgs.class, PageQuery.class); requestHandlerMap.put(requestPayloadClass, requestHandler); } + for (RequestInterceptor requestInterceptor : requestInterceptors) { + Class requestPayloadClass = ClassUtils.resolveGenericTypeClass( + requestInterceptor.getClass(), 0, + RequestInterceptor.class); + List> interceptors = requestInterceptorMap.computeIfAbsent(requestPayloadClass, cls -> new ArrayList<>()); + interceptors.add(requestInterceptor); + } } + @Override - public Object request(PARAM param){ - return request(param, (Class)param.getClass(), Object.class); + public Object send(REQUEST request) { + return send(request, (Class) request.getClass(), Object.class); } + @Override - public RESULT request(PARAM param, Class resultClass) { - return request(param, (Class)param.getClass(), resultClass); + public RESPONSE send(REQUEST request, Class responseClass) { + return send(request, (Class) request.getClass(), responseClass); } @Override - public RESULT request(PARAM param, Class paramClass, Class resultClass) { + public RESPONSE send(REQUEST request, Class requestClass, Class responseClass) { init(); - return (RESULT) requestHandlerMap.get(paramClass).exec(param); + requestInterceptorMap.getOrDefault(requestClass, Collections.emptyList()) + .forEach(interceptor -> ((RequestInterceptor) interceptor).preRequest(request)); + RESPONSE response = ((RequestHandler) requestHandlerMap.get(requestClass)).exec(request); + requestInterceptorMap.getOrDefault(requestClass, Collections.emptyList()) + .forEach(interceptor -> ((RequestInterceptor) interceptor).postRequest(request, response)); + return response; } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactory.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactory.java new file mode 100644 index 0000000..8c6b43a --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactory.java @@ -0,0 +1,31 @@ +package org.netcorepal.cap4j.ddd.domain.aggregate; + +/** + * 聚合工厂 + * + * @author binking338 + * @date 2024/9/3 + */ +public interface AggregateFactory { + + /** + * 创建新聚合实例 + * + * @param initHandler + * @return + */ + ENTITY create(InitHandler initHandler); + + public static interface InitHandler { + public static InitHandler getDefault() { + return new EmptyInitHandler(); + } + + void init(ENTITY entity); + + static class EmptyInitHandler implements InitHandler { + public void init(Object entity) { + } + } + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisor.java new file mode 100644 index 0000000..c1e7298 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisor.java @@ -0,0 +1,32 @@ +package org.netcorepal.cap4j.ddd.domain.aggregate; + +/** + * 聚合工厂管理器 + * + * @author binking338 + * @date 2024/9/3 + */ +public interface AggregateFactorySupervisor { + public static AggregateFactorySupervisor getInstance() { + return AggregateFactorySupervisorSupport.instance; + } + + /** + * 创建新聚合实例 + * + * @param entityClass + * @param + * @return + */ + ENTITY create(Class entityClass); + + /** + * 创建新聚合实例 + * + * @param entityClass + * @param initHandler + * @param + * @return + */ + ENTITY create(Class entityClass, AggregateFactory.InitHandler initHandler); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisorSupport.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisorSupport.java new file mode 100644 index 0000000..58c9df1 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisorSupport.java @@ -0,0 +1,15 @@ +package org.netcorepal.cap4j.ddd.domain.aggregate; + +/** + * 聚合工厂管理器配置 + * + * @author binking338 + * @date 2024/9/3 + */ +public class AggregateFactorySupervisorSupport { + static AggregateFactorySupervisor instance; + + public static void configure(AggregateFactorySupervisor aggregateFactorySupervisor) { + instance = aggregateFactorySupervisor; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Specification.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/Specification.java similarity index 80% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Specification.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/Specification.java index 9233c95..55ba630 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Specification.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/Specification.java @@ -1,4 +1,4 @@ -package org.netcorepal.cap4j.ddd.domain.repo; +package org.netcorepal.cap4j.ddd.domain.aggregate; import lombok.Getter; @@ -9,6 +9,15 @@ * @date 2023/8/5 */ public interface Specification { + /** + * 是否强制在事务开启前执行规格校验 + * + * @return + */ + default boolean beforeTransaction() { + return false; + } + /** * 校验实体是否符合规格约束 * diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/SpecificationManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/SpecificationManager.java similarity index 77% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/SpecificationManager.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/SpecificationManager.java index 026ac85..f8ec268 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/SpecificationManager.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/SpecificationManager.java @@ -1,4 +1,4 @@ -package org.netcorepal.cap4j.ddd.domain.repo; +package org.netcorepal.cap4j.ddd.domain.aggregate; /** * 实体规格约束管理器 @@ -9,16 +9,19 @@ public interface SpecificationManager { /** * 校验实体是否符合规格约束 + * * @param entity - * @return * @param + * @return */ - Specification.Result specify(Entity entity); + Specification.Result specifyInTransaction(Entity entity); + /** * 校验实体是否符合规格约束(事务开启前) + * * @param entity - * @return * @param + * @return */ Specification.Result specifyBeforeTransaction(Entity entity); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java index c64746c..0f73862 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java @@ -9,47 +9,53 @@ /** * 聚合信息 + * * @author binking338 * @date 2024/8/23 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Aggregate { + public static final String TYPE_ROOT = "root"; + public static final String TYPE_ENTITY = "entity"; + public static final String TYPE_ENUM = "enum"; + public static final String TYPE_REPOSITORY = "repository"; + public static final String TYPE_DOMAIN_EVENT = "domain-event"; + public static final String TYPE_SPECIFICATION = "specification"; + /** - * 实体名称 + * 所属聚合 + * * @return */ - @AliasFor("entity") - String value() default ""; + String aggregate() default ""; /** - * 实体名称 + * 元素名称 + * * @return */ - @AliasFor("value") - String entity() default ""; + String name() default ""; /** - * 是否聚合根 + * 元素类型 + * root、entity、domain-event、specification、repository + * * @return */ - boolean root() default false; + String type() default ""; /** * 实体描述 + * * @return */ String description() default ""; /** - * 所属聚合 - * @return - */ - String aggregate() default ""; - - /** - * 归属实体名称 + * 关联元素名称 + * * @return */ - String parentEntity() default ""; + String[] relevant() default {}; } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/AbstractSpecification.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/AbstractSpecification.java new file mode 100644 index 0000000..3ecabde --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/AbstractSpecification.java @@ -0,0 +1,14 @@ +package org.netcorepal.cap4j.ddd.domain.aggregate.impl; + +import org.netcorepal.cap4j.ddd.domain.aggregate.Specification; + +/** + * 默认实体规约抽象类 + * + * @author binking338 + * @date 2023/8/13 + */ +public abstract class AbstractSpecification implements Specification { + + public abstract Result specify(Entity entity); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java new file mode 100644 index 0000000..74eaf2c --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java @@ -0,0 +1,74 @@ +package org.netcorepal.cap4j.ddd.domain.aggregate.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactorySupervisor; +import org.netcorepal.cap4j.ddd.share.DomainException; +import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 默认聚合工厂管理器 + * + * @author binking338 + * @date 2024/9/3 + */ +@RequiredArgsConstructor +public class DefaultAggregateFactorySupervisor implements AggregateFactorySupervisor { + private final List> factories; + + private Map, AggregateFactory> factoryMap = null; + + public void init() { + if (null != factoryMap) { + return; + } + synchronized (this) { + if (null != factoryMap) { + return; + } + factoryMap = new HashMap<>(); + factories.forEach(factory -> { + + factoryMap.put( + ClassUtils.resolveGenericTypeClass( + factory.getClass(), 0, + AggregateFactory.class + ), + factory + ); + + }); + } + } + + @Override + public ENTITY create(Class entityClass) { + return create(entityClass, null); + } + + @Override + public ENTITY create(Class entityClass, AggregateFactory.InitHandler initHandler) { + AggregateFactory factory = factoryMap.computeIfAbsent(entityClass, (cls) -> (i) -> { + try { + ENTITY entity = (ENTITY) entityClass.newInstance(); + if (null != i) { + i.init(entity); + } + return entity; + } catch (Exception e) { + throw new DomainException("聚合实例创建异常", e); + } + }); + + Object instance = ((AggregateFactory) factory).create( + null != initHandler + ? (AggregateFactory.InitHandler) initHandler + : AggregateFactory.InitHandler.getDefault() + ); + return (ENTITY) instance; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultSpecificationManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultSpecificationManager.java new file mode 100644 index 0000000..4d168a4 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultSpecificationManager.java @@ -0,0 +1,85 @@ +package org.netcorepal.cap4j.ddd.domain.aggregate.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.domain.aggregate.Specification; +import org.netcorepal.cap4j.ddd.domain.aggregate.SpecificationManager; +import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.OrderUtils; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * 默认实体规约管理器 + * + * @author binking338 + * @date 2023/8/13 + */ +@RequiredArgsConstructor +public class DefaultSpecificationManager implements SpecificationManager { + protected final List> specifications; + protected Map, List>> specificationMap; + + public void init() { + if (null != specificationMap) { + return; + } + synchronized (this) { + if (null != specificationMap) { + return; + } + specificationMap = new java.util.HashMap<>(); + specifications.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE)) + ); + for (Specification specification : specifications) { + Class entityClass = ClassUtils.resolveGenericTypeClass( + specification.getClass(), 0, + AbstractSpecification.class, Specification.class + ); + if (!specificationMap.containsKey(entityClass)) { + specificationMap.put(entityClass, new java.util.ArrayList<>()); + } + List> specificationList = specificationMap.get(entityClass); + specificationList.add(specification); + } + } + } + + @Override + public Specification.Result specifyInTransaction(Entity entity) { + init(); + List> specifications = specificationMap.get(entity.getClass()); + if (specifications != null) { + for (Specification specification : specifications) { + if (specification.beforeTransaction()) { + continue; + } + Specification.Result result = ((Specification) specification).specify(entity); + if (!result.isPassed()) { + return result; + } + } + } + return Specification.Result.pass(); + } + + @Override + public Specification.Result specifyBeforeTransaction(Entity entity) { + init(); + List> specifications = specificationMap.get(entity.getClass()); + if (specifications != null) { + for (Specification specification : specifications) { + if (!specification.beforeTransaction()) { + continue; + } + Specification.Result result = ((Specification) specification).specify(entity); + if (!result.isPassed()) { + return result; + } + } + } + return Specification.Result.pass(); + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommitingEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommitingEvent.java new file mode 100644 index 0000000..048e120 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommitingEvent.java @@ -0,0 +1,28 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.util.List; + +/** + * 领域事件所在事务正在提交事件 + * + * @author binking338 + * @date 2024/8/28 + */ +public class DomainEventAttachedTransactionCommitingEvent extends ApplicationEvent { + @Getter + List events; + + /** + * Create a new {@code ApplicationEvent}. + * + * @param source the object on which the event initially occurred or with + * which the event is associated (never {@code null}) + */ + public DomainEventAttachedTransactionCommitingEvent(Object source, List events) { + super(source); + this.events = events; + } +} \ No newline at end of file diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittedEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittedEvent.java new file mode 100644 index 0000000..d428c53 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittedEvent.java @@ -0,0 +1,28 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.util.List; + +/** + * 领域事件所在事务成功提交事件 + * + * @author binking338 + * @date 2024/8/28 + */ +public class DomainEventAttachedTransactionCommittedEvent extends ApplicationEvent { + @Getter + List events; + + /** + * Create a new {@code ApplicationEvent}. + * + * @param source the object on which the event initially occurred or with + * which the event is associated (never {@code null}) + */ + public DomainEventAttachedTransactionCommittedEvent(Object source, List events) { + super(source); + this.events = events; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptor.java new file mode 100644 index 0000000..f1967f4 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptor.java @@ -0,0 +1,28 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import org.netcorepal.cap4j.ddd.share.EventInterceptor; + +import java.time.LocalDateTime; + +/** + * 领域事件拦截器 + * + * @author binking338 + * @date 2024/8/27 + */ +public interface DomainEventInterceptor extends EventInterceptor { + /** + * 附加 + * @param eventPayload + * @param entity + * @param schedule + */ + void onAttach(Object eventPayload, Object entity, LocalDateTime schedule); + + /** + * 解除附加 + * @param eventPayload + * @param entity + */ + void onDetach(Object eventPayload, Object entity); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventPublisher.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventPublisher.java deleted file mode 100644 index 512d958..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventPublisher.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.event; - -import org.springframework.messaging.Message; - -/** - * 领域事件发布接口 - * - * @author binking338 - * @date 2023/8/5 - */ -public interface DomainEventPublisher { - - /** - * 发布事件 - * @param message - * @param event - */ - void publish(Message message, EventRecord event); -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSubscriberManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSubscriberManager.java deleted file mode 100644 index 88c9eed..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSubscriberManager.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.event; - -/** - * 领域事件订阅管理器接口 - * - * @author binking338 - * @date 2023/8/13 - */ -public interface DomainEventSubscriberManager { - void trigger(Event eventPayload); - - boolean hasSubscriber(Class eventClass); -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java index 4a9e2ef..810311a 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java @@ -16,85 +16,67 @@ public interface DomainEventSupervisor { * @return 领域事件管理器 */ static DomainEventSupervisor getInstance() { - return DomainEventSupervisorConfiguration.domainEventSupervisor; + return DomainEventSupervisorSupport.instance; } /** - * 附加事件 - * @param eventPayload 事件对象 + * 附加领域事件 + * @param domainEventPayload 领域事件消息体 */ - void attach(Object eventPayload); + void attach(DOMAIN_EVENT domainEventPayload); /** - * 附加事件 - * @param eventPayload 事件对象 + * 附加领域事件 + * @param domainEventPayload 领域事件消息体 * @param delay 延迟发送 */ - void attach(Object eventPayload, Duration delay); + void attach(DOMAIN_EVENT domainEventPayload, Duration delay); /** - * 附加事件 - * @param eventPayload 事件对象 + * 附加领域事件 + * @param domainEventPayload 领域事件消息体 * @param schedule 指定时间发送 */ - void attach(Object eventPayload, LocalDateTime schedule); + void attach(DOMAIN_EVENT domainEventPayload, LocalDateTime schedule); /** - * 附加事件 - * @param eventPayload 事件对象 - * @param entity 绑定实体,该实体对象进入持久化上下文才会触发事件分发 + * 附加领域事件 + * @param domainEventPayload 领域事件消息体 + * @param entity 绑定实体,该实体对象进入持久化上下文且事务提交时才会触发领域事件分发 */ - void attach(Object eventPayload, Object entity); + void attach(DOMAIN_EVENT domainEventPayload, ENTITY entity); /** - * 附加事件 - * @param eventPayload 事件对象 - * @param entity 绑定实体,该实体对象进入持久化上下文才会触发事件分发 + * 附加领域事件 + * @param domainEventPayload 领域事件消息体 + * @param entity 绑定实体,该实体对象进入持久化上下文且事务提交时才会触发领域事件分发 * @param delay 延迟发送 */ - void attach(Object eventPayload, Object entity, Duration delay); + void attach(DOMAIN_EVENT domainEventPayload, ENTITY entity, Duration delay); /** - * 附加事件 - * @param eventPayload 事件对象 - * @param entity 绑定实体,该实体对象进入持久化上下文才会触发事件分发 + * 附加领域事件 + * @param domainEventPayload 领域事件消息体 + * @param entity 绑定实体,该实体对象进入持久化上下文且事务提交时才会触发领域事件分发 * @param schedule 指定时间发送 */ - void attach(Object eventPayload, Object entity, LocalDateTime schedule); + void attach(DOMAIN_EVENT domainEventPayload, ENTITY entity, LocalDateTime schedule); /** - * 剥离事件 - * @param eventPayload 事件对象 + * 剥离领域事件 + * @param domainEventPayload 领域事件消息体 */ - void detach(Object eventPayload); + void detach(DOMAIN_EVENT domainEventPayload); /** - * 剥离事件 - * @param eventPayload 事件对象 + * 剥离领域事件 + * @param domainEventPayload 领域事件消息体 * @param entity 关联实体 */ - void detach(Object eventPayload, Object entity); - /** - * 重置事件 - */ - void reset(); - - /** - * 弹出事件列表 - * @return 事件列表 - */ - Set popEvents(); - - /** - * 弹出实体绑定的事件列表 - * @param entity 关联实体 - * @return 事件列表 - */ - public Set popEvents(Object entity); + void detach(DOMAIN_EVENT domainEventPayload, ENTITY entity); /** - * 获取发送事件 - * @param eventPayload 事件对象 - * @return 发送时间 + * 发布附加到指定实体以及所有未附加到实体的领域事件 + * @param entities 指定实体集合 */ - LocalDateTime getDeliverTime(Object eventPayload); + void release(Set entities); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java deleted file mode 100644 index d6a6671..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorConfiguration.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.event; - -import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; - -/** - * 领域事件管理器配置 - * - * @author binking338 - * @date 2024/8/24 - */ -public class DomainEventSupervisorConfiguration { - static DomainEventSupervisor domainEventSupervisor = null; - - static EventSupervisor eventSupervisor = null; - - /** - * 配置领域事件管理器 - * @param domainEventSupervisor {@link DomainEventSupervisor} - */ - public static void configure(DomainEventSupervisor domainEventSupervisor) { - DomainEventSupervisorConfiguration.domainEventSupervisor = domainEventSupervisor; - } - - /** - * 配置事件管理器 - * @param eventSupervisor {@link EventSupervisor} - */ - public static void configure(EventSupervisor eventSupervisor) { - DomainEventSupervisorConfiguration.eventSupervisor = eventSupervisor; - } -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorSupport.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorSupport.java new file mode 100644 index 0000000..390601e --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorSupport.java @@ -0,0 +1,28 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +/** + * 领域事件管理器配置 + * + * @author binking338 + * @date 2024/8/24 + */ +public class DomainEventSupervisorSupport { + static DomainEventSupervisor instance = null; + + /** + * 配置领域事件管理器 + * @param domainEventSupervisor {@link DomainEventSupervisor} + */ + public static void configure(DomainEventSupervisor domainEventSupervisor) { + DomainEventSupervisorSupport.instance = domainEventSupervisor; + } + + /** + * for entity import static + * + * @return + */ + public static DomainEventSupervisor events(){ + return instance; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventMessageInterceptor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventMessageInterceptor.java similarity index 76% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventMessageInterceptor.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventMessageInterceptor.java index 6219ca6..437921f 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventMessageInterceptor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventMessageInterceptor.java @@ -13,23 +13,45 @@ * @author binking338 * @date 2024/8/8 */ -public interface DomainEventMessageInterceptor { +public interface EventMessageInterceptor { /** - * 发送前 + * 提交发布 * * @param message * @return */ - Message beforePublish(Message message); + void initPublish(Message message); /** - * 消费前 + * 发布前 + * + * @param message + */ + void prePublish(Message message); + + /** + * 发布后 + * + * @param message + */ + void postPublish(Message message); + + /** + * 订阅处理前 + * + * @param message + * @return + */ + void preSubscribe(Message message); + + /** + * 订阅处理后 * * @param message * @return */ - Message beforeSubscribe(Message message); + void postSubscribe(Message message); /** * 可变更消息头 diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventPublisher.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventPublisher.java new file mode 100644 index 0000000..79be83f --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventPublisher.java @@ -0,0 +1,19 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import org.netcorepal.cap4j.ddd.domain.event.EventRecord; + +/** + * 事件发布接口 + * + * @author binking338 + * @date 2023/8/5 + */ +public interface EventPublisher { + + /** + * 发布事件 + * + * @param event + */ + void publish(EventRecord event); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecord.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecord.java index bc6a95a..deb7bc4 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecord.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecord.java @@ -1,5 +1,7 @@ package org.netcorepal.cap4j.ddd.domain.event; +import org.springframework.messaging.Message; + import java.time.Duration; import java.time.LocalDateTime; @@ -50,6 +52,24 @@ public interface EventRecord { */ LocalDateTime getNextTryTime(); + /** + * 标记是否持久化 + * @param persist + */ + void markPersist(boolean persist); + + /** + * 是否持久化 + * @return + */ + boolean isPersist(); + + /** + * 创建消息 + * @return + */ + Message getMessage(); + /** * 是否发送中(等待确认结果) * @return @@ -82,16 +102,16 @@ public interface EventRecord { boolean cancelDelivery(LocalDateTime now); /** - * 发生异常 + * 确认时间已发出 * @param now - * @param throwable - * @return */ - void occuredException(LocalDateTime now, Throwable throwable); + void confirmedDelivery(LocalDateTime now); /** - * 确认时间已发出 + * 发生异常 * @param now + * @param throwable + * @return */ - void confirmedDelivery(LocalDateTime now); + void occuredException(LocalDateTime now, Throwable throwable); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecordRepository.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecordRepository.java index d97fdab..ba80e2e 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecordRepository.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecordRepository.java @@ -9,4 +9,5 @@ public interface EventRecordRepository { public EventRecord create(); public void save(EventRecord event); + public EventRecord getById(String id); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSubscriber.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSubscriber.java similarity index 83% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSubscriber.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSubscriber.java index 04643b8..2fec59e 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSubscriber.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSubscriber.java @@ -6,7 +6,7 @@ * @author binking338 * @date 2023/8/5 */ -public interface DomainEventSubscriber { +public interface EventSubscriber { /** * 领域事件消费逻辑 * diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSubscriberManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSubscriberManager.java new file mode 100644 index 0000000..b240dbf --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSubscriberManager.java @@ -0,0 +1,34 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +/** + * 领域事件订阅管理器接口 + * + * @author binking338 + * @date 2023/8/13 + */ +public interface EventSubscriberManager { + /** + * 订阅事件 + * + * @param eventPayloadClass + * @param subscriber + * @return + */ + boolean subscribe(Class eventPayloadClass, EventSubscriber subscriber); + + /** + * 取消订阅 + * + * @param eventPayloadClass + * @param subscriber + * @return + */ + boolean unsubscribe(Class eventPayloadClass, EventSubscriber subscriber); + + /** + * 分发事件到所有订阅者 + * + * @param eventPayload + */ + void dispatch(Object eventPayload); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSupervisor.java deleted file mode 100644 index 6ea35e3..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSupervisor.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.event; - -import java.time.Duration; -import java.time.LocalDateTime; - -/** - * todo: 类描述 - * - * @author binking338 - * @date 2024/8/25 - */ -public interface EventSupervisor { - - static EventSupervisor getInstance() { - return DomainEventSupervisorConfiguration.eventSupervisor; - } - - /** - * 通知事件 - * - * @param eventPayload 事件消息体 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload); - - /** - * 延迟通知事件 - * @param eventPayload 事件消息体 - * @param delay 延迟时长 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload, Duration delay); - - /** - * 定时通知事件 - * @param eventPayload 事件消息体 - * @param schedule 定时时间 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload, LocalDateTime schedule); - - /** - * 通知事件 - * @param eventPayload 事件消息体 - * @param entity 事件关联实体 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload, Object entity); - - /** - * 延迟通知事件 - * @param eventPayload 事件消息体 - * @param entity 事件关联实体 - * @param delay 延迟时长 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload, Object entity, Duration delay); - - /** - * 定时通知事件 - * @param eventPayload 事件消息体 - * @param entity 事件关联实体 - * @param schedule 定时时间 - * @param 事件消息类型 - */ - void notify(EVENT eventPayload, Object entity, LocalDateTime schedule); -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/AutoAttach.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/AutoAttach.java new file mode 100644 index 0000000..7ae3fce --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/AutoAttach.java @@ -0,0 +1,53 @@ +package org.netcorepal.cap4j.ddd.domain.event.annotation; + +import org.netcorepal.cap4j.ddd.domain.repo.PersistType; +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自动附加领域事件 + * 聚合根持久化变更 -> 领域事件 + * + * @author binking338 + * @date 2024/8/29 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AutoAttach { + + /** + * 持久化变更源实体类型 + * + * @return + */ + Class sourceEntityClass() default Void.class; + + /** + * 持久化变更类型 + * + * @return + */ + PersistType[] persistType() default { + PersistType.CREATE, + PersistType.UPDATE, + PersistType.DELETE + }; + + /** + * 延迟发布(秒) + * @return + */ + int delayInSeconds() default 0; + + /** + * 实体 -> 领域事件 转换器 + * {@link org.springframework.core.convert.converter.Converter} + * @return {@link Class} + */ + Class converterClass() default Void.class; + +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java index f3034c6..f8e5268 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java @@ -1,7 +1,5 @@ package org.netcorepal.cap4j.ddd.domain.event.annotation; -import org.springframework.core.annotation.AliasFor; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -16,35 +14,15 @@ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface DomainEvent { - public static final String NONE_SUBSCRIBER = "[none]"; /** - * 集成事件名称 + * 领域事件名称 * @return */ - @AliasFor("intergration") String value() default ""; - /** - * 集成事件名称 - * 该字段非空即为集成事件 - * (通常作为MQ topic名称) - * @return - */ - @AliasFor("value") - String intergration() default ""; - - /** - * 订阅者 - * (通常作为MQ consumer group名称) - * @return - */ - String subscriber() default NONE_SUBSCRIBER; - /** * 事件记录是否持久化 - * 普通领域事件选择记录持久化,则事件发送失败将会有重试机制。 - * 集成事件必然持久化,该字段值无效。 * @return */ boolean persist() default false; diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/AbstractEventSubscriber.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/AbstractEventSubscriber.java new file mode 100644 index 0000000..aaeec58 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/AbstractEventSubscriber.java @@ -0,0 +1,20 @@ +package org.netcorepal.cap4j.ddd.domain.event.impl; + +import org.netcorepal.cap4j.ddd.domain.event.EventSubscriber; + +/** + * 基于RocketMq的领域事件订阅抽象类 + * + * @author binking338 + * @date 2023/8/13 + */ +public abstract class AbstractEventSubscriber implements EventSubscriber { + + /** + * 领域事件消费逻辑 + * + * @param event + */ + @Override + public abstract void onEvent(Event event); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java index bb2835d..98c6bc0 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java @@ -1,7 +1,15 @@ package org.netcorepal.cap4j.ddd.domain.event.impl; -import org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisor; -import org.netcorepal.cap4j.ddd.domain.event.EventSupervisor; +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.domain.event.EventPublisher; +import org.netcorepal.cap4j.ddd.domain.event.*; +import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionCommitingEvent; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionCommittedEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.OrderUtils; +import org.springframework.transaction.event.TransactionalEventListener; import java.time.Duration; import java.time.LocalDateTime; @@ -13,11 +21,40 @@ * @author binking338 * @date 2023/8/13 */ -public class DefaultDomainEventSupervisor implements DomainEventSupervisor, EventSupervisor { +@RequiredArgsConstructor +public class DefaultDomainEventSupervisor implements DomainEventSupervisor { + private final EventRecordRepository eventRecordRepository; + private final List domainEventInterceptors; + private final EventPublisher eventPublisher; + private final ApplicationEventPublisher applicationEventPublisher; + private final String svcName; + + private static final ThreadLocal> TL_EVENT_PAYLOADS = new ThreadLocal>(); private static final ThreadLocal>> TL_ENTITY_EVENT_PAYLOADS = new ThreadLocal>>(); private static final ThreadLocal> TL_EVENT_SCHEDULE_MAP = new ThreadLocal>(); private static final Set EMPTY_EVENT_PAYLOADS = Collections.emptySet(); + /** + * 默认事件过期时间(分钟) + */ + private static final int DEFAULT_EVENT_EXPIRE_MINUTES = 30; + /** + * 默认事件重试次数 + */ + private static final int DEFAULT_EVENT_RETRY_TIMES = 16; + + private List sortedDomainEventInterceptors = null; + /** + * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 + * @return + */ + protected List getOrderedDomainEventInterceptors() { + if(sortedDomainEventInterceptors == null){ + sortedDomainEventInterceptors = new ArrayList<>(domainEventInterceptors); + sortedDomainEventInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); + } + return sortedDomainEventInterceptors; + } @Override public void attach(Object eventPayload) { @@ -37,8 +74,8 @@ public void attach(Object eventPayload, LocalDateTime schedule) { TL_EVENT_PAYLOADS.set(eventPayloads); } eventPayloads.add(eventPayload); - putDeliverTime(eventPayload, schedule); + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onAttach(eventPayload, null, schedule)); } @Override @@ -53,6 +90,8 @@ public void attach(Object eventPayload, Object entity, Duration delay) { @Override public void attach(Object eventPayload, Object entity, LocalDateTime schedule) { + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onAttach(eventPayload, entity, schedule)); + Map> entityEventPayloads = TL_ENTITY_EVENT_PAYLOADS.get(); if (entityEventPayloads == null) { entityEventPayloads = new HashMap<>(); @@ -73,6 +112,7 @@ public void detach(Object eventPayload) { return; } eventPayloads.remove(eventPayload); + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onDetach(eventPayload, null)); } @Override @@ -87,24 +127,101 @@ public void detach(Object eventPayload, Object entity) { } eventPayloads.remove(eventPayload); + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onDetach(eventPayload, null)); } @Override - public void reset() { + public void release(Set entities) { + Set eventPayloads = new HashSet<>(); + eventPayloads.addAll(this.popEvents()); + for (Object entity : entities) { + eventPayloads.addAll(this.popEvents(entity)); + } + List persistedEvents = new ArrayList<>(eventPayloads.size()); + List transientEvents = new ArrayList<>(eventPayloads.size()); + LocalDateTime now = LocalDateTime.now(); + for (Object eventPayload : eventPayloads) { + LocalDateTime deliverTime = this.getDeliverTime(eventPayload); + EventRecord event = eventRecordRepository.create(); + event.init(eventPayload, this.svcName, deliverTime, Duration.ofMinutes(DEFAULT_EVENT_EXPIRE_MINUTES), DEFAULT_EVENT_RETRY_TIMES); + boolean isDelayDeliver = !deliverTime.isAfter(now); + if (!isDomainEventNeedPersist(eventPayload) && isDelayDeliver) { + event.markPersist(false); + transientEvents.add(event); + } else { + event.markPersist(true); + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.prePersist(event)); + eventRecordRepository.save(event); + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.postPersist(event)); + persistedEvents.add(event); + } + } + DomainEventAttachedTransactionCommitingEvent domainEventAttachedTransactionCommitingEvent = new DomainEventAttachedTransactionCommitingEvent(this, transientEvents); + DomainEventAttachedTransactionCommittedEvent domainEventAttachedTransactionCommittedEvent = new DomainEventAttachedTransactionCommittedEvent(this, persistedEvents); + onTransactionCommiting(domainEventAttachedTransactionCommitingEvent); + applicationEventPublisher.publishEvent(domainEventAttachedTransactionCommitingEvent); + applicationEventPublisher.publishEvent(domainEventAttachedTransactionCommittedEvent); + } + + /** + * 判断事件是否需要持久化 + * - 延迟或定时领域事件视情况进行持久化 + * - 显式指定persist=true的领域事件必须持久化 + * @param payload + * @return + */ + protected boolean isDomainEventNeedPersist(Object payload) { + DomainEvent domainEvent = payload == null + ? null + : payload.getClass().getAnnotation(DomainEvent.class); + if (domainEvent != null) { + return domainEvent.persist(); + } else { + return false; + } + } + + protected void onTransactionCommiting(DomainEventAttachedTransactionCommitingEvent domainEventAttachedTransactionCommitingEvent) { + List events = domainEventAttachedTransactionCommitingEvent.getEvents(); + publish(events); + } + + @TransactionalEventListener(fallbackExecution = true, classes = DomainEventAttachedTransactionCommittedEvent.class) + public void onTransactionCommitted(DomainEventAttachedTransactionCommittedEvent domainEventAttachedTransactionCommittedEvent) { + List events = domainEventAttachedTransactionCommittedEvent.getEvents(); + publish(events); + } + + private void publish(List events) { + if (events != null && !events.isEmpty()) { + events.forEach(event -> { + eventPublisher.publish(event); + }); + } + } + + public static void reset() { TL_EVENT_PAYLOADS.remove(); TL_ENTITY_EVENT_PAYLOADS.remove(); TL_EVENT_SCHEDULE_MAP.remove(); } - @Override - public Set popEvents() { + /** + * 弹出事件列表 + * @return 事件列表 + */ + protected Set popEvents() { Set eventPayloads = TL_EVENT_PAYLOADS.get(); TL_EVENT_PAYLOADS.remove(); return eventPayloads != null ? eventPayloads : EMPTY_EVENT_PAYLOADS; } - @Override - public Set popEvents(Object entity) { + /** + * 弹出实体绑定的事件列表 + * @param entity 关联实体 + * @return 事件列表 + */ + protected Set popEvents(Object entity) { Map> entityEventPayloads = TL_ENTITY_EVENT_PAYLOADS.get(); if(entityEventPayloads == null || !entityEventPayloads.containsKey(entity)){ return EMPTY_EVENT_PAYLOADS; @@ -113,6 +230,11 @@ public Set popEvents(Object entity) { return eventPayloads != null ? eventPayloads : EMPTY_EVENT_PAYLOADS; } + /** + * 记录事件发送时间 + * @param eventPayload + * @param schedule + */ protected void putDeliverTime(Object eventPayload, LocalDateTime schedule) { Map eventScheduleMap = TL_EVENT_SCHEDULE_MAP.get(); if (eventScheduleMap == null) { @@ -122,7 +244,6 @@ protected void putDeliverTime(Object eventPayload, LocalDateTime schedule) { eventScheduleMap.put(eventPayload, schedule); } - @Override public LocalDateTime getDeliverTime(Object eventPayload) { Map eventScheduleMap = TL_EVENT_SCHEDULE_MAP.get(); if (eventScheduleMap != null && eventScheduleMap.containsKey(eventPayload)) { @@ -131,35 +252,4 @@ public LocalDateTime getDeliverTime(Object eventPayload) { return LocalDateTime.now(); } } - - - @Override - public void notify(EVENT eventPayload) { - attach(eventPayload); - } - - @Override - public void notify(EVENT eventPayload, Duration delay) { - attach(eventPayload, delay); - } - - @Override - public void notify(EVENT eventPayload, LocalDateTime schedule) { - attach(eventPayload, schedule); - } - - @Override - public void notify(EVENT eventPayload, Object entity) { - attach(eventPayload, entity); - } - - @Override - public void notify(EVENT eventPayload, Object entity, Duration delay) { - attach(eventPayload, entity, delay); - } - - @Override - public void notify(EVENT eventPayload, Object entity, LocalDateTime schedule) { - attach(eventPayload, entity, schedule); - } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java new file mode 100644 index 0000000..2c1383d --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java @@ -0,0 +1,234 @@ +package org.netcorepal.cap4j.ddd.domain.event.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.event.*; +import org.netcorepal.cap4j.ddd.domain.event.*; +import org.netcorepal.cap4j.ddd.share.DomainException; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.OrderUtils; +import org.springframework.messaging.Message; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import static org.netcorepal.cap4j.ddd.share.Constants.*; + +/** + * 默认事件发布器 + * + * @author binking338 + * @date 2023/8/13 + */ +@RequiredArgsConstructor +public class DefaultEventPublisher implements EventPublisher { + private final EventSubscriberManager eventSubscriberManager; + private final List integrationEventPublisheres; + private final EventRecordRepository eventRecordRepository; + private final List eventMessageInterceptors; + private final List domainEventInterceptors; + private final List integrationEventInterceptors; + private final int threadPoolsize; + + private ScheduledExecutorService executor = null; + + public void init() { + if (null != this.executor) { + return; + } + synchronized (this) { + if (null != this.executor) { + return; + } + this.executor = Executors.newScheduledThreadPool(threadPoolsize); + } + } + + /** + * 发布事件 + * + * @param event + */ + public void publish(EventRecord event) { + init(); + + Message message = event.getMessage(); + // 事件消息拦截器 - 初始化 + getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.initPublish(message)); + + // 填入消息头 + String eventType = ((String) message.getHeaders().getOrDefault(HEADER_KEY_CAP4J_EVENT_TYPE, null)); + Duration delay = Duration.ZERO; + if (message.getHeaders().containsKey(HEADER_KEY_CAP4J_SCHEDULE)) { + LocalDateTime scheduleAt = LocalDateTime.ofEpochSecond((Long) message.getHeaders().get(HEADER_KEY_CAP4J_SCHEDULE), 0, ZoneOffset.UTC); + if (scheduleAt != null) { + delay = Duration.between(LocalDateTime.now(), scheduleAt); + } + } + + // 根据事件类型,选择不同的发布方式 + switch (eventType) { + case HEADER_VALUE_CAP4J_EVENT_TYPE_INTEGRATION: + if (delay.isNegative() || delay.isZero()) { + internalPublish4IntegrationEvent(event); +// executor.submit(() -> { +// internalPublish4IntegrationEvent(event); +// }); + } else { + executor.schedule(() -> { + internalPublish4IntegrationEvent(event); + }, delay.getSeconds(), TimeUnit.SECONDS); + } + break; + case HEADER_VALUE_CAP4J_EVENT_TYPE_DOMAIN: + default: + boolean persist = (Boolean) message.getHeaders().getOrDefault(HEADER_KEY_CAP4J_PERSIST, false); + if (persist) { + if (delay.isNegative() || delay.isZero()) { + internalPublish4DomainEvent(event); +// executor.submit(() -> { +// internalPublish4DomainEvent(event); +// }); + } else { + executor.schedule(() -> { + internalPublish4DomainEvent(event); + }, delay.getSeconds(), TimeUnit.SECONDS); + } + } else { + internalPublish4DomainEvent(event); + } + break; + } + } + + /** + * 内部发布实现 - 领域事件 + * + * @param event + */ + protected void internalPublish4DomainEvent(EventRecord event) { + try { + Message message = event.getMessage(); + boolean persist = (Boolean) message.getHeaders().getOrDefault(HEADER_KEY_CAP4J_PERSIST, false); + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.preRelease(event)); + getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.prePublish(message)); + // 进程内消息 + LocalDateTime now = LocalDateTime.now(); + eventSubscriberManager.dispatch(event.getPayload()); + event.confirmedDelivery(now); + if (persist) { + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.prePersist(event)); + eventRecordRepository.save(event); + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.postPersist(event)); + } + getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.postPublish(message)); + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.postRelease(event)); + } catch (Exception ex) { + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onException(ex, event)); + } + } + + /** + * 内部发布实现 - 集成事件 + * + * @param event + */ + protected void internalPublish4IntegrationEvent(EventRecord event) { + try { + getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.preRelease(event)); + getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.prePublish(event.getMessage())); + + integrationEventPublisheres.forEach(integrationEventPublisher -> integrationEventPublisher.publish(event, new IntegrationEventSendPublishCallback(getOrderedEventMessageInterceptors(), getOrderedIntegrationEventInterceptors(), eventRecordRepository))); + + } catch (Exception ex) { + throw new DomainException(String.format("集成事件发布失败: %s", event.getId()), ex); + } + } + + @RequiredArgsConstructor + public static class IntegrationEventSendPublishCallback implements IntegrationEventPublisher.PublishCallback { + private final List orderedEventMessageInterceptors; + private final List orderedIntegrationEventInterceptor; + private final EventRecordRepository eventRecordRepository; + + @Override + public void onSuccess(EventRecord event) { + LocalDateTime now = LocalDateTime.now(); + // 修改事件消费状态 + event.confirmedDelivery(now); + + orderedIntegrationEventInterceptor.forEach(interceptor -> interceptor.prePersist(event)); + eventRecordRepository.save(event); + orderedIntegrationEventInterceptor.forEach(interceptor -> interceptor.postPersist(event)); + + orderedEventMessageInterceptors.forEach(interceptor -> interceptor.postPublish(event.getMessage())); + orderedIntegrationEventInterceptor.forEach(interceptor -> interceptor.postRelease(event)); + } + + @Override + public void onException(EventRecord event, Throwable throwable) { + LocalDateTime now = LocalDateTime.now(); + // 修改事件异常状态 + event.occuredException(now, throwable); + + orderedIntegrationEventInterceptor.forEach(interceptor -> interceptor.prePersist(event)); + eventRecordRepository.save(event); + orderedIntegrationEventInterceptor.forEach(interceptor -> interceptor.postPersist(event)); + + orderedIntegrationEventInterceptor.forEach(interceptor -> interceptor.onException(throwable, event)); + } + } + + + private List orderedEventMessageInterceptors = null; + + /** + * 获取排序后的事件消息拦截器 + * 基于{@link org.springframework.core.annotation.Order} + * + * @return + */ + private List getOrderedEventMessageInterceptors() { + if (orderedEventMessageInterceptors == null) { + orderedEventMessageInterceptors = new ArrayList<>(eventMessageInterceptors); + orderedEventMessageInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); + } + return orderedEventMessageInterceptors; + } + + private List sortedDomainEventInterceptors = null; + + /** + * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 + * + * @return + */ + protected List getOrderedDomainEventInterceptors() { + if (sortedDomainEventInterceptors == null) { + sortedDomainEventInterceptors = new ArrayList<>(domainEventInterceptors); + sortedDomainEventInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); + } + return sortedDomainEventInterceptors; + } + + private List sortedIntegrationEventInterceptors = null; + + /** + * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 + * + * @return + */ + protected List getOrderedIntegrationEventInterceptors() { + if (sortedIntegrationEventInterceptors == null) { + sortedIntegrationEventInterceptors = new ArrayList<>(integrationEventInterceptors); + sortedIntegrationEventInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); + } + return sortedIntegrationEventInterceptors; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java new file mode 100644 index 0000000..19885d3 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java @@ -0,0 +1,127 @@ +package org.netcorepal.cap4j.ddd.domain.event.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.RequestSupervisor; +import org.netcorepal.cap4j.ddd.domain.event.EventSubscriber; +import org.netcorepal.cap4j.ddd.domain.event.EventSubscriberManager; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventSupervisor; +import org.netcorepal.cap4j.ddd.application.event.annotation.AutoNotify; +import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRequest; +import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; +import org.netcorepal.cap4j.ddd.share.misc.ScanUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.OrderUtils; +import org.springframework.core.convert.converter.Converter; + +import java.time.Duration; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * 基于RocketMq的领域事件订阅管理器 + * + * @author binking338 + * @date 2023/8/13 + */ +@RequiredArgsConstructor +public class DefaultEventSubscriberManager implements EventSubscriberManager { + private final List> subscribers; + private final ApplicationEventPublisher applicationEventPublisher; + private final String scanPath; + private Map, List>> subscriberMap = null; + + public void init() { + if (null != subscriberMap) { + return; + } + synchronized (this) { + if (null != subscriberMap) { + return; + } + subscriberMap = new java.util.HashMap<>(); + subscribers.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE)) + ); + for (EventSubscriber subscriber : subscribers) { + Class eventClass = ClassUtils.resolveGenericTypeClass( + subscriber.getClass(), 0, + AbstractEventSubscriber.class, EventSubscriber.class); + subscribe(eventClass, subscriber); + } + // 领域事件 + ScanUtils.findDomainEventClasses(scanPath).forEach(domainEventClass -> { + // 自动实现 Spring EventListener 适配 + subscribe(domainEventClass, applicationEventPublisher::publishEvent); +// +// // 自动实现 Event -> Request 转发 +// if( null != domainEventClass.getAnnotation(AutoRequest.class)){ +// AutoRequest autoRequest = domainEventClass.getAnnotation(AutoRequest.class); +// Class converterClass = null; +// if (Converter.class.isAssignableFrom(autoRequest.converterClass())) { +// converterClass = autoRequest.converterClass(); +// } +// Converter converter = ClassUtils.newConverterInstance(domainEventClass, autoRequest.requestClass(), converterClass); +// subscribe(domainEventClass, domainEvent -> RequestSupervisor.getInstance().request(converter.convert(domainEvent))); +// } + }); + // 集成事件 + ScanUtils.findIntegrationEventClasses(scanPath).forEach(integrationEventClass -> { + // 自动实现 DomainEvent -> IntegrationEvent 适配 + if (null != integrationEventClass.getAnnotation(AutoNotify.class)) { + AutoNotify autoNotify = integrationEventClass.getAnnotation(AutoNotify.class); + Class converterClass = null; + if (Converter.class.isAssignableFrom(integrationEventClass)) { + converterClass = integrationEventClass; + } + if (Converter.class.isAssignableFrom(autoNotify.converterClass())) { + converterClass = autoNotify.converterClass(); + } + Converter converter = ClassUtils.newConverterInstance(autoNotify.sourceDomainEventClass(), integrationEventClass, converterClass); + subscribe(autoNotify.sourceDomainEventClass(), domainEvent -> IntegrationEventSupervisor.getInstance().notify(converter.convert(domainEvent), Duration.ofSeconds(autoNotify.delayInSeconds()))); + } + + // 自动实现 Event -> Request 转发 + if (null != integrationEventClass.getAnnotation(AutoRequest.class)) { + AutoRequest autoRequest = integrationEventClass.getAnnotation(AutoRequest.class); + Class converterClass = null; + if (Converter.class.isAssignableFrom(autoRequest.converterClass())) { + converterClass = autoRequest.converterClass(); + } + Converter converter = ClassUtils.newConverterInstance(integrationEventClass, autoRequest.targetRequestClass(), converterClass); + subscribe(integrationEventClass, integrationEvent -> RequestSupervisor.getInstance().send(converter.convert(integrationEvent))); + } + }); + } + } + + + @Override + public boolean subscribe(Class eventPayloadClass, EventSubscriber subscriber) { + List> subscribers = + subscriberMap.computeIfAbsent(eventPayloadClass, k -> new java.util.ArrayList>()); + return subscribers.add(subscriber); + } + + @Override + public boolean unsubscribe(Class eventPayloadClass, EventSubscriber subscriber) { + List> subscribers = subscriberMap.get(eventPayloadClass); + if (subscribers == null) { + return false; + } + return subscribers.remove(subscriber); + } + + + @Override + public void dispatch(Object eventPayload) { + init(); + List> subscribersForEvent = subscriberMap.get(eventPayload.getClass()); + if (subscribersForEvent == null || subscribersForEvent.isEmpty()) { + return; + } + for (EventSubscriber subscriber : subscribersForEvent) { + ((EventSubscriber) subscriber).onEvent(eventPayload); + } + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistListener.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistListener.java index 8343038..9d23e1b 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistListener.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistListener.java @@ -11,25 +11,8 @@ public interface PersistListener { /** * 持久化变更 * @param entity + * @param type */ - void onChange(Entity entity); - - /** - * 新增实体时 - * @param entity - */ - void onCreate(Entity entity); - - /** - * 更新实体时 - * @param entity - */ - void onUpdate(Entity entity); - - /** - * 删除实体时 - * @param entity - */ - void onDelete(Entity entity); + void onChange(Entity entity, PersistType type); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistListenerManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistListenerManager.java index ba061d1..fc07f75 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistListenerManager.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistListenerManager.java @@ -7,11 +7,5 @@ * @date 2024/1/31 */ public interface PersistListenerManager { - void onChange(Entity entity); - - void onCreate(Entity entity); - - void onUpdate(Entity entity); - - void onDelete(Entity entity); + void onChange(Entity entity, PersistType type); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistType.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistType.java new file mode 100644 index 0000000..05c6612 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/PersistType.java @@ -0,0 +1,13 @@ +package org.netcorepal.cap4j.ddd.domain.repo; + +/** + * 持久化类型 + * + * @author binking338 + * @date 2024/9/1 + */ +public enum PersistType { + CREATE, + UPDATE, + DELETE +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java index de9486a..1d93d76 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java @@ -15,7 +15,7 @@ */ public interface RepositorySupervisor { static RepositorySupervisor getInstance(){ - return RepositorySupervisorConfiguration.instance; + return RepositorySupervisorSupport.instance; } /** @@ -71,6 +71,35 @@ static RepositorySupervisor getInstance(){ */ List findByIds(Class entityClass, Iterable ids); + /** + * 根据条件删除实体 + * + * @param entityClass + * @param condition + * @param limit + * @return + * @param + */ + List remove(Class entityClass, Object condition, int limit); + + /** + * 根据ID删除实体 + * @param entityClass + * @param id + * @return + * @param + */ + Optional removeById(Class entityClass, Object id); + + /** + * 根据ID批量删除实体 + * @param entityClass + * @param ids + * @return + * @param + */ + List removeByIds(Class entityClass, Iterable ids); + /** * 根据条件获取实体计数 * @param entityClass diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisorSupport.java similarity index 86% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisorConfiguration.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisorSupport.java index d0fd565..215bfc5 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisorConfiguration.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisorSupport.java @@ -6,7 +6,7 @@ * @author binking338 * @date 2024/8/25 */ -public class RepositorySupervisorConfiguration { +public class RepositorySupervisorSupport { static RepositorySupervisor instance = null; public static void configure(RepositorySupervisor repositorySupervisor) { diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/AbstractPersistListener.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/AbstractPersistListener.java new file mode 100644 index 0000000..f35956b --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/AbstractPersistListener.java @@ -0,0 +1,24 @@ +package org.netcorepal.cap4j.ddd.domain.repo.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.domain.repo.PersistListener; +import org.netcorepal.cap4j.ddd.domain.repo.PersistType; + +/** + * 默认实体持久化监听抽象类 + * + * @author binking338 + * @date 2024/3/9 + */ +@RequiredArgsConstructor +public abstract class AbstractPersistListener implements PersistListener { + + /** + * 持久化变更 + * + * @param entity + * @param type + */ + @Override + public abstract void onChange(Entity entity, PersistType type); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java new file mode 100644 index 0000000..c2646d1 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java @@ -0,0 +1,111 @@ +package org.netcorepal.cap4j.ddd.domain.repo.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisor; +import org.netcorepal.cap4j.ddd.domain.event.annotation.AutoAttach; +import org.netcorepal.cap4j.ddd.domain.repo.PersistListener; +import org.netcorepal.cap4j.ddd.domain.repo.PersistListenerManager; +import org.netcorepal.cap4j.ddd.domain.repo.PersistType; +import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; +import org.netcorepal.cap4j.ddd.share.misc.ScanUtils; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.OrderUtils; +import org.springframework.core.convert.converter.Converter; + +import java.time.Duration; +import java.util.*; + +/** + * 默认实体持久化监听管理器 + * + * @author binking338 + * @date 2024/3/9 + */ +@RequiredArgsConstructor +public class DefaultPersistListenerManager implements PersistListenerManager { + protected final List> persistListeners; + protected final String scanPath; + + Map, List>> persistListenersMap; + + public void init() { + if (null != persistListenersMap) { + return; + } + synchronized (this) { + if (null != persistListenersMap) { + return; + } + persistListenersMap = new HashMap<>(); + persistListeners.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); + for (PersistListener persistListener : persistListeners) { + Class entityClass = ClassUtils.resolveGenericTypeClass( + persistListener.getClass(), 0, + AbstractPersistListener.class, PersistListener.class + ); + subscribe(entityClass, persistListener); + } + + ScanUtils.findDomainEventClasses(scanPath).stream() + .filter(domainEventClass -> + null != domainEventClass.getAnnotation(AutoAttach.class)) + .forEach(domainEventClass -> { + AutoAttach autoAttach = domainEventClass.getAnnotation(AutoAttach.class); + Class converterClass = null; + if(Converter.class.isAssignableFrom(domainEventClass)){ + converterClass = domainEventClass; + } + if(Converter.class.isAssignableFrom(autoAttach.converterClass())){ + converterClass = autoAttach.converterClass(); + } + Converter converter = ClassUtils.newConverterInstance(autoAttach.sourceEntityClass(), domainEventClass, converterClass); + subscribe(autoAttach.sourceEntityClass(), (e, t) -> { + for (PersistType listenPersistType : autoAttach.persistType()) { + if(listenPersistType == t){ + DomainEventSupervisor.getInstance().attach(converter.convert(e), Duration.ofSeconds(autoAttach.delayInSeconds())); + DomainEventSupervisor.getInstance().release(Collections.singleton(e)); + break; + } + } + }); + }); + } + } + + /** + * 订阅持久化事件监听器 + * @param entityClass + * @param persistListener + */ + private void subscribe(Class entityClass, PersistListener persistListener){ + if (persistListenersMap == null) { + persistListenersMap = new HashMap<>(); + } + if(!persistListenersMap.containsKey(entityClass)){ + persistListenersMap.put(entityClass, new java.util.ArrayList<>()); + } + persistListenersMap.get(entityClass).add(persistListener); + } + + /** + * onCreate & onUpdate & onDelete + * @param entity + * @param type + * @param + */ + @Override + public void onChange(Entity entity, PersistType type) { + init(); + List> listeners = persistListenersMap.get(entity.getClass()); + if (listeners != null) { + for (PersistListener listener : + listeners) { + try { + ((PersistListener)listener).onChange(entity, type); + } catch (Exception ex){ + throw ex; + } + } + } + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisor.java new file mode 100644 index 0000000..681fd4a --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisor.java @@ -0,0 +1,22 @@ +package org.netcorepal.cap4j.ddd.domain.service; + +/** + * 领域服务管理器 + * + * @author binking338 + * @date 2024/9/4 + */ +public interface DomainServiceSupervisor { + + public static DomainServiceSupervisor getInstance(){ + return DomainServiceSupervisorConfiguration.instance; + } + + /** + * 获取领域服务 + * @param domainServiceClass + * @return + * @param + */ + DOMAIN_SERVICE getService(Class domainServiceClass); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisorConfiguration.java new file mode 100644 index 0000000..cc4c58d --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisorConfiguration.java @@ -0,0 +1,15 @@ +package org.netcorepal.cap4j.ddd.domain.service; + +/** + * 领域服务管理 + * + * @author binking338 + * @date 2024/9/4 + */ +public class DomainServiceSupervisorConfiguration { + static DomainServiceSupervisor instance; + + public static void configure(DomainServiceSupervisor domainServiceSupervisor){ + domainServiceSupervisor = instance; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/annotation/DomainService.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/annotation/DomainService.java new file mode 100644 index 0000000..6690d18 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/annotation/DomainService.java @@ -0,0 +1,23 @@ +package org.netcorepal.cap4j.ddd.domain.service.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 领域服务注解 + * + * @author binking338 + * @date 2024/9/3 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DomainService { + /** + * 领域服务名称 + * + * @return + */ + String name() default ""; +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/impl/DefaultDomainServiceSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/impl/DefaultDomainServiceSupervisor.java new file mode 100644 index 0000000..279d73c --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/impl/DefaultDomainServiceSupervisor.java @@ -0,0 +1,26 @@ +package org.netcorepal.cap4j.ddd.domain.service.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.domain.service.DomainServiceSupervisor; +import org.netcorepal.cap4j.ddd.domain.service.annotation.DomainService; +import org.springframework.context.ApplicationContext; + +/** + * 默认领域服务管理器 + * + * @author binking338 + * @date 2024/9/4 + */ +@RequiredArgsConstructor +public class DefaultDomainServiceSupervisor implements DomainServiceSupervisor { + private final ApplicationContext applicationContext; + + @Override + public DOMAIN_SERVICE getService(Class domainServiceClass) { + DOMAIN_SERVICE domainService = applicationContext.getBean(domainServiceClass); + if(null == domainService.getClass().getAnnotation(DomainService.class)) { + return null; + } + return domainService; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java new file mode 100644 index 0000000..b3fda94 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java @@ -0,0 +1,154 @@ +package org.netcorepal.cap4j.ddd.impl; + +import org.netcorepal.cap4j.ddd.Mediator; +import org.netcorepal.cap4j.ddd.application.RequestSupervisor; +import org.netcorepal.cap4j.ddd.application.UnitOfWork; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventSupervisor; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactorySupervisor; +import org.netcorepal.cap4j.ddd.domain.repo.Repository; +import org.netcorepal.cap4j.ddd.domain.repo.RepositorySupervisor; +import org.netcorepal.cap4j.ddd.domain.service.DomainServiceSupervisor; +import org.netcorepal.cap4j.ddd.share.OrderInfo; +import org.netcorepal.cap4j.ddd.share.PageData; +import org.netcorepal.cap4j.ddd.share.PageParam; +import org.springframework.transaction.annotation.Propagation; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +/** + * 默认中介者 + * + * @author binking338 + * @date 2024/8/24 + */ +public class DefaultMediator implements Mediator { + + @Override + public ENTITY create(Class entityClass) { + return AggregateFactorySupervisor.getInstance().create(entityClass); + } + + @Override + public ENTITY create(Class entityClass, AggregateFactory.InitHandler initHandler) { + return AggregateFactorySupervisor.getInstance().create(entityClass, initHandler); + } + + @Override + public Repository repo(Class entityClass) { + return RepositorySupervisor.getInstance().repo(entityClass); + } + + @Override + public List find(Class entityClass, Object condition, List orders) { + return RepositorySupervisor.getInstance().find(entityClass, condition, orders); + } + + @Override + public Optional findOne(Class entityClass, Object condition) { + return RepositorySupervisor.getInstance().findOne(entityClass, condition); + } + + @Override + public PageData findPage(Class entityClass, Object condition, PageParam pageParam) { + return RepositorySupervisor.getInstance().findPage(entityClass, condition, pageParam); + } + + @Override + public Optional findById(Class entityClass, Object id) { + return RepositorySupervisor.getInstance().findById(entityClass, id); + } + + @Override + public List findByIds(Class entityClass, Iterable ids) { + return RepositorySupervisor.getInstance().findByIds(entityClass, ids); + } + + @Override + public long count(Class entityClass, Object condition) { + return RepositorySupervisor.getInstance().count(entityClass, condition); + } + + @Override + public boolean exists(Class entityClass, Object condition) { + return RepositorySupervisor.getInstance().exists(entityClass, condition); + } + + @Override + public boolean existsById(Class entityClass, Object id) { + return RepositorySupervisor.getInstance().existsById(entityClass, id); + } + + @Override + public DOMAIN_SERVICE getService(Class domainServiceClass) { + return DomainServiceSupervisor.getInstance().getService(domainServiceClass); + } + + @Override + public List remove(Class entityClass, Object condition, int limit) { + return RepositorySupervisor.getInstance().remove(entityClass, condition, limit); + } + + @Override + public Optional removeById(Class entityClass, Object id) { + return RepositorySupervisor.getInstance().removeById(entityClass, id); + } + + @Override + public List removeByIds(Class entityClass, Iterable ids) { + return RepositorySupervisor.getInstance().removeByIds(entityClass, ids); + } + + @Override + public void persist(Object entity) { + UnitOfWork.getInstance().persist(entity); + } + + @Override + public void remove(Object entity) { + UnitOfWork.getInstance().remove(entity); + } + + @Override + public void save() { + UnitOfWork.getInstance().save(); + } + + @Override + public void save(Propagation propagation) { + UnitOfWork.getInstance().save(propagation); + } + + @Override + public Object send(REQUEST request) { + return RequestSupervisor.getInstance().send(request); + } + + @Override + public RESPONSE send(REQUEST request, Class resultClass) { + return RequestSupervisor.getInstance().send(request, resultClass); + } + + @Override + public RESPONSE send(REQUEST request, Class paramClass, Class resultClass) { + return RequestSupervisor.getInstance().send(request, paramClass, resultClass); + } + + @Override + public void notify(INTEGRATION_EVENT integrationEventPayload) { + IntegrationEventSupervisor.getInstance().notify(integrationEventPayload); + } + + @Override + public void notify(INTEGRATION_EVENT integrationEventPayload, Duration delay) { + IntegrationEventSupervisor.getInstance().notify(integrationEventPayload, delay); + } + + @Override + public void notify(INTEGRATION_EVENT integrationEventPayload, LocalDateTime schedule) { + IntegrationEventSupervisor.getInstance().notify(integrationEventPayload, schedule); + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ClassUtils.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ClassUtils.java deleted file mode 100644 index 9972c7d..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ClassUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.netcorepal.cap4j.ddd.share; - -import java.lang.reflect.Method; -import java.util.Objects; -import java.util.function.Predicate; - -/** - * 类型工具类 - * - * @author binking338 - */ -public class ClassUtils { - - /** - * 查找方法 - * @param clazz 查找基于类型 - * @param name 方法名称 - * @param methodPredicate - * @return - */ - public static Method findMethod(Class clazz, String name, Predicate methodPredicate){ - Method[] methods = clazz.getMethods(); - for (Method method : methods) { - if (Objects.equals(method.getName(), name)) { - if(methodPredicate!=null){ - if (methodPredicate.test(method)){ - return method; - } - } else { - return method; - } - } - } - return null; - } -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/Constants.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/Constants.java index 4b792c3..dd5625b 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/Constants.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/Constants.java @@ -7,7 +7,15 @@ * @date 2023/11/2 */ public class Constants { + public static final String HEADER_KEY_CAP4J_EVENT_ID = "cap4j-event-id"; + public static final String HEADER_KEY_CAP4J_EVENT_TYPE = "cap4j-event-type"; + public static final String HEADER_VALUE_CAP4J_EVENT_TYPE_DOMAIN = "domain"; + public static final String HEADER_VALUE_CAP4J_EVENT_TYPE_INTEGRATION = "integration"; + public static final String HEADER_KEY_CAP4J_TIMESTAMP = "cap4j-timestamp"; public static final String HEADER_KEY_CAP4J_SCHEDULE = "cap4j-schedule"; + public static final String HEADER_KEY_CAP4J_PERSIST = "cap4j-persist"; public static final String CONFIG_KEY_4_SVC_NAME = "${spring.application.name:default}"; + public static final String CONFIG_KEY_4_ROCKETMQ_NAME_SERVER = "${rocketmq.name-server:}"; + public static final String CONFIG_KEY_4_ROCKETMQ_MSG_CHARSET = "${rocketmq.msg-charset:UTF-8}"; } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/DomainException.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/DomainException.java index eb75db5..3ddd395 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/DomainException.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/DomainException.java @@ -6,10 +6,11 @@ * @author binking338 * @date 2023/8/15 */ -public class DomainException extends RuntimeException{ +public class DomainException extends RuntimeException { public DomainException(String message) { super(message); } + public DomainException(String message, Throwable innerException) { super(message, innerException); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/EventInterceptor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/EventInterceptor.java new file mode 100644 index 0000000..a37084d --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/EventInterceptor.java @@ -0,0 +1,48 @@ +package org.netcorepal.cap4j.ddd.share; + +import org.netcorepal.cap4j.ddd.domain.event.EventRecord; + +/** + * 事件拦截器 + * + * @author binking338 + * @date 2024/8/30 + */ +public interface EventInterceptor { + + /** + * 持久化前 + * + * @param event + */ + void prePersist(EventRecord event); + + /** + * 持久化后 + * + * @param event + */ + void postPersist(EventRecord event); + + /** + * 发布前 + * + * @param event + */ + void preRelease(EventRecord event); + + /** + * 发布后 + * + * @param event + */ + void postRelease(EventRecord event); + + /** + * 发布异常 + * + * @param throwable + * @param event + */ + void onException(Throwable throwable, EventRecord event); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java new file mode 100644 index 0000000..b9c64f9 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java @@ -0,0 +1,106 @@ +package org.netcorepal.cap4j.ddd.share.misc; + +import lombok.SneakyThrows; +import org.netcorepal.cap4j.ddd.share.DomainException; +import org.springframework.cglib.beans.BeanCopier; +import org.springframework.core.ResolvableType; +import org.springframework.core.convert.converter.Converter; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * 类型工具类 + * + * @author binking338 + */ +public class ClassUtils { + + /** + * 获取指定类或接口泛型参数类型 + * + * @param clazz + * @param typeArgumentIndex + * @param superClasses + * @return + */ + public static Class resolveGenericTypeClass(Class clazz, int typeArgumentIndex, Class... superClasses) { + ParameterizedType parameterizedType = null; + if (Arrays.stream(superClasses).anyMatch( + superClass -> superClass.equals(ResolvableType.forType(clazz.getGenericSuperclass()).toClass())) + ) { + parameterizedType = (ParameterizedType) clazz.getGenericSuperclass(); + } else { + for (Type type : clazz.getGenericInterfaces()) { + if (Arrays.stream(superClasses).anyMatch( + superClass -> superClass.equals(ResolvableType.forType(type).toClass())) + ) { + parameterizedType = (ParameterizedType) type; + } + } + } + if (null == parameterizedType) { + return Object.class; + } + return ResolvableType.forType( + parameterizedType.getActualTypeArguments()[typeArgumentIndex] + ).toClass(); + } + + /** + * 查找方法 + * + * @param clazz 查找基于类型 + * @param name 方法名称 + * @param methodPredicate + * @return + */ + public static Method findMethod(Class clazz, String name, Predicate methodPredicate) { + Method[] methods = clazz.getMethods(); + for (Method method : methods) { + if (Objects.equals(method.getName(), name)) { + if (methodPredicate != null) { + if (methodPredicate.test(method)) { + return method; + } + } else { + return method; + } + } + } + return null; + } + + @SneakyThrows + public static Converter newConverterInstance(Class srcClass, Class descClass, Class converterClass) { + Converter converter = null; + try { + converter = Void.class.equals(converterClass) + ? null + : (Converter) converterClass.newInstance(); + } catch (Exception e) { + throw new DomainException("事件Converter无法实例化", e); + } + if (converter == null) { + BeanCopier copier = BeanCopier.create( + srcClass, + descClass, false); + converter = source -> { + Object dest = null; + try { + dest = descClass.newInstance(); + } catch (Exception e) { + throw new DomainException("无法完成事件自动转换", e); + } + copier.copy(source, dest, null); + return dest; + }; + } + return converter; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ScanUtils.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ScanUtils.java similarity index 50% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ScanUtils.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ScanUtils.java index aac5fcc..3a57db5 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/ScanUtils.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ScanUtils.java @@ -1,6 +1,7 @@ -package org.netcorepal.cap4j.ddd.share; +package org.netcorepal.cap4j.ddd.share.misc; -import lombok.extern.slf4j.Slf4j; +import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; +import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; @@ -11,7 +12,9 @@ import org.springframework.util.ClassUtils; import java.util.HashSet; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; /** * 类扫描工具 @@ -42,10 +45,45 @@ public static Set> scanClass(String scanPath, boolean concrete) { MetadataReader metadataReader = METADATA_READER_FACTORY.getMetadataReader(resource); ClassMetadata classMetadata = metadataReader.getClassMetadata(); if (!concrete || classMetadata.isConcrete()) { - classes.add(Class.forName(classMetadata.getClassName())); + try { + Class cls = Class.forName(classMetadata.getClassName()); + if (null != cls) { + classes.add(cls); + } else { + System.err.println(String.format("无法加载:%s", classMetadata.getClassName())); + } + } catch (Exception ex) { + System.err.println(String.format("无法加载:%s", classMetadata.getClassName())); + } } } } return classes; } + + public static Set> findDomainEventClasses(String scanPath) { + Set> classes = ScanUtils.scanClass(scanPath, true) + .stream().filter(cls -> { + DomainEvent domainEvent = cls.getAnnotation(DomainEvent.class); + if (!Objects.isNull(domainEvent)) { + return true; + } else { + return false; + } + }).collect(Collectors.toSet()); + return classes; + } + + public static Set> findIntegrationEventClasses(String scanPath) { + Set> classes = ScanUtils.scanClass(scanPath, true) + .stream().filter(cls -> { + IntegrationEvent domainEvent = cls.getAnnotation(IntegrationEvent.class); + if (!Objects.isNull(domainEvent)) { + return true; + } else { + return false; + } + }).collect(Collectors.toSet()); + return classes; + } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/TextUtils.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/TextUtils.java similarity index 93% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/TextUtils.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/TextUtils.java index 89187d7..46cd4e1 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/TextUtils.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/TextUtils.java @@ -1,4 +1,4 @@ -package org.netcorepal.cap4j.ddd.share; +package org.netcorepal.cap4j.ddd.share.misc; import org.springframework.core.env.Environment; diff --git a/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/SnowflakeIdentifierGenerator.java b/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/SnowflakeIdentifierGenerator.java similarity index 56% rename from ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/SnowflakeIdentifierGenerator.java rename to ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/SnowflakeIdentifierGenerator.java index d2bd3f1..1da4cd9 100644 --- a/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/SnowflakeIdentifierGenerator.java +++ b/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/SnowflakeIdentifierGenerator.java @@ -1,9 +1,9 @@ -package org.netcorepal.cap4j.ddd.application.distributed; +package org.netcorepal.cap4j.ddd.domain.distributed; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; -import org.netcorepal.cap4j.ddd.application.distributed.snowflake.SnowflakeIdGenerator; +import org.netcorepal.cap4j.ddd.domain.distributed.snowflake.SnowflakeIdGenerator; import java.io.Serializable; @@ -14,10 +14,14 @@ * @date 2024/8/11 */ public class SnowflakeIdentifierGenerator implements IdentifierGenerator { - static SnowflakeIdGenerator snowflakeIdGenerator; + static SnowflakeIdGenerator snowflakeIdGeneratorImpl; + + public static void configure(SnowflakeIdGenerator snowflakeIdGenerator) { + snowflakeIdGeneratorImpl = snowflakeIdGenerator; + } @Override public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException { - return snowflakeIdGenerator.nextId(); + return snowflakeIdGeneratorImpl.nextId(); } } diff --git a/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/snowflake/DefaultSnowflakeWorkerIdDispatcher.java b/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/snowflake/DefaultSnowflakeWorkerIdDispatcher.java similarity index 81% rename from ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/snowflake/DefaultSnowflakeWorkerIdDispatcher.java rename to ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/snowflake/DefaultSnowflakeWorkerIdDispatcher.java index bd55998..4d61b65 100644 --- a/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/snowflake/DefaultSnowflakeWorkerIdDispatcher.java +++ b/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/snowflake/DefaultSnowflakeWorkerIdDispatcher.java @@ -1,4 +1,4 @@ -package org.netcorepal.cap4j.ddd.application.distributed.snowflake; +package org.netcorepal.cap4j.ddd.domain.distributed.snowflake; import lombok.RequiredArgsConstructor; @@ -15,10 +15,10 @@ public class DefaultSnowflakeWorkerIdDispatcher implements SnowflakeWorkerIdDisp @Override public long acquire(Long workerId, Long datacenterId) { - if(workerId == null){ + if (workerId == null) { workerId = this.workerId; } - if(datacenterId == null){ + if (datacenterId == null) { datacenterId = this.datacenterId; } return datacenterId << 5 + workerId; @@ -34,7 +34,7 @@ public boolean pong() { } @Override - public void remind(){ + public void remind() { } } diff --git a/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/snowflake/SnowflakeIdGenerator.java b/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/snowflake/SnowflakeIdGenerator.java similarity index 98% rename from ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/snowflake/SnowflakeIdGenerator.java rename to ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/snowflake/SnowflakeIdGenerator.java index 53244a4..4770307 100644 --- a/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/snowflake/SnowflakeIdGenerator.java +++ b/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/snowflake/SnowflakeIdGenerator.java @@ -1,4 +1,4 @@ -package org.netcorepal.cap4j.ddd.application.distributed.snowflake; +package org.netcorepal.cap4j.ddd.domain.distributed.snowflake; /** * SnowflakeId生成算法 diff --git a/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/snowflake/SnowflakeWorkerIdDispatcher.java b/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/snowflake/SnowflakeWorkerIdDispatcher.java similarity index 65% rename from ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/snowflake/SnowflakeWorkerIdDispatcher.java rename to ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/snowflake/SnowflakeWorkerIdDispatcher.java index 6748848..2356578 100644 --- a/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/snowflake/SnowflakeWorkerIdDispatcher.java +++ b/ddd-distributed-idgenerator-snowflake/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/snowflake/SnowflakeWorkerIdDispatcher.java @@ -1,4 +1,4 @@ -package org.netcorepal.cap4j.ddd.application.distributed.snowflake; +package org.netcorepal.cap4j.ddd.domain.distributed.snowflake; /** * WorkerId调度器 @@ -9,7 +9,8 @@ public interface SnowflakeWorkerIdDispatcher { /** * 获取WorkerId占用 - * @param workerId 指定workerId + * + * @param workerId 指定workerId * @param datacenterId 指定datacenterId * @return */ @@ -17,19 +18,20 @@ public interface SnowflakeWorkerIdDispatcher { /** * 释放WorkerId占用 + * * @return */ void release(); /** * 心跳上报 - * 如果长期失联,需通知运维介入 + * * @return */ boolean pong(); /** - * 心跳失败累计到一定次数,提醒运维或相关人员,以便介入处理 + * 心跳上报如果长期失联,失败累计到一定次数,提醒运维或相关人员,以便介入处理 */ void remind(); } diff --git a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecordImpl.java b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecordImpl.java index de3e1c6..25f995b 100644 --- a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecordImpl.java +++ b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventRecordImpl.java @@ -1,10 +1,17 @@ package org.netcorepal.cap4j.ddd.domain.event; import lombok.extern.slf4j.Slf4j; +import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; import org.netcorepal.cap4j.ddd.domain.event.persistence.Event; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.GenericMessage; import java.time.Duration; import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.UUID; + +import static org.netcorepal.cap4j.ddd.share.Constants.*; /** * 事件记录实现 @@ -15,21 +22,23 @@ @Slf4j public class EventRecordImpl implements EventRecord { private Event event; + private boolean persist = false; + private Message message = null; - public EventRecordImpl(){ + public EventRecordImpl() { event = Event.builder().build(); } - public void resume(Event event){ + public void resume(Event event) { this.event = event; } - public Event getEvent(){ + public Event getEvent() { return event; } @Override - public String toString(){ + public String toString() { return event.toString(); } @@ -40,7 +49,7 @@ public void init(Object payload, String svcName, LocalDateTime now, Duration exp } @Override - public String getId(){ + public String getId() { return event.getEventUuid(); } @@ -64,6 +73,39 @@ public LocalDateTime getNextTryTime() { return event.getNextTryTime(); } + @Override + public void markPersist(boolean persist) { + this.persist = persist; + } + + @Override + public boolean isPersist() { + return persist; + } + + @Override + public Message getMessage() { + if (null != this.message) { + return this.message; + } + this.message = new GenericMessage( + this.getPayload(), + new EventMessageInterceptor.ModifiableMessageHeaders(null, UUID.fromString(this.getId()), null) + ); + boolean isIntegrationEvent = this.getPayload().getClass().getAnnotation(IntegrationEvent.class) != null; + message.getHeaders().put(HEADER_KEY_CAP4J_EVENT_ID, event.getId()); + message.getHeaders().put(HEADER_KEY_CAP4J_EVENT_TYPE, isIntegrationEvent + ? HEADER_VALUE_CAP4J_EVENT_TYPE_INTEGRATION + : HEADER_VALUE_CAP4J_EVENT_TYPE_DOMAIN); + message.getHeaders().put(HEADER_KEY_CAP4J_PERSIST, this.persist); + LocalDateTime now = LocalDateTime.now(); + message.getHeaders().put(HEADER_KEY_CAP4J_TIMESTAMP, now.toEpochSecond(ZoneOffset.UTC)); + if (this.getScheduleTime().isAfter(now)) { + message.getHeaders().put(HEADER_KEY_CAP4J_SCHEDULE, this.getScheduleTime().toEpochSecond(ZoneOffset.UTC)); + } + return message; + } + @Override public boolean isTrying() { return event.isTrying(); diff --git a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/JpaEventRecordRepository.java b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/JpaEventRecordRepository.java index f5dc15b..0321c47 100644 --- a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/JpaEventRecordRepository.java +++ b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/JpaEventRecordRepository.java @@ -1,11 +1,16 @@ package org.netcorepal.cap4j.ddd.domain.event; +import lombok.Getter; import lombok.RequiredArgsConstructor; import org.netcorepal.cap4j.ddd.domain.event.persistence.Event; -import org.netcorepal.cap4j.ddd.domain.event.persistence.EventRepository; +import org.netcorepal.cap4j.ddd.domain.event.persistence.EventJpaRepository; +import org.netcorepal.cap4j.ddd.share.DomainException; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + /** * 基于Jpa的事件记录仓储实现 * @@ -14,7 +19,11 @@ */ @RequiredArgsConstructor public class JpaEventRecordRepository implements EventRecordRepository { - private final EventRepository eventRepository; + private final EventJpaRepository eventJpaRepository; + + @Getter + @PersistenceContext + protected EntityManager entityManager; @Override public EventRecord create() { @@ -25,7 +34,17 @@ public EventRecord create() { @Transactional(propagation = Propagation.REQUIRED) public void save(EventRecord eventRecord) { EventRecordImpl eventRecordImpl = (EventRecordImpl) eventRecord; - Event event = eventRepository.saveAndFlush(eventRecordImpl.getEvent()); + Event event = eventRecordImpl.getEvent(); + event = eventJpaRepository.saveAndFlush(event); + eventRecordImpl.resume(event); + } + + @Override + public EventRecord getById(String id) { + Event event = eventJpaRepository.findOne((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get(Event.F_EVENT_UUID), id)) + .orElseThrow(() -> new DomainException("EventRecord not found")); + EventRecordImpl eventRecordImpl = new EventRecordImpl(); eventRecordImpl.resume(event); + return eventRecordImpl; } } diff --git a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/JpaEventScheduleService.java b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/JpaEventScheduleService.java index 9b8ed16..c873df1 100644 --- a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/JpaEventScheduleService.java +++ b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/JpaEventScheduleService.java @@ -9,25 +9,20 @@ import org.netcorepal.cap4j.ddd.domain.event.persistence.ArchivedEvent; import org.netcorepal.cap4j.ddd.domain.event.persistence.ArchivedEventJpaRepository; import org.netcorepal.cap4j.ddd.domain.event.persistence.Event; -import org.netcorepal.cap4j.ddd.domain.event.persistence.EventRepository; +import org.netcorepal.cap4j.ddd.domain.event.persistence.EventJpaRepository; +import org.netcorepal.cap4j.ddd.share.DomainException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.GenericMessage; import org.springframework.transaction.annotation.Transactional; -import javax.annotation.PostConstruct; import java.time.Duration; import java.time.LocalDateTime; import java.util.Date; import java.util.List; -import java.util.UUID; import java.util.stream.Collectors; -import static org.netcorepal.cap4j.ddd.share.Constants.*; - /** * 事件调度服务 * 失败定时重试 @@ -38,19 +33,17 @@ @RequiredArgsConstructor @Slf4j public class JpaEventScheduleService { - - private final Locker locker; - private final DomainEventPublisher domainEventPublisher; - private final DomainEventMessageInterceptor domainEventMessageInterceptor; + private final EventPublisher eventPublisher; private final EventRecordRepository eventRecordRepository; - private final EventRepository eventRepository; + private final EventJpaRepository eventJpaRepository; private final ArchivedEventJpaRepository archivedEventJpaRepository; + private final Locker locker; private final String svcName; private final String compensationLockerKey; private final String archiveLockerKey; private final boolean enableAddPartition; + private final JdbcTemplate jdbcTemplate; - @PostConstruct public void init() { addPartition(); } @@ -83,7 +76,7 @@ public void compense(int batchSize, int maxConcurrency, Duration interval, Durat if (!locker.acquire(lockerKey, pwd, maxLockDuration)) { return; } - Page events = eventRepository.findAll((root, cq, cb) -> { + Page events = eventJpaRepository.findAll((root, cq, cb) -> { cq.where(cb.or( cb.and( // 【初始状态】 @@ -124,24 +117,14 @@ public void compense(int batchSize, int maxConcurrency, Duration interval, Durat ) { eventRecordImpl.beginDelivery(eventRecordImpl.getNextTryTime()); if (maxTry-- <= 0) { - throw new RuntimeException("疑似死循环"); + throw new DomainException("疑似死循环"); } } eventRecordRepository.save(eventRecordImpl); if (delivering) { - Message message = new GenericMessage( - eventRecordImpl.getPayload(), - new DomainEventMessageInterceptor.ModifiableMessageHeaders(null, UUID.fromString(eventRecordImpl.getId()), null) - ); - if(deliverTime.isAfter(now)){ - message.getHeaders().put(HEADER_KEY_CAP4J_SCHEDULE, deliverTime); - } - if (domainEventMessageInterceptor != null) { - message = domainEventMessageInterceptor.beforePublish(message); - } - final Message finalMessage = message; - domainEventPublisher.publish(finalMessage, eventRecordImpl); + eventRecordImpl.markPersist(true); + eventPublisher.publish(eventRecordImpl); } } } catch (Exception ex) { @@ -176,7 +159,7 @@ public void archive(int expireDays, int batchSize, Duration maxLockDuration) { int failCount = 0; while (true) { try { - Page events = eventRepository.findAll((root, cq, cb) -> { + Page events = eventJpaRepository.findAll((root, cq, cb) -> { cq.where( cb.and( // 【状态】 @@ -216,7 +199,7 @@ public void archive(int expireDays, int batchSize, Duration maxLockDuration) { @Transactional public void migrate(List events, List archivedEvents) { archivedEventJpaRepository.saveAll(archivedEvents); - eventRepository.deleteInBatch(events); + eventJpaRepository.deleteInBatch(events); } public void addPartition() { @@ -228,8 +211,6 @@ public void addPartition() { addPartition("__archived_event", DateUtils.addMonths(now, 1)); } - private final JdbcTemplate jdbcTemplate; - /** * 创建date日期所在月下个月的分区 * @@ -237,7 +218,7 @@ public void addPartition() { * @param date */ private void addPartition(String table, Date date) { - String sql = "alter table `" + table + "` add partition (partition p" + DateFormatUtils.format(date, "yyyyMM") + " values less than (to_days('" + DateFormatUtils.format(DateUtils.addMonths(date, 1), "yyyy-MM") + "-01')) ENGINE=InnoDB)"; + String sql = "alter table " + table + " add partition (partition p" + DateFormatUtils.format(date, "yyyyMM") + " values less than (to_days('" + DateFormatUtils.format(DateUtils.addMonths(date, 1), "yyyy-MM") + "-01')) ENGINE=InnoDB)"; try { jdbcTemplate.execute(sql); } catch (Exception ex) { diff --git a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/ArchivedEvent.java b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/ArchivedEvent.java index 579b88a..a064f3d 100644 --- a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/ArchivedEvent.java +++ b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/ArchivedEvent.java @@ -1,6 +1,5 @@ package org.netcorepal.cap4j.ddd.domain.event.persistence; - import com.alibaba.fastjson.JSON; import lombok.AllArgsConstructor; import lombok.Builder; @@ -38,7 +37,7 @@ public void archiveFrom(Event event) { this.eventType = event.getEventType(); this.data = event.getData(); this.dataType = event.getDataType(); - this.exception =event.getException(); + this.exception = event.getException(); this.createAt = event.getCreateAt(); this.expireAt = event.getExpireAt(); this.eventState = event.getEventState(); diff --git a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java index 6b6db3a..7d35d43 100644 --- a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java +++ b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java @@ -1,6 +1,5 @@ package org.netcorepal.cap4j.ddd.domain.event.persistence; - import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import lombok.AllArgsConstructor; @@ -9,11 +8,12 @@ import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; -import org.netcorepal.cap4j.ddd.share.DomainException; -import org.netcorepal.cap4j.ddd.share.annotation.Retry; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; +import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; +import org.netcorepal.cap4j.ddd.share.DomainException; +import org.netcorepal.cap4j.ddd.share.annotation.Retry; +import org.springframework.core.annotation.AnnotationUtils; import javax.persistence.*; import java.io.PrintWriter; @@ -143,12 +143,12 @@ public boolean cancelDelivery(LocalDateTime now) { } public void occuredException(LocalDateTime now, Throwable ex) { - if(isDelivered()) { + if (isDelivered()) { return; } this.eventState = EventState.EXCEPTION; StringWriter sw = new StringWriter(); - ex.printStackTrace(new PrintWriter(sw,true)); + ex.printStackTrace(new PrintWriter(sw, true)); this.exception = sw.toString(); } @@ -159,11 +159,11 @@ private void loadPayload(Object payload) { this.payload = payload; this.data = JSON.toJSONString(payload); this.dataType = payload.getClass().getName(); - DomainEvent domainEvent = payload == null + IntegrationEvent integrationEvent = payload == null ? null - : payload.getClass().getAnnotation(DomainEvent.class); - if (domainEvent != null) { - this.eventType = domainEvent.intergration(); + : payload.getClass().getAnnotation(IntegrationEvent.class); + if (integrationEvent != null) { + this.eventType = integrationEvent.value(); } Retry retry = payload == null ? null diff --git a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/EventRepository.java b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/EventJpaRepository.java similarity index 71% rename from ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/EventRepository.java rename to ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/EventJpaRepository.java index 0f9a93c..1e4956f 100644 --- a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/EventRepository.java +++ b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/EventJpaRepository.java @@ -9,5 +9,5 @@ * @author binking338 * @date 2023/8/15 */ -public interface EventRepository extends JpaRepository, JpaSpecificationExecutor { +public interface EventJpaRepository extends JpaRepository, JpaSpecificationExecutor { } diff --git a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventPublisher.java b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventPublisher.java deleted file mode 100644 index 994fcd7..0000000 --- a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventPublisher.java +++ /dev/null @@ -1,145 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.event; - -import com.alibaba.fastjson.JSON; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.rocketmq.client.producer.SendCallback; -import org.apache.rocketmq.client.producer.SendResult; -import org.apache.rocketmq.spring.core.RocketMQTemplate; -import org.netcorepal.cap4j.ddd.share.DomainException; -import org.netcorepal.cap4j.ddd.share.TextUtils; -import org.springframework.core.env.Environment; -import org.springframework.messaging.Message; - -import java.time.Duration; -import java.time.LocalDateTime; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import static org.netcorepal.cap4j.ddd.share.Constants.HEADER_KEY_CAP4J_SCHEDULE; - -/** - * 基于RocketMq的领域事件发布器 - * - * @author binking338 - * @date 2023/8/13 - */ -@Slf4j -public class RocketMqDomainEventPublisher implements DomainEventPublisher { - private final RocketMqDomainEventSubscriberManager rocketMqDomainEventSubscriberManager; - private final RocketMQTemplate rocketMQTemplate; - private final EventRecordRepository eventRecordRepository; - private final Environment environment; - private final ScheduledExecutorService executor; - - /** - * 如下配置需配置好,保障RocketMqTemplate被初始化 - * ## rocketmq - * #rocketmq.name-server = myrocket.nameserver:9876 - * #rocketmq.producer.group=${spring.application.name} - * - * @param rocketMqDomainEventSubscriberManager - * @param rocketMQTemplate - * @param eventRecordRepository - */ - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") - public RocketMqDomainEventPublisher( - RocketMqDomainEventSubscriberManager rocketMqDomainEventSubscriberManager, - RocketMQTemplate rocketMQTemplate, - EventRecordRepository eventRecordRepository, - int threadPoolsize, - Environment environment - ) { - this.rocketMqDomainEventSubscriberManager = rocketMqDomainEventSubscriberManager; - this.rocketMQTemplate = rocketMQTemplate; - this.eventRecordRepository = eventRecordRepository; - this.executor = Executors.newScheduledThreadPool(threadPoolsize); - this.environment = environment; - - } - - /** - * 发布事件 - * - * @param event - */ - public void publish(Message message, EventRecord event) { - Duration delay = Duration.ZERO; - if (message.getHeaders().containsKey(HEADER_KEY_CAP4J_SCHEDULE)) { - LocalDateTime scheduleAt = (LocalDateTime) message.getHeaders().get(HEADER_KEY_CAP4J_SCHEDULE); - if (scheduleAt != null) { - delay = Duration.between(LocalDateTime.now(), scheduleAt); - } - } - if (delay.isNegative() || delay.isZero()) { - executor.submit(() -> { - internalPublish(message, event); - }); - } else { - executor.schedule(() -> { - internalPublish(message, event); - }, delay.getSeconds(), TimeUnit.SECONDS); - } - } - - private void internalPublish(Message message, EventRecord event) { - String destination = event.getEventTopic(); - destination = TextUtils.resolvePlaceholderWithCache(destination, environment); - boolean isIntergrationEvent = destination != null && !destination.isEmpty(); - try { - // MQ消息 - if (isIntergrationEvent) { - rocketMQTemplate.asyncSend(destination, message, new DomainEventSendCallback(event, eventRecordRepository, environment)); - } else { - // 进程内消息 - rocketMqDomainEventSubscriberManager.trigger(event.getPayload()); - event.confirmedDelivery(LocalDateTime.now()); - eventRecordRepository.save(event); - } - } catch (Exception ex) { - log.error(String.format("%s发布失败: %s", isIntergrationEvent ? "集成事件" : "领域事件", event.toString()), ex); - } - } - - @RequiredArgsConstructor - @Slf4j - public static class DomainEventSendCallback implements SendCallback { - private final EventRecord event; - private final EventRecordRepository eventRecordRepository; - private final Environment environment; - - @Override - public void onSuccess(SendResult sendResult) { - if (event == null) { - throw new DomainException("集成事件为NULL"); - } - try { - LocalDateTime now = LocalDateTime.now(); - // 修改事件消费状态 - event.confirmedDelivery(now); - eventRecordRepository.save(event); - log.info(String.format("集成事件发送成功, destination=%s, body=%s", TextUtils.resolvePlaceholderWithCache(event.getEventTopic(), environment), JSON.toJSONString(event.getPayload()))); - } catch (Exception ex) { - log.error("本地事件库持久化失败", ex); - } - } - - @Override - public void onException(Throwable throwable) { - if (event == null) { - throw new DomainException("集成事件为NULL"); - } - try { - LocalDateTime now = LocalDateTime.now(); - // 修改事件异常状态 - event.occuredException(now, throwable); - eventRecordRepository.save(event); - log.error(String.format("集成事件发送失败, destination=%s, body=%s", TextUtils.resolvePlaceholderWithCache(event.getEventTopic(), environment), JSON.toJSONString(event.getPayload())), throwable); - } catch (Exception ex) { - log.error("本地事件库持久化失败", ex); - } - } - } -} diff --git a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java deleted file mode 100644 index b5008f7..0000000 --- a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriber.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.event; - -import org.netcorepal.cap4j.ddd.share.ClassUtils; -import org.springframework.core.ResolvableType; - -/** - * 基于RocketMq的领域事件订阅抽象类 - * - * @author binking338 - * @date 2023/8/13 - */ -public abstract class RocketMqDomainEventSubscriber implements DomainEventSubscriber { - /** - * 监听的领域事件类型 - * - * @return - */ - public Class forDomainEventClass() { - return (Class) ClassUtils.findMethod( - this.getClass(), - "onEvent", - m -> m.getParameterCount() == 1 - ).getParameters()[0].getType(); - } - - /** - * 领域事件消费逻辑 - * - * @param event - */ - @Override - public abstract void onEvent(Event event); -} diff --git a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberAdapter.java b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberAdapter.java index 9895178..506d2a2 100644 --- a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberAdapter.java +++ b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberAdapter.java @@ -12,14 +12,15 @@ import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.message.MessageExt; -import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; -import org.netcorepal.cap4j.ddd.share.ScanUtils; -import org.netcorepal.cap4j.ddd.share.TextUtils; +import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; +import org.netcorepal.cap4j.ddd.share.misc.ScanUtils; +import org.netcorepal.cap4j.ddd.share.misc.TextUtils; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.OrderUtils; import org.springframework.core.env.Environment; import org.springframework.messaging.Message; import org.springframework.messaging.support.GenericMessage; -import javax.annotation.PostConstruct; import java.util.*; /** @@ -31,33 +32,26 @@ @Slf4j @RequiredArgsConstructor public class RocketMqDomainEventSubscriberAdapter { - private final RocketMqDomainEventSubscriberManager rocketMqDomainEventSubscriberManager; - private final Environment environment; - private final DomainEventMessageInterceptor domainEventMessageInterceptor; + private final EventSubscriberManager eventSubscriberManager; + private final List eventMessageInterceptors; private final MQConsumerConfigure mqConsumerConfigure; + + private final Environment environment; + private final String scanPath; private final String applicationName; private final String defaultNameSrv; private final String msgCharset; - private final String scanPath; List mqPushConsumers = new ArrayList<>(); - @PostConstruct public void init() { - Set> classes = ScanUtils.scanClass(scanPath, true); + Set> classes = ScanUtils.findIntegrationEventClasses(scanPath); classes.stream().filter(cls -> { - DomainEvent domainEvent = cls.getAnnotation(DomainEvent.class); - if (!Objects.isNull(domainEvent) && StringUtils.isNotEmpty(domainEvent.intergration()) - & !DomainEvent.NONE_SUBSCRIBER.equalsIgnoreCase(domainEvent.subscriber())) { - return true; - } else { - return false; - } + IntegrationEvent integrationEvent = cls.getAnnotation(IntegrationEvent.class); + return !Objects.isNull(integrationEvent) && StringUtils.isNotEmpty(integrationEvent.value()) + & !IntegrationEvent.NONE_SUBSCRIBER.equalsIgnoreCase(integrationEvent.subscriber()); }).forEach(domainEventClass -> { - MQPushConsumer mqPushConsumer = null; - if (mqPushConsumer != null) { - mqPushConsumer = mqConsumerConfigure.get(domainEventClass); - } + MQPushConsumer mqPushConsumer = mqConsumerConfigure == null ? null : mqConsumerConfigure.get(domainEventClass); if (mqPushConsumer == null) { mqPushConsumer = createDefaultConsumer(domainEventClass); } @@ -72,6 +66,22 @@ public void init() { }); } + private List orderedEventMessageInterceptors = null; + + /** + * 获取排序后的事件消息拦截器 + * 基于{@link org.springframework.core.annotation.Order} + * + * @return + */ + private List getOrderedEventMessageInterceptors() { + if (orderedEventMessageInterceptors == null) { + orderedEventMessageInterceptors = new ArrayList<>(eventMessageInterceptors); + orderedEventMessageInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); + } + return orderedEventMessageInterceptors; + } + public void shutdown() { log.info("集成事件消息监听退出..."); if (mqPushConsumers == null || mqPushConsumers.isEmpty()) { @@ -88,47 +98,47 @@ public void shutdown() { }); } - public DefaultMQPushConsumer createDefaultConsumer(Class domainEventClass) { - DomainEvent domainEvent = (DomainEvent) domainEventClass.getAnnotation(DomainEvent.class); - if (Objects.isNull(domainEvent) || StringUtils.isBlank(domainEvent.intergration()) - || DomainEvent.NONE_SUBSCRIBER.equalsIgnoreCase(domainEvent.subscriber())) { + public DefaultMQPushConsumer createDefaultConsumer(Class integrationEventClass) { + IntegrationEvent integrationEvent = integrationEventClass.getAnnotation(IntegrationEvent.class); + if (Objects.isNull(integrationEvent) || StringUtils.isBlank(integrationEvent.value()) + || IntegrationEvent.NONE_SUBSCRIBER.equalsIgnoreCase(integrationEvent.subscriber())) { // 不是集成事件, 或显式标明无订阅 return null; } -// if (!rocketMqDomainEventSubscriberManager.hasSubscriber(domainEventClass)) { -// // 不存在订阅 -// return null; -// } - String target = domainEvent.intergration(); + String target = integrationEvent.value(); target = TextUtils.resolvePlaceholderWithCache(target, environment); String topic = target.lastIndexOf(':') > 0 ? target.substring(0, target.lastIndexOf(':')) : target; String tag = target.lastIndexOf(':') > 0 ? target.substring(target.lastIndexOf(':') + 1) : ""; DefaultMQPushConsumer mqPushConsumer = new DefaultMQPushConsumer(); - mqPushConsumer.setConsumerGroup(getTopicConsumerGroup(topic, domainEvent.subscriber())); + mqPushConsumer.setConsumerGroup(getTopicConsumerGroup(topic, integrationEvent.subscriber())); mqPushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET); mqPushConsumer.setInstanceName(applicationName); String nameServerAddr = getTopicNamesrvAddr(topic, defaultNameSrv); mqPushConsumer.setNamesrvAddr(nameServerAddr); - mqPushConsumer.setUnitName(domainEventClass.getSimpleName()); + mqPushConsumer.setUnitName(integrationEventClass.getSimpleName()); mqPushConsumer.registerMessageListener((List msgs, ConsumeConcurrentlyContext context) -> { try { - for (MessageExt msg : - msgs) { + for (MessageExt msg : msgs) { String strMsg = new String(msg.getBody(), msgCharset); - Object event = JSON.parseObject(strMsg, domainEventClass, Feature.SupportNonPublicField); - Map headers = new HashMap<>(); - msg.getProperties().forEach((k, v) -> headers.put(k, v)); - if (domainEventMessageInterceptor != null) { - Message message = new GenericMessage(event, new DomainEventMessageInterceptor.ModifiableMessageHeaders(headers)); - message = domainEventMessageInterceptor.beforeSubscribe(message); - event = message.getPayload(); + Object eventPayload = JSON.parseObject(strMsg, integrationEventClass, Feature.SupportNonPublicField); + + if (getOrderedEventMessageInterceptors().isEmpty()) { + eventSubscriberManager.dispatch(eventPayload); + } else { + Map headers = new HashMap<>(); + headers.putAll(msg.getProperties()); + Message message = new GenericMessage<>(eventPayload, new EventMessageInterceptor.ModifiableMessageHeaders(headers)); + getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.preSubscribe(message)); + // 拦截器可能修改消息,重新赋值 + eventPayload = message.getPayload(); + eventSubscriberManager.dispatch(eventPayload); + getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.postSubscribe(message)); } - rocketMqDomainEventSubscriberManager.trigger(event); } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } catch (Exception ex) { - log.error("集成事件消息消费失败", ex); + log.error("集成事件消息消费异常", ex); return ConsumeConcurrentlyStatus.RECONSUME_LATER; } }); diff --git a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberManager.java b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberManager.java deleted file mode 100644 index be1f940..0000000 --- a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqDomainEventSubscriberManager.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.event; - -import com.alibaba.fastjson.JSON; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.netcorepal.cap4j.ddd.share.DomainException; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.OrderUtils; - -import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; - -/** - * 基于RocketMq的领域事件订阅管理器 - * @author binking338 - * @date 2023/8/13 - */ -@RequiredArgsConstructor -@Slf4j -public class RocketMqDomainEventSubscriberManager implements DomainEventSubscriberManager { - private final List subscribers; - private final ApplicationEventPublisher applicationEventPublisher; - private Map> subscriberMap = null; - - @PostConstruct - public void init(){ - subscriberMap = new java.util.HashMap>(); - subscribers.sort((a, b) -> - OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE) - OrderUtils.getOrder(b.getClass(), Ordered.LOWEST_PRECEDENCE) - ); - for (RocketMqDomainEventSubscriber subscriber : subscribers) { - if (subscriberMap.get(subscriber.forDomainEventClass()) == null) { - subscriberMap.put(subscriber.forDomainEventClass(), new java.util.ArrayList()); - } - subscriberMap.get(subscriber.forDomainEventClass()).add(subscriber); - } - } - - @Override - public void trigger(Event eventPayload) { - try { - applicationEventPublisher.publishEvent(eventPayload); - } catch (Exception e) { - log.error("领域事件处理失败 eventPayload=" + JSON.toJSONString(eventPayload), e); - throw new DomainException("领域事件处理失败 eventPayload=" + JSON.toJSONString(eventPayload), e); - } - if (subscriberMap == null) { - - } - List subscribersForEvent = subscriberMap.get(eventPayload.getClass()); - if (subscribersForEvent == null || subscribersForEvent.isEmpty()) { - return; - } - for (RocketMqDomainEventSubscriber subscriber : subscribersForEvent) { - try { - subscriber.onEvent(eventPayload); - } catch (Exception e) { - log.error("领域事件处理失败 eventPayload=" + JSON.toJSONString(eventPayload), e); - throw new DomainException("领域事件处理失败 eventPayload=" + JSON.toJSONString(eventPayload), e); - } - } - } - - public boolean hasSubscriber(Class eventClass) { - return subscriberMap.containsKey(eventClass) && subscriberMap.get(eventClass).size() > 0; - } -} diff --git a/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqIntegrationEventPublisher.java b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqIntegrationEventPublisher.java new file mode 100644 index 0000000..edfc0a1 --- /dev/null +++ b/ddd-domain-event-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/RocketMqIntegrationEventPublisher.java @@ -0,0 +1,91 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.SendCallback; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventPublisher; +import org.netcorepal.cap4j.ddd.share.DomainException; +import org.netcorepal.cap4j.ddd.share.misc.TextUtils; +import org.springframework.core.env.Environment; + +/** + * 基于RocketMq的领域事件发布器 + * 如下配置需配置好,保障RocketMqTemplate被初始化 + * ## rocketmq + * #rocketmq.name-server = myrocket.nameserver:9876 + * #rocketmq.producer.group=${spring.application.name} + * + * @author binking338 + * @date 2023/8/13 + */ +@Slf4j +@RequiredArgsConstructor +public class RocketMqIntegrationEventPublisher implements IntegrationEventPublisher { + private final RocketMQTemplate rocketMQTemplate; + private final Environment environment; + + @Override + public void publish(EventRecord event, PublishCallback publishCallback) { + try { + String destination = event.getEventTopic(); + destination = TextUtils.resolvePlaceholderWithCache(destination, environment); + if (destination == null || destination.isEmpty()) { + throw new DomainException(String.format("集成事件发布失败: %s 缺失topic", event.getId())); + } + // MQ消息通道 + rocketMQTemplate.asyncSend( + destination, + event.getMessage(), + new DomainEventSendCallback( + event, + publishCallback + ) + ); + } catch (Exception ex) { + log.error(String.format("集成事件发布失败: %s", event.getId()), ex); + } + } + + @RequiredArgsConstructor + @Slf4j + public static class DomainEventSendCallback implements SendCallback { + private final EventRecord event; + private final PublishCallback publishCallback; + + @Override + public void onSuccess(SendResult sendResult) { + if (event == null) { + throw new DomainException("集成事件为NULL"); + } + try { + log.info(String.format("集成事件发送成功, %s msgId=%s", + event.getId(), + sendResult.getMsgId())); + publishCallback.onSuccess(event); + } catch (Exception ex) { + log.error("回调失败(事件发送成功)", ex); + publishCallback.onException(event, ex); + } + } + + @Override + public void onException(Throwable throwable) { + if (event == null) { + throw new DomainException("集成事件为NULL"); + } + try { + String msg = String.format("集成事件发送失败, %s body=%s", + event.getId(), + JSON.toJSONString(event.getPayload())); + log.error(msg, throwable); + publishCallback.onException(event, new DomainException(msg)); + } catch (Exception ex) { + log.error("回调失败(事件发送异常)", ex); + publishCallback.onException(event, ex); + } + } + } +} diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java similarity index 60% rename from ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java rename to ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java index db405d0..3111e92 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaUnitOfWork.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java @@ -1,34 +1,33 @@ -package org.netcorepal.cap4j.ddd.domain.repo; +package org.netcorepal.cap4j.ddd.application.impl; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.netcorepal.cap4j.ddd.domain.event.*; -import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; -import org.netcorepal.cap4j.ddd.share.*; import org.hibernate.engine.spi.SessionImplementor; +import org.netcorepal.cap4j.ddd.application.UnitOfWork; +import org.netcorepal.cap4j.ddd.domain.aggregate.Specification; +import org.netcorepal.cap4j.ddd.domain.aggregate.SpecificationManager; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisor; +import org.netcorepal.cap4j.ddd.domain.repo.PersistListenerManager; +import org.netcorepal.cap4j.ddd.domain.repo.PersistType; +import org.netcorepal.cap4j.ddd.share.DomainException; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.GenericMessage; +import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport; +import org.springframework.data.repository.core.EntityInformation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.event.TransactionalEventListener; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; -import java.time.Duration; -import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.netcorepal.cap4j.ddd.share.Constants.*; - /** * 基于Jpa的UnitOfWork实现 * @@ -38,97 +37,24 @@ @RequiredArgsConstructor @Slf4j public class JpaUnitOfWork implements UnitOfWork { - private final List repositories; - private final ApplicationEventPublisher applicationEventPublisher; private final DomainEventSupervisor domainEventSupervisor; - private final DomainEventPublisher domainEventPublisher; - private final DomainEventSubscriberManager domainEventSubscriberManager; - private final EventRecordRepository eventRecordRepository; - private final JpaSpecificationManager jpaSpecificationManager; - private final JpaPersistListenerManager jpaPersistListenerManager; - private final DomainEventMessageInterceptor domainEventMessageInterceptor; + private final SpecificationManager specificationManager; + private final PersistListenerManager persistListenerManager; + private final ApplicationEventPublisher applicationEventPublisher; + private final int retrieveCountWarnThreshold; @Getter @PersistenceContext protected EntityManager entityManager; protected static JpaUnitOfWork instance; - public static void fixJpaAopWrapper(JpaUnitOfWork unitOfWork) { - instance = unitOfWork; - } - - private final String svcName; - private final String entityGetIdMethod; - private final int retrieveCountWarnThreshold; - private final int eventDeliveryCompensationIntervalSeconds; - - private ThreadLocal> persistedEntitiesThreadLocal = new ThreadLocal<>(); - private ThreadLocal> removedEntitiesThreadLocal = new ThreadLocal<>(); - private ThreadLocal entityPersisttedEventThreadLocal = ThreadLocal.withInitial(() -> new EntityPersisttedEvent(instance, new HashSet<>(), new HashSet<>(), new HashSet<>())); - - protected Map repositoryMap = null; - public void init(){ - if(repositoryMap != null){ - return; - } - synchronized (this){ - if(repositoryMap != null){ - return; - } - } - repositoryMap = new HashMap<>(); - repositories.forEach(repository -> { - repositoryMap.put(repository.forEntityClass(), repository); - }); - } - - @Override - public Repository repo(Class entityClass) { - init(); - if(!repositoryMap.containsKey(entityClass)){ - throw new DomainException("仓储不存在:" + entityClass.getTypeName()); - } - return repositoryMap.get(entityClass); - } - - @Override - public List find(Class entityClass, Object condition, List orders) { - return repo(entityClass).find(condition, orders); - } - @Override - public Optional findOne(Class entityClass, Object condition) { - return repo(entityClass).findOne(condition); - } - - @Override - public PageData findPage(Class entityClass, Object condition, PageParam pageParam) { - return repo(entityClass).findPage(condition, pageParam); - } - - @Override - public Optional findById(Class entityClass, Object id) { - return repo(entityClass).findById(id); - } - - @Override - public List findByIds(Class entityClass, Iterable ids) { - return repo(entityClass).findByIds(ids); - } - - @Override - public long count(Class entityClass, Object condition) { - return repo(entityClass).count(condition); - } - - @Override - public boolean exists(Class entityClass, Object condition) { - return repo(entityClass).exists(condition); + public static void fixAopWrapper(JpaUnitOfWork unitOfWork) { + instance = unitOfWork; } - @Override - public boolean existsById(Class entityClass, Object id) { - return repo(entityClass).existsById(id); - } + private static ThreadLocal> persistedEntitiesThreadLocal = new ThreadLocal<>(); + private static ThreadLocal> removedEntitiesThreadLocal = new ThreadLocal<>(); + private static ThreadLocal entityPersisttedEventThreadLocal = new ThreadLocal<>(); public void persist(Object entity) { @@ -156,14 +82,14 @@ public void save() { public void save(Propagation propagation) { Set persistEntityList = null; if (persistedEntitiesThreadLocal.get() != null) { - persistEntityList = persistedEntitiesThreadLocal.get().stream().collect(Collectors.toSet()); + persistEntityList = new HashSet<>(persistedEntitiesThreadLocal.get()); persistedEntitiesThreadLocal.get().clear(); } else { persistEntityList = new HashSet<>(); } Set deleteEntityList = null; if (removedEntitiesThreadLocal.get() != null) { - deleteEntityList = removedEntitiesThreadLocal.get().stream().collect(Collectors.toSet()); + deleteEntityList = new HashSet<>(removedEntitiesThreadLocal.get()); removedEntitiesThreadLocal.get().clear(); } else { deleteEntityList = new HashSet<>(); @@ -174,6 +100,11 @@ public void save(Propagation propagation) { persistEntityList.add(entity); } } + if (!persistEntityList.isEmpty() || !deleteEntityList.isEmpty()) { + if (null == entityPersisttedEventThreadLocal.get()) { + entityPersisttedEventThreadLocal.set(new EntityPersisttedEvent(this, new HashSet<>(), new HashSet<>(), new HashSet<>())); + } + } specifyEntitesBeforeTransaction(persistEntityList); Set[] saveAndDeleteEntityList = new Set[]{persistEntityList, deleteEntityList}; save(input -> { @@ -185,13 +116,8 @@ public void save(Propagation propagation) { if (persistEntities != null && !persistEntities.isEmpty()) { flush = true; for (Object entity : persistEntities) { - Object id = null; - try { - id = entity.getClass().getMethod(entityGetIdMethod).invoke(entity); - } catch (Exception _ex) { - /* we don't care */ - } - if (id != null) { + EntityInformation entityInformation = JpaEntityInformationSupport.getEntityInformation(entity.getClass(), getEntityManager()); + if (!entityInformation.isNew(entity)) { if (!getEntityManager().contains(entity)) { getEntityManager().merge(entity); } @@ -226,18 +152,26 @@ public void save(Propagation propagation) { getEntityManager().refresh(entity); } } - applicationEventPublisher.publishEvent(entityPersisttedEventThreadLocal.get().clone()); - entityPersisttedEventThreadLocal.get().reset(); + EntityPersisttedEvent entityPersisttedEvent = entityPersisttedEventThreadLocal.get(); + entityPersisttedEventThreadLocal.remove(); + applicationEventPublisher.publishEvent(entityPersisttedEvent); + } + Set entities = new HashSet<>(); + if (persistEntities != null && !persistEntities.isEmpty()) { + entities.addAll(persistEntities); + } + if (deleteEntities != null && !deleteEntities.isEmpty()) { + entities.addAll(deleteEntities); } - publishTransactionEvent(saveAndDeleteEntityList); + domainEventSupervisor.release(entities); return null; }, saveAndDeleteEntityList, propagation); } - public void reset() { + public static void reset() { persistedEntitiesThreadLocal.remove(); removedEntitiesThreadLocal.remove(); - entityPersisttedEventThreadLocal.get().reset(); + entityPersisttedEventThreadLocal.remove(); } public interface QueryBuilder { @@ -462,7 +396,7 @@ protected List persistenceContextEntities() { protected void specifyEntitesInTransaction(Set entities) { if (entities != null && !entities.isEmpty()) { for (Object entity : entities) { - Specification.Result result = jpaSpecificationManager.specify(entity); + Specification.Result result = specificationManager.specifyInTransaction(entity); if (!result.isPassed()) { throw new DomainException(result.getMessage()); } @@ -478,7 +412,7 @@ protected void specifyEntitesInTransaction(Set entities) { protected void specifyEntitesBeforeTransaction(Set entities) { if (entities != null && !entities.isEmpty()) { for (Object entity : entities) { - Specification.Result result = jpaSpecificationManager.specifyBeforeTransaction(entity); + Specification.Result result = specificationManager.specifyBeforeTransaction(entity); if (!result.isPassed()) { throw new DomainException(result.getMessage()); } @@ -486,44 +420,6 @@ protected void specifyEntitesBeforeTransaction(Set entities) { } } - /** - * UoW事务成功提交事件 - */ - public static class TransactionCommittedEvent extends ApplicationEvent { - @Getter - List events; - - /** - * Create a new {@code ApplicationEvent}. - * - * @param source the object on which the event initially occurred or with - * which the event is associated (never {@code null}) - */ - public TransactionCommittedEvent(Object source, List events) { - super(source); - this.events = events; - } - } - - /** - * UoW事务正在提交事件 - */ - public static class TransactionCommitingEvent extends ApplicationEvent { - @Getter - List events; - - /** - * Create a new {@code ApplicationEvent}. - * - * @param source the object on which the event initially occurred or with - * which the event is associated (never {@code null}) - */ - public TransactionCommitingEvent(Object source, List events) { - super(source); - this.events = events; - } - } - /** * UoW实体持久化事件 */ @@ -542,7 +438,7 @@ public EntityPersisttedEvent(Object source, Set createdEntities, Set... entities) { - Set eventPayloads = new HashSet<>(); - eventPayloads.addAll(domainEventSupervisor.popEvents()); - for (Set entitySet : entities) { - for (Object entity : entitySet) { - eventPayloads.addAll(domainEventSupervisor.popEvents(entity)); - } - } - List persistedEvents = new ArrayList<>(eventPayloads.size()); - List transientEvents = new ArrayList<>(eventPayloads.size()); - LocalDateTime now = LocalDateTime.now(); - for (Object eventPayload : eventPayloads) { - LocalDateTime deliverTime = domainEventSupervisor.getDeliverTime(eventPayload); - EventRecord event = eventRecordRepository.create(); - event.init(eventPayload, this.svcName, deliverTime, Duration.ofMinutes(DEFAULT_EVENT_EXPIRE_MINUTES), DEFAULT_EVENT_RETRY_TIMES); - if (!isDomainEventPersist(eventPayload) && !deliverTime.isAfter(now)) { - transientEvents.add(event); - } else { - eventRecordRepository.save(event); - persistedEvents.add(event); - } - } - applicationEventPublisher.publishEvent(new TransactionCommitingEvent(this, transientEvents)); - applicationEventPublisher.publishEvent(new TransactionCommittedEvent(this, persistedEvents)); - } - - public boolean isDomainEventPersist(Object payload) { - DomainEvent domainEvent = payload == null - ? null - : payload.getClass().getAnnotation(DomainEvent.class); - if (domainEvent != null) { - return domainEvent.persist() || (domainEvent.intergration() != null && !domainEvent.intergration().trim().isEmpty()); - } else { - return false; - } - } - - @TransactionalEventListener(fallbackExecution = true, classes = EntityPersisttedEvent.class) - public void onTransactionCommitted(EntityPersisttedEvent event) { + @EventListener(classes = EntityPersisttedEvent.class) + public void onTransactionCommiting(EntityPersisttedEvent event) { for (Object entity : event.getCreatedEntities()) { - jpaPersistListenerManager.onChange(entity); - jpaPersistListenerManager.onCreate(entity); + persistListenerManager.onChange(entity, PersistType.CREATE); } for (Object entity : event.getUpdatedEntities()) { - jpaPersistListenerManager.onChange(entity); - jpaPersistListenerManager.onUpdate(entity); + persistListenerManager.onChange(entity, PersistType.UPDATE); } for (Object entity : event.getDeletedEntities()) { - jpaPersistListenerManager.onChange(entity); - jpaPersistListenerManager.onDelete(entity); - } - } - - @TransactionalEventListener(fallbackExecution = true, classes = TransactionCommittedEvent.class) - public void onTransactionCommitted(TransactionCommittedEvent transactionCommittedEvent) { - List events = transactionCommittedEvent.getEvents(); - if (events != null && !events.isEmpty()) { - LocalDateTime now = LocalDateTime.now(); - events.forEach(e -> { - EventRecord event = (EventRecord) e; - Message message = new GenericMessage( - event.getPayload(), - new DomainEventMessageInterceptor.ModifiableMessageHeaders(null, UUID.fromString(event.getId()), null) - ); - if (event.getScheduleTime().isAfter(now)) { - Duration delay = Duration.between(now, event.getScheduleTime()); - if (delay.getSeconds() < eventDeliveryCompensationIntervalSeconds) { - message.getHeaders().put(HEADER_KEY_CAP4J_SCHEDULE, event.getScheduleTime()); - } else { - return; - } - } - if (domainEventMessageInterceptor != null) { - message = domainEventMessageInterceptor.beforePublish(message); - } - domainEventPublisher.publish(message, event); - }); - } - } - - @EventListener(classes = TransactionCommitingEvent.class) - public void onTransactionCommiting(TransactionCommitingEvent transactionCommitingEvent) { - List events = transactionCommitingEvent.getEvents(); - if (events != null && !events.isEmpty()) { - events.forEach(e -> { - EventRecord event = (EventRecord) e; - event.beginDelivery(LocalDateTime.now()); - domainEventSubscriberManager.trigger(event.getPayload()); - }); + persistListenerManager.onChange(entity, PersistType.DELETE); } } } diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java deleted file mode 100644 index c4e828d..0000000 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaPersistListener.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.repo; - -import org.netcorepal.cap4j.ddd.share.ClassUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; - -/** - * 基于Jpq的实体持久化监听抽象类 - * - * @author binking338 - * @date 2024/3/9 - */ -public abstract class AbstractJpaPersistListener implements PersistListener { - - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") - @Autowired - ApplicationContext applicationContext; - - public T getBean(Class clazz){ - return applicationContext.getBean(clazz); - } - public T getBean(String name, Class clazz){ - return applicationContext.getBean(name, clazz); - } - - - Class forEntityClass(){ - return ((Class) ClassUtils.findMethod( - this.getClass(), - "onChange", - m -> m.getParameterCount() == 1 - ).getParameters()[0].getType()); - } - - public boolean throwOnException() { - return true; - } - - - /** - * 持久化变更 - * @param entity - */ - @Override - public void onChange(Entity entity){ - - } - - /** - * 新增实体时 - * @param entity - */ - @Override - public void onCreate(Entity entity){ - - } - - /** - * 更新实体时 - * @param entity - */ - @Override - public void onUpdate(Entity entity){ - - } - - /** - * 删除实体时 - * @param entity - */ - @Override - public void onDelete(Entity entity){ - - } -} diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java index 94de95d..6e7eccf 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java @@ -1,11 +1,9 @@ package org.netcorepal.cap4j.ddd.domain.repo; import lombok.RequiredArgsConstructor; -import org.netcorepal.cap4j.ddd.share.ClassUtils; import org.netcorepal.cap4j.ddd.share.OrderInfo; import org.netcorepal.cap4j.ddd.share.PageData; import org.netcorepal.cap4j.ddd.share.PageParam; -import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -13,8 +11,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -31,13 +27,6 @@ public class AbstractJpaRepository implements Repository { private final JpaSpecificationExecutor jpaSpecificationExecutor; private final JpaRepository jpaRepository; - Class forEntityClass(){ - return (Class) ResolvableType.forType( - ((ParameterizedType) this.getClass().getGenericSuperclass()) - .getActualTypeArguments()[0] - ).toClass(); - } - public Optional findById(Object id) { List ids = new ArrayList<>(1); ids.add((ID) id); @@ -45,7 +34,7 @@ public Optional findById(Object id) { return entity; } - public List findByIds(Iterable ids){ + public List findByIds(Iterable ids) { List entities = jpaRepository.findAllById((Iterable) ids); return entities; } diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java deleted file mode 100644 index f4f4d49..0000000 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaSpecification.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.repo; - -import org.netcorepal.cap4j.ddd.share.ClassUtils; - -/** - * 基于Jpa的实体规约抽象类 - * - * @author binking338 - * @date 2023/8/13 - */ -public abstract class AbstractJpaSpecification implements Specification { - Class forEntityClass(){ - return ((Class) ClassUtils.findMethod( - this.getClass(), - "specify", - m -> m.getParameterCount() == 1 - ).getParameters()[0].getType()); - } - - public boolean forceBeforeTransaction(){ - return false; - } - - public abstract Result specify(Entity entity); -} diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPageUtils.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPageUtils.java index 6697baa..5f5f173 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPageUtils.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPageUtils.java @@ -1,5 +1,6 @@ package org.netcorepal.cap4j.ddd.domain.repo; +import org.netcorepal.cap4j.ddd.share.DomainException; import org.netcorepal.cap4j.ddd.share.PageData; import org.netcorepal.cap4j.ddd.share.PageParam; import org.springframework.beans.BeanUtils; @@ -39,9 +40,9 @@ public static PageData fromSpringData(Page page, Class desClass) try { d = (D) desClass.newInstance(); } catch (InstantiationException e) { - throw new RuntimeException(e); + throw new DomainException("分页类型转换异常", e); } catch (IllegalAccessException e) { - throw new RuntimeException(e); + throw new DomainException("分页类型转换异常", e); } BeanUtils.copyProperties(s, d); return d; diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java deleted file mode 100644 index ea63a95..0000000 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPersistListenerManager.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.repo; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.OrderUtils; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 基于Jpa的实体持久化监听管理器 - * - * @author binking338 - * @date 2024/3/9 - */ -@RequiredArgsConstructor -@Slf4j -public class JpaPersistListenerManager implements PersistListenerManager { - protected final List persistListeners; - - Map> persistListenersMap; - - public void init() { - if (persistListenersMap != null) { - return; - } - synchronized (this) { - if (persistListenersMap != null) { - return; - } - persistListenersMap = new HashMap<>(); - persistListeners.sort((a, b) -> - OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE) - OrderUtils.getOrder(b.getClass(), Ordered.LOWEST_PRECEDENCE) - ); - for (AbstractJpaPersistListener persistListener : persistListeners) { - if (!persistListenersMap.containsKey(persistListener.forEntityClass())) { - persistListenersMap.put(persistListener.forEntityClass(), new java.util.ArrayList()); - } - List persistListenerList = persistListenersMap.get(persistListener.forEntityClass()); - persistListenerList.add(persistListener); - } - } - } - - - - - /** - * onCreate & onUpdate & onDelete - * @param entity - * @param - */ - @Override - public void onChange(Entity entity) { - init(); - List listeners = persistListenersMap.get(entity.getClass()); - if (listeners != null) { - for (AbstractJpaPersistListener listener : - listeners) { - try { - listener.onChange(entity); - } catch (Exception ex){ - log.error("onPersist 异常", ex); - if(listener.throwOnException()){ - throw ex; - } - } - } - } - } - - @Override - public void onCreate(Entity entity) { - init(); - List listeners = persistListenersMap.get(entity.getClass()); - if (listeners != null) { - for (AbstractJpaPersistListener listener : - listeners) { - try { - listener.onCreate(entity); - } catch (Exception ex){ - log.error("onCreate 异常", ex); - if(listener.throwOnException()){ - throw ex; - } - } - } - } - } - - @Override - public void onUpdate(Entity entity) { - init(); - List listeners = persistListenersMap.get(entity.getClass()); - if (listeners != null) { - for (AbstractJpaPersistListener listener : - listeners) { - try { - listener.onUpdate(entity); - } catch (Exception ex){ - log.error("onUpdate 异常", ex); - if(listener.throwOnException()){ - throw ex; - } - } - } - } - - } - - @Override - public void onDelete(Entity entity) { - init(); - List listeners = persistListenersMap.get(entity.getClass()); - if (listeners != null) { - for (AbstractJpaPersistListener listener : - listeners) { - try { - listener.onDelete(entity); - } catch (Exception ex){ - log.error("onDelete 异常", ex); - if(listener.throwOnException()){ - throw ex; - } - } - } - } - - } -} diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java deleted file mode 100644 index f2e9d63..0000000 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaSpecificationManager.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.repo; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.OrderUtils; - -import java.util.List; -import java.util.Map; - -/** - * 基于Jpa的实体规约管理器 - * - * @author binking338 - * @date 2023/8/13 - */ -@RequiredArgsConstructor -@Slf4j -public class JpaSpecificationManager implements SpecificationManager { - protected final List specifications; - protected Map> specificationMap; - - public void init(){ - if(specificationMap != null){ - return; - } - synchronized (this){ - - if(specificationMap != null){ - return; - } - specificationMap = new java.util.HashMap>(); - specifications.sort((a,b)-> - OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE) - OrderUtils.getOrder(b.getClass(), Ordered.LOWEST_PRECEDENCE) - ); - for (AbstractJpaSpecification specification : specifications) { - if(!specificationMap.containsKey(specification.forEntityClass())){ - specificationMap.put(specification.forEntityClass(), new java.util.ArrayList()); - } - List specificationList = specificationMap.get(specification.forEntityClass()); - specificationList.add(specification); - } - } - } - - @Override - public Specification.Result specify(Entity entity) { - init(); - List specifications = specificationMap.get(entity.getClass()); - if(specifications != null) { - for (AbstractJpaSpecification specification : specifications) { - if(!specification.forceBeforeTransaction()) continue; - Specification.Result result = specification.specify(entity); - if (!result.isPassed()) { - return result; - } - } - } - return Specification.Result.pass(); - } - @Override - - public Specification.Result specifyBeforeTransaction(Entity entity) { - init(); - List specifications = specificationMap.get(entity.getClass()); - if(specifications != null) { - for (AbstractJpaSpecification specification : specifications) { - if(!specification.forceBeforeTransaction()) continue; - Specification.Result result = specification.specify(entity); - if (!result.isPassed()) { - return result; - } - } - } - return Specification.Result.pass(); - } -} diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java new file mode 100644 index 0000000..a7d2305 --- /dev/null +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java @@ -0,0 +1,143 @@ +package org.netcorepal.cap4j.ddd.domain.repo.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.UnitOfWork; +import org.netcorepal.cap4j.ddd.domain.repo.AbstractJpaRepository; +import org.netcorepal.cap4j.ddd.domain.repo.Repository; +import org.netcorepal.cap4j.ddd.domain.repo.RepositorySupervisor; +import org.netcorepal.cap4j.ddd.share.DomainException; +import org.netcorepal.cap4j.ddd.share.OrderInfo; +import org.netcorepal.cap4j.ddd.share.PageData; +import org.netcorepal.cap4j.ddd.share.PageParam; +import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 默认仓储管理器 + * + * @author binking338 + * @date 2024/9/3 + */ +@RequiredArgsConstructor +public class DefaultRepositorySupervisor implements RepositorySupervisor { + private final List> repositories; + private final UnitOfWork unitOfWork; + + protected Map, Repository> repositoryMap = null; + + public void init() { + if (repositoryMap != null) { + return; + } + synchronized (this) { + if (repositoryMap != null) { + return; + } + } + repositoryMap = new HashMap<>(); + repositories.forEach(repository -> { + Class entityClass = ClassUtils.resolveGenericTypeClass( + repository.getClass(), 0, + AbstractJpaRepository.class, Repository.class + ); + repositoryMap.put(entityClass, repository); + }); + } + + @Override + public Repository repo(Class entityClass) { + init(); + if (!repositoryMap.containsKey(entityClass)) { + throw new DomainException("仓储不存在:" + entityClass.getTypeName()); + } + return (Repository) repositoryMap.get(entityClass); + } + + @Override + public List find(Class entityClass, Object condition, List orders) { + List entities = repo(entityClass).find(condition, orders); + if (entities != null) { + entities.forEach(unitOfWork::persist); + } + return entities; + } + + @Override + public Optional findOne(Class entityClass, Object condition) { + Optional entity = repo(entityClass).findOne(condition); + entity.ifPresent(unitOfWork::persist); + return entity; + } + + @Override + public PageData findPage(Class entityClass, Object condition, PageParam pageParam) { + PageData page = repo(entityClass).findPage(condition, pageParam); + if (page.getList() != null) { + page.getList().forEach(unitOfWork::persist); + } + return page; + } + + @Override + public Optional findById(Class entityClass, Object id) { + Optional entity = repo(entityClass).findById(id); + entity.ifPresent(unitOfWork::persist); + return entity; + } + + @Override + public List findByIds(Class entityClass, Iterable ids) { + List entities = repo(entityClass).findByIds(ids); + if (entities != null) { + entities.forEach(unitOfWork::persist); + } + return entities; + } + + @Override + public List remove(Class entityClass, Object condition, int limit) { + PageParam page = new PageParam(); + page.setPageNum(1); + page.setPageSize(limit); + PageData entities = repo(entityClass).findPage(condition, page); + if (entities.getList() != null) { + entities.getList().forEach(unitOfWork::remove); + } + return entities.getList(); + } + + @Override + public Optional removeById(Class entityClass, Object id) { + Optional entity = repo(entityClass).findById(id); + entity.ifPresent(unitOfWork::remove); + return entity; + } + + @Override + public List removeByIds(Class entityClass, Iterable ids) { + List entities = repo(entityClass).findByIds(ids); + if (entities != null) { + entities.forEach(unitOfWork::remove); + } + return entities; + } + + @Override + public long count(Class entityClass, Object condition) { + return repo(entityClass).count(condition); + } + + @Override + public boolean exists(Class entityClass, Object condition) { + return repo(entityClass).exists(condition); + } + + @Override + public boolean existsById(Class entityClass, Object id) { + return repo(entityClass).existsById(id); + } +} From 0558cc4512449a0f623ee0657f2ae6d8d8f8a698 Mon Sep 17 00:00:00 2001 From: binking338 Date: Fri, 6 Sep 2024 00:17:01 +0800 Subject: [PATCH 13/62] =?UTF-8?q?=E6=8A=BD=E5=8F=96MySql=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E3=80=82=E5=87=86=E5=A4=87=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=85=B6=E4=BB=96=E6=95=B0=E6=8D=AE=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../cap4j/ddd/codegen/GenEntityMojo.java | 499 ++++-------- .../cap4j/ddd/codegen/MyAbstractMojo.java | 78 +- .../ddd/codegen/misc/MysqlSchemaUtils.java | 369 --------- .../ddd/codegen/misc/SqlSchemaUtils.java | 734 ++++++++++++++++++ .../codegen/misc/SqlSchemaUtils4Mysql.java | 240 ++++++ 6 files changed, 1177 insertions(+), 745 deletions(-) delete mode 100644 cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/MysqlSchemaUtils.java create mode 100644 cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java create mode 100644 cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Mysql.java diff --git a/README.md b/README.md index eeeae12..3743f81 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ ``` 通常,`cap4j-ddd-codegen`插件只需要我们根据团队或项目的实际情况调整以下配置项即可使用。 -> - `basePackage`: 项目基础包名,一般为com.yourcompany.project +> - `basePackage`: 项目基础包名,一般为 _com.yourcompany.project_ > - `connectionString`: 数据库连接串 > - `user`: 数据库账号 > - `pwd`: 数据库密码 diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index 1d55288..cbc6942 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -6,18 +6,18 @@ import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; import org.netcorepal.cap4j.ddd.codegen.misc.Inflector; -import org.netcorepal.cap4j.ddd.codegen.misc.MysqlSchemaUtils; import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; +import org.netcorepal.cap4j.ddd.codegen.misc.SqlSchemaUtils; import java.io.*; import java.nio.charset.Charset; -import java.sql.*; import java.util.*; import java.util.stream.Collectors; import static org.netcorepal.cap4j.ddd.codegen.misc.NamingUtils.toLowerCamelCase; import static org.netcorepal.cap4j.ddd.codegen.misc.NamingUtils.toUpperCamelCase; import static org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils.writeLine; +import static org.netcorepal.cap4j.ddd.codegen.misc.SqlSchemaUtils.*; /** * 生成实体 @@ -28,11 +28,21 @@ @Mojo(name = "gen-entity") public class GenEntityMojo extends MyAbstractMojo { - private Map> TableMap = new HashMap<>(); - private Map>> ColumnsMap = new HashMap<>(); - private Map> EnumConfigMap = new HashMap<>(); - private Map EnumPackageMap = new HashMap<>(); - private Map EnumTableNameMap = new HashMap<>(); + public Map> TableMap = new HashMap<>(); + public Map>> ColumnsMap = new HashMap<>(); + public Map> EnumConfigMap = new HashMap<>(); + public Map EnumPackageMap = new HashMap<>(); + public Map EnumTableNameMap = new HashMap<>(); + public Map EntityJavaTypeMap = new HashMap<>(); + /** + * 注解缓存,注释为Key + */ + public Map> AnnotaionsCache = new HashMap<>(); + + public String dbType = "mysql"; + + public final String PATTERN_SPLITTER = "[\\,\\;]"; + @Override public void execute() throws MojoExecutionException, MojoFailureException { @@ -62,7 +72,9 @@ public void execute() throws MojoExecutionException, MojoFailureException { } getLog().info(""); this.getLog().info("开始生成实体代码"); - MysqlSchemaUtils.mojo = this; + SqlSchemaUtils.mojo = this; + this.dbType = recognizeDbType(connectionString); + processSqlDialet(this.dbType); // 项目结构解析 String absoluteCurrentDir, projectDir, domainModulePath, applicationModulePath, adapterModulePath; @@ -104,35 +116,31 @@ public void execute() throws MojoExecutionException, MojoFailureException { } // 数据库解析 - String tableSql = "select * from `information_schema`.`tables` where table_schema= '" + schema + "'"; - String columnSql = "select * from `information_schema`.`columns` where table_schema= '" + schema + "'"; - if (StringUtils.isNotBlank(table)) { - String whereClause = String.join(" or ", Arrays.stream(table.split("[\\,\\;]")).map(t -> "table_name like '" + t + "'").collect(Collectors.toList())); - tableSql += " and (" + whereClause + ")"; - columnSql += " and (" + whereClause + ")"; - } - if (StringUtils.isNotBlank(ignoreTable)) { - String whereClause = String.join(" or ", Arrays.stream(ignoreTable.split("[\\,\\;]")).map(t -> "table_name like '" + t + "'").collect(Collectors.toList())); - tableSql += " and not (" + whereClause + ")"; - columnSql += " and not (" + whereClause + ")"; - } - List> tables = executeQuery(tableSql, connectionString, user, pwd); - List> columns = executeQuery(columnSql, connectionString, user, pwd); + List> tables = resolveTables(connectionString, user, pwd); + List> columns = resolveColumns(connectionString, user, pwd); Map> relations = new HashMap<>(); Map tablePackageMap = new HashMap<>(); getLog().info(""); getLog().info("待解析数据库表:"); for (Map table : tables) { - List> tableColumns = columns.stream().filter(col -> col.get("TABLE_NAME").equals(table.get("TABLE_NAME"))) - .sorted((a, b) -> (Integer.parseInt(a.get("ORDINAL_POSITION").toString())) - Integer.parseInt(b.get("ORDINAL_POSITION").toString())) + List> tableColumns = columns.stream().filter(column -> isColumnInTable(column, table)) + .sorted(Comparator.comparingInt(SqlSchemaUtils::getOridinalPosition)) .collect(Collectors.toList()); - TableMap.put(MysqlSchemaUtils.getTableName(table), table); - ColumnsMap.put(MysqlSchemaUtils.getTableName(table), tableColumns); + TableMap.put(getTableName(table), table); + ColumnsMap.put(getTableName(table), tableColumns); getLog().info(String.format("%20s : (%s)", - MysqlSchemaUtils.getTableName(table), - String.join(", ", tableColumns.stream().map(c -> String.format("%s %s", c.get("DATA_TYPE"), MysqlSchemaUtils.getColumnName(c))).collect(Collectors.toList())))); + getTableName(table), + String.join(", ", tableColumns.stream() + .map(column -> + String.format( + "%s %s", + getColumnDbDataType(column), + getColumnName(column) + )).collect(Collectors.toList()) + ) + )); } getLog().info(""); getLog().info(""); @@ -141,9 +149,9 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info(""); for (Map table : TableMap.values()) { - List> tableColumns = ColumnsMap.get(MysqlSchemaUtils.getTableName(table)); + List> tableColumns = ColumnsMap.get(getTableName(table)); // 解析表关系 - getLog().info("开始解析表关系:" + MysqlSchemaUtils.getTableName(table)); + getLog().info("开始解析表关系:" + getTableName(table)); Map> relationTable = resolveRelationTable(table, tableColumns); for (Map.Entry> entry : relationTable.entrySet()) { @@ -153,8 +161,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { relations.get(entry.getKey()).putAll(entry.getValue()); } } - tablePackageMap.put(MysqlSchemaUtils.getTableName(table), resolvePackage(table, basePackage, domainModulePath)); - getLog().info("结束解析表关系:" + MysqlSchemaUtils.getTableName(table)); + tablePackageMap.put(getTableName(table), resolvePackage(table, basePackage, domainModulePath)); + getLog().info("结束解析表关系:" + getTableName(table)); getLog().info(""); } @@ -163,15 +171,15 @@ public void execute() throws MojoExecutionException, MojoFailureException { if (isIgnoreTable(table)) { continue; } - List> tableColumns = ColumnsMap.get(MysqlSchemaUtils.getTableName(table)); + List> tableColumns = ColumnsMap.get(getTableName(table)); for (Map column : tableColumns) { - if (MysqlSchemaUtils.hasEnum(column)) { - Map enumConfig = MysqlSchemaUtils.getEnum(column); + if (hasEnum(column)) { + Map enumConfig = getEnum(column); if (enumConfig.size() > 0) { - EnumConfigMap.put(MysqlSchemaUtils.getType(column), enumConfig); - EnumPackageMap.put(MysqlSchemaUtils.getType(column), basePackage + "." + getEntityPackage(MysqlSchemaUtils.getTableName(table)) + ".enums"); - EnumTableNameMap.put(MysqlSchemaUtils.getType(column), MysqlSchemaUtils.getTableName(table)); + EnumConfigMap.put(getType(column), enumConfig); + EnumPackageMap.put(getType(column), basePackage + "." + getEntityPackage(getTableName(table)) + ".enums"); + EnumTableNameMap.put(getType(column), getTableName(table)); } } } @@ -208,7 +216,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { } for (Map table : TableMap.values()) { - List> tableColumns = ColumnsMap.get(MysqlSchemaUtils.getTableName(table)); + List> tableColumns = ColumnsMap.get(getTableName(table)); try { writeEntitySourceFile(table, tableColumns, tablePackageMap, relations, basePackage, domainModulePath); } catch (IOException e) { @@ -228,42 +236,6 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info(""); } - public List> executeQuery(String sql, String connectionString, String user, String pwd) { - String URL = connectionString; - String USER = user; - String PASSWORD = pwd; - List> result = new ArrayList<>(); - try { - //1.加载驱动程序 - Class.forName("com.mysql.jdbc.Driver"); - //2.获得数据库链接 - Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); - //3.通过数据库的连接操作数据库,实现增删改查(使用Statement类) - Statement st = conn.createStatement(); - ResultSet rs = st.executeQuery(sql); - //4.处理数据库的返回结果(使用ResultSet类) - while (rs.next()) { - HashMap map = new HashMap<>(); - ResultSetMetaData metaData = rs.getMetaData(); - for (int i = 1; i <= metaData.getColumnCount(); i++) { - map.put(metaData.getColumnName(i), rs.getObject(i)); - if (rs.getObject(i) != null && rs.getObject(i) instanceof byte[]) { - map.put(metaData.getColumnName(i), new String((byte[]) rs.getObject(i))); - } - } - result.add(map); - } - //关闭资源 - rs.close(); - st.close(); - conn.close(); - } catch (Throwable e) { - this.getLog().error(e); - } - - return result; - } - /** * 获取模块 @@ -273,14 +245,14 @@ public List> executeQuery(String sql, String connectionStrin */ public String getModule(String tableName) { Map table = TableMap.get(tableName); - String module = MysqlSchemaUtils.getModule(table); - getLog().info("尝试解析模块:" + MysqlSchemaUtils.getTableName(table) + " " + module); - while (!MysqlSchemaUtils.isAggregateRoot(table) && StringUtils.isBlank(module)) { - table = TableMap.get(MysqlSchemaUtils.getParent(table)); - module = MysqlSchemaUtils.getModule(table); - getLog().info("尝试父表模块:" + MysqlSchemaUtils.getTableName(table) + " " + module); - } - getLog().info("模块解析结果:" + MysqlSchemaUtils.getTableName(table) + " " + module); + String module = SqlSchemaUtils.getModule(table); + getLog().info("尝试解析模块:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[缺失]" : module)); + while (!isAggregateRoot(table) && StringUtils.isBlank(module)) { + table = TableMap.get(getParent(table)); + module = SqlSchemaUtils.getModule(table); + getLog().info("尝试父表模块:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[缺失]" : module)); + } + getLog().info("模块解析结果:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[无]" : module)); return module; } @@ -292,26 +264,24 @@ public String getModule(String tableName) { */ public String getAggregate(String tableName) { Map table = TableMap.get(tableName); - String aggregate = MysqlSchemaUtils.getAggregate(table); - getLog().info("尝试解析聚合:" + MysqlSchemaUtils.getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); - while (!MysqlSchemaUtils.isAggregateRoot(table) && StringUtils.isBlank(aggregate)) { - String parent = MysqlSchemaUtils.getParent(table); + String aggregate = SqlSchemaUtils.getAggregate(table); + getLog().info("尝试解析聚合:" + getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); + while (!isAggregateRoot(table) && StringUtils.isBlank(aggregate)) { + String parent = getParent(table); if (StringUtils.isBlank(parent)) { break; } table = TableMap.get(parent); - aggregate = MysqlSchemaUtils.getAggregate(table); - getLog().info("尝试父表聚合:" + MysqlSchemaUtils.getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); + aggregate = SqlSchemaUtils.getAggregate(table); + getLog().info("尝试父表聚合:" + getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); } if (StringUtils.isBlank(aggregate)) { - aggregate = getEntityJavaType(MysqlSchemaUtils.getTableName(table)); + aggregate = getEntityJavaType(getTableName(table)); } getLog().info("聚合解析结果:" + tableName + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); return aggregate; } - private Map EntityJavaTypeMap = new HashMap<>(); - /** * 获取实体类 Class.SimpleName * @@ -323,12 +293,12 @@ public String getEntityJavaType(String tableName) { return EntityJavaTypeMap.get(tableName); } Map table = TableMap.get(tableName); - String type = MysqlSchemaUtils.getType(table); + String type = getType(table); if (StringUtils.isBlank(type)) { type = toUpperCamelCase(tableName); } if (StringUtils.isNotBlank(type)) { - getLog().info("解析实体类名:" + MysqlSchemaUtils.getTableName(table) + " --> " + type); + getLog().info("解析实体类名:" + getTableName(table) + " --> " + type); EntityJavaTypeMap.put(tableName, type); return type; } @@ -353,7 +323,7 @@ public String getEntityPackage(String tableName) { public boolean isReservedColumn(Map column) { - String columnName = MysqlSchemaUtils.getColumnName(column).toLowerCase(); + String columnName = getColumnName(column).toLowerCase(); boolean isReserved = idField.equalsIgnoreCase(columnName) || versionField.equalsIgnoreCase(columnName) || columnName.startsWith("db_"); @@ -361,12 +331,12 @@ public boolean isReservedColumn(Map column) { } public boolean isReadOnlyColumn(Map column) { - if (MysqlSchemaUtils.hasReadOnly(column)) { + if (hasReadOnly(column)) { return true; } - String columnName = MysqlSchemaUtils.getColumnName(column).toLowerCase(); + String columnName = getColumnName(column).toLowerCase(); if (StringUtils.isNotBlank(readonlyFields) - && Arrays.stream(readonlyFields.toLowerCase().split("[\\,\\;]")).anyMatch( + && Arrays.stream(readonlyFields.toLowerCase().split(PATTERN_SPLITTER)).anyMatch( c -> columnName.matches(c.replace("%", ".*")))) { return true; } @@ -375,108 +345,25 @@ public boolean isReadOnlyColumn(Map column) { } public boolean isIgnoreTable(Map table) { - if (MysqlSchemaUtils.isIgnore(table)) { + if (isIgnore(table)) { return true; } return false; } public boolean isIgnoreColumn(Map column) { - if (MysqlSchemaUtils.isIgnore(column)) { + if (isIgnore(column)) { return true; } - String columnName = MysqlSchemaUtils.getColumnName(column).toLowerCase(); + String columnName = getColumnName(column).toLowerCase(); if (StringUtils.isNotBlank(ignoreFields) - && Arrays.stream(ignoreFields.toLowerCase().split("[\\,\\;]")).anyMatch( + && Arrays.stream(ignoreFields.toLowerCase().split(PATTERN_SPLITTER)).anyMatch( c -> columnName.matches(c.replace("%", ".*")))) { return true; } return false; } - /** - * 获取列的Java映射类型 - * - * @param column - * @return - */ - public String getColumnJavaType(Map column) { - if (MysqlSchemaUtils.hasType(column)) { - String customerType = MysqlSchemaUtils.getType(column); - if (MysqlSchemaUtils.hasEnum(column) && EnumPackageMap.containsKey(customerType)) { - return EnumPackageMap.get(customerType) + "." + customerType; - } else { - return customerType; - } - } - String dataType = column.get("DATA_TYPE").toString().toLowerCase(); - String columnType = column.get("COLUMN_TYPE").toString().toLowerCase(); - String comment = MysqlSchemaUtils.getComment(column); - String columnName = MysqlSchemaUtils.getColumnName(column).toLowerCase(); - if (typeRemapping != null && typeRemapping.containsKey(dataType)) { - // 类型重映射 - return typeRemapping.get(dataType); - } - switch (dataType) { - case "varchar": - case "text": - case "mediumtext": - case "longtext": - case "char": - return "String"; - case "timestamp": - case "datetime": - if ("java.time".equalsIgnoreCase(datePackage4Java)) { - return "java.time.LocalDateTime"; - } else { - return "java.util.Date"; - } - case "date": - if ("java.time".equalsIgnoreCase(datePackage4Java)) { - return "java.time.LocalDate"; - } else { - return "java.util.Date"; - } - case "time": - if ("java.time".equalsIgnoreCase(datePackage4Java)) { - return "java.time.LocalTime"; - } else { - return "java.util.Date"; - } - case "int": - return "Integer"; - case "bigint": - return "Long"; - case "smallint": - return "Short"; - case "bit": - return "Boolean"; - case "tinyint": - if (".deleted.".contains("." + columnName + ".")) { - return "Boolean"; - } - if (deletedField.equalsIgnoreCase(columnName)) { - return "Boolean"; - } - if (columnType.equalsIgnoreCase("tinyint(1)")) { - return "Boolean"; - } - if (comment.contains("是否")) { - return "Boolean"; - } - return "Byte"; - case "float": - return "Float"; - case "double": - return "Double"; - case "decimal": - return "java.math.BigDecimal"; - default: - break; - } - throw new RuntimeException("包含未支持字段类型!" + dataType); - } - /** * 关系格式 table1 table2 relation;join_column;[inverse_join_column;][table;][LAZY;] *

@@ -490,36 +377,36 @@ public String getColumnJavaType(Map column) { */ public Map> resolveRelationTable(Map table, List> columns) { Map> result = new HashMap<>(); - String tableName = MysqlSchemaUtils.getTableName(table); + String tableName = getTableName(table); if (isIgnoreTable(table)) { return result; } // 聚合内部关系 OneToMany - if (!MysqlSchemaUtils.isAggregateRoot(table)) { - String parent = MysqlSchemaUtils.getParent(table); + if (!isAggregateRoot(table)) { + String parent = getParent(table); result.putIfAbsent(parent, new HashMap<>()); boolean rewrited = false; for (Map column : columns) { - if (MysqlSchemaUtils.hasReference(column)) { - if (parent.equalsIgnoreCase(MysqlSchemaUtils.getReference(column))) { - boolean lazy = MysqlSchemaUtils.isLazy(column, "LAZY".equalsIgnoreCase(this.fetchType)); - result.get(parent).putIfAbsent(tableName, "OneToMany;" + MysqlSchemaUtils.getColumnName(column) + (lazy ? ";LAZY" : "")); + if (hasReference(column)) { + if (parent.equalsIgnoreCase(getReference(column))) { + boolean lazy = isLazy(column, "LAZY".equalsIgnoreCase(this.fetchType)); + result.get(parent).putIfAbsent(tableName, "OneToMany;" + getColumnName(column) + (lazy ? ";LAZY" : "")); rewrited = true; } } } if (!rewrited) { - Map column = columns.stream().filter(c -> MysqlSchemaUtils.getColumnName(c).equals(parent + "_id")).findFirst().orElseGet(() -> null); + Map column = columns.stream().filter(c -> getColumnName(c).equals(parent + "_id")).findFirst().orElseGet(() -> null); if (column != null) { - boolean lazy = MysqlSchemaUtils.isLazy(column, "LAZY".equalsIgnoreCase(this.fetchType)); + boolean lazy = isLazy(column, "LAZY".equalsIgnoreCase(this.fetchType)); result.get(parent).putIfAbsent(tableName, "OneToMany;" + parent + "_id" + (lazy ? ";LAZY" : "")); } } } // 聚合之间关系 - if (MysqlSchemaUtils.hasRelation(table)) { + if (hasRelation(table)) { // ManyToMany String owner = ""; String beowned = ""; @@ -527,17 +414,17 @@ public Map> resolveRelationTable(Map String inverseJoinColumn = ""; boolean ownerLazy = false; for (Map column : columns) { - if (MysqlSchemaUtils.hasReference(column)) { - String refTableName = MysqlSchemaUtils.getReference(column); + if (hasReference(column)) { + String refTableName = getReference(column); result.putIfAbsent(refTableName, new HashMap<>()); - boolean lazy = MysqlSchemaUtils.isLazy(column, "LAZY".equalsIgnoreCase(this.fetchType)); + boolean lazy = isLazy(column, "LAZY".equalsIgnoreCase(this.fetchType)); if (StringUtils.isBlank(owner)) { ownerLazy = lazy; owner = refTableName; - joinCol = MysqlSchemaUtils.getColumnName(column); + joinCol = getColumnName(column); } else { beowned = refTableName; - inverseJoinColumn = MysqlSchemaUtils.getColumnName(column); + inverseJoinColumn = getColumnName(column); result.get(beowned).putIfAbsent(owner, "*ManyToMany;" + inverseJoinColumn + (lazy ? ";LAZY" : "")); } } @@ -546,14 +433,14 @@ public Map> resolveRelationTable(Map } for (Map column : columns) { - String colRel = MysqlSchemaUtils.getRelation(column); - String colName = MysqlSchemaUtils.getColumnName(column); + String colRel = getRelation(column); + String colName = getColumnName(column); String refTableName = null; - if (StringUtils.isNotBlank(colRel) || MysqlSchemaUtils.hasRelation(column)) { + if (StringUtils.isNotBlank(colRel) || hasRelation(column)) { switch (colRel) { case "OneToOne": case "1:1": - refTableName = MysqlSchemaUtils.getReference(column); + refTableName = getReference(column); result.putIfAbsent(tableName, new HashMap<>()); result.get(tableName).putIfAbsent(refTableName, "OneToOne;" + colName); result.putIfAbsent(refTableName, new HashMap<>()); @@ -562,7 +449,7 @@ public Map> resolveRelationTable(Map case "ManyToOne": case "*:1": default: - refTableName = MysqlSchemaUtils.getReference(column); + refTableName = getReference(column); result.putIfAbsent(tableName, new HashMap<>()); result.get(tableName).putIfAbsent(refTableName, "ManyToOne;" + colName); result.putIfAbsent(refTableName, new HashMap<>()); @@ -574,60 +461,6 @@ public Map> resolveRelationTable(Map return result; } - public String getColumnDefaultJavaLiteral(Map column) { - String columnDefault = column.get("COLUMN_DEFAULT") == null ? null : column.get("COLUMN_DEFAULT").toString(); - switch (getColumnJavaType(column)) { - case "String": - if (StringUtils.isNotEmpty(columnDefault)) { - return "\"" + columnDefault.replace("\"", "\\\"") + "\""; - } else { - return "\"\""; - } - case "Integer": - case "Short": - case "Byte": - if (StringUtils.isNotEmpty(columnDefault)) { - return "" + columnDefault; - } else { - return "0"; - } - case "Long": - if (StringUtils.isNotEmpty(columnDefault)) { - return "" + columnDefault + "L"; - } else { - return "0L"; - } - case "Boolean": - if (StringUtils.isNotEmpty(columnDefault)) { - if (columnDefault.trim().equalsIgnoreCase("b'1'")) { - return "false"; - } else if (columnDefault.trim().equalsIgnoreCase("b'0'")) { - return "true"; - } - return "" + (columnDefault.trim().equalsIgnoreCase("0") ? "false" : "true"); - } else { - return "false"; - } - case "Float": - case "Double": - if (StringUtils.isNotEmpty(columnDefault)) { - return "" + columnDefault; - } else { - return "0"; - } - case "java.math.BigDecimal": - if (StringUtils.isNotEmpty(columnDefault)) { - return "java.math.BigDecimal.valueOf(" + columnDefault + ")"; - } else { - return "java.math.BigDecimal.ZERO"; - } - case "java.util.Date": - default: - break; - } - return ""; // = "" - } - public boolean readCustomerSourceFile(String filePath, List importLines, List annotationLines, List customerLines) throws IOException { if (FileUtils.fileExists(filePath)) { String simpleClassName = SourceFileUtils.resolveSimpleClassName(filePath); @@ -695,14 +528,14 @@ public void processImportLines(Map table, String basePackage, Li } importLines.add(""); importLines.add("/**"); - for (String comment : MysqlSchemaUtils.getComment(table).split("[\\r\\n]")) { + for (String comment : getComment(table).split("[\\r\\n]")) { if (StringUtils.isEmpty(comment)) { continue; } importLines.add(" * " + comment); } importLines.add(" *"); - // importLines.add(" * " + MysqlSchemaUtils.getComment(table).replaceAll("[\\r\\n]", " ")); + // importLines.add(" * " + getComment(table).replaceAll("[\\r\\n]", " ")); importLines.add(" * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); importLines.add(" * 警告:请勿手工修改该文件的字段声明,重新生成会覆盖字段声明"); importLines.add(" */"); @@ -727,7 +560,7 @@ public void processImportLines(Map table, String basePackage, Li String importLine = importLines.get(i); if (importLine.contains(" org.hibernate.annotations.") && !importLine.contains("*")) { - String hibernateAnnotation = importLine.substring(importLine.lastIndexOf('.') + 1).replace(";", "").trim(); + String hibernateAnnotation = importLine.substring(importLine.lastIndexOf(".") + 1).replace(";", "").trim(); if (!content.contains(hibernateAnnotation)) { importLines.remove(importLine); i--; @@ -737,16 +570,16 @@ public void processImportLines(Map table, String basePackage, Li } public void processAnnotationLines(Map table, List> columns, List annotationLines) { - String tableName = MysqlSchemaUtils.getTableName(table); + String tableName = getTableName(table); boolean annotationEmpty = annotationLines.size() == 0; SourceFileUtils.removeText(annotationLines, "@Aggregate\\(.*\\)"); - if (MysqlSchemaUtils.isAggregateRoot(table)) { - SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(aggregate = \"" + getAggregate(tableName) + "\", name = \"" + getEntityJavaType(tableName) + "\", type = \"root\", description = \"" + MysqlSchemaUtils.getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); + if (isAggregateRoot(table)) { + SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(aggregate = \"" + getAggregate(tableName) + "\", name = \"" + getEntityJavaType(tableName) + "\", type = \"root\", description = \"" + getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); } else { - SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(aggregate = \"" + getAggregate(tableName) + "\", name = \"" + getEntityJavaType(tableName) + "\", type = \"entity\", relevant = { \"" + getEntityJavaType(MysqlSchemaUtils.getParent(table)) + "\" }, description = \"" + MysqlSchemaUtils.getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); + SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(aggregate = \"" + getAggregate(tableName) + "\", name = \"" + getEntityJavaType(tableName) + "\", type = \"entity\", relevant = { \"" + getEntityJavaType(getParent(table)) + "\" }, description = \"" + getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); } if (StringUtils.isNotBlank(getAggregateRootAnnotation())) { - if (MysqlSchemaUtils.isAggregateRoot(table)) { + if (isAggregateRoot(table)) { SourceFileUtils.addIfNone(annotationLines, getAggregateRootAnnotation() + "(\\(.*\\))?", getAggregateRootAnnotation()); } else { SourceFileUtils.removeText(annotationLines, getAggregateRootAnnotation() + "(\\(.*\\))?"); @@ -754,26 +587,26 @@ public void processAnnotationLines(Map table, List table, List table, String basePackage, String baseDir) { - String tableName = MysqlSchemaUtils.getTableName(table); + String tableName = getTableName(table); String simpleClassName = getEntityJavaType(tableName); String packageName = (basePackage + "." + getEntityPackage(tableName)); @@ -803,13 +636,13 @@ public String resolvePackage(Map table, String basePackage, Stri } public void writeEntitySourceFile(Map table, List> columns, Map tablePackageMap, Map> relations, String basePackage, String baseDir) throws IOException { - String tableName = MysqlSchemaUtils.getTableName(table); + String tableName = getTableName(table); if (isIgnoreTable(table)) { getLog().info("跳过忽略表:" + tableName); return; } - if (MysqlSchemaUtils.hasRelation(table) - // &&"ManyToMany".equalsIgnoreCase(MysqlSchemaUtils.getRelation(table)) + if (hasRelation(table) + // &&"ManyToMany".equalsIgnoreCase(getRelation(table)) ) { getLog().info("跳过关系表:" + tableName); return; @@ -819,7 +652,7 @@ public void writeEntitySourceFile(Map table, List table, List table, List> columns, Map tablePackageMap, Map> relations, List enums, List annotationLines, List customerLines) throws IOException { - String tableName = MysqlSchemaUtils.getTableName(table); + String tableName = getTableName(table); String simpleClassName = getEntityJavaType(tableName); StringWriter stringWriter = new StringWriter(); BufferedWriter out = new BufferedWriter(stringWriter); @@ -874,29 +707,29 @@ private String generateEntityClassMainSource(Map table, List column : columns) { writeColumnProperty(out, table, column, relations, enums); } writeRelationProperty(out, table, relations, tablePackageMap); - if (MysqlSchemaUtils.hasColumn(versionField, columns)) { + if (hasColumn(versionField, columns)) { writeLine(out, ""); writeLine(out, " /**"); writeLine(out, " * 数据版本(支持乐观锁)"); writeLine(out, " */"); writeLine(out, " @Version"); - writeLine(out, " @Column(name = \"`" + versionField + "`\")"); + writeLine(out, " @Column(name = \"" + LEFT_QUOTES_4_ID_ALIAS + versionField + RIGHT_QUOTES_4_ID_ALIAS + "\")"); if (generateDefault) { writeLine(out, " @Builder.Default"); writeLine(out, " Integer " + toLowerCamelCase(versionField) + " = 0;"); @@ -915,8 +748,8 @@ private String generateEntityClassMainSource(Map table, List table, Map column, Map> relations) { - String tableName = MysqlSchemaUtils.getTableName(table); - String columnName = MysqlSchemaUtils.getColumnName(column); + String tableName = getTableName(table); + String columnName = getColumnName(column); if (isIgnoreColumn(column)) { return false; } @@ -924,8 +757,8 @@ public boolean needGenerateField(Map table, Map return false; } - if (!MysqlSchemaUtils.isAggregateRoot(table)) { - if (columnName.equalsIgnoreCase(MysqlSchemaUtils.getParent(table) + "_id")) { + if (!isAggregateRoot(table)) { + if (columnName.equalsIgnoreCase(getParent(table) + "_id")) { return false; } } @@ -959,10 +792,10 @@ public void writeEntityBuilderSourceFile(String basePackage, String baseDir, Map if (isIgnoreTable(table)) { continue; } - if (MysqlSchemaUtils.hasRelation(table)) { + if (hasRelation(table)) { continue; } - String tableName = MysqlSchemaUtils.getTableName(table); + String tableName = getTableName(table); String simpleClassName = getEntityJavaType(tableName); writeLine(out, "import " + tablePackageMap.get(tableName) + "." + simpleClassName + ";"); } @@ -977,7 +810,7 @@ public void writeEntityBuilderSourceFile(String basePackage, String baseDir, Map if (isIgnoreTable(table)) { continue; } - if (MysqlSchemaUtils.hasRelation(table)) { + if (hasRelation(table)) { continue; } writeBuildEntityMethod(out, table, ColumnsMap.get(tableEntry.getKey()), relations); @@ -990,11 +823,11 @@ public void writeEntityBuilderSourceFile(String basePackage, String baseDir, Map public void writeBuildEntityMethod(BufferedWriter out, Map table, List> columns, Map> relations) { - String tableName = MysqlSchemaUtils.getTableName(table); + String tableName = getTableName(table); String simpleClassName = getEntityJavaType(tableName); writeLine(out, ""); writeLine(out, " /**"); - for (String comment : MysqlSchemaUtils.getComment(table).split("[\\r\\n]")) { + for (String comment : getComment(table).split("[\\r\\n]")) { if (StringUtils.isEmpty(comment)) { continue; } @@ -1017,10 +850,10 @@ public void writeBuildEntityMethod(BufferedWriter out, Map table if ("Byte".equalsIgnoreCase(getColumnJavaType(column))) { defaultJavaLiteral = "(byte)" + defaultJavaLiteral; } - writeLine(out, " ." + toLowerCamelCase(MysqlSchemaUtils.getColumnName(column)) + "(fieldFillNull ? null : " + defaultJavaLiteral + ")"); + writeLine(out, " ." + toLowerCamelCase(getColumnName(column)) + "(fieldFillNull ? null : " + defaultJavaLiteral + ")"); } - if (MysqlSchemaUtils.hasColumn(versionField, columns)) { + if (hasColumn(versionField, columns)) { writeLine(out, " ." + toLowerCamelCase(versionField) + "(fieldFillNull ? null : 0)"); } writeLine(out, " ;"); @@ -1028,7 +861,7 @@ public void writeBuildEntityMethod(BufferedWriter out, Map table } public void writeColumnProperty(BufferedWriter out, Map table, Map column, Map> relations, List enums) { - String columnName = MysqlSchemaUtils.getColumnName(column); + String columnName = getColumnName(column); String columnJavaType = getColumnJavaType(column); if (!needGenerateField(table, column, relations)) { @@ -1038,36 +871,30 @@ public void writeColumnProperty(BufferedWriter out, Map table, M boolean updatable = true; boolean insertable = true; if (getColumnJavaType(column).contains("Date")) { - String extra = column.get("EXTRA") == null ? "" : column.get("EXTRA").toString(); - if ("on update CURRENT_TIMESTAMP".equalsIgnoreCase(extra)) { - updatable = false; - } - String defaultData = column.get("COLUMN_DEFAULT") == null ? "" : column.get("COLUMN_DEFAULT").toString(); - if ("CURRENT_TIMESTAMP".equalsIgnoreCase(defaultData)) { - insertable = true; - } + updatable = !isAutoUpdateDateColumn(column); + insertable = !isAutoInsertDateColumn(column); } if (isReadOnlyColumn(column)) { insertable = false; updatable = false; } - if (MysqlSchemaUtils.hasIgnoreInsert(column)) { + if (hasIgnoreInsert(column)) { insertable = false; } - if (MysqlSchemaUtils.hasIgnoreUpdate(column)) { + if (hasIgnoreUpdate(column)) { updatable = false; } writeLine(out, ""); writeColumnComment(out, column); - if (MysqlSchemaUtils.hasEnum(column)) { + if (hasEnum(column)) { enums.add(columnJavaType); writeLine(out, " @Convert(converter = " + columnJavaType + ".Converter.class)"); } if (!updatable || !insertable) { - writeLine(out, " @Column(name = \"`" + columnName + "`\", insertable = " + (insertable ? "true" : "false") + ", updatable = " + (updatable ? "true" : "false") + ")"); + writeLine(out, " @Column(name = \"" + LEFT_QUOTES_4_ID_ALIAS + columnName + RIGHT_QUOTES_4_ID_ALIAS + "\", insertable = " + (insertable ? "true" : "false") + ", updatable = " + (updatable ? "true" : "false") + ")"); } else { - writeLine(out, " @Column(name = \"`" + columnName + "`\")"); + writeLine(out, " @Column(name = \"" + LEFT_QUOTES_4_ID_ALIAS + columnName + RIGHT_QUOTES_4_ID_ALIAS + "\")"); } if (generateDefault) { String defaultJavaLiteral = getColumnDefaultJavaLiteral(column); @@ -1081,19 +908,19 @@ public void writeColumnProperty(BufferedWriter out, Map table, M } public void writeColumnComment(BufferedWriter out, Map column) { - String columnName = MysqlSchemaUtils.getColumnName(column); + String columnName = getColumnName(column); String columnJavaType = getColumnJavaType(column); writeLine(out, " /**"); - for (String comment : MysqlSchemaUtils.getComment(column).split("[\\r\\n]")) { + for (String comment : getComment(column).split("[\\r\\n]")) { if (StringUtils.isEmpty(comment)) { continue; } writeLine(out, " * " + comment); - if (MysqlSchemaUtils.hasEnum(column)) { + if (hasEnum(column)) { getLog().info("获取枚举java类型:" + columnName + " -> " + columnJavaType); Map enumMap = EnumConfigMap.get(columnJavaType); if (enumMap == null) { - enumMap = EnumConfigMap.get(MysqlSchemaUtils.getType(column)); + enumMap = EnumConfigMap.get(getType(column)); } if (enumMap != null) { writeLine(out, " * " + String.join(";", enumMap.entrySet().stream() @@ -1102,13 +929,13 @@ public void writeColumnComment(BufferedWriter out, Map column) { } } if (generateDbType) { - writeLine(out, " * " + MysqlSchemaUtils.getColumnDbType(column)); + writeLine(out, " * " + getColumnDbType(column)); } writeLine(out, " */"); } public void writeRelationProperty(BufferedWriter out, Map table, Map> relations, Map tablePackageMap) { - String tableName = MysqlSchemaUtils.getTableName(table); + String tableName = getTableName(table); if (relations.containsKey(tableName)) { for (Map.Entry entry : relations.get(tableName).entrySet()) { String[] refInfos = entry.getValue().split(";"); @@ -1119,8 +946,8 @@ public void writeRelationProperty(BufferedWriter out, Map table, } else { fetchType = "EAGER"; } - if (MysqlSchemaUtils.hasLazy(navTable)) { - fetchType = MysqlSchemaUtils.isLazy(navTable) ? "LAZY" : "EAGER"; + if (hasLazy(navTable)) { + fetchType = isLazy(navTable) ? "LAZY" : "EAGER"; getLog().warn(tableName + ":" + entry.getKey() + ":" + fetchType); } if ("ManyToOne".equals(refInfos[0])) { @@ -1131,8 +958,8 @@ public void writeRelationProperty(BufferedWriter out, Map table, switch (refInfos[0]) { case "OneToMany": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY, orphanRemoval = true)"); - writeLine(out, " @JoinColumn(name = \"`" + refInfos[1] + "`\", nullable = false)"); - boolean countIsOne = MysqlSchemaUtils.countIsOne(navTable); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\", nullable = false)"); + boolean countIsOne = countIsOne(navTable); if (countIsOne) { writeLine(out, " @Getter(lombok.AccessLevel.PROTECTED)"); } @@ -1147,12 +974,12 @@ public void writeRelationProperty(BufferedWriter out, Map table, break; case "ManyToOne": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)"); - writeLine(out, " @JoinColumn(name = \"`" + refInfos[1] + "`\")"); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")"); writeLine(out, " private " + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + " " + toLowerCamelCase(getEntityJavaType(entry.getKey())) + ";"); break; case "OneToOne": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)"); - writeLine(out, " @JoinColumn(name = \"`" + refInfos[1] + "`\")"); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")"); writeLine(out, " private " + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + " " + toLowerCamelCase(getEntityJavaType(entry.getKey())) + ";"); break; case "*OneToMany": @@ -1165,7 +992,7 @@ public void writeRelationProperty(BufferedWriter out, Map table, break; case "ManyToMany": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)"); - writeLine(out, " @JoinTable(name = \"`" + refInfos[3] + "`\", joinColumns = {@JoinColumn(name = \"`" + refInfos[1] + "`\")}, inverseJoinColumns = {@JoinColumn(name = \"`" + refInfos[2] + "`\")})"); + writeLine(out, " @JoinTable(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[3] + RIGHT_QUOTES_4_ID_ALIAS + "\", joinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")}, inverseJoinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[2] + RIGHT_QUOTES_4_ID_ALIAS + "\")})"); writeLine(out, " private java.util.List<" + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + "> " + Inflector.getInstance().pluralize(toLowerCamelCase(getEntityJavaType(entry.getKey()))) + ";"); break; case "*ManyToMany": @@ -1181,8 +1008,8 @@ public void writeRelationProperty(BufferedWriter out, Map table, switch (refInfos[0]) { case "OneToMany": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true) @Fetch(FetchMode." + fetchMode + ")"); - writeLine(out, " @JoinColumn(name = \"`" + refInfos[1] + "`\", nullable = false)"); - boolean countIsOne = MysqlSchemaUtils.countIsOne(navTable); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\", nullable = false)"); + boolean countIsOne = countIsOne(navTable); if (countIsOne) { writeLine(out, " @Getter(lombok.AccessLevel.PROTECTED)"); } @@ -1197,12 +1024,12 @@ public void writeRelationProperty(BufferedWriter out, Map table, break; case "ManyToOne": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) @Fetch(FetchMode." + fetchMode + ")"); - writeLine(out, " @JoinColumn(name = \"`" + refInfos[1] + "`\")"); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")"); writeLine(out, " private " + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + " " + toLowerCamelCase(getEntityJavaType(entry.getKey())) + ";"); break; case "OneToOne": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) @Fetch(FetchMode." + fetchMode + ")"); - writeLine(out, " @JoinColumn(name = \"`" + refInfos[1] + "`\")"); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")"); writeLine(out, " private " + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + " " + toLowerCamelCase(getEntityJavaType(entry.getKey())) + ";"); break; case "*OneToMany": @@ -1215,7 +1042,7 @@ public void writeRelationProperty(BufferedWriter out, Map table, break; case "ManyToMany": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) @Fetch(FetchMode." + fetchMode + ")"); - writeLine(out, " @JoinTable(name = \"`" + refInfos[3] + "`\", joinColumns = {@JoinColumn(name = \"`" + refInfos[1] + "`\")}, inverseJoinColumns = {@JoinColumn(name = \"`" + refInfos[2] + "`\")})"); + writeLine(out, " @JoinTable(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[3] + RIGHT_QUOTES_4_ID_ALIAS + "\", joinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")}, inverseJoinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[2] + RIGHT_QUOTES_4_ID_ALIAS + "\")})"); writeLine(out, " private java.util.List<" + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + "> " + Inflector.getInstance().pluralize(toLowerCamelCase(getEntityJavaType(entry.getKey()))) + ";"); break; case "*ManyToMany": @@ -1311,7 +1138,7 @@ public void writeEnumSourceFile(Map enumConfigs, String enumT } public void writeSchemaSourceFile(Map table, List> columns, Map tablePackageMap, Map> relations, String basePackage, String baseDir) throws IOException { - String tableName = MysqlSchemaUtils.getTableName(table); + String tableName = getTableName(table); String packageName = null; if ("abs".equalsIgnoreCase(entityMetaInfoClassOutputMode)) { packageName = basePackage + "." + entityMetaInfoClassOutputPackage + ".schemas"; @@ -1341,7 +1168,7 @@ public void writeSchemaSourceFile(Map table, List table, List " + toLowerCamelCase(MysqlSchemaUtils.getColumnName(column)) + "() {\n" + - " return root == null ? new Schema.Field<>(\"" + toLowerCamelCase(MysqlSchemaUtils.getColumnName(column)) + "\") : new Schema.Field<>(root.get(\"" + toLowerCamelCase(MysqlSchemaUtils.getColumnName(column)) + "\"));\n" + + writeLine(out, " public Schema.Field<" + getColumnJavaType(column) + "> " + toLowerCamelCase(getColumnName(column)) + "() {\n" + + " return root == null ? new Schema.Field<>(\"" + toLowerCamelCase(getColumnName(column)) + "\") : new Schema.Field<>(root.get(\"" + toLowerCamelCase(getColumnName(column)) + "\"));\n" + " }"); } writeLine(out, ""); @@ -1456,7 +1283,7 @@ public void writeSchemaSourceFile(Map table, List table, Map> relations, Map tablePackageMap) { - String tableName = MysqlSchemaUtils.getTableName(table); + String tableName = getTableName(table); int count = 0; if (relations.containsKey(tableName)) { for (Map.Entry entry : relations.get(tableName).entrySet()) { diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java index 13940ed..4a67d63 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java @@ -21,7 +21,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${archTemplate}" */ @Parameter(property = "archTemplate", defaultValue = "") - String archTemplate = ""; + public String archTemplate = ""; /** * 代码模板配置文件编码,默认UFT-8 @@ -29,7 +29,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${archTemplateEncoding}" */ @Parameter(property = "archTemplateEncoding", defaultValue = "UTF-8") - String archTemplateEncoding = "UTF-8"; + public String archTemplateEncoding = "UTF-8"; /** * 基础包路径 @@ -37,7 +37,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${basePackage}" */ @Parameter(property = "basePackage", defaultValue = "") - String basePackage = ""; + public String basePackage = ""; /** * 是否多模块项目 @@ -45,28 +45,28 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${multiModule}" */ @Parameter(property = "multiModule", defaultValue = "false") - Boolean multiModule = false; + public Boolean multiModule = false; /** * adapter模块名称后缀 * * @parameter expression="${moduleNameSuffix4Adapter}" */ @Parameter(property = "moduleNameSuffix4Adapter", defaultValue = "-adapter") - String moduleNameSuffix4Adapter = "-adapter"; + public String moduleNameSuffix4Adapter = "-adapter"; /** * domain模块名称后缀 * * @parameter expression="${moduleNameSuffix4Domain}" */ @Parameter(property = "moduleNameSuffix4Domain", defaultValue = "-domain") - String moduleNameSuffix4Domain = "-domain"; + public String moduleNameSuffix4Domain = "-domain"; /** * application模块名称后缀 * * @parameter expression="${moduleNameSuffix4Application}" */ @Parameter(property = "moduleNameSuffix4Application", defaultValue = "-application") - String moduleNameSuffix4Application = "-application"; + public String moduleNameSuffix4Application = "-application"; /** * 数据库连接地址 @@ -74,28 +74,28 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${connectionString}" */ @Parameter(property = "connectionString") - String connectionString = ""; + public String connectionString = ""; /** * 数据库连接用户 * * @parameter expression="${user}" */ @Parameter(property = "user") - String user = ""; + public String user = ""; /** * 数据库连接密码 * * @parameter expression="${pwd}" */ @Parameter(property = "pwd") - String pwd = ""; + public String pwd = ""; /** * 数据库过滤 * * @parameter expression="${schema}" */ @Parameter(property = "schema") - String schema = ""; + public String schema = ""; /** * 数据表过滤 * 逗号','或分号';'分割,支持通配符'%' @@ -103,7 +103,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${table}" */ @Parameter(property = "table", defaultValue = "") - String table = ""; + public String table = ""; /** * 数据表忽略 * 被忽略的表不生成实体 @@ -111,28 +111,28 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${ignoreTable}" */ @Parameter(property = "ignoreTable", defaultValue = "") - String ignoreTable = ""; + public String ignoreTable = ""; /** * 主键字段名 默认'id' * * @parameter expression="${idField}" */ @Parameter(property = "idField", defaultValue = "id") - String idField = "id"; + public String idField = "id"; /** * 乐观锁字段 默认'version' * * @parameter expression="${versionField}" */ @Parameter(property = "versionField", defaultValue = "version") - String versionField = "version"; + public String versionField = "version"; /** * 软删字段 默认'deleted' * * @parameter expression="${deletedField}" */ @Parameter(property = "deletedField", defaultValue = "deleted") - String deletedField = "deleted"; + public String deletedField = "deleted"; /** * 只读字段 * 逗号','或分号';'分割,不会通过ORM更新到数据库 @@ -140,7 +140,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${readonlyFields}" */ @Parameter(property = "readonlyFields", defaultValue = "") - String readonlyFields = ""; + public String readonlyFields = ""; /** * 忽略字段 * 逗号','或分号';'分割,不会通过ORM绑定到实体 @@ -148,7 +148,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${ignoreFields}" */ @Parameter(property = "ignoreFields", defaultValue = "") - String ignoreFields = ""; + public String ignoreFields = ""; /** * 实体基类 @@ -156,7 +156,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${entityBaseClass}" */ @Parameter(property = "entityBaseClass", defaultValue = "") - String entityBaseClass = ""; + public String entityBaseClass = ""; /** * 实体类附加导入包 @@ -164,7 +164,7 @@ public abstract class MyAbstractMojo extends AbstractMojo { * @parameter expression="${entityClassExtraImports}" */ @Parameter(property = "entityClassExtraImports", defaultValue = "") - List entityClassExtraImports = new ArrayList<>(); + public List entityClassExtraImports = new ArrayList<>(); public List getEntityClassExtraImports() { List importList = Arrays.asList( @@ -193,7 +193,7 @@ public List getEntityClassExtraImports() { * @parameter expression="${entityMetaInfoClassOutputMode}" */ @Parameter(property = "entityMetaInfoClassOutputMode", defaultValue = "") - String entityMetaInfoClassOutputMode = "abs"; + public String entityMetaInfoClassOutputMode = "abs"; /** * 实体辅助类输出包 @@ -201,7 +201,7 @@ public List getEntityClassExtraImports() { * @parameter expression="${entityMetaInfoClassOutputPackage}" */ @Parameter(property = "entityMetaInfoClassOutputPackage", defaultValue = "domain._share.meta") - String entityMetaInfoClassOutputPackage = "domain._share.meta"; + public String entityMetaInfoClassOutputPackage = "domain._share.meta"; /** * 关联实体加载模式 LAZY | EAGER @@ -209,14 +209,14 @@ public List getEntityClassExtraImports() { * @parameter expression="${fetchType}" */ @Parameter(property = "fetchType", defaultValue = "EAGER") - String fetchType = "EAGER"; + public String fetchType = "EAGER"; /** * 关联实体加载模式 SUBSELECT | JOIN | SELECT * * @parameter expression="${fetchMode}" */ @Parameter(property = "fetchMode", defaultValue = "SUBSELECT") - String fetchMode = "SUBSELECT"; + public String fetchMode = "SUBSELECT"; /** * 主键生成器 默认自增策略 @@ -224,28 +224,28 @@ public List getEntityClassExtraImports() { * @parameter expression="${idGenerator}" */ @Parameter(property = "idGenerator", defaultValue = "") - String idGenerator = ""; + public String idGenerator = ""; /** * 枚举类型【值】字段名称 * * @parameter expression="${enumValueField}" */ @Parameter(property = "enumValueField", defaultValue = "value") - String enumValueField = "value"; + public String enumValueField = "value"; /** * 枚举类型【名】字段名称 * * @parameter expression="${enumNameField}" */ @Parameter(property = "enumNameField", defaultValue = "name") - String enumNameField = "name"; + public String enumNameField = "name"; /** * 枚举值转换不匹配时,是否抛出异常 * * @parameter expression="${enumUnmatchedThrowException}" */ @Parameter(property = "enumUnmatchedThrowException", defaultValue = "true") - Boolean enumUnmatchedThrowException = true; + public Boolean enumUnmatchedThrowException = true; /** * 日期类型映射使用的包,java.util | java.time,默认java.util @@ -253,14 +253,14 @@ public List getEntityClassExtraImports() { * @parameter expression="${datePackage}" */ @Parameter(property = "datePackage4Java", defaultValue = "java.util") - String datePackage4Java = "java.util"; + public String datePackage4Java = "java.util"; /** * 自定义数据库字段【类型】到【代码类型】映射 * * @parameter expression="${typeRemapping}" */ @Parameter(property = "typeRemapping", defaultValue = "") - Map typeRemapping = new HashMap<>(); + public Map typeRemapping = new HashMap<>(); /** * 实体字段是否生成默认值,来源数据库默认值 @@ -268,21 +268,21 @@ public List getEntityClassExtraImports() { * @parameter expression="${generateDefault}" */ @Parameter(property = "generateDefault", defaultValue = "false") - Boolean generateDefault = false; + public Boolean generateDefault = false; /** * 实体字段注释是否包含生成数据库字段类型 * * @parameter expression="${generateDbType}" */ @Parameter(property = "generateDbType", defaultValue = "false") - Boolean generateDbType = false; + public Boolean generateDbType = false; /** * 是否生成Schema类,辅助Jpa查询 * * @parameter expression="${generateSchema}" */ @Parameter(property = "generateSchema", defaultValue = "false") - Boolean generateSchema = false; + public Boolean generateSchema = false; /** * 是否生成EntitBuilder类 @@ -290,7 +290,7 @@ public List getEntityClassExtraImports() { * @parameter expression="${generateBuild}" */ @Parameter(property = "generateBuild", defaultValue = "false") - Boolean generateBuild = false; + public Boolean generateBuild = false; /** * 聚合唯一标识类型 @@ -298,7 +298,7 @@ public List getEntityClassExtraImports() { * @parameter expression="${aggregateIdentityClass}" */ @Parameter(property = "aggregateIdentityClass", defaultValue = "Long") - String aggregateIdentityClass = "Long"; + public String aggregateIdentityClass = "Long"; /** * 聚合根注解 @@ -306,7 +306,7 @@ public List getEntityClassExtraImports() { * @parameter expression="${aggregateRootAnnotation}" */ @Parameter(property = "aggregateRootAnnotation", defaultValue = "") - String aggregateRootAnnotation = ""; + public String aggregateRootAnnotation = ""; public String getAggregateRootAnnotation() { if (StringUtils.isNotEmpty(aggregateRootAnnotation)) { @@ -324,7 +324,7 @@ public String getAggregateRootAnnotation() { * @parameter expression="${aggregateRepositoryBaseClass}" */ @Parameter(property = "aggregateRepositoryBaseClass", defaultValue = "") - String aggregateRepositoryBaseClass = ""; + public String aggregateRepositoryBaseClass = ""; public String getAggregateRepositoryBaseClass() { if (StringUtils.isBlank(aggregateRepositoryBaseClass)) { @@ -340,7 +340,7 @@ public String getAggregateRepositoryBaseClass() { * @parameter expression="${aggregateRepositoryCustomerCode}" */ @Parameter(property = "aggregateRepositoryCustomerCode", defaultValue = "") - String aggregateRepositoryCustomerCode = ""; + public String aggregateRepositoryCustomerCode = ""; public String getAggregateRepositoryCustomerCode() { if (StringUtils.isBlank(aggregateRepositoryCustomerCode)) { @@ -365,5 +365,5 @@ public String getAggregateRepositoryCustomerCode() { * @parameter expression="${ignoreAggregateRoots}" */ @Parameter(property = "ignoreAggregateRoots", defaultValue = "") - String ignoreAggregateRoots = ""; + public String ignoreAggregateRoots = ""; } diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/MysqlSchemaUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/MysqlSchemaUtils.java deleted file mode 100644 index 10929a4..0000000 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/MysqlSchemaUtils.java +++ /dev/null @@ -1,369 +0,0 @@ -package org.netcorepal.cap4j.ddd.codegen.misc; - -import org.apache.maven.plugin.Mojo; -import org.apache.maven.plugin.logging.Log; -import org.codehaus.plexus.util.StringUtils; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -/** - * @author binking338 - * @date 2022-03-06 - */ -public class MysqlSchemaUtils { - public static Mojo mojo; - - static Pattern ANNOTATION_PATTERN = Pattern.compile("@([A-Za-z]+)(\\=[^;]+)?;?"); - - private static Log getLog() { - return mojo.getLog(); - } - - - public static boolean hasColumn(String columnName, List> columns) { - return columns.stream().anyMatch(col -> col.get("COLUMN_NAME").toString().equalsIgnoreCase(columnName)); - } - - public static String getName(Map tableOrColumn) { - return tableOrColumn.containsKey("COLUMN_NAME") ? getColumnName(tableOrColumn) : getTableName(tableOrColumn); - } - - public static String getColumnName(Map column) { - return column.get("COLUMN_NAME").toString(); - } - - public static String getTableName(Map tableOrColumn) { - return tableOrColumn.get("TABLE_NAME").toString(); - } - - public static String getColumnDbType(Map column) { - return column.get("COLUMN_TYPE").toString(); - } - - public static boolean isColumnNullable(Map column) { - return "YES".equalsIgnoreCase(column.get("IS_NULLABLE").toString()); - } - - public static String getComment(Map tableOrColumn, boolean cleanAnnotations) { - String comment = ""; - if (tableOrColumn.containsKey("TABLE_COMMENT")) { - comment = tableOrColumn.get("TABLE_COMMENT") == null ? "" : tableOrColumn.get("TABLE_COMMENT").toString(); - } else if (tableOrColumn.containsKey("COLUMN_COMMENT")) { - comment = tableOrColumn.get("COLUMN_COMMENT") == null ? "" : tableOrColumn.get("COLUMN_COMMENT").toString(); - } - - if (cleanAnnotations) { - comment = ANNOTATION_PATTERN.matcher(comment).replaceAll(""); - } - return comment; - } - - public static String getComment(Map tableOrColumn) { - return getComment(tableOrColumn, true); - } - - /** - * 注解缓存,注释为Key - */ - static Map> AnnotaionsCache = new HashMap<>(); - - public static Map getAnnotations(Map tableOrColumn) { - String comment = getComment(tableOrColumn, false); - if (AnnotaionsCache.containsKey(comment)) { - return AnnotaionsCache.get(comment); - } - HashMap annotations = new HashMap<>(); - Matcher matcher = ANNOTATION_PATTERN.matcher(comment); - while (matcher.find()) { - if (matcher.groupCount() > 1 && StringUtils.isNotBlank(matcher.group(1))) { - String key = matcher.group(1); - String val = matcher.group(2); - if (StringUtils.isNotBlank(val) && val.length() > 0) { - annotations.put(key, val.substring(1)); - getLog().debug("找到注解:" + getTableName(tableOrColumn) + " @" + key + "=" + val.substring(1) + ";"); - } else { - annotations.put(key, ""); - getLog().debug("找到注解:" + getTableName(tableOrColumn) + " @" + key + ";"); - } - } - } - AnnotaionsCache.putIfAbsent(comment, annotations); - return annotations; - } - - public static boolean hasAnnotation(Map tableOrColumn, String annotation) { - return getAnnotations(tableOrColumn).containsKey(annotation); - } - - public static String getAnnotation(Map tableOrColumn, String annotation) { - return getAnnotations(tableOrColumn).getOrDefault(annotation, ""); - } - - public static String getAnyAnnotation(Map tableOrColumn, List annotations) { - for (String annotaion : - annotations) { - if (hasAnnotation(tableOrColumn, annotaion)) { - return getAnnotation(tableOrColumn, annotaion); - } - } - return ""; - } - - public static boolean hasAnyAnnotation(Map tableOrColumn, List annotations) { - for (String annotaion : - annotations) { - if (hasAnnotation(tableOrColumn, annotaion)) { - return true; - } - } - return false; - } - - public static boolean hasLazy(Map table) { - return hasAnyAnnotation(table, Arrays.asList("Lazy", "L")); - } - - public static boolean isLazy(Map table) { - return isLazy(table, false); - } - - public static boolean isLazy(Map table, boolean defaultLazy) { - String val = getAnyAnnotation(table, Arrays.asList("Lazy", "L")); - if (defaultLazy) { - return "false".equalsIgnoreCase(val) || "0".equalsIgnoreCase(val) ? false : true; - } else { - return "true".equalsIgnoreCase(val) || "1".equalsIgnoreCase(val) ? true : false; - } - } - - public static boolean countIsOne(Map table) { - String val = getAnyAnnotation(table, Arrays.asList("Count", "C")); - return "One".equalsIgnoreCase(val) || "1".equalsIgnoreCase(val) ? true : false; - } - - /** - * 是否需要忽略 - * - * @param tableOrColumn - * @return - */ - public static boolean isIgnore(Map tableOrColumn) { - return hasAnyAnnotation(tableOrColumn, Arrays.asList("Ignore", "I")); - } - - /** - * 是否聚合根 - * - * @param table - * @return - */ - public static boolean isAggregateRoot(Map table) { - return hasAnyAnnotation(table, Arrays.asList("AggregateRoot", "Root", "R")) - || !hasAnyAnnotation(table, Arrays.asList("Parent", "P")); - } - - /** - * 获取聚合关系中的父表 - * - * @param table - * @return - */ - public static String getParent(Map table) { - return getAnyAnnotation(table, Arrays.asList("Parent", "P")); - } - - /** - * 获取模块 - * - * @param table - * @return - */ - public static String getModule(Map table) { - String module = getAnyAnnotation(table, Arrays.asList("Module", "M")); - return module; - } - - /** - * 获取聚合 - * - * @param table - * @return - */ - public static String getAggregate(Map table) { - String aggregate = getAnyAnnotation(table, Arrays.asList("Aggregate", "A")); - return aggregate; - } - - /** - * 字段是否忽略插入 - * - * @param column - * @return - */ - public static boolean hasIgnoreInsert(Map column) { - return hasAnyAnnotation(column, Arrays.asList("IgnoreInsert", "II")); - } - - /** - * 字段是否忽略更新 - * - * @param column - * @return - */ - public static boolean hasIgnoreUpdate(Map column) { - return hasAnyAnnotation(column, Arrays.asList("IgnoreUpdate", "IU")); - } - - /** - * 字段是否忽略更新 - * - * @param column - * @return - */ - public static boolean hasReadOnly(Map column) { - return hasAnyAnnotation(column, Arrays.asList("ReadOnly", "RO")); - } - - /** - * 是否关系 - * - * @param columnOrTable - * @return - */ - public static boolean hasRelation(Map columnOrTable) { - return hasAnyAnnotation(columnOrTable, Arrays.asList("Relation", "Rel")); - } - - /** - * 获取关系 - * OneToManny - * ManyToOne - * ManyToMany - * - * @param columnOrTable - * @return - */ - public static String getRelation(Map columnOrTable) { - return getAnyAnnotation(columnOrTable, Arrays.asList("Relation", "Rel")); - } - - /** - * 是否引用 - * - * @param column - * @return - */ - public static boolean hasReference(Map column) { - return hasAnyAnnotation(column, Arrays.asList("Reference", "Ref")); - } - - /** - * 获取引用,会以 {table}_id 尝试推断 - * - * @param column - * @return - */ - public static String getReference(Map column) { - String ref = getAnyAnnotation(column, Arrays.asList("Reference", "Ref")); - String columnName = getColumnName(column).toLowerCase(); - if (StringUtils.isBlank(ref) && columnName.endsWith("_id")) { - ref = columnName.replaceAll("_id$", ""); - } - if (StringUtils.isBlank(ref)) { - return columnName; - } - return ref; - } - - public static boolean hasIdGenerator(Map table) { - return hasAnyAnnotation(table, Arrays.asList("IdGenerator", "IG")); - } - - public static String getIdGenerator(Map table) { - String idGenerator = getAnyAnnotation(table, Arrays.asList("IdGenerator", "IG")); - return idGenerator; - } - - /** - * 是否有类型注解 - * - * @param columnOrTable - * @return - */ - public static boolean hasType(Map columnOrTable) { - return hasAnyAnnotation(columnOrTable, Arrays.asList("Type", "T")); - } - - /** - * 获取类型注解 - * - * @param columnOrTable - * @return - */ - public static String getType(Map columnOrTable) { - return getAnyAnnotation(columnOrTable, Arrays.asList("Type", "T")); - } - - /** - * 是否枚举字段 - * - * @param columnOrTable - * @return - */ - public static boolean hasEnum(Map columnOrTable) { - return hasType(columnOrTable) && hasAnyAnnotation(columnOrTable, Arrays.asList("Enum", "E")); - } - - /** - * 获取枚举设置 - * - * @param column - * @return - * @example \@Enum=0:NONE:无|1:MALE:男|2:FEMALE:女 - */ - public static Map getEnum(Map column) { - String enumsConfig = getAnyAnnotation(column, Arrays.asList("Enum", "E")); - Map result = new HashMap<>(); - if (StringUtils.isNotBlank(enumsConfig)) { - String[] enumConfigs = enumsConfig.split("\\|"); - for (int i = 0; i < enumConfigs.length; i++) { - String enumConfig = enumConfigs[i]; - getLog().debug(enumConfig); - List pair = Arrays.stream(enumConfig.split("\\:")) - .map(c -> c.trim() - .replace("\n", "") - .replace("\r", "") - .replace("\t", "")) - .collect(Collectors.toList()); - if (pair.size() == 0) { - continue; - } else if (pair.size() == 1) { - if (pair.get(0).matches("^[-+]?[0-9]+$")) { - continue; - } else { - result.put(i, new String[]{pair.get(0), pair.get(0)}); - } - } else if (pair.size() == 2) { - if (pair.get(0).matches("^[-+]?[0-9]+$")) { - result.put(Integer.parseInt(pair.get(0)), new String[]{pair.get(1), pair.get(1)}); - } else { - result.put(i, new String[]{pair.get(0), pair.get(1)}); - } - } else { - if (pair.get(0).matches("^[-+]?[0-9]+$")) { - result.put(Integer.parseInt(pair.get(0)), new String[]{pair.get(1), pair.get(2)}); - } else { - result.put(i, new String[]{pair.get(0), pair.get(1)}); - } - } - } - } - return result; - } -} diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java new file mode 100644 index 0000000..57640da --- /dev/null +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java @@ -0,0 +1,734 @@ +package org.netcorepal.cap4j.ddd.codegen.misc; + +import org.apache.maven.plugin.logging.Log; +import org.codehaus.plexus.util.StringUtils; +import org.netcorepal.cap4j.ddd.codegen.GenEntityMojo; + +import java.sql.*; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * @author binking338 + * @date 2022-03-06 + */ +public class SqlSchemaUtils { + public static final String DB_TYPE_MYSQL = "mysql"; + public static final String DB_TYPE_POSTGRESQL = "postgresql"; + public static final String DB_TYPE_SQLSERVER = "sqlserver"; + public static final String DB_TYPE_ORACLE = "oracle"; + + public static String LEFT_QUOTES_4_ID_ALIAS = "`"; + public static String RIGHT_QUOTES_4_ID_ALIAS = "`"; + public static String LEFT_QUOTES_4_LITERAL_STRING = "'"; + public static String RIGHT_QUOTES_4_LITERAL_STRING = "'"; + public static GenEntityMojo mojo; + + static Pattern ANNOTATION_PATTERN = Pattern.compile("@([A-Za-z]+)(\\=[^;]+)?;?"); + + private static Log getLog() { + return mojo.getLog(); + } + + + /** + * 识别数据库类型 + * + * @param connectionString + * @return + */ + public static String recognizeDbType(String connectionString) { + try { + return connectionString.split(":")[1]; + } catch (Exception ex) { + getLog().error("数据库连接串异常 " + connectionString, ex); + } + return DB_TYPE_MYSQL; + } + + /** + * 处理数据库方言语法配置 + * + * @param dbType + */ + public static void processSqlDialet(String dbType) { + switch (dbType) { + default: + case DB_TYPE_MYSQL: + LEFT_QUOTES_4_ID_ALIAS = "`"; + RIGHT_QUOTES_4_ID_ALIAS = "`"; + LEFT_QUOTES_4_LITERAL_STRING = "'"; + RIGHT_QUOTES_4_LITERAL_STRING = "'"; + break; + case DB_TYPE_POSTGRESQL: + case DB_TYPE_ORACLE: + LEFT_QUOTES_4_ID_ALIAS = "\""; + RIGHT_QUOTES_4_ID_ALIAS = "\""; + LEFT_QUOTES_4_LITERAL_STRING = "'"; + RIGHT_QUOTES_4_LITERAL_STRING = "'"; + break; + case DB_TYPE_SQLSERVER: + LEFT_QUOTES_4_ID_ALIAS = "["; + RIGHT_QUOTES_4_ID_ALIAS = "]"; + LEFT_QUOTES_4_LITERAL_STRING = "'"; + RIGHT_QUOTES_4_LITERAL_STRING = "'"; + break; + } + } + + /** + * 执行SQL查询 + * + * @param sql + * @param connectionString + * @param user + * @param pwd + * @return + */ + static List> executeQuery(String sql, String connectionString, String user, String pwd) { + String URL = connectionString; + String USER = user; + String PASSWORD = pwd; + List> result = new ArrayList<>(); + try { + //1.加载驱动程序 + switch (recognizeDbType(connectionString)) { + default: + case DB_TYPE_MYSQL: + Class.forName("com.mysql.jdbc.Driver"); + break; + } + //2.获得数据库链接 + Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); + //3.通过数据库的连接操作数据库,实现增删改查(使用Statement类) + Statement st = conn.createStatement(); + ResultSet rs = st.executeQuery(sql); + //4.处理数据库的返回结果(使用ResultSet类) + while (rs.next()) { + HashMap map = new HashMap<>(); + ResultSetMetaData metaData = rs.getMetaData(); + for (int i = 1; i <= metaData.getColumnCount(); i++) { + map.put(metaData.getColumnName(i), rs.getObject(i)); + if (rs.getObject(i) != null && rs.getObject(i) instanceof byte[]) { + map.put(metaData.getColumnName(i), new String((byte[]) rs.getObject(i))); + } + } + result.add(map); + } + //关闭资源 + rs.close(); + st.close(); + conn.close(); + } catch (Throwable e) { + getLog().error(e); + } + + return result; + } + + /** + * 获取表的信息 + * + * @param connectionString + * @param user + * @param pwd + * @return + */ + public static List> resolveTables(String connectionString, String user, String pwd) { + String dbType = recognizeDbType(connectionString); + switch (dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.resolveTables(connectionString, user, pwd); + } + } + + /** + * 获取表的列信息 + * + * @param connectionString + * @param user + * @param pwd + * @return + */ + public static List> resolveColumns(String connectionString, String user, String pwd) { + String dbType = recognizeDbType(connectionString); + switch (dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.resolveColumns(connectionString, user, pwd); + } + } + + /** + * 获取列的Java映射类型 + * + * @param column + * @return + */ + public static String getColumnJavaType(Map column) { + if (SqlSchemaUtils.hasType(column)) { + String customerType = SqlSchemaUtils.getType(column); + if (SqlSchemaUtils.hasEnum(column) && mojo.EnumPackageMap.containsKey(customerType)) { + return mojo.EnumPackageMap.get(customerType) + "." + customerType; + } else { + return customerType; + } + } + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.getColumnJavaType(column); + } + } + + /** + * 获取字段的默认Java字面量 + * + * @param column + * @return + */ + public static String getColumnDefaultJavaLiteral(Map column) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.getColumnDefaultJavaLiteral(column); + } + } + + /** + * 判断字段是否为自动更新时间戳 + * + * @param column + * @return + */ + public static boolean isAutoUpdateDateColumn(Map column) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.isAutoUpdateDateColumn(column); + } + } + + /** + * 判断字段是否为自动插入时间戳 + * + * @param column + * @return + */ + public static boolean isAutoInsertDateColumn(Map column) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.isAutoInsertDateColumn(column); + } + } + + /** + * 判断字段是否在表内 + * + * @param column + * @param table + * @return + */ + public static boolean isColumnInTable(Map column, Map table) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.isColumnInTable(column, table); + } + } + + /** + * 获取列的序号 + * + * @param column + * @return + */ + public static int getOridinalPosition(Map column) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.getOridinalPosition(column); + } + } + + /** + * 判断是否包含指定字段 + * + * @param columnName + * @param columns + * @return + */ + public static boolean hasColumn(String columnName, List> columns) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.hasColumn(columnName, columns); + } + } + + /** + * 获取名称 + * + * @param tableOrColumn + * @return + */ + public static String getName(Map tableOrColumn) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.getName(tableOrColumn); + } + } + + /** + * 获取列名 + * + * @param column + * @return + */ + public static String getColumnName(Map column) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.getColumnName(column); + } + } + + /** + * 获取表名 + * + * @param tableOrColumn + * @return + */ + public static String getTableName(Map tableOrColumn) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.getTableName(tableOrColumn); + } + } + + /** + * 获取数据库类型 + * + * @param column + * @return + */ + public static String getColumnDbType(Map column) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.getColumnDbType(column); + } + } + + /** + * 获取数据库类型 + * + * @param column + * @return + */ + public static String getColumnDbDataType(Map column) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.getColumnDbDataType(column); + } + } + + /** + * 是否可空字段 + * + * @param column + * @return + */ + public static boolean isColumnNullable(Map column) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.isColumnNullable(column); + } + } + + /** + * 读取注释 + * + * @param tableOrColumn + * @param cleanAnnotations + * @return + */ + public static String getComment(Map tableOrColumn, boolean cleanAnnotations) { + switch (mojo.dbType) { + default: + case DB_TYPE_MYSQL: + return SqlSchemaUtils4Mysql.getComment(tableOrColumn, cleanAnnotations); + } + } + + /** + * 读取注释 + * + * @param tableOrColumn + * @return + */ + public static String getComment(Map tableOrColumn) { + return getComment(tableOrColumn, true); + } + + /** + * 读取注释注解 + * + * @param tableOrColumn + * @return + */ + public static Map getAnnotations(Map tableOrColumn) { + String comment = getComment(tableOrColumn, false); + if (mojo.AnnotaionsCache.containsKey(comment)) { + return mojo.AnnotaionsCache.get(comment); + } + HashMap annotations = new HashMap<>(); + Matcher matcher = ANNOTATION_PATTERN.matcher(comment); + while (matcher.find()) { + if (matcher.groupCount() > 1 && StringUtils.isNotBlank(matcher.group(1))) { + String key = matcher.group(1); + String val = matcher.group(2); + if (StringUtils.isNotBlank(val) && val.length() > 0) { + annotations.put(key, val.substring(1)); + getLog().debug("找到注解:" + getTableName(tableOrColumn) + " @" + key + "=" + val.substring(1) + ";"); + } else { + annotations.put(key, ""); + getLog().debug("找到注解:" + getTableName(tableOrColumn) + " @" + key + ";"); + } + } + } + mojo.AnnotaionsCache.putIfAbsent(comment, annotations); + return annotations; + } + + /** + * 判断是否包含指定注解 + * + * @param tableOrColumn + * @param annotation + * @return + */ + public static boolean hasAnnotation(Map tableOrColumn, String annotation) { + return getAnnotations(tableOrColumn).containsKey(annotation); + } + + /** + * 获取单个注解值 + * + * @param tableOrColumn + * @param annotation + * @return + */ + public static String getAnnotation(Map tableOrColumn, String annotation) { + return getAnnotations(tableOrColumn).getOrDefault(annotation, ""); + } + + /** + * 获取任意注解值 + * + * @param tableOrColumn + * @param annotations + * @return + */ + public static String getAnyAnnotation(Map tableOrColumn, List annotations) { + for (String annotaion : + annotations) { + if (hasAnnotation(tableOrColumn, annotaion)) { + return getAnnotation(tableOrColumn, annotaion); + } + } + return ""; + } + + /** + * 是否包含任意注解 + * + * @param tableOrColumn + * @param annotations + * @return + */ + public static boolean hasAnyAnnotation(Map tableOrColumn, List annotations) { + for (String annotaion : + annotations) { + if (hasAnnotation(tableOrColumn, annotaion)) { + return true; + } + } + return false; + } + + /** + * 是否包含懒加载注解 + * + * @param table + * @return + */ + public static boolean hasLazy(Map table) { + return hasAnyAnnotation(table, Arrays.asList("Lazy", "L")); + } + + /** + * 是否启用JPA懒加载 + * + * @param table + * @return + */ + public static boolean isLazy(Map table) { + return isLazy(table, false); + } + + /** + * 是否启用JPA懒加载 + * + * @param table + * @param defaultLazy + * @return + */ + public static boolean isLazy(Map table, boolean defaultLazy) { + String val = getAnyAnnotation(table, Arrays.asList("Lazy", "L")); + if (defaultLazy) { + return "false".equalsIgnoreCase(val) || "0".equalsIgnoreCase(val) ? false : true; + } else { + return "true".equalsIgnoreCase(val) || "1".equalsIgnoreCase(val) ? true : false; + } + } + + /** + * 是否是计数为1的实体成员 + * + * @param table + * @return + */ + public static boolean countIsOne(Map table) { + String val = getAnyAnnotation(table, Arrays.asList("Count", "C")); + return "One".equalsIgnoreCase(val) || "1".equalsIgnoreCase(val) ? true : false; + } + + /** + * 是否需要忽略 + * + * @param tableOrColumn + * @return + */ + public static boolean isIgnore(Map tableOrColumn) { + return hasAnyAnnotation(tableOrColumn, Arrays.asList("Ignore", "I")); + } + + /** + * 是否聚合根 + * + * @param table + * @return + */ + public static boolean isAggregateRoot(Map table) { + return hasAnyAnnotation(table, Arrays.asList("AggregateRoot", "Root", "R")) + || !hasAnyAnnotation(table, Arrays.asList("Parent", "P")); + } + + /** + * 获取聚合关系中的父表 + * + * @param table + * @return + */ + public static String getParent(Map table) { + return getAnyAnnotation(table, Arrays.asList("Parent", "P")); + } + + /** + * 获取模块 + * + * @param table + * @return + */ + public static String getModule(Map table) { + String module = getAnyAnnotation(table, Arrays.asList("Module", "M")); + return module; + } + + /** + * 获取聚合 + * + * @param table + * @return + */ + public static String getAggregate(Map table) { + String aggregate = getAnyAnnotation(table, Arrays.asList("Aggregate", "A")); + return aggregate; + } + + /** + * 字段是否忽略插入 + * + * @param column + * @return + */ + public static boolean hasIgnoreInsert(Map column) { + return hasAnyAnnotation(column, Arrays.asList("IgnoreInsert", "II")); + } + + /** + * 字段是否忽略更新 + * + * @param column + * @return + */ + public static boolean hasIgnoreUpdate(Map column) { + return hasAnyAnnotation(column, Arrays.asList("IgnoreUpdate", "IU")); + } + + /** + * 字段是否忽略更新 + * + * @param column + * @return + */ + public static boolean hasReadOnly(Map column) { + return hasAnyAnnotation(column, Arrays.asList("ReadOnly", "RO")); + } + + /** + * 是否关系 + * + * @param columnOrTable + * @return + */ + public static boolean hasRelation(Map columnOrTable) { + return hasAnyAnnotation(columnOrTable, Arrays.asList("Relation", "Rel")); + } + + /** + * 获取关系 + * OneToManny + * ManyToOne + * ManyToMany + * + * @param columnOrTable + * @return + */ + public static String getRelation(Map columnOrTable) { + return getAnyAnnotation(columnOrTable, Arrays.asList("Relation", "Rel")); + } + + /** + * 是否引用 + * + * @param column + * @return + */ + public static boolean hasReference(Map column) { + return hasAnyAnnotation(column, Arrays.asList("Reference", "Ref")); + } + + /** + * 获取引用,会以 {table}_id 尝试推断 + * + * @param column + * @return + */ + public static String getReference(Map column) { + String ref = getAnyAnnotation(column, Arrays.asList("Reference", "Ref")); + String columnName = getColumnName(column).toLowerCase(); + if (StringUtils.isBlank(ref) && columnName.endsWith("_id")) { + ref = columnName.replaceAll("_id$", ""); + } + if (StringUtils.isBlank(ref)) { + return columnName; + } + return ref; + } + + public static boolean hasIdGenerator(Map table) { + return hasAnyAnnotation(table, Arrays.asList("IdGenerator", "IG")); + } + + public static String getIdGenerator(Map table) { + String idGenerator = getAnyAnnotation(table, Arrays.asList("IdGenerator", "IG")); + return idGenerator; + } + + /** + * 是否有类型注解 + * + * @param columnOrTable + * @return + */ + public static boolean hasType(Map columnOrTable) { + return hasAnyAnnotation(columnOrTable, Arrays.asList("Type", "T")); + } + + /** + * 获取类型注解 + * + * @param columnOrTable + * @return + */ + public static String getType(Map columnOrTable) { + return getAnyAnnotation(columnOrTable, Arrays.asList("Type", "T")); + } + + /** + * 是否枚举字段 + * + * @param columnOrTable + * @return + */ + public static boolean hasEnum(Map columnOrTable) { + return hasType(columnOrTable) && hasAnyAnnotation(columnOrTable, Arrays.asList("Enum", "E")); + } + + /** + * 获取枚举设置 + * + * @param column + * @return + * @example \@Enum=0:NONE:无|1:MALE:男|2:FEMALE:女 + */ + public static Map getEnum(Map column) { + String enumsConfig = getAnyAnnotation(column, Arrays.asList("Enum", "E")); + Map result = new HashMap<>(); + if (StringUtils.isNotBlank(enumsConfig)) { + String[] enumConfigs = enumsConfig.split("\\|"); + for (int i = 0; i < enumConfigs.length; i++) { + String enumConfig = enumConfigs[i]; + getLog().debug(enumConfig); + List pair = Arrays.stream(enumConfig.split("\\:")) + .map(c -> c.trim() + .replace("\n", "") + .replace("\r", "") + .replace("\t", "")) + .collect(Collectors.toList()); + if (pair.size() == 0) { + continue; + } else if (pair.size() == 1) { + if (pair.get(0).matches("^[-+]?[0-9]+$")) { + continue; + } else { + result.put(i, new String[]{pair.get(0), pair.get(0)}); + } + } else if (pair.size() == 2) { + if (pair.get(0).matches("^[-+]?[0-9]+$")) { + result.put(Integer.parseInt(pair.get(0)), new String[]{pair.get(1), pair.get(1)}); + } else { + result.put(i, new String[]{pair.get(0), pair.get(1)}); + } + } else { + if (pair.get(0).matches("^[-+]?[0-9]+$")) { + result.put(Integer.parseInt(pair.get(0)), new String[]{pair.get(1), pair.get(2)}); + } else { + result.put(i, new String[]{pair.get(0), pair.get(1)}); + } + } + } + } + return result; + } +} diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Mysql.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Mysql.java new file mode 100644 index 0000000..4909ee3 --- /dev/null +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Mysql.java @@ -0,0 +1,240 @@ +package org.netcorepal.cap4j.ddd.codegen.misc; + +import org.codehaus.plexus.util.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.netcorepal.cap4j.ddd.codegen.misc.SqlSchemaUtils.*; + +/** + * mysql 数据库schema工具 + * + * @author binking338 + * @date 2024/9/5 + */ +public class SqlSchemaUtils4Mysql { + + public static List> resolveTables(String connectionString, String user, String pwd){ + String tableSql = "select * from " + LEFT_QUOTES_4_ID_ALIAS + "information_schema" + RIGHT_QUOTES_4_ID_ALIAS + "." + LEFT_QUOTES_4_ID_ALIAS + "tables" + RIGHT_QUOTES_4_ID_ALIAS + " where table_schema= " + LEFT_QUOTES_4_LITERAL_STRING + mojo.schema + RIGHT_QUOTES_4_LITERAL_STRING; + if (StringUtils.isNotBlank(mojo.table)) { + String whereClause = String.join(" or ", Arrays.stream(mojo.table.split(mojo.PATTERN_SPLITTER)).map(t -> "table_name like " + LEFT_QUOTES_4_LITERAL_STRING + t + RIGHT_QUOTES_4_LITERAL_STRING).collect(Collectors.toList())); + tableSql += " and (" + whereClause + ")"; + } + if (StringUtils.isNotBlank(mojo.ignoreTable)) { + String whereClause = String.join(" or ", Arrays.stream(mojo.ignoreTable.split(mojo.PATTERN_SPLITTER)).map(t -> "table_name like " + LEFT_QUOTES_4_LITERAL_STRING + t + RIGHT_QUOTES_4_LITERAL_STRING).collect(Collectors.toList())); + tableSql += " and not (" + whereClause + ")"; + } + return executeQuery(tableSql, connectionString, user, pwd); + } + public static List> resolveColumns(String connectionString, String user, String pwd){ + String columnSql = "select * from " + LEFT_QUOTES_4_ID_ALIAS + "information_schema" + RIGHT_QUOTES_4_ID_ALIAS + "." + LEFT_QUOTES_4_ID_ALIAS + "columns" + RIGHT_QUOTES_4_ID_ALIAS + " where table_schema= " + LEFT_QUOTES_4_LITERAL_STRING + mojo.schema + RIGHT_QUOTES_4_LITERAL_STRING; + if (StringUtils.isNotBlank(mojo.table)) { + String whereClause = String.join(" or ", Arrays.stream(mojo.table.split(mojo.PATTERN_SPLITTER)).map(t -> "table_name like " + LEFT_QUOTES_4_LITERAL_STRING + t + RIGHT_QUOTES_4_LITERAL_STRING).collect(Collectors.toList())); + columnSql += " and (" + whereClause + ")"; + } + if (StringUtils.isNotBlank(mojo.ignoreTable)) { + String whereClause = String.join(" or ", Arrays.stream(mojo.ignoreTable.split(mojo.PATTERN_SPLITTER)).map(t -> "table_name like " + LEFT_QUOTES_4_LITERAL_STRING + t + RIGHT_QUOTES_4_LITERAL_STRING).collect(Collectors.toList())); + columnSql += " and not (" + whereClause + ")"; + } + return executeQuery(columnSql, connectionString, user, pwd); + } + + /** + * 获取列的Java映射类型(Mysql) + * + * @param column + * @return + */ + public static String getColumnJavaType(Map column) { + String dataType = column.get("DATA_TYPE").toString().toLowerCase(); + String columnType = column.get("COLUMN_TYPE").toString().toLowerCase(); + String comment = SqlSchemaUtils.getComment(column); + String columnName = SqlSchemaUtils.getColumnName(column).toLowerCase(); + if (mojo.typeRemapping != null && mojo.typeRemapping.containsKey(dataType)) { + // 类型重映射 + return mojo.typeRemapping.get(dataType); + } + switch (dataType) { + case "varchar": + case "text": + case "mediumtext": + case "longtext": + case "char": + return "String"; + case "timestamp": + case "datetime": + if ("java.time".equalsIgnoreCase(mojo.datePackage4Java)) { + return "java.time.LocalDateTime"; + } else { + return "java.util.Date"; + } + case "date": + if ("java.time".equalsIgnoreCase(mojo.datePackage4Java)) { + return "java.time.LocalDate"; + } else { + return "java.util.Date"; + } + case "time": + if ("java.time".equalsIgnoreCase(mojo.datePackage4Java)) { + return "java.time.LocalTime"; + } else { + return "java.util.Date"; + } + case "int": + return "Integer"; + case "bigint": + return "Long"; + case "smallint": + return "Short"; + case "bit": + return "Boolean"; + case "tinyint": + if (".deleted.".contains("." + columnName + ".")) { + return "Boolean"; + } + if (mojo.deletedField.equalsIgnoreCase(columnName)) { + return "Boolean"; + } + if (columnType.equalsIgnoreCase("tinyint(1)")) { + return "Boolean"; + } + if (comment.contains("是否")) { + return "Boolean"; + } + return "Byte"; + case "float": + return "Float"; + case "double": + return "Double"; + case "decimal": + return "java.math.BigDecimal"; + default: + break; + } + throw new RuntimeException("包含未支持字段类型!" + dataType); + } + + public static String getColumnDefaultJavaLiteral(Map column) { + String columnDefault = column.get("COLUMN_DEFAULT") == null ? null : column.get("COLUMN_DEFAULT").toString(); + switch (SqlSchemaUtils.getColumnJavaType(column)) { + case "String": + if (StringUtils.isNotEmpty(columnDefault)) { + return "\"" + columnDefault.replace("\"", "\\\"") + "\""; + } else { + return "\"\""; + } + case "Integer": + case "Short": + case "Byte": + if (StringUtils.isNotEmpty(columnDefault)) { + return "" + columnDefault; + } else { + return "0"; + } + case "Long": + if (StringUtils.isNotEmpty(columnDefault)) { + return "" + columnDefault + "L"; + } else { + return "0L"; + } + case "Boolean": + if (StringUtils.isNotEmpty(columnDefault)) { + if (columnDefault.trim().equalsIgnoreCase("b'1'")) { + return "false"; + } else if (columnDefault.trim().equalsIgnoreCase("b'0'")) { + return "true"; + } + return "" + (columnDefault.trim().equalsIgnoreCase("0") ? "false" : "true"); + } else { + return "false"; + } + case "Float": + case "Double": + if (StringUtils.isNotEmpty(columnDefault)) { + return "" + columnDefault; + } else { + return "0"; + } + case "java.math.BigDecimal": + if (StringUtils.isNotEmpty(columnDefault)) { + return "java.math.BigDecimal.valueOf(" + columnDefault + ")"; + } else { + return "java.math.BigDecimal.ZERO"; + } + case "java.util.Date": + default: + break; + } + return ""; // = "" + } + + public static boolean isAutoUpdateDateColumn(Map column) { + String extra = column.get("EXTRA") == null ? "" : column.get("EXTRA").toString(); + if ("on update CURRENT_TIMESTAMP".equalsIgnoreCase(extra)) { + return true; + } + return false; + } + + public static boolean isAutoInsertDateColumn(Map column){ + String defaultData = column.get("COLUMN_DEFAULT") == null ? "" : column.get("COLUMN_DEFAULT").toString(); + if ("CURRENT_TIMESTAMP".equalsIgnoreCase(defaultData)) { + return true; + } + return false; + } + + public static boolean isColumnInTable(Map column, Map table){ + return column.get("TABLE_NAME").toString().equalsIgnoreCase(table.get("TABLE_NAME").toString()); + } + + public static int getOridinalPosition(Map column){ + return Integer.parseInt(column.get("ORDINAL_POSITION").toString()); + } + + + public static boolean hasColumn(String columnName, List> columns) { + return columns.stream().anyMatch(col -> col.get("COLUMN_NAME").toString().equalsIgnoreCase(columnName)); + } + + public static String getName(Map tableOrColumn) { + return tableOrColumn.containsKey("COLUMN_NAME") ? getColumnName(tableOrColumn) : getTableName(tableOrColumn); + } + + public static String getColumnName(Map column) { + return column.get("COLUMN_NAME").toString(); + } + + public static String getTableName(Map tableOrColumn) { + return tableOrColumn.get("TABLE_NAME").toString(); + } + + public static String getColumnDbType(Map column) { + return column.get("COLUMN_TYPE").toString(); + } + + public static String getColumnDbDataType(Map column) { + return column.get("DATA_TYPE").toString(); + } + + public static boolean isColumnNullable(Map column) { + return "YES".equalsIgnoreCase(column.get("IS_NULLABLE").toString()); + } + + public static String getComment(Map tableOrColumn, boolean cleanAnnotations) { + String comment = ""; + if (tableOrColumn.containsKey("TABLE_COMMENT")) { + comment = tableOrColumn.get("TABLE_COMMENT") == null ? "" : tableOrColumn.get("TABLE_COMMENT").toString(); + } else if (tableOrColumn.containsKey("COLUMN_COMMENT")) { + comment = tableOrColumn.get("COLUMN_COMMENT") == null ? "" : tableOrColumn.get("COLUMN_COMMENT").toString(); + } + + if (cleanAnnotations) { + comment = SqlSchemaUtils.ANNOTATION_PATTERN.matcher(comment).replaceAll(""); + } + return comment.trim(); + } +} From 0a41d4034e41d1d4403de4ab0da69ceb365cd722 Mon Sep 17 00:00:00 2001 From: binking338 Date: Fri, 6 Sep 2024 00:21:21 +0800 Subject: [PATCH 14/62] =?UTF-8?q?PersistListener=E5=B0=86=E7=9B=91?= =?UTF-8?q?=E5=90=AC=E6=89=80=E6=9C=89=E5=AE=9E=E4=BD=93=E7=9A=84=E6=8C=81?= =?UTF-8?q?=E4=B9=85=E5=8C=96=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repo/impl/DefaultPersistListenerManager.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java index c2646d1..75103c3 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java @@ -107,5 +107,18 @@ public void onChange(Entity entity, PersistType type) { } } } + if(!Object.class.equals(entity)){ + listeners = persistListenersMap.get(Object.class); + if (listeners != null) { + for (PersistListener listener : + listeners) { + try { + ((PersistListener)listener).onChange(entity, type); + } catch (Exception ex){ + throw ex; + } + } + } + } } } From 5599db73e222eb6a105df5fab7d5675a62c02cbe Mon Sep 17 00:00:00 2001 From: binking338 Date: Sun, 8 Sep 2024 13:18:03 +0800 Subject: [PATCH 15/62] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=85=83=E7=B4=A0=E5=9F=BA=E4=BA=8E=E6=9E=B6=E6=9E=84=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E7=9A=84=E4=BB=A3=E7=A0=81=E6=A1=86=E6=9E=B6=E7=94=9F?= =?UTF-8?q?=E6=88=90=E6=9C=BA=E5=88=B6=EF=BC=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenArchMojo.java | 649 +++++++++++++++--- .../cap4j/ddd/codegen/MyAbstractMojo.java | 12 + .../cap4j/ddd/codegen/misc/TextUtils.java | 31 + 3 files changed, 582 insertions(+), 110 deletions(-) create mode 100644 cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/TextUtils.java diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index 60820c8..7ffe309 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -2,18 +2,22 @@ import com.alibaba.fastjson.JSON; import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.FileUtils; +import org.netcorepal.cap4j.ddd.codegen.misc.NamingUtils; import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; +import org.netcorepal.cap4j.ddd.codegen.misc.TextUtils; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; /** * 生成项目目录结构 @@ -24,8 +28,10 @@ @Mojo(name = "gen-arch") public class GenArchMojo extends MyAbstractMojo { + public final String PATTERN_SPLITTER = "[\\,\\;]"; + /** - * 脚手架模板配置节点 + * 脚手架模板文件节点 */ @Data public static class PathNode { @@ -33,6 +39,10 @@ public static class PathNode { * 节点类型:root|dir|file */ String type; + /** + * 节点标签:关联模板 + */ + String tag; /** * 节点名称 */ @@ -48,28 +58,117 @@ public static class PathNode { /** * 冲突处理:skip|warn|overwrite */ - String conflict = "warn"; + String conflict = "skip"; /** * 下级节点 */ List children; + + public PathNode clone() { + PathNode pathNode = JSON.parseObject(JSON.toJSONString(this), PathNode.class); + return pathNode; + } + + public PathNode resolve(Map context) throws IOException { + if (null != this.name) { + this.name = this.name.replace("${basePackage}", "${basePackage__as_path}"); + this.name = escape(this.name, context); + } + String rawData = ""; + switch (this.format) { + case "url": + if (null != this.data) { + rawData = SourceFileUtils.loadFileContent(this.data, context.get("archTemplateEncoding")); + } + break; + case "raw": + default: + rawData = this.data; + break; + } + this.data = escape(rawData, context); + this.format = "raw"; + if (null != this.children) { + for (PathNode child : this.children) { + child.resolve(context); + } + } + return this; + } + + protected String escape(String content, Map context) { + if (null == content) { + return ""; + } + String result = content; + for (Map.Entry kv : context.entrySet()) { + result = result.replace("${" + kv.getKey() + "}", kv.getValue() == null ? "" : kv.getValue()); + } + return result; + } + } + + /** + * 脚手架模板模板节点 + */ + public static class TemplateNode extends PathNode { + + public TemplateNode clone() { + TemplateNode templateNode = JSON.parseObject(JSON.toJSONString(this), TemplateNode.class); + return templateNode; + } + + @Override + public PathNode resolve(Map context) throws IOException { + super.resolve(context); + this.tag = ""; + return this; + } + } + + /** + * 模板 + */ + @Data + @NoArgsConstructor + public static class Template extends PathNode { + + /** + * 模板节点 + */ + List templates = null; + + /** + * 获取模板 + * + * @param tag + * @return + */ + public TemplateNode select(String tag) { + if (this.templates == null) return null; + Optional node = templates.stream().filter(t -> Objects.equals(t.tag, tag)).findFirst(); + return node.orElse(null); + } } private String projectGroupId = ""; private String projectArtifactId = ""; private String projectVersion = ""; + private String projectDir = ""; + private Template template = null; + @Override public void execute() throws MojoExecutionException, MojoFailureException { - getLog().info("当前默认编码:" + Charset.defaultCharset().name()); - getLog().info("archTemplate设置编码:" + Charset.defaultCharset().name()); + getLog().info("当前系统默认编码:" + Charset.defaultCharset().name()); + getLog().info("设置模板读取编码:" + archTemplateEncoding + " (from archTemplateEncoding)"); String templateContent = ""; try { if (null == archTemplate || archTemplate.isEmpty()) { // templateContent = SourceFileUtils.loadResourceFileContent("template.json", archTemplateEncoding); - getLog().error("请设置archTemplate参数"); + getLog().error("请设置(archTemplate)参数"); return; } else { templateContent = SourceFileUtils.loadFileContent(archTemplate, archTemplateEncoding); @@ -79,7 +178,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { } getLog().debug(templateContent); if (basePackage == null || basePackage.isEmpty()) { - getLog().warn("请设置basePackage参数"); + getLog().warn("请设置(basePackage)参数"); return; } MavenProject mavenProject = ((MavenProject) getPluginContext().get("project")); @@ -88,24 +187,39 @@ public void execute() throws MojoExecutionException, MojoFailureException { projectArtifactId = mavenProject.getArtifactId(); projectVersion = mavenProject.getVersion(); } - PathNode arch = JSON.parseObject(templateContent, PathNode.class); // 项目结构解析 - String projectDir; projectDir = new File("").getAbsolutePath(); getLog().info("项目目录:" + projectDir); - render(arch, projectDir); + template = JSON.parseObject(templateContent, Template.class); + try { + template.resolve(getEscapeContext()); + } catch (IOException e) { + getLog().error("模板文件加载失败!"); + } + try { + render(template, projectDir); + } catch (IOException e) { + throw new RuntimeException(e); + } } - public void render(PathNode pathNode, String parentPath) { - String path = parentPath + File.separator + pathNode.name; + /** + * 构建路径节点 + * + * @param pathNode + * @param parentPath + * @throws IOException + */ + public String render(PathNode pathNode, String parentPath) throws IOException { + String path = parentPath; switch (pathNode.type) { case "file": - renderFile(pathNode, parentPath); + path = renderFile(pathNode, parentPath); break; case "dir": - renderDir(pathNode, parentPath); + path = renderDir(pathNode, parentPath); if (pathNode.children != null) { for (PathNode childPathNode : pathNode.children) { render(childPathNode, path); @@ -120,44 +234,382 @@ public void render(PathNode pathNode, String parentPath) { } break; } + return path; + } + + public String alias4Design(String name) { + switch (name.toLowerCase()) { + case "commands": + case "command": + case "cmd": + return "command"; + case "queries": + case "query": + case "qry": + return "query"; + case "clients": + case "client": + case "cli": + return "client"; + case "integration_events": + case "integration_event": + case "events": + case "event": + case "evt": + case "i_e": + case "ie": + return "integration_event"; + case "integration_event_handlers": + case "integration_event_handler": + case "event_handlers": + case "event_handler": + case "evt_hdl": + case "i_e_h": + case "ieh": + return "integration_event_handler"; + case "domain_events": + case "domain_event": + case "d_e": + case "de": + return "domain_event"; + case "domain_event_handlers": + case "domain_event_handler": + case "d_e_h": + case "deh": + return "domain_event_handler"; + case "domain_service": + case "service": + case "svc": + return "domain_service"; + default: + return name; + } } - public void renderDir(PathNode pathNode, String parentPath) { + /** + * 构建模型设计元素 + * + * @param templateNode + * @param parentPath + * @throws IOException + */ + public void renderDesign(TemplateNode templateNode, String parentPath) throws IOException { + Map> designMap = Arrays.stream(this.design.split(PATTERN_SPLITTER)) + .map(item -> TextUtils.splitWithTrim(item, ":", 2)) + .filter(item -> item.length == 2) + .collect(Collectors.groupingBy(g -> alias4Design(g[0]), Collectors.mapping(g -> g[1].trim(), Collectors.toSet()))); + switch (alias4Design(templateNode.tag)) { + case "command": + if (designMap.containsKey("command")) { + for (String literalCommand : + designMap.get("command")) { + renderAppLayerCommand(literalCommand, parentPath, templateNode); + } + } + break; + case "query": + case "query_handler": + if (designMap.containsKey("query")) { + for (String literalCommand : + designMap.get("query")) { + renderAppLayerQuery(literalCommand, parentPath, templateNode); + } + } + break; + case "client": + case "client_handler": + if (designMap.containsKey("client")) { + for (String literalCommand : + designMap.get("client")) { + renderAppLayerClient(literalCommand, parentPath, templateNode); + } + } + break; + case "integration_event": + if (designMap.containsKey("integration_event")) { + for (String literalCommand : + designMap.get("integration_event")) { + renderAppLayerIntegrationEvent(literalCommand, parentPath, templateNode); + } + } + case "integration_event_handler": + if (designMap.containsKey("integration_event_handler")) { + for (String literalCommand : + designMap.get("integration_event_handler")) { + renderAppLayerIntegrationEvent(literalCommand, parentPath, templateNode); + } + } + break; + case "domain_event": + case "domain_event_handler": + if (designMap.containsKey("domain_event")) { + for (String literalCommand : + designMap.get("domain_event")) { + renderDomainLayerDomainEvent(literalCommand, parentPath, templateNode); + } + } + break; + case "domain_service": + if (designMap.containsKey("domain_service")) { + for (String literalCommand : + designMap.get("domain_service")) { + renderDomainLayerDomainService(literalCommand, parentPath, templateNode); + } + } + break; + default: + if (designMap.containsKey(templateNode.tag)) { + for (String literalCommand : + designMap.get(templateNode.tag)) { + renderGenericDesign(literalCommand, parentPath, templateNode); + } + } + break; + } + } + + /** + * @param literalCommandDeclaration 文本化命令声明 CommandName[:ResponseType_default_is_Boolean] + * @param templateNode 模板配置 + */ + public void renderAppLayerCommand(String literalCommandDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析命令设计:" + literalCommandDeclaration); + String[] segments = TextUtils.splitWithTrim(literalCommandDeclaration, ":"); + Map context = new HashMap<>(getEscapeContext()); + for (int i = 0; i < segments.length; i++) { + context.put("Val" + i, segments[i]); + context.put("val" + i, segments[i].toLowerCase()); + } + String name = NamingUtils.toUpperCamelCase(segments[0]); + if (!name.endsWith("Cmd") && !name.endsWith("Command")) { + name += "Cmd"; + } + context.put("Name", name); + context.put("name", segments[0].toLowerCase()); + context.put("ReturnType", segments.length > 1 ? segments[1] : "Boolean"); + context.put("Command", context.get("Name")); + context.put("Request", context.get("Name")); + context.put("command", context.get("name")); + context.put("request", context.get("name")); + context.put("Response", context.get("ReturnType")); + PathNode pathNode = templateNode.clone().resolve(context); + String path = render(pathNode, parentPath); + getLog().info("生成命令代码:" + path); + } + + /** + * @param literalQueryDeclaration 文本化查询声明 QueryName[:ResponseType_default_is_QueryNameResponse] + * @param templateNode 模板配置 + */ + public void renderAppLayerQuery(String literalQueryDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析查询设计:" + literalQueryDeclaration); + String[] segments = TextUtils.splitWithTrim(literalQueryDeclaration, ":"); + Map context = new HashMap<>(getEscapeContext()); + for (int i = 0; i < segments.length; i++) { + context.put("Val" + i, segments[i]); + context.put("val" + i, segments[i].toLowerCase()); + } + String name = NamingUtils.toUpperCamelCase(segments[0]); + if (!name.endsWith("Qry") && !name.endsWith("Query")) { + name += "Qry"; + } + context.put("Name", name); + context.put("name", segments[0].toLowerCase()); + context.put("ReturnType", segments.length > 1 ? segments[1] : (segments[0] + "Response")); + context.put("Query", context.get("Name")); + context.put("Request", context.get("Name")); + context.put("query", context.get("name")); + context.put("request", context.get("name")); + context.put("Response", context.get("ReturnType")); + PathNode pathNode = templateNode.clone().resolve(context); + String path = render(pathNode, parentPath); + getLog().info("生成查询代码:" + path); + } + + /** + * @param literalClientDeclaration 文本化防腐端声明 ClientName[:ResponseType_default_is_ClientNameResponse] + * @param templateNode 模板配置 + */ + public void renderAppLayerClient(String literalClientDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析防腐端设计:" + literalClientDeclaration); + String[] segments = TextUtils.splitWithTrim(literalClientDeclaration, ":"); + Map context = new HashMap<>(getEscapeContext()); + for (int i = 0; i < segments.length; i++) { + context.put("Val" + i, segments[i]); + context.put("val" + i, segments[i].toLowerCase()); + } + String name = NamingUtils.toUpperCamelCase(segments[0]); + if (!name.endsWith("Cli") && !name.endsWith("Client")) { + name += "Client"; + } + context.put("Name", name); + context.put("name", segments[0].toLowerCase()); + context.put("ReturnType", NamingUtils.toUpperCamelCase(segments.length > 1 ? segments[1] : (segments[0] + "Response"))); + context.put("Client", context.get("Name")); + context.put("Request", context.get("Name")); + context.put("client", context.get("name")); + context.put("request", context.get("name")); + context.put("Response", context.get("ReturnType")); + PathNode pathNode = templateNode.clone().resolve(context); + String path = render(pathNode, parentPath); + getLog().info("生成防腐端代码:" + path); + } + + /** + * @param literalIntegrationEventDeclaration 文本化集成事件声明 IntegrationEventName[:mq-topic[:mq-consumer]] + * @param templateNode 模板配置 + */ + public void renderAppLayerIntegrationEvent(String literalIntegrationEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析集成事件设计:" + literalIntegrationEventDeclaration); + String[] segments = TextUtils.splitWithTrim(literalIntegrationEventDeclaration, ":"); + Map context = new HashMap<>(getEscapeContext()); + for (int i = 0; i < segments.length; i++) { + context.put("Val" + i, segments[i]); + context.put("val" + i, segments[i].toLowerCase()); + } + String name = NamingUtils.toUpperCamelCase(segments[0]); + if (!name.endsWith("Evt") && !name.endsWith("Event")) { + name += "IntegrationEvent"; + } + context.put("Name", name); + context.put("name", segments[0].toLowerCase()); + context.put("MQ_TOPIC", segments.length > 1 ? segments[1] : segments[0]); + if (Objects.equals("event_handler", templateNode.tag)) { + context.put("MQ_CONSUMER", segments.length > 2 ? segments[2] : "${spring.application.name}"); + } else { + context.put("MQ_CONSUMER", "[none]"); + } + context.put("IntegrationEvent", context.get("Name")); + context.put("Event", context.get("Name")); + context.put("integration_event", context.get("name")); + context.put("event", context.get("name")); + PathNode pathNode = templateNode.clone().resolve(context); + String path = render(pathNode, parentPath); + getLog().info("生成集成事件代码:" + path); + } + + /** + * @param literalDomainEventDeclaration 文本化领域事件声明 Val1[:Val2[:...]] + * @param templateNode 模板配置 + */ + public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析领域事件设计:" + literalDomainEventDeclaration); + String[] segments = TextUtils.splitWithTrim(literalDomainEventDeclaration, ":"); + Map context = new HashMap<>(getEscapeContext()); + for (int i = 0; i < segments.length; i++) { + context.put("Val" + i, segments[i]); + context.put("val" + i, segments[i].toLowerCase()); + } + String name = NamingUtils.toUpperCamelCase(segments[0]); + if (!name.endsWith("Evt") && !name.endsWith("Event")) { + name += "DomainEvent"; + } + context.put("Name", name); + context.put("name", segments[0].toLowerCase()); + context.put("DomainEvent", context.get("Name")); + context.put("Event", context.get("Name")); + context.put("domain_event", context.get("name")); + context.put("event", context.get("name")); + PathNode pathNode = templateNode.clone().resolve(context); + String path = render(pathNode, parentPath); + getLog().info("生成领域事件代码:" + path); + } + + + /** + * @param literalDomainServiceDeclaration 文本化领域服务声明 DomainServiceName + * @param templateNode 模板配置 + */ + public void renderDomainLayerDomainService(String literalDomainServiceDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析领域服务设计:" + literalDomainServiceDeclaration); + String[] segments = TextUtils.splitWithTrim(literalDomainServiceDeclaration, ":"); + Map context = new HashMap<>(getEscapeContext()); + for (int i = 0; i < segments.length; i++) { + context.put("Val" + i, segments[i]); + context.put("val" + i, segments[i].toLowerCase()); + } + String name = NamingUtils.toUpperCamelCase(segments[0]); + if (!name.endsWith("Svc") && !name.endsWith("Service")) { + name += "DomainService"; + } + context.put("Name", name); + context.put("name", segments[0].toLowerCase()); + context.put("DomainService", context.get("Name")); + context.put("domain_service", context.get("name")); + PathNode pathNode = templateNode.clone().resolve(context); + String path = render(pathNode, parentPath); + getLog().info("生成领域服务代码:" + path); + } + + /** + * @param literalGenericDeclaration 文本化自定义元素声明 Val1[:Val2[:...]] + * @param templateNode 模板配置 + */ + public void renderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析自定义元素设计:" + literalGenericDeclaration); + String[] segments = TextUtils.splitWithTrim(literalGenericDeclaration, ":"); + Map context = new HashMap<>(getEscapeContext()); + for (int i = 0; i < segments.length; i++) { + context.put("Val" + i, segments[i]); + context.put("val" + i, segments[i].toLowerCase()); + } + String name = NamingUtils.toUpperCamelCase(segments[0]); + context.put("Name", name); + context.put("name", segments[0].toLowerCase()); + PathNode pathNode = templateNode.clone().resolve(context); + String path = render(pathNode, parentPath); + getLog().info("生成自定义元素代码:" + path); + } + + /** + * @param pathNode + * @param parentPath + * @return + * @throws IOException + */ + public String renderDir(PathNode pathNode, String parentPath) throws IOException { if (!"dir".equalsIgnoreCase(pathNode.type)) { throw new RuntimeException("节点类型必须是目录"); } if (pathNode.name == null || pathNode.name.isEmpty()) { - throw new RuntimeException("模板节点配置 name 不得为空 parentPath = " + parentPath); + return parentPath; } String path = parentPath + File.separator + pathNode.name; - path = escapePath(path); - if (FileUtils.fileExists(path)) { - switch (pathNode.conflict) { case "warn": getLog().warn("目录存在:" + path); - return; + break; case "overwrite": getLog().info("目录覆盖:" + path); + FileUtils.deleteDirectory(path); + FileUtils.mkdir(path); break; case "skip": getLog().info("目录存在:" + path); - return; - default: - getLog().info("目录创建:" + path); break; } + } else { + getLog().warn("目录创建:" + path); + FileUtils.mkdir(path); } - try { - FileUtils.deleteDirectory(path); - } catch (IOException ex) { - getLog().error("获取模板源文件异常", ex); + + if (StringUtils.isNotBlank(pathNode.tag)) { + TemplateNode templateNode = template.select(pathNode.tag); + if (null != templateNode) { + renderDesign(templateNode, path); + } } - FileUtils.mkdir(path); + + return path; } - public void renderFile(PathNode pathNode, String parentPath) { + /** + * @param pathNode + * @param parentPath + * @return + */ + public String renderFile(PathNode pathNode, String parentPath) throws IOException { if (!"file".equalsIgnoreCase(pathNode.type)) { throw new RuntimeException("节点类型必须是文件"); } @@ -165,102 +617,79 @@ public void renderFile(PathNode pathNode, String parentPath) { throw new RuntimeException("模板节点配置 name 不得为空 parentPath = " + parentPath); } String path = parentPath + File.separator + pathNode.name; - path = escapePath(path); - String content = ""; - switch (pathNode.format) { - case "raw": - content = pathNode.data; - break; - case "url": - try { - content = SourceFileUtils.loadFileContent(pathNode.data, archTemplateEncoding); - } catch (IOException ex) { - getLog().error("获取模板源文件异常", ex); - } - break; - } - content = escapeContent(content); + String content = pathNode.data; if (FileUtils.fileExists(path)) { switch (pathNode.conflict) { case "warn": getLog().warn("文件存在:" + path); - return; + break; case "overwrite": getLog().info("文件覆盖:" + path); + FileUtils.fileDelete(path); + FileUtils.fileWrite(path, content); break; case "skip": - getLog().info("文件存在:" + path); - return; default: - getLog().info("文件创建:" + path); + getLog().info("文件存在:" + path); break; } - } - - try { - FileUtils.fileDelete(path); + } else { + getLog().warn("文件创建:" + path); FileUtils.fileWrite(path, content); - } catch (IOException e) { - getLog().error("写入模板文件异常", e); - } - } - - public String escapePath(String path) { - path = path.replace("${basePackage}", basePackage.replace(".", File.separator)); - if (projectArtifactId != null && !projectArtifactId.isEmpty()) { - path = path.replace("${artifactId}", projectArtifactId); } return path; } - public String escapeContent(String content) { - content = content.replace("${groupId}", projectGroupId); - content = content.replace("${artifactId}", projectArtifactId); - content = content.replace("${version}", projectVersion); - content = content.replace("${archTemplate}", archTemplate); - content = content.replace("${archTemplateEncoding}", archTemplateEncoding); - content = content.replace("${basePackage}", basePackage); - content = content.replace("${multiModule}", multiModule ? "true" : "false"); - content = content.replace("${moduleNameSuffix4Adapter}", moduleNameSuffix4Adapter); - content = content.replace("${moduleNameSuffix4Domain}", moduleNameSuffix4Domain); - content = content.replace("${moduleNameSuffix4Application}", moduleNameSuffix4Application); - content = content.replace("${connectionString}", connectionString); - content = content.replace("${user}", user); - content = content.replace("${pwd}", pwd); - content = content.replace("${schema}", schema); - content = content.replace("${table}", table); - content = content.replace("${ignoreTable}", ignoreTable); - content = content.replace("${idField}", idField); - content = content.replace("${versionField}", versionField); - content = content.replace("${deletedField}", deletedField); - content = content.replace("${readonlyFields}", readonlyFields); - content = content.replace("${ignoreFields}", ignoreFields); - content = content.replace("${entityBaseClass}", entityBaseClass); - content = content.replace("${entityClassExtraImports}", stringfyEntityClassImportPackages()); - content = content.replace("${entityMetaInfoClassOutputPackage}", entityMetaInfoClassOutputPackage); - content = content.replace("${entityMetaInfoClassOutputMode}", entityMetaInfoClassOutputMode); - content = content.replace("${idGenerator}", idGenerator); - content = content.replace("${fetchType}", fetchType); - content = content.replace("${fetchMode}", fetchMode); - content = content.replace("${enumValueField}", enumValueField); - content = content.replace("${enumNameField}", enumNameField); - content = content.replace("${enumUnmatchedThrowException}", enumUnmatchedThrowException ? "true" : "false"); - content = content.replace("${datePackage4Java}", datePackage4Java); - content = content.replace("${typeRemapping}", stringfyTypeRemapping()); - content = content.replace("${generateDefault}", generateDefault ? "true" : "false"); - content = content.replace("${generateDbType}", generateDbType ? "true" : "false"); - content = content.replace("${generateSchema}", generateSchema ? "true" : "false"); - content = content.replace("${generateBuild}", generateBuild ? "true" : "false"); - content = content.replace("${aggregateRootAnnotation}", aggregateRootAnnotation); - content = content.replace("${aggregateRepositoryBaseClass}", aggregateRepositoryBaseClass); - content = content.replace("${aggregateIdentityClass}", aggregateIdentityClass); - content = content.replace("${aggregateRepositoryCustomerCode}", aggregateRepositoryCustomerCode); - content = content.replace("${ignoreAggregateRoots}", ignoreAggregateRoots); - content = content.replace("${symbol_pound}", "#"); - content = content.replace("${symbol_escape}", "\\"); - content = content.replace("${symbol_dollar}", "$"); - return content; + public Map getEscapeContext() { + Map context = new HashMap<>(); + context.put("groupId", projectGroupId); + context.put("artifactId", projectArtifactId); + context.put("version", projectVersion); + context.put("archTemplate", archTemplate); + context.put("archTemplateEncoding", archTemplateEncoding); + context.put("basePackage", basePackage); + context.put("basePackage__as_path", basePackage.replace(".", File.separator)); + context.put("multiModule", multiModule ? "true" : "false"); + context.put("moduleNameSuffix4Adapter", moduleNameSuffix4Adapter); + context.put("moduleNameSuffix4Domain", moduleNameSuffix4Domain); + context.put("moduleNameSuffix4Application", moduleNameSuffix4Application); + context.put("connectionString", connectionString); + context.put("user", user); + context.put("pwd", pwd); + context.put("schema", schema); + context.put("table", table); + context.put("ignoreTable", ignoreTable); + context.put("idField", idField); + context.put("versionField", versionField); + context.put("deletedField", deletedField); + context.put("readonlyFields", readonlyFields); + context.put("ignoreFields", ignoreFields); + context.put("entityBaseClass", entityBaseClass); + context.put("entityClassExtraImports", stringfyEntityClassImportPackages()); + context.put("entityMetaInfoClassOutputPackage", entityMetaInfoClassOutputPackage); + context.put("entityMetaInfoClassOutputMode", entityMetaInfoClassOutputMode); + context.put("idGenerator", idGenerator); + context.put("fetchType", fetchType); + context.put("fetchMode", fetchMode); + context.put("enumValueField", enumValueField); + context.put("enumNameField", enumNameField); + context.put("enumUnmatchedThrowException", enumUnmatchedThrowException ? "true" : "false"); + context.put("datePackage4Java", datePackage4Java); + context.put("typeRemapping", stringfyTypeRemapping()); + context.put("generateDefault", generateDefault ? "true" : "false"); + context.put("generateDbType", generateDbType ? "true" : "false"); + context.put("generateSchema", generateSchema ? "true" : "false"); + context.put("generateBuild", generateBuild ? "true" : "false"); + context.put("aggregateRootAnnotation", aggregateRootAnnotation); + context.put("aggregateRepositoryBaseClass", aggregateRepositoryBaseClass); + context.put("aggregateIdentityClass", aggregateIdentityClass); + context.put("aggregateRepositoryCustomerCode", aggregateRepositoryCustomerCode); + context.put("ignoreAggregateRoots", ignoreAggregateRoots); + context.put("symbol_pound", "#"); + context.put("symbol_escape", "\\"); + context.put("symbol_dollar", "$"); + return context; } private String stringfyTypeRemapping() { @@ -270,7 +699,7 @@ private String stringfyTypeRemapping() { String result = ""; for (Map.Entry kv : typeRemapping.entrySet()) { - result += "<" + kv.getKey() + ">" + kv.getValue() + ""; + result += "<" + kv.getKey() + ">" + kv.getValue() + ""; } return result; } diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java index 4a67d63..3d4e041 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java @@ -68,6 +68,18 @@ public abstract class MyAbstractMojo extends AbstractMojo { @Parameter(property = "moduleNameSuffix4Application", defaultValue = "-application") public String moduleNameSuffix4Application = "-application"; + /** + * 添加应用层设计元素(命令cmd、查询qry、集成事件event、防腐客户端cli...) + * + * cmd:PlaceOrderCommand:Long, qry:GetOrderQuery, event:OrderPlacedIntegrationEvent, cli:GenerateBillClient + * + * @parameter expression="${design}" + */ + @Parameter(property = "design", defaultValue = "") + public String design = ""; + + + /** * 数据库连接地址 * diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/TextUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/TextUtils.java new file mode 100644 index 0000000..3550384 --- /dev/null +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/TextUtils.java @@ -0,0 +1,31 @@ +package org.netcorepal.cap4j.ddd.codegen.misc; + +/** + * 文本工具 + * + * @author binking338 + * @date 2024/9/8 + */ +public class TextUtils { + public static String[] splitWithTrim(String val, String regexSplitter){ + return splitWithTrim(val, regexSplitter, 0); + } + + /** + * 字符串分割 + * @param val + * @param regexSplitter + * @param limit + * @return + */ + public static String[] splitWithTrim(String val, String regexSplitter, int limit){ + if(null == val || val.isEmpty()){ + return new String[0]; + } + String[] segments = val.split(regexSplitter, limit); + for (int i = 0; i < segments.length; i++) { + segments[i] = segments[i].trim(); + } + return segments; + } +} From b5aadcb8fdb37a65cca90f57fb5a06499ec2ca7f Mon Sep 17 00:00:00 2001 From: binking338 Date: Sun, 8 Sep 2024 17:48:50 +0800 Subject: [PATCH 16/62] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=85=83=E7=B4=A0=E5=9F=BA=E4=BA=8E=E6=9E=B6=E6=9E=84=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E7=9A=84=E4=BB=A3=E7=A0=81=E6=A1=86=E6=9E=B6=E7=94=9F?= =?UTF-8?q?=E6=88=90=E6=9C=BA=E5=88=B6=EF=BC=8C=E8=BF=BD=E5=8A=A0=E5=B7=A5?= =?UTF-8?q?=E5=8E=82=E5=92=8C=E8=A7=84=E7=BA=A6=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenArchMojo.java | 275 ++++++++++-------- .../cap4j/ddd/codegen/misc/NamingUtils.java | 30 ++ 2 files changed, 185 insertions(+), 120 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index 7ffe309..acfa157 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -267,6 +268,20 @@ public String alias4Design(String name) { case "i_e_h": case "ieh": return "integration_event_handler"; + case "repositories": + case "repository": + case "repos": + case "repo": + return "repository"; + case "factories": + case "factory": + case "fac": + return "factory"; + case "specifications": + case "specification": + case "specs": + case "spec": + return "specification"; case "domain_events": case "domain_event": case "d_e": @@ -374,26 +389,20 @@ public void renderDesign(TemplateNode templateNode, String parentPath) throws IO */ public void renderAppLayerCommand(String literalCommandDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析命令设计:" + literalCommandDeclaration); - String[] segments = TextUtils.splitWithTrim(literalCommandDeclaration, ":"); - Map context = new HashMap<>(getEscapeContext()); - for (int i = 0; i < segments.length; i++) { - context.put("Val" + i, segments[i]); - context.put("val" + i, segments[i].toLowerCase()); - } - String name = NamingUtils.toUpperCamelCase(segments[0]); - if (!name.endsWith("Cmd") && !name.endsWith("Command")) { - name += "Cmd"; - } - context.put("Name", name); - context.put("name", segments[0].toLowerCase()); - context.put("ReturnType", segments.length > 1 ? segments[1] : "Boolean"); - context.put("Command", context.get("Name")); - context.put("Request", context.get("Name")); - context.put("command", context.get("name")); - context.put("request", context.get("name")); - context.put("Response", context.get("ReturnType")); - PathNode pathNode = templateNode.clone().resolve(context); - String path = render(pathNode, parentPath); + String path = internalRenderGenericDesign(literalCommandDeclaration, parentPath, templateNode, context -> { + String name = context.get("Name"); + if (!name.endsWith("Cmd") && !name.endsWith("Command")) { + name += "Cmd"; + } + context.put("Name", name); + context.put("ReturnType", NamingUtils.toUpperCamelCase(context.containsKey("Val1") ? context.get("Val1") : "Boolean")); + context.put("Command", context.get("Name")); + context.put("Request", context.get("Name")); + context.put("command", context.get("name")); + context.put("request", context.get("name")); + context.put("Response", context.get("ReturnType")); + return context; + }); getLog().info("生成命令代码:" + path); } @@ -403,26 +412,20 @@ public void renderAppLayerCommand(String literalCommandDeclaration, String paren */ public void renderAppLayerQuery(String literalQueryDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析查询设计:" + literalQueryDeclaration); - String[] segments = TextUtils.splitWithTrim(literalQueryDeclaration, ":"); - Map context = new HashMap<>(getEscapeContext()); - for (int i = 0; i < segments.length; i++) { - context.put("Val" + i, segments[i]); - context.put("val" + i, segments[i].toLowerCase()); - } - String name = NamingUtils.toUpperCamelCase(segments[0]); - if (!name.endsWith("Qry") && !name.endsWith("Query")) { - name += "Qry"; - } - context.put("Name", name); - context.put("name", segments[0].toLowerCase()); - context.put("ReturnType", segments.length > 1 ? segments[1] : (segments[0] + "Response")); - context.put("Query", context.get("Name")); - context.put("Request", context.get("Name")); - context.put("query", context.get("name")); - context.put("request", context.get("name")); - context.put("Response", context.get("ReturnType")); - PathNode pathNode = templateNode.clone().resolve(context); - String path = render(pathNode, parentPath); + String path = internalRenderGenericDesign(literalQueryDeclaration, parentPath, templateNode, context -> { + String name = context.get("Name"); + if (!name.endsWith("Qry") && !name.endsWith("Query")) { + name += "Qry"; + } + context.put("Name", name); + context.put("ReturnType", NamingUtils.toUpperCamelCase(context.containsKey("Val1") ? context.get("Val1") : (context.get("Val0") + "Response"))); + context.put("Query", context.get("Name")); + context.put("Request", context.get("Name")); + context.put("query", context.get("name")); + context.put("request", context.get("name")); + context.put("Response", context.get("ReturnType")); + return context; + }); getLog().info("生成查询代码:" + path); } @@ -432,26 +435,20 @@ public void renderAppLayerQuery(String literalQueryDeclaration, String parentPat */ public void renderAppLayerClient(String literalClientDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析防腐端设计:" + literalClientDeclaration); - String[] segments = TextUtils.splitWithTrim(literalClientDeclaration, ":"); - Map context = new HashMap<>(getEscapeContext()); - for (int i = 0; i < segments.length; i++) { - context.put("Val" + i, segments[i]); - context.put("val" + i, segments[i].toLowerCase()); - } - String name = NamingUtils.toUpperCamelCase(segments[0]); - if (!name.endsWith("Cli") && !name.endsWith("Client")) { - name += "Client"; - } - context.put("Name", name); - context.put("name", segments[0].toLowerCase()); - context.put("ReturnType", NamingUtils.toUpperCamelCase(segments.length > 1 ? segments[1] : (segments[0] + "Response"))); - context.put("Client", context.get("Name")); - context.put("Request", context.get("Name")); - context.put("client", context.get("name")); - context.put("request", context.get("name")); - context.put("Response", context.get("ReturnType")); - PathNode pathNode = templateNode.clone().resolve(context); - String path = render(pathNode, parentPath); + String path = internalRenderGenericDesign(literalClientDeclaration, parentPath, templateNode, context -> { + String name = context.get("Name"); + if (!name.endsWith("Cli") && !name.endsWith("Client")) { + name += "Client"; + } + context.put("Name", name); + context.put("ReturnType", NamingUtils.toUpperCamelCase(context.containsKey("Val1") ? context.get("Val1") : (context.get("Val0") + "Response"))); + context.put("Client", context.get("Name")); + context.put("Request", context.get("Name")); + context.put("client", context.get("name")); + context.put("request", context.get("name")); + context.put("Response", context.get("ReturnType")); + return context; + }); getLog().info("生成防腐端代码:" + path); } @@ -461,30 +458,24 @@ public void renderAppLayerClient(String literalClientDeclaration, String parentP */ public void renderAppLayerIntegrationEvent(String literalIntegrationEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析集成事件设计:" + literalIntegrationEventDeclaration); - String[] segments = TextUtils.splitWithTrim(literalIntegrationEventDeclaration, ":"); - Map context = new HashMap<>(getEscapeContext()); - for (int i = 0; i < segments.length; i++) { - context.put("Val" + i, segments[i]); - context.put("val" + i, segments[i].toLowerCase()); - } - String name = NamingUtils.toUpperCamelCase(segments[0]); - if (!name.endsWith("Evt") && !name.endsWith("Event")) { - name += "IntegrationEvent"; - } - context.put("Name", name); - context.put("name", segments[0].toLowerCase()); - context.put("MQ_TOPIC", segments.length > 1 ? segments[1] : segments[0]); - if (Objects.equals("event_handler", templateNode.tag)) { - context.put("MQ_CONSUMER", segments.length > 2 ? segments[2] : "${spring.application.name}"); - } else { - context.put("MQ_CONSUMER", "[none]"); - } - context.put("IntegrationEvent", context.get("Name")); - context.put("Event", context.get("Name")); - context.put("integration_event", context.get("name")); - context.put("event", context.get("name")); - PathNode pathNode = templateNode.clone().resolve(context); - String path = render(pathNode, parentPath); + String path = internalRenderGenericDesign(literalIntegrationEventDeclaration, parentPath, templateNode, context -> { + String name = context.get("Name"); + if (!name.endsWith("Evt") && !name.endsWith("Event")) { + name += "IntegrationEvent"; + } + context.put("Name", name); + context.put("MQ_TOPIC", context.containsKey("Val1") ? context.get("Val1") : context.get("Val0")); + if (Objects.equals("event_handler", templateNode.tag)) { + context.put("MQ_CONSUMER", context.containsKey("Val2") ? context.get("Val2") : "${spring.application.name}"); + } else { + context.put("MQ_CONSUMER", "[none]"); + } + context.put("IntegrationEvent", context.get("Name")); + context.put("Event", context.get("Name")); + context.put("integration_event", context.get("name")); + context.put("event", context.get("name")); + return context; + }); getLog().info("生成集成事件代码:" + path); } @@ -494,27 +485,62 @@ public void renderAppLayerIntegrationEvent(String literalIntegrationEventDeclara */ public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析领域事件设计:" + literalDomainEventDeclaration); - String[] segments = TextUtils.splitWithTrim(literalDomainEventDeclaration, ":"); - Map context = new HashMap<>(getEscapeContext()); - for (int i = 0; i < segments.length; i++) { - context.put("Val" + i, segments[i]); - context.put("val" + i, segments[i].toLowerCase()); - } - String name = NamingUtils.toUpperCamelCase(segments[0]); - if (!name.endsWith("Evt") && !name.endsWith("Event")) { - name += "DomainEvent"; - } - context.put("Name", name); - context.put("name", segments[0].toLowerCase()); - context.put("DomainEvent", context.get("Name")); - context.put("Event", context.get("Name")); - context.put("domain_event", context.get("name")); - context.put("event", context.get("name")); - PathNode pathNode = templateNode.clone().resolve(context); - String path = render(pathNode, parentPath); + String path = internalRenderGenericDesign(literalDomainEventDeclaration, parentPath, templateNode, context -> { + String name = context.get("Name"); + if (!name.endsWith("Evt") && !name.endsWith("Event")) { + name += "DomainEvent"; + } + context.put("Name", name); + context.put("DomainEvent", context.get("Name")); + context.put("Event", context.get("Name")); + context.put("domain_event", context.get("name")); + context.put("event", context.get("name")); + return context; + }); getLog().info("生成领域事件代码:" + path); } + /** + * @param literalAggregateFactoryDeclaration 文本化聚合工厂声明 AggregateFactoryName + * @param templateNode 模板配置 + */ + public void renderDomainLayerAggregateFactory(String literalAggregateFactoryDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析聚合工厂设计:" + literalAggregateFactoryDeclaration); + String path = internalRenderGenericDesign(literalAggregateFactoryDeclaration, parentPath, templateNode, context -> { + String name = context.get("Name"); + if (!name.endsWith("Fac") && !name.endsWith("Factory")) { + name += "Factory"; + } + context.put("Name", name); + context.put("Factory", context.get("Name")); + context.put("Fac", context.get("Name")); + context.put("factory", context.get("name")); + context.put("fac", context.get("name")); + return context; + }); + getLog().info("生成聚合工厂代码:" + path); + } + + /** + * @param literalSpecificationDeclaration 文本化聚合工厂声明 SpecificationName + * @param templateNode 模板配置 + */ + public void renderDomainLayerSpecificaton(String literalSpecificationDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析实体规约设计:" + literalSpecificationDeclaration); + String path = internalRenderGenericDesign(literalSpecificationDeclaration, parentPath, templateNode, context -> { + String name = context.get("Name"); + if (!name.endsWith("Spec") && !name.endsWith("Specification")) { + name += "Specification"; + } + context.put("Name", name); + context.put("Spec", context.get("Name")); + context.put("Specification", context.get("Name")); + context.put("spec", context.get("name")); + context.put("specification", context.get("name")); + return context; + }); + getLog().info("生成实体规约代码:" + path); + } /** * @param literalDomainServiceDeclaration 文本化领域服务声明 DomainServiceName @@ -522,22 +548,17 @@ public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, S */ public void renderDomainLayerDomainService(String literalDomainServiceDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析领域服务设计:" + literalDomainServiceDeclaration); - String[] segments = TextUtils.splitWithTrim(literalDomainServiceDeclaration, ":"); - Map context = new HashMap<>(getEscapeContext()); - for (int i = 0; i < segments.length; i++) { - context.put("Val" + i, segments[i]); - context.put("val" + i, segments[i].toLowerCase()); - } - String name = NamingUtils.toUpperCamelCase(segments[0]); - if (!name.endsWith("Svc") && !name.endsWith("Service")) { - name += "DomainService"; - } - context.put("Name", name); - context.put("name", segments[0].toLowerCase()); - context.put("DomainService", context.get("Name")); - context.put("domain_service", context.get("name")); - PathNode pathNode = templateNode.clone().resolve(context); - String path = render(pathNode, parentPath); + String path = internalRenderGenericDesign(literalDomainServiceDeclaration, parentPath, templateNode, context -> { + String name = context.get("Name"); + if (!name.endsWith("Svc") && !name.endsWith("Service")) { + name += "DomainService"; + } + + context.put("Name", name); + context.put("DomainService", context.get("Name")); + context.put("domain_service", context.get("name")); + return context; + }); getLog().info("生成领域服务代码:" + path); } @@ -547,18 +568,32 @@ public void renderDomainLayerDomainService(String literalDomainServiceDeclaratio */ public void renderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析自定义元素设计:" + literalGenericDeclaration); + String path = internalRenderGenericDesign(literalGenericDeclaration, parentPath, templateNode, null); + getLog().info("生成自定义元素代码:" + path); + } + + public String internalRenderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode, Function, Map> contextBuilder) throws IOException { String[] segments = TextUtils.splitWithTrim(literalGenericDeclaration, ":"); Map context = new HashMap<>(getEscapeContext()); for (int i = 0; i < segments.length; i++) { context.put("Val" + i, segments[i]); context.put("val" + i, segments[i].toLowerCase()); } - String name = NamingUtils.toUpperCamelCase(segments[0]); + + String name = NamingUtils.toUpperCamelCase(NamingUtils.getLastPackageName(segments[0])); + String reletivePath = NamingUtils.parentPackageName(segments[0]) + .replace(".", File.pathSeparator); + context.put("Name", name); context.put("name", segments[0].toLowerCase()); + context.put("path", reletivePath); + context.put("package", reletivePath.replace(File.separator, ".")); + if (null != contextBuilder) { + context = contextBuilder.apply(context); + } PathNode pathNode = templateNode.clone().resolve(context); String path = render(pathNode, parentPath); - getLog().info("生成自定义元素代码:" + path); + return path; } /** diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/NamingUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/NamingUtils.java index faa69f7..78e14a0 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/NamingUtils.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/NamingUtils.java @@ -53,4 +53,34 @@ public static String toUpperCamelCase(String underlineStr) { String camel = toLowerCamelCase(underlineStr); return camel.substring(0, 1).toUpperCase() + camel.substring(1); } + + /** + * 获取末位包名 + * + * @param packageName + * @return + */ + public static String getLastPackageName(String packageName){ + if(null == packageName || packageName.isEmpty()){ + return ""; + } + if(!packageName.contains(".")){ + return packageName; + } + return packageName.substring(packageName.lastIndexOf(".") + 1); + } + + /** + * 获取父包名 + * + * @param packageName + * @return + */ + public static String parentPackageName(String packageName){ + String lastPackageName = getLastPackageName(packageName); + if(packageName.length() == lastPackageName.length()){ + return ""; + } + return packageName.substring(0, packageName.length() - lastPackageName.length() - 1); + } } From 982c103b91c29ba715adb93d37a5e510a8f2a0a1 Mon Sep 17 00:00:00 2001 From: binking338 Date: Tue, 10 Sep 2024 13:14:57 +0800 Subject: [PATCH 17/62] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=A1=A8=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=20@Factory=20@Specification=20@DomainEvent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenArchMojo.java | 274 +++++++++++----- .../cap4j/ddd/codegen/GenEntityMojo.java | 310 ++++++++++++++++-- .../cap4j/ddd/codegen/GenRepositoryMojo.java | 9 +- .../cap4j/ddd/codegen/HelpMojo.java | 14 + .../ddd/codegen/misc/SqlSchemaUtils.java | 37 ++- 5 files changed, 519 insertions(+), 125 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index acfa157..9c63066 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -16,6 +16,8 @@ import java.io.File; import java.io.IOException; import java.nio.charset.Charset; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -344,14 +346,22 @@ public void renderDesign(TemplateNode templateNode, String parentPath) throws IO if (designMap.containsKey("integration_event")) { for (String literalCommand : designMap.get("integration_event")) { - renderAppLayerIntegrationEvent(literalCommand, parentPath, templateNode); + renderAppLayerIntegrationEvent("integration_event", literalCommand, parentPath, templateNode); } } case "integration_event_handler": if (designMap.containsKey("integration_event_handler")) { for (String literalCommand : designMap.get("integration_event_handler")) { - renderAppLayerIntegrationEvent(literalCommand, parentPath, templateNode); + renderAppLayerIntegrationEvent("integration_event_handler", literalCommand, parentPath, templateNode); + } + } + if (designMap.containsKey("integration_event")) { + for (String literalCommand : + designMap.get("integration_event")) { + if(literalCommand.split(":").length >= 3){ + renderAppLayerIntegrationEvent("integration_event_handler", literalCommand, parentPath, templateNode); + } } } break; @@ -364,6 +374,22 @@ public void renderDesign(TemplateNode templateNode, String parentPath) throws IO } } break; + case "specification": + if (designMap.containsKey("specification")) { + for (String literalCommand : + designMap.get("specification")) { + renderDomainLayerSpecificaton(literalCommand, parentPath, templateNode); + } + } + break; + case "factory": + if (designMap.containsKey("factory")) { + for (String literalCommand : + designMap.get("factory")) { + renderDomainLayerAggregateFactory(literalCommand, parentPath, templateNode); + } + } + break; case "domain_service": if (designMap.containsKey("domain_service")) { for (String literalCommand : @@ -384,159 +410,220 @@ public void renderDesign(TemplateNode templateNode, String parentPath) throws IO } /** - * @param literalCommandDeclaration 文本化命令声明 CommandName[:ResponseType_default_is_Boolean] + * @param literalCommandDeclaration 文本化命令声明 CommandName * @param templateNode 模板配置 */ public void renderAppLayerCommand(String literalCommandDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析命令设计:" + literalCommandDeclaration); String path = internalRenderGenericDesign(literalCommandDeclaration, parentPath, templateNode, context -> { - String name = context.get("Name"); - if (!name.endsWith("Cmd") && !name.endsWith("Command")) { - name += "Cmd"; + String Name = context.get("Name"); + if (!Name.endsWith("Cmd") && !Name.endsWith("Command")) { + Name += "Cmd"; } - context.put("Name", name); - context.put("ReturnType", NamingUtils.toUpperCamelCase(context.containsKey("Val1") ? context.get("Val1") : "Boolean")); + context.put("Name", Name); context.put("Command", context.get("Name")); - context.put("Request", context.get("Name")); - context.put("command", context.get("name")); - context.put("request", context.get("name")); - context.put("Response", context.get("ReturnType")); + context.put("command", context.get("Name").toLowerCase()); + context.put("Request", context.get("Command") + "Request"); + context.put("Response", context.get("Command") + "Response"); + + context.put("ReturnType", context.get("Response")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 命令描述"); + context.put("comment", context.get("Comment")); return context; }); getLog().info("生成命令代码:" + path); } /** - * @param literalQueryDeclaration 文本化查询声明 QueryName[:ResponseType_default_is_QueryNameResponse] + * @param literalQueryDeclaration 文本化查询声明 QueryName * @param templateNode 模板配置 */ public void renderAppLayerQuery(String literalQueryDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析查询设计:" + literalQueryDeclaration); String path = internalRenderGenericDesign(literalQueryDeclaration, parentPath, templateNode, context -> { - String name = context.get("Name"); - if (!name.endsWith("Qry") && !name.endsWith("Query")) { - name += "Qry"; + String Name = context.get("Name"); + if (!Name.endsWith("Qry") && !Name.endsWith("Query")) { + Name += "Qry"; } - context.put("Name", name); - context.put("ReturnType", NamingUtils.toUpperCamelCase(context.containsKey("Val1") ? context.get("Val1") : (context.get("Val0") + "Response"))); + context.put("Name", Name); context.put("Query", context.get("Name")); - context.put("Request", context.get("Name")); - context.put("query", context.get("name")); - context.put("request", context.get("name")); - context.put("Response", context.get("ReturnType")); + context.put("query", context.get("Name").toLowerCase()); + context.put("Request", context.get("Query") + "Request"); + context.put("Response", context.get("Query") + "Response"); + + context.put("ReturnType", context.get("Response")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 查询描述"); + context.put("comment", context.get("Comment")); return context; }); getLog().info("生成查询代码:" + path); } /** - * @param literalClientDeclaration 文本化防腐端声明 ClientName[:ResponseType_default_is_ClientNameResponse] + * @param literalClientDeclaration 文本化防腐端声明 ClientName * @param templateNode 模板配置 */ public void renderAppLayerClient(String literalClientDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析防腐端设计:" + literalClientDeclaration); String path = internalRenderGenericDesign(literalClientDeclaration, parentPath, templateNode, context -> { - String name = context.get("Name"); - if (!name.endsWith("Cli") && !name.endsWith("Client")) { - name += "Client"; + String Name = context.get("Name"); + if (!Name.endsWith("Cli") && !Name.endsWith("Client")) { + Name += "Cli"; } - context.put("Name", name); - context.put("ReturnType", NamingUtils.toUpperCamelCase(context.containsKey("Val1") ? context.get("Val1") : (context.get("Val0") + "Response"))); + context.put("Name", Name); context.put("Client", context.get("Name")); - context.put("Request", context.get("Name")); - context.put("client", context.get("name")); - context.put("request", context.get("name")); - context.put("Response", context.get("ReturnType")); + context.put("client", context.get("Name").toLowerCase()); + context.put("Request", context.get("Name") + "Request"); + context.put("Response", context.get("Name") + "Response"); + + context.put("ReturnType", context.get("Response")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 防腐端描述"); + context.put("comment", context.get("Comment")); return context; }); getLog().info("生成防腐端代码:" + path); } /** + * @param literalType 设计类型 * @param literalIntegrationEventDeclaration 文本化集成事件声明 IntegrationEventName[:mq-topic[:mq-consumer]] * @param templateNode 模板配置 */ - public void renderAppLayerIntegrationEvent(String literalIntegrationEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + public void renderAppLayerIntegrationEvent(String literalType, String literalIntegrationEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析集成事件设计:" + literalIntegrationEventDeclaration); String path = internalRenderGenericDesign(literalIntegrationEventDeclaration, parentPath, templateNode, context -> { - String name = context.get("Name"); - if (!name.endsWith("Evt") && !name.endsWith("Event")) { - name += "IntegrationEvent"; + String Name = context.get("Name"); + if (!Name.endsWith("Evt") && !Name.endsWith("Event")) { + Name += "IntegrationEvent"; } - context.put("Name", name); - context.put("MQ_TOPIC", context.containsKey("Val1") ? context.get("Val1") : context.get("Val0")); - if (Objects.equals("event_handler", templateNode.tag)) { - context.put("MQ_CONSUMER", context.containsKey("Val2") ? context.get("Val2") : "${spring.application.name}"); + context.put("Name", Name); + context.put("IntegrationEvent", context.get("Name")); + context.put("Event", context.get("IntegrationEvent")); + context.put("INTEGRATION_EVENT", context.get("IntegrationEvent")); + context.put("IE", context.get("IntegrationEvent")); + context.put("I_E", context.get("IntegrationEvent")); + context.put("integration_event", context.get("Name").toLowerCase()); + context.put("event", context.get("integration_event")); + context.put("ie", context.get("integration_event")); + context.put("i_e", context.get("integration_event")); + context.put("MQ_TOPIC", context.containsKey("Val1") ? ("\"" + context.get("Val1") + "\"") : ("\"" + context.get("Val0") + "\"")); + if (Objects.equals(literalType, "integration_event_handler") && !context.containsKey("Val2")) { + context.put("MQ_CONSUMER", "IntegrationEvent.NONE_SUBSCRIBER"); } else { - context.put("MQ_CONSUMER", "[none]"); + context.put("MQ_CONSUMER", context.containsKey("Val2") ? ("\"" + context.get("Val2") + "\"") : "\"${spring.application.name}\""); } - context.put("IntegrationEvent", context.get("Name")); - context.put("Event", context.get("Name")); - context.put("integration_event", context.get("name")); - context.put("event", context.get("name")); + + context.put("Comment", context.containsKey("Val3") ? context.get("Val3") : "todo: 集成事件描述"); + context.put("comment", context.get("Comment")); return context; }); getLog().info("生成集成事件代码:" + path); } /** - * @param literalDomainEventDeclaration 文本化领域事件声明 Val1[:Val2[:...]] + * @param literalDomainEventDeclaration 文本化领域事件声明 AggregateRootEntityName:DomainEventName * @param templateNode 模板配置 */ public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析领域事件设计:" + literalDomainEventDeclaration); String path = internalRenderGenericDesign(literalDomainEventDeclaration, parentPath, templateNode, context -> { - String name = context.get("Name"); - if (!name.endsWith("Evt") && !name.endsWith("Event")) { - name += "DomainEvent"; + String reletivePath = NamingUtils.parentPackageName(context.get("Val0")) + .replace(".", File.separator); + if (StringUtils.isNotBlank(reletivePath)) { + context.put("path", reletivePath); + context.put("package", StringUtils.isEmpty(reletivePath) ? "" : ("." + reletivePath.replace(File.separator, "."))); } - context.put("Name", name); + if(!context.containsKey("Val1")){ + throw new RuntimeException("缺失领域事件名称,领域事件设计格式:AggregateRootEntityName:DomainEventName"); + } + String Name = NamingUtils.toUpperCamelCase(context.get("Val1")); + if (!Name.endsWith("Evt") && !Name.endsWith("Event")) { + Name += "DomainEvent"; + } + context.put("Name", Name); context.put("DomainEvent", context.get("Name")); - context.put("Event", context.get("Name")); - context.put("domain_event", context.get("name")); - context.put("event", context.get("name")); + context.put("DOMAIN_EVENT", context.get("DomainEvent")); + context.put("Event", context.get("DomainEvent")); + String entity = NamingUtils.toUpperCamelCase( + NamingUtils.getLastPackageName(context.get("Val0")) + ); + boolean persist = false; + if(context.containsKey("val2") && "`true`persist`1`".contains("`" + context.get("val2") + "`")){ + persist = true; + } + context.put("persist", persist ? "true" : "false"); + context.put("PERSIST", context.get("persist")); + context.put("Entity", entity); + context.put("ENTITY", context.get("Entity")); + context.put("AggregateRoot", context.get("Entity")); + context.put("AGGREGATE_ROOT", context.get("Entity")); + context.put("aggregate_root", context.get("Entity")); + context.put("Aggregate", context.get("package")); + context.put("aggregate", context.get("package")); + + context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件描述"); + context.put("comment", context.get("Comment")); return context; }); getLog().info("生成领域事件代码:" + path); } /** - * @param literalAggregateFactoryDeclaration 文本化聚合工厂声明 AggregateFactoryName + * @param literalAggregateFactoryDeclaration 文本化聚合工厂声明 AggregateRootEntityName * @param templateNode 模板配置 */ public void renderDomainLayerAggregateFactory(String literalAggregateFactoryDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析聚合工厂设计:" + literalAggregateFactoryDeclaration); String path = internalRenderGenericDesign(literalAggregateFactoryDeclaration, parentPath, templateNode, context -> { - String name = context.get("Name"); - if (!name.endsWith("Fac") && !name.endsWith("Factory")) { - name += "Factory"; - } - context.put("Name", name); + String entity = context.get("Name"); + String Name = entity + "Factory"; + context.put("Name", Name); + context.put("name", Name.toLowerCase()); + context.put("Entity", entity); + context.put("entity", entity.toLowerCase()); + context.put("ENTITY", context.get("Entity")); + context.put("AggregateRoot", context.get("Entity")); + context.put("AGGREGATE_ROOT", context.get("Entity")); + context.put("aggregate_root", context.get("Entity")); + context.put("Aggregate", context.get("package")); + context.put("aggregate", context.get("package")); context.put("Factory", context.get("Name")); - context.put("Fac", context.get("Name")); - context.put("factory", context.get("name")); - context.put("fac", context.get("name")); + context.put("FACTORY", context.get("Factory")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 聚合工厂描述"); + context.put("comment", context.get("Comment")); return context; }); getLog().info("生成聚合工厂代码:" + path); } /** - * @param literalSpecificationDeclaration 文本化聚合工厂声明 SpecificationName + * @param literalSpecificationDeclaration 文本化聚合工厂声明 AggregateRootEntityName * @param templateNode 模板配置 */ public void renderDomainLayerSpecificaton(String literalSpecificationDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析实体规约设计:" + literalSpecificationDeclaration); String path = internalRenderGenericDesign(literalSpecificationDeclaration, parentPath, templateNode, context -> { - String name = context.get("Name"); - if (!name.endsWith("Spec") && !name.endsWith("Specification")) { - name += "Specification"; - } - context.put("Name", name); - context.put("Spec", context.get("Name")); + String entity = context.get("Name"); + String Name = entity + "Specification"; + context.put("Name", Name); + context.put("name", Name.toLowerCase()); + context.put("Entity", entity); + context.put("entity", entity.toLowerCase()); + context.put("ENTITY", context.get("Entity")); + context.put("AggregateRoot", context.get("Entity")); + context.put("AGGREGATE_ROOT", context.get("Entity")); + context.put("aggregate_root", context.get("Entity")); + context.put("Aggregate", context.get("package")); + context.put("aggregate", context.get("package")); context.put("Specification", context.get("Name")); - context.put("spec", context.get("name")); - context.put("specification", context.get("name")); + context.put("SPECIFICATION", context.get("Specification")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 实体规约描述"); + context.put("comment", context.get("Comment")); return context; }); getLog().info("生成实体规约代码:" + path); @@ -556,7 +643,10 @@ public void renderDomainLayerDomainService(String literalDomainServiceDeclaratio context.put("Name", name); context.put("DomainService", context.get("Name")); - context.put("domain_service", context.get("name")); + context.put("DOMAIN_SERVICE", context.get("DomainService")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 领域服务描述"); + context.put("comment", context.get("Comment")); return context; }); getLog().info("生成领域服务代码:" + path); @@ -580,20 +670,20 @@ public String internalRenderGenericDesign(String literalGenericDeclaration, Stri context.put("val" + i, segments[i].toLowerCase()); } - String name = NamingUtils.toUpperCamelCase(NamingUtils.getLastPackageName(segments[0])); - String reletivePath = NamingUtils.parentPackageName(segments[0]) - .replace(".", File.pathSeparator); + String name = segments[0].toLowerCase(); + String Name = NamingUtils.toUpperCamelCase(NamingUtils.getLastPackageName(segments[0])); + String path = NamingUtils.parentPackageName(segments[0]) + .replace(".", File.separator); - context.put("Name", name); - context.put("name", segments[0].toLowerCase()); - context.put("path", reletivePath); - context.put("package", reletivePath.replace(File.separator, ".")); + context.put("Name", Name); + context.put("name", name); + context.put("path", path); + context.put("package", StringUtils.isEmpty(path) ? "" : ("." + path.replace(File.separator, "."))); if (null != contextBuilder) { context = contextBuilder.apply(context); } PathNode pathNode = templateNode.clone().resolve(context); - String path = render(pathNode, parentPath); - return path; + return render(pathNode, parentPath); } /** @@ -621,18 +711,21 @@ public String renderDir(PathNode pathNode, String parentPath) throws IOException FileUtils.mkdir(path); break; case "skip": - getLog().info("目录存在:" + path); +// getLog().info("目录存在:" + path); break; } } else { - getLog().warn("目录创建:" + path); + getLog().info("目录创建:" + path); FileUtils.mkdir(path); } - if (StringUtils.isNotBlank(pathNode.tag)) { - TemplateNode templateNode = template.select(pathNode.tag); - if (null != templateNode) { - renderDesign(templateNode, path); + if (StringUtils.isNotBlank(pathNode.getTag())) { + String[] tags = pathNode.getTag().split(PATTERN_SPLITTER); + for (String tag : tags) { + TemplateNode templateNode = template.select(tag); + if (null != templateNode) { + renderDesign(templateNode, path); + } } } @@ -666,11 +759,11 @@ public String renderFile(PathNode pathNode, String parentPath) throws IOExceptio break; case "skip": default: - getLog().info("文件存在:" + path); +// getLog().info("文件存在:" + path); break; } } else { - getLog().warn("文件创建:" + path); + getLog().info("文件创建:" + path); FileUtils.fileWrite(path, content); } return path; @@ -721,6 +814,9 @@ public Map getEscapeContext() { context.put("aggregateIdentityClass", aggregateIdentityClass); context.put("aggregateRepositoryCustomerCode", aggregateRepositoryCustomerCode); context.put("ignoreAggregateRoots", ignoreAggregateRoots); + context.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); + context.put("SEPARATOR", File.separator); + context.put("separator", File.separator); context.put("symbol_pound", "#"); context.put("symbol_escape", "\\"); context.put("symbol_dollar", "$"); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index cbc6942..ad49687 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -5,12 +5,12 @@ import org.apache.maven.plugins.annotations.Mojo; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; -import org.netcorepal.cap4j.ddd.codegen.misc.Inflector; -import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; -import org.netcorepal.cap4j.ddd.codegen.misc.SqlSchemaUtils; +import org.netcorepal.cap4j.ddd.codegen.misc.*; import java.io.*; import java.nio.charset.Charset; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @@ -29,6 +29,8 @@ public class GenEntityMojo extends MyAbstractMojo { public Map> TableMap = new HashMap<>(); + public Map TableModuleMap = new HashMap<>(); + public Map TableAggregateMap = new HashMap<>(); public Map>> ColumnsMap = new HashMap<>(); public Map> EnumConfigMap = new HashMap<>(); public Map EnumPackageMap = new HashMap<>(); @@ -193,7 +195,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info(""); for (Map.Entry> entry : EnumConfigMap.entrySet()) { try { - writeEnumSourceFile(entry.getValue(), entry.getKey(), EnumPackageMap.get(entry.getKey()), getAggregate(EnumTableNameMap.get(entry.getKey())), domainModulePath, enumValueField, enumNameField); + writeEnumSourceFile(entry.getValue(), entry.getKey(), EnumPackageMap.get(entry.getKey()), domainModulePath, enumValueField, enumNameField); } catch (IOException e) { e.printStackTrace(); getLog().error(e); @@ -244,16 +246,18 @@ public void execute() throws MojoExecutionException, MojoFailureException { * @return */ public String getModule(String tableName) { - Map table = TableMap.get(tableName); - String module = SqlSchemaUtils.getModule(table); - getLog().info("尝试解析模块:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[缺失]" : module)); - while (!isAggregateRoot(table) && StringUtils.isBlank(module)) { - table = TableMap.get(getParent(table)); - module = SqlSchemaUtils.getModule(table); - getLog().info("尝试父表模块:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[缺失]" : module)); - } - getLog().info("模块解析结果:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[无]" : module)); - return module; + return TableModuleMap.computeIfAbsent(tableName, tn -> { + Map table = TableMap.get(tableName); + String module = SqlSchemaUtils.getModule(table); + getLog().info("尝试解析模块:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[缺失]" : module)); + while (!isAggregateRoot(table) && StringUtils.isBlank(module)) { + table = TableMap.get(getParent(table)); + module = SqlSchemaUtils.getModule(table); + getLog().info("尝试父表模块:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[缺失]" : module)); + } + getLog().info("模块解析结果:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[无]" : module)); + return module; + }); } /** @@ -263,23 +267,41 @@ public String getModule(String tableName) { * @return */ public String getAggregate(String tableName) { - Map table = TableMap.get(tableName); - String aggregate = SqlSchemaUtils.getAggregate(table); - getLog().info("尝试解析聚合:" + getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); - while (!isAggregateRoot(table) && StringUtils.isBlank(aggregate)) { - String parent = getParent(table); - if (StringUtils.isBlank(parent)) { - break; + return TableAggregateMap.computeIfAbsent(tableName, tn -> { + Map table = TableMap.get(tableName); + String aggregate = SqlSchemaUtils.getAggregate(table); + getLog().info("尝试解析聚合:" + getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); + while (!isAggregateRoot(table) && StringUtils.isBlank(aggregate)) { + String parent = getParent(table); + if (StringUtils.isBlank(parent)) { + break; + } + table = TableMap.get(parent); + aggregate = SqlSchemaUtils.getAggregate(table); + getLog().info("尝试父表聚合:" + getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); } - table = TableMap.get(parent); - aggregate = SqlSchemaUtils.getAggregate(table); - getLog().info("尝试父表聚合:" + getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); - } - if (StringUtils.isBlank(aggregate)) { - aggregate = getEntityJavaType(getTableName(table)); + if (StringUtils.isBlank(aggregate)) { + aggregate = getEntityJavaType(getTableName(table)); + } + getLog().info("聚合解析结果:" + tableName + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); + return aggregate; + }); + } + + /** + * 获取聚合名称 + * 格式: 模块.聚合 + * + * @param tableName + * @return + */ + public String getAggregateWithModule(String tableName) { + String module = getModule(tableName); + if (StringUtils.isNotBlank(module)) { + return module + "." + getAggregate(tableName); + } else { + return getAggregate(tableName); } - getLog().info("聚合解析结果:" + tableName + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); - return aggregate; } /** @@ -538,6 +560,8 @@ public void processImportLines(Map table, String basePackage, Li // importLines.add(" * " + getComment(table).replaceAll("[\\r\\n]", " ")); importLines.add(" * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); importLines.add(" * 警告:请勿手工修改该文件的字段声明,重新生成会覆盖字段声明"); + importLines.add(" * @author cap4j-ddd-codegen"); + importLines.add(" * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); importLines.add(" */"); } else { for (String entityClassExtraImport : entityClassExtraImports) { @@ -574,9 +598,9 @@ public void processAnnotationLines(Map table, List 0); + SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + getEntityJavaType(tableName) + "\", type = Aggregate.TYPE_ROOT, description = \"" + getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); } else { - SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(aggregate = \"" + getAggregate(tableName) + "\", name = \"" + getEntityJavaType(tableName) + "\", type = \"entity\", relevant = { \"" + getEntityJavaType(getParent(table)) + "\" }, description = \"" + getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); + SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + getEntityJavaType(tableName) + "\", type = Aggregate.TYPE_ENTITY, relevant = { \"" + getEntityJavaType(getParent(table)) + "\" }, description = \"" + getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); } if (StringUtils.isNotBlank(getAggregateRootAnnotation())) { if (isAggregateRoot(table)) { @@ -626,7 +650,7 @@ public void processAnnotationLines(Map table, List table, String basePackage, String baseDir) { String tableName = getTableName(table); String simpleClassName = getEntityJavaType(tableName); - String packageName = (basePackage + "." + getEntityPackage(tableName)); + String packageName = basePackage + "." + getEntityPackage(tableName); Optional existFilePath = SourceFileUtils.findJavaFileBySimpleClassName(baseDir, simpleClassName); if (existFilePath.isPresent()) { @@ -657,6 +681,33 @@ public void writeEntitySourceFile(Map table, List domainEvents = getDomainEvent(table); + for (String domainEvent : domainEvents) { + if (StringUtils.isBlank(domainEvent)) { + continue; + } + String[] segments = TextUtils.splitWithTrim(domainEvent, ":"); + String domainEventClassName = NamingUtils.toUpperCamelCase(segments[0]); + if (!domainEventClassName.endsWith("Event") && !domainEventClassName.endsWith("Evt")) { + domainEventClassName += "DomainEvent"; + } + String domainEventDescription = segments.length > 1 ? segments[1] : "todo: 领域事件说明"; + try { + writeDomainEventSourceFile(table, domainEventClassName, domainEventDescription, baseDir); + } catch (IOException ex) { + getLog().error("领域事件" + domainEventClassName + "代码生成异常"); + throw ex; + } + } + } } String filePath = SourceFileUtils.resolveSourceFile(baseDir, packageName, simpleClassName); @@ -803,6 +854,8 @@ public void writeEntityBuilderSourceFile(String basePackage, String baseDir, Map writeLine(out, "/**"); writeLine(out, " * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); writeLine(out, " * 警告:请勿手工修改该文件,重新生成会覆盖该文件"); + writeLine(out, " * @author cap4j-ddd-codegen"); + writeLine(out, " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); writeLine(out, " */"); writeLine(out, "public class EntityBuilder {"); for (Map.Entry> tableEntry : TableMap.entrySet()) { @@ -1058,7 +1111,192 @@ public void writeRelationProperty(BufferedWriter out, Map table, } } - public void writeEnumSourceFile(Map enumConfigs, String enumType, String enumPackage, String belongAggregate, String baseDir, String enumValueField, String enumNameField) throws IOException { + public void writeFactorySourceFile(Map table, String baseDir) throws IOException { + String tableName = getTableName(table); + String entityPackage = basePackage + "." + getEntityPackage(tableName); + String simpleClassName = getEntityJavaType(tableName); + String filePath = SourceFileUtils.resolveSourceFile(baseDir, entityPackage + ".factory", simpleClassName + "Payload"); + if (!FileUtils.fileExists(filePath)) { + getLog().info("开始生成工厂负载:" + filePath); + BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); + writeLine(out, + "package " + entityPackage + ".factory;\n" + + "\n" + + "import " + entityPackage + "." + simpleClassName + ";\n" + + "import lombok.Builder;\n" + + "import lombok.Data;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload;\n" + + "\n" + + "/**\n" + + " * " + simpleClassName + "工厂负载\n" + + " * todo: 工厂负载描述\n" + + " *\n" + + " * @author cap4j-ddd-codegen\n" + + " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) + "\n" + + " */\n" + + "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + simpleClassName + "Payload\", type = Aggregate.TYPE_FACTORY_PAYLOAD, description = \"\")\n" + + "@Data\n" + + "@Builder\n" + + "public class " + simpleClassName + "Payload implements AggregatePayload<" + simpleClassName + "> {\n" + + " private Long id;\n" + + "}" + ); + out.flush(); + out.close(); + } + + filePath = SourceFileUtils.resolveSourceFile(baseDir, entityPackage + ".factory", simpleClassName + "Factory"); + if (!FileUtils.fileExists(filePath)) { + getLog().info("开始生成聚合工厂:" + filePath); + BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); + writeLine(out, + "package " + entityPackage + ".factory;\n" + + "\n" + + "import " + entityPackage + "." + simpleClassName + ";\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + + "import org.springframework.stereotype.Service;\n" + + "\n" + + "/**\n" + + " * " + simpleClassName + "聚合工厂\n" + + " * todo: 聚合工厂描述\n" + + " *\n" + + " * @author cap4j-ddd-codegen\n" + + " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) + "\n" + + " */\n" + + "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + simpleClassName + "Factory\", type = Aggregate.TYPE_FACTORY, description = \"\")\n" + + "@Service\n" + + "public class " + simpleClassName + "Factory implements AggregateFactory<" + simpleClassName + "Payload, " + simpleClassName + "> {\n" + + "\n" + + " @Override\n" + + " public " + simpleClassName + " create(" + simpleClassName + "Payload payload) {\n" + + "\n" + + " return " + simpleClassName + ".builder()\n" + + "\n" + + " .build();\n" + + " }\n" + + "}" + ); + out.flush(); + out.close(); + } + } + + public void writeSpecificationSourceFile(Map table, String baseDir) throws IOException { + String tableName = getTableName(table); + String entityPackage = basePackage + "." + getEntityPackage(tableName); + String simpleClassName = getEntityJavaType(tableName); + String filePath = SourceFileUtils.resolveSourceFile(baseDir, entityPackage + ".specs", simpleClassName + "Specification"); + if (FileUtils.fileExists(filePath)) { + return; + } + getLog().info("开始生成实体规约:" + filePath); + BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); + writeLine(out, + "package " + entityPackage + ".specs;\n" + + "\n" + + "import " + entityPackage + "." + simpleClassName + ";\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.Specification;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + + "import org.springframework.stereotype.Service;\n" + + "\n" + + "/**\n" + + " * " + simpleClassName + "的规格约束\n" + + " * todo: 规格约束描述\n" + + " *\n" + + " * @author cap4j-ddd-codegen\n" + + " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) + "\n" + + " */\n" + + "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + simpleClassName + "Specification\", type = Aggregate.TYPE_SPECIFICATION, description = \"\")\n" + + "@Service\n" + + "public class " + simpleClassName + "Specification implements Specification<" + simpleClassName + "> {\n" + + " @Override\n" + + " public Result specify(" + simpleClassName + " entity) {\n" + + " return Result.fail(\"未实现\");\n" + + " }\n" + + "}" + ); + + out.flush(); + out.close(); + } + + public void writeDomainEventSourceFile(Map table, String domainEventClassName, String domainEventDescription, String baseDir) throws IOException { + String tableName = getTableName(table); + String entityPackage = basePackage + "." + getEntityPackage(tableName); + String simpleClassName = getEntityJavaType(tableName); + String filePath = SourceFileUtils.resolveSourceFile(baseDir, entityPackage + ".events", domainEventClassName); + if (!FileUtils.fileExists(filePath)) { + getLog().info("开始生成领域事件:" + filePath); + BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); + writeLine(out, + "package " + entityPackage + ".events;\n" + + "\n" + +// "import " + entityPackage + "." + simpleClassName + ";\n" + + "import lombok.AllArgsConstructor;\n" + + "import lombok.Builder;\n" + + "import lombok.Data;\n" + + "import lombok.NoArgsConstructor;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + + "import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent;\n" + + "\n" + + "/**\n" + + " * " + domainEventDescription + "\n" + + " *\n" + + " * @author cap4j-ddd-codegen\n" + + " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) + "\n" + + " */\n" + + "@DomainEvent(persist = false)\n" + + "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + domainEventClassName + "\", type = Aggregate.TYPE_DOMAIN_EVENT, description = \"" + domainEventDescription.replaceAll("(\\r\\n)|(\\r)|(\\n)", "\\n") + "\")\n" + + "@Data\n" + + "@Builder\n" + + "@AllArgsConstructor\n" + + "@NoArgsConstructor\n" + + "public class " + domainEventClassName + " {\n" + + " Long id;\n" + + "}" + ); + out.flush(); + out.close(); + } + + String subscriberPackage = basePackage + ".application.subscribers"; + filePath = SourceFileUtils.resolveSourceFile(baseDir, subscriberPackage, domainEventClassName + "Subscriber"); + if (!FileUtils.fileExists(filePath)) { + + getLog().info("开始生成领域事件订阅:" + filePath); + BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); + writeLine(out, + "package " + subscriberPackage + ";\n" + + "\n" + + "import " + entityPackage + ".events." + domainEventClassName + ";\n" + + "import lombok.RequiredArgsConstructor;\n" + + "import org.springframework.context.event.EventListener;\n" + + "import org.springframework.stereotype.Service;\n" + + "\n" + + "/**\n" + + " * todo: 领域事件说明\n" + + " */\n" + + "@Service\n" + + "@RequiredArgsConstructor\n" + + "public class " + domainEventClassName + "Subscriber {\n" + + "\n" + + " @EventListener(" + domainEventClassName + ".class)\n" + + " public void on(" + domainEventClassName + " event) {\n" + + "\n" + + " }\n" + + "\n" + + "}" + ); + out.flush(); + out.close(); + } + } + + public void writeEnumSourceFile(Map enumConfigs, String enumType, String enumPackage, String baseDir, String enumValueField, String enumNameField) throws IOException { + String tableName = EnumTableNameMap.get(enumType); + new File(SourceFileUtils.resolveDirectory(baseDir, enumPackage)).mkdirs(); String filePath = SourceFileUtils.resolveSourceFile(baseDir, enumPackage, enumType); @@ -1077,8 +1315,10 @@ public void writeEnumSourceFile(Map enumConfigs, String enumT writeLine(out, "/**"); writeLine(out, " * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); writeLine(out, " * 警告:请勿手工修改该文件,重新生成会覆盖该文件"); + writeLine(out, " * @author cap4j-ddd-codegen"); + writeLine(out, " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); writeLine(out, " */"); - writeLine(out, "@Aggregate(aggregate = \"" + belongAggregate + "\", name = \"" + enumType + "\", type = \"enum\", description = \"\")"); + writeLine(out, "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + enumType + "\", type = \"enum\", description = \"\")"); writeLine(out, "public enum " + enumType + " {"); writeLine(out, ""); for (Map.Entry entry : enumConfigs.entrySet()) { @@ -1171,6 +1411,8 @@ public void writeSchemaSourceFile(Map table, List 1) { @@ -263,6 +268,8 @@ public void writeAggregateRepositorySourceFile(String entitySourceFilePath, Stri writeLine(out, ""); writeLine(out, "/**"); writeLine(out, " * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); + writeLine(out, " * @author cap4j-ddd-codegen"); + writeLine(out, " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); writeLine(out, " */"); } writeLine(out, "public interface " + simpleClassName + "Repository extends " + replacePlaceholder(getAggregateRepositoryBaseClass(), simpleClassName, aggregateIdentityClass, aggregate) + " {"); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java index b51aa17..ae00c13 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java @@ -155,6 +155,20 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("@IG[={generator}];"); getLog().info("功能:标注主键生成策略。org.hibernate.id.IdentifierGenerator"); getLog().info("--------------------------------------------------"); + getLog().info("@DomainEvent={domain_event_name1}[:{description}][|{domain_event_name2}[:{description}]];"); + getLog().info("@DE={domain_event_name1}[:{description}][|{domain_event_name2}[:{description}]];"); + getLog().info("@Event={domain_event_name1}[:{description}][|{domain_event_name2}[:{description}]];"); + getLog().info("@Evt={domain_event_name1}[:{description}][|{domain_event_name2}[:{description}]];"); + getLog().info("功能:标注该表对应聚合内的领域事件列表,多个领域事件使用','分割"); + getLog().info("--------------------------------------------------"); + getLog().info("@Factory;"); + getLog().info("@Fac;"); + getLog().info("功能:标注该表对应聚合需生成聚合工厂"); + getLog().info("--------------------------------------------------"); + getLog().info("@Specification;"); + getLog().info("@Spec;"); + getLog().info("功能:标注该表对应聚合需生成实体规约"); + getLog().info("--------------------------------------------------"); getLog().info(""); getLog().info(""); getLog().info("gen-ddd:gen-entity 支持如下【列注解】:"); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java index 57640da..19e939e 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java @@ -696,7 +696,7 @@ public static Map getEnum(Map column) { String enumsConfig = getAnyAnnotation(column, Arrays.asList("Enum", "E")); Map result = new HashMap<>(); if (StringUtils.isNotBlank(enumsConfig)) { - String[] enumConfigs = enumsConfig.split("\\|"); + String[] enumConfigs = TextUtils.splitWithTrim(enumsConfig, "\\|"); for (int i = 0; i < enumConfigs.length; i++) { String enumConfig = enumConfigs[i]; getLog().debug(enumConfig); @@ -731,4 +731,39 @@ public static Map getEnum(Map column) { } return result; } + + /** + * 是否生成工厂 + * + * @param table + * @return + */ + public static boolean hasFactory(Map table) { + return isAggregateRoot(table) && hasAnyAnnotation(table, Arrays.asList("Factory", "Fac")); + } + + /** + * 是否生成规约 + * + * @param table + * @return + */ + public static boolean hasSpecification(Map table) { + return isAggregateRoot(table) && hasAnyAnnotation(table, Arrays.asList("Specification", "Spec")); + } + + public static boolean hasDomainEvent(Map table) { + return isAggregateRoot(table) && hasAnyAnnotation(table, Arrays.asList("DomainEvent", "DE", "Event", "Evt")); + } + + public static List getDomainEvent(Map table) { + if (!isAggregateRoot(table)) { + return Collections.emptyList(); + } + String literalDomainEvents = getAnyAnnotation(table, Arrays.asList("DomainEvent", "DE", "Event", "Evt")); + if (StringUtils.isBlank(literalDomainEvents)) { + return Collections.emptyList(); + } + return Arrays.stream(TextUtils.splitWithTrim(literalDomainEvents, "\\|")).collect(Collectors.toList()); + } } From d36da61d0119079a6845c5257f0608f66506ac4c Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 11 Sep 2024 08:28:15 +0800 Subject: [PATCH 18/62] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E8=81=9A=E5=90=88?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E6=94=B9=E4=B8=BA=E8=81=9A=E5=90=88=E6=A0=B9?= =?UTF-8?q?=E7=B1=BB=E5=90=8D=E5=B0=8F=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index ad49687..e589075 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -281,7 +281,7 @@ public String getAggregate(String tableName) { getLog().info("尝试父表聚合:" + getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); } if (StringUtils.isBlank(aggregate)) { - aggregate = getEntityJavaType(getTableName(table)); + aggregate = getEntityJavaType(getTableName(table)).toLowerCase(); } getLog().info("聚合解析结果:" + tableName + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); return aggregate; From d00b24d2bf2daa108a3804db62869fba0ca84b1f Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 11 Sep 2024 08:29:38 +0800 Subject: [PATCH 19/62] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=93=E5=82=A8?= =?UTF-8?q?=E3=80=81request=E7=AD=89mediator=E6=A8=A1=E5=BC=8F=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cap4j-ddd-codegen-template.json | 263 ++++++++++++++++-- .../SnowflakeAutoConfiguration.java | 3 +- .../configure/SnowflakeProperties.java | 2 +- .../repo/JpaRepositoryAutoConfiguration.java | 8 +- .../main/resources/META-INF/spring.factories | 2 +- .../cap4j/ddd/application/RequestHandler.java | 2 +- .../cap4j/ddd/application/RequestParam.java | 10 + .../ddd/application/RequestSupervisor.java | 24 +- .../ddd/application/command/Command.java | 3 +- .../application/command/CommandNoneParam.java | 16 -- .../command/CommandNoneParamAndResult.java | 17 -- .../command/CommandNoneResult.java | 12 - .../command/NoneResultCommandParam.java | 14 + ....java => IntegrationEventNotifyEvent.java} | 6 +- .../DefaultIntegrationEventSupervisor.java | 25 +- .../impl/DefaultRequestSupervisor.java | 27 +- .../ddd/application/query/ListQuery.java | 4 +- .../application/query/ListQueryNoArgs.java | 18 -- .../ddd/application/query/ListQueryParam.java | 14 + .../ddd/application/query/PageQuery.java | 4 +- .../ddd/application/query/PageQueryParam.java | 14 + .../cap4j/ddd/application/query/Query.java | 3 +- .../ddd/application/query/QueryNoArgs.java | 16 -- .../domain/aggregate/AggregateFactory.java | 19 +- .../aggregate/AggregateFactorySupervisor.java | 15 +- .../domain/aggregate/AggregatePayload.java | 11 + .../aggregate/annotation/Aggregate.java | 2 + .../aggregate/impl/AbstractSpecification.java | 14 - .../DefaultAggregateFactorySupervisor.java | 40 +-- .../impl/DefaultSpecificationManager.java | 2 +- ...ntAttachedTransactionPostCommitEvent.java} | 4 +- ...entAttachedTransactionPreCommitEvent.java} | 4 +- .../impl/DefaultDomainEventSupervisor.java | 24 +- .../event/impl/DefaultEventPublisher.java | 29 +- .../impl/DefaultEventSubscriberManager.java | 28 +- .../cap4j/ddd/domain/repo/Predicate.java | 11 + .../cap4j/ddd/domain/repo/Repository.java | 57 ++-- .../ddd/domain/repo/RepositorySupervisor.java | 74 +---- .../cap4j/ddd/impl/DefaultMediator.java | 74 ++--- .../cap4j/ddd/share/misc/ClassUtils.java | 2 +- .../ddd/domain/event/persistence/Event.java | 2 + .../ddd/application/impl/JpaUnitOfWork.java | 6 - .../domain/repo/AbstractJpaRepository.java | 50 ++-- .../cap4j/ddd/domain/repo/JpaPredicate.java | 34 +++ .../impl/DefaultRepositorySupervisor.java | 71 ++--- 45 files changed, 549 insertions(+), 531 deletions(-) rename cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/{application => domain}/distributed/configure/SnowflakeProperties.java (87%) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestParam.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParam.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParamAndResult.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneResult.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/NoneResultCommandParam.java rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/{IntegrationEventAttachedTransactionCommittedEvent.java => IntegrationEventNotifyEvent.java} (70%) delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryNoArgs.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryParam.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/PageQueryParam.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/QueryNoArgs.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregatePayload.java delete mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/AbstractSpecification.java rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/{DomainEventAttachedTransactionCommittedEvent.java => DomainEventAttachedTransactionPostCommitEvent.java} (75%) rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/{DomainEventAttachedTransactionCommitingEvent.java => DomainEventAttachedTransactionPreCommitEvent.java} (82%) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Predicate.java create mode 100644 ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index 997836c..afc5764 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -1,5 +1,193 @@ { "type": "root", + "templates": [ + { + "type": "dir", + "tag": "command", + "name": "${path}", + "children": [ + { + "type": "file", + "name": "${Command}Request.java", + "format": "raw", + "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n/**\n * todo: 命令请求参数描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\npublic class ${Command}Request implements RequestParam<${ReturnType}> {\n\n}" + }, + { + "type": "file", + "name": "${Command}Response.java", + "format": "raw", + "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * todo: 命令响应描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Command}Response {\n boolean success;\n}\n" + }, + { + "type": "file", + "name": "${Command}Handler.java", + "format": "raw", + "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.netcorepal.cap4j.ddd.Mediator;\nimport org.netcorepal.cap4j.ddd.application.command.Command;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 命令处理器描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class ${Command}Handler implements Command<${Command}Request, ${Command}Response> {\n\n @Override\n public ${Command}Response exec(${Command}Request cmd) {\n Mediator.uow().save();\n \n return null;\n }\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "query", + "name": "${path}", + "children": [ + { + "type": "file", + "name": "${Query}Request.java", + "format": "raw", + "data": "package ${basePackage}.application.queries${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n\n/**\n * todo: 查询请求参数描述\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Query}Request implements RequestParam<${Query}Response> {\n Long id;\n}" + }, + { + "type": "file", + "name": "${Query}Response.java", + "format": "raw", + "data": "package ${basePackage}.application.queries${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * todo: 查询响应描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Query}Response {\n Long id;\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "query_handler", + "name": "", + "children": [ + { + "type": "file", + "name": "${Query}Handler.java", + "format": "raw", + "data": "package ${basePackage}.adapter.application.queries;\n\nimport ${basePackage}.application.queries${package}.${Query}Request;\nimport ${basePackage}.application.queries${package}.${Query}Response;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.netcorepal.cap4j.ddd.application.query.Query;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 查询处理器描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class ${Query}Handler implements Query<${Query}Request, ${Query}Response> {\n \n @Override\n public ${Query}Response exec(${Query}Request request) {\n // mybatis / jpa 哪个顺手就用哪个吧!\n return null;\n }\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "client", + "name": "${path}", + "children": [ + { + "type": "file", + "name": "${Client}Request.java", + "format": "raw", + "data": "package ${basePackage}.application.distributed.clients;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n/**\n * todo: 防腐端请求参数描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Client}Request implements RequestParam<${Client}Response> {\n Long id;\n}\n" + }, + { + "type": "file", + "name": "${Client}Response.java", + "format": "raw", + "data": "package ${basePackage}.application.distributed.clients;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * todo: 防腐端响应描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Client}Response {\n Long id;\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "client_handler", + "name": "${path}", + "children": [ + { + "type": "file", + "name": "${Client}Handler.java", + "format": "raw", + "data": "package ${basePackage}.adapter.application.distributed.clients${package};\n\nimport ${basePackage}.application.distributed.clients${package}.${Client}Request;\nimport ${basePackage}.application.distributed.clients${package}.${Client}Response;\nimport org.netcorepal.cap4j.ddd.application.RequestHandler;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 防腐端描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\npublic class ${Client}Handler implements RequestHandler<${Client}Request, ${Client}Response> {\n @Override\n public ${Client}Response exec(${Client}Request ${Client}Request) {\n return null;\n }\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "integration_event", + "name": "", + "children": [ + { + "type": "file", + "name": "${IntegrationEvent}.java", + "format": "raw", + "data": "package ${basePackage}.application.distributed.events;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent;\n\n/**\n * todo: 集成事件描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@IntegrationEvent(value = ${MQ_TOPIC}, subscriber = ${MQ_CONSUMER})\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ${IntegrationEvent} {\n private Long id;\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "integration_event_handler", + "name": "", + "children": [ + { + "type": "file", + "name": "${IntegrationEvent}Subscriber.java", + "format": "raw", + "data": "package ${basePackage}.application.subscribers;\n\nimport ${basePackage}.application.distributed.events.${IntegrationEvent};\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 集成事件描述\n */\n@Service\n@RequiredArgsConstructor\npublic class ${IntegrationEvent}Subscriber {\n\n @EventListener(${IntegrationEvent}.class)\n public void on(${IntegrationEvent} event) {\n \n }\n\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "domain_event", + "name": "${path}${SEPARATOR}events", + "children": [ + { + "type": "file", + "name": "${DomainEvent}.java", + "format": "raw", + "data": "package ${basePackage}.domain.aggregates${package}.events;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent;\n\n/**\n * todo: 领域事件描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@DomainEvent(persist = ${persist})\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${DomainEvent}\", type = Aggregate.TYPE_DOMAIN_EVENT, description = \"\")\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ${DomainEvent} {\n Long id;\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "domain_event_handler", + "name": "", + "children": [ + { + "type": "file", + "name": "${DomainEvent}Subscriber.java", + "format": "raw", + "data": "package ${basePackage}.application.subscribers;\n\nimport ${basePackage}.domain.aggregates${package}.events.${DomainEvent};\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 领域事件订阅说明\n */\n@Service\n@RequiredArgsConstructor\npublic class ${DomainEvent}Subscriber {\n\n @EventListener(${DomainEvent}.class)\n public void on(${DomainEvent} event) {\n \n }\n\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "factory", + "name": "${path}${SEPARATOR}factory", + "children": [ + { + "type": "file", + "name": "${Entity}Payload.java", + "format": "raw", + "data": "package ${basePackage}.domain.aggregates${package}.factory;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport lombok.Builder;\nimport lombok.Data;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload;\n\n/**\n * todo: 工厂负载描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Payload\", type = Aggregate.TYPE_FACTORY_PAYLOAD, description = \"\")\n@Data\n@Builder\npublic class ${Entity}Payload implements AggregatePayload<${Entity}> {\n \n}\n" + }, + { + "type": "file", + "name": "${Entity}Factory.java", + "format": "raw", + "data": "package ${basePackage}.domain.aggregates${package}.factory;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory;\nimport org.springframework.stereotype.Service;\n\n/**\n * TODO: 聚合工厂\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Factory\", type = Aggregate.TYPE_FACTORY, description = \"\")\n@Service\npublic class ${Entity}Factory implements AggregateFactory<${Entity}Payload, ${Entity}> {\n\n @Override\n public ${Entity} create(${Entity}Payload payload) {\n\n return ${Entity}.builder()\n\n .build();\n }\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "specification", + "name": "${path}${SEPARATOR}specs", + "children": [ + { + "type": "file", + "name": "${Entity}Specification.java", + "format": "raw", + "data": "package ${basePackage}.domain.aggregates${package}.specs;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.Specification;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 实体规格约束描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Specification\", type = Aggregate.TYPE_SPECIFICATION, description = \"\")\n@Service\npublic class ${Entity}Specification implements Specification<${Entity}> {\n @Override\n public Result specify(${Entity} entity) {\n return Result.fail(\"未实现\");\n }\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "domain_service", + "name": "", + "children": [ + { + "type": "file", + "name": "${Name}.java", + "format": "raw", + "data": "package ${basePackage}.domain.services;\n\nimport org.netcorepal.cap4j.ddd.domain.service.annotation.DomainService;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 领域服务描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@DomainService\n@Service\npublic class ${Name} {\n}\n" + } + ] + } + ], "children": [ { "type": "dir", @@ -29,19 +217,19 @@ "type": "file", "name": "ErrorException.java", "format": "raw", - "data": "package ${basePackage}._share.exception;\n\nimport ${basePackage}._share.CodeEnum;\nimport org.slf4j.event.Level;\n\n/**\n * 错误\n * @author binking338\n */\npublic class ErrorException extends KnownException {\n\n public ErrorException(String msg) {\n super(CodeEnum.FAIL.getCode(), msg, Level.ERROR.toString());\n }\n\n public ErrorException(String msg, Throwable e) {\n super(CodeEnum.FAIL.getCode(), msg, Level.ERROR.toString(), e);\n }\n\n public ErrorException(CodeEnum codeEnum){\n super(codeEnum, Level.ERROR.toString());\n }\n\n public ErrorException(CodeEnum codeEnum, Throwable Throwable){\n super(codeEnum, Level.ERROR.toString(), Throwable);\n }\n\n public ErrorException(Integer code, String msg) {\n super(code, msg, Level.ERROR.toString());\n }\n\n public ErrorException(Integer code, String msg, Throwable Throwable) {\n super(code, msg, Level.ERROR.toString(), Throwable);\n }\n}\n" + "data": "package ${basePackage}._share.exception;\n\nimport ${basePackage}._share.CodeEnum;\nimport org.slf4j.event.Level;\n\n/**\n * 错误\n * @author cap4j-ddd-codegen\n */\npublic class ErrorException extends KnownException {\n\n public ErrorException(String msg) {\n super(CodeEnum.FAIL.getCode(), msg, Level.ERROR.toString());\n }\n\n public ErrorException(String msg, Throwable e) {\n super(CodeEnum.FAIL.getCode(), msg, Level.ERROR.toString(), e);\n }\n\n public ErrorException(CodeEnum codeEnum){\n super(codeEnum, Level.ERROR.toString());\n }\n\n public ErrorException(CodeEnum codeEnum, Throwable Throwable){\n super(codeEnum, Level.ERROR.toString(), Throwable);\n }\n\n public ErrorException(Integer code, String msg) {\n super(code, msg, Level.ERROR.toString());\n }\n\n public ErrorException(Integer code, String msg, Throwable Throwable) {\n super(code, msg, Level.ERROR.toString(), Throwable);\n }\n}\n" }, { "type": "file", "name": "KnownException.java", "format": "raw", - "data": "package ${basePackage}._share.exception;\n\nimport ${basePackage}._share.CodeEnum;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\nimport org.slf4j.event.Level;\n\n/**\n * \n * @author binnking338\n */\n@Data\n@Slf4j\npublic class KnownException extends RuntimeException {\n\n private Integer code;\n\n private String msg;\n\n private String level;\n\n public KnownException(CodeEnum codeEnum) {\n this(codeEnum.getCode(), codeEnum.getName());\n }\n\n public KnownException(CodeEnum codeEnum, String level){\n this(codeEnum.getCode(), codeEnum.getName(), level);\n }\n\n public KnownException(CodeEnum codeEnum, String level, Throwable Throwable){\n this(codeEnum.getCode(), codeEnum.getName(), level, Throwable);\n }\n\n public KnownException(String msg) {\n this(CodeEnum.FAIL.getCode(), msg);\n }\n\n public KnownException(Integer code, String msg) {\n this(code, msg, Level.DEBUG.toString());\n }\n\n public KnownException(Integer code, String msg, String level) {\n this(code, msg, level, null);\n }\n\n public KnownException(Integer code, String msg, String level, Throwable Throwable) {\n super(msg, Throwable);\n this.code = code;\n this.msg = msg;\n this.level = level;\n }\n\n\n public static KnownException systemError() {\n return new KnownException(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getName(), Level.ERROR.toString());\n }\n\n public static KnownException systemError(Throwable Throwable) {\n return new KnownException(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getName(), Level.ERROR.toString(), Throwable);\n }\n\n public static KnownException illegalArgument() {\n return new KnownException(CodeEnum.PARAM_INVALIDATE.getCode(), CodeEnum.PARAM_INVALIDATE.getName(), Level.ERROR.toString());\n }\n\n public static KnownException illegalArgument(String argumentName) {\n return new KnownException(CodeEnum.PARAM_INVALIDATE.getCode(), CodeEnum.PARAM_INVALIDATE.getName(), Level.ERROR.toString(), new IllegalArgumentException(argumentName));\n }\n}\n" + "data": "package ${basePackage}._share.exception;\n\nimport ${basePackage}._share.CodeEnum;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\nimport org.slf4j.event.Level;\n\n/**\n * \n * @author cap4j-gen-arch\n */\n@Data\n@Slf4j\npublic class KnownException extends RuntimeException {\n\n private Integer code;\n\n private String msg;\n\n private String level;\n\n public KnownException(CodeEnum codeEnum) {\n this(codeEnum.getCode(), codeEnum.getName());\n }\n\n public KnownException(CodeEnum codeEnum, String level){\n this(codeEnum.getCode(), codeEnum.getName(), level);\n }\n\n public KnownException(CodeEnum codeEnum, String level, Throwable Throwable){\n this(codeEnum.getCode(), codeEnum.getName(), level, Throwable);\n }\n\n public KnownException(String msg) {\n this(CodeEnum.FAIL.getCode(), msg);\n }\n\n public KnownException(Integer code, String msg) {\n this(code, msg, Level.DEBUG.toString());\n }\n\n public KnownException(Integer code, String msg, String level) {\n this(code, msg, level, null);\n }\n\n public KnownException(Integer code, String msg, String level, Throwable Throwable) {\n super(msg, Throwable);\n this.code = code;\n this.msg = msg;\n this.level = level;\n }\n\n\n public static KnownException systemError() {\n return new KnownException(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getName(), Level.ERROR.toString());\n }\n\n public static KnownException systemError(Throwable Throwable) {\n return new KnownException(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getName(), Level.ERROR.toString(), Throwable);\n }\n\n public static KnownException illegalArgument() {\n return new KnownException(CodeEnum.PARAM_INVALIDATE.getCode(), CodeEnum.PARAM_INVALIDATE.getName(), Level.ERROR.toString());\n }\n\n public static KnownException illegalArgument(String argumentName) {\n return new KnownException(CodeEnum.PARAM_INVALIDATE.getCode(), CodeEnum.PARAM_INVALIDATE.getName(), Level.ERROR.toString(), new IllegalArgumentException(argumentName));\n }\n}\n" }, { "type": "file", "name": "WarnException.java", "format": "raw", - "data": "package ${basePackage}._share.exception;\n\nimport ${basePackage}._share.CodeEnum;\nimport org.slf4j.event.Level;\n\n/**\n * 告警\n * @author binnking338\n */\npublic class WarnException extends KnownException {\n\n public WarnException(String msg) {\n super(CodeEnum.FAIL.getCode(), msg, Level.WARN.toString());\n }\n\n public WarnException(String msg, Throwable e) {\n super(CodeEnum.FAIL.getCode(), msg, Level.WARN.toString(), e);\n }\n\n public WarnException(CodeEnum codeEnum) {\n super(codeEnum, Level.WARN.toString());\n }\n\n public WarnException(CodeEnum codeEnum, Throwable Throwable) {\n super(codeEnum, Level.WARN.toString(), Throwable);\n }\n\n public WarnException(Integer code, String msg) {\n super(code, msg, Level.WARN.toString());\n }\n\n public WarnException(Integer code, String msg, Throwable Throwable) {\n super(code, msg, Level.WARN.toString(), Throwable);\n }\n}\n" + "data": "package ${basePackage}._share.exception;\n\nimport ${basePackage}._share.CodeEnum;\nimport org.slf4j.event.Level;\n\n/**\n * 告警\n * @author cap4j-gen-arch\n */\npublic class WarnException extends KnownException {\n\n public WarnException(String msg) {\n super(CodeEnum.FAIL.getCode(), msg, Level.WARN.toString());\n }\n\n public WarnException(String msg, Throwable e) {\n super(CodeEnum.FAIL.getCode(), msg, Level.WARN.toString(), e);\n }\n\n public WarnException(CodeEnum codeEnum) {\n super(codeEnum, Level.WARN.toString());\n }\n\n public WarnException(CodeEnum codeEnum, Throwable Throwable) {\n super(codeEnum, Level.WARN.toString(), Throwable);\n }\n\n public WarnException(Integer code, String msg) {\n super(code, msg, Level.WARN.toString());\n }\n\n public WarnException(Integer code, String msg, Throwable Throwable) {\n super(code, msg, Level.WARN.toString(), Throwable);\n }\n}\n" } ] }, @@ -49,13 +237,13 @@ "type": "file", "name": "Constants.java", "format": "raw", - "data": "package ${basePackage}._share;\n\n/**\n * 常量定义\n * @author binking338\n */\npublic class Constants {\n public final static String UTF_8 = \"UTF-8\";\n}\n" + "data": "package ${basePackage}._share;\n\n/**\n * 常量定义\n * @author cap4j-ddd-codegen\n */\npublic class Constants {\n public final static String UTF_8 = \"UTF-8\";\n}\n" }, { "type": "file", "name": "CodeEnum.java", "format": "raw", - "data": "package ${basePackage}._share;\n\n/**\n * Api 状态码\n * @author binnking338\n */\npublic enum CodeEnum {\n\n /**\n * 成功\n */\n SUCCESS(0, \"成功\"),\n /**\n * 失败\n */\n FAIL(-1, \"失败\"),\n /**\n * 参数错误\n */\n PARAM_INVALIDATE(-2, \"参数错误\"),\n /**\n * 约束未通过\n */\n SPECIFICATION_UNSATISFIED(-3, \"约束不满足\"),\n /**\n * 系统异常\n */\n ERROR(-9, \"系统异常\"),\n /**\n * 404\n */\n NOT_FOUND(404, \"没找到请求\"),\n /**\n * 不支持当前请求方法\n */\n METHOD_NOT_SUPPORTED(405, \"不支持当前请求方法\"),\n /**\n * 消息不能读取\n */\n MESSAGE_NOT_READABLE(407, \"消息不能读取\");\n\n private final Integer code;\n private final String name;\n\n CodeEnum(Integer code, String name) {\n this.code = code;\n this.name = name;\n }\n\n public Integer getCode() {\n return code;\n }\n\n public String getName() {\n return name;\n }\n}\n" + "data": "package ${basePackage}._share;\n\n/**\n * Api 状态码\n * @author cap4j-gen-arch\n */\npublic enum CodeEnum {\n\n /**\n * 成功\n */\n SUCCESS(0, \"成功\"),\n /**\n * 失败\n */\n FAIL(-1, \"失败\"),\n /**\n * 参数错误\n */\n PARAM_INVALIDATE(-2, \"参数错误\"),\n /**\n * 约束未通过\n */\n SPECIFICATION_UNSATISFIED(-3, \"约束不满足\"),\n /**\n * 系统异常\n */\n ERROR(-9, \"系统异常\"),\n /**\n * 404\n */\n NOT_FOUND(404, \"没找到请求\"),\n /**\n * 不支持当前请求方法\n */\n METHOD_NOT_SUPPORTED(405, \"不支持当前请求方法\"),\n /**\n * 消息不能读取\n */\n MESSAGE_NOT_READABLE(407, \"消息不能读取\");\n\n private final Integer code;\n private final String name;\n\n CodeEnum(Integer code, String name) {\n this.code = code;\n this.name = name;\n }\n\n public Integer getCode() {\n return code;\n }\n\n public String getName() {\n return name;\n }\n}\n" } ] }, @@ -97,7 +285,7 @@ "type": "file", "name": "MyIntegrationEventInterceptor.java", "format": "raw", - "data": "package ${basePackage}.adapter.application._share.configure;\n\nimport org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor;\nimport org.netcorepal.cap4j.ddd.domain.event.EventRecord;\nimport org.springframework.stereotype.Service;\n\nimport java.time.LocalDateTime;\n\n/**\n * 集成事件拦截器\n *\n * @author binking338\n * @date 2024/9/4\n */\n@Service\npublic class MyIntegrationEventInterceptor implements IntegrationEventInterceptor {\n @Override\n public void onNotify(Object eventPayload, LocalDateTime schedule) {\n\n }\n\n @Override\n public void prePersist(EventRecord event) {\n\n }\n\n @Override\n public void postPersist(EventRecord event) {\n\n }\n\n @Override\n public void preRelease(EventRecord event) {\n\n }\n\n @Override\n public void postRelease(EventRecord event) {\n\n }\n\n @Override\n public void onException(Throwable throwable, EventRecord event) {\n\n }\n}\n" + "data": "package ${basePackage}.adapter.application._share.configure;\n\nimport org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor;\nimport org.netcorepal.cap4j.ddd.domain.event.EventRecord;\nimport org.springframework.stereotype.Service;\n\nimport java.time.LocalDateTime;\n\n/**\n * 集成事件拦截器\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\npublic class MyIntegrationEventInterceptor implements IntegrationEventInterceptor {\n @Override\n public void onNotify(Object eventPayload, LocalDateTime schedule) {\n\n }\n\n @Override\n public void prePersist(EventRecord event) {\n\n }\n\n @Override\n public void postPersist(EventRecord event) {\n\n }\n\n @Override\n public void preRelease(EventRecord event) {\n\n }\n\n @Override\n public void postRelease(EventRecord event) {\n\n }\n\n @Override\n public void onException(Throwable throwable, EventRecord event) {\n\n }\n}\n" } ] } @@ -105,7 +293,19 @@ }, { "type": "dir", - "name": "clients" + "name": "distributed", + "children": [ + { + "type": "dir", + "name": "clients", + "tag": "client_handler" + } + ] + }, + { + "type": "dir", + "name": "queries", + "tag": "query_handler" } ] }, @@ -125,13 +325,13 @@ "type": "file", "name": "MyDomainEventInterceptor.java", "format": "raw", - "data": "package ${basePackage}.adapter.domain._share.configure;\n\nimport org.netcorepal.cap4j.ddd.domain.event.DomainEventInterceptor;\nimport org.netcorepal.cap4j.ddd.domain.event.EventRecord;\nimport org.springframework.stereotype.Service;\n\nimport java.time.LocalDateTime;\n\n/**\n * 领域事件拦截器\n *\n * @author binking338\n * @date 2024/9/4\n */\n@Service\npublic class MyDomainEventInterceptor implements DomainEventInterceptor {\n @Override\n public void onAttach(Object eventPayload, Object entity, LocalDateTime schedule) {\n\n }\n\n @Override\n public void onDetach(Object eventPayload, Object entity) {\n\n }\n\n @Override\n public void prePersist(EventRecord event) {\n\n }\n\n @Override\n public void postPersist(EventRecord event) {\n\n }\n\n @Override\n public void preRelease(EventRecord event) {\n\n }\n\n @Override\n public void postRelease(EventRecord event) {\n\n }\n\n @Override\n public void onException(Throwable throwable, EventRecord event) {\n\n }\n}\n" + "data": "package ${basePackage}.adapter.domain._share.configure;\n\nimport org.netcorepal.cap4j.ddd.domain.event.DomainEventInterceptor;\nimport org.netcorepal.cap4j.ddd.domain.event.EventRecord;\nimport org.springframework.stereotype.Service;\n\nimport java.time.LocalDateTime;\n\n/**\n * 领域事件拦截器\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\npublic class MyDomainEventInterceptor implements DomainEventInterceptor {\n @Override\n public void onAttach(Object eventPayload, Object entity, LocalDateTime schedule) {\n\n }\n\n @Override\n public void onDetach(Object eventPayload, Object entity) {\n\n }\n\n @Override\n public void prePersist(EventRecord event) {\n\n }\n\n @Override\n public void postPersist(EventRecord event) {\n\n }\n\n @Override\n public void preRelease(EventRecord event) {\n\n }\n\n @Override\n public void postRelease(EventRecord event) {\n\n }\n\n @Override\n public void onException(Throwable throwable, EventRecord event) {\n\n }\n}\n" }, { "type": "file", "name": "MyEventMessageInterceptor.java", "format": "raw", - "data": "package ${basePackage}.adapter.domain._share.configure;\n\nimport org.netcorepal.cap4j.ddd.domain.event.EventMessageInterceptor;\nimport org.springframework.messaging.Message;\nimport org.springframework.stereotype.Service;\n\n/**\n * 领域事件消息拦截器\n *\n * @author binking338\n */\n@Service\npublic class MyEventMessageInterceptor implements EventMessageInterceptor {\n\n @Override\n public void initPublish(Message message) {\n\n }\n\n @Override\n public void prePublish(Message message) {\n\n }\n\n @Override\n public void postPublish(Message message) {\n\n }\n\n @Override\n public void preSubscribe(Message message) {\n\n }\n\n @Override\n public void postSubscribe(Message message) {\n\n }\n}\n" + "data": "package ${basePackage}.adapter.domain._share.configure;\n\nimport org.netcorepal.cap4j.ddd.domain.event.EventMessageInterceptor;\nimport org.springframework.messaging.Message;\nimport org.springframework.stereotype.Service;\n\n/**\n * 领域事件消息拦截器\n *\n * @author cap4j-ddd-codegen\n */\n@Service\npublic class MyEventMessageInterceptor implements EventMessageInterceptor {\n\n @Override\n public void initPublish(Message message) {\n\n }\n\n @Override\n public void prePublish(Message message) {\n\n }\n\n @Override\n public void postPublish(Message message) {\n\n }\n\n @Override\n public void preSubscribe(Message message) {\n\n }\n\n @Override\n public void postSubscribe(Message message) {\n\n }\n}\n" } ] } @@ -159,7 +359,7 @@ "type": "file", "name": "NamedParameterJdbcTemplateDao.java", "format": "raw", - "data": "package ${basePackage}.adapter.infra.jdbc;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeanWrapper;\nimport org.springframework.beans.PropertyAccessorFactory;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.GenericConverter;\nimport org.springframework.core.convert.support.DefaultConversionService;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;\nimport org.springframework.stereotype.Service;\n\nimport javax.persistence.AttributeConverter;\nimport javax.persistence.Convert;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\n/**\n * @author binking338\n */\n@Slf4j\n@Service\npublic class NamedParameterJdbcTemplateDao {\n private final JdbcTemplate jdbcTemplate;\n private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;\n\n public NamedParameterJdbcTemplateDao(JdbcTemplate jdbcTemplate) {\n this.jdbcTemplate = jdbcTemplate;\n this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);\n }\n\n /**\n * 查询一个实体,数据异常返回0或多条记录将会抛出异常\n *\n * @param entityClass\n * @param sql\n * @param paramBeans\n * @param \n * @return\n */\n public E queryOne(Class entityClass, String sql, Object... paramBeans) {\n Map params = resolveParamMap(paramBeans);\n E result = null;\n if (entityClass.isEnum()) {\n Integer val = this.namedParameterJdbcTemplate.queryForObject(sql, params, Integer.class);\n result = (E) EnumConvertUtil.getEnumFromCode(entityClass, val);\n } else if (isPrimitiveType(entityClass)) {\n result = this.namedParameterJdbcTemplate.queryForObject(sql, params, entityClass);\n } else {\n result = this.namedParameterJdbcTemplate.queryForObject(sql, params, generateRowMapper(entityClass));\n }\n return result;\n }\n\n /**\n * 查询第一条实体记录\n *\n * @param entityClass\n * @param sql\n * @param paramBeans\n * @param \n * @return\n */\n public Optional queryFirst(Class entityClass, String sql, Object... paramBeans) {\n Map params = resolveParamMap(paramBeans);\n Pattern limitPattern = Pattern.compile(\"\\\\s+LIMIT\\\\s+\", Pattern.CASE_INSENSITIVE);\n Matcher matcher = limitPattern.matcher(sql);\n if (!matcher.find()) {\n if (sql.trim().endsWith(\";\")) {\n sql = sql.replaceFirst(\";\\\\s*$\", \" LIMIT 1;\");\n } else {\n sql += \" LIMIT 1;\";\n }\n }\n List result = null;\n if (entityClass.isEnum()) {\n result = this.namedParameterJdbcTemplate.queryForList(sql, params, Integer.class)\n .stream().map(i -> (E) EnumConvertUtil.getEnumFromCode(entityClass, i)).collect(Collectors.toList());\n } else if (isPrimitiveType(entityClass)) {\n result = this.namedParameterJdbcTemplate.queryForList(sql, params, entityClass);\n } else {\n result = this.namedParameterJdbcTemplate.query(sql, params, generateRowMapper(entityClass));\n }\n return result.stream().findFirst();\n }\n\n /**\n * 查询实体列表\n *\n * @param entityClass\n * @param sql\n * @param paramBeans\n * @param \n * @return\n */\n public List queryList(Class entityClass, String sql, Object... paramBeans) {\n Map params = resolveParamMap(paramBeans);\n List result = null;\n if (entityClass.isEnum()) {\n result = this.namedParameterJdbcTemplate.queryForList(sql, params, Integer.class)\n .stream().map(i -> (E) EnumConvertUtil.getEnumFromCode(entityClass, i)).collect(Collectors.toList());\n } else if (isPrimitiveType(entityClass)) {\n result = this.namedParameterJdbcTemplate.queryForList(sql, params, entityClass);\n } else {\n result = this.namedParameterJdbcTemplate.query(sql, params, generateRowMapper(entityClass));\n }\n return result;\n }\n\n /**\n * 解析参数\n *\n * @param paramBeans\n * @return\n */\n private Map resolveParamMap(Object... paramBeans) {\n HashMap params = new HashMap<>();\n for (Object paramBean : paramBeans) {\n convertToPropertiesMap(params, paramBean);\n }\n return params;\n }\n\n /**\n * 参数对象转Map\n *\n * @param resultMap\n * @param object\n * @param \n * @return\n */\n private static Map convertToPropertiesMap(Map resultMap, T object) {\n resultMap = resultMap == null\n ? new HashMap<>()\n : resultMap;\n\n if (object == null) {\n return resultMap;\n }\n if ((Map.class).isAssignableFrom(object.getClass())) {\n resultMap.putAll((Map) object);\n } else {\n BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(object);\n\n for (Field propertyDescriptor : wrapper.getWrappedClass().getDeclaredFields()) {\n String fieldName = propertyDescriptor.getName();\n\n try {\n Object value = wrapper.getPropertyValue(fieldName);\n value = EnumConvertUtil.processWithJpaConverter(propertyDescriptor, value);\n value = EnumConvertUtil.processWithGetEnumCode(propertyDescriptor, value);\n resultMap.put(fieldName, value);\n } catch (Exception e) {\n // 处理获取属性值时发生的异常\n log.error(\"命名参数转换异常 fieldName=\" + fieldName, e);\n }\n }\n }\n\n return resultMap;\n }\n\n /**\n * 判断是否基础类型\n *\n * @param clazz\n * @return\n */\n private static boolean isPrimitiveType(Class clazz) {\n if (Long.class.equals(clazz)\n || Integer.class.equals(clazz)\n || Short.class.equals(clazz)\n || Byte.class.equals(clazz)\n || Float.class.equals(clazz)\n || Double.class.equals(clazz)\n || Boolean.class.equals(clazz)\n || String.class.equals(clazz)\n || BigDecimal.class.equals(clazz)\n || Date.class.equals(clazz)\n || LocalDateTime.class.equals(clazz)\n || LocalDate.class.equals(clazz)\n || LocalTime.class.equals(clazz)\n ) {\n return true;\n }\n return false;\n }\n\n\n private static Map beanPropertyRowMapperMap = new HashMap<>();\n\n /**\n * 生成支持JpaConvert注解转化的RowMapper\n *\n * @param clazz\n * @param \n * @return\n */\n private static BeanPropertyRowMapper generateRowMapper(Class clazz) {\n if (!beanPropertyRowMapperMap.containsKey(clazz)) {\n DefaultConversionService conversionService = new DefaultConversionService();\n BeanPropertyRowMapper beanPropertyRowMapper = BeanPropertyRowMapper.newInstance(clazz, conversionService);\n for (Field propertyDescriptor : clazz.getDeclaredFields()) {\n Convert convert = propertyDescriptor.getAnnotation(Convert.class);\n if (convert != null && !convert.disableConversion()) {\n try {\n AttributeConverter converter = (AttributeConverter) convert.converter().newInstance();\n conversionService.addConverter(new JpaNumber2EnumConverter(Integer.class, propertyDescriptor.getType(), converter));\n conversionService.addConverter(new JpaNumber2EnumConverter(Long.class, propertyDescriptor.getType(), converter));\n conversionService.addConverter(new JpaNumber2EnumConverter(Short.class, propertyDescriptor.getType(), converter));\n } catch (InstantiationException e) {\n throw new RuntimeException(e);\n } catch (IllegalAccessException e) {\n throw new RuntimeException(e);\n }\n } else if (propertyDescriptor.getType().isEnum()) {\n conversionService.addConverter(new GenericNumber2EnumConverter(Integer.class, propertyDescriptor.getType()));\n conversionService.addConverter(new GenericNumber2EnumConverter(Long.class, propertyDescriptor.getType()));\n conversionService.addConverter(new GenericNumber2EnumConverter(Short.class, propertyDescriptor.getType()));\n }\n }\n beanPropertyRowMapperMap.put(clazz, beanPropertyRowMapper);\n }\n return beanPropertyRowMapperMap.get(clazz);\n }\n\n /**\n * 枚举转化工具\n */\n private static class EnumConvertUtil {\n private static final String ENUM_PERSIST_FIELD_METHOD = \"getCode\";\n\n /**\n * 反射读取枚举数字编码\n *\n * @param e\n * @return\n */\n private static Object getEnumCode(Object e) {\n Object code = null;\n for (String fm : ENUM_PERSIST_FIELD_METHOD.split(\",\")) {\n try {\n Method m = e.getClass().getMethod(fm);\n if (m != null) {\n code = m.invoke(e);\n break;\n }\n } catch (IllegalAccessException ex) {\n throw new RuntimeException(ex);\n } catch (InvocationTargetException ex) {\n throw new RuntimeException(ex);\n } catch (NoSuchMethodException ex) {\n throw new RuntimeException(ex);\n }\n }\n return code;\n }\n\n private static final Map> CODE_ENUM_MAP_CACHE = new HashMap<>();\n\n /**\n * 数字编码转换成指定枚举类型\n *\n * @param enumClass\n * @param val\n * @return\n */\n private static Object getEnumFromCode(Class enumClass, Object val) {\n if (!CODE_ENUM_MAP_CACHE.containsKey(enumClass)) {\n Map codeEnumMap = Arrays.stream(enumClass.getEnumConstants())\n .collect(Collectors.toMap(e -> getEnumCode(e), e -> e));\n CODE_ENUM_MAP_CACHE.put(enumClass, codeEnumMap);\n }\n return CODE_ENUM_MAP_CACHE.get(enumClass).get(val);\n }\n\n /**\n * 尝试JpaConvert注解转换枚举数字编码\n *\n * @param propertyDescriptor\n * @param value\n * @return\n */\n private static Object processWithJpaConverter(Field propertyDescriptor, Object value) {\n Convert convert = propertyDescriptor.getAnnotation(Convert.class);\n if (convert != null && !convert.disableConversion()) {\n try {\n AttributeConverter converter = null;\n converter = (AttributeConverter) convert.converter().newInstance();\n return converter.convertToDatabaseColumn(value);\n } catch (InstantiationException e) {\n throw new RuntimeException(e);\n } catch (IllegalAccessException e) {\n throw new RuntimeException(e);\n }\n }\n return value;\n }\n\n /**\n * 尝试反射获取枚举数字编码\n *\n * @param propertyDescriptor\n * @param value\n * @return\n */\n private static Object processWithGetEnumCode(Field propertyDescriptor, Object value) {\n if (value != null && value.getClass().isEnum()) {\n return getEnumCode(value);\n }\n return value;\n }\n }\n\n /**\n * 基于JPA Convert注解的GenericConverter\n */\n private static class JpaNumber2EnumConverter implements GenericConverter {\n Set set = new HashSet<>();\n AttributeConverter attributeConverter;\n\n public JpaNumber2EnumConverter(Class sourceType, Class targetType, AttributeConverter converter) {\n set.add(new ConvertiblePair(sourceType, targetType));\n this.attributeConverter = converter;\n }\n\n @Override\n public Set getConvertibleTypes() {\n return set;\n }\n\n @Override\n public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n return attributeConverter.convertToEntityAttribute(source);\n }\n }\n\n /**\n * 基于反射的GenericConverter\n */\n private static class GenericNumber2EnumConverter implements GenericConverter {\n\n Set set = new HashSet<>();\n\n public GenericNumber2EnumConverter(Class sourceType, Class targetType) {\n set.add(new ConvertiblePair(sourceType, targetType));\n }\n\n @Override\n public Set getConvertibleTypes() {\n return set;\n }\n\n @Override\n public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n return EnumConvertUtil.getEnumFromCode(targetType.getType(), source);\n }\n }\n\n}\n" + "data": "package ${basePackage}.adapter.infra.jdbc;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeanWrapper;\nimport org.springframework.beans.PropertyAccessorFactory;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.GenericConverter;\nimport org.springframework.core.convert.support.DefaultConversionService;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;\nimport org.springframework.stereotype.Service;\n\nimport javax.persistence.AttributeConverter;\nimport javax.persistence.Convert;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\n/**\n * @author cap4j-ddd-codegen\n */\n@Slf4j\n@Service\npublic class NamedParameterJdbcTemplateDao {\n private final JdbcTemplate jdbcTemplate;\n private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;\n\n public NamedParameterJdbcTemplateDao(JdbcTemplate jdbcTemplate) {\n this.jdbcTemplate = jdbcTemplate;\n this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);\n }\n\n /**\n * 查询一个实体,数据异常返回0或多条记录将会抛出异常\n *\n * @param entityClass\n * @param sql\n * @param paramBeans\n * @param \n * @return\n */\n public E queryOne(Class entityClass, String sql, Object... paramBeans) {\n Map params = resolveParamMap(paramBeans);\n E result = null;\n if (entityClass.isEnum()) {\n Integer val = this.namedParameterJdbcTemplate.queryForObject(sql, params, Integer.class);\n result = (E) EnumConvertUtil.getEnumFromCode(entityClass, val);\n } else if (isPrimitiveType(entityClass)) {\n result = this.namedParameterJdbcTemplate.queryForObject(sql, params, entityClass);\n } else {\n result = this.namedParameterJdbcTemplate.queryForObject(sql, params, generateRowMapper(entityClass));\n }\n return result;\n }\n\n /**\n * 查询第一条实体记录\n *\n * @param entityClass\n * @param sql\n * @param paramBeans\n * @param \n * @return\n */\n public Optional queryFirst(Class entityClass, String sql, Object... paramBeans) {\n Map params = resolveParamMap(paramBeans);\n Pattern limitPattern = Pattern.compile(\"\\\\s+LIMIT\\\\s+\", Pattern.CASE_INSENSITIVE);\n Matcher matcher = limitPattern.matcher(sql);\n if (!matcher.find()) {\n if (sql.trim().endsWith(\";\")) {\n sql = sql.replaceFirst(\";\\\\s*$\", \" LIMIT 1;\");\n } else {\n sql += \" LIMIT 1;\";\n }\n }\n List result = null;\n if (entityClass.isEnum()) {\n result = this.namedParameterJdbcTemplate.queryForList(sql, params, Integer.class)\n .stream().map(i -> (E) EnumConvertUtil.getEnumFromCode(entityClass, i)).collect(Collectors.toList());\n } else if (isPrimitiveType(entityClass)) {\n result = this.namedParameterJdbcTemplate.queryForList(sql, params, entityClass);\n } else {\n result = this.namedParameterJdbcTemplate.query(sql, params, generateRowMapper(entityClass));\n }\n return result.stream().findFirst();\n }\n\n /**\n * 查询实体列表\n *\n * @param entityClass\n * @param sql\n * @param paramBeans\n * @param \n * @return\n */\n public List queryList(Class entityClass, String sql, Object... paramBeans) {\n Map params = resolveParamMap(paramBeans);\n List result = null;\n if (entityClass.isEnum()) {\n result = this.namedParameterJdbcTemplate.queryForList(sql, params, Integer.class)\n .stream().map(i -> (E) EnumConvertUtil.getEnumFromCode(entityClass, i)).collect(Collectors.toList());\n } else if (isPrimitiveType(entityClass)) {\n result = this.namedParameterJdbcTemplate.queryForList(sql, params, entityClass);\n } else {\n result = this.namedParameterJdbcTemplate.query(sql, params, generateRowMapper(entityClass));\n }\n return result;\n }\n\n /**\n * 解析参数\n *\n * @param paramBeans\n * @return\n */\n private Map resolveParamMap(Object... paramBeans) {\n HashMap params = new HashMap<>();\n for (Object paramBean : paramBeans) {\n convertToPropertiesMap(params, paramBean);\n }\n return params;\n }\n\n /**\n * 参数对象转Map\n *\n * @param resultMap\n * @param object\n * @param \n * @return\n */\n private static Map convertToPropertiesMap(Map resultMap, T object) {\n resultMap = resultMap == null\n ? new HashMap<>()\n : resultMap;\n\n if (object == null) {\n return resultMap;\n }\n if ((Map.class).isAssignableFrom(object.getClass())) {\n resultMap.putAll((Map) object);\n } else {\n BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(object);\n\n for (Field propertyDescriptor : wrapper.getWrappedClass().getDeclaredFields()) {\n String fieldName = propertyDescriptor.getName();\n\n try {\n Object value = wrapper.getPropertyValue(fieldName);\n value = EnumConvertUtil.processWithJpaConverter(propertyDescriptor, value);\n value = EnumConvertUtil.processWithGetEnumCode(propertyDescriptor, value);\n resultMap.put(fieldName, value);\n } catch (Exception e) {\n // 处理获取属性值时发生的异常\n log.error(\"命名参数转换异常 fieldName=\" + fieldName, e);\n }\n }\n }\n\n return resultMap;\n }\n\n /**\n * 判断是否基础类型\n *\n * @param clazz\n * @return\n */\n private static boolean isPrimitiveType(Class clazz) {\n if (Long.class.equals(clazz)\n || Integer.class.equals(clazz)\n || Short.class.equals(clazz)\n || Byte.class.equals(clazz)\n || Float.class.equals(clazz)\n || Double.class.equals(clazz)\n || Boolean.class.equals(clazz)\n || String.class.equals(clazz)\n || BigDecimal.class.equals(clazz)\n || Date.class.equals(clazz)\n || LocalDateTime.class.equals(clazz)\n || LocalDate.class.equals(clazz)\n || LocalTime.class.equals(clazz)\n ) {\n return true;\n }\n return false;\n }\n\n\n private static Map beanPropertyRowMapperMap = new HashMap<>();\n\n /**\n * 生成支持JpaConvert注解转化的RowMapper\n *\n * @param clazz\n * @param \n * @return\n */\n private static BeanPropertyRowMapper generateRowMapper(Class clazz) {\n if (!beanPropertyRowMapperMap.containsKey(clazz)) {\n DefaultConversionService conversionService = new DefaultConversionService();\n BeanPropertyRowMapper beanPropertyRowMapper = BeanPropertyRowMapper.newInstance(clazz, conversionService);\n for (Field propertyDescriptor : clazz.getDeclaredFields()) {\n Convert convert = propertyDescriptor.getAnnotation(Convert.class);\n if (convert != null && !convert.disableConversion()) {\n try {\n AttributeConverter converter = (AttributeConverter) convert.converter().newInstance();\n conversionService.addConverter(new JpaNumber2EnumConverter(Integer.class, propertyDescriptor.getType(), converter));\n conversionService.addConverter(new JpaNumber2EnumConverter(Long.class, propertyDescriptor.getType(), converter));\n conversionService.addConverter(new JpaNumber2EnumConverter(Short.class, propertyDescriptor.getType(), converter));\n } catch (InstantiationException e) {\n throw new RuntimeException(e);\n } catch (IllegalAccessException e) {\n throw new RuntimeException(e);\n }\n } else if (propertyDescriptor.getType().isEnum()) {\n conversionService.addConverter(new GenericNumber2EnumConverter(Integer.class, propertyDescriptor.getType()));\n conversionService.addConverter(new GenericNumber2EnumConverter(Long.class, propertyDescriptor.getType()));\n conversionService.addConverter(new GenericNumber2EnumConverter(Short.class, propertyDescriptor.getType()));\n }\n }\n beanPropertyRowMapperMap.put(clazz, beanPropertyRowMapper);\n }\n return beanPropertyRowMapperMap.get(clazz);\n }\n\n /**\n * 枚举转化工具\n */\n private static class EnumConvertUtil {\n private static final String ENUM_PERSIST_FIELD_METHOD = \"getCode\";\n\n /**\n * 反射读取枚举数字编码\n *\n * @param e\n * @return\n */\n private static Object getEnumCode(Object e) {\n Object code = null;\n for (String fm : ENUM_PERSIST_FIELD_METHOD.split(\",\")) {\n try {\n Method m = e.getClass().getMethod(fm);\n if (m != null) {\n code = m.invoke(e);\n break;\n }\n } catch (IllegalAccessException ex) {\n throw new RuntimeException(ex);\n } catch (InvocationTargetException ex) {\n throw new RuntimeException(ex);\n } catch (NoSuchMethodException ex) {\n throw new RuntimeException(ex);\n }\n }\n return code;\n }\n\n private static final Map> CODE_ENUM_MAP_CACHE = new HashMap<>();\n\n /**\n * 数字编码转换成指定枚举类型\n *\n * @param enumClass\n * @param val\n * @return\n */\n private static Object getEnumFromCode(Class enumClass, Object val) {\n if (!CODE_ENUM_MAP_CACHE.containsKey(enumClass)) {\n Map codeEnumMap = Arrays.stream(enumClass.getEnumConstants())\n .collect(Collectors.toMap(e -> getEnumCode(e), e -> e));\n CODE_ENUM_MAP_CACHE.put(enumClass, codeEnumMap);\n }\n return CODE_ENUM_MAP_CACHE.get(enumClass).get(val);\n }\n\n /**\n * 尝试JpaConvert注解转换枚举数字编码\n *\n * @param propertyDescriptor\n * @param value\n * @return\n */\n private static Object processWithJpaConverter(Field propertyDescriptor, Object value) {\n Convert convert = propertyDescriptor.getAnnotation(Convert.class);\n if (convert != null && !convert.disableConversion()) {\n try {\n AttributeConverter converter = null;\n converter = (AttributeConverter) convert.converter().newInstance();\n return converter.convertToDatabaseColumn(value);\n } catch (InstantiationException e) {\n throw new RuntimeException(e);\n } catch (IllegalAccessException e) {\n throw new RuntimeException(e);\n }\n }\n return value;\n }\n\n /**\n * 尝试反射获取枚举数字编码\n *\n * @param propertyDescriptor\n * @param value\n * @return\n */\n private static Object processWithGetEnumCode(Field propertyDescriptor, Object value) {\n if (value != null && value.getClass().isEnum()) {\n return getEnumCode(value);\n }\n return value;\n }\n }\n\n /**\n * 基于JPA Convert注解的GenericConverter\n */\n private static class JpaNumber2EnumConverter implements GenericConverter {\n Set set = new HashSet<>();\n AttributeConverter attributeConverter;\n\n public JpaNumber2EnumConverter(Class sourceType, Class targetType, AttributeConverter converter) {\n set.add(new ConvertiblePair(sourceType, targetType));\n this.attributeConverter = converter;\n }\n\n @Override\n public Set getConvertibleTypes() {\n return set;\n }\n\n @Override\n public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n return attributeConverter.convertToEntityAttribute(source);\n }\n }\n\n /**\n * 基于反射的GenericConverter\n */\n private static class GenericNumber2EnumConverter implements GenericConverter {\n\n Set set = new HashSet<>();\n\n public GenericNumber2EnumConverter(Class sourceType, Class targetType) {\n set.add(new ConvertiblePair(sourceType, targetType));\n }\n\n @Override\n public Set getConvertibleTypes() {\n return set;\n }\n\n @Override\n public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n return EnumConvertUtil.getEnumFromCode(targetType.getType(), source);\n }\n }\n\n}\n" } ] }, @@ -175,7 +375,7 @@ "type": "file", "name": "MyEnumTypeHandler.java", "format": "raw", - "data": "package ${basePackage}.adapter.infra.mybatis._share;\n\nimport org.apache.ibatis.type.BaseTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * Mybatis枚举转化\n *\n * @author binking338\n */\npublic class MyEnumTypeHandler > extends BaseTypeHandler {\n private static final String ENUM_PERSIST_FIELD_METHOD = \"getCode\";\n private Integer getEnumCode(E e){\n Integer code = 0;\n for (String fm : ENUM_PERSIST_FIELD_METHOD.split(\",\")) {\n try {\n Method m = e.getClass().getMethod(fm);\n if (m != null) {\n code = (Integer) m.invoke(e);\n break;\n }\n } catch (IllegalAccessException ex) {\n throw new RuntimeException(ex);\n } catch (InvocationTargetException ex) {\n throw new RuntimeException(ex);\n } catch (NoSuchMethodException ex) {\n throw new RuntimeException(ex);\n }\n }\n return code;\n }\n\n private Map enums;\n\n public MyEnumTypeHandler(Class type) {\n if (type == null) {\n throw new IllegalArgumentException(\"Type argument cannot be null\");\n } else {\n this.enums = Arrays.stream(type.getEnumConstants()).collect(Collectors.toMap(e -> getEnumCode((E) e), e -> (E) e));\n if (this.enums == null) {\n throw new IllegalArgumentException(type.getSimpleName() + \" does not represent an enum type.\");\n }\n }\n }\n\n @Override\n public void setNonNullParameter(PreparedStatement preparedStatement, int i, E e, JdbcType jdbcType) throws SQLException {\n int val = getEnumCode(e);\n preparedStatement.setInt(i, val);\n }\n\n @Override\n public E getNullableResult(ResultSet resultSet, String s) throws SQLException {\n int ordinal = resultSet.getInt(s);\n return this.enums.get(ordinal);\n }\n\n @Override\n public E getNullableResult(ResultSet resultSet, int i) throws SQLException {\n int ordinal = resultSet.getInt(i);\n return this.enums.get(ordinal);\n }\n\n @Override\n public E getNullableResult(CallableStatement callableStatement, int i) throws SQLException {\n int ordinal = callableStatement.getInt(i);\n return this.enums.get(ordinal);\n }\n}\n" + "data": "package ${basePackage}.adapter.infra.mybatis._share;\n\nimport org.apache.ibatis.type.BaseTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * Mybatis枚举转化\n *\n * @author cap4j-ddd-codegen\n */\npublic class MyEnumTypeHandler > extends BaseTypeHandler {\n private static final String ENUM_PERSIST_FIELD_METHOD = \"getCode\";\n private Integer getEnumCode(E e){\n Integer code = 0;\n for (String fm : ENUM_PERSIST_FIELD_METHOD.split(\",\")) {\n try {\n Method m = e.getClass().getMethod(fm);\n if (m != null) {\n code = (Integer) m.invoke(e);\n break;\n }\n } catch (IllegalAccessException ex) {\n throw new RuntimeException(ex);\n } catch (InvocationTargetException ex) {\n throw new RuntimeException(ex);\n } catch (NoSuchMethodException ex) {\n throw new RuntimeException(ex);\n }\n }\n return code;\n }\n\n private Map enums;\n\n public MyEnumTypeHandler(Class type) {\n if (type == null) {\n throw new IllegalArgumentException(\"Type argument cannot be null\");\n } else {\n this.enums = Arrays.stream(type.getEnumConstants()).collect(Collectors.toMap(e -> getEnumCode((E) e), e -> (E) e));\n if (this.enums == null) {\n throw new IllegalArgumentException(type.getSimpleName() + \" does not represent an enum type.\");\n }\n }\n }\n\n @Override\n public void setNonNullParameter(PreparedStatement preparedStatement, int i, E e, JdbcType jdbcType) throws SQLException {\n int val = getEnumCode(e);\n preparedStatement.setInt(i, val);\n }\n\n @Override\n public E getNullableResult(ResultSet resultSet, String s) throws SQLException {\n int ordinal = resultSet.getInt(s);\n return this.enums.get(ordinal);\n }\n\n @Override\n public E getNullableResult(ResultSet resultSet, int i) throws SQLException {\n int ordinal = resultSet.getInt(i);\n return this.enums.get(ordinal);\n }\n\n @Override\n public E getNullableResult(CallableStatement callableStatement, int i) throws SQLException {\n int ordinal = callableStatement.getInt(i);\n return this.enums.get(ordinal);\n }\n}\n" } ] }, @@ -207,19 +407,19 @@ "type": "file", "name": "CommonExceptionHandler.java", "format": "raw", - "data": "package ${basePackage}.adapter.portal.api._share.configure;\n\nimport ${basePackage}._share.CodeEnum;\nimport ${basePackage}.adapter.portal.api._share.ResponseData;\nimport ${basePackage}._share.exception.ErrorException;\nimport ${basePackage}._share.exception.KnownException;\nimport ${basePackage}._share.exception.WarnException;\nimport com.alibaba.fastjson.JSON;\nimport com.fasterxml.jackson.annotation.JsonPropertyOrder;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.catalina.connector.ClientAbortException;\nimport org.slf4j.event.Level;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.validation.BindException;\nimport org.springframework.web.HttpRequestMethodNotSupportedException;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.bind.MissingRequestHeaderException;\nimport org.springframework.web.bind.MissingServletRequestParameterException;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\nimport org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;\nimport org.springframework.web.servlet.NoHandlerFoundException;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 公共的全局异常处理器\n *\n * @author binking338\n */\n@Slf4j\n@RestControllerAdvice\n@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)\npublic class CommonExceptionHandler {\n\n public String getRequestInfo(HttpServletRequest request) {\n RequestMsg requestMsg = new RequestMsg();\n Map param = request.getParameterMap();\n String url = request.getRequestURI();\n requestMsg.setParams(param);\n requestMsg.setUrl(url);\n return JSON.toJSONString(requestMsg);\n }\n\n @Data\n @JsonPropertyOrder({\"url\", \"params\"})\n public static class RequestMsg {\n private String url;\n private Map params;\n }\n\n @ExceptionHandler(NoHandlerFoundException.class)\n @ResponseStatus(HttpStatus.NOT_FOUND)\n public ResponseData handleError(NoHandlerFoundException e) {\n log.warn(String.format(\"404没找到请求:%s\", e.getMessage()), e);\n return ResponseData.fail(CodeEnum.NOT_FOUND);\n }\n\n @ExceptionHandler(HttpRequestMethodNotSupportedException.class)\n @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)\n public ResponseData handleError(HttpRequestMethodNotSupportedException e) {\n log.warn(String.format(\"不支持当前请求方法:%s\", e.getMessage()), e);\n return ResponseData.fail(CodeEnum.METHOD_NOT_SUPPORTED);\n }\n\n /**\n * 参数校验异常\n *\n * @param e exception\n * @return ResponseData\n */\n @ExceptionHandler(ConstraintViolationException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(HttpServletRequest request, ConstraintViolationException e) {\n String requestInfo = getRequestInfo(request);\n log.warn(String.format(\"参数校验异常:%s request:%s \", e.getMessage(), requestInfo), e);\n Set> constraintViolations = e.getConstraintViolations();\n for (ConstraintViolation constraintViolation : constraintViolations) {\n String message = constraintViolation.getMessage();\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), message);\n }\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), e.getLocalizedMessage());\n }\n\n @ExceptionHandler(MissingServletRequestParameterException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(MissingServletRequestParameterException e) {\n log.warn(String.format(\"缺少请求参数:%s\", e.getMessage()), e);\n String message = String.format(\"缺少必要的请求参数: %s\", e.getParameterName());\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), message);\n }\n\n @ExceptionHandler(MethodArgumentTypeMismatchException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(MethodArgumentTypeMismatchException e) {\n log.warn(String.format(\"请求参数格式错误:%s\", e.getMessage()), e);\n String message = String.format(\"请求参数格式错误: %s\", e.getName());\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), message);\n }\n\n @ExceptionHandler(MethodArgumentNotValidException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(MethodArgumentNotValidException e) {\n log.warn(String.format(\"参数验证失败:%s\", e.getMessage()), e);\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), \"参数[\" + e.getBindingResult().getFieldError().getField() + \"]不正确:\" + e.getBindingResult().getFieldError().getDefaultMessage());\n }\n\n @ExceptionHandler(BindException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(BindException e) {\n log.warn(String.format(\"参数绑定失败:%s\", e.getMessage()), e);\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), \"参数不正确\");\n }\n\n @ExceptionHandler(HttpMessageNotReadableException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(HttpServletRequest request, HttpMessageNotReadableException e) {\n log.error(String.format(\"消息不能读取:%s request:%s\", e.getMessage(), getRequestInfo(request)), e);\n return ResponseData.fail(CodeEnum.MESSAGE_NOT_READABLE);\n }\n\n @ExceptionHandler(value = MissingRequestHeaderException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData headerParamException(HttpServletRequest request, MissingRequestHeaderException e) {\n log.warn(String.format(\"缺少header参数:%s request:%s\", e.getHeaderName(), getRequestInfo(request)), e);\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), \"缺少header参数\");\n }\n\n @ExceptionHandler(value = KnownException.class)\n @ResponseStatus(HttpStatus.OK)\n public ResponseData knownException(KnownException be) {\n if (Level.ERROR.toString().equalsIgnoreCase(be.getLevel())) {\n log.error(\"发生业务错误: \", be);\n } else if (Level.WARN.toString().equalsIgnoreCase(be.getLevel())) {\n log.warn(\"发生业务警告: \", be);\n } else if (log.isDebugEnabled()) {\n log.debug(\"业务失败返回: \", be);\n }\n return ResponseData.fail(be);\n }\n\n @ExceptionHandler(value = WarnException.class)\n @ResponseStatus(HttpStatus.OK)\n public ResponseData warnException(WarnException be) {\n log.warn(\"发生业务警告: \", be);\n return ResponseData.fail(be);\n }\n\n @ExceptionHandler(value = ErrorException.class)\n @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)\n public ResponseData errorException(ErrorException be) {\n log.error(\"发生业务错误: \", be);\n return ResponseData.fail(be);\n }\n\n @ExceptionHandler(value = ClientAbortException.class)\n @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)\n public ResponseData clientAbortException(ClientAbortException ce) {\n log.warn(\"客户端中断异常: \", ce);\n return ResponseData.fail(\"断开的连接:Broken pipe\");\n }\n\n @ExceptionHandler(Throwable.class)\n @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)\n public ResponseData handleError(HttpServletRequest request, Throwable e) {\n KnownException ke = getKnownException(e);\n if (ke != null) {\n return knownException(ke);\n }\n log.error(String.format(\"发生未知异常:%s request:%s\", e.getMessage(), getRequestInfo(request)), e);\n return ResponseData.fail(CodeEnum.ERROR);\n }\n\n private KnownException getKnownException(Throwable e) {\n if (e instanceof KnownException) {\n return (KnownException) e;\n }\n if (e.getCause() != null && e.getCause() != e) {\n return getKnownException(e.getCause());\n }\n return null;\n }\n}" + "data": "package ${basePackage}.adapter.portal.api._share.configure;\n\nimport ${basePackage}._share.CodeEnum;\nimport ${basePackage}.adapter.portal.api._share.ResponseData;\nimport ${basePackage}._share.exception.ErrorException;\nimport ${basePackage}._share.exception.KnownException;\nimport ${basePackage}._share.exception.WarnException;\nimport com.alibaba.fastjson.JSON;\nimport com.fasterxml.jackson.annotation.JsonPropertyOrder;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.catalina.connector.ClientAbortException;\nimport org.slf4j.event.Level;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.validation.BindException;\nimport org.springframework.web.HttpRequestMethodNotSupportedException;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.bind.MissingRequestHeaderException;\nimport org.springframework.web.bind.MissingServletRequestParameterException;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\nimport org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;\nimport org.springframework.web.servlet.NoHandlerFoundException;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 公共的全局异常处理器\n *\n * @author cap4j-ddd-codegen\n */\n@Slf4j\n@RestControllerAdvice\n@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)\npublic class CommonExceptionHandler {\n\n public String getRequestInfo(HttpServletRequest request) {\n RequestMsg requestMsg = new RequestMsg();\n Map param = request.getParameterMap();\n String url = request.getRequestURI();\n requestMsg.setParams(param);\n requestMsg.setUrl(url);\n return JSON.toJSONString(requestMsg);\n }\n\n @Data\n @JsonPropertyOrder({\"url\", \"params\"})\n public static class RequestMsg {\n private String url;\n private Map params;\n }\n\n @ExceptionHandler(NoHandlerFoundException.class)\n @ResponseStatus(HttpStatus.NOT_FOUND)\n public ResponseData handleError(NoHandlerFoundException e) {\n log.warn(String.format(\"404没找到请求:%s\", e.getMessage()), e);\n return ResponseData.fail(CodeEnum.NOT_FOUND);\n }\n\n @ExceptionHandler(HttpRequestMethodNotSupportedException.class)\n @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)\n public ResponseData handleError(HttpRequestMethodNotSupportedException e) {\n log.warn(String.format(\"不支持当前请求方法:%s\", e.getMessage()), e);\n return ResponseData.fail(CodeEnum.METHOD_NOT_SUPPORTED);\n }\n\n /**\n * 参数校验异常\n *\n * @param e exception\n * @return ResponseData\n */\n @ExceptionHandler(ConstraintViolationException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(HttpServletRequest request, ConstraintViolationException e) {\n String requestInfo = getRequestInfo(request);\n log.warn(String.format(\"参数校验异常:%s request:%s \", e.getMessage(), requestInfo), e);\n Set> constraintViolations = e.getConstraintViolations();\n for (ConstraintViolation constraintViolation : constraintViolations) {\n String message = constraintViolation.getMessage();\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), message);\n }\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), e.getLocalizedMessage());\n }\n\n @ExceptionHandler(MissingServletRequestParameterException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(MissingServletRequestParameterException e) {\n log.warn(String.format(\"缺少请求参数:%s\", e.getMessage()), e);\n String message = String.format(\"缺少必要的请求参数: %s\", e.getParameterName());\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), message);\n }\n\n @ExceptionHandler(MethodArgumentTypeMismatchException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(MethodArgumentTypeMismatchException e) {\n log.warn(String.format(\"请求参数格式错误:%s\", e.getMessage()), e);\n String message = String.format(\"请求参数格式错误: %s\", e.getName());\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), message);\n }\n\n @ExceptionHandler(MethodArgumentNotValidException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(MethodArgumentNotValidException e) {\n log.warn(String.format(\"参数验证失败:%s\", e.getMessage()), e);\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), \"参数[\" + e.getBindingResult().getFieldError().getField() + \"]不正确:\" + e.getBindingResult().getFieldError().getDefaultMessage());\n }\n\n @ExceptionHandler(BindException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(BindException e) {\n log.warn(String.format(\"参数绑定失败:%s\", e.getMessage()), e);\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), \"参数不正确\");\n }\n\n @ExceptionHandler(HttpMessageNotReadableException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData handleError(HttpServletRequest request, HttpMessageNotReadableException e) {\n log.error(String.format(\"消息不能读取:%s request:%s\", e.getMessage(), getRequestInfo(request)), e);\n return ResponseData.fail(CodeEnum.MESSAGE_NOT_READABLE);\n }\n\n @ExceptionHandler(value = MissingRequestHeaderException.class)\n @ResponseStatus(HttpStatus.BAD_REQUEST)\n public ResponseData headerParamException(HttpServletRequest request, MissingRequestHeaderException e) {\n log.warn(String.format(\"缺少header参数:%s request:%s\", e.getHeaderName(), getRequestInfo(request)), e);\n return ResponseData.fail(CodeEnum.PARAM_INVALIDATE.getCode(), \"缺少header参数\");\n }\n\n @ExceptionHandler(value = KnownException.class)\n @ResponseStatus(HttpStatus.OK)\n public ResponseData knownException(KnownException be) {\n if (Level.ERROR.toString().equalsIgnoreCase(be.getLevel())) {\n log.error(\"发生业务错误: \", be);\n } else if (Level.WARN.toString().equalsIgnoreCase(be.getLevel())) {\n log.warn(\"发生业务警告: \", be);\n } else if (log.isDebugEnabled()) {\n log.debug(\"业务失败返回: \", be);\n }\n return ResponseData.fail(be);\n }\n\n @ExceptionHandler(value = WarnException.class)\n @ResponseStatus(HttpStatus.OK)\n public ResponseData warnException(WarnException be) {\n log.warn(\"发生业务警告: \", be);\n return ResponseData.fail(be);\n }\n\n @ExceptionHandler(value = ErrorException.class)\n @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)\n public ResponseData errorException(ErrorException be) {\n log.error(\"发生业务错误: \", be);\n return ResponseData.fail(be);\n }\n\n @ExceptionHandler(value = ClientAbortException.class)\n @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)\n public ResponseData clientAbortException(ClientAbortException ce) {\n log.warn(\"客户端中断异常: \", ce);\n return ResponseData.fail(\"断开的连接:Broken pipe\");\n }\n\n @ExceptionHandler(Throwable.class)\n @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)\n public ResponseData handleError(HttpServletRequest request, Throwable e) {\n KnownException ke = getKnownException(e);\n if (ke != null) {\n return knownException(ke);\n }\n log.error(String.format(\"发生未知异常:%s request:%s\", e.getMessage(), getRequestInfo(request)), e);\n return ResponseData.fail(CodeEnum.ERROR);\n }\n\n private KnownException getKnownException(Throwable e) {\n if (e instanceof KnownException) {\n return (KnownException) e;\n }\n if (e.getCause() != null && e.getCause() != e) {\n return getKnownException(e.getCause());\n }\n return null;\n }\n}" }, { "type": "file", "name": "MvcConfig.java", "format": "raw", - "data": "package ${basePackage}.adapter.portal.api._share.configure;\n\nimport lombok.RequiredArgsConstructor;\nimport org.netcorepal.cap4j.ddd.domain.web.ClearDomainContextInterceptor;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n/**\n * Mvc配置\n * @author binking338\n */\n@Configuration\n@RequiredArgsConstructor\npublic class MvcConfig implements WebMvcConfigurer {\n private final ClearDomainContextInterceptor clearDomainContextInterceptor;\n\n @Override\n public void addInterceptors(InterceptorRegistry registry) {\n registry.addInterceptor(clearDomainContextInterceptor).addPathPatterns(\"/**\");\n }\n}\n" + "data": "package ${basePackage}.adapter.portal.api._share.configure;\n\nimport lombok.RequiredArgsConstructor;\nimport org.netcorepal.cap4j.ddd.domain.web.ClearDomainContextInterceptor;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n/**\n * Mvc配置\n * @author cap4j-ddd-codegen\n */\n@Configuration\n@RequiredArgsConstructor\npublic class MvcConfig implements WebMvcConfigurer {\n private final ClearDomainContextInterceptor clearDomainContextInterceptor;\n\n @Override\n public void addInterceptors(InterceptorRegistry registry) {\n registry.addInterceptor(clearDomainContextInterceptor).addPathPatterns(\"/**\");\n }\n}\n" }, { "type": "file", "name": "SwaggerConfig.java", "format": "raw", - "data": "package ${basePackage}.adapter.portal.api._share.configure;\n\nimport io.swagger.v3.oas.models.OpenAPI;\nimport io.swagger.v3.oas.models.info.Info;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springdoc.core.GroupedOpenApi;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.web.context.WebServerInitializedEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n//import org.springframework.web.bind.annotation.RestController;\n//import springfox.documentation.builders.ApiInfoBuilder;\n//import springfox.documentation.builders.PathSelectors;\n//import springfox.documentation.builders.RequestHandlerSelectors;\n//import springfox.documentation.spi.DocumentationType;\n//import springfox.documentation.spring.web.plugins.Docket;\n//import springfox.documentation.swagger2.annotations.EnableSwagger2;\n\n/**\n * swagger文档配置\n *\n * @author binking338\n */\n@Configuration\n@Slf4j\n//@EnableSwagger2\npublic class SwaggerConfig implements ApplicationListener {\n @Value(\"${spring.application.name:${artifactId}}\")\n private String applicationName;\n @Value(\"${spring.application.version:${version}}\")\n private String applicationVersion;\n private String description = \"\";\n\n\n private OpenAPI openApiConfig(OpenAPI openAPI) {\n return openAPI.info(new Info()\n .title(applicationName)\n .version(applicationVersion)\n .description(description));\n }\n\n /**\n * 用于单个文档情形,文档地址是统一的 /v3/api-docs,该文档地址可以通过配置项 springdoc.api-docs.path 进行自定义配置\n * 需依赖 com.github.xiaoymin:knife4j-springdoc-ui,并取消依赖 com.github.xiaoymin:knife4j-spring-boot-starter\n * 可配置项\n * springdoc.api-docs.path=/v3/openapi\n * springdoc.packagesToScan=package1, package2\n * springdoc.pathsToMatch=/v1, /api/balance/**\n *\n * @return\n */\n @Bean\n public OpenAPI openAPI() {\n return openApiConfig(new OpenAPI());\n }\n\n /**\n * 用于多个文档情形,文档地址会加分组名称 /v3/api-docs/{group},该文档地址前缀可以通过配置项 springdoc.api-docs.path 进行自定义配置\n * knife4j ui需要分组支持\n *\n * @return\n */\n @Bean\n public GroupedOpenApi groupedOpenApi() {\n String[] paths = {\"/**\"};\n String pacakage = this.getClass().getPackage().getName().split(\".adapter\")[0]\n + \".adapter.portal.api\";\n return GroupedOpenApi.builder()\n .group(applicationName)\n .pathsToMatch(paths)\n .packagesToScan(pacakage)\n .addOperationCustomizer((operation, handlerMethod) -> operation)\n .addOpenApiCustomiser(openApi -> openApiConfig(openApi))\n .build();\n }\n\n// /**\n// * springfox 文档配置\n// * 需依赖 com.github.xiaoymin:knife4j-spring-boot-starter,并取消依赖 com.github.xiaoymin:knife4j-springdoc-ui\n// * 标记 @EnableSwagger2\n// * @return\n// */\n// @Bean\n// public Docket docket() {\n// Docket docket=new Docket(DocumentationType.SWAGGER_2)\n// .apiInfo(new ApiInfoBuilder()\n// .title(applicationName)\n// .version(applicationVersion)\n// .description(description)\n// .build())\n// //.groupName(applicationName)\n// .select()\n// .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))\n// .paths(PathSelectors.any())\n// .build();\n// return docket;\n// }\n\n @Value(\"${server.port:80}\")\n private String serverPort;\n @Value(\"${server.servlet.context-path:/}\")\n private String serverServletContentPath;\n\n @Override\n public void onApplicationEvent(WebServerInitializedEvent webServerInitializedEvent) {\n log.info(\"swagger URL: http://localhost:\" + serverPort + serverServletContentPath + \"/swagger-ui/index.html\");\n log.info(\"knife4j URL: http://localhost:\" + serverPort + serverServletContentPath + \"/doc.html\");\n }\n}\n" + "data": "package ${basePackage}.adapter.portal.api._share.configure;\n\nimport io.swagger.v3.oas.models.OpenAPI;\nimport io.swagger.v3.oas.models.info.Info;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springdoc.core.GroupedOpenApi;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.web.context.WebServerInitializedEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n//import org.springframework.web.bind.annotation.RestController;\n//import springfox.documentation.builders.ApiInfoBuilder;\n//import springfox.documentation.builders.PathSelectors;\n//import springfox.documentation.builders.RequestHandlerSelectors;\n//import springfox.documentation.spi.DocumentationType;\n//import springfox.documentation.spring.web.plugins.Docket;\n//import springfox.documentation.swagger2.annotations.EnableSwagger2;\n\n/**\n * swagger文档配置\n *\n * @author cap4j-ddd-codegen\n */\n@Configuration\n@Slf4j\n//@EnableSwagger2\npublic class SwaggerConfig implements ApplicationListener {\n @Value(\"${spring.application.name:${artifactId}}\")\n private String applicationName;\n @Value(\"${spring.application.version:${version}}\")\n private String applicationVersion;\n private String description = \"\";\n\n\n private OpenAPI openApiConfig(OpenAPI openAPI) {\n return openAPI.info(new Info()\n .title(applicationName)\n .version(applicationVersion)\n .description(description));\n }\n\n /**\n * 用于单个文档情形,文档地址是统一的 /v3/api-docs,该文档地址可以通过配置项 springdoc.api-docs.path 进行自定义配置\n * 需依赖 com.github.xiaoymin:knife4j-springdoc-ui,并取消依赖 com.github.xiaoymin:knife4j-spring-boot-starter\n * 可配置项\n * springdoc.api-docs.path=/v3/openapi\n * springdoc.packagesToScan=package1, package2\n * springdoc.pathsToMatch=/v1, /api/balance/**\n *\n * @return\n */\n @Bean\n public OpenAPI openAPI() {\n return openApiConfig(new OpenAPI());\n }\n\n /**\n * 用于多个文档情形,文档地址会加分组名称 /v3/api-docs/{group},该文档地址前缀可以通过配置项 springdoc.api-docs.path 进行自定义配置\n * knife4j ui需要分组支持\n *\n * @return\n */\n @Bean\n public GroupedOpenApi groupedOpenApi() {\n String[] paths = {\"/**\"};\n String pacakage = this.getClass().getPackage().getName().split(\".adapter\")[0]\n + \".adapter.portal.api\";\n return GroupedOpenApi.builder()\n .group(applicationName)\n .pathsToMatch(paths)\n .packagesToScan(pacakage)\n .addOperationCustomizer((operation, handlerMethod) -> operation)\n .addOpenApiCustomiser(openApi -> openApiConfig(openApi))\n .build();\n }\n\n// /**\n// * springfox 文档配置\n// * 需依赖 com.github.xiaoymin:knife4j-spring-boot-starter,并取消依赖 com.github.xiaoymin:knife4j-springdoc-ui\n// * 标记 @EnableSwagger2\n// * @return\n// */\n// @Bean\n// public Docket docket() {\n// Docket docket=new Docket(DocumentationType.SWAGGER_2)\n// .apiInfo(new ApiInfoBuilder()\n// .title(applicationName)\n// .version(applicationVersion)\n// .description(description)\n// .build())\n// //.groupName(applicationName)\n// .select()\n// .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))\n// .paths(PathSelectors.any())\n// .build();\n// return docket;\n// }\n\n @Value(\"${server.port:80}\")\n private String serverPort;\n @Value(\"${server.servlet.context-path:/}\")\n private String serverServletContentPath;\n\n @Override\n public void onApplicationEvent(WebServerInitializedEvent webServerInitializedEvent) {\n log.info(\"swagger URL: http://localhost:\" + serverPort + serverServletContentPath + \"/swagger-ui/index.html\");\n log.info(\"knife4j URL: http://localhost:\" + serverPort + serverServletContentPath + \"/doc.html\");\n }\n}\n" } ] }, @@ -227,13 +427,13 @@ "type": "file", "name": "ResponseData.java", "format": "raw", - "data": "package ${basePackage}.adapter.portal.api._share;\n\nimport ${basePackage}._share.CodeEnum;\nimport ${basePackage}._share.exception.ErrorException;\nimport ${basePackage}._share.exception.KnownException;\nimport ${basePackage}._share.exception.WarnException;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.Objects;\n\n/**\n * @author binking338\n */\n@Data\n@Schema(description = \"接口响应格式\")\npublic class ResponseData {\n @Schema(description=\"状态码\")\n private Status status;\n @Schema(description=\"结果集\")\n private T data;\n @Schema(description=\"服务器时间\")\n private long timestamp;\n\n private ResponseData() {\n }\n\n private ResponseData(T data) {\n this.status = new Status(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getName());\n this.timestamp = System.currentTimeMillis();\n this.data = data;\n }\n\n private ResponseData(Integer code, String msg, T data) {\n this.status = new Status(code, msg);\n if (code == null) {\n this.status.setCode(CodeEnum.FAIL.getCode());\n }\n\n if (msg == null) {\n this.status.setMsg(\"\");\n }\n\n this.data = data;\n this.timestamp = System.currentTimeMillis();\n }\n\n public static ResponseData success(T data) {\n return new ResponseData(data);\n }\n\n public static ResponseData success(String msg, T data) {\n return new ResponseData(CodeEnum.SUCCESS.getCode(), msg, data);\n }\n\n public static ResponseData fail(String msg) {\n return new ResponseData(CodeEnum.FAIL.getCode(), msg, null);\n }\n\n public static ResponseData fail(CodeEnum codeEnum) {\n return new ResponseData(codeEnum.getCode(), codeEnum.getName(), null);\n }\n\n public static ResponseData fail(KnownException knownException) {\n return new ResponseData(knownException.getCode(), knownException.getMsg(), null);\n }\n\n public static ResponseData fail(Integer code, String msg) {\n code = code == null ? CodeEnum.FAIL.getCode() : code;\n return new ResponseData(code, msg, null);\n }\n\n public static ResponseData illegalArgument() {\n return new ResponseData(CodeEnum.PARAM_INVALIDATE.getCode(), CodeEnum.PARAM_INVALIDATE.getName(), null);\n }\n\n public static ResponseData systemError() {\n return new ResponseData(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getName(), null);\n }\n\n @JsonIgnore\n public boolean isSuccess() {\n Integer retCode = status.getCode();\n return Objects.equals(CodeEnum.SUCCESS.getCode(), retCode);\n }\n\n public void throwKnownException(){\n throw new KnownException(status.getCode(), status.getMsg());\n }\n\n public void throwWarnException(){\n throw new WarnException(status.getCode(), status.getMsg());\n }\n\n public void throwErrorException(){\n throw new ErrorException(status.getCode(), status.getMsg());\n }\n\n public void tryThrowKnownException(){\n if(!isSuccess()){\n throwKnownException();\n }\n }\n\n public void tryThrowWarnException(){\n if(!isSuccess()){\n throwWarnException();\n }\n }\n\n public void tryThrowErrorException(){\n if(!isSuccess()){\n throwErrorException();\n }\n }\n}\n\n" + "data": "package ${basePackage}.adapter.portal.api._share;\n\nimport ${basePackage}._share.CodeEnum;\nimport ${basePackage}._share.exception.ErrorException;\nimport ${basePackage}._share.exception.KnownException;\nimport ${basePackage}._share.exception.WarnException;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.util.Objects;\n\n/**\n * @author cap4j-ddd-codegen\n */\n@Data\n@Schema(description = \"接口响应格式\")\npublic class ResponseData {\n @Schema(description=\"状态码\")\n private Status status;\n @Schema(description=\"结果集\")\n private T data;\n @Schema(description=\"服务器时间\")\n private long timestamp;\n\n private ResponseData() {\n }\n\n private ResponseData(T data) {\n this.status = new Status(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getName());\n this.timestamp = System.currentTimeMillis();\n this.data = data;\n }\n\n private ResponseData(Integer code, String msg, T data) {\n this.status = new Status(code, msg);\n if (code == null) {\n this.status.setCode(CodeEnum.FAIL.getCode());\n }\n\n if (msg == null) {\n this.status.setMsg(\"\");\n }\n\n this.data = data;\n this.timestamp = System.currentTimeMillis();\n }\n\n public static ResponseData success(T data) {\n return new ResponseData(data);\n }\n\n public static ResponseData success(String msg, T data) {\n return new ResponseData(CodeEnum.SUCCESS.getCode(), msg, data);\n }\n\n public static ResponseData fail(String msg) {\n return new ResponseData(CodeEnum.FAIL.getCode(), msg, null);\n }\n\n public static ResponseData fail(CodeEnum codeEnum) {\n return new ResponseData(codeEnum.getCode(), codeEnum.getName(), null);\n }\n\n public static ResponseData fail(KnownException knownException) {\n return new ResponseData(knownException.getCode(), knownException.getMsg(), null);\n }\n\n public static ResponseData fail(Integer code, String msg) {\n code = code == null ? CodeEnum.FAIL.getCode() : code;\n return new ResponseData(code, msg, null);\n }\n\n public static ResponseData illegalArgument() {\n return new ResponseData(CodeEnum.PARAM_INVALIDATE.getCode(), CodeEnum.PARAM_INVALIDATE.getName(), null);\n }\n\n public static ResponseData systemError() {\n return new ResponseData(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getName(), null);\n }\n\n @JsonIgnore\n public boolean isSuccess() {\n Integer retCode = status.getCode();\n return Objects.equals(CodeEnum.SUCCESS.getCode(), retCode);\n }\n\n public void throwKnownException(){\n throw new KnownException(status.getCode(), status.getMsg());\n }\n\n public void throwWarnException(){\n throw new WarnException(status.getCode(), status.getMsg());\n }\n\n public void throwErrorException(){\n throw new ErrorException(status.getCode(), status.getMsg());\n }\n\n public void tryThrowKnownException(){\n if(!isSuccess()){\n throwKnownException();\n }\n }\n\n public void tryThrowWarnException(){\n if(!isSuccess()){\n throwWarnException();\n }\n }\n\n public void tryThrowErrorException(){\n if(!isSuccess()){\n throwErrorException();\n }\n }\n}\n\n" }, { "type": "file", "name": "Status.java", "format": "raw", - "data": "package ${basePackage}.adapter.portal.api._share;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n/**\n * @author binking338\n */\n@Data\n@Schema(description = \"接口返回状态\")\npublic class Status {\n @Schema(description=\"业务状态码\")\n private Integer code;\n @Schema(description=\"业务状态信息\")\n private String msg;\n\n public Status() {\n }\n\n public Status(Integer code, String msg) {\n this.code = code;\n this.msg = msg;\n }\n}\n" + "data": "package ${basePackage}.adapter.portal.api._share;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\n/**\n * @author cap4j-ddd-codegen\n */\n@Data\n@Schema(description = \"接口返回状态\")\npublic class Status {\n @Schema(description=\"业务状态码\")\n private Integer code;\n @Schema(description=\"业务状态信息\")\n private String msg;\n\n public Status() {\n }\n\n public Status(Integer code, String msg) {\n this.code = code;\n this.msg = msg;\n }\n}\n" } ] }, @@ -241,7 +441,7 @@ "type": "file", "name": "TestController.java", "format": "raw", - "data": "package ${basePackage}.adapter.portal.api;\n\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport ${basePackage}.adapter.portal.api._share.ResponseData;\nimport org.springframework.web.bind.annotation.*;\n\n/**\n *\n * @author binking338\n */\n@Tag(name = \"测试控制器\")\n@RestController\n@RequestMapping(value = \"/test\")\n@Slf4j\npublic class TestController {\n\n @PostMapping(value = \"\")\n public ResponseData test(@RequestParam(\"msg\") String msg) {\n return ResponseData.success(\"echo: \" + msg);\n }\n\n}\n" + "data": "package ${basePackage}.adapter.portal.api;\n\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport lombok.extern.slf4j.Slf4j;\nimport ${basePackage}.adapter.portal.api._share.ResponseData;\nimport org.springframework.web.bind.annotation.*;\n\n/**\n *\n * @author cap4j-ddd-codegen\n */\n@Tag(name = \"测试控制器\")\n@RestController\n@RequestMapping(value = \"/test\")\n@Slf4j\npublic class TestController {\n\n @PostMapping(value = \"\")\n public ResponseData test(@RequestParam(\"msg\") String msg) {\n return ResponseData.success(\"echo: \" + msg);\n }\n\n}\n" } ] }, @@ -290,14 +490,11 @@ "name": "enums" } ] - },, - { - "type": "dir", - "name": "clients" }, { "type": "dir", - "name": "commands" + "name": "commands", + "tag": "command" }, { "type": "dir", @@ -305,7 +502,13 @@ "children": [ { "type": "dir", - "name": "events" + "name": "clients", + "tag": "client" + }, + { + "type": "dir", + "name": "events", + "tag": "integration_event" }, { "type": "dir", @@ -315,11 +518,13 @@ }, { "type": "dir", - "name": "queries" + "name": "queries", + "tag": "query" }, { "type": "dir", - "name": "subscribers" + "name": "subscribers", + "tag": "domain_event_handler,integration_event_handler" } ] }, @@ -333,11 +538,13 @@ }, { "type": "dir", - "name": "aggregates" + "name": "aggregates", + "tag": "domain_event,factory,specification" }, { "type": "dir", - "name": "services" + "name": "services", + "tag": "domain_service" } ] }, @@ -345,7 +552,7 @@ "type": "file", "name": "StartApplication.java", "format": "raw", - "data": "package ${basePackage};\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.domain.EntityScan;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.data.jpa.repository.config.EnableJpaRepositories;\nimport org.springframework.scheduling.annotation.EnableScheduling;\n\n/**\n * @author binking338\n */\n@SpringBootApplication\n@EnableFeignClients\n@EnableScheduling\n@EnableJpaRepositories(basePackages = \"${basePackage}.adapter.domain.repositories\")\n@EntityScan(basePackages = \"${basePackage}.domain.aggregates\")\npublic class StartApplication {\n public static void main(String[] args) {\n SpringApplication.run(StartApplication.class, args);\n }\n\n}" + "data": "package ${basePackage};\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.domain.EntityScan;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.data.jpa.repository.config.EnableJpaRepositories;\nimport org.springframework.scheduling.annotation.EnableScheduling;\n\n/**\n * @author cap4j-ddd-codegen\n */\n@SpringBootApplication\n@EnableFeignClients\n@EnableScheduling\n@EnableJpaRepositories(basePackages = \"${basePackage}.adapter.domain.repositories\")\n@EntityScan(basePackages = \"${basePackage}.domain.aggregates\")\npublic class StartApplication {\n public static void main(String[] args) {\n SpringApplication.run(StartApplication.class, args);\n }\n\n}" } ] } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/SnowflakeAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/SnowflakeAutoConfiguration.java index c91e11a..f626287 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/SnowflakeAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/SnowflakeAutoConfiguration.java @@ -2,8 +2,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.netcorepal.cap4j.ddd.application.distributed.configure.SnowflakeProperties; -import org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator; +import org.netcorepal.cap4j.ddd.domain.distributed.configure.SnowflakeProperties; import org.netcorepal.cap4j.ddd.domain.distributed.snowflake.DefaultSnowflakeWorkerIdDispatcher; import org.netcorepal.cap4j.ddd.domain.distributed.snowflake.SnowflakeIdGenerator; import org.netcorepal.cap4j.ddd.domain.distributed.snowflake.SnowflakeWorkerIdDispatcher; diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/SnowflakeProperties.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/configure/SnowflakeProperties.java similarity index 87% rename from cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/SnowflakeProperties.java rename to cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/configure/SnowflakeProperties.java index 2e54460..0e8d581 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/SnowflakeProperties.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/distributed/configure/SnowflakeProperties.java @@ -1,4 +1,4 @@ -package org.netcorepal.cap4j.ddd.application.distributed.configure; +package org.netcorepal.cap4j.ddd.domain.distributed.configure; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java index 9de3c62..168113c 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java @@ -5,10 +5,7 @@ import org.netcorepal.cap4j.ddd.application.UnitOfWork; import org.netcorepal.cap4j.ddd.application.impl.JpaUnitOfWork; import org.netcorepal.cap4j.ddd.application.UnitOfWorkSupport; -import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory; -import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactorySupervisor; -import org.netcorepal.cap4j.ddd.domain.aggregate.Specification; -import org.netcorepal.cap4j.ddd.domain.aggregate.SpecificationManager; +import org.netcorepal.cap4j.ddd.domain.aggregate.*; import org.netcorepal.cap4j.ddd.domain.aggregate.impl.DefaultAggregateFactorySupervisor; import org.netcorepal.cap4j.ddd.domain.event.*; import org.netcorepal.cap4j.ddd.domain.event.configure.EventProperties; @@ -97,12 +94,13 @@ public RepositorySupervisor defaultRepositorySupervisor( @Bean @Primary public AggregateFactorySupervisor defaultAggregateFactorySupervisor( - List> factories + List> factories ){ DefaultAggregateFactorySupervisor aggregateFactorySupervisor = new DefaultAggregateFactorySupervisor( factories ); aggregateFactorySupervisor.init(); + AggregateFactorySupervisorSupport.configure(aggregateFactorySupervisor); return aggregateFactorySupervisor; } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories b/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories index 9d011cf..a3b76bc 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/resources/META-INF/spring.factories @@ -1,5 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.netcorepal.cap4j.ddd.application.distributed.configure.SnowflakeProperties,\ + org.netcorepal.cap4j.ddd.domain.distributed.configure.SnowflakeProperties,\ org.netcorepal.cap4j.ddd.application.distributed.configure.JdbcLockerProperties,\ org.netcorepal.cap4j.ddd.application.distributed.JdbcLockerAutoConfiguration,\ org.netcorepal.cap4j.ddd.application.event.RocketMqEventAutoConfiguration,\ diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java index e59bc57..a9403f8 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestHandler.java @@ -8,7 +8,7 @@ * @author binking338 * @date 2024/8/24 */ -public interface RequestHandler { +public interface RequestHandler, RESPONSE> { /** * 执行请求 diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestParam.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestParam.java new file mode 100644 index 0000000..b07a292 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestParam.java @@ -0,0 +1,10 @@ +package org.netcorepal.cap4j.ddd.application; + +/** + * 请求参数 + * + * @author binking338 + * @date 2024/9/6 + */ +public interface RequestParam { +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java index 5bf22a8..41a8c00 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisor.java @@ -23,27 +23,5 @@ static RequestSupervisor getInstance() { * @param request 请求参数 * @param 请求参数类型 */ - Object send(REQUEST request); - - /** - * 执行请求 - * - * @param request 请求参数 - * @param resultClass 返回结果类型 - * @param 请求参数类型 - * @param 返回结果类型 - */ - RESPONSE send(REQUEST request, Class resultClass); - - /** - * 执行请求 - * - * @param request 请求参数 - * @param paramClass 请求参数类型 - * @param resultClass 返回结果类型 - * @param 请求参数类型 - * @param 返回结果类型 - * @return 请求结果 - */ - RESPONSE send(REQUEST request, Class paramClass, Class resultClass); + , RESPONSE> RESPONSE send(REQUEST request); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java index c9fc3c8..a458d21 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/Command.java @@ -1,6 +1,7 @@ package org.netcorepal.cap4j.ddd.application.command; import org.netcorepal.cap4j.ddd.application.RequestHandler; +import org.netcorepal.cap4j.ddd.application.RequestParam; /** * 命令接口 @@ -10,7 +11,7 @@ * @author binking338 * @date */ -public interface Command extends RequestHandler { +public interface Command, RESULT> extends RequestHandler { @Override RESULT exec(PARAM param); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParam.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParam.java deleted file mode 100644 index 8af891b..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParam.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.netcorepal.cap4j.ddd.application.command; - -/** - * 无参数命令 - * - * @author binking338 - * @date - */ -public abstract class CommandNoneParam implements Command { - @Override - public RESULT exec(Void aVoid) { - return exec(); - } - - public abstract RESULT exec(); -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParamAndResult.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParamAndResult.java deleted file mode 100644 index 9810c0f..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneParamAndResult.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.netcorepal.cap4j.ddd.application.command; - -/** - * 无参无返回命令 - * - * @author binking338 - * @date - */ -public abstract class CommandNoneParamAndResult implements Command { - @Override - public Void exec(Void param) { - exec(); - return null; - } - - public abstract void exec(); -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneResult.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneResult.java deleted file mode 100644 index fbef2e3..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/CommandNoneResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.netcorepal.cap4j.ddd.application.command; - -/** - * 无参数命令 - * - * @author binking338 - * @date - */ -public abstract class CommandNoneResult implements Command { - @Override - public abstract Void exec(PARAM param); -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/NoneResultCommandParam.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/NoneResultCommandParam.java new file mode 100644 index 0000000..1c99eb3 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/command/NoneResultCommandParam.java @@ -0,0 +1,14 @@ +package org.netcorepal.cap4j.ddd.application.command; + +import org.netcorepal.cap4j.ddd.application.RequestParam; + +/** + * 无返回命令 + * + * @author binking338 + * @date + */ +public abstract class NoneResultCommandParam> implements Command { + @Override + public abstract Void exec(PARAM param); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventAttachedTransactionCommittedEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventNotifyEvent.java similarity index 70% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventAttachedTransactionCommittedEvent.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventNotifyEvent.java index c83e4f2..ffb6828 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventAttachedTransactionCommittedEvent.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventNotifyEvent.java @@ -7,12 +7,12 @@ import java.util.List; /** - * 集成事件所在事务成功提交事件 + * 集成事件通知中 * * @author binking338 * @date 2024/8/28 */ -public class IntegrationEventAttachedTransactionCommittedEvent extends ApplicationEvent { +public class IntegrationEventNotifyEvent extends ApplicationEvent { @Getter List events; @@ -22,7 +22,7 @@ public class IntegrationEventAttachedTransactionCommittedEvent extends Applicati * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ - public IntegrationEventAttachedTransactionCommittedEvent(Object source, List events) { + public IntegrationEventNotifyEvent(Object source, List events) { super(source); this.events = events; } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java index b4220c1..f41c6ce 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java @@ -1,8 +1,8 @@ package org.netcorepal.cap4j.ddd.application.event.impl; import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventNotifyEvent; import org.netcorepal.cap4j.ddd.domain.event.EventPublisher; -import org.netcorepal.cap4j.ddd.application.event.IntegrationEventAttachedTransactionCommittedEvent; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventSupervisor; import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; @@ -12,11 +12,11 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.Ordered; import org.springframework.core.annotation.OrderUtils; -import org.springframework.transaction.event.TransactionalEventListener; import java.time.Duration; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.List; @@ -73,11 +73,12 @@ public void notify(INTEGRATION_EVENT eventPayload, LocalDate getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.prePersist(event)); eventRecordRepository.save(event); getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.postPersist(event)); - List events = new ArrayList<>(); - events.add(event); - IntegrationEventAttachedTransactionCommittedEvent integrationEventAttachedTransactionCommittedEvent - = new IntegrationEventAttachedTransactionCommittedEvent(this, events); + + event.markPersist(true); + eventPublisher.publish(event); + IntegrationEventNotifyEvent integrationEventAttachedTransactionCommittedEvent + = new IntegrationEventNotifyEvent(this, Arrays.asList(event)); applicationEventPublisher.publishEvent(integrationEventAttachedTransactionCommittedEvent); } @@ -90,16 +91,4 @@ public void notify(INTEGRATION_EVENT eventPayload) { public void notify(INTEGRATION_EVENT eventPayload, Duration delay) { notify(eventPayload, LocalDateTime.now().plus(delay)); } - - @TransactionalEventListener(fallbackExecution = true, classes = IntegrationEventAttachedTransactionCommittedEvent.class) - public void onTransactionCommitted(IntegrationEventAttachedTransactionCommittedEvent transactionCommittedEvent) { - List events = transactionCommittedEvent.getEvents(); - if (events != null && !events.isEmpty()) { - LocalDateTime now = LocalDateTime.now(); - events.forEach(event -> { - event.markPersist(true); - eventPublisher.publish(event); - }); - } - } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java index e472867..61b96dc 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/impl/DefaultRequestSupervisor.java @@ -3,11 +3,10 @@ import lombok.RequiredArgsConstructor; import org.netcorepal.cap4j.ddd.application.RequestHandler; import org.netcorepal.cap4j.ddd.application.RequestInterceptor; +import org.netcorepal.cap4j.ddd.application.RequestParam; import org.netcorepal.cap4j.ddd.application.RequestSupervisor; import org.netcorepal.cap4j.ddd.application.command.Command; -import org.netcorepal.cap4j.ddd.application.command.CommandNoneParam; -import org.netcorepal.cap4j.ddd.application.command.CommandNoneParamAndResult; -import org.netcorepal.cap4j.ddd.application.command.CommandNoneResult; +import org.netcorepal.cap4j.ddd.application.command.NoneResultCommandParam; import org.netcorepal.cap4j.ddd.application.query.*; import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; @@ -42,8 +41,8 @@ public void init() { Class requestPayloadClass = ClassUtils.resolveGenericTypeClass( requestHandler.getClass(), 0, RequestHandler.class, - Command.class, CommandNoneParam.class, CommandNoneResult.class, CommandNoneParamAndResult.class, - Query.class, QueryNoArgs.class, ListQuery.class, ListQueryNoArgs.class, PageQuery.class); + Command.class, NoneResultCommandParam.class, + Query.class, ListQuery.class, PageQuery.class); requestHandlerMap.put(requestPayloadClass, requestHandler); } for (RequestInterceptor requestInterceptor : requestInterceptors) { @@ -56,22 +55,12 @@ public void init() { } @Override - public Object send(REQUEST request) { - return send(request, (Class) request.getClass(), Object.class); - } - - @Override - public RESPONSE send(REQUEST request, Class responseClass) { - return send(request, (Class) request.getClass(), responseClass); - } - - @Override - public RESPONSE send(REQUEST request, Class requestClass, Class responseClass) { + public , RESPONSE> RESPONSE send(REQUEST request) { init(); - requestInterceptorMap.getOrDefault(requestClass, Collections.emptyList()) + requestInterceptorMap.getOrDefault(request.getClass(), Collections.emptyList()) .forEach(interceptor -> ((RequestInterceptor) interceptor).preRequest(request)); - RESPONSE response = ((RequestHandler) requestHandlerMap.get(requestClass)).exec(request); - requestInterceptorMap.getOrDefault(requestClass, Collections.emptyList()) + RESPONSE response = ((RequestHandler) requestHandlerMap.get(request.getClass())).exec(request); + requestInterceptorMap.getOrDefault(request.getClass(), Collections.emptyList()) .forEach(interceptor -> ((RequestInterceptor) interceptor).postRequest(request, response)); return response; } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQuery.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQuery.java index fed3454..9aaa8aa 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQuery.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQuery.java @@ -1,5 +1,7 @@ package org.netcorepal.cap4j.ddd.application.query; +import org.netcorepal.cap4j.ddd.application.RequestParam; + import java.util.List; /** @@ -8,5 +10,5 @@ * @author binking338 * @date */ -public interface ListQuery extends Query> { +public interface ListQuery>,ITEM> extends Query> { } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryNoArgs.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryNoArgs.java deleted file mode 100644 index edc5bbe..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryNoArgs.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.netcorepal.cap4j.ddd.application.query; - -import java.util.List; - -/** - * 无参列表查询 - * - * @author binking338 - * @date - */ -public abstract class ListQueryNoArgs implements ListQuery { - @Override - public List exec(Void aVoid) { - return query(); - } - - public abstract List query(); -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryParam.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryParam.java new file mode 100644 index 0000000..d29a071 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryParam.java @@ -0,0 +1,14 @@ +package org.netcorepal.cap4j.ddd.application.query; + +import org.netcorepal.cap4j.ddd.application.RequestParam; + +import java.util.List; + +/** + * 列表查询参数 + * + * @author binking338 + * @date 2024/9/6 + */ +public class ListQueryParam implements RequestParam> { +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/PageQuery.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/PageQuery.java index 1ab84d3..b23efe5 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/PageQuery.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/PageQuery.java @@ -1,8 +1,8 @@ package org.netcorepal.cap4j.ddd.application.query; +import org.netcorepal.cap4j.ddd.application.RequestParam; import org.netcorepal.cap4j.ddd.share.PageData; -import org.netcorepal.cap4j.ddd.share.PageParam; /** * 分页查询 @@ -10,5 +10,5 @@ * @author binking338 * @date */ -public interface PageQuery extends Query> { +public interface PageQuery>, ITEM> extends Query> { } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/PageQueryParam.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/PageQueryParam.java new file mode 100644 index 0000000..2e79032 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/PageQueryParam.java @@ -0,0 +1,14 @@ +package org.netcorepal.cap4j.ddd.application.query; + +import org.netcorepal.cap4j.ddd.application.RequestParam; +import org.netcorepal.cap4j.ddd.share.PageData; +import org.netcorepal.cap4j.ddd.share.PageParam; + +/** + * 分页查询参数 + * + * @author binking338 + * @date 2024/9/6 + */ +public abstract class PageQueryParam extends PageParam implements RequestParam> { +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/Query.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/Query.java index 53ce71d..3555a0d 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/Query.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/Query.java @@ -1,6 +1,7 @@ package org.netcorepal.cap4j.ddd.application.query; import org.netcorepal.cap4j.ddd.application.RequestHandler; +import org.netcorepal.cap4j.ddd.application.RequestParam; /** * 查询接口 @@ -11,7 +12,7 @@ * @param 查询参数 * @param 查询结果 */ -public interface Query extends RequestHandler { +public interface Query, RESULT> extends RequestHandler { @Override RESULT exec(PARAM param); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/QueryNoArgs.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/QueryNoArgs.java deleted file mode 100644 index 1120b5b..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/QueryNoArgs.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.netcorepal.cap4j.ddd.application.query; - -/** - * 无参查询 - * - * @author binking338 - * @date - */ -public abstract class QueryNoArgs implements Query { - @Override - public RESULT exec(Void aVoid) { - return query(); - } - - public abstract RESULT query(); -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactory.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactory.java index 8c6b43a..a6935c8 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactory.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactory.java @@ -6,26 +6,13 @@ * @author binking338 * @date 2024/9/3 */ -public interface AggregateFactory { +public interface AggregateFactory { /** * 创建新聚合实例 * - * @param initHandler + * @param entityPayload * @return */ - ENTITY create(InitHandler initHandler); - - public static interface InitHandler { - public static InitHandler getDefault() { - return new EmptyInitHandler(); - } - - void init(ENTITY entity); - - static class EmptyInitHandler implements InitHandler { - public void init(Object entity) { - } - } - } + ENTITY create(ENTITY_PAYLOAD entityPayload); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisor.java index c1e7298..61bfb1a 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactorySupervisor.java @@ -14,19 +14,10 @@ public static AggregateFactorySupervisor getInstance() { /** * 创建新聚合实例 * - * @param entityClass - * @param + * @param entityPayload * @return + * @param */ - ENTITY create(Class entityClass); + , ENTITY> ENTITY create(ENTITY_PAYLOAD entityPayload); - /** - * 创建新聚合实例 - * - * @param entityClass - * @param initHandler - * @param - * @return - */ - ENTITY create(Class entityClass, AggregateFactory.InitHandler initHandler); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregatePayload.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregatePayload.java new file mode 100644 index 0000000..75f9900 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregatePayload.java @@ -0,0 +1,11 @@ +package org.netcorepal.cap4j.ddd.domain.aggregate; + +/** + * 聚合工厂负载接口 + * 用于标记返回实体类型 + * + * @author binking338 + * @date 2024/9/6 + */ +public interface AggregatePayload { +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java index 0f73862..3e46994 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java @@ -21,6 +21,8 @@ public static final String TYPE_ENUM = "enum"; public static final String TYPE_REPOSITORY = "repository"; public static final String TYPE_DOMAIN_EVENT = "domain-event"; + public static final String TYPE_FACTORY = "factory"; + public static final String TYPE_FACTORY_PAYLOAD = "factory-payload"; public static final String TYPE_SPECIFICATION = "specification"; /** diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/AbstractSpecification.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/AbstractSpecification.java deleted file mode 100644 index 3ecabde..0000000 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/AbstractSpecification.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.netcorepal.cap4j.ddd.domain.aggregate.impl; - -import org.netcorepal.cap4j.ddd.domain.aggregate.Specification; - -/** - * 默认实体规约抽象类 - * - * @author binking338 - * @date 2023/8/13 - */ -public abstract class AbstractSpecification implements Specification { - - public abstract Result specify(Entity entity); -} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java index 74eaf2c..238554d 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory; import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactorySupervisor; -import org.netcorepal.cap4j.ddd.share.DomainException; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload; import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; import java.util.HashMap; @@ -18,9 +18,9 @@ */ @RequiredArgsConstructor public class DefaultAggregateFactorySupervisor implements AggregateFactorySupervisor { - private final List> factories; + private final List> factories; - private Map, AggregateFactory> factoryMap = null; + private Map, AggregateFactory> factoryMap = null; public void init() { if (null != factoryMap) { @@ -32,7 +32,6 @@ public void init() { } factoryMap = new HashMap<>(); factories.forEach(factory -> { - factoryMap.put( ClassUtils.resolveGenericTypeClass( factory.getClass(), 0, @@ -40,35 +39,18 @@ public void init() { ), factory ); - }); } } @Override - public ENTITY create(Class entityClass) { - return create(entityClass, null); - } - - @Override - public ENTITY create(Class entityClass, AggregateFactory.InitHandler initHandler) { - AggregateFactory factory = factoryMap.computeIfAbsent(entityClass, (cls) -> (i) -> { - try { - ENTITY entity = (ENTITY) entityClass.newInstance(); - if (null != i) { - i.init(entity); - } - return entity; - } catch (Exception e) { - throw new DomainException("聚合实例创建异常", e); - } - }); - - Object instance = ((AggregateFactory) factory).create( - null != initHandler - ? (AggregateFactory.InitHandler) initHandler - : AggregateFactory.InitHandler.getDefault() - ); - return (ENTITY) instance; + public , ENTITY> ENTITY create(ENTITY_PAYLOAD entityPayload) { + init(); + AggregateFactory factory = factoryMap.get(entityPayload.getClass()); + if (null == factory) { + return null; + } + ENTITY instance = ((AggregateFactory) factory).create(entityPayload); + return instance; } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultSpecificationManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultSpecificationManager.java index 4d168a4..2aded78 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultSpecificationManager.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultSpecificationManager.java @@ -36,7 +36,7 @@ public void init() { for (Specification specification : specifications) { Class entityClass = ClassUtils.resolveGenericTypeClass( specification.getClass(), 0, - AbstractSpecification.class, Specification.class + Specification.class ); if (!specificationMap.containsKey(entityClass)) { specificationMap.put(entityClass, new java.util.ArrayList<>()); diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittedEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPostCommitEvent.java similarity index 75% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittedEvent.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPostCommitEvent.java index d428c53..d5f319f 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittedEvent.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPostCommitEvent.java @@ -11,7 +11,7 @@ * @author binking338 * @date 2024/8/28 */ -public class DomainEventAttachedTransactionCommittedEvent extends ApplicationEvent { +public class DomainEventAttachedTransactionPostCommitEvent extends ApplicationEvent { @Getter List events; @@ -21,7 +21,7 @@ public class DomainEventAttachedTransactionCommittedEvent extends ApplicationEve * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ - public DomainEventAttachedTransactionCommittedEvent(Object source, List events) { + public DomainEventAttachedTransactionPostCommitEvent(Object source, List events) { super(source); this.events = events; } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommitingEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPreCommitEvent.java similarity index 82% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommitingEvent.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPreCommitEvent.java index 048e120..0bea1f5 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommitingEvent.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPreCommitEvent.java @@ -11,7 +11,7 @@ * @author binking338 * @date 2024/8/28 */ -public class DomainEventAttachedTransactionCommitingEvent extends ApplicationEvent { +public class DomainEventAttachedTransactionPreCommitEvent extends ApplicationEvent { @Getter List events; @@ -21,7 +21,7 @@ public class DomainEventAttachedTransactionCommitingEvent extends ApplicationEve * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ - public DomainEventAttachedTransactionCommitingEvent(Object source, List events) { + public DomainEventAttachedTransactionPreCommitEvent(Object source, List events) { super(source); this.events = events; } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java index 98c6bc0..1c97baa 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java @@ -4,8 +4,8 @@ import org.netcorepal.cap4j.ddd.domain.event.EventPublisher; import org.netcorepal.cap4j.ddd.domain.event.*; import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; -import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionCommitingEvent; -import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionCommittedEvent; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionPreCommitEvent; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionPostCommitEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.Ordered; import org.springframework.core.annotation.OrderUtils; @@ -156,11 +156,11 @@ public void release(Set entities) { persistedEvents.add(event); } } - DomainEventAttachedTransactionCommitingEvent domainEventAttachedTransactionCommitingEvent = new DomainEventAttachedTransactionCommitingEvent(this, transientEvents); - DomainEventAttachedTransactionCommittedEvent domainEventAttachedTransactionCommittedEvent = new DomainEventAttachedTransactionCommittedEvent(this, persistedEvents); - onTransactionCommiting(domainEventAttachedTransactionCommitingEvent); - applicationEventPublisher.publishEvent(domainEventAttachedTransactionCommitingEvent); - applicationEventPublisher.publishEvent(domainEventAttachedTransactionCommittedEvent); + DomainEventAttachedTransactionPreCommitEvent domainEventAttachedTransactionPreCommitEvent = new DomainEventAttachedTransactionPreCommitEvent(this, transientEvents); + DomainEventAttachedTransactionPostCommitEvent domainEventAttachedTransactionPostCommitEvent = new DomainEventAttachedTransactionPostCommitEvent(this, persistedEvents); + onTransactionCommiting(domainEventAttachedTransactionPreCommitEvent); + applicationEventPublisher.publishEvent(domainEventAttachedTransactionPreCommitEvent); + applicationEventPublisher.publishEvent(domainEventAttachedTransactionPostCommitEvent); } /** @@ -181,14 +181,14 @@ protected boolean isDomainEventNeedPersist(Object payload) { } } - protected void onTransactionCommiting(DomainEventAttachedTransactionCommitingEvent domainEventAttachedTransactionCommitingEvent) { - List events = domainEventAttachedTransactionCommitingEvent.getEvents(); + protected void onTransactionCommiting(DomainEventAttachedTransactionPreCommitEvent domainEventAttachedTransactionPreCommitEvent) { + List events = domainEventAttachedTransactionPreCommitEvent.getEvents(); publish(events); } - @TransactionalEventListener(fallbackExecution = true, classes = DomainEventAttachedTransactionCommittedEvent.class) - public void onTransactionCommitted(DomainEventAttachedTransactionCommittedEvent domainEventAttachedTransactionCommittedEvent) { - List events = domainEventAttachedTransactionCommittedEvent.getEvents(); + @TransactionalEventListener(fallbackExecution = true, classes = DomainEventAttachedTransactionPostCommitEvent.class) + public void onTransactionCommitted(DomainEventAttachedTransactionPostCommitEvent domainEventAttachedTransactionPostCommitEvent) { + List events = domainEventAttachedTransactionPostCommitEvent.getEvents(); publish(events); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java index 2c1383d..414ca11 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java @@ -1,7 +1,8 @@ package org.netcorepal.cap4j.ddd.domain.event.impl; import lombok.RequiredArgsConstructor; -import org.netcorepal.cap4j.ddd.application.event.*; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventPublisher; import org.netcorepal.cap4j.ddd.domain.event.*; import org.netcorepal.cap4j.ddd.share.DomainException; import org.springframework.core.Ordered; @@ -77,9 +78,6 @@ public void publish(EventRecord event) { case HEADER_VALUE_CAP4J_EVENT_TYPE_INTEGRATION: if (delay.isNegative() || delay.isZero()) { internalPublish4IntegrationEvent(event); -// executor.submit(() -> { -// internalPublish4IntegrationEvent(event); -// }); } else { executor.schedule(() -> { internalPublish4IntegrationEvent(event); @@ -88,20 +86,19 @@ public void publish(EventRecord event) { break; case HEADER_VALUE_CAP4J_EVENT_TYPE_DOMAIN: default: - boolean persist = (Boolean) message.getHeaders().getOrDefault(HEADER_KEY_CAP4J_PERSIST, false); - if (persist) { - if (delay.isNegative() || delay.isZero()) { - internalPublish4DomainEvent(event); -// executor.submit(() -> { -// internalPublish4DomainEvent(event); -// }); - } else { - executor.schedule(() -> { + if (delay.isNegative() || delay.isZero()) { + boolean persist = (Boolean) message.getHeaders().getOrDefault(HEADER_KEY_CAP4J_PERSIST, false); + if (persist) { + executor.submit(() -> { internalPublish4DomainEvent(event); - }, delay.getSeconds(), TimeUnit.SECONDS); + }); + } else { + internalPublish4DomainEvent(event); } } else { - internalPublish4DomainEvent(event); + executor.schedule(() -> { + internalPublish4DomainEvent(event); + }, delay.getSeconds(), TimeUnit.SECONDS); } break; } @@ -131,6 +128,7 @@ protected void internalPublish4DomainEvent(EventRecord event) { getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.postRelease(event)); } catch (Exception ex) { getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onException(ex, event)); + throw new DomainException(String.format("领域事件发布失败:%s", event.getId()), ex); } } @@ -147,6 +145,7 @@ protected void internalPublish4IntegrationEvent(EventRecord event) { integrationEventPublisheres.forEach(integrationEventPublisher -> integrationEventPublisher.publish(event, new IntegrationEventSendPublishCallback(getOrderedEventMessageInterceptors(), getOrderedIntegrationEventInterceptors(), eventRecordRepository))); } catch (Exception ex) { + getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onException(ex, event)); throw new DomainException(String.format("集成事件发布失败: %s", event.getId()), ex); } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java index 19885d3..c3ac567 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java @@ -1,6 +1,7 @@ package org.netcorepal.cap4j.ddd.domain.event.impl; import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.RequestParam; import org.netcorepal.cap4j.ddd.application.RequestSupervisor; import org.netcorepal.cap4j.ddd.domain.event.EventSubscriber; import org.netcorepal.cap4j.ddd.domain.event.EventSubscriberManager; @@ -53,20 +54,23 @@ public void init() { ScanUtils.findDomainEventClasses(scanPath).forEach(domainEventClass -> { // 自动实现 Spring EventListener 适配 subscribe(domainEventClass, applicationEventPublisher::publishEvent); -// -// // 自动实现 Event -> Request 转发 -// if( null != domainEventClass.getAnnotation(AutoRequest.class)){ -// AutoRequest autoRequest = domainEventClass.getAnnotation(AutoRequest.class); -// Class converterClass = null; -// if (Converter.class.isAssignableFrom(autoRequest.converterClass())) { -// converterClass = autoRequest.converterClass(); -// } -// Converter converter = ClassUtils.newConverterInstance(domainEventClass, autoRequest.requestClass(), converterClass); -// subscribe(domainEventClass, domainEvent -> RequestSupervisor.getInstance().request(converter.convert(domainEvent))); -// } + + // 自动实现 Event -> Request + // 领域事件转Request容易导致数据库长时事务,慎用! + if( null != domainEventClass.getAnnotation(AutoRequest.class)){ + AutoRequest autoRequest = domainEventClass.getAnnotation(AutoRequest.class); + Class converterClass = null; + if (Converter.class.isAssignableFrom(autoRequest.converterClass())) { + converterClass = autoRequest.converterClass(); + } + Converter converter = ClassUtils.newConverterInstance(domainEventClass, autoRequest.targetRequestClass(), converterClass); + subscribe(domainEventClass, domainEvent -> RequestSupervisor.getInstance().send((RequestParam)converter.convert(domainEvent))); + } }); // 集成事件 ScanUtils.findIntegrationEventClasses(scanPath).forEach(integrationEventClass -> { + subscribe(integrationEventClass, applicationEventPublisher::publishEvent); + // 自动实现 DomainEvent -> IntegrationEvent 适配 if (null != integrationEventClass.getAnnotation(AutoNotify.class)) { AutoNotify autoNotify = integrationEventClass.getAnnotation(AutoNotify.class); @@ -89,7 +93,7 @@ public void init() { converterClass = autoRequest.converterClass(); } Converter converter = ClassUtils.newConverterInstance(integrationEventClass, autoRequest.targetRequestClass(), converterClass); - subscribe(integrationEventClass, integrationEvent -> RequestSupervisor.getInstance().send(converter.convert(integrationEvent))); + subscribe(integrationEventClass, integrationEvent -> RequestSupervisor.getInstance().send((RequestParam)converter.convert(integrationEvent))); } }); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Predicate.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Predicate.java new file mode 100644 index 0000000..59ce9f9 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Predicate.java @@ -0,0 +1,11 @@ +package org.netcorepal.cap4j.ddd.domain.repo; + +/** + * 仓储检索断言 + * + * @author binking338 + * @date 2024/9/8 + */ +public interface Predicate { + +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java index c1f67e7..5251407 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java @@ -17,56 +17,55 @@ public interface Repository { /** * 根据条件获取实体列表 - * @param condition + * @param predicate * @param orders * @return */ - List find(Object condition, List orders); - /** - * 通过ID获取实体 - * @param id - * @return - */ - Optional findById(Object id); - - /** - * 通过ID获取实体 - * @param ids - * @return - */ - List findByIds(Iterable ids); + List find(Predicate predicate, List orders); +// /** +// * 通过ID获取实体 +// * @param id +// * @return +// */ +// Optional findById(Object id); +// /** +// * 通过ID获取实体 +// * @param ids +// * @return +// */ +// List findByIds(Iterable ids); /** * 根据条件获取实体 - * @param condition + * @param predicate * @return */ - Optional findOne(Object condition); + Optional findOne(Predicate predicate); /** * 根据条件获取实体分页列表 - * @param condition + * @param predicate * @param pageParam * @return */ - PageData findPage(Object condition, PageParam pageParam); + PageData findPage(Predicate predicate, PageParam pageParam); /** * 根据条件获取实体计数 - * @param condition + * @param predicate * @return */ - long count(Object condition); + long count(Predicate predicate); /** * 根据条件判断实体是否存在 - * @param condition + * @param predicate * @return */ - boolean exists(Object condition); + boolean exists(Predicate predicate); - /** - * 通过ID判断实体是否存在 - * @param id - * @return - */ - boolean existsById(Object id); +// /** +// * 通过ID判断实体是否存在 +// * @param id +// * @return +// */ +// boolean existsById(Object id); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java index 1d93d76..8bc2143 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java @@ -28,103 +28,53 @@ static RepositorySupervisor getInstance(){ /** * 根据条件获取实体列表 - * @param entityClass - * @param condition + * @param predicate * @param orders * @return * @param */ - List find(Class entityClass, Object condition, List orders); + List find(Predicate predicate, List orders); /** * 根据条件获取单个实体 - * @param entityClass - * @param condition + * @param predicate * @return * @param */ - Optional findOne(Class entityClass, Object condition); + Optional findOne(Predicate predicate); /** * 根据条件获取实体分页列表 - * @param entityClass - * @param condition + * @param predicate * @param pageParam * @return * @param */ - PageData findPage(Class entityClass, Object condition, PageParam pageParam); - - /** - * 根据ID获取实体 - * @param entityClass - * @param id - * @return - * @param - */ - Optional findById(Class entityClass, Object id); - /** - * 根据ID获取实体列表 - * @param entityClass - * @param ids - * @return - * @param - */ - List findByIds(Class entityClass, Iterable ids); + PageData findPage(Predicate predicate, PageParam pageParam); /** * 根据条件删除实体 * - * @param entityClass - * @param condition + * @param predicate * @param limit * @return * @param */ - List remove(Class entityClass, Object condition, int limit); - - /** - * 根据ID删除实体 - * @param entityClass - * @param id - * @return - * @param - */ - Optional removeById(Class entityClass, Object id); - - /** - * 根据ID批量删除实体 - * @param entityClass - * @param ids - * @return - * @param - */ - List removeByIds(Class entityClass, Iterable ids); + List remove(Predicate predicate, int limit); /** * 根据条件获取实体计数 - * @param entityClass - * @param condition + * @param predicate * @return * @param */ - long count(Class entityClass, Object condition); + long count(Predicate predicate); /** * 根据条件判断实体是否存在 - * @param entityClass - * @param condition - * @return - * @param - */ - boolean exists(Class entityClass, Object condition); - - /** - * 通过ID判断实体是否存在 - * @param entityClass - * @param id + * @param predicate * @return * @param */ - boolean existsById(Class entityClass, Object id); + boolean exists(Predicate predicate); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java index b3fda94..1f404b1 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java @@ -1,11 +1,13 @@ package org.netcorepal.cap4j.ddd.impl; import org.netcorepal.cap4j.ddd.Mediator; +import org.netcorepal.cap4j.ddd.application.RequestParam; import org.netcorepal.cap4j.ddd.application.RequestSupervisor; import org.netcorepal.cap4j.ddd.application.UnitOfWork; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventSupervisor; -import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory; import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactorySupervisor; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload; +import org.netcorepal.cap4j.ddd.domain.repo.Predicate; import org.netcorepal.cap4j.ddd.domain.repo.Repository; import org.netcorepal.cap4j.ddd.domain.repo.RepositorySupervisor; import org.netcorepal.cap4j.ddd.domain.service.DomainServiceSupervisor; @@ -28,13 +30,8 @@ public class DefaultMediator implements Mediator { @Override - public ENTITY create(Class entityClass) { - return AggregateFactorySupervisor.getInstance().create(entityClass); - } - - @Override - public ENTITY create(Class entityClass, AggregateFactory.InitHandler initHandler) { - return AggregateFactorySupervisor.getInstance().create(entityClass, initHandler); + public , ENTITY> ENTITY create(ENTITY_PAYLOAD entityPayload) { + return AggregateFactorySupervisor.getInstance().create(entityPayload); } @Override @@ -43,43 +40,28 @@ public Repository repo(Class entityClass) { } @Override - public List find(Class entityClass, Object condition, List orders) { - return RepositorySupervisor.getInstance().find(entityClass, condition, orders); - } - - @Override - public Optional findOne(Class entityClass, Object condition) { - return RepositorySupervisor.getInstance().findOne(entityClass, condition); - } - - @Override - public PageData findPage(Class entityClass, Object condition, PageParam pageParam) { - return RepositorySupervisor.getInstance().findPage(entityClass, condition, pageParam); - } - - @Override - public Optional findById(Class entityClass, Object id) { - return RepositorySupervisor.getInstance().findById(entityClass, id); + public List find(Predicate predicate, List orders) { + return RepositorySupervisor.getInstance().find(predicate, orders); } @Override - public List findByIds(Class entityClass, Iterable ids) { - return RepositorySupervisor.getInstance().findByIds(entityClass, ids); + public Optional findOne(Predicate predicate) { + return RepositorySupervisor.getInstance().findOne(predicate); } @Override - public long count(Class entityClass, Object condition) { - return RepositorySupervisor.getInstance().count(entityClass, condition); + public PageData findPage(Predicate predicate, PageParam pageParam) { + return RepositorySupervisor.getInstance().findPage(predicate, pageParam); } @Override - public boolean exists(Class entityClass, Object condition) { - return RepositorySupervisor.getInstance().exists(entityClass, condition); + public long count(Predicate predicate) { + return RepositorySupervisor.getInstance().count(predicate); } @Override - public boolean existsById(Class entityClass, Object id) { - return RepositorySupervisor.getInstance().existsById(entityClass, id); + public boolean exists(Predicate predicate) { + return RepositorySupervisor.getInstance().exists(predicate); } @Override @@ -88,18 +70,8 @@ public DOMAIN_SERVICE getService(Class domainSe } @Override - public List remove(Class entityClass, Object condition, int limit) { - return RepositorySupervisor.getInstance().remove(entityClass, condition, limit); - } - - @Override - public Optional removeById(Class entityClass, Object id) { - return RepositorySupervisor.getInstance().removeById(entityClass, id); - } - - @Override - public List removeByIds(Class entityClass, Iterable ids) { - return RepositorySupervisor.getInstance().removeByIds(entityClass, ids); + public List remove(Predicate predicate, int limit) { + return RepositorySupervisor.getInstance().remove(predicate, limit); } @Override @@ -123,20 +95,10 @@ public void save(Propagation propagation) { } @Override - public Object send(REQUEST request) { + public , RESPONSE> RESPONSE send(REQUEST request) { return RequestSupervisor.getInstance().send(request); } - @Override - public RESPONSE send(REQUEST request, Class resultClass) { - return RequestSupervisor.getInstance().send(request, resultClass); - } - - @Override - public RESPONSE send(REQUEST request, Class paramClass, Class resultClass) { - return RequestSupervisor.getInstance().send(request, paramClass, resultClass); - } - @Override public void notify(INTEGRATION_EVENT integrationEventPayload) { IntegrationEventSupervisor.getInstance().notify(integrationEventPayload); diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java index b9c64f9..11a45b6 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java @@ -80,7 +80,7 @@ public static Method findMethod(Class clazz, String name, Predicate meth public static Converter newConverterInstance(Class srcClass, Class descClass, Class converterClass) { Converter converter = null; try { - converter = Void.class.equals(converterClass) + converter = null == converterClass || Void.class.equals(converterClass) ? null : (Converter) converterClass.newInstance(); } catch (Exception e) { diff --git a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java index 7d35d43..7d3cfb3 100644 --- a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java +++ b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java @@ -164,6 +164,8 @@ private void loadPayload(Object payload) { : payload.getClass().getAnnotation(IntegrationEvent.class); if (integrationEvent != null) { this.eventType = integrationEvent.value(); + } else { + this.eventType = ""; } Retry retry = payload == null ? null diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java index 3111e92..c958921 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java @@ -438,12 +438,6 @@ public EntityPersisttedEvent(Object source, Set createdEntities, Set(createdEntities), new HashSet<>(updatedEntities), new HashSet<>(deletedEntities)); } diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java index 6e7eccf..54260ca 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java @@ -8,12 +8,13 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.function.Supplier; import java.util.stream.Collectors; /** @@ -27,47 +28,46 @@ public class AbstractJpaRepository implements Repository { private final JpaSpecificationExecutor jpaSpecificationExecutor; private final JpaRepository jpaRepository; - public Optional findById(Object id) { - List ids = new ArrayList<>(1); - ids.add((ID) id); - Optional entity = jpaRepository.findAllById(ids).stream().findFirst(); - return entity; - } - - public List findByIds(Iterable ids) { - List entities = jpaRepository.findAllById((Iterable) ids); - return entities; - } - - public boolean existsById(Object id) { - return jpaRepository.existsById((ID) id); - } +// public Optional findById(Object id) { +// List ids = new ArrayList<>(1); +// ids.add((ID) id); +// Optional entity = jpaRepository.findAllById(ids).stream().findFirst(); +// return entity; +// } +// +// public List findByIds(Iterable ids) { +// List entities = jpaRepository.findAllById((Iterable) ids); +// return entities; +// } +// +// public boolean existsById(Object id) { +// return jpaRepository.existsById((ID) id); +// } - public Optional findOne(Object condition) { - return jpaSpecificationExecutor.findOne((org.springframework.data.jpa.domain.Specification) condition); + public Optional findOne(Predicate predicate) { + return jpaSpecificationExecutor.findOne(JpaPredicate.resume(predicate)); } - @Override - public PageData findPage(Object condition, PageParam pageParam) { - Page page = jpaSpecificationExecutor.findAll((org.springframework.data.jpa.domain.Specification) condition, convertPageable(pageParam)); + public PageData findPage(Predicate predicate, PageParam pageParam) { + Page page = jpaSpecificationExecutor.findAll(JpaPredicate.resume(predicate), convertPageable(pageParam)); return convertPageData(page); } - public List find(Object condition, List orders) { + public List find(Predicate predicate, List orders) { Sort sort = Sort.unsorted(); if (orders != null && !orders.isEmpty()) { sort = convertSort(orders); } - List entities = jpaSpecificationExecutor.findAll((org.springframework.data.jpa.domain.Specification) condition, sort); + List entities = jpaSpecificationExecutor.findAll(JpaPredicate.resume(predicate), sort); return entities; } - public long count(Object condition) { + public long count(Predicate condition) { long result = jpaSpecificationExecutor.count((org.springframework.data.jpa.domain.Specification) condition); return result; } - public boolean exists(Object condition) { + public boolean exists(Predicate condition) { boolean result = jpaSpecificationExecutor.exists((org.springframework.data.jpa.domain.Specification) condition); return result; } diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java new file mode 100644 index 0000000..e03f3c1 --- /dev/null +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java @@ -0,0 +1,34 @@ +package org.netcorepal.cap4j.ddd.domain.repo; + +import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; +import org.springframework.data.jpa.domain.Specification; + +import java.lang.reflect.Method; + +/** + * Jpa仓储检索断言 + * + * @author binking338 + * @date 2024/9/8 + */ +public class JpaPredicate implements Predicate { + private Class entityClass; + private Specification spec; + + protected JpaPredicate(Class entityClass, Specification spec) { + this.entityClass = entityClass; + this.spec = spec; + } + + public static Predicate from(Class entityClass, Specification specification) { + return new JpaPredicate<>(entityClass, specification); + } + + public static Specification resume(Predicate predicate){ + return ((JpaPredicate) predicate).spec; + } + + public static Class reflectEntityClass(Predicate predicate) { + return ((JpaPredicate) predicate).entityClass; + } +} diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java index a7d2305..7dba219 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java @@ -2,9 +2,7 @@ import lombok.RequiredArgsConstructor; import org.netcorepal.cap4j.ddd.application.UnitOfWork; -import org.netcorepal.cap4j.ddd.domain.repo.AbstractJpaRepository; -import org.netcorepal.cap4j.ddd.domain.repo.Repository; -import org.netcorepal.cap4j.ddd.domain.repo.RepositorySupervisor; +import org.netcorepal.cap4j.ddd.domain.repo.*; import org.netcorepal.cap4j.ddd.share.DomainException; import org.netcorepal.cap4j.ddd.share.OrderInfo; import org.netcorepal.cap4j.ddd.share.PageData; @@ -58,8 +56,9 @@ public Repository repo(Class entityClass) { } @Override - public List find(Class entityClass, Object condition, List orders) { - List entities = repo(entityClass).find(condition, orders); + public List find(Predicate predicate, List orders) { + Class entityClass = JpaPredicate.reflectEntityClass(predicate); + List entities = repo(entityClass).find(predicate, orders); if (entities != null) { entities.forEach(unitOfWork::persist); } @@ -67,15 +66,17 @@ public List find(Class entityClass, Object condition, L } @Override - public Optional findOne(Class entityClass, Object condition) { - Optional entity = repo(entityClass).findOne(condition); + public Optional findOne(Predicate predicate) { + Class entityClass = JpaPredicate.reflectEntityClass(predicate); + Optional entity = repo(entityClass).findOne(predicate); entity.ifPresent(unitOfWork::persist); return entity; } @Override - public PageData findPage(Class entityClass, Object condition, PageParam pageParam) { - PageData page = repo(entityClass).findPage(condition, pageParam); + public PageData findPage(Predicate predicate, PageParam pageParam) { + Class entityClass = JpaPredicate.reflectEntityClass(predicate); + PageData page = repo(entityClass).findPage(predicate, pageParam); if (page.getList() != null) { page.getList().forEach(unitOfWork::persist); } @@ -83,27 +84,12 @@ public PageData findPage(Class entityClass, Object cond } @Override - public Optional findById(Class entityClass, Object id) { - Optional entity = repo(entityClass).findById(id); - entity.ifPresent(unitOfWork::persist); - return entity; - } - - @Override - public List findByIds(Class entityClass, Iterable ids) { - List entities = repo(entityClass).findByIds(ids); - if (entities != null) { - entities.forEach(unitOfWork::persist); - } - return entities; - } - - @Override - public List remove(Class entityClass, Object condition, int limit) { + public List remove(Predicate predicate, int limit) { + Class entityClass = JpaPredicate.reflectEntityClass(predicate); PageParam page = new PageParam(); page.setPageNum(1); page.setPageSize(limit); - PageData entities = repo(entityClass).findPage(condition, page); + PageData entities = repo(entityClass).findPage(predicate, page); if (entities.getList() != null) { entities.getList().forEach(unitOfWork::remove); } @@ -111,33 +97,14 @@ public List remove(Class entityClass, Object condition, } @Override - public Optional removeById(Class entityClass, Object id) { - Optional entity = repo(entityClass).findById(id); - entity.ifPresent(unitOfWork::remove); - return entity; - } - - @Override - public List removeByIds(Class entityClass, Iterable ids) { - List entities = repo(entityClass).findByIds(ids); - if (entities != null) { - entities.forEach(unitOfWork::remove); - } - return entities; - } - - @Override - public long count(Class entityClass, Object condition) { - return repo(entityClass).count(condition); - } - - @Override - public boolean exists(Class entityClass, Object condition) { - return repo(entityClass).exists(condition); + public long count(Predicate predicate) { + Class entityClass = JpaPredicate.reflectEntityClass(predicate); + return repo(entityClass).count(predicate); } @Override - public boolean existsById(Class entityClass, Object id) { - return repo(entityClass).existsById(id); + public boolean exists(Predicate predicate) { + Class entityClass = JpaPredicate.reflectEntityClass(predicate); + return repo(entityClass).exists(predicate); } } From 20640bf26b48291fbfd32d7584505b99861e3ff9 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 11 Sep 2024 14:19:55 +0800 Subject: [PATCH 20/62] =?UTF-8?q?@AutoNotify=20@AutoRequest=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81@Repeatable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenEntityMojo.java | 2 +- cap4j-ddd-codegen-template.json | 17 ++++- .../event/annotation/AutoNotify.java | 6 +- .../event/annotation/AutoNotifys.java | 16 +++++ .../event/annotation/AutoRequest.java | 7 +- .../event/annotation/AutoRequests.java | 13 ++++ .../impl/DefaultEventSubscriberManager.java | 68 +++++++++++++------ .../cap4j/ddd/share/misc/ClassUtils.java | 42 ++++++++++-- 8 files changed, 131 insertions(+), 40 deletions(-) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotifys.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequests.java diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index e589075..da53861 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -1261,7 +1261,7 @@ public void writeDomainEventSourceFile(Map table, String domainE out.close(); } - String subscriberPackage = basePackage + ".application.subscribers"; + String subscriberPackage = basePackage + ".application.subscribers.domain"; filePath = SourceFileUtils.resolveSourceFile(baseDir, subscriberPackage, domainEventClassName + "Subscriber"); if (!FileUtils.fileExists(filePath)) { diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index afc5764..f778198 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -112,7 +112,7 @@ "type": "file", "name": "${IntegrationEvent}Subscriber.java", "format": "raw", - "data": "package ${basePackage}.application.subscribers;\n\nimport ${basePackage}.application.distributed.events.${IntegrationEvent};\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 集成事件描述\n */\n@Service\n@RequiredArgsConstructor\npublic class ${IntegrationEvent}Subscriber {\n\n @EventListener(${IntegrationEvent}.class)\n public void on(${IntegrationEvent} event) {\n \n }\n\n}\n" + "data": "package ${basePackage}.application.subscribers.integration;\n\nimport ${basePackage}.application.distributed.events.${IntegrationEvent};\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 集成事件描述\n */\n@Service\n@RequiredArgsConstructor\npublic class ${IntegrationEvent}Subscriber {\n\n @EventListener(${IntegrationEvent}.class)\n public void on(${IntegrationEvent} event) {\n \n }\n\n}\n" } ] }, @@ -138,7 +138,7 @@ "type": "file", "name": "${DomainEvent}Subscriber.java", "format": "raw", - "data": "package ${basePackage}.application.subscribers;\n\nimport ${basePackage}.domain.aggregates${package}.events.${DomainEvent};\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 领域事件订阅说明\n */\n@Service\n@RequiredArgsConstructor\npublic class ${DomainEvent}Subscriber {\n\n @EventListener(${DomainEvent}.class)\n public void on(${DomainEvent} event) {\n \n }\n\n}\n" + "data": "package ${basePackage}.application.subscribers.domain;\n\nimport ${basePackage}.domain.aggregates${package}.events.${DomainEvent};\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 领域事件订阅说明\n */\n@Service\n@RequiredArgsConstructor\npublic class ${DomainEvent}Subscriber {\n\n @EventListener(${DomainEvent}.class)\n public void on(${DomainEvent} event) {\n \n }\n\n}\n" } ] }, @@ -524,7 +524,18 @@ { "type": "dir", "name": "subscribers", - "tag": "domain_event_handler,integration_event_handler" + "children": [ + { + "type": "dir", + "name": "domain", + "tag": "domain_event_handler" + }, + { + "type": "dir", + "name": "integration", + "tag": "integration_event_handler" + } + ] } ] }, diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java index 25c93d7..629dc20 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java @@ -1,9 +1,6 @@ package org.netcorepal.cap4j.ddd.application.event.annotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.lang.annotation.*; /** * 自动发布 @@ -14,6 +11,7 @@ */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) +@Repeatable(AutoNotifys.class) public @interface AutoNotify { /** diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotifys.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotifys.java new file mode 100644 index 0000000..a5f6b4d --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotifys.java @@ -0,0 +1,16 @@ +package org.netcorepal.cap4j.ddd.application.event.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author binking338 + * @date 2024/9/11 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AutoNotifys { + AutoNotify[] value(); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequest.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequest.java index 9f170e5..fee2858 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequest.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequest.java @@ -1,9 +1,6 @@ package org.netcorepal.cap4j.ddd.application.event.annotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.lang.annotation.*; /** * 自动触发请求 @@ -14,8 +11,8 @@ */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) +@Repeatable(AutoRequests.class) public @interface AutoRequest { - /** * 目标请求 * diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequests.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequests.java new file mode 100644 index 0000000..c53e458 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRequests.java @@ -0,0 +1,13 @@ +package org.netcorepal.cap4j.ddd.application.event.annotation; + +import java.lang.annotation.*; + +/** + * @author binking338 + * @date 2024/9/11 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AutoRequests { + AutoRequest[] value(); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java index c3ac567..75aca0f 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java @@ -3,11 +3,13 @@ import lombok.RequiredArgsConstructor; import org.netcorepal.cap4j.ddd.application.RequestParam; import org.netcorepal.cap4j.ddd.application.RequestSupervisor; -import org.netcorepal.cap4j.ddd.domain.event.EventSubscriber; -import org.netcorepal.cap4j.ddd.domain.event.EventSubscriberManager; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventSupervisor; import org.netcorepal.cap4j.ddd.application.event.annotation.AutoNotify; +import org.netcorepal.cap4j.ddd.application.event.annotation.AutoNotifys; import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRequest; +import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRequests; +import org.netcorepal.cap4j.ddd.domain.event.EventSubscriber; +import org.netcorepal.cap4j.ddd.domain.event.EventSubscriberManager; import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; import org.netcorepal.cap4j.ddd.share.misc.ScanUtils; import org.springframework.context.ApplicationEventPublisher; @@ -16,6 +18,7 @@ import org.springframework.core.convert.converter.Converter; import java.time.Duration; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -57,14 +60,23 @@ public void init() { // 自动实现 Event -> Request // 领域事件转Request容易导致数据库长时事务,慎用! - if( null != domainEventClass.getAnnotation(AutoRequest.class)){ - AutoRequest autoRequest = domainEventClass.getAnnotation(AutoRequest.class); + List autoRequests = null; + if (null != domainEventClass.getAnnotation(AutoRequest.class)) { + autoRequests = Arrays.asList(domainEventClass.getAnnotation(AutoRequest.class)); + } + if (null != domainEventClass.getAnnotation(AutoRequests.class)) { + autoRequests = Arrays.asList(domainEventClass.getAnnotation(AutoRequests.class).value()); + } + if (null == autoRequests) { + return; + } + for (AutoRequest autoRequest : autoRequests) { Class converterClass = null; if (Converter.class.isAssignableFrom(autoRequest.converterClass())) { converterClass = autoRequest.converterClass(); } Converter converter = ClassUtils.newConverterInstance(domainEventClass, autoRequest.targetRequestClass(), converterClass); - subscribe(domainEventClass, domainEvent -> RequestSupervisor.getInstance().send((RequestParam)converter.convert(domainEvent))); + subscribe(domainEventClass, domainEvent -> RequestSupervisor.getInstance().send((RequestParam) converter.convert(domainEvent))); } }); // 集成事件 @@ -72,28 +84,44 @@ public void init() { subscribe(integrationEventClass, applicationEventPublisher::publishEvent); // 自动实现 DomainEvent -> IntegrationEvent 适配 + List autoNotifys = null; if (null != integrationEventClass.getAnnotation(AutoNotify.class)) { - AutoNotify autoNotify = integrationEventClass.getAnnotation(AutoNotify.class); - Class converterClass = null; - if (Converter.class.isAssignableFrom(integrationEventClass)) { - converterClass = integrationEventClass; - } - if (Converter.class.isAssignableFrom(autoNotify.converterClass())) { - converterClass = autoNotify.converterClass(); + autoNotifys = Arrays.asList(integrationEventClass.getAnnotation(AutoNotify.class)); + } + if (null != integrationEventClass.getAnnotation(AutoNotifys.class)) { + autoNotifys = Arrays.asList(integrationEventClass.getAnnotation(AutoNotifys.class).value()); + } + if (null != autoNotifys) { + for (AutoNotify autoNotify : autoNotifys) { + Class converterClass = null; + if (Converter.class.isAssignableFrom(integrationEventClass)) { + converterClass = integrationEventClass; + } + if (Converter.class.isAssignableFrom(autoNotify.converterClass())) { + converterClass = autoNotify.converterClass(); + } + Converter converter = ClassUtils.newConverterInstance(autoNotify.sourceDomainEventClass(), integrationEventClass, converterClass); + subscribe(autoNotify.sourceDomainEventClass(), domainEvent -> IntegrationEventSupervisor.getInstance().notify(converter.convert(domainEvent), Duration.ofSeconds(autoNotify.delayInSeconds()))); } - Converter converter = ClassUtils.newConverterInstance(autoNotify.sourceDomainEventClass(), integrationEventClass, converterClass); - subscribe(autoNotify.sourceDomainEventClass(), domainEvent -> IntegrationEventSupervisor.getInstance().notify(converter.convert(domainEvent), Duration.ofSeconds(autoNotify.delayInSeconds()))); } // 自动实现 Event -> Request 转发 + List autoRequests = null; if (null != integrationEventClass.getAnnotation(AutoRequest.class)) { - AutoRequest autoRequest = integrationEventClass.getAnnotation(AutoRequest.class); - Class converterClass = null; - if (Converter.class.isAssignableFrom(autoRequest.converterClass())) { - converterClass = autoRequest.converterClass(); + autoRequests = Arrays.asList(integrationEventClass.getAnnotation(AutoRequest.class)); + } + if (null != integrationEventClass.getAnnotation(AutoRequests.class)) { + autoRequests = Arrays.asList(integrationEventClass.getAnnotation(AutoRequests.class).value()); + } + if (null != autoRequests) { + for (AutoRequest autoRequest : autoRequests) { + Class converterClass = null; + if (Converter.class.isAssignableFrom(autoRequest.converterClass())) { + converterClass = autoRequest.converterClass(); + } + Converter converter = ClassUtils.newConverterInstance(integrationEventClass, autoRequest.targetRequestClass(), converterClass); + subscribe(integrationEventClass, integrationEvent -> RequestSupervisor.getInstance().send((RequestParam) converter.convert(integrationEvent))); } - Converter converter = ClassUtils.newConverterInstance(integrationEventClass, autoRequest.targetRequestClass(), converterClass); - subscribe(integrationEventClass, integrationEvent -> RequestSupervisor.getInstance().send((RequestParam)converter.convert(integrationEvent))); } }); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java index 11a45b6..59375fc 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java @@ -6,13 +6,13 @@ import org.springframework.core.ResolvableType; import org.springframework.core.convert.converter.Converter; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Objects; import java.util.function.Predicate; -import java.util.stream.Stream; /** * 类型工具类 @@ -76,24 +76,52 @@ public static Method findMethod(Class clazz, String name, Predicate meth return null; } + /** + * 获取 Converter对象 + * + * @param srcClass 源类型 + * @param destClass 模板类型 + * @param converterClass 转换类 + * @return + */ @SneakyThrows - public static Converter newConverterInstance(Class srcClass, Class descClass, Class converterClass) { - Converter converter = null; + public static Converter newConverterInstance(Class srcClass, Class destClass, Class converterClass) { + Converter converter = null; try { converter = null == converterClass || Void.class.equals(converterClass) ? null - : (Converter) converterClass.newInstance(); + : (Converter) converterClass.newInstance(); } catch (Exception e) { throw new DomainException("事件Converter无法实例化", e); } + + Method method = findMethod( + destClass, + "convert", + m -> m.getParameterCount() == 1 + && srcClass.isAssignableFrom(m.getParameterTypes()[0]) + && destClass.isAssignableFrom(m.getReturnType()) + ); + if(null != method) { + return (src) ->{ + Object dest = null; + try { + dest = method.invoke(destClass.newInstance(), src); + } catch (Exception e) { + throw new RuntimeException(e); + } + return dest; + }; + } + if (converter == null) { BeanCopier copier = BeanCopier.create( srcClass, - descClass, false); + destClass, false); converter = source -> { Object dest = null; try { - dest = descClass.newInstance(); + dest = destClass.newInstance(); } catch (Exception e) { throw new DomainException("无法完成事件自动转换", e); } @@ -101,6 +129,6 @@ public static Converter newConverterInstance(Class srcClass, return dest; }; } - return converter; + return (Converter) converter; } } From 6ebc866ee686714e6a103ea920f722b593fa2a1b Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 11 Sep 2024 14:57:19 +0800 Subject: [PATCH 21/62] =?UTF-8?q?gen-arch=20gen-entity=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E6=A8=A1=E6=9D=BF=E6=A0=BC=E5=BC=8F=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenArchMojo.java | 13 +++++--- .../cap4j/ddd/codegen/GenEntityMojo.java | 6 ++-- cap4j-ddd-codegen-template.json | 32 +++++++++---------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index 9c63066..a43ad40 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -359,7 +359,7 @@ public void renderDesign(TemplateNode templateNode, String parentPath) throws IO if (designMap.containsKey("integration_event")) { for (String literalCommand : designMap.get("integration_event")) { - if(literalCommand.split(":").length >= 3){ + if (literalCommand.split(":").length >= 3) { renderAppLayerIntegrationEvent("integration_event_handler", literalCommand, parentPath, templateNode); } } @@ -536,7 +536,7 @@ public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, S context.put("path", reletivePath); context.put("package", StringUtils.isEmpty(reletivePath) ? "" : ("." + reletivePath.replace(File.separator, "."))); } - if(!context.containsKey("Val1")){ + if (!context.containsKey("Val1")) { throw new RuntimeException("缺失领域事件名称,领域事件设计格式:AggregateRootEntityName:DomainEventName"); } String Name = NamingUtils.toUpperCamelCase(context.get("Val1")); @@ -551,7 +551,7 @@ public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, S NamingUtils.getLastPackageName(context.get("Val0")) ); boolean persist = false; - if(context.containsKey("val2") && "`true`persist`1`".contains("`" + context.get("val2") + "`")){ + if (context.containsKey("val2") && "`true`persist`1`".contains("`" + context.get("val2") + "`")) { persist = true; } context.put("persist", persist ? "true" : "false"); @@ -563,8 +563,11 @@ public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, S context.put("aggregate_root", context.get("Entity")); context.put("Aggregate", context.get("package")); context.put("aggregate", context.get("package")); - - context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件描述"); + if (Objects.equals("domain_event_handler", alias4Design(templateNode.getTag()))) { + context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件订阅描述"); + } else { + context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件描述"); + } context.put("comment", context.get("Comment")); return context; }); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index da53861..d2b8c8e 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -1202,7 +1202,7 @@ public void writeSpecificationSourceFile(Map table, String baseD "import org.springframework.stereotype.Service;\n" + "\n" + "/**\n" + - " * " + simpleClassName + "的规格约束\n" + + " * " + simpleClassName + "规格约束\n" + " * todo: 规格约束描述\n" + " *\n" + " * @author cap4j-ddd-codegen\n" + @@ -1242,6 +1242,7 @@ public void writeDomainEventSourceFile(Map table, String domainE "import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent;\n" + "\n" + "/**\n" + + " * " + simpleClassName + "领域事件\n" + " * " + domainEventDescription + "\n" + " *\n" + " * @author cap4j-ddd-codegen\n" + @@ -1276,7 +1277,8 @@ public void writeDomainEventSourceFile(Map table, String domainE "import org.springframework.stereotype.Service;\n" + "\n" + "/**\n" + - " * todo: 领域事件说明\n" + + " * " + simpleClassName + "领域事件订阅\n" + + " * todo: 领域事件订阅描述\n" + " */\n" + "@Service\n" + "@RequiredArgsConstructor\n" + diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index f778198..3316ad2 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -10,19 +10,19 @@ "type": "file", "name": "${Command}Request.java", "format": "raw", - "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n/**\n * todo: 命令请求参数描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\npublic class ${Command}Request implements RequestParam<${ReturnType}> {\n\n}" + "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n/**\n * ${Command}命令请求参数\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\npublic class ${Command}Request implements RequestParam<${ReturnType}> {\n\n}" }, { "type": "file", "name": "${Command}Response.java", "format": "raw", - "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * todo: 命令响应描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Command}Response {\n boolean success;\n}\n" + "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * ${Command}命令响应\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Command}Response {\n boolean success;\n}\n" }, { "type": "file", "name": "${Command}Handler.java", "format": "raw", - "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.netcorepal.cap4j.ddd.Mediator;\nimport org.netcorepal.cap4j.ddd.application.command.Command;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 命令处理器描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class ${Command}Handler implements Command<${Command}Request, ${Command}Response> {\n\n @Override\n public ${Command}Response exec(${Command}Request cmd) {\n Mediator.uow().save();\n \n return null;\n }\n}\n" + "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.netcorepal.cap4j.ddd.Mediator;\nimport org.netcorepal.cap4j.ddd.application.command.Command;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${Command}命令请求实现\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class ${Command}Handler implements Command<${Command}Request, ${Command}Response> {\n\n @Override\n public ${Command}Response exec(${Command}Request cmd) {\n Mediator.uow().save();\n \n return null;\n }\n}\n" } ] }, @@ -35,13 +35,13 @@ "type": "file", "name": "${Query}Request.java", "format": "raw", - "data": "package ${basePackage}.application.queries${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n\n/**\n * todo: 查询请求参数描述\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Query}Request implements RequestParam<${Query}Response> {\n Long id;\n}" + "data": "package ${basePackage}.application.queries${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n\n/**\n * ${Query}查询请求参数\n * ${Comment}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Query}Request implements RequestParam<${Query}Response> {\n Long id;\n}" }, { "type": "file", "name": "${Query}Response.java", "format": "raw", - "data": "package ${basePackage}.application.queries${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * todo: 查询响应描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Query}Response {\n Long id;\n}\n" + "data": "package ${basePackage}.application.queries${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * ${Query}查询响应\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Query}Response {\n Long id;\n}\n" } ] }, @@ -54,7 +54,7 @@ "type": "file", "name": "${Query}Handler.java", "format": "raw", - "data": "package ${basePackage}.adapter.application.queries;\n\nimport ${basePackage}.application.queries${package}.${Query}Request;\nimport ${basePackage}.application.queries${package}.${Query}Response;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.netcorepal.cap4j.ddd.application.query.Query;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 查询处理器描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class ${Query}Handler implements Query<${Query}Request, ${Query}Response> {\n \n @Override\n public ${Query}Response exec(${Query}Request request) {\n // mybatis / jpa 哪个顺手就用哪个吧!\n return null;\n }\n}\n" + "data": "package ${basePackage}.adapter.application.queries;\n\nimport ${basePackage}.application.queries${package}.${Query}Request;\nimport ${basePackage}.application.queries${package}.${Query}Response;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.netcorepal.cap4j.ddd.application.query.Query;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${Query}查询请求适配实现\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class ${Query}Handler implements Query<${Query}Request, ${Query}Response> {\n \n @Override\n public ${Query}Response exec(${Query}Request request) {\n // mybatis / jpa 哪个顺手就用哪个吧!\n return null;\n }\n}\n" } ] }, @@ -67,13 +67,13 @@ "type": "file", "name": "${Client}Request.java", "format": "raw", - "data": "package ${basePackage}.application.distributed.clients;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n/**\n * todo: 防腐端请求参数描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Client}Request implements RequestParam<${Client}Response> {\n Long id;\n}\n" + "data": "package ${basePackage}.application.distributed.clients;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n/**\n * ${Client}防腐端请求参数\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Client}Request implements RequestParam<${Client}Response> {\n Long id;\n}\n" }, { "type": "file", "name": "${Client}Response.java", "format": "raw", - "data": "package ${basePackage}.application.distributed.clients;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * todo: 防腐端响应描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Client}Response {\n Long id;\n}\n" + "data": "package ${basePackage}.application.distributed.clients;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * ${Client}防腐端响应\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Client}Response {\n Long id;\n}\n" } ] }, @@ -86,7 +86,7 @@ "type": "file", "name": "${Client}Handler.java", "format": "raw", - "data": "package ${basePackage}.adapter.application.distributed.clients${package};\n\nimport ${basePackage}.application.distributed.clients${package}.${Client}Request;\nimport ${basePackage}.application.distributed.clients${package}.${Client}Response;\nimport org.netcorepal.cap4j.ddd.application.RequestHandler;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 防腐端描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\npublic class ${Client}Handler implements RequestHandler<${Client}Request, ${Client}Response> {\n @Override\n public ${Client}Response exec(${Client}Request ${Client}Request) {\n return null;\n }\n}\n" + "data": "package ${basePackage}.adapter.application.distributed.clients${package};\n\nimport ${basePackage}.application.distributed.clients${package}.${Client}Request;\nimport ${basePackage}.application.distributed.clients${package}.${Client}Response;\nimport org.netcorepal.cap4j.ddd.application.RequestHandler;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${Client}防腐端\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\npublic class ${Client}Handler implements RequestHandler<${Client}Request, ${Client}Response> {\n @Override\n public ${Client}Response exec(${Client}Request ${Client}Request) {\n return null;\n }\n}\n" } ] }, @@ -99,7 +99,7 @@ "type": "file", "name": "${IntegrationEvent}.java", "format": "raw", - "data": "package ${basePackage}.application.distributed.events;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent;\n\n/**\n * todo: 集成事件描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@IntegrationEvent(value = ${MQ_TOPIC}, subscriber = ${MQ_CONSUMER})\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ${IntegrationEvent} {\n private Long id;\n}\n" + "data": "package ${basePackage}.application.distributed.events;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent;\n\n/**\n * ${IntegrationEvent}集成事件\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@IntegrationEvent(value = ${MQ_TOPIC}, subscriber = ${MQ_CONSUMER})\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ${IntegrationEvent} {\n private Long id;\n}\n" } ] }, @@ -112,7 +112,7 @@ "type": "file", "name": "${IntegrationEvent}Subscriber.java", "format": "raw", - "data": "package ${basePackage}.application.subscribers.integration;\n\nimport ${basePackage}.application.distributed.events.${IntegrationEvent};\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 集成事件描述\n */\n@Service\n@RequiredArgsConstructor\npublic class ${IntegrationEvent}Subscriber {\n\n @EventListener(${IntegrationEvent}.class)\n public void on(${IntegrationEvent} event) {\n \n }\n\n}\n" + "data": "package ${basePackage}.application.subscribers.integration;\n\nimport ${basePackage}.application.distributed.events.${IntegrationEvent};\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n/**\n ${IntegrationEvent}集成事件订阅\n * ${Comment}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${IntegrationEvent}Subscriber {\n\n @EventListener(${IntegrationEvent}.class)\n public void on(${IntegrationEvent} event) {\n \n }\n\n}\n" } ] }, @@ -125,7 +125,7 @@ "type": "file", "name": "${DomainEvent}.java", "format": "raw", - "data": "package ${basePackage}.domain.aggregates${package}.events;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent;\n\n/**\n * todo: 领域事件描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@DomainEvent(persist = ${persist})\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${DomainEvent}\", type = Aggregate.TYPE_DOMAIN_EVENT, description = \"\")\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ${DomainEvent} {\n Long id;\n}\n" + "data": "package ${basePackage}.domain.aggregates${package}.events;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent;\n\n/**\n * ${Entity}.${DomainEvent}领域事件\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@DomainEvent(persist = ${persist})\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${DomainEvent}\", type = Aggregate.TYPE_DOMAIN_EVENT, description = \"\")\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class ${DomainEvent} {\n Long id;\n}\n" } ] }, @@ -138,7 +138,7 @@ "type": "file", "name": "${DomainEvent}Subscriber.java", "format": "raw", - "data": "package ${basePackage}.application.subscribers.domain;\n\nimport ${basePackage}.domain.aggregates${package}.events.${DomainEvent};\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 领域事件订阅说明\n */\n@Service\n@RequiredArgsConstructor\npublic class ${DomainEvent}Subscriber {\n\n @EventListener(${DomainEvent}.class)\n public void on(${DomainEvent} event) {\n \n }\n\n}\n" + "data": "package ${basePackage}.application.subscribers.domain;\n\nimport ${basePackage}.domain.aggregates${package}.events.${DomainEvent};\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${Entity}.${DomainEvent}领域事件订阅\n * ${Comment}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${DomainEvent}Subscriber {\n\n @EventListener(${DomainEvent}.class)\n public void on(${DomainEvent} event) {\n \n }\n\n}\n" } ] }, @@ -151,13 +151,13 @@ "type": "file", "name": "${Entity}Payload.java", "format": "raw", - "data": "package ${basePackage}.domain.aggregates${package}.factory;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport lombok.Builder;\nimport lombok.Data;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload;\n\n/**\n * todo: 工厂负载描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Payload\", type = Aggregate.TYPE_FACTORY_PAYLOAD, description = \"\")\n@Data\n@Builder\npublic class ${Entity}Payload implements AggregatePayload<${Entity}> {\n \n}\n" + "data": "package ${basePackage}.domain.aggregates${package}.factory;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport lombok.Builder;\nimport lombok.Data;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload;\n\n/**\n * ${Entity}工厂负载\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Payload\", type = Aggregate.TYPE_FACTORY_PAYLOAD, description = \"\")\n@Data\n@Builder\npublic class ${Entity}Payload implements AggregatePayload<${Entity}> {\n \n}\n" }, { "type": "file", "name": "${Entity}Factory.java", "format": "raw", - "data": "package ${basePackage}.domain.aggregates${package}.factory;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory;\nimport org.springframework.stereotype.Service;\n\n/**\n * TODO: 聚合工厂\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Factory\", type = Aggregate.TYPE_FACTORY, description = \"\")\n@Service\npublic class ${Entity}Factory implements AggregateFactory<${Entity}Payload, ${Entity}> {\n\n @Override\n public ${Entity} create(${Entity}Payload payload) {\n\n return ${Entity}.builder()\n\n .build();\n }\n}\n" + "data": "package ${basePackage}.domain.aggregates${package}.factory;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${Entity}聚合工厂\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Factory\", type = Aggregate.TYPE_FACTORY, description = \"\")\n@Service\npublic class ${Entity}Factory implements AggregateFactory<${Entity}Payload, ${Entity}> {\n\n @Override\n public ${Entity} create(${Entity}Payload payload) {\n\n return ${Entity}.builder()\n\n .build();\n }\n}\n" } ] }, @@ -170,7 +170,7 @@ "type": "file", "name": "${Entity}Specification.java", "format": "raw", - "data": "package ${basePackage}.domain.aggregates${package}.specs;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.Specification;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 实体规格约束描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Specification\", type = Aggregate.TYPE_SPECIFICATION, description = \"\")\n@Service\npublic class ${Entity}Specification implements Specification<${Entity}> {\n @Override\n public Result specify(${Entity} entity) {\n return Result.fail(\"未实现\");\n }\n}\n" + "data": "package ${basePackage}.domain.aggregates${package}.specs;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.Specification;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${Entity}规格约束\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Specification\", type = Aggregate.TYPE_SPECIFICATION, description = \"\")\n@Service\npublic class ${Entity}Specification implements Specification<${Entity}> {\n @Override\n public Result specify(${Entity} entity) {\n return Result.fail(\"未实现\");\n }\n}\n" } ] }, From 1e776368a493a3fefa6a2fc065afa54d7b87c739 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 11 Sep 2024 19:15:01 +0800 Subject: [PATCH 22/62] =?UTF-8?q?JpaPredicate=E6=94=AF=E6=8C=81=20byId=20b?= =?UTF-8?q?yIds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/domain/repo/Repository.java | 31 ++++--- .../ddd/domain/repo/RepositorySupervisor.java | 34 +++++-- .../domain/repo/AbstractJpaRepository.java | 90 +++++++++++++------ .../cap4j/ddd/domain/repo/JpaPredicate.java | 29 +++--- .../ddd/domain/repo/JpaPredicateSupport.java | 59 ++++++++++++ .../impl/DefaultRepositorySupervisor.java | 12 +-- 6 files changed, 188 insertions(+), 67 deletions(-) create mode 100644 ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicateSupport.java diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java index 5251407..edc7fa5 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Repository.java @@ -1,7 +1,7 @@ package org.netcorepal.cap4j.ddd.domain.repo; -import org.netcorepal.cap4j.ddd.share.PageData; import org.netcorepal.cap4j.ddd.share.OrderInfo; +import org.netcorepal.cap4j.ddd.share.PageData; import org.netcorepal.cap4j.ddd.share.PageParam; import java.util.List; @@ -17,31 +17,34 @@ public interface Repository { /** * 根据条件获取实体列表 + * + * @param predicate + * @return + */ + default List find(Predicate predicate) { + return find(predicate, null); + } + + /** + * 根据条件获取实体列表 + * * @param predicate * @param orders * @return */ List find(Predicate predicate, List orders); -// /** -// * 通过ID获取实体 -// * @param id -// * @return -// */ -// Optional findById(Object id); -// /** -// * 通过ID获取实体 -// * @param ids -// * @return -// */ -// List findByIds(Iterable ids); + /** * 根据条件获取实体 + * * @param predicate * @return */ Optional findOne(Predicate predicate); + /** * 根据条件获取实体分页列表 + * * @param predicate * @param pageParam * @return @@ -50,6 +53,7 @@ public interface Repository { /** * 根据条件获取实体计数 + * * @param predicate * @return */ @@ -57,6 +61,7 @@ public interface Repository { /** * 根据条件判断实体是否存在 + * * @param predicate * @return */ diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java index 8bc2143..1ec4856 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java @@ -14,40 +14,56 @@ * @date 2024/8/25 */ public interface RepositorySupervisor { - static RepositorySupervisor getInstance(){ + static RepositorySupervisor getInstance() { return RepositorySupervisorSupport.instance; } /** * 获取仓储 + * * @param entityClass 实体类型 + * @param 实体类型 * @return {@link Repository} - * @param 实体类型 */ Repository repo(Class entityClass); /** * 根据条件获取实体列表 + * * @param predicate - * @param orders + * @param * @return + */ + default List find(Predicate predicate) { + return find(predicate, null); + } + + /** + * 根据条件获取实体列表 + * + * @param predicate + * @param orders * @param + * @return */ List find(Predicate predicate, List orders); /** * 根据条件获取单个实体 + * * @param predicate - * @return * @param + * @return */ Optional findOne(Predicate predicate); + /** * 根据条件获取实体分页列表 + * * @param predicate * @param pageParam - * @return * @param + * @return */ PageData findPage(Predicate predicate, PageParam pageParam); @@ -56,24 +72,26 @@ static RepositorySupervisor getInstance(){ * * @param predicate * @param limit - * @return * @param + * @return */ List remove(Predicate predicate, int limit); /** * 根据条件获取实体计数 + * * @param predicate - * @return * @param + * @return */ long count(Predicate predicate); /** * 根据条件判断实体是否存在 + * * @param predicate - * @return * @param + * @return */ boolean exists(Predicate predicate); diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java index 54260ca..6e84a44 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java @@ -8,13 +8,13 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.function.Supplier; import java.util.stream.Collectors; /** @@ -28,28 +28,37 @@ public class AbstractJpaRepository implements Repository { private final JpaSpecificationExecutor jpaSpecificationExecutor; private final JpaRepository jpaRepository; -// public Optional findById(Object id) { -// List ids = new ArrayList<>(1); -// ids.add((ID) id); -// Optional entity = jpaRepository.findAllById(ids).stream().findFirst(); -// return entity; -// } -// -// public List findByIds(Iterable ids) { -// List entities = jpaRepository.findAllById((Iterable) ids); -// return entities; -// } -// -// public boolean existsById(Object id) { -// return jpaRepository.existsById((ID) id); -// } - public Optional findOne(Predicate predicate) { - return jpaSpecificationExecutor.findOne(JpaPredicate.resume(predicate)); + if (null != JpaPredicateSupport.resumeId(predicate)) { + return jpaRepository.findById((ID) JpaPredicateSupport.resumeId(predicate)); + } + if (null != JpaPredicateSupport.resumeIds(predicate)) { + if (!JpaPredicateSupport.resumeIds(predicate).iterator().hasNext()) { + return Optional.empty(); + } + return jpaRepository.findAllById((List) JpaPredicateSupport.resumeIds(predicate)) + .stream().findFirst(); + } + return jpaSpecificationExecutor.findOne(JpaPredicateSupport.resumeSpecification(predicate)); } public PageData findPage(Predicate predicate, PageParam pageParam) { - Page page = jpaSpecificationExecutor.findAll(JpaPredicate.resume(predicate), convertPageable(pageParam)); + if (null != JpaPredicateSupport.resumeId(predicate)) { + List entities = jpaRepository.findAllById(Arrays.asList((ID) JpaPredicateSupport.resumeId(predicate))); + return PageData.create(pageParam, Long.valueOf(entities.size()), entities); + } + if (null != JpaPredicateSupport.resumeIds(predicate)) { + if (!JpaPredicateSupport.resumeIds(predicate).iterator().hasNext()) { + return PageData.empty(pageParam.getPageSize(), JpaPredicateSupport.reflectEntityClass(predicate)); + } + List entities = jpaRepository.findAllById((List) JpaPredicateSupport.resumeIds(predicate)) + .stream() + .skip((pageParam.getPageNum() - 1) * pageParam.getPageSize()) + .limit(pageParam.getPageSize()) + .collect(Collectors.toList()); + return PageData.create(pageParam, Long.valueOf(entities.size()), entities); + } + Page page = jpaSpecificationExecutor.findAll(JpaPredicateSupport.resumeSpecification(predicate), convertPageable(pageParam)); return convertPageData(page); } @@ -58,17 +67,48 @@ public List find(Predicate predicate, List orders) { if (orders != null && !orders.isEmpty()) { sort = convertSort(orders); } - List entities = jpaSpecificationExecutor.findAll(JpaPredicate.resume(predicate), sort); + if (null != JpaPredicateSupport.resumeId(predicate)) { + return jpaRepository.findAllById(Arrays.asList((ID) JpaPredicateSupport.resumeId(predicate))); + } + if (null != JpaPredicateSupport.resumeIds(predicate)) { + if (!JpaPredicateSupport.resumeIds(predicate).iterator().hasNext()) { + return Collections.emptyList(); + } + return jpaRepository.findAllById((List) JpaPredicateSupport.resumeIds(predicate)); + } + List entities = jpaSpecificationExecutor.findAll(JpaPredicateSupport.resumeSpecification(predicate), sort); return entities; } - public long count(Predicate condition) { - long result = jpaSpecificationExecutor.count((org.springframework.data.jpa.domain.Specification) condition); + public long count(Predicate predicate) { + if (null != JpaPredicateSupport.resumeId(predicate)) { + return jpaRepository.findById((ID) JpaPredicateSupport.resumeId(predicate)).isPresent() ? + 1L : + 0L; + } + if (null != JpaPredicateSupport.resumeIds(predicate)) { + if (!JpaPredicateSupport.resumeIds(predicate).iterator().hasNext()) { + return 0L; + } + return jpaRepository.findAllById((List) JpaPredicateSupport.resumeIds(predicate)) + .size(); + } + long result = jpaSpecificationExecutor.count(JpaPredicateSupport.resumeSpecification(predicate)); return result; } - public boolean exists(Predicate condition) { - boolean result = jpaSpecificationExecutor.exists((org.springframework.data.jpa.domain.Specification) condition); + public boolean exists(Predicate predicate) { + if (null != JpaPredicateSupport.resumeId(predicate)) { + return jpaRepository.findById((ID) JpaPredicateSupport.resumeId(predicate)).isPresent(); + } + if (null != JpaPredicateSupport.resumeIds(predicate)) { + if (!JpaPredicateSupport.resumeIds(predicate).iterator().hasNext()) { + return false; + } + return jpaRepository.findAllById((List) JpaPredicateSupport.resumeIds(predicate)) + .size() > 0; + } + boolean result = jpaSpecificationExecutor.exists(JpaPredicateSupport.resumeSpecification(predicate)); return result; } diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java index e03f3c1..9e00675 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java @@ -1,9 +1,10 @@ package org.netcorepal.cap4j.ddd.domain.repo; -import org.netcorepal.cap4j.ddd.share.misc.ClassUtils; +import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.domain.Specification; -import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; /** * Jpa仓储检索断言 @@ -11,24 +12,22 @@ * @author binking338 * @date 2024/9/8 */ +@RequiredArgsConstructor public class JpaPredicate implements Predicate { - private Class entityClass; - private Specification spec; + final Class entityClass; + final Specification spec; + final Object id; + final Iterable ids; - protected JpaPredicate(Class entityClass, Specification spec) { - this.entityClass = entityClass; - this.spec = spec; + public static Predicate byId(Class entityClass, Object id) { + return new JpaPredicate<>(entityClass, null, id, null); } - public static Predicate from(Class entityClass, Specification specification) { - return new JpaPredicate<>(entityClass, specification); + public static Predicate byIds(Class entityClass, Iterable ids) { + return new JpaPredicate<>(entityClass, null, null, ids); } - public static Specification resume(Predicate predicate){ - return ((JpaPredicate) predicate).spec; - } - - public static Class reflectEntityClass(Predicate predicate) { - return ((JpaPredicate) predicate).entityClass; + public static Predicate bySpecification(Class entityClass, Specification specification) { + return new JpaPredicate<>(entityClass, specification, null, null); } } diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicateSupport.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicateSupport.java new file mode 100644 index 0000000..ead284d --- /dev/null +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicateSupport.java @@ -0,0 +1,59 @@ +package org.netcorepal.cap4j.ddd.domain.repo; + +import org.springframework.data.jpa.domain.Specification; + +import java.util.Collection; +import java.util.List; + +/** + * 断言Support + * + * @author binking338 + * @date 2024/9/11 + */ +public class JpaPredicateSupport { + + /** + * 复原ID + * + * @param predicate + * @return + * @param + */ + public static Object resumeId(Predicate predicate){ + return ((JpaPredicate) predicate).id; + } + + /** + * 复原IDS + * + * @param predicate + * @return + * @param + */ + public static Iterable resumeIds(Predicate predicate){ + return ((JpaPredicate) predicate).ids; + } + + /** + * 复原Specification + * + * @param predicate + * @param + * @return + */ + public static Specification resumeSpecification(Predicate predicate) { + return ((JpaPredicate) predicate).spec; + } + + /** + * 获取断言实体类型 + * + * @param predicate + * @param + * @return + */ + public static Class reflectEntityClass(Predicate predicate) { + return ((JpaPredicate) predicate).entityClass; + } +} diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java index 7dba219..e9d4b43 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java @@ -57,7 +57,7 @@ public Repository repo(Class entityClass) { @Override public List find(Predicate predicate, List orders) { - Class entityClass = JpaPredicate.reflectEntityClass(predicate); + Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); List entities = repo(entityClass).find(predicate, orders); if (entities != null) { entities.forEach(unitOfWork::persist); @@ -67,7 +67,7 @@ public List find(Predicate predicate, List o @Override public Optional findOne(Predicate predicate) { - Class entityClass = JpaPredicate.reflectEntityClass(predicate); + Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); Optional entity = repo(entityClass).findOne(predicate); entity.ifPresent(unitOfWork::persist); return entity; @@ -75,7 +75,7 @@ public Optional findOne(Predicate predicate) { @Override public PageData findPage(Predicate predicate, PageParam pageParam) { - Class entityClass = JpaPredicate.reflectEntityClass(predicate); + Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); PageData page = repo(entityClass).findPage(predicate, pageParam); if (page.getList() != null) { page.getList().forEach(unitOfWork::persist); @@ -85,7 +85,7 @@ public PageData findPage(Predicate predicate, PageParam @Override public List remove(Predicate predicate, int limit) { - Class entityClass = JpaPredicate.reflectEntityClass(predicate); + Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); PageParam page = new PageParam(); page.setPageNum(1); page.setPageSize(limit); @@ -98,13 +98,13 @@ public List remove(Predicate predicate, int limit) { @Override public long count(Predicate predicate) { - Class entityClass = JpaPredicate.reflectEntityClass(predicate); + Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); return repo(entityClass).count(predicate); } @Override public boolean exists(Predicate predicate) { - Class entityClass = JpaPredicate.reflectEntityClass(predicate); + Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); return repo(entityClass).exists(predicate); } } From 4c1f1856c8be02f512fdec7bfcdefad3a98ed233 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 11 Sep 2024 19:16:13 +0800 Subject: [PATCH 23/62] =?UTF-8?q?=E5=88=86=E9=A1=B5=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0fluent=E9=A3=8E=E6=A0=BC=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../netcorepal/cap4j/ddd/share/OrderInfo.java | 30 ++++++- .../netcorepal/cap4j/ddd/share/PageParam.java | 78 ++++++++++++++++++- 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/OrderInfo.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/OrderInfo.java index acc6e26..16bb50e 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/OrderInfo.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/OrderInfo.java @@ -18,13 +18,39 @@ public class OrderInfo { /** * 排序字段 */ - @Schema(description="排序字段") + @Schema(description = "排序字段") @ApiModelProperty(value = "排序字段") String field; /** * 是否降序 */ - @Schema(description="是否降序") + @Schema(description = "是否降序") @ApiModelProperty(value = "是否降序") Boolean desc; + + /** + * 降序 + * + * @param field + * @return + */ + public static OrderInfo desc(String field) { + OrderInfo orderInfo = new OrderInfo(); + orderInfo.field = field; + orderInfo.desc = true; + return orderInfo; + } + + /** + * 升序 + * + * @param field + * @return + */ + public static OrderInfo asc(String field) { + OrderInfo orderInfo = new OrderInfo(); + orderInfo.field = field; + orderInfo.desc = false; + return orderInfo; + } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/PageParam.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/PageParam.java index c6756ad..3bb8043 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/PageParam.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/PageParam.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.util.ArrayList; import java.util.List; /** @@ -20,21 +21,92 @@ public class PageParam { /** * 页码 */ - @Schema(description="页码") + @Schema(description = "页码") @ApiModelProperty(value = "页码") private Integer pageNum; /** * 页大小 */ - @Schema(description="页大小") + @Schema(description = "页大小") @ApiModelProperty(value = "页大小") private Integer pageSize; /** * 排序 */ - @Schema(description="排序") + @Schema(description = "排序") @ApiModelProperty(value = "排序") private List sort; + + /** + * 添加排序字段 + * + * @param field + * @param desc + * @return + */ + public PageParam orderBy(String field, boolean desc) { + if (null == this.sort) { + this.sort = new ArrayList<>(); + } + this.sort.add(desc ? OrderInfo.desc(field) : OrderInfo.asc(field)); + return this; + } + + /** + * 添加排序字段 + * + * @param field + * @return + */ + public PageParam orderByDesc(String field) { + return orderBy(field, true); + } + + /** + * 添加排序字段 + * + * @param field + * @return + */ + public PageParam orderByAsc(String field) { + return orderBy(field, false); + } + + /** + * 重置排序字段 + * + * @return + */ + public PageParam orderReset() { + if (null != this.sort) { + this.sort.clear(); + } + return this; + } + + /** + * 创建分页参数 + * + * @param pageNum + * @param pageSize + * @return + */ + public static PageParam of(int pageNum, int pageSize) { + PageParam pageParam = new PageParam(); + pageParam.pageNum = pageNum; + pageParam.pageSize = pageSize; + return pageParam; + } + + /** + * 创建分页参数,pageNum=1 + * + * @param pageSize + * @return + */ + public static PageParam limit(int pageSize) { + return of(1, pageSize); + } } From 7311bce0a76b6e8019dc8359bcd4947ad5348299 Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 12 Sep 2024 01:48:30 +0800 Subject: [PATCH 24/62] =?UTF-8?q?=E9=9B=86=E6=88=90=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E9=99=84=E5=8A=A0=E6=8C=81=E4=B9=85=E5=8C=96=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/RocketMqEventAutoConfiguration.java | 3 +- .../event/DomainEventAutoConfiguration.java | 5 +- .../repo/JpaRepositoryAutoConfiguration.java | 7 +- .../web/ClearDomainContextInterceptor.java | 2 + ...entAttachedTransactionCommittedEvent.java} | 7 +- .../event/IntegrationEventInterceptor.java | 14 +- .../event/IntegrationEventManager.java | 15 ++ .../event/IntegrationEventSupervisor.java | 41 ++++-- .../IntegrationEventSupervisorSupport.java | 10 ++ .../{AutoNotify.java => AutoRelease.java} | 4 +- .../{AutoNotifys.java => AutoReleases.java} | 4 +- .../DefaultIntegrationEventSupervisor.java | 132 ++++++++++++++---- ...entAttachedTransactionCommittedEvent.java} | 8 +- ...ntAttachedTransactionCommittingEvent.java} | 8 +- .../domain/event/DomainEventInterceptor.java | 2 - .../ddd/domain/event/DomainEventManager.java | 18 +++ .../domain/event/DomainEventSupervisor.java | 50 +++---- .../event/DomainEventSupervisorSupport.java | 9 ++ .../event}/EventInterceptor.java | 2 +- .../impl/DefaultDomainEventSupervisor.java | 106 ++++---------- .../event/impl/DefaultEventPublisher.java | 9 +- .../impl/DefaultEventSubscriberManager.java | 29 ++-- .../impl/DefaultPersistListenerManager.java | 4 +- .../cap4j/ddd/impl/DefaultMediator.java | 14 +- .../ddd/application/impl/JpaUnitOfWork.java | 9 +- 25 files changed, 302 insertions(+), 210 deletions(-) rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/{IntegrationEventNotifyEvent.java => IntegrationEventAttachedTransactionCommittedEvent.java} (70%) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventManager.java rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/{AutoNotify.java => AutoRelease.java} (92%) rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/{AutoNotifys.java => AutoReleases.java} (85%) rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/{DomainEventAttachedTransactionPreCommitEvent.java => DomainEventAttachedTransactionCommittedEvent.java} (76%) rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/{DomainEventAttachedTransactionPostCommitEvent.java => DomainEventAttachedTransactionCommittingEvent.java} (76%) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventManager.java rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/{share => domain/event}/EventInterceptor.java (93%) diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java index b810870..d6ab5bf 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java @@ -49,7 +49,8 @@ public DefaultIntegrationEventSupervisor defaultIntegrationEventSupervisor( svcName ); - IntegrationEventSupervisorSupport.configure(defaultIntegrationEventSupervisor); + IntegrationEventSupervisorSupport.configure((IntegrationEventSupervisor) defaultIntegrationEventSupervisor); + IntegrationEventSupervisorSupport.configure((IntegrationEventManager) defaultIntegrationEventSupervisor); return defaultIntegrationEventSupervisor; } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java index 47629a0..f5f2432 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java @@ -156,7 +156,7 @@ public EventSubscriberManager defaultEventSubscriberManager( @Bean @Primary - public DomainEventSupervisor defaultDomainEventSupervisor( + public DefaultDomainEventSupervisor defaultDomainEventSupervisor( EventRecordRepository eventRecordRepository, List domainEventInterceptors, EventPublisher eventPublisher, @@ -172,7 +172,8 @@ public DomainEventSupervisor defaultDomainEventSupervisor( svcName ); - DomainEventSupervisorSupport.configure(defaultDomainEventSupervisor); + DomainEventSupervisorSupport.configure((DomainEventSupervisor)defaultDomainEventSupervisor); + DomainEventSupervisorSupport.configure((DomainEventManager) defaultDomainEventSupervisor); return defaultDomainEventSupervisor; } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java index 168113c..0931bcf 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.netcorepal.cap4j.ddd.application.UnitOfWork; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventManager; import org.netcorepal.cap4j.ddd.application.impl.JpaUnitOfWork; import org.netcorepal.cap4j.ddd.application.UnitOfWorkSupport; import org.netcorepal.cap4j.ddd.domain.aggregate.*; @@ -63,14 +64,16 @@ public SpecificationManager defaultSpecificationManager(List> s @Bean @Primary public JpaUnitOfWork jpaUnitOfWork( - DomainEventSupervisor domainEventSupervisor, + DomainEventManager domainEventManager, + IntegrationEventManager integrationEventManager, ApplicationEventPublisher applicationEventPublisher, SpecificationManager specificationManager, PersistListenerManager persistListenerManager, JpaUnitOfWorkProperties jpaUnitOfWorkProperties ) { JpaUnitOfWork unitOfWork = new JpaUnitOfWork( - domainEventSupervisor, + domainEventManager, + integrationEventManager, specificationManager, persistListenerManager, applicationEventPublisher, diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/web/ClearDomainContextInterceptor.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/web/ClearDomainContextInterceptor.java index 3163917..9c21ea7 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/web/ClearDomainContextInterceptor.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/web/ClearDomainContextInterceptor.java @@ -1,6 +1,7 @@ package org.netcorepal.cap4j.ddd.domain.web; import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.event.impl.DefaultIntegrationEventSupervisor; import org.netcorepal.cap4j.ddd.application.impl.JpaUnitOfWork; import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; @@ -25,5 +26,6 @@ public class ClearDomainContextInterceptor implements HandlerInterceptor { public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { JpaUnitOfWork.reset(); DefaultDomainEventSupervisor.reset(); + DefaultIntegrationEventSupervisor.reset(); } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventNotifyEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventAttachedTransactionCommittedEvent.java similarity index 70% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventNotifyEvent.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventAttachedTransactionCommittedEvent.java index ffb6828..28c5608 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventNotifyEvent.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventAttachedTransactionCommittedEvent.java @@ -7,12 +7,12 @@ import java.util.List; /** - * 集成事件通知中 + * 集成事件所在事务成功提交事件 * * @author binking338 * @date 2024/8/28 */ -public class IntegrationEventNotifyEvent extends ApplicationEvent { +public class IntegrationEventAttachedTransactionCommittedEvent extends ApplicationEvent { @Getter List events; @@ -22,9 +22,8 @@ public class IntegrationEventNotifyEvent extends ApplicationEvent { * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ - public IntegrationEventNotifyEvent(Object source, List events) { + public IntegrationEventAttachedTransactionCommittedEvent(Object source, List events) { super(source); this.events = events; } } - diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptor.java index f224055..6c79ac0 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptor.java @@ -1,6 +1,6 @@ package org.netcorepal.cap4j.ddd.application.event; -import org.netcorepal.cap4j.ddd.share.EventInterceptor; +import org.netcorepal.cap4j.ddd.domain.event.EventInterceptor; import java.time.LocalDateTime; @@ -11,12 +11,16 @@ * @date 2024/8/29 */ public interface IntegrationEventInterceptor extends EventInterceptor { - /** - * 调用通知时 - * + * 附加 * @param eventPayload * @param schedule */ - void onNotify(Object eventPayload, LocalDateTime schedule); + void onAttach(Object eventPayload, LocalDateTime schedule); + + /** + * 解除附加 + * @param eventPayload + */ + void onDetach(Object eventPayload); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventManager.java new file mode 100644 index 0000000..6a9dde9 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventManager.java @@ -0,0 +1,15 @@ +package org.netcorepal.cap4j.ddd.application.event; + +/** + * todo: 类描述 + * + * @author binking338 + * @date 2024/9/11 + */ +public interface IntegrationEventManager { + + /** + * 发布附加到持久化上下文的所有集成事件 + */ + void release(); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisor.java index 5509436..f67e38b 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisor.java @@ -15,29 +15,42 @@ static IntegrationEventSupervisor getInstance() { return IntegrationEventSupervisorSupport.instance; } + static IntegrationEventManager getManager(){ + return IntegrationEventSupervisorSupport.manager; + } + + /** - * 通知集成事件 + * 附加事件 * - * @param integrationEventPayload 集成事件消息体 - * @param 事件消息类型 + * @param eventPayload 事件消息体 */ - void notify(INTEGRATION_EVENT integrationEventPayload); + default void attach(EVENT eventPayload) { + attach(eventPayload, LocalDateTime.now()); + } + + /** + * 附加事件到持久化上下文 + * + * @param eventPayload 事件消息体 + * @param delay 延迟发送 + */ + default void attach(EVENT eventPayload, Duration delay){ + attach(eventPayload, LocalDateTime.now().plus(delay)); + } /** - * 延迟通知集成事件 + * 附加事件到持久化上下文 * - * @param integrationEventPayload 集成事件消息体 - * @param delay 延迟时长 - * @param 事件消息类型 + * @param eventPayload 事件消息体 + * @param schedule 指定时间发送 */ - void notify(INTEGRATION_EVENT integrationEventPayload, Duration delay); + void attach(EVENT eventPayload, LocalDateTime schedule); /** - * 定时通知集成事件 + * 从持久化上下文剥离事件 * - * @param integrationEventPayload 集成事件消息体 - * @param schedule 定时时间 - * @param 事件消息类型 + * @param eventPayload 事件消息体 */ - void notify(INTEGRATION_EVENT integrationEventPayload, LocalDateTime schedule); + void detach(EVENT eventPayload); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisorSupport.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisorSupport.java index f256f76..f09a6d3 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisorSupport.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisorSupport.java @@ -9,6 +9,8 @@ public class IntegrationEventSupervisorSupport { static IntegrationEventSupervisor instance = null; + static IntegrationEventManager manager = null; + /** * 配置事件管理器 @@ -18,4 +20,12 @@ public class IntegrationEventSupervisorSupport { public static void configure(IntegrationEventSupervisor integrationEventSupervisor) { IntegrationEventSupervisorSupport.instance = integrationEventSupervisor; } + /** + * 配置事件管理器 + * + * @param integrationEventManager {@link IntegrationEventManager} + */ + public static void configure(IntegrationEventManager integrationEventManager) { + IntegrationEventSupervisorSupport.manager = integrationEventManager; + } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRelease.java similarity index 92% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRelease.java index 629dc20..edb7e8a 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotify.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoRelease.java @@ -11,8 +11,8 @@ */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -@Repeatable(AutoNotifys.class) -public @interface AutoNotify { +@Repeatable(AutoReleases.class) +public @interface AutoRelease { /** * 源领域事件类型 diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotifys.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoReleases.java similarity index 85% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotifys.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoReleases.java index a5f6b4d..4fc7d1b 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoNotifys.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/annotation/AutoReleases.java @@ -11,6 +11,6 @@ */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -public @interface AutoNotifys { - AutoNotify[] value(); +public @interface AutoReleases { + AutoRelease[] value(); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java index f41c6ce..7698f22 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java @@ -1,24 +1,23 @@ package org.netcorepal.cap4j.ddd.application.event.impl; import lombok.RequiredArgsConstructor; -import org.netcorepal.cap4j.ddd.application.event.IntegrationEventNotifyEvent; -import org.netcorepal.cap4j.ddd.domain.event.EventPublisher; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventAttachedTransactionCommittedEvent; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventManager; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventSupervisor; import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; +import org.netcorepal.cap4j.ddd.domain.event.EventPublisher; import org.netcorepal.cap4j.ddd.domain.event.EventRecord; import org.netcorepal.cap4j.ddd.domain.event.EventRecordRepository; import org.netcorepal.cap4j.ddd.share.DomainException; import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.Ordered; import org.springframework.core.annotation.OrderUtils; +import org.springframework.transaction.event.TransactionalEventListener; import java.time.Duration; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; +import java.util.*; /** * 默认事件管理器 @@ -27,13 +26,17 @@ * @date 2024/8/28 */ @RequiredArgsConstructor -public class DefaultIntegrationEventSupervisor implements IntegrationEventSupervisor { +public class DefaultIntegrationEventSupervisor implements IntegrationEventSupervisor, IntegrationEventManager { private final EventPublisher eventPublisher; private final EventRecordRepository eventRecordRepository; private final List integrationEventInterceptors; private final ApplicationEventPublisher applicationEventPublisher; private final String svcName; + private static final ThreadLocal> TL_EVENT_PAYLOADS = new ThreadLocal>(); + private static final ThreadLocal> TL_EVENT_SCHEDULE_MAP = new ThreadLocal>(); + private static final Set EMPTY_EVENT_PAYLOADS = Collections.emptySet(); + private List sortedIntegrationEventInterceptors = null; /** @@ -53,42 +56,113 @@ protected List getOrderedIntegrationEventIntercepto * 默认事件过期时间(分钟) * 一天 60*24 = 1440 */ - private static final int DEFAULT_IntegrationEvent_EXPIRE_MINUTES = 1440; + private static final int DEFAULT_EVENT_EXPIRE_MINUTES = 1440; /** * 默认事件重试次数 */ - private static final int DEFAULT_IntegrationEvent_RETRY_TIMES = 200; + private static final int DEFAULT_EVENT_RETRY_TIMES = 200; @Override - public void notify(INTEGRATION_EVENT eventPayload, LocalDateTime schedule) { + public void attach(EVENT eventPayload, LocalDateTime schedule) { // 判断集成事件,仅支持集成事件。 - if (eventPayload == null || !eventPayload.getClass().isAnnotationPresent(IntegrationEvent.class)) { - throw new DomainException("事件类型必须为领域事件"); + if (eventPayload == null) { + throw new DomainException("事件负载不能为空"); } + if (!eventPayload.getClass().isAnnotationPresent(IntegrationEvent.class)) { + throw new DomainException("事件类型必须为集成事件"); + } + Set eventPayloads = TL_EVENT_PAYLOADS.get(); + if (eventPayloads == null) { + eventPayloads = new HashSet<>(); + TL_EVENT_PAYLOADS.set(eventPayloads); + } + eventPayloads.add(eventPayload); + putDeliverTime(eventPayload, schedule); + getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onAttach(eventPayload, schedule)); - getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onNotify(eventPayload, schedule)); - - EventRecord event = eventRecordRepository.create(); - event.init(eventPayload, this.svcName, schedule, Duration.ofMinutes(DEFAULT_IntegrationEvent_EXPIRE_MINUTES), DEFAULT_IntegrationEvent_RETRY_TIMES); - getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.prePersist(event)); - eventRecordRepository.save(event); - getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.postPersist(event)); + } + @Override + public void detach(EVENT eventPayload) { + Set eventPayloads = TL_EVENT_PAYLOADS.get(); + if (eventPayloads == null) { + return; + } + eventPayloads.remove(eventPayload); + getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onDetach(eventPayload)); + } - event.markPersist(true); - eventPublisher.publish(event); - IntegrationEventNotifyEvent integrationEventAttachedTransactionCommittedEvent - = new IntegrationEventNotifyEvent(this, Arrays.asList(event)); + @Override + public void release() { + Set eventPayloads = new HashSet<>(); + eventPayloads.addAll(this.popEvents()); + List persistedEvents = new ArrayList<>(eventPayloads.size()); + for (Object eventPayload : eventPayloads) { + LocalDateTime deliverTime = this.getDeliverTime(eventPayload); + EventRecord event = eventRecordRepository.create(); + event.init(eventPayload, this.svcName, deliverTime, Duration.ofMinutes(DEFAULT_EVENT_EXPIRE_MINUTES), DEFAULT_EVENT_RETRY_TIMES); + event.markPersist(true); + getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.prePersist(event)); + eventRecordRepository.save(event); + getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.postPersist(event)); + persistedEvents.add(event); + } + IntegrationEventAttachedTransactionCommittedEvent integrationEventAttachedTransactionCommittedEvent = new IntegrationEventAttachedTransactionCommittedEvent(this, persistedEvents); applicationEventPublisher.publishEvent(integrationEventAttachedTransactionCommittedEvent); } - @Override - public void notify(INTEGRATION_EVENT eventPayload) { - notify(eventPayload, LocalDateTime.now()); + @TransactionalEventListener(fallbackExecution = true, classes = IntegrationEventAttachedTransactionCommittedEvent.class) + public void onTransactionCommitted(IntegrationEventAttachedTransactionCommittedEvent integrationEventAttachedTransactionCommittedEvent) { + List events = integrationEventAttachedTransactionCommittedEvent.getEvents(); + publish(events); } - @Override - public void notify(INTEGRATION_EVENT eventPayload, Duration delay) { - notify(eventPayload, LocalDateTime.now().plus(delay)); + private void publish(List events) { + if (events != null && !events.isEmpty()) { + events.forEach(event -> { + eventPublisher.publish(event); + }); + } + } + + + public static void reset() { + TL_EVENT_PAYLOADS.remove(); + TL_EVENT_SCHEDULE_MAP.remove(); + } + + /** + * 弹出事件列表 + * + * @return 事件列表 + */ + protected Set popEvents() { + Set eventPayloads = TL_EVENT_PAYLOADS.get(); + TL_EVENT_PAYLOADS.remove(); + return eventPayloads != null ? eventPayloads : EMPTY_EVENT_PAYLOADS; + } + + /** + * 记录事件发送时间 + * + * @param eventPayload + * @param schedule + */ + protected void putDeliverTime(Object eventPayload, LocalDateTime schedule) { + Map eventScheduleMap = TL_EVENT_SCHEDULE_MAP.get(); + if (eventScheduleMap == null) { + eventScheduleMap = new HashMap<>(); + TL_EVENT_SCHEDULE_MAP.set(eventScheduleMap); + } + eventScheduleMap.put(eventPayload, schedule); + } + + public LocalDateTime getDeliverTime(Object eventPayload) { + Map eventScheduleMap = TL_EVENT_SCHEDULE_MAP.get(); + if (eventScheduleMap != null && eventScheduleMap.containsKey(eventPayload)) { + return eventScheduleMap.get(eventPayload); + } else { + return LocalDateTime.now(); + } } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPreCommitEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittedEvent.java similarity index 76% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPreCommitEvent.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittedEvent.java index 0bea1f5..d428c53 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPreCommitEvent.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittedEvent.java @@ -6,12 +6,12 @@ import java.util.List; /** - * 领域事件所在事务正在提交事件 + * 领域事件所在事务成功提交事件 * * @author binking338 * @date 2024/8/28 */ -public class DomainEventAttachedTransactionPreCommitEvent extends ApplicationEvent { +public class DomainEventAttachedTransactionCommittedEvent extends ApplicationEvent { @Getter List events; @@ -21,8 +21,8 @@ public class DomainEventAttachedTransactionPreCommitEvent extends ApplicationEve * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ - public DomainEventAttachedTransactionPreCommitEvent(Object source, List events) { + public DomainEventAttachedTransactionCommittedEvent(Object source, List events) { super(source); this.events = events; } -} \ No newline at end of file +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPostCommitEvent.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittingEvent.java similarity index 76% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPostCommitEvent.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittingEvent.java index d5f319f..c23044a 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionPostCommitEvent.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAttachedTransactionCommittingEvent.java @@ -6,12 +6,12 @@ import java.util.List; /** - * 领域事件所在事务成功提交事件 + * 领域事件所在事务正在提交事件 * * @author binking338 * @date 2024/8/28 */ -public class DomainEventAttachedTransactionPostCommitEvent extends ApplicationEvent { +public class DomainEventAttachedTransactionCommittingEvent extends ApplicationEvent { @Getter List events; @@ -21,8 +21,8 @@ public class DomainEventAttachedTransactionPostCommitEvent extends ApplicationEv * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ - public DomainEventAttachedTransactionPostCommitEvent(Object source, List events) { + public DomainEventAttachedTransactionCommittingEvent(Object source, List events) { super(source); this.events = events; } -} +} \ No newline at end of file diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptor.java index f1967f4..00a09e2 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptor.java @@ -1,7 +1,5 @@ package org.netcorepal.cap4j.ddd.domain.event; -import org.netcorepal.cap4j.ddd.share.EventInterceptor; - import java.time.LocalDateTime; /** diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventManager.java new file mode 100644 index 0000000..d419f4a --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventManager.java @@ -0,0 +1,18 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import java.util.Set; + +/** + * 领域事件发布管理器 + * + * @author binking338 + * @date 2024/9/11 + */ +public interface DomainEventManager { + + /** + * 发布附加到指定实体以及所有未附加到实体的领域事件 + * @param entities 指定实体集合 + */ + void release(Set entities); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java index 810311a..c85dfe6 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java @@ -20,42 +20,35 @@ static DomainEventSupervisor getInstance() { } /** - * 附加领域事件 - * @param domainEventPayload 领域事件消息体 - */ - void attach(DOMAIN_EVENT domainEventPayload); - - /** - * 附加领域事件 - * @param domainEventPayload 领域事件消息体 - * @param delay 延迟发送 + * 获取领域事件发布管理器 + * @return */ - void attach(DOMAIN_EVENT domainEventPayload, Duration delay); - - /** - * 附加领域事件 - * @param domainEventPayload 领域事件消息体 - * @param schedule 指定时间发送 - */ - void attach(DOMAIN_EVENT domainEventPayload, LocalDateTime schedule); + static DomainEventManager getManager(){ + return DomainEventSupervisorSupport.manager; + } /** - * 附加领域事件 + * 附加领域事件到持久化上下文 * @param domainEventPayload 领域事件消息体 * @param entity 绑定实体,该实体对象进入持久化上下文且事务提交时才会触发领域事件分发 */ - void attach(DOMAIN_EVENT domainEventPayload, ENTITY entity); + default void attach(DOMAIN_EVENT domainEventPayload, ENTITY entity){ + attach(domainEventPayload, entity, LocalDateTime.now()); + } + /** - * 附加领域事件 + * 附加领域事件到持久化上下文 * @param domainEventPayload 领域事件消息体 * @param entity 绑定实体,该实体对象进入持久化上下文且事务提交时才会触发领域事件分发 * @param delay 延迟发送 */ - void attach(DOMAIN_EVENT domainEventPayload, ENTITY entity, Duration delay); + default void attach(DOMAIN_EVENT domainEventPayload, ENTITY entity, Duration delay){ + attach(domainEventPayload, entity, LocalDateTime.now().plus(delay)); + } /** - * 附加领域事件 + * 附加领域事件到持久化上下文 * @param domainEventPayload 领域事件消息体 * @param entity 绑定实体,该实体对象进入持久化上下文且事务提交时才会触发领域事件分发 * @param schedule 指定时间发送 @@ -63,20 +56,9 @@ static DomainEventSupervisor getInstance() { void attach(DOMAIN_EVENT domainEventPayload, ENTITY entity, LocalDateTime schedule); /** - * 剥离领域事件 - * @param domainEventPayload 领域事件消息体 - */ - void detach(DOMAIN_EVENT domainEventPayload); - /** - * 剥离领域事件 + * 从持久化上下文剥离领域事件 * @param domainEventPayload 领域事件消息体 * @param entity 关联实体 */ void detach(DOMAIN_EVENT domainEventPayload, ENTITY entity); - - /** - * 发布附加到指定实体以及所有未附加到实体的领域事件 - * @param entities 指定实体集合 - */ - void release(Set entities); } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorSupport.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorSupport.java index 390601e..209f233 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorSupport.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisorSupport.java @@ -8,6 +8,7 @@ */ public class DomainEventSupervisorSupport { static DomainEventSupervisor instance = null; + static DomainEventManager manager = null; /** * 配置领域事件管理器 @@ -17,6 +18,14 @@ public static void configure(DomainEventSupervisor domainEventSupervisor) { DomainEventSupervisorSupport.instance = domainEventSupervisor; } + /** + * 配置领域事件发布管理器 + * @param domainEventManager {@link DomainEventManager} + */ + public static void configure(DomainEventManager domainEventManager) { + DomainEventSupervisorSupport.manager = domainEventManager; + } + /** * for entity import static * diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/EventInterceptor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventInterceptor.java similarity index 93% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/EventInterceptor.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventInterceptor.java index a37084d..8b5f53c 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/EventInterceptor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventInterceptor.java @@ -1,4 +1,4 @@ -package org.netcorepal.cap4j.ddd.share; +package org.netcorepal.cap4j.ddd.domain.event; import org.netcorepal.cap4j.ddd.domain.event.EventRecord; diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java index 1c97baa..31d56f1 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java @@ -1,11 +1,13 @@ package org.netcorepal.cap4j.ddd.domain.event.impl; import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; import org.netcorepal.cap4j.ddd.domain.event.EventPublisher; import org.netcorepal.cap4j.ddd.domain.event.*; import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; -import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionPreCommitEvent; -import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionPostCommitEvent; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionCommittingEvent; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionCommittedEvent; +import org.netcorepal.cap4j.ddd.share.DomainException; import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.Ordered; import org.springframework.core.annotation.OrderUtils; @@ -22,7 +24,7 @@ * @date 2023/8/13 */ @RequiredArgsConstructor -public class DefaultDomainEventSupervisor implements DomainEventSupervisor { +public class DefaultDomainEventSupervisor implements DomainEventSupervisor, DomainEventManager { private final EventRecordRepository eventRecordRepository; private final List domainEventInterceptors; private final EventPublisher eventPublisher; @@ -30,7 +32,7 @@ public class DefaultDomainEventSupervisor implements DomainEventSupervisor { private final String svcName; - private static final ThreadLocal> TL_EVENT_PAYLOADS = new ThreadLocal>(); +// private static final ThreadLocal> TL_EVENT_PAYLOADS = new ThreadLocal>(); private static final ThreadLocal>> TL_ENTITY_EVENT_PAYLOADS = new ThreadLocal>>(); private static final ThreadLocal> TL_EVENT_SCHEDULE_MAP = new ThreadLocal>(); private static final Set EMPTY_EVENT_PAYLOADS = Collections.emptySet(); @@ -56,42 +58,15 @@ protected List getOrderedDomainEventInterceptors() { return sortedDomainEventInterceptors; } - @Override - public void attach(Object eventPayload) { - attach(eventPayload, LocalDateTime.now()); - } - - @Override - public void attach(Object eventPayload, Duration delay) { - attach(eventPayload, LocalDateTime.now().plus(delay)); - } - - @Override - public void attach(Object eventPayload, LocalDateTime schedule) { - Set eventPayloads = TL_EVENT_PAYLOADS.get(); - if (eventPayloads == null) { - eventPayloads = new HashSet<>(); - TL_EVENT_PAYLOADS.set(eventPayloads); - } - eventPayloads.add(eventPayload); - putDeliverTime(eventPayload, schedule); - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onAttach(eventPayload, null, schedule)); - } - - @Override - public void attach(Object eventPayload, Object entity) { - attach(eventPayload, entity, LocalDateTime.now()); - } - - @Override - public void attach(Object eventPayload, Object entity, Duration delay) { - attach(eventPayload, entity, LocalDateTime.now().plus(delay)); - } - @Override public void attach(Object eventPayload, Object entity, LocalDateTime schedule) { - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onAttach(eventPayload, entity, schedule)); - + // 判断领域事件,不支持集成事件。 + if (eventPayload == null) { + throw new DomainException("事件负载不能为空"); + } + if (eventPayload.getClass().isAnnotationPresent(IntegrationEvent.class)) { + throw new DomainException("事件类型不能为集成事件"); + } Map> entityEventPayloads = TL_ENTITY_EVENT_PAYLOADS.get(); if (entityEventPayloads == null) { entityEventPayloads = new HashMap<>(); @@ -103,16 +78,7 @@ public void attach(Object eventPayload, Object entity, LocalDateTime schedule) { entityEventPayloads.get(entity).add(eventPayload); putDeliverTime(eventPayload, schedule); - } - - @Override - public void detach(Object eventPayload) { - Set eventPayloads = TL_EVENT_PAYLOADS.get(); - if (eventPayloads == null) { - return; - } - eventPayloads.remove(eventPayload); - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onDetach(eventPayload, null)); + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onAttach(eventPayload, entity, schedule)); } @Override @@ -127,15 +93,16 @@ public void detach(Object eventPayload, Object entity) { } eventPayloads.remove(eventPayload); - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onDetach(eventPayload, null)); + getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onDetach(eventPayload, entity)); } @Override public void release(Set entities) { Set eventPayloads = new HashSet<>(); - eventPayloads.addAll(this.popEvents()); - for (Object entity : entities) { - eventPayloads.addAll(this.popEvents(entity)); + if(null != entities && !entities.isEmpty()) { + for (Object entity : entities) { + eventPayloads.addAll(this.popEvents(entity)); + } } List persistedEvents = new ArrayList<>(eventPayloads.size()); List transientEvents = new ArrayList<>(eventPayloads.size()); @@ -144,8 +111,8 @@ public void release(Set entities) { LocalDateTime deliverTime = this.getDeliverTime(eventPayload); EventRecord event = eventRecordRepository.create(); event.init(eventPayload, this.svcName, deliverTime, Duration.ofMinutes(DEFAULT_EVENT_EXPIRE_MINUTES), DEFAULT_EVENT_RETRY_TIMES); - boolean isDelayDeliver = !deliverTime.isAfter(now); - if (!isDomainEventNeedPersist(eventPayload) && isDelayDeliver) { + boolean isDelayDeliver = deliverTime.isAfter(now); + if (!isDomainEventNeedPersist(eventPayload) && !isDelayDeliver) { event.markPersist(false); transientEvents.add(event); } else { @@ -156,11 +123,11 @@ public void release(Set entities) { persistedEvents.add(event); } } - DomainEventAttachedTransactionPreCommitEvent domainEventAttachedTransactionPreCommitEvent = new DomainEventAttachedTransactionPreCommitEvent(this, transientEvents); - DomainEventAttachedTransactionPostCommitEvent domainEventAttachedTransactionPostCommitEvent = new DomainEventAttachedTransactionPostCommitEvent(this, persistedEvents); - onTransactionCommiting(domainEventAttachedTransactionPreCommitEvent); - applicationEventPublisher.publishEvent(domainEventAttachedTransactionPreCommitEvent); - applicationEventPublisher.publishEvent(domainEventAttachedTransactionPostCommitEvent); + DomainEventAttachedTransactionCommittingEvent domainEventAttachedTransactionCommittingEvent = new DomainEventAttachedTransactionCommittingEvent(this, transientEvents); + DomainEventAttachedTransactionCommittedEvent domainEventAttachedTransactionCommittedEvent = new DomainEventAttachedTransactionCommittedEvent(this, persistedEvents); + onTransactionCommiting(domainEventAttachedTransactionCommittingEvent); + applicationEventPublisher.publishEvent(domainEventAttachedTransactionCommittingEvent); + applicationEventPublisher.publishEvent(domainEventAttachedTransactionCommittedEvent); } /** @@ -181,14 +148,14 @@ protected boolean isDomainEventNeedPersist(Object payload) { } } - protected void onTransactionCommiting(DomainEventAttachedTransactionPreCommitEvent domainEventAttachedTransactionPreCommitEvent) { - List events = domainEventAttachedTransactionPreCommitEvent.getEvents(); + protected void onTransactionCommiting(DomainEventAttachedTransactionCommittingEvent domainEventAttachedTransactionCommittingEvent) { + List events = domainEventAttachedTransactionCommittingEvent.getEvents(); publish(events); } - @TransactionalEventListener(fallbackExecution = true, classes = DomainEventAttachedTransactionPostCommitEvent.class) - public void onTransactionCommitted(DomainEventAttachedTransactionPostCommitEvent domainEventAttachedTransactionPostCommitEvent) { - List events = domainEventAttachedTransactionPostCommitEvent.getEvents(); + @TransactionalEventListener(fallbackExecution = true, classes = DomainEventAttachedTransactionCommittedEvent.class) + public void onTransactionCommitted(DomainEventAttachedTransactionCommittedEvent domainEventAttachedTransactionCommittedEvent) { + List events = domainEventAttachedTransactionCommittedEvent.getEvents(); publish(events); } @@ -201,21 +168,10 @@ private void publish(List events) { } public static void reset() { - TL_EVENT_PAYLOADS.remove(); TL_ENTITY_EVENT_PAYLOADS.remove(); TL_EVENT_SCHEDULE_MAP.remove(); } - /** - * 弹出事件列表 - * @return 事件列表 - */ - protected Set popEvents() { - Set eventPayloads = TL_EVENT_PAYLOADS.get(); - TL_EVENT_PAYLOADS.remove(); - return eventPayloads != null ? eventPayloads : EMPTY_EVENT_PAYLOADS; - } - /** * 弹出实体绑定的事件列表 * @param entity 关联实体 diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java index 414ca11..5eb2cd4 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java @@ -142,7 +142,14 @@ protected void internalPublish4IntegrationEvent(EventRecord event) { getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.preRelease(event)); getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.prePublish(event.getMessage())); - integrationEventPublisheres.forEach(integrationEventPublisher -> integrationEventPublisher.publish(event, new IntegrationEventSendPublishCallback(getOrderedEventMessageInterceptors(), getOrderedIntegrationEventInterceptors(), eventRecordRepository))); + integrationEventPublisheres.forEach(integrationEventPublisher -> integrationEventPublisher.publish( + event, + new IntegrationEventSendPublishCallback( + getOrderedEventMessageInterceptors(), + getOrderedIntegrationEventInterceptors(), + eventRecordRepository) + ) + ); } catch (Exception ex) { getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onException(ex, event)); diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java index 75aca0f..c78e125 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventSubscriberManager.java @@ -4,8 +4,8 @@ import org.netcorepal.cap4j.ddd.application.RequestParam; import org.netcorepal.cap4j.ddd.application.RequestSupervisor; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventSupervisor; -import org.netcorepal.cap4j.ddd.application.event.annotation.AutoNotify; -import org.netcorepal.cap4j.ddd.application.event.annotation.AutoNotifys; +import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRelease; +import org.netcorepal.cap4j.ddd.application.event.annotation.AutoReleases; import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRequest; import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRequests; import org.netcorepal.cap4j.ddd.domain.event.EventSubscriber; @@ -84,24 +84,27 @@ public void init() { subscribe(integrationEventClass, applicationEventPublisher::publishEvent); // 自动实现 DomainEvent -> IntegrationEvent 适配 - List autoNotifys = null; - if (null != integrationEventClass.getAnnotation(AutoNotify.class)) { - autoNotifys = Arrays.asList(integrationEventClass.getAnnotation(AutoNotify.class)); + List autoReleases = null; + if (null != integrationEventClass.getAnnotation(AutoRelease.class)) { + autoReleases = Arrays.asList(integrationEventClass.getAnnotation(AutoRelease.class)); } - if (null != integrationEventClass.getAnnotation(AutoNotifys.class)) { - autoNotifys = Arrays.asList(integrationEventClass.getAnnotation(AutoNotifys.class).value()); + if (null != integrationEventClass.getAnnotation(AutoReleases.class)) { + autoReleases = Arrays.asList(integrationEventClass.getAnnotation(AutoReleases.class).value()); } - if (null != autoNotifys) { - for (AutoNotify autoNotify : autoNotifys) { + if (null != autoReleases) { + for (AutoRelease autoRelease : autoReleases) { Class converterClass = null; if (Converter.class.isAssignableFrom(integrationEventClass)) { converterClass = integrationEventClass; } - if (Converter.class.isAssignableFrom(autoNotify.converterClass())) { - converterClass = autoNotify.converterClass(); + if (Converter.class.isAssignableFrom(autoRelease.converterClass())) { + converterClass = autoRelease.converterClass(); } - Converter converter = ClassUtils.newConverterInstance(autoNotify.sourceDomainEventClass(), integrationEventClass, converterClass); - subscribe(autoNotify.sourceDomainEventClass(), domainEvent -> IntegrationEventSupervisor.getInstance().notify(converter.convert(domainEvent), Duration.ofSeconds(autoNotify.delayInSeconds()))); + Converter converter = ClassUtils.newConverterInstance(autoRelease.sourceDomainEventClass(), integrationEventClass, converterClass); + subscribe(autoRelease.sourceDomainEventClass(), domainEvent -> { + IntegrationEventSupervisor.getInstance().attach(converter.convert(domainEvent), Duration.ofSeconds(autoRelease.delayInSeconds())); + IntegrationEventSupervisor.getManager().release(); + }); } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java index 75103c3..e31e6fb 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java @@ -62,8 +62,8 @@ public void init() { subscribe(autoAttach.sourceEntityClass(), (e, t) -> { for (PersistType listenPersistType : autoAttach.persistType()) { if(listenPersistType == t){ - DomainEventSupervisor.getInstance().attach(converter.convert(e), Duration.ofSeconds(autoAttach.delayInSeconds())); - DomainEventSupervisor.getInstance().release(Collections.singleton(e)); + DomainEventSupervisor.getInstance().attach(converter.convert(e), e, Duration.ofSeconds(autoAttach.delayInSeconds())); + DomainEventSupervisor.getManager().release(Collections.singleton(e)); break; } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java index 1f404b1..ad60511 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java @@ -16,7 +16,6 @@ import org.netcorepal.cap4j.ddd.share.PageParam; import org.springframework.transaction.annotation.Propagation; -import java.time.Duration; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -100,17 +99,12 @@ public , RESPONSE> RESPONSE send(REQUEST } @Override - public void notify(INTEGRATION_EVENT integrationEventPayload) { - IntegrationEventSupervisor.getInstance().notify(integrationEventPayload); + public void attach(INTEGRATION_EVENT integrationEventPayload, LocalDateTime schedule) { + IntegrationEventSupervisor.getInstance().attach(integrationEventPayload, schedule); } @Override - public void notify(INTEGRATION_EVENT integrationEventPayload, Duration delay) { - IntegrationEventSupervisor.getInstance().notify(integrationEventPayload, delay); - } - - @Override - public void notify(INTEGRATION_EVENT integrationEventPayload, LocalDateTime schedule) { - IntegrationEventSupervisor.getInstance().notify(integrationEventPayload, schedule); + public void detach(INTEGRATION_EVENT integrationEventPayload) { + IntegrationEventSupervisor.getInstance().detach(integrationEventPayload); } } diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java index c958921..56007b8 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java @@ -5,9 +5,10 @@ import lombok.extern.slf4j.Slf4j; import org.hibernate.engine.spi.SessionImplementor; import org.netcorepal.cap4j.ddd.application.UnitOfWork; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventManager; import org.netcorepal.cap4j.ddd.domain.aggregate.Specification; import org.netcorepal.cap4j.ddd.domain.aggregate.SpecificationManager; -import org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisor; +import org.netcorepal.cap4j.ddd.domain.event.DomainEventManager; import org.netcorepal.cap4j.ddd.domain.repo.PersistListenerManager; import org.netcorepal.cap4j.ddd.domain.repo.PersistType; import org.netcorepal.cap4j.ddd.share.DomainException; @@ -37,7 +38,8 @@ @RequiredArgsConstructor @Slf4j public class JpaUnitOfWork implements UnitOfWork { - private final DomainEventSupervisor domainEventSupervisor; + private final DomainEventManager domainEventManager; + private final IntegrationEventManager integrationEventManager; private final SpecificationManager specificationManager; private final PersistListenerManager persistListenerManager; private final ApplicationEventPublisher applicationEventPublisher; @@ -163,7 +165,8 @@ public void save(Propagation propagation) { if (deleteEntities != null && !deleteEntities.isEmpty()) { entities.addAll(deleteEntities); } - domainEventSupervisor.release(entities); + domainEventManager.release(entities); + integrationEventManager.release(); return null; }, saveAndDeleteEntityList, propagation); } From 932e7187bdef232af11c1e5702b74e4b1a9923a1 Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 12 Sep 2024 09:34:09 +0800 Subject: [PATCH 25/62] =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1067 +---------------- ...23\346\236\204\344\273\213\347\273\215.md" | 231 ++++ ...26\347\240\201\346\214\207\345\215\227.md" | 688 +++++++++++ ...26\347\240\201\346\214\207\345\215\227.md" | 175 +++ ...26\347\240\201\346\214\207\345\215\227.md" | 32 + 5 files changed, 1153 insertions(+), 1040 deletions(-) create mode 100644 "doc/00_\351\241\271\347\233\256\345\210\206\345\261\202\347\273\223\346\236\204\344\273\213\347\273\215.md" create mode 100644 "doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" create mode 100644 "doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" create mode 100644 "doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" diff --git a/README.md b/README.md index 3743f81..e21b92f 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,21 @@ [![Maven Central Version](https://img.shields.io/maven-central/v/io.github.netcorepal/cap4j)](https://central.sonatype.com/artifact/io.github.netcorepal/cap4j) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/netcorepal/cap4j/blob/main/LICENSE) -本项目是 [CAP](https://github.com/dotnetcore/CAP) 项目的 Java 实现,基于[整洁架构](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)、[Outbox](https://www.kamilgrzybek.com/blog/posts/the-outbox-pattern)模式、[CQS](https://martinfowler.com/bliki/CommandQuerySeparation.html)模式以及[UoW](https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/june/the-unit-of-work-pattern-and-persistence-ignorance)模式等理念,cap4j期望解决基于`领域模型`如何`实现领域驱动设计`的问题。 +本项目是 [CAP](https://github.com/dotnetcore/CAP) 项目的 Java 实现超集,基于 +[整洁架构](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)、 +Mediator中介者模式、 +[Outbox发件箱](https://www.kamilgrzybek.com/blog/posts/the-outbox-pattern)模式、 +[CQS命令查询分离](https://martinfowler.com/bliki/CommandQuerySeparation.html)模式 +以及[UoW](https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/june/the-unit-of-work-pattern-and-persistence-ignorance)模式 +等理念,cap4j期望解决如何基于`领域模型` 方便地 `实现领域驱动设计`的问题。 如果对以上架构理念有充分了解,那么cap4j的使用将会非常顺手。另一方面,通过cap4j来构建你的服务,你将学会一种实现领域驱动设计的完整落地方法。 ## 快速开始 ### 脚手架搭建 +为了方便框架应用与理解,cap4j配备了代码生成插件`cap4j-ddd-codegen`,基于该插件,我们可以非常方便地生成初始项目脚手架、实体映射代码和基于JPA的聚合仓储代码。 + #### **第一步**:新建一个空的maven项目 > 定好maven坐标三要素:`groupId`、`artifactId`、`version` @@ -90,27 +98,31 @@ ``` 通常,`cap4j-ddd-codegen`插件只需要我们根据团队或项目的实际情况调整以下配置项即可使用。 -> - `basePackage`: 项目基础包名,一般为 _com.yourcompany.project_ -> - `connectionString`: 数据库连接串 -> - `user`: 数据库账号 -> - `pwd`: 数据库密码 -> - `schema`: 数据库名称 +> ``_项目基础包名,一般为com.yourcompany.project_`` +> +> ``_数据库密码_`` +> +> ``_数据库密码_`` +> +> ``_数据库密码_`` +> +> ``_数据库名称_`` #### **第三步**:执行插件命令,生成项目脚手架 -> 插件配置项`archTemplate`是`gen-arch`命令生成脚手架目录与项目基础代码的配置文件地址。开放自定义方便大家根据自己团队需求进行定制化。格式说明后续再,不过格式很简单,按示例中的配置自己应该就能看懂并应用。有兴趣更详细了解的参考源码[GenArchMojo](cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java) +> 插件配置项`archTemplate`是`gen-arch`命令生成脚手架目录与项目基础代码的配置文件地址。开放自定义方便大家根据自己团队需求进行定制化。格式说明后续补充,不过格式很简单,按示例中的配置自己应该就能看懂并应用。有兴趣更详细了解的参考源码[GenArchMojo](cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java) ```shell mvn cap4j-ddd-codegen:gen-arch ``` -如果没有意外,项目结构通过`cap4j-ddd-codegen`插件已初始化完毕! +如果没有意外,`cap4j-ddd-codegen`插件将根据配置文件[cap4j-ddd-codegen-template.json](https://raw.githubusercontent.com/netcorepal/cap4j/main/cap4j-ddd-codegen-template.json)完成项目结构初始化! -### 目录结构介绍 -#### 简介 +#### 项目结构介绍 +基于基础包路径配置, ```xml org.netcorepal.cap4j.ddd.example ``` -基于基础包路径配置,在maven项目源码目录`src/main/java/org/netcorepal/cap4j/ddd/example`下将会生成4个`package`。 +`cap4j-ddd-codegen`插件在maven项目源码目录`src/main/java/org/netcorepal/cap4j/ddd/example`下将会生成4个`package`。 > - `_share` 公共代码 > - `adapter` 适配层(Interface Adapter) > - `application` 应用层(Application Business Rules) @@ -119,1036 +131,11 @@ mvn cap4j-ddd-codegen:gen-arch 以上代码分层完全遵循[整洁架构](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)对于代码分层组织的观点。 ![整洁架构](https://blog.cleancoder.com/uncle-bob/images/2012-08-13-the-clean-architecture/CleanArchitecture.jpg) -#### 领域层 -实现领域模型,聚合、实体、领域事件以及集成事件定义。 -```text -└── org.netcorepal.cap4j.ddd.example - └── domain - ├── _share (领域层公共代码,仅供领域层引用) - ├── aggregates (实体聚合声明) - └── services (领域服务) -``` - -#### 应用层 -实现`CQS`模式,将功能用例(`UseCase`)抽象成命令或查询来实现。 -```text -└── org.netcorepal.cap4j.ddd.example - └── application -    ├── _share (应用层公共代码,仅供领域层引用) -    │   ├── clients (防腐层:包装三方服务调用接口) -    │   ├── enums (应用层枚举类型) -    │   └── events (声明三方服务集成事件) -    ├── commands (CQS的C:命令) -    ├── queries (CQS的Q:查询) -    └── subscribers (领域事件或集成事件的订阅处理逻辑) -``` - -#### 适配层 -如适配字面意思,放置各层(领域层`domain`、应用层`application`)定义的接口实现。整洁架构中称其为接口适配层(`Interface Adapters`)。 - -该层是领域层和应用层业务逻辑所依赖的`抽象功能接口`的技术适配实现,遵循DI原则。 - -举个例子来理解抽象功能接口,比如我们常见的电商场景,用户在商城下单,需要通知仓库打包发货。那么这个`通知`可能就会需要抽象出一个`通知功能接口`,来承接下单流程的连续性。至此,通知功能接口的定义都是应用层关心的事。但是通知功能接口如何实现,就是适配层的事了,你是短信也好、电话也好,能够实现通知功能接口定义的核心效果即可。 - -```text -└── org.netcorepal.cap4j.ddd.example - └── adapter -    ├── _share (适配层公共代码,仅供适配层引用) -    │   └── configure -    │   └── ApolloConfig.java (配置中心) -    ├── application (应用层接口实现) -    │   ├── _share -    │   └── clients -    ├── domain (领域层接口实现) -    │   ├── _share -    │   │   └── configure -    │   │   └── MyEventMessageInterceptor.java (集成事件消息拦截器) -    │   └── repositories (实现聚合仓储接口) -    ├── infra (基础设施适配接口实现) -    │   ├── _share -    │   ├── jdbc (服务于应用层CQS的Q,jdbc查询工具类) -    │   │   └── NamedParameterJdbcTemplateDao.java -    │   └── mybatis (服务于应用层CQS的Q,mybatis集成) -    │   ├── _share -    │   │   └── MyEnumTypeHandler.java -    │   └── mapper -    └── portal (端口) -    ├── api (SpringMVC相关代码) -    │   ├── TestController.java -    │   └── _share -    │   ├── ResponseData.java -    │   ├── Status.java -    │   └── configure -    │   ├── CommonExceptionHandler.java -    │   ├── MvcConfig.java -    │   └── SwaggerConfig.java -    ├── jobs (定时任务相关代码) -    │   └── _share -    │   └── configure -    │   └── XxlJobConfig.java -    └── queues (消息队列相关代码) -``` - -#### 公共代码 -放置公共代码。 -```text -└── org.netcorepal.cap4j.ddd.example - └── _share -    ├── CodeEnum.java (响应状态码枚举) -    ├── Constants.java (公共常量) -    └── exception (自定义业务异常) -    ├── ErrorException.java -    ├── KnownException.java -    └── WarnException.java -``` - -#### 项目目录树 -```text -. -├── pom.xml -└── src - ├── main - │   ├── java - │   │   └── org - │   │   └── netcorepal - │   │   └── cap4j - │   │   └── ddd - │   │   └── example - │   │   ├── StartApplication.java - │   │   ├── _share - │   │   │   ├── CodeEnum.java - │   │   │   ├── Constants.java - │   │   │   └── exception - │   │   │   ├── ErrorException.java - │   │   │   ├── KnownException.java - │   │   │   └── WarnException.java - │   │   ├── adapter - │   │   │   ├── _share - │   │   │   │   └── configure - │   │   │   │   └── ApolloConfig.java - │   │   │   ├── application - │   │   │   │   ├── _share - │   │   │   │   └── clients - │   │   │   ├── domain - │   │   │   │   ├── _share - │   │   │   │   │   └── configure - │   │   │   │   │   └── MyEventMessageInterceptor.java - │   │   │   │   └── repositories - │   │   │   ├── infra - │   │   │   │   ├── _share - │   │   │   │   ├── jdbc - │   │   │   │   │   └── NamedParameterJdbcTemplateDao.java - │   │   │   │   └── mybatis - │   │   │   │   ├── _share - │   │   │   │   │   └── MyEnumTypeHandler.java - │   │   │   │   └── mapper - │   │   │   └── portal - │   │   │   ├── api - │   │   │   │   ├── TestController.java - │   │   │   │   └── _share - │   │   │   │   ├── ResponseData.java - │   │   │   │   ├── Status.java - │   │   │   │   └── configure - │   │   │   │   ├── CommonExceptionHandler.java - │   │   │   │   ├── MvcConfig.java - │   │   │   │   └── SwaggerConfig.java - │   │   │   ├── jobs - │   │   │   │   └── _share - │   │   │   │   └── configure - │   │   │   │   └── XxlJobConfig.java - │   │   │   └── queues - │   │   ├── application - │   │   │   ├── _share - │   │   │   │   ├── clients - │   │   │   │   ├── enums - │   │   │   │   └── events - │   │   │   ├── commands - │   │   │   ├── queries - │   │   │   └── subscribers - │   │   └── domain - │   │   ├── _share - │   │   ├── aggregates - │   │   └── services - │   └── resources - │   ├── mapper - │   ├── application.properties - │   ├── ddl.sql - │   └── logback.xml - └── test - └── java - └── org - └── netcorepal - └── cap4j - └── ddd - └── example - └── AppTest.java -``` +更详细分层结构介绍,移步[项目分层结构介绍](doc/00_项目分层结构介绍.md)。 ### 编码最佳实践 +1. [领域层编码指南](doc/01_领域层编码指南.md) +2. [应用层编码指南](doc/02_应用层编码指南.md) +3. [适配层编码指南](doc/03_适配层编码指南.md) -#### 领域层 -##### ORM代码生成 -根据领域模型中的实体以及聚合关系,完成数据库表设计。 - -为了方便实体到数据库表映射的枯燥工作(ORM),我们设计了一套基于数据库注释的注解语法,并且这套语法非常简单。 -通常情况下(比如都是单实体聚合的领域模型)我们不需要这些注解语法也可以让实体代码生成正常工作。 - -大部分情况下,我们也只需要熟悉一个表注解和两个列注解即可: -- 表注解 `@P`=_root_entity_table_; -- 列注解 `@T`=_JavaType_; `@E`=_0_:_ENUM_FIELD_:_枚举字段注释_; - -```sql -CREATE TABLE `order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_no` varchar(100) NOT NULL DEFAULT '' COMMENT '订单编号', - `order_status` int unsigned NOT NULL DEFAULT '0' COMMENT '订单状态@T=OrderStatus;@E=0:INIT:待支付|1:PAID:已支付|-1:CLOSED:已关闭;', - `amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '总金额', - `version` bigint unsigned NOT NULL DEFAULT '0', - `db_created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `db_updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `db_deleted` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - KEY `idx_db_created_at` (`db_created_at`), - KEY `idx_db_updated_at` (`db_updated_at`) -) COMMENT='订单\n'; - -CREATE TABLE `order_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_id` bigint NOT NULL DEFAULT '0' COMMENT '关联主订单', - `name` varchar(100) NOT NULL DEFAULT '' COMMENT '名称', - `price` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '单价', - `count` int NOT NULL DEFAULT '0' COMMENT '数量', - `version` bigint unsigned NOT NULL DEFAULT '0', - `db_created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `db_updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `db_deleted` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - KEY `idx_db_created_at` (`db_created_at`), - KEY `idx_db_updated_at` (`db_updated_at`) -) COMMENT='订单项\n @P=order'; -# 以上sql语句隐含了如下实体映射关系: -# 订单表(order)对应实体是一个聚合根,并且订单项表(order_item)对应实体是order聚合的实体成员。 -# 订单表(order)的订单状态字段(order_status)将会映射成OrderStatus的Java类型,该OrderStatus是一个enum类型,有3个字段成员,INIT、PAID、CLOSED -``` -默认情况下,所有数据库表都将会映射成一个Java实体类,该实体类将构成一个聚合,并且作为该聚合的聚合根。如果聚合存在其他实体,则其他实体对应的表注释标注@P注解即可。 - -> @P指示该表对应的Java实体类属于某个聚合内的实体成员。 -> -> @E负责生成OrderStatus枚举。@E需要配合@T才能完成数据库字段的Java枚举映射。 -> -> @T负责将Order实体的orderStatus字段映射成OrderStatus枚举,@T可以单独工作,用于DB类型<->Java类型的强制自定义映射。 -> -> 如果想要对这套语法有个详细完整的了解,可以通过如下maven指令获取语法帮助。 -> ```shell -> mvn io.github.netcorepal:cap4j-ddd-codegen-maven-plugin:1.0.0-alpha-2:help -> # or -> mvn cap4j-ddd-codegen:help -> ``` -> 需要注意的是,当前`cap4j-ddd-codegen:gen-entity`仅支持基于MySQL数据库注释的注解解析。 - -先后执行 -```shell -mvn cap4j-ddd-codegen:gen-entity -mvn cap4j-ddd-codegen:gen-repository -``` -代码生成结果 -```java -package org.netcorepal.cap4j.ddd.example.domain.aggregates; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import org.hibernate.annotations.Fetch; -import org.hibernate.annotations.FetchMode; -import org.hibernate.annotations.SQLDelete; -import org.hibernate.annotations.Where; - -import javax.persistence.*; - -/** - * 订单 - * - * 本文件由[cap4j-ddd-codegen-maven-plugin]生成 - * 警告:请勿手工修改该文件的字段声明,重新生成会覆盖字段声明 - */ -/* @AggregateRoot */ -@Entity -@Table(name = "`order`") -@DynamicInsert -@DynamicUpdate -@SQLDelete(sql = "update `order` set `db_deleted` = 1 where id = ? and `version` = ? ") -@Where(clause = "`db_deleted` = 0") - -@AllArgsConstructor -@NoArgsConstructor -@Builder -@Getter -public class Order { - - // 【行为方法开始】 - - - - // 【行为方法结束】 - - - - // 【字段映射开始】本段落由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 - - @Id - @GeneratedValue(generator = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") - @GenericGenerator(name = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator", strategy = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") - @Column(name = "`id`") - Long id; - - - /** - * 订单编号 - * varchar(100) - */ - @Column(name = "`order_no`") - String orderNo; - - /** - * 订单状态 - * 0:INIT:待支付;-1:CLOSED:已关闭;1:PAID:已支付 - * int unsigned - */ - @Convert(converter = org.netcorepal.cap4j.ddd.example.domain.aggregates.enums.OrderStatus.Converter.class) - @Column(name = "`order_status`") - org.netcorepal.cap4j.ddd.example.domain.aggregates.enums.OrderStatus orderStatus; - - /** - * 总金额 - * decimal(14,2) - */ - @Column(name = "`amount`") - java.math.BigDecimal amount; - - @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true) @Fetch(FetchMode.SUBSELECT) - @JoinColumn(name = "`order_id`", nullable = false) - private java.util.List orderItems; - - /** - * 数据版本(支持乐观锁) - */ - @Version - @Column(name = "`version`") - Integer version; - - // 【字段映射结束】本段落由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 -} - -``` - -```java -package org.netcorepal.cap4j.ddd.example.domain.aggregates; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.DynamicInsert; -import org.hibernate.annotations.DynamicUpdate; -import org.hibernate.annotations.Fetch; -import org.hibernate.annotations.FetchMode; -import org.hibernate.annotations.SQLDelete; -import org.hibernate.annotations.Where; - -import javax.persistence.*; - -/** - * 订单项 - * - * - * 本文件由[cap4j-ddd-codegen-maven-plugin]生成 - * 警告:请勿手工修改该文件的字段声明,重新生成会覆盖字段声明 - */ -@Entity -@Table(name = "`order_item`") -@DynamicInsert -@DynamicUpdate -@SQLDelete(sql = "update `order_item` set `db_deleted` = 1 where id = ? and `version` = ? ") -@Where(clause = "`db_deleted` = 0") - -@AllArgsConstructor -@NoArgsConstructor -@Builder -@Getter -public class OrderItem { - - // 【行为方法开始】 - - - - // 【行为方法结束】 - - - - // 【字段映射开始】本段落由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 - - @Id - @GeneratedValue(generator = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") - @GenericGenerator(name = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator", strategy = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") - @Column(name = "`id`") - Long id; - - - /** - * 名称 - * varchar(100) - */ - @Column(name = "`name`") - String name; - - /** - * 单价 - * decimal(14,2) - */ - @Column(name = "`price`") - java.math.BigDecimal price; - - /** - * 数量 - * int - */ - @Column(name = "`count`") - Integer count; - - /** - * 数据版本(支持乐观锁) - */ - @Version - @Column(name = "`version`") - Integer version; - - // 【字段映射结束】本段落由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 -} - - -``` - -```java -package org.netcorepal.cap4j.ddd.example.domain.aggregates.enums; - -import lombok.Getter; - -import javax.persistence.*; -import java.util.HashMap; -import java.util.Map; - -/** - * 本文件由[cap4j-ddd-codegen-maven-plugin]生成 - * 警告:请勿手工修改该文件,重新生成会覆盖该文件 - */ -public enum OrderStatus { - - /** - * 待支付 - */ - INIT(0, "待支付"), - /** - * 已关闭 - */ - CLOSED(-1, "已关闭"), - /** - * 已支付 - */ - PAID(1, "已支付"), -; - @Getter - private int code; - @Getter - private String name; - - OrderStatus(Integer code, String name){ - this.code = code; - this.name = name; - } - - private static Map enums = null; - public static OrderStatus valueOf(Integer code) { - if(enums == null) { - enums = new HashMap<>(); - for (OrderStatus val : OrderStatus.values()) { - enums.put(val.code, val); - } - } - if(enums.containsKey(code)){ - return enums.get(code); - } - throw new RuntimeException("枚举类型OrderStatus枚举值转换异常,不存在的值" + code); - } - - /** - * JPA转换器 - */ - public static class Converter implements AttributeConverter{ - @Override - public Integer convertToDatabaseColumn(OrderStatus val) { - return val.code; - } - - @Override - public OrderStatus convertToEntityAttribute(Integer code) { - return OrderStatus.valueOf(code); - } - } -} - - -``` - -```java -package org.netcorepal.cap4j.ddd.example.adapter.domain.repositories; - -import org.netcorepal.cap4j.ddd.example.domain.aggregates.Order; - -/** - * 本文件由[cap4j-ddd-codegen-maven-plugin]生成 - */ -public interface OrderRepository extends org.netcorepal.cap4j.ddd.domain.repo.AggregateRepository { - // 【自定义代码开始】本段落之外代码由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 - - @org.springframework.stereotype.Component - public static class OrderJpaRepositoryAdapter extends org.netcorepal.cap4j.ddd.domain.repo.AbstractJpaRepository - { - public OrderJpaRepositoryAdapter(org.springframework.data.jpa.repository.JpaSpecificationExecutor jpaSpecificationExecutor, org.springframework.data.jpa.repository.JpaRepository jpaRepository) { - super(jpaSpecificationExecutor, jpaRepository); - } - } - - // 【自定义代码结束】本段落之外代码由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 -} - -``` - -##### UniOfWork模式 -简单来说[UoW](https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/june/the-unit-of-work-pattern-and-persistence-ignorance)实现了将当前线程上下文中所有实体的变更操作一并转化成对应的关系型数据库的持久化DML(insert、update、delete)的能力。缩短事务执行时间的同时,可以让我们将更多的精力放在业务逻辑实现和优化上。 - -UnitOfWork 常用接口 -- `persist(Object entity)` 待持久化添加或更新 -- `remove(Object entity)` 待持久化删除 -- `save()` 以整体事务提交以上待持久化的变更(添加、更新或删除) - -示例 -```java -// 代码省略... -public class Order { - - // 【行为方法开始】 - - /** - * 下单初始化 - * @param items - */ - public void init(List items){ - this.orderNo = "order-" + System.currentTimeMillis(); - this.orderStatus = OrderStatus.INIT; - BigDecimal amount = orderItems.stream() - .map(i -> i.getPrice().multiply(BigDecimal.valueOf( i.getCount()))) - .reduce(BigDecimal.ZERO, (a,b) -> a.add(b)); - this.amount = amount; - this.orderItems = items; - } - - // 【行为方法结束】 - // 代码省略... -} -``` - -```java -package org.netcorepal.cap4j.ddd.example.application.commands; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.netcorepal.cap4j.ddd.application.command.Command; -import org.netcorepal.cap4j.ddd.domain.repo.AggregateRepository; -import org.netcorepal.cap4j.ddd.application.UnitOfWork; -import org.netcorepal.cap4j.ddd.example.domain.aggregates.Order; -import org.netcorepal.cap4j.ddd.example.domain.aggregates.OrderItem; -import org.springframework.stereotype.Service; - -import java.math.BigDecimal; -import java.util.List; -import java.util.stream.Collectors; - - -/** - * 下单 - * - * @date 2024/8/21 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class PlaceOrderCmd { - - @Schema(description = "订单项列表") - List orderItems; - - - @Schema(description = "订单项") - public static class Item{ - - @Schema(description = "名称") - String name; - - @Schema(description = "价格") - BigDecimal price; - - @Schema(description = "数量") - Integer count; - } - - @Service - @RequiredArgsConstructor - @Slf4j - public static class Handler implements Command { - private final AggregateRepository repo; - private final UnitOfWork unitOfWork; - - @Override - public String exec(PlaceOrderCmd cmd) { - - Order order = Order.builder().build(); - - List orderItems = cmd.orderItems.stream() - .map(i -> OrderItem.builder() - .name(i.name) - .price(i.price) - .count(i.count) - .build()) - .collect(Collectors.toList()); - - order.init(orderItems); - - unitOfWork.persist(order); - unitOfWork.save(); - - return order.getOrderNo(); - } - } -} -``` - -##### 事件定义、订阅、发布 -**创建发件箱表** - -为了实现`Outbox`模式,cap4j需要在业务库中创建发件箱表。脚手架初始化后,使用项目内`resources/ddl.sql`包含的完整的发件箱表建表语句初始化数据库。 - -**领域事件定义** - -通常领域事件发布需配合`UnitOfWork`模式实现,这指的是领域事件的发布与聚合内实体属性状态变更的持久化是捆绑的(归属同一事务),所以更合理的做法是领域事件一般定义在领域层(`domain`)。 - -通过[`DomainEvent`](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java)注解的类,cap4j将会识别成领域事件。 -后续即可通过[`DefaultDomainEventSupervisor`](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java).`instance`.`attach`方法来向当前线程上线文附加领域事件。 -一旦 [`UnitOfWork`](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java).save() 顺利提交事务。则cap4j将会保障事件被提交到具体适配好的消息队列(比如当前cap4j实现的RocketMQ)中。 - -```java -package org.netcorepal.cap4j.ddd.example.domain.aggregates.events; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; - -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 下单领域事件 - * - * @author bingking338 - */ -@DomainEvent( - persist = true -) -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class OrderPlacedDomainEvent { - /** - * 订单号 - */ - String orderNo; - /** - * 订单金额 - */ - BigDecimal amount; - /** - * 下单时间 - */ - LocalDateTime orderTime; -} - -``` -> 注解属性详解 -> - `integration()` integration 字段非空,则事件会被识别为集成事件,意味着该事件将通过消息队列适配,通知到分布式系统中的其他服务进程。 -> - `subscriber()` 集成事件订阅场景,必须定义该字段,通常该字段的值将会被适配的消息队列应用到消费分组配置中。 -> - `persist()` 控制事件发布记录持久化。集成事件发布场景,该字段无意义。非集成事件发布场景(仅在本服务进程内部有订阅需求),可以通过`persist=true`控制事件进入发件箱表,并脱离事件发布上下文事务中。以避免订阅逻辑异常影响发布事务的完成。 -> -> 应用场景例子说明 -> - `基于MQ发送方` @DomainEvent(integration="event-name-used-for-mq-topic") -> - `基于MQ订阅方` @DomainEvent(subscriber="subscriber-name-used-for-mq-consumer-group") -> - `消费方与订阅方事务隔离` @DomainEvent(persist=true) -> - `消费方与订阅方同一事务` @DomainEvent -> -> 关于领域事件与集成事件 -> -> 集成事件指会对系统内其他服务发布的领域事件。通常如果要区分领域事件和集成事件,那么领域事件一般指的是不需要对外发布的业务事件,仅在内部聚合之间应用。很多地方都不区分领域事件与集成事件,但是我认为这个区分是价值的。 -> - - -**领域事件发布** - -通常应在实体行为中,发布领域事件。 - -接口[DomainEventSupervisor.java](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java) -> - `即时发送` DefaultDomainEventSupervisor.instance.attach(Object eventPayload, Object entity) -> - `延时发送` DefaultDomainEventSupervisor.instance.attach(Object eventPayload, Object entity, Duration delay) -> - `定时发送` DefaultDomainEventSupervisor.instance.attach(Object eventPayload, Object entity, LocalDateTime schedule) - -```java -import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; - - -// 代码省略... -public class Order { - // 代码省略... - public class Order { - - // 【行为方法开始】 - - /** - * 下单初始化 - * @param items - */ - public void init(List items){ - // 代码省略... - DefaultDomainEventSupervisor.instance.attach(OrderPlacedDomainEvent.builder() - .orderNo(this.orderNo) - .amount(this.amount) - .orderTime(LocalDateTime.now()) - .build(), this); - } - - // 【行为方法结束】 - // 代码省略... - } -} -``` - -**领域事件订阅** - -领域事件订阅定义在应用层(`application`),通常放置在 `${basePackage}.application.subscribers` 包中。 - -领域事件订阅支持Spring注解式声明订阅(监听)的方式。 - -```java - -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; - -@Service -public class OrderPlacedDomainEventSubscriber{ - @EventListener(DeliveryReceivedDomainEvent.class) - public void onEvent(DeliveryReceivedDomainEvent event){ - // 事件处理逻辑 - } -} -``` - - -#### 应用层 -##### IDEA代码模板 -`${basePackage}.application.commands`中的类模板 - -模板名称:`Command` -```java - -#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end - -import lombok.Builder; -import lombok.Data; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -#parse("File Header.java") - -/** - * todo: 命令描述 - * - * @author binking338 - * @date ${DATE} - */ -@Data -@Builder -public class ${NAME} { - - @Service - @RequiredArgsConstructor - @Slf4j - public static class Handler implements Command<${NAME}, ${ReturnType}>{ - private final AggregateRepository<${Entity}, Long> repo; - private final UnitOfWork unitOfWork; - - @Override - public ${ReturnType} exec(${NAME} cmd) { - - return null; - } - } -} -``` -`${basePackage}.application.queries`中的类模板 - -模板名称:`Query` -```java - -#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end - -import lombok.Builder; -import lombok.Data; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.netcorepal.cap4j.ddd.application.query.Query; -import org.netcorepal.cap4j.ddd.domain.repo.AggregateRepository; -import org.springframework.stereotype.Service; - -#parse("File Header.java") - -/** - * todo: 查询描述 - * - * @author binking338 - * @date ${DATE} - */ -@Data -@Builder -public class ${NAME} { - private Long id; - - @Service - @RequiredArgsConstructor - @Slf4j - public static class Handler implements Query<${NAME}, ${NAME}Dto>{ - private final AggregateRepository<${Entity}, Long> repo; - - @Override - public ${NAME}Dto exec(${NAME} param) { - ${Entity} entity = repo.findOne(${Entity}Schema.specify( - root -> root.id().eq(param.id) - )).orElseThrow(() -> new KnownException("不存在")); - - return null; - } - } - - @Data - public static class ${NAME}Dto{ - private Long id; - - } -} -``` - -模板名称:`QueryList` -```java - -#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end - -import lombok.Builder; -import lombok.Data; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.netcorepal.cap4j.ddd.application.query.ListQuery; -import org.netcorepal.cap4j.ddd.domain.repo.AggregateRepository; -import org.springframework.stereotype.Service; - -#parse("File Header.java") - -/** - * todo: 查询描述 - * - * @author binking338 - * @date ${DATE} - */ -@Data -@Builder -public class ${NAME} { - private Long id; - - @Service - @RequiredArgsConstructor - @Slf4j - public static class Handler implements ListQuery<${NAME}, ${NAME}Dto>{ - private final AggregateRepository<${Entity}, Long> repo; - - @Override - public List<${NAME}Dto> exec(${NAME} param) { - List<${Entity}> list = repo.findAll(${Entity}Schema.specify( - root -> root.id().gt(param.id) - )); - - return null; - } - } - - @Data - public static class ${NAME}Dto{ - private Long id; - - } -} -``` - -模板名称:`QueryPage` -```java -#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.netcorepal.cap4j.ddd.application.query.PageQuery; -import org.netcorepal.cap4j.ddd.domain.repo.AggregateRepository; -import org.netcorepal.cap4j.ddd.domain.repo.JpaPageUtils; -import org.netcorepal.cap4j.ddd.share.PageData; -import org.netcorepal.cap4j.ddd.share.PageParam; -import org.springframework.data.domain.Page; -import org.springframework.stereotype.Service; - -#parse("File Header.java") - -/** - * todo: 查询描述 - * @author binking338 - * @date ${DATE} - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ${NAME} extends PageParam { - private Long id; - - @Service - @RequiredArgsConstructor - @Slf4j - public static class Handler implements PageQuery<${NAME}, ${NAME}Dto>{ - private final AggregateRepository<${Entity}, Long> repo; - - @Override - public PageData<${NAME}Dto> exec(${NAME} param) { - Page<${Entity}> page = repo.findAll(${Entity}Schema.specify( - root -> root.id().gt(param.id) - ), JpaPageUtils.toSpringData(param)); - - return JpaPageUtils.fromSpringData(page, p -> ${NAME}Dto.builder() - .id(p.getId()) - .build()); - } - } - - @Data - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class ${NAME}Dto{ - private Long id; - - } -} -``` - - -#### 适配层 -##### IDEA LiveTemplate -`acmd` 适配mvc透出命令 -```java - -@Autowired -$Cmd$.Handler $cmd$Handler; - -@Data -@NoArgsConstructor -public static class $Cmd$Request { - // todo: 添加参数 - - @Schema(description = "参数说明") - String param; - - public DeductWalletCmd toCommand() { - return $Cmd$.builder() - .param(param) - .build(); - } -} -@Schema(description = "接口说明") -@PostMapping("/$cmd$") -public ResponseData<$ReturnType$> $cmd$(@RequestBody @Valid $Cmd$Request request) { - $ReturnType$ result = $cmd$Handler.exec(request.toCommand()); - return ResponseData.success(result); -} -``` -> Edit Template Variables 技巧 -> -> cmd参数的Expression可以填入decapitalize(Cmd) - -`aqry` 适配mvc透出查询详情 -```java - -@Autowired -$Qry$.Handler $qry$Handler; - -@Schema(description = "接口说明") -@GetMapping("/$qry$") -public ResponseData<$Qry$.$Qry$Dto> $qry$(@Valid $Qry$ param) { - $Qry$.$Qry$Dto result = $qry$Handler.exec(param); - return ResponseData.success(result); -} -``` -`aqryl` 适配mvc透出查询列表 -```java - -@Autowired -$Qry$.Handler $qry$Handler; - -@Schema(description = "接口说明") -@GetMapping("/$qry$") -public ResponseData> $qry$(@Valid $Qry$ param) { - List<$Qry$.$Qry$Dto> result = $qry$Handler.exec(param); - return ResponseData.success(result); -} -``` -`aqryp` 适配mvc透出查询分页列表 -```java - -@Autowired -$Qry$.Handler $qry$Handler; - -@Schema(description = "接口说明") -@PostMapping("/$qry$") -public ResponseData> $qry$(@RequestBody @Valid $Qry$ param) { - PageData<$Qry$.$Qry$Dto> result = $qry$Handler.exec(param); - return ResponseData.success(result); -} -``` ### have a nice trip! \ No newline at end of file diff --git "a/doc/00_\351\241\271\347\233\256\345\210\206\345\261\202\347\273\223\346\236\204\344\273\213\347\273\215.md" "b/doc/00_\351\241\271\347\233\256\345\210\206\345\261\202\347\273\223\346\236\204\344\273\213\347\273\215.md" new file mode 100644 index 0000000..465fa32 --- /dev/null +++ "b/doc/00_\351\241\271\347\233\256\345\210\206\345\261\202\347\273\223\346\236\204\344\273\213\347\273\215.md" @@ -0,0 +1,231 @@ +# 项目分层结构介绍 +## 简介 +```xml +org.netcorepal.cap4j.ddd.example +``` +基于基础包路径配置,`cap4j-ddd-codegen`插件在maven项目源码目录`src/main/java/org/netcorepal/cap4j/ddd/example`下将会生成4个`package`。 +> - `_share` 公共代码 +> - `adapter` 适配层(Interface Adapter) +> - `application` 应用层(Application Business Rules) +> - `domain` 领域层(Enterpprise Business Rules) + +以上代码分层完全遵循[整洁架构](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)对于代码分层组织的观点。 +当然,如果你有自己的理解,插件也支持完全自主的自定义模板,仅需自己修改脚手架模板[cap4j-ddd-codegen-template.json](../cap4j-ddd-codegen-template.json)并应用即可。 +![整洁架构](https://blog.cleancoder.com/uncle-bob/images/2012-08-13-the-clean-architecture/CleanArchitecture.jpg) + +## 领域层 +实现领域模型,聚合、实体、领域事件以及集成事件定义。 +```text +└── org.netcorepal.cap4j.ddd.example + └── domain + ├── _share (领域层公共代码,仅供领域层引用) + ├── aggregates (聚合) + └── services (领域服务) +``` +聚合内部包定义。 +```text +└── aggregates + └── order + ├── Order.java (聚合根) + ├── OrderItem.java (实体) + ├── enums (枚举) + │   └── OrderStatus.java + ├── events (领域事件) + │   └── OrderPlacedDomainEvent.java + ├── factory (聚合工厂) + │   ├── OrderFactory.java (聚合工厂类) + │   └── OrderPayload.java (工厂负载) + ├── schemas + │   ├── OrderItemSchema.java + │   └── OrderSchema.java + └── specs (实体规格约束) + └── OrderSpecification.java +``` + +## 应用层 +实现`CQS`模式,将功能用例(`UseCase`)抽象成命令(`Command`)或查询(`Query`)来实现。 +```text +└── org.netcorepal.cap4j.ddd.example + └── application +    ├── _share (应用层公共代码,仅供领域层引用) +    │   └── enums (应用层枚举类型) +    ├── clients (防腐层:包装三方服务调用接口) +    ├── commands (CQS的C:命令) +    ├── distributed (分布式) +    ├   ├── events (声明三方服务集成事件) +    │   └── sagas (SAGA是一种分布式事务最终一致性方案) +    ├── queries (CQS的Q:查询) +    └── subscribers (事件的订阅处理逻辑,或者称为eventhandlers) +    ├── domain (领域事件订阅处理逻辑) +    └── integration (集成事件订阅处理逻辑) +``` + +## 适配层 +如`适配`字面意思,放置各层(领域层`domain`、应用层`application`)定义的接口实现。整洁架构中称其为接口适配层(`Interface Adapters`)。 + +该层是领域层和应用层业务逻辑所依赖`抽象接口`的技术适配实现,适配层设计是DI原则的实践。 + +举个例子来理解抽象接口,比如我们常见的电商场景,用户在商城下单,需要通知仓库打包发货。那么这个`通知`可能就会需要抽象出一个`通知功能接口`,来承接下单流程的顺利进行。 +通知功能接口的定义(方法签名、入参、出参等)是应用层关心的事。但是通知功能接口如何实现,就是适配层的事了,通过短信也好、电话也好,能够实现通知功能接口定义的核心效果即可。 + +```text +└── org.netcorepal.cap4j.ddd.example + └── adapter +    ├── _share (适配层公共代码,仅供适配层引用) +    │   └── configure +    │   └── ApolloConfig.java (配置中心) +    ├── application (应用层接口实现) +    │   ├── _share +    │   │   └── configure +    │   │   └── MyIntegrationEventInterceptor.java (集成事件拦截器) +    │   ├── distributed +    │   │   └── clients (防腐端实现) +    │   └── queries (查询请求适配实现) +    ├── domain (领域层接口实现) +    │   ├── _share +    │   │   └── configure +    │   │   ├── MyDomainEventInterceptor.java (领域事件拦截器) +    │   │   └── MyEventMessageInterceptor.java (事件消息拦截器) +    │   └── repositories (实现聚合仓储接口) +    ├── infra (基础设施适配接口实现) +    │   ├── _share +    │   ├── jdbc (服务于应用层CQS的Q,jdbc查询工具类) +    │   │   └── NamedParameterJdbcTemplateDao.java +    │   └── mybatis (服务于应用层CQS的Q,mybatis集成) +    │   ├── _share +    │   │   └── MyEnumTypeHandler.java +    │   └── mapper +    └── portal (端口) +    ├── api (SpringMVC相关代码) +    │   ├── TestController.java +    │   └── _share +    │   ├── ResponseData.java +    │   ├── Status.java +    │   └── configure +    │   ├── CommonExceptionHandler.java +    │   ├── MvcConfig.java +    │   └── SwaggerConfig.java +    ├── jobs (定时任务相关代码) +    │   └── _share +    │   └── configure +    │   └── XxlJobConfig.java +    └── queues (消息队列相关代码) +``` + +## 公共代码 +放置公共代码。 +```text +└── org.netcorepal.cap4j.ddd.example + └── _share +    ├── CodeEnum.java (响应状态码枚举) +    ├── Constants.java (公共常量) +    └── exception (自定义业务异常) +    ├── ErrorException.java +    ├── KnownException.java +    └── WarnException.java +``` + +## 项目目录树 +```text +. +├── pom.xml +└── src + ├── main + │   ├── java + │   │   └── org + │   │   └── netcorepal + │   │   └── cap4j + │   │   └── ddd + │   │   └── example + │   │   ├── StartApplication.java + │   │   ├── _share + │   │   │   ├── CodeEnum.java + │   │   │   ├── Constants.java + │   │   │   └── exception + │   │   │   ├── ErrorException.java + │   │   │   ├── KnownException.java + │   │   │   └── WarnException.java + │   │   ├── adapter + │   │   │   ├── _share + │   │   │   │   └── configure + │   │   │   │   └── ApolloConfig.java + │   │   │   ├── application + │   │   │   │   ├── _share + │   │   │   │   │   └── configure + │   │   │   │   │   └── MyIntegrationEventInterceptor.java + │   │   │   │   ├── distributed + │   │   │   │   │   └── clients (防腐端实现) + │   │   │   │   └── queries (查询请求适配实现) + │   │   │   ├── domain + │   │   │   │   ├── _share + │   │   │   │   │   └── configure + │   │   │   │   │   ├── MyDomainEventInterceptor.java + │   │   │   │   │   └── MyEventMessageInterceptor.java + │   │   │   │   └── repositories + │   │   │   ├── infra + │   │   │   │   ├── _share + │   │   │   │   ├── jdbc + │   │   │   │   │   └── NamedParameterJdbcTemplateDao.java + │   │   │   │   └── mybatis + │   │   │   │   ├── _share + │   │   │   │   │   └── MyEnumTypeHandler.java + │   │   │   │   └── mapper + │   │   │   └── portal + │   │   │   ├── api + │   │   │   │   ├── TestController.java + │   │   │   │   └── _share + │   │   │   │   ├── ResponseData.java + │   │   │   │   ├── Status.java + │   │   │   │   └── configure + │   │   │   │   ├── CommonExceptionHandler.java + │   │   │   │   ├── MvcConfig.java + │   │   │   │   └── SwaggerConfig.java + │   │   │   ├── jobs + │   │   │   │   └── _share + │   │   │   │   └── configure + │   │   │   │   └── XxlJobConfig.java + │   │   │   └── queues + │   │   ├── application + │   │   │   ├── _share + │   │   │   │ └── enums + │   │   │   │── commands + │   │   │   ├── distributed + │   │   ├   │ ├── events + │   │   │   │ └── sagas + │   │   │   ├── queries + │   │   │   └── subscribers + │   │   │   ├── domain + │   │   │   └── integration + │   │   └── domain + │   │   ├── _share + │   │   ├── aggregates + │   │   │ └── order (示例聚合) + │   │   │ ├── Order.java (聚合根) + │   │   │ ├── OrderItem.java (实体) + │   │   │ ├── enums (枚举) + │   │   │ │   └── OrderStatus.java + │   │   │ ├── events (领域事件) + │   │   │ │   └── OrderPlacedDomainEvent.java + │   │   │ ├── factory (聚合工厂) + │   │   │ │   ├── OrderFactory.java (聚合工厂类) + │   │   │ │   └── OrderPayload.java (工厂负载) + │   │   │ ├── schemas + │   │   │ │   ├── OrderItemSchema.java + │   │   │ │   └── OrderSchema.java + │   │   │ └── specs (实体规格约束) + │   │   │ └── OrderSpecification.java + │   │   └── services + │   └── resources + │   ├── mapper + │   ├── application.properties + │   ├── ddl.sql + │   └── logback.xml + └── test + └── java + └── org + └── netcorepal + └── cap4j + └── ddd + └── example + └── AppTest.java +``` diff --git "a/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" new file mode 100644 index 0000000..bcdb971 --- /dev/null +++ "b/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -0,0 +1,688 @@ +# 领域层编码指南 +领域层用于实现领域模型,是企业核心业务逻辑的实现方案。 + +众所`应`周知,在领域模型中,作为聚合根的实体是领域模型中的核心元素。 +以聚合根(Aggregate Root)为中心,实体(Enity)、值对象(Value Object)、领域事件(Domain Event)、仓储(Repository)、规约(Specification)、聚合工厂(Domain Model Factory)各司其职,共同组成一个分工协作职责分明的聚合。 + +而作为业务信息持久化载体的实体和值对象是领域模型中最重要的元素,其结构设计将直接影响聚合内其他元素的设计与实现。 + +所以,我们可以激进一点地认为,领域模型的设计关键,就是实体(或值对象)及其之间关系的设计。 + +为了能够在设计实现阶段方便地进行实体模型迭代,cap4j在`cap4j-ddd-codegen`中提供了两个命令来支持基于db-first模式的实体模型设计。 +- `gen-entity` +- `gen-repository` + + +## 实体持久化映射 +这在大部分的场景下,就是对ORM技术的应用。 + +### How to DB First? +简单来说,即通过数据库schema元信息,来完成实体代码生成。 +这通过如下映射规则完成: + +- `表 table` _<--映射-->_ `实体类 entity class` +- `字段 column` _<--映射-->_ `实体属性 entity property` +- `主键字段引用 key reference` _<--映射-->_ `实体关系 entity reference` + +但数据库的shcema元信息只能表达实体类、实体类属性以及实体之间的关系信息。 + +关于聚合内哪个实体是聚合根?有哪些领域事件?都不确定。 + +为了增强设计迭代的顺畅性,cap4j设计了一套基于数据库注释(comment)的注解语法,来增强数据库schema的信息表达能力。 + +这套语法的使用非常简单。 通常情况下(比如都是单实体聚合的领域模型)甚至不需要这些注解语法也可以让ORM代码生成正常工作。 + + +### 关键注解详细介绍 +> `@R`; 支持表注解 +> +> 指示插件将该表映射成一个聚合根实体。该注解可省略,除非标注`@P`,否则默认认为是所有表所映射的实体单独构成一个聚合。 + +> `@P`=_parent_entity_table_; 支持表注解 +> +> 指示插件将改变映射成聚合内普通实体,该实体在 _parent_entity_table_ 的实体中有一个引用属性。 + +> `@T`=_JavaType_; 支持表注解、列注解 +> +> 在表的注释中注解指示插件该表映射的实体类名不按默认规则生成,直接使用注解赋赋值的 _JavaType_ 作为类名。 + +> `@E`=_0_:_ENUM_FIELD_:_枚举字段注释_; 支持列注解 +> +> 指示插件映射该字段是,该字段需要映射成Java枚举类型,该注解需要配合`@T`一起工作。需要利用`@T`注解给枚举类命名。 + +**详细帮助** + +通过插件help命令可以获得 + +```shell +mvn io.github.netcorepal:cap4j-ddd-codegen-maven-plugin:1.0.0-alpha-2:help +# or +mvn cap4j-ddd-codegen:help +``` + +### ORM实体生成示例 +```sql +CREATE TABLE `order` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `order_no` varchar(100) NOT NULL DEFAULT '' COMMENT '订单编号', + `order_status` int unsigned NOT NULL DEFAULT '0' COMMENT '订单状态@T=OrderStatus;@E=0:INIT:待支付|1:PAID:已支付|-1:CLOSED:已关闭;', + `amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '总金额', + `version` bigint unsigned NOT NULL DEFAULT '0', + `db_created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `db_updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `db_deleted` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `idx_db_created_at` (`db_created_at`), + KEY `idx_db_updated_at` (`db_updated_at`) +) COMMENT='订单'; + +CREATE TABLE `order_item` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `order_id` bigint NOT NULL DEFAULT '0' COMMENT '关联主订单', + `name` varchar(100) NOT NULL DEFAULT '' COMMENT '名称', + `price` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '单价', + `count` int NOT NULL DEFAULT '0' COMMENT '数量', + `version` bigint unsigned NOT NULL DEFAULT '0', + `db_created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `db_updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `db_deleted` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `idx_db_created_at` (`db_created_at`), + KEY `idx_db_updated_at` (`db_updated_at`) +) COMMENT='订单项\n @P=order'; +# 以上sql语句隐含了如下实体映射关系: +# 订单表(order)对应实体是一个聚合根,并且订单项表(order_item)对应实体是order聚合的实体成员。 +# 订单表(order)的订单状态字段(order_status)将会映射成OrderStatus的Java类型,该OrderStatus是一个enum类型,有3个字段成员,INIT、PAID、CLOSED +``` +默认情况下,所有数据库表都将会映射成一个Java实体类,该实体类将构成一个聚合,并且作为该聚合的聚合根。如果聚合存在其他实体,则其他实体对应的表注释标注@P注解即可。 + +> @P指示该表对应的Java实体类属于某个聚合内的实体成员。 +> +> @E负责生成OrderStatus枚举。@E需要配合@T才能完成数据库字段的Java枚举映射。 +> +> @T负责将Order实体的orderStatus字段映射成OrderStatus枚举,@T也可以单独工作,用于DB类型<->Java类型的强制自定义映射。 +> +> +> 如果想要对这套语法有个详细完整的了解,可以通过如下maven指令获取语法帮助。 +> 需要注意的是,当前`cap4j-ddd-codegen:gen-entity`仅支持基于MySQL数据库注释的注解解析,后续会增加其他数据的支持。 + +核对pom.xml配置的gen-entity所需要的关键信息 +> ``_项目基础包名,一般为com.yourcompany.project_`` +> +> ``_数据库密码_`` +> +> ``_数据库密码_`` +> +> ``_数据库密码_`` +> +> ``_数据库名称_`` + +如果没有问题,先后执行如下命令即可查看代码生成源文件 +```shell +mvn cap4j-ddd-codegen:gen-entity +mvn cap4j-ddd-codegen:gen-repository +``` + +**实体(聚合根)** + +**代码位置规范**:`${basePackage}.domain.aggregates.${aggregate}` +```java +package org.netcorepal.cap4j.ddd.example.domain.aggregates; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; +import static org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisorSupport.events; + +import javax.persistence.*; + +/** + * 订单 + * + * 本文件由[cap4j-ddd-codegen-maven-plugin]生成 + * 警告:请勿手工修改该文件的字段声明,重新生成会覆盖字段声明 + */ +@Aggregate(aggregate = "Order", name = "Order", type = "root", description = "订单") +@Entity +@Table(name = "`order`") +@DynamicInsert +@DynamicUpdate +@SQLDelete(sql = "update `order` set `db_deleted` = 1 where `id` = ? and `version` = ? ") +@Where(clause = "`db_deleted` = 0") + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +public class Order { + + // 【行为方法开始】 + + + + // 【行为方法结束】 + + + + // 【字段映射开始】本段落由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 + + @Id + @GeneratedValue(generator = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") + @GenericGenerator(name = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator", strategy = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") + @Column(name = "`id`") + Long id; + + + /** + * 订单编号 + * varchar(100) + */ + @Column(name = "`order_no`") + String orderNo; + + /** + * 订单状态 + * 0:INIT:待支付;-1:CLOSED:已关闭;1:PAID:已支付 + * int unsigned + */ + @Convert(converter = org.netcorepal.cap4j.ddd.example.domain.aggregates.enums.OrderStatus.Converter.class) + @Column(name = "`order_status`") + org.netcorepal.cap4j.ddd.example.domain.aggregates.enums.OrderStatus orderStatus; + + /** + * 总金额 + * decimal(14,2) + */ + @Column(name = "`amount`") + java.math.BigDecimal amount; + + @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true) @Fetch(FetchMode.SUBSELECT) + @JoinColumn(name = "`order_id`", nullable = false) + private java.util.List orderItems; + + /** + * 数据版本(支持乐观锁) + */ + @Version + @Column(name = "`version`") + Integer version; + + // 【字段映射结束】本段落由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 +} + +``` +**实体(聚合的普通实体成员)** + +**代码位置规范**:`${basePackage}.domain.aggregates.${aggregate}` + +```java +package org.netcorepal.cap4j.ddd.example.domain.aggregates.order; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; +import static org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisorSupport.events; + +import javax.persistence.*; + +/** + * 订单项 + * + * 本文件由[cap4j-ddd-codegen-maven-plugin]生成 + * 警告:请勿手工修改该文件的字段声明,重新生成会覆盖字段声明 + */ +@Aggregate(aggregate = "Order", name = "OrderItem", type = "entity", relevant = { "Order" }, description = "订单项") +@Entity +@Table(name = "`order_item`") +@DynamicInsert +@DynamicUpdate +@SQLDelete(sql = "update `order_item` set `db_deleted` = 1 where `id` = ? and `version` = ? ") +@Where(clause = "`db_deleted` = 0") + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +public class OrderItem { + + // 【行为方法开始】 + + + + // 【行为方法结束】 + + + + // 【字段映射开始】本段落由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 + + @Id + @GeneratedValue(generator = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") + @GenericGenerator(name = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator", strategy = "org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator") + @Column(name = "`id`") + Long id; + + + /** + * 名称 + * varchar(100) + */ + @Column(name = "`name`") + String name; + + /** + * 单价 + * decimal(14,2) + */ + @Column(name = "`price`") + java.math.BigDecimal price; + + /** + * 数量 + * int + */ + @Column(name = "`count`") + Integer count; + + /** + * 数据版本(支持乐观锁) + */ + @Version + @Column(name = "`version`") + Integer version; + + // 【字段映射结束】本段落由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 +} + + +``` +**枚举** + +**代码位置规范**:`${basePackage}.domain.aggregates.${aggregate}.enums` + +```java +package org.netcorepal.cap4j.ddd.example.domain.aggregates.order.enums; + +import lombok.Getter; + +import javax.persistence.*; +import java.util.HashMap; +import java.util.Map; + +/** + * 本文件由[cap4j-ddd-codegen-maven-plugin]生成 + * 警告:请勿手工修改该文件,重新生成会覆盖该文件 + */ +public enum OrderStatus { + + /** + * 待支付 + */ + INIT(0, "待支付"), + /** + * 已关闭 + */ + CLOSED(-1, "已关闭"), + /** + * 已支付 + */ + PAID(1, "已支付"), + ; + @Getter + private int code; + @Getter + private String name; + + OrderStatus(Integer code, String name){ + this.code = code; + this.name = name; + } + + private static Map enums = null; + public static OrderStatus valueOf(Integer code) { + if(enums == null) { + enums = new HashMap<>(); + for (OrderStatus val : OrderStatus.values()) { + enums.put(val.code, val); + } + } + if(enums.containsKey(code)){ + return enums.get(code); + } + throw new RuntimeException("枚举类型OrderStatus枚举值转换异常,不存在的值" + code); + } + + /** + * JPA转换器 + */ + public static class Converter implements AttributeConverter{ + @Override + public Integer convertToDatabaseColumn(OrderStatus val) { + return val.code; + } + + @Override + public OrderStatus convertToEntityAttribute(Integer code) { + return OrderStatus.valueOf(code); + } + } +} + + +``` +**聚合仓储** + +**代码位置规范**:`${basePackage}.adapter.domain.repositories` + +```java +package org.netcorepal.cap4j.ddd.example.adapter.domain.repositories; + +import org.netcorepal.cap4j.ddd.example.domain.aggregates.Order; + +/** + * 本文件由[cap4j-ddd-codegen-maven-plugin]生成 + */ +public interface OrderRepository extends org.netcorepal.cap4j.ddd.domain.repo.AggregateRepository { + // 【自定义代码开始】本段落之外代码由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 + + @org.springframework.stereotype.Component + @org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate(aggregate = "Order", name = "Order", type = "repository", description = "") + public static class OrderJpaRepositoryAdapter extends org.netcorepal.cap4j.ddd.domain.repo.AbstractJpaRepository + { + public OrderJpaRepositoryAdapter(org.springframework.data.jpa.repository.JpaSpecificationExecutor jpaSpecificationExecutor, org.springframework.data.jpa.repository.JpaRepository jpaRepository) { + super(jpaSpecificationExecutor, jpaRepository); + } + } + + // 【自定义代码结束】本段落之外代码由[cap4j-ddd-codegen-maven-plugin]维护,请不要手工改动 +} + +``` + +## 领域事件定义与发布 +领域事件是发布订阅模式在领域模型中的应用,他将聚合之间的业务依赖关系给独立出来,使得聚合之间没有任何依赖。 + +基于`Outbox`模式,cap4j可以实现分离聚合间的持久化变更事务,并能通过内置的补偿机制,自动实现聚合间事务的最终一致性。 + +当然,这个事务分离的选择是由设计人员控制的,我们可以基于实际场景的需要,选择是否需要开启这一特性。 + +而在应用这一特性之前,我们需要做一些基本的依赖部署工作。 + +### 创建发件箱表 + +为了实现`Outbox`模式,本地业务库中需要创建标准发件箱表。 + +这一步非常简单,利用插件`gen-arch`命令对项目结构初始化后,项目内`resources/ddl.sql`即包含了完整的发件箱表建表SQL语句。只需将里面的语句在服务所在业务数据库中执行即可。 +> 这里有个隐含的原则,即我们的服务都有独属于自己的业务数据库。这很重要,如果对这个原则有疑惑,可以基于issue讨论这个原则的必要性。默认大家都认同这个普遍共识的原则。 + +### 领域事件定义 +**代码位置规范**:`${basePackage}.domain.aggregates.${aggregate}.events` + +cap4j的领域事件发布需配合`UnitOfWork`模式实现,因为领域事件的发布与聚合内实体属性状态变更的持久化是捆绑的,这也是领域事件一般定义在领域层(`domain`)的原因。 + +通过[`DomainEvent`](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/annotation/DomainEvent.java)注解的类,会被cap4j识别为领域事件。 + +`DomainEvent` 注解属性`persist`设置成 `true`,cap4j将会认为该领域事件的传播需要事务隔离,并将应用Outbox模式进行事件的传播。 + +后续即可通过[`DomainEventSupervisor`](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java)接口的`attach`方法来向当前线程上线文附加领域事件。 +一旦 [`UnitOfWork`](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java)接口的`save()`方法成功提交事务。则cap4j将会保障事件被各个订阅方至少消费一次。 + +@DomainEvent注解属性详解 +> - `value()` 领域事件名称。 +> - `persist()` 事件发布记录持久化控制。可以通过`persist=true`控制事件进入发件箱表,并脱离事件所在实体持久化变更上下文事务中。以避免订阅逻辑异常影响发布事务的完成。 + +应用场景例子说明 +> - `消费方与订阅方事务隔离` @DomainEvent(persist=true) +> - `消费方与订阅方同一事务` @DomainEvent + +关于领域事件与集成事件 +> 集成事件指会对系统内其他服务发布的领域事件。通常如果要区分领域事件和集成事件,那么领域事件一般指的是不需要对外发布的业务事件,仅在内部聚合之间应用。 +> + +```java +package org.netcorepal.cap4j.ddd.example.domain.aggregates.events; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 下单领域事件 + * + * @author bingking338 + */ +@DomainEvent( + persist = true +) +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class OrderPlacedDomainEvent { + /** + * 订单号 + */ + String orderNo; + /** + * 订单金额 + */ + BigDecimal amount; + /** + * 下单时间 + */ + LocalDateTime orderTime; +} +``` + +### 领域事件发布 +**代码位置规范**:`${basePackage}.domain.aggregates.${aggregate}.${AggregateRoot}` + +重点:领域事件从定义上来说就是基于业务实体状态的变更行为所产生产生的,所以我们应在实体行为中发布领域事件, + +领域事件的发布分两个步骤完成 +1. 事件附加到实体: + 通过[DomainEventSupervisor.java](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventSupervisor.java)接口的attach方法在实体行为方法内完成。可以通过 `import static` 将[RequestSupervisorSupport](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/RequestSupervisorSupport.java)的静态方法`events()`引入到实体类中,插件默认配置已支持。 +2. 关联实体持久化事务提交成功: + 在应用层的命令中调用[`UnitOfWork`](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java)接口的`save()`方法。 + +领域事件附加有三种方式 + +> `即时发送` DomainEventSupervisorSupport.events().attach(Object eventPayload, Object entity) +> +> `延时发送` DomainEventSupervisorSupport.events().attach(Object eventPayload, Object entity, Duration delay) +> +> `定时发送` DomainEventSupervisorSupport.events().attach(Object eventPayload, Object entity, LocalDateTime schedule) +> +小技巧:可以通过静态导入,更方便地调用领域事件管理器。 +```java +import static org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisorSupport.events; +``` + +**发送重试** + +以Outbox模式传播的领域事件内部会有重试机制来保障事务的最终一致性,以实现领域事件被各个订阅方至少消费一次的承诺。 +但是为了保障系统不会因为传播链路的故障出现无限重试,这个`至少一次`也是有前提的,通过[@Retry](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/annotation/Retry.java)我们可以配置事件传播的失效以及重试次数。 + +### 事件附加示例 +```java +import static org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisorSupport.events; + + +// 代码省略... +public class Order { + // 代码省略... + public class Order { + + // 【行为方法开始】 + + /** + * 下单初始化 + * @param items + */ + public void init(List items){ + // 代码省略... + events().attach(OrderPlacedDomainEvent.builder() + .orderNo(this.orderNo) + .amount(this.amount) + .orderTime(LocalDateTime.now()) + .build(), this); + } + + // 【行为方法结束】 + // 代码省略... + } +} +``` + +## 聚合工厂(领域模型工厂) +**代码位置规范**:`${basePackage}.domain.aggregates.${aggregate}` + +领域模型工厂负责聚合根实例的初始创建。 + +推荐有两种编码风格来实现领域模型工厂。 + +### 工厂方法模式 +在聚合根中定义静态方法。优势:简单方便 +```java + +// 代码省略... +@Builder +public class Order { + // 代码省略... + public class Order { + + // 【行为方法开始】 + + /** + * 工厂方法 + */ + public static Order create(){ + // 代码省略... + return builder().build(); + } + + // 【行为方法结束】 + // 代码省略... + } +} +``` + +### 创建聚合工厂服务 +优势:模型完整性更强,设计掌控力度更大。 + +实现 [AggregateFactory](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactory.java)接口。并标记`@Service`注入`Spring IoC`容器。 + +```java +package org.netcorepal.cap4j.ddd.example.domain.aggregates.order.factory; + +import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.Order; +import lombok.Builder; +import lombok.Data; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload; + +import java.math.BigDecimal; + +/** + * todo: 工厂负载描述 + * + * @author cap4j-ddd-codegen + * @date 2024/09/09 + */ +@Data +@Builder +public class OrderPayload implements AggregatePayload { + private String orderNo; + //... +} + +``` +```java +package org.netcorepal.cap4j.ddd.example.domain.aggregates.order.factory; + +import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.Order; +import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.enums.OrderStatus; +import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory; +import org.springframework.stereotype.Service; + +/** + * TODO: 聚合工厂 + * + * @author cap4j-ddd-codegen + * @date 2024/09/09 + */ +@Service +public class OrderFactory implements AggregateFactory { + + @Override + public Order create(OrderPayload payload) { + + return Order.builder() + .orderStatus(OrderStatus.INIT) + .orderNo(payload.getOrderNo()) + //... + .build(); + } +} + +``` +## 工作单元 (实现由cap4j组件提供,无需编码) +即`UnitOfWork`模式的具体实现。工作单元通常在应用层(application)使用。 +[UnitOfWork](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java) +其仅有三个系列方法 +> `persist`: 附加实体**状态变更同步到持久化源**的`意图`到持久化上下文,save方法调用后,状态变更将自动同步到持久化源。 +> `remove`: 附加实体**从持久化源中移除**的`意图`到持久化上下文,save方法调用后,实体将自动同步到持久化源。 +> `save`: 以上`persist`和`remove`的意图以事物方式同步到持久化源。 +> +> 持久化源即数据库,比如Mysql数据库。 + +## 仓储(实现由插件自动生成,无需编码) +**代码位置规范**:`${basePackage}.adapter.domain.repositories` + +仓储负责聚合根实例的检索查询需求。仓储严格遵循以`集合(Collection)`的抽象方式来管理存在持久化源中的聚合实体。 + +而实体存入持久化源的职责将完全由[UnitOfWork](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java)承接实现。 + +基于以上的原则,cap4j实现了完全无需额外开发的聚合仓储存取机制。仓储代码可完全由`cap4j-ddd-codegen:gen-repository`命令自动生成。 + +而UnitOfWork的实现由cap4j组件内部实现提供可以直接使用。 + +以上代码完全依赖成熟的`Spring JPA`来实现。 + +> 仓储接口实现[AbstractJpaRepository](../ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/AbstractJpaRepository.java) +> 中的所有 `Object condition` 参数仅支持 org.springframework.data.jpa.domain.Specification 实例。 +> +> 详细使用请参照 org.springframework.data.jpa.repository.JpaSpecificationExecutor 接口。 + +## 规约 +**代码位置规范**:`${basePackage}.domain.aggregates.${aggregate}.specs` + +实现规约接口[Specification](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/Specification.java) +并标记`@Service`注入`Spring IoC`容器,当对应聚合根持久化时会自动获取对应实体聚合类型的规约实例,并自动调用`specify`接口执行校验。 + +## 领域服务 +**代码位置规范**:`${basePackage}.domain.services` + +> `@DomainService`(name = "领域服务名称") +> +领域服务需标记[@DomainService](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/annotation/DomainService.java)注解,并标记`@Service`注入`Spring IoC`容器。 + diff --git "a/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" new file mode 100644 index 0000000..f47175b --- /dev/null +++ "b/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -0,0 +1,175 @@ +# 应用层编码指南 +**原则**:两件事情 + +1. `CQS`:命令的定义与实现;查询的定义; +2. `事件`:领域事件的订阅;集成事件的发布与订阅; + +## 中介者 +cap4j提供中介者模式来方便调用各个模型设计元素。 +> `聚合工厂中介者`: `Mediator`.`factories`().`create`(_entityPayload_); // 创建聚合根实例 +> +> `仓储中介者`: `Mediator`.`repositories`().`find*`(); // 查询检索聚合根 +> +> `领域服务中介者`: `Mediator`.`service`().`getService`(_domainServiceClass_); // 查询检索聚合根 +> +> `工作单元实例`: `Mediator`.`uow`().`persist`(_entity_)、`remove`(_entity_)、 `save`(); // 聚合根实例字段变更同步持久化 +> +> `集成事件中介者`: `Mediator`.`events`().`attach`(_eventPayload_); // 发布集成事件 +> +> `命令中介者`: `Mediator`.`commands`().`send`(_commandRequest_); // 发出命令请求 +> +> `查询中介者`: `Mediator`.`queries`().`send`(_queryRequest_); // 发出命令请求 +> +> `请求中介者`: `Mediator`.`requests`().`send`(_request_); // 发出请求 + +## 代码生成 +cap4j提供快速创建模型设计元素。 + + +## 命令 +**代码位置规范**:`${basePackage}.application.commands` + +重点关注使用`聚合工厂中介者`、`仓储中介者`、`工作单元实例`。 + +在某些情形下会使用到`请求中介者`执行外部服务请求,以及`集成事件中介者`对外发出集成事件。 + +特别需慎用`领域服务中介者`,警惕领域模型贫血化问题。 + +### +`Mediator`.`repositories`() + + + +## 查询 +基于CQS模式,查询可独立实现。不必依赖领域模型(领域层)实现,可直接依据DI原则,由适配层实现即可。 + +应用层仅需提供查询(Query)出入参(Request\Response)声明定义。 + +### 查询响应类 +```java +package org.netcorepal.cap4j.ddd.example.application.queries.order; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * todo: 查询响应描述 + * + * @author cap4j-ddd-codegen + * @date 2024/09/09 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GetOrderQryResponse { + Long id; +} + +``` + +### 查询参数类 +```java +package org.netcorepal.cap4j.ddd.example.application.queries.order; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.netcorepal.cap4j.ddd.application.RequestParam; + + +/** + * todo: 查询请求参数描述 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GetOrderQryRequest implements RequestParam { + Long id; +} +``` + +## 集成事件声明 +**代码位置规范**:`${basePackage}.application.distributed.events` + +仅需标记@IntegrationEvent 注解,即可定义集成事件。 +```java +@IntegrationEvent(value = "some-integration-event-topic", subscriber = "${spring.application.name}") +public class SomeIntegrationEvent { + // 省略消息体字段定义... +} +``` + +## 集成事件发布 +### 显式发布集成事件 +**代码位置规范**:`${basePackage}.application.subscribers.domain`包中的领域事件订阅实现逻辑中; + +**代码位置规范**:`${basePackage}.application.commands`包中的命令实现逻辑中。 + +```java +Mediator.events().attach(SomeIntegrationEvent.builder() + //... + .build()); +``` + + +>[Mediator](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/Mediator.java).`events`() +返回[IntegrationEventSupervisor](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisor.java)接口。 +### 声明式发布集成事件 +集成事件声明上标记`@AutoRelease`注解。cap4j将会利用反射技术关联到对应领域事件的发布并自动触发集成事件的发布。 +```java +package org.netcorepal.cap4j.ddd.example.application.distributed.events; + +import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.events.OrderClosedDomainEvent; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRelease; +import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; + +/** + * todo: 集成事件描述 + * + * @author cap4j-ddd-codegen + * @date 2024/09/09 + */ +@IntegrationEvent(value = "cap4j-example-order-placed", subscriber = "${spring.application.name}") +@AutoRelease(sourceDomainEventClass = OrderClosedDomainEvent.class) +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class OrderClosedIntegrationEvent implements Converter { + private Long id; + + @Override + public OrderClosedIntegrationEvent convert(OrderClosedDomainEvent source) { + return OrderClosedIntegrationEvent.builder() + .id(source.getId()) + //... + .build(); + } +} + +``` + +## 集成事件订阅 +**代码位置规范**:`${basePackage}.application.subscribers.integration` + +实现对应事件的[EventSubscriber](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventSubscriber.java) +服务并标记`@Service`注解注册到`Spring IoC`容器中,即可实现对应事件发生时订阅逻辑的自动触发调用。 + + +## 领域事件订阅, +**代码位置规范**:`${basePackage}.application.subscribers.domain` +领域事件与集成事件的订阅在业务上的职责与概念并无差别,接口也并无差别。 + +直接参考集成事件订阅说明。 + +## SAGA +待实现... diff --git "a/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" new file mode 100644 index 0000000..bb90d19 --- /dev/null +++ "b/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -0,0 +1,32 @@ +# 适配层编码指南 +**原则**:两件事情 + +1. 实现领域层、应用层的抽象接口。 +2. 对外开放端口实现。(接口、队列、任务...) + +## 查询适配实现 +**代码位置规范**:`${basePackage}.adapter.application.queries` + +待补充... + +## 防腐端适配实现 +**代码位置规范**:`${basePackage}.adapter.application.queries` + +待补充... + +## MVC接口 +**代码位置规范**:`${basePackage}.adapter.application.portal.api` + +待补充... + +## 定时任务 +**代码位置规范**:`${basePackage}.adapter.application.portal.job` + +待补充... + +## 消息队列 +**代码位置规范**:`${basePackage}.adapter.application.portal.queues` + +待补充... + + From 4eb1d2474ef4d0e56ec570f28fa65b853e8725eb Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 12 Sep 2024 15:37:32 +0800 Subject: [PATCH 26/62] =?UTF-8?q?=E6=8A=BD=E8=B1=A1=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E6=8B=A6=E6=88=AA=E5=99=A8=E7=AE=A1=E7=90=86=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/RocketMqEventAutoConfiguration.java | 4 +- .../event/DomainEventAutoConfiguration.java | 46 ++++++---- ddd-core/pom.xml | 6 ++ .../IntegrationEventInterceptorManager.java | 26 ++++++ .../DefaultIntegrationEventSupervisor.java | 32 ++----- .../event/DomainEventInterceptorManager.java | 24 +++++ .../ddd/domain/event/EventInterceptor.java | 2 - .../event/EventMessageInterceptorManager.java | 17 ++++ .../impl/DefaultDomainEventSupervisor.java | 27 ++---- .../event/impl/DefaultEventPublisher.java | 89 +++++------------- .../impl/DefaultEventInterceptorManager.java | 90 +++++++++++++++++++ .../cap4j/ddd/share/misc/ClassUtils.java | 6 +- 12 files changed, 232 insertions(+), 137 deletions(-) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptorManager.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptorManager.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventMessageInterceptorManager.java create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultEventInterceptorManager.java diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java index d6ab5bf..cc1377a 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/event/RocketMqEventAutoConfiguration.java @@ -36,7 +36,7 @@ public class RocketMqEventAutoConfiguration { public DefaultIntegrationEventSupervisor defaultIntegrationEventSupervisor( EventPublisher eventPublisher, EventRecordRepository eventRecordRepository, - List integrationEventInterceptors, + IntegrationEventInterceptorManager integrationEventInterceptorManager, ApplicationEventPublisher applicationEventPublisher, @Value(CONFIG_KEY_4_SVC_NAME) String svcName @@ -44,7 +44,7 @@ public DefaultIntegrationEventSupervisor defaultIntegrationEventSupervisor( DefaultIntegrationEventSupervisor defaultIntegrationEventSupervisor = new DefaultIntegrationEventSupervisor( eventPublisher, eventRecordRepository, - integrationEventInterceptors, + integrationEventInterceptorManager, applicationEventPublisher, svcName ); diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java index f5f2432..c91a18a 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java @@ -2,14 +2,17 @@ import lombok.RequiredArgsConstructor; import org.netcorepal.cap4j.ddd.application.distributed.Locker; -import org.netcorepal.cap4j.ddd.application.event.*; -import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultEventPublisher; -import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultEventSubscriberManager; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptorManager; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventPublisher; import org.netcorepal.cap4j.ddd.domain.event.configure.EventProperties; import org.netcorepal.cap4j.ddd.domain.event.configure.EventScheduleProperties; import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultDomainEventSupervisor; +import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultEventPublisher; +import org.netcorepal.cap4j.ddd.domain.event.impl.DefaultEventSubscriberManager; import org.netcorepal.cap4j.ddd.domain.event.persistence.ArchivedEventJpaRepository; import org.netcorepal.cap4j.ddd.domain.event.persistence.EventJpaRepository; +import org.netcorepal.cap4j.ddd.impl.DefaultEventInterceptorManager; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.domain.EntityScan; @@ -25,13 +28,11 @@ import java.time.Duration; import java.util.List; -import static org.netcorepal.cap4j.ddd.share.Constants.*; +import static org.netcorepal.cap4j.ddd.share.Constants.CONFIG_KEY_4_SVC_NAME; /** * 基于RocketMq的领域事件(集成事件)实现自动配置类 * - * - * * @author binking338 * @date 2023/9/10 */ @@ -119,18 +120,18 @@ public EventPublisher defaultEventPublisher( EventSubscriberManager eventSubscriberManager, List integrationEventPublisheres, EventRecordRepository eventRecordRepository, - List eventMessageInterceptors, - List domainEventInterceptors, - List integrationEventInterceptors, + EventMessageInterceptorManager eventMessageInterceptorManager, + DomainEventInterceptorManager domainEventInterceptorManager, + IntegrationEventInterceptorManager integrationEventInterceptorManager, EventProperties eventProperties - ){ + ) { DefaultEventPublisher defaultEventPublisher = new DefaultEventPublisher( eventSubscriberManager, integrationEventPublisheres, eventRecordRepository, - eventMessageInterceptors, - domainEventInterceptors, - integrationEventInterceptors, + eventMessageInterceptorManager, + domainEventInterceptorManager, + integrationEventInterceptorManager, eventProperties.getPublisherThreadPoolSize() ); defaultEventPublisher.init(); @@ -158,7 +159,7 @@ public EventSubscriberManager defaultEventSubscriberManager( @Primary public DefaultDomainEventSupervisor defaultDomainEventSupervisor( EventRecordRepository eventRecordRepository, - List domainEventInterceptors, + DomainEventInterceptorManager domainEventInterceptorManager, EventPublisher eventPublisher, ApplicationEventPublisher applicationEventPublisher, @Value(CONFIG_KEY_4_SVC_NAME) @@ -166,15 +167,28 @@ public DefaultDomainEventSupervisor defaultDomainEventSupervisor( ) { DefaultDomainEventSupervisor defaultDomainEventSupervisor = new DefaultDomainEventSupervisor( eventRecordRepository, - domainEventInterceptors, + domainEventInterceptorManager, eventPublisher, applicationEventPublisher, svcName ); - DomainEventSupervisorSupport.configure((DomainEventSupervisor)defaultDomainEventSupervisor); + DomainEventSupervisorSupport.configure((DomainEventSupervisor) defaultDomainEventSupervisor); DomainEventSupervisorSupport.configure((DomainEventManager) defaultDomainEventSupervisor); return defaultDomainEventSupervisor; } + @Bean + @Primary + public DefaultEventInterceptorManager defaultEventInterceptorManager( + List eventMessageInterceptors, + List interceptors + ) { + DefaultEventInterceptorManager defaultEventInterceptorManager = new DefaultEventInterceptorManager( + eventMessageInterceptors, + interceptors + ); + return defaultEventInterceptorManager; + } + } \ No newline at end of file diff --git a/ddd-core/pom.xml b/ddd-core/pom.xml index 1c72f74..37df2b3 100644 --- a/ddd-core/pom.xml +++ b/ddd-core/pom.xml @@ -17,6 +17,12 @@ org.projectlombok lombok + + org.slf4j + slf4j-api + 1.7.36 + provided + org.springframework spring-tx diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptorManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptorManager.java new file mode 100644 index 0000000..5847d4d --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventInterceptorManager.java @@ -0,0 +1,26 @@ +package org.netcorepal.cap4j.ddd.application.event; + +import org.netcorepal.cap4j.ddd.domain.event.EventInterceptor; + +import java.util.Set; + +/** + * 集成事件拦截器管理器 + * + * @author binking338 + * @date 2024/9/12 + */ +public interface IntegrationEventInterceptorManager { + /** + * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 + * @return + */ + Set getOrderedIntegrationEventInterceptors(); + + /** + * + * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 + * @return + */ + Set getOrderedEventInterceptors4IntegrationEvent(); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java index 7698f22..fe7a57c 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/impl/DefaultIntegrationEventSupervisor.java @@ -1,18 +1,13 @@ package org.netcorepal.cap4j.ddd.application.event.impl; import lombok.RequiredArgsConstructor; -import org.netcorepal.cap4j.ddd.application.event.IntegrationEventAttachedTransactionCommittedEvent; -import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor; -import org.netcorepal.cap4j.ddd.application.event.IntegrationEventManager; -import org.netcorepal.cap4j.ddd.application.event.IntegrationEventSupervisor; +import org.netcorepal.cap4j.ddd.application.event.*; import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; import org.netcorepal.cap4j.ddd.domain.event.EventPublisher; import org.netcorepal.cap4j.ddd.domain.event.EventRecord; import org.netcorepal.cap4j.ddd.domain.event.EventRecordRepository; import org.netcorepal.cap4j.ddd.share.DomainException; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.OrderUtils; import org.springframework.transaction.event.TransactionalEventListener; import java.time.Duration; @@ -29,7 +24,7 @@ public class DefaultIntegrationEventSupervisor implements IntegrationEventSupervisor, IntegrationEventManager { private final EventPublisher eventPublisher; private final EventRecordRepository eventRecordRepository; - private final List integrationEventInterceptors; + private final IntegrationEventInterceptorManager integrationEventInterceptorManager; private final ApplicationEventPublisher applicationEventPublisher; private final String svcName; @@ -37,21 +32,6 @@ public class DefaultIntegrationEventSupervisor implements IntegrationEventSuperv private static final ThreadLocal> TL_EVENT_SCHEDULE_MAP = new ThreadLocal>(); private static final Set EMPTY_EVENT_PAYLOADS = Collections.emptySet(); - private List sortedIntegrationEventInterceptors = null; - - /** - * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 - * - * @return - */ - protected List getOrderedIntegrationEventInterceptors() { - if (sortedIntegrationEventInterceptors == null) { - sortedIntegrationEventInterceptors = new ArrayList<>(integrationEventInterceptors); - sortedIntegrationEventInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); - } - return sortedIntegrationEventInterceptors; - } - /** * 默认事件过期时间(分钟) * 一天 60*24 = 1440 @@ -78,7 +58,7 @@ public void attach(EVENT eventPayload, LocalDateTime schedule) { } eventPayloads.add(eventPayload); putDeliverTime(eventPayload, schedule); - getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onAttach(eventPayload, schedule)); + integrationEventInterceptorManager.getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onAttach(eventPayload, schedule)); } @@ -89,7 +69,7 @@ public void detach(EVENT eventPayload) { return; } eventPayloads.remove(eventPayload); - getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onDetach(eventPayload)); + integrationEventInterceptorManager.getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onDetach(eventPayload)); } @Override @@ -102,9 +82,9 @@ public void release() { EventRecord event = eventRecordRepository.create(); event.init(eventPayload, this.svcName, deliverTime, Duration.ofMinutes(DEFAULT_EVENT_EXPIRE_MINUTES), DEFAULT_EVENT_RETRY_TIMES); event.markPersist(true); - getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.prePersist(event)); + integrationEventInterceptorManager.getOrderedEventInterceptors4IntegrationEvent().forEach(interceptor -> interceptor.prePersist(event)); eventRecordRepository.save(event); - getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.postPersist(event)); + integrationEventInterceptorManager.getOrderedEventInterceptors4IntegrationEvent().forEach(interceptor -> interceptor.postPersist(event)); persistedEvents.add(event); } IntegrationEventAttachedTransactionCommittedEvent integrationEventAttachedTransactionCommittedEvent = new IntegrationEventAttachedTransactionCommittedEvent(this, persistedEvents); diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptorManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptorManager.java new file mode 100644 index 0000000..80d3bb7 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventInterceptorManager.java @@ -0,0 +1,24 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import java.util.Set; + +/** + * 领域事件拦截器管理器 + * + * @author binking338 + * @date 2024/9/12 + */ +public interface DomainEventInterceptorManager { + /** + * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 + * @return + */ + Set getOrderedDomainEventInterceptors(); + + /** + * + * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 + * @return + */ + Set getOrderedEventInterceptors4DomainEvent(); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventInterceptor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventInterceptor.java index 8b5f53c..ff673f4 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventInterceptor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventInterceptor.java @@ -1,7 +1,5 @@ package org.netcorepal.cap4j.ddd.domain.event; -import org.netcorepal.cap4j.ddd.domain.event.EventRecord; - /** * 事件拦截器 * diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventMessageInterceptorManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventMessageInterceptorManager.java new file mode 100644 index 0000000..286f960 --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/EventMessageInterceptorManager.java @@ -0,0 +1,17 @@ +package org.netcorepal.cap4j.ddd.domain.event; + +import java.util.Set; + +/** + * 事件消息拦截器管理器 + * + * @author binking338 + * @date 2024/9/12 + */ +public interface EventMessageInterceptorManager { + /** + * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 + * @return + */ + Set getOrderedEventMessageInterceptors(); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java index 31d56f1..78ad0f9 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultDomainEventSupervisor.java @@ -9,8 +9,6 @@ import org.netcorepal.cap4j.ddd.domain.event.DomainEventAttachedTransactionCommittedEvent; import org.netcorepal.cap4j.ddd.share.DomainException; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.OrderUtils; import org.springframework.transaction.event.TransactionalEventListener; import java.time.Duration; @@ -26,13 +24,11 @@ @RequiredArgsConstructor public class DefaultDomainEventSupervisor implements DomainEventSupervisor, DomainEventManager { private final EventRecordRepository eventRecordRepository; - private final List domainEventInterceptors; + private final DomainEventInterceptorManager domainEventInterceptorManager; private final EventPublisher eventPublisher; private final ApplicationEventPublisher applicationEventPublisher; private final String svcName; - -// private static final ThreadLocal> TL_EVENT_PAYLOADS = new ThreadLocal>(); private static final ThreadLocal>> TL_ENTITY_EVENT_PAYLOADS = new ThreadLocal>>(); private static final ThreadLocal> TL_EVENT_SCHEDULE_MAP = new ThreadLocal>(); private static final Set EMPTY_EVENT_PAYLOADS = Collections.emptySet(); @@ -45,19 +41,6 @@ public class DefaultDomainEventSupervisor implements DomainEventSupervisor, Doma */ private static final int DEFAULT_EVENT_RETRY_TIMES = 16; - private List sortedDomainEventInterceptors = null; - /** - * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 - * @return - */ - protected List getOrderedDomainEventInterceptors() { - if(sortedDomainEventInterceptors == null){ - sortedDomainEventInterceptors = new ArrayList<>(domainEventInterceptors); - sortedDomainEventInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); - } - return sortedDomainEventInterceptors; - } - @Override public void attach(Object eventPayload, Object entity, LocalDateTime schedule) { // 判断领域事件,不支持集成事件。 @@ -78,7 +61,7 @@ public void attach(Object eventPayload, Object entity, LocalDateTime schedule) { entityEventPayloads.get(entity).add(eventPayload); putDeliverTime(eventPayload, schedule); - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onAttach(eventPayload, entity, schedule)); + domainEventInterceptorManager.getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onAttach(eventPayload, entity, schedule)); } @Override @@ -93,7 +76,7 @@ public void detach(Object eventPayload, Object entity) { } eventPayloads.remove(eventPayload); - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onDetach(eventPayload, entity)); + domainEventInterceptorManager.getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onDetach(eventPayload, entity)); } @Override @@ -117,9 +100,9 @@ public void release(Set entities) { transientEvents.add(event); } else { event.markPersist(true); - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.prePersist(event)); + domainEventInterceptorManager.getOrderedEventInterceptors4DomainEvent().forEach(interceptor -> interceptor.prePersist(event)); eventRecordRepository.save(event); - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.postPersist(event)); + domainEventInterceptorManager.getOrderedEventInterceptors4DomainEvent().forEach(interceptor -> interceptor.postPersist(event)); persistedEvents.add(event); } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java index 5eb2cd4..3c0b5d5 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/event/impl/DefaultEventPublisher.java @@ -1,7 +1,9 @@ package org.netcorepal.cap4j.ddd.domain.event.impl; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptorManager; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventPublisher; import org.netcorepal.cap4j.ddd.domain.event.*; import org.netcorepal.cap4j.ddd.share.DomainException; @@ -15,6 +17,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -28,13 +31,14 @@ * @date 2023/8/13 */ @RequiredArgsConstructor +@Slf4j public class DefaultEventPublisher implements EventPublisher { private final EventSubscriberManager eventSubscriberManager; private final List integrationEventPublisheres; private final EventRecordRepository eventRecordRepository; - private final List eventMessageInterceptors; - private final List domainEventInterceptors; - private final List integrationEventInterceptors; + private final EventMessageInterceptorManager eventMessageInterceptorManager; + private final DomainEventInterceptorManager domainEventInterceptorManager; + private final IntegrationEventInterceptorManager integrationEventInterceptorManager; private final int threadPoolsize; private ScheduledExecutorService executor = null; @@ -61,7 +65,7 @@ public void publish(EventRecord event) { Message message = event.getMessage(); // 事件消息拦截器 - 初始化 - getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.initPublish(message)); + eventMessageInterceptorManager.getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.initPublish(message)); // 填入消息头 String eventType = ((String) message.getHeaders().getOrDefault(HEADER_KEY_CAP4J_EVENT_TYPE, null)); @@ -113,21 +117,22 @@ protected void internalPublish4DomainEvent(EventRecord event) { try { Message message = event.getMessage(); boolean persist = (Boolean) message.getHeaders().getOrDefault(HEADER_KEY_CAP4J_PERSIST, false); - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.preRelease(event)); - getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.prePublish(message)); + domainEventInterceptorManager.getOrderedEventInterceptors4DomainEvent().forEach(interceptor -> interceptor.preRelease(event)); + eventMessageInterceptorManager.getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.prePublish(message)); // 进程内消息 LocalDateTime now = LocalDateTime.now(); eventSubscriberManager.dispatch(event.getPayload()); event.confirmedDelivery(now); if (persist) { - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.prePersist(event)); + domainEventInterceptorManager.getOrderedEventInterceptors4DomainEvent().forEach(interceptor -> interceptor.prePersist(event)); eventRecordRepository.save(event); - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.postPersist(event)); + domainEventInterceptorManager.getOrderedEventInterceptors4DomainEvent().forEach(interceptor -> interceptor.postPersist(event)); } - getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.postPublish(message)); - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.postRelease(event)); + eventMessageInterceptorManager.getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.postPublish(message)); + domainEventInterceptorManager.getOrderedEventInterceptors4DomainEvent().forEach(interceptor -> interceptor.postRelease(event)); } catch (Exception ex) { - getOrderedDomainEventInterceptors().forEach(interceptor -> interceptor.onException(ex, event)); + domainEventInterceptorManager.getOrderedEventInterceptors4DomainEvent().forEach(interceptor -> interceptor.onException(ex, event)); + log.error(String.format("领域事件发布失败:%s", event.getId()), ex); throw new DomainException(String.format("领域事件发布失败:%s", event.getId()), ex); } } @@ -139,28 +144,29 @@ protected void internalPublish4DomainEvent(EventRecord event) { */ protected void internalPublish4IntegrationEvent(EventRecord event) { try { - getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.preRelease(event)); - getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.prePublish(event.getMessage())); + integrationEventInterceptorManager.getOrderedEventInterceptors4IntegrationEvent().forEach(interceptor -> interceptor.preRelease(event)); + eventMessageInterceptorManager.getOrderedEventMessageInterceptors().forEach(interceptor -> interceptor.prePublish(event.getMessage())); integrationEventPublisheres.forEach(integrationEventPublisher -> integrationEventPublisher.publish( event, new IntegrationEventSendPublishCallback( - getOrderedEventMessageInterceptors(), - getOrderedIntegrationEventInterceptors(), + eventMessageInterceptorManager.getOrderedEventMessageInterceptors(), + integrationEventInterceptorManager.getOrderedEventInterceptors4IntegrationEvent(), eventRecordRepository) ) ); } catch (Exception ex) { - getOrderedIntegrationEventInterceptors().forEach(interceptor -> interceptor.onException(ex, event)); + integrationEventInterceptorManager.getOrderedEventInterceptors4IntegrationEvent().forEach(interceptor -> interceptor.onException(ex, event)); + log.error(String.format("集成事件发布失败:%s", event.getId()), ex); throw new DomainException(String.format("集成事件发布失败: %s", event.getId()), ex); } } @RequiredArgsConstructor public static class IntegrationEventSendPublishCallback implements IntegrationEventPublisher.PublishCallback { - private final List orderedEventMessageInterceptors; - private final List orderedIntegrationEventInterceptor; + private final Set orderedEventMessageInterceptors; + private final Set orderedIntegrationEventInterceptor; private final EventRecordRepository eventRecordRepository; @Override @@ -190,51 +196,4 @@ public void onException(EventRecord event, Throwable throwable) { orderedIntegrationEventInterceptor.forEach(interceptor -> interceptor.onException(throwable, event)); } } - - - private List orderedEventMessageInterceptors = null; - - /** - * 获取排序后的事件消息拦截器 - * 基于{@link org.springframework.core.annotation.Order} - * - * @return - */ - private List getOrderedEventMessageInterceptors() { - if (orderedEventMessageInterceptors == null) { - orderedEventMessageInterceptors = new ArrayList<>(eventMessageInterceptors); - orderedEventMessageInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); - } - return orderedEventMessageInterceptors; - } - - private List sortedDomainEventInterceptors = null; - - /** - * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 - * - * @return - */ - protected List getOrderedDomainEventInterceptors() { - if (sortedDomainEventInterceptors == null) { - sortedDomainEventInterceptors = new ArrayList<>(domainEventInterceptors); - sortedDomainEventInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); - } - return sortedDomainEventInterceptors; - } - - private List sortedIntegrationEventInterceptors = null; - - /** - * 拦截器基于 {@link org.springframework.core.annotation.Order} 排序 - * - * @return - */ - protected List getOrderedIntegrationEventInterceptors() { - if (sortedIntegrationEventInterceptors == null) { - sortedIntegrationEventInterceptors = new ArrayList<>(integrationEventInterceptors); - sortedIntegrationEventInterceptors.sort(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))); - } - return sortedIntegrationEventInterceptors; - } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultEventInterceptorManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultEventInterceptorManager.java new file mode 100644 index 0000000..660e84d --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultEventInterceptorManager.java @@ -0,0 +1,90 @@ +package org.netcorepal.cap4j.ddd.impl; + +import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor; +import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptorManager; +import org.netcorepal.cap4j.ddd.domain.event.*; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.OrderUtils; + +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 事件拦截器管理器实现 + * + * @author binking338 + * @date 2024/9/12 + */ +@RequiredArgsConstructor +public class DefaultEventInterceptorManager implements EventMessageInterceptorManager, DomainEventInterceptorManager, IntegrationEventInterceptorManager { + private final List eventMessageInterceptors; + private final List eventInterceptors; + + private Set orderedEventMessageInterceptors; + private Set orderedEventInterceptors4DomainEvent; + private Set orderedEventInterceptors4IntegrationEvent; + private Set orderedDomainEventInterceptors; + private Set orderedIntegrationEventInterceptors; + + @Override + public Set getOrderedDomainEventInterceptors() { + if(orderedDomainEventInterceptors == null){ + orderedDomainEventInterceptors = eventInterceptors.stream() + .filter(i -> DomainEventInterceptor.class.isAssignableFrom(i.getClass())) + .map(i -> (DomainEventInterceptor) i) + .sorted(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + return orderedDomainEventInterceptors; + } + + @Override + public Set getOrderedEventInterceptors4DomainEvent() { + if(orderedEventInterceptors4DomainEvent == null){ + orderedEventInterceptors4DomainEvent = eventInterceptors.stream() + .filter(i -> DomainEventInterceptor.class.isAssignableFrom(i.getClass()) + || !IntegrationEventInterceptor.class.isAssignableFrom(i.getClass())) + .sorted(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + return orderedEventInterceptors4DomainEvent; + } + + @Override + public Set getOrderedEventMessageInterceptors() { + if(orderedEventMessageInterceptors == null){ + orderedEventMessageInterceptors = eventMessageInterceptors.stream() + .sorted(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + return orderedEventMessageInterceptors; + } + + @Override + public Set getOrderedIntegrationEventInterceptors() { + if(orderedIntegrationEventInterceptors == null){ + orderedIntegrationEventInterceptors = eventInterceptors.stream() + .filter(i -> IntegrationEventInterceptor.class.isAssignableFrom(i.getClass())) + .map(i -> (IntegrationEventInterceptor) i) + .sorted(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + return orderedIntegrationEventInterceptors; + } + + @Override + public Set getOrderedEventInterceptors4IntegrationEvent() { + if(orderedEventInterceptors4IntegrationEvent == null){ + orderedEventInterceptors4IntegrationEvent = eventInterceptors.stream() + .filter(i -> !DomainEventInterceptor.class.isAssignableFrom(i.getClass()) + || IntegrationEventInterceptor.class.isAssignableFrom(i.getClass())) + .sorted(Comparator.comparingInt(a -> OrderUtils.getOrder(a.getClass(), Ordered.LOWEST_PRECEDENCE))) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + return orderedEventInterceptors4IntegrationEvent; + } +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java index 59375fc..82c9fd3 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/misc/ClassUtils.java @@ -1,12 +1,10 @@ package org.netcorepal.cap4j.ddd.share.misc; import lombok.SneakyThrows; -import org.netcorepal.cap4j.ddd.share.DomainException; import org.springframework.cglib.beans.BeanCopier; import org.springframework.core.ResolvableType; import org.springframework.core.convert.converter.Converter; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -92,7 +90,7 @@ public static Converter newConverterInstance(Class srcClass, ? null : (Converter) converterClass.newInstance(); } catch (Exception e) { - throw new DomainException("事件Converter无法实例化", e); + throw new RuntimeException("事件Converter无法实例化", e); } Method method = findMethod( @@ -123,7 +121,7 @@ public static Converter newConverterInstance(Class srcClass, try { dest = destClass.newInstance(); } catch (Exception e) { - throw new DomainException("无法完成事件自动转换", e); + throw new RuntimeException("无法完成事件自动转换", e); } copier.copy(source, dest, null); return dest; From 8d73e6ae2f3906397bfec598ead8988e6a6c3550 Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 12 Sep 2024 16:01:13 +0800 Subject: [PATCH 27/62] =?UTF-8?q?=E9=A2=86=E5=9F=9F=E4=BA=8B=E4=BB=B6event?= =?UTF-8?q?=5Ftype=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/domain/event/persistence/Event.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java index 7d3cfb3..4fd94ae 100644 --- a/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java +++ b/ddd-domain-event-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/event/persistence/Event.java @@ -11,9 +11,9 @@ import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; +import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent; import org.netcorepal.cap4j.ddd.share.DomainException; import org.netcorepal.cap4j.ddd.share.annotation.Retry; -import org.springframework.core.annotation.AnnotationUtils; import javax.persistence.*; import java.io.PrintWriter; @@ -162,8 +162,13 @@ private void loadPayload(Object payload) { IntegrationEvent integrationEvent = payload == null ? null : payload.getClass().getAnnotation(IntegrationEvent.class); + DomainEvent domainEvent = payload == null + ? null + : payload.getClass().getAnnotation(DomainEvent.class); if (integrationEvent != null) { this.eventType = integrationEvent.value(); + } else if (domainEvent != null) { + this.eventType = domainEvent.value(); } else { this.eventType = ""; } From 165a2725ba6fb9778daee2f77b9e2c7905a0fd2d Mon Sep 17 00:00:00 2001 From: binking338 Date: Fri, 13 Sep 2024 13:31:36 +0800 Subject: [PATCH 28/62] =?UTF-8?q?LiteralDesign=E6=94=AF=E6=8C=81=E5=8C=BA?= =?UTF-8?q?=E5=88=86Query=20ListQuery=20=E5=92=8C=20PageQuery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenArchMojo.java | 233 +++++++++++------- cap4j-ddd-codegen-template.json | 70 ++++++ .../ddd/application/query/ListQueryParam.java | 2 +- 3 files changed, 217 insertions(+), 88 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index a43ad40..97e070d 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -20,6 +20,7 @@ import java.time.format.DateTimeFormatter; import java.util.*; import java.util.function.Function; +import java.util.regex.Pattern; import java.util.stream.Collectors; /** @@ -115,8 +116,14 @@ protected String escape(String content, Map context) { /** * 脚手架模板模板节点 */ + @Data public static class TemplateNode extends PathNode { + /** + * 元素匹配正则 + */ + String pattern; + public TemplateNode clone() { TemplateNode templateNode = JSON.parseObject(JSON.toJSONString(this), TemplateNode.class); return templateNode; @@ -148,10 +155,10 @@ public static class Template extends PathNode { * @param tag * @return */ - public TemplateNode select(String tag) { + public List select(String tag) { if (this.templates == null) return null; - Optional node = templates.stream().filter(t -> Objects.equals(t.tag, tag)).findFirst(); - return node.orElse(null); + List nodes = templates.stream().filter(t -> Objects.equals(t.tag, tag)).collect(Collectors.toList()); + return nodes; } } @@ -306,106 +313,121 @@ public String alias4Design(String name) { /** * 构建模型设计元素 * - * @param templateNode + * @param templateNodes * @param parentPath * @throws IOException */ - public void renderDesign(TemplateNode templateNode, String parentPath) throws IOException { + public void renderDesign(List templateNodes, String parentPath) throws IOException { Map> designMap = Arrays.stream(this.design.split(PATTERN_SPLITTER)) .map(item -> TextUtils.splitWithTrim(item, ":", 2)) .filter(item -> item.length == 2) .collect(Collectors.groupingBy(g -> alias4Design(g[0]), Collectors.mapping(g -> g[1].trim(), Collectors.toSet()))); - switch (alias4Design(templateNode.tag)) { - case "command": - if (designMap.containsKey("command")) { - for (String literalCommand : - designMap.get("command")) { - renderAppLayerCommand(literalCommand, parentPath, templateNode); + + + for (TemplateNode templateNode : templateNodes) { + switch (alias4Design(templateNode.tag)) { + case "command": + if (designMap.containsKey("command")) { + for (String literalCommand : designMap.get("command")) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + renderAppLayerCommand(literalCommand, parentPath, templateNode); + } + } } - } - break; - case "query": - case "query_handler": - if (designMap.containsKey("query")) { - for (String literalCommand : - designMap.get("query")) { - renderAppLayerQuery(literalCommand, parentPath, templateNode); + break; + case "query": + case "query_handler": + if (designMap.containsKey("query")) { + for (String literalCommand : designMap.get("query")) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + renderAppLayerQuery(literalCommand, parentPath, templateNode); + } + } } - } - break; - case "client": - case "client_handler": - if (designMap.containsKey("client")) { - for (String literalCommand : - designMap.get("client")) { - renderAppLayerClient(literalCommand, parentPath, templateNode); + break; + case "client": + case "client_handler": + if (designMap.containsKey("client")) { + for (String literalCommand : designMap.get("client")) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + renderAppLayerClient(literalCommand, parentPath, templateNode); + } + } } - } - break; - case "integration_event": - if (designMap.containsKey("integration_event")) { - for (String literalCommand : - designMap.get("integration_event")) { - renderAppLayerIntegrationEvent("integration_event", literalCommand, parentPath, templateNode); + break; + case "integration_event": + if (designMap.containsKey("integration_event")) { + for (String literalCommand : designMap.get("integration_event")) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + renderAppLayerIntegrationEvent("integration_event", literalCommand, parentPath, templateNode); + } + } } - } - case "integration_event_handler": - if (designMap.containsKey("integration_event_handler")) { - for (String literalCommand : - designMap.get("integration_event_handler")) { - renderAppLayerIntegrationEvent("integration_event_handler", literalCommand, parentPath, templateNode); + case "integration_event_handler": + if (designMap.containsKey("integration_event_handler")) { + for (String literalCommand : designMap.get("integration_event_handler")) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + renderAppLayerIntegrationEvent("integration_event_handler", literalCommand, parentPath, templateNode); + } + } } - } - if (designMap.containsKey("integration_event")) { - for (String literalCommand : - designMap.get("integration_event")) { - if (literalCommand.split(":").length >= 3) { - renderAppLayerIntegrationEvent("integration_event_handler", literalCommand, parentPath, templateNode); + if (designMap.containsKey("integration_event")) { + for (String literalCommand : designMap.get("integration_event")) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + if (literalCommand.split(":").length >= 3) { + renderAppLayerIntegrationEvent("integration_event_handler", literalCommand, parentPath, templateNode); + } + } } } - } - break; - case "domain_event": - case "domain_event_handler": - if (designMap.containsKey("domain_event")) { - for (String literalCommand : - designMap.get("domain_event")) { - renderDomainLayerDomainEvent(literalCommand, parentPath, templateNode); + break; + case "domain_event": + case "domain_event_handler": + if (designMap.containsKey("domain_event")) { + for (String literalCommand : designMap.get("domain_event")) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + renderDomainLayerDomainEvent(literalCommand, parentPath, templateNode); + } + } } - } - break; - case "specification": - if (designMap.containsKey("specification")) { - for (String literalCommand : - designMap.get("specification")) { - renderDomainLayerSpecificaton(literalCommand, parentPath, templateNode); + break; + case "specification": + if (designMap.containsKey("specification")) { + for (String literalCommand : designMap.get("specification")) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + renderDomainLayerSpecificaton(literalCommand, parentPath, templateNode); + } + } } - } - break; - case "factory": - if (designMap.containsKey("factory")) { - for (String literalCommand : - designMap.get("factory")) { - renderDomainLayerAggregateFactory(literalCommand, parentPath, templateNode); + break; + case "factory": + if (designMap.containsKey("factory")) { + for (String literalCommand : designMap.get("factory")) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + renderDomainLayerAggregateFactory(literalCommand, parentPath, templateNode); + } + } } - } - break; - case "domain_service": - if (designMap.containsKey("domain_service")) { - for (String literalCommand : - designMap.get("domain_service")) { - renderDomainLayerDomainService(literalCommand, parentPath, templateNode); + break; + case "domain_service": + if (designMap.containsKey("domain_service")) { + for (String literalCommand : designMap.get("domain_service")) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + renderDomainLayerDomainService(literalCommand, parentPath, templateNode); + } + } } - } - break; - default: - if (designMap.containsKey(templateNode.tag)) { - for (String literalCommand : - designMap.get(templateNode.tag)) { - renderGenericDesign(literalCommand, parentPath, templateNode); + break; + default: + if (designMap.containsKey(templateNode.tag)) { + for (String literalCommand : designMap.get(templateNode.tag)) { + if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { + renderGenericDesign(literalCommand, parentPath, templateNode); + } + } } - } - break; + break; + } } } @@ -432,6 +454,10 @@ public void renderAppLayerCommand(String literalCommandDeclaration, String paren context.put("comment", context.get("Comment")); return context; }); + if (StringUtils.isBlank(path)) { + getLog().info("模板不匹配:" + templateNode.pattern); + return; + } getLog().info("生成命令代码:" + path); } @@ -458,6 +484,10 @@ public void renderAppLayerQuery(String literalQueryDeclaration, String parentPat context.put("comment", context.get("Comment")); return context; }); + if (StringUtils.isBlank(path)) { + getLog().info("模板不匹配:" + templateNode.pattern); + return; + } getLog().info("生成查询代码:" + path); } @@ -484,6 +514,10 @@ public void renderAppLayerClient(String literalClientDeclaration, String parentP context.put("comment", context.get("Comment")); return context; }); + if (StringUtils.isBlank(path)) { + getLog().info("模板不匹配:" + templateNode.pattern); + return; + } getLog().info("生成防腐端代码:" + path); } @@ -520,6 +554,10 @@ public void renderAppLayerIntegrationEvent(String literalType, String literalInt context.put("comment", context.get("Comment")); return context; }); + if (StringUtils.isBlank(path)) { + getLog().info("模板不匹配:" + templateNode.pattern); + return; + } getLog().info("生成集成事件代码:" + path); } @@ -571,6 +609,10 @@ public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, S context.put("comment", context.get("Comment")); return context; }); + if (StringUtils.isBlank(path)) { + getLog().info("模板不匹配:" + templateNode.pattern); + return; + } getLog().info("生成领域事件代码:" + path); } @@ -600,6 +642,10 @@ public void renderDomainLayerAggregateFactory(String literalAggregateFactoryDecl context.put("comment", context.get("Comment")); return context; }); + if (StringUtils.isBlank(path)) { + getLog().info("模板不匹配:" + templateNode.pattern); + return; + } getLog().info("生成聚合工厂代码:" + path); } @@ -652,6 +698,10 @@ public void renderDomainLayerDomainService(String literalDomainServiceDeclaratio context.put("comment", context.get("Comment")); return context; }); + if (StringUtils.isBlank(path)) { + getLog().info("模板不匹配:" + templateNode.pattern); + return; + } getLog().info("生成领域服务代码:" + path); } @@ -662,10 +712,19 @@ public void renderDomainLayerDomainService(String literalDomainServiceDeclaratio public void renderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析自定义元素设计:" + literalGenericDeclaration); String path = internalRenderGenericDesign(literalGenericDeclaration, parentPath, templateNode, null); + if (StringUtils.isBlank(path)) { + getLog().info("模板不匹配:" + templateNode.pattern); + return; + } getLog().info("生成自定义元素代码:" + path); } public String internalRenderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode, Function, Map> contextBuilder) throws IOException { + if (StringUtils.isNotBlank(templateNode.pattern)) { + if (!Pattern.compile(templateNode.pattern).asPredicate().test(literalGenericDeclaration)) { + return null; + } + } String[] segments = TextUtils.splitWithTrim(literalGenericDeclaration, ":"); Map context = new HashMap<>(getEscapeContext()); for (int i = 0; i < segments.length; i++) { @@ -725,9 +784,9 @@ public String renderDir(PathNode pathNode, String parentPath) throws IOException if (StringUtils.isNotBlank(pathNode.getTag())) { String[] tags = pathNode.getTag().split(PATTERN_SPLITTER); for (String tag : tags) { - TemplateNode templateNode = template.select(tag); - if (null != templateNode) { - renderDesign(templateNode, path); + List templateNodes = template.select(tag); + if (null != templateNodes) { + renderDesign(templateNodes, path); } } } diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index 3316ad2..f41eb5a 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -30,6 +30,7 @@ "type": "dir", "tag": "query", "name": "${path}", + "pattern": "^(?!.*(List|list|Page|page)).*$", "children": [ { "type": "file", @@ -45,10 +46,51 @@ } ] }, + { + "type": "dir", + "tag": "query", + "name": "${path}", + "pattern": "^.*(List|list).*$", + "children": [ + { + "type": "file", + "name": "${Query}Request.java", + "format": "raw", + "data": "package ${basePackage}.application.queries${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.query.ListQueryParam;\n\n\n/**\n * ${Query}查询请求参数\n * ${Comment}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Query}Request implements ListQueryParam<${Query}Response> {\n Long id;\n}" + }, + { + "type": "file", + "name": "${Query}Response.java", + "format": "raw", + "data": "package ${basePackage}.application.queries${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * ${Query}查询响应\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Query}Response {\n Long id;\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "query", + "name": "${path}", + "pattern": "^.*(Page|page).*$", + "children": [ + { + "type": "file", + "name": "${Query}Request.java", + "format": "raw", + "data": "package ${basePackage}.application.queries${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.query.PageQueryParam;\n\n/**\n * ${Query}查询请求参数\n * ${Comment}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Query}Request extends PageQueryParam<${Query}Response> {\n Long id;\n}" + }, + { + "type": "file", + "name": "${Query}Response.java", + "format": "raw", + "data": "package ${basePackage}.application.queries${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * ${Query}查询响应\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Query}Response {\n Long id;\n}\n" + } + ] + }, { "type": "dir", "tag": "query_handler", "name": "", + "pattern": "^(?!.*(List|list|Page|page)).*$", "children": [ { "type": "file", @@ -58,6 +100,34 @@ } ] }, + { + "type": "dir", + "tag": "query_handler", + "name": "", + "pattern": "^.*(List|list).*$", + "children": [ + { + "type": "file", + "name": "${Query}Handler.java", + "format": "raw", + "data": "package ${basePackage}.adapter.application.queries;\n\nimport ${basePackage}.application.queries${package}.${Query}Request;\nimport ${basePackage}.application.queries${package}.${Query}Response;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.netcorepal.cap4j.ddd.application.query.ListQuery;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n/**\n * ${Query}查询请求适配实现\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class ${Query}Handler implements ListQuery<${Query}Request, ${Query}Response> {\n \n @Override\n public List<${Query}Response> exec(${Query}Request request) {\n // mybatis / jpa 哪个顺手就用哪个吧!\n return null;\n }\n}\n" + } + ] + }, + { + "type": "dir", + "tag": "query_handler", + "name": "", + "pattern": "^.*(Page|page).*$", + "children": [ + { + "type": "file", + "name": "${Query}Handler.java", + "format": "raw", + "data": "package ${basePackage}.adapter.application.queries;\n\nimport ${basePackage}.application.queries${package}.${Query}Request;\nimport ${basePackage}.application.queries${package}.${Query}Response;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.netcorepal.cap4j.ddd.application.query.PageQuery;\nimport org.netcorepal.cap4j.ddd.share.PageData;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${Query}查询请求适配实现\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class ${Query}Handler implements PageQuery<${Query}Request, ${Query}Response> {\n \n @Override\n public PageData<${Query}Response> exec(${Query}Request request) {\n // mybatis / jpa 哪个顺手就用哪个吧!\n return null;\n }\n}\n" + } + ] + }, { "type": "dir", "tag": "client", diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryParam.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryParam.java index d29a071..089fefe 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryParam.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/query/ListQueryParam.java @@ -10,5 +10,5 @@ * @author binking338 * @date 2024/9/6 */ -public class ListQueryParam implements RequestParam> { +public interface ListQueryParam extends RequestParam> { } From afd514d85f604c79ad872220fd7f1501b9485a92 Mon Sep 17 00:00:00 2001 From: binking338 Date: Fri, 13 Sep 2024 14:46:29 +0800 Subject: [PATCH 29/62] =?UTF-8?q?=E6=95=B4=E7=90=86IoC=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MediatorAutoConfiguration.java | 3 +- .../event/DomainEventAutoConfiguration.java | 130 +++++++++--------- .../repo/JpaRepositoryAutoConfiguration.java | 82 +++++------ .../DomainServiceAutoConfiguration.java | 3 +- 4 files changed, 108 insertions(+), 110 deletions(-) diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java index 6481619..26fbe0b 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/MediatorAutoConfiguration.java @@ -22,8 +22,7 @@ @RequiredArgsConstructor public class MediatorAutoConfiguration { @Bean - @Primary - public RequestSupervisor defaultRequestSupervisor( + public DefaultRequestSupervisor defaultRequestSupervisor( List> requestHandlers, List> requestInterceptors ){ diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java index c91a18a..d6bfce1 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/event/DomainEventAutoConfiguration.java @@ -2,7 +2,6 @@ import lombok.RequiredArgsConstructor; import org.netcorepal.cap4j.ddd.application.distributed.Locker; -import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptorManager; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventPublisher; import org.netcorepal.cap4j.ddd.domain.event.configure.EventProperties; @@ -19,11 +18,11 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; import java.time.Duration; import java.util.List; @@ -44,48 +43,36 @@ @EntityScan(basePackages = { "org.netcorepal.cap4j.ddd.domain.event.persistence" }) -@EnableScheduling public class DomainEventAutoConfiguration { public static final String CONFIG_KEY_4_EVENT_COMPENSE_LOCKER_KEY = "event_compense[" + CONFIG_KEY_4_SVC_NAME + "]"; public static final String CONFIG_KEY_4_EVENT_ARCHIVE_LOCKER_KEY = "event_archive[" + CONFIG_KEY_4_SVC_NAME + "]"; - private static final String CONFIG_KEY_4_COMPENSE_CRON = "${cap4j.ddd.domain.event.schedule.compenseCron:${cap4j.ddd.domain.event.schedule.compense-cron:0 */1 * * * ?}}"; - private static final String CONFIG_KEY_4_ARCHIVE_CRON = "${cap4j.ddd.domain.event.schedule.archiveCron:${cap4j.ddd.domain.event.schedule.archive-cron:0 0 2 * * ?}}"; - private static final String CONFIG_KEY_4_ADD_PARTITION_CRON = "${cap4j.ddd.domain.event.schedule.addPartitionCron:${cap4j.ddd.domain.event.schedule.add-partition-cron:0 0 0 * * ?}}"; - - - private final EventScheduleProperties eventScheduleProperties; - private JpaEventScheduleService scheduleService = null; - - @Scheduled(cron = CONFIG_KEY_4_COMPENSE_CRON) - public void compensation() { - if (scheduleService == null) return; - scheduleService.compense( - eventScheduleProperties.getCompenseBatchSize(), - eventScheduleProperties.getCompenseMaxConcurrency(), - Duration.ofSeconds(eventScheduleProperties.getCompenseIntervalSeconds()), - Duration.ofSeconds(eventScheduleProperties.getCompenseMaxLockSeconds()) - ); - } - - @Scheduled(cron = CONFIG_KEY_4_ARCHIVE_CRON) - public void archive() { - if (scheduleService == null) return; - scheduleService.archive( - eventScheduleProperties.getArchiveExpireDays(), - eventScheduleProperties.getArchiveBatchSize(), - Duration.ofSeconds(eventScheduleProperties.getArchiveMaxLockSeconds()) + @Bean + public DefaultDomainEventSupervisor defaultDomainEventSupervisor( + EventRecordRepository eventRecordRepository, + DomainEventInterceptorManager domainEventInterceptorManager, + EventPublisher eventPublisher, + ApplicationEventPublisher applicationEventPublisher, + @Value(CONFIG_KEY_4_SVC_NAME) + String svcName + ) { + DefaultDomainEventSupervisor defaultDomainEventSupervisor = new DefaultDomainEventSupervisor( + eventRecordRepository, + domainEventInterceptorManager, + eventPublisher, + applicationEventPublisher, + svcName ); - } - @Scheduled(cron = CONFIG_KEY_4_ADD_PARTITION_CRON) - public void addTablePartition() { - scheduleService.addPartition(); + DomainEventSupervisorSupport.configure((DomainEventSupervisor) defaultDomainEventSupervisor); + DomainEventSupervisorSupport.configure((DomainEventManager) defaultDomainEventSupervisor); + return defaultDomainEventSupervisor; } @Bean - public JpaEventScheduleService eventScheduleService( + @ConditionalOnMissingBean(JpaEventScheduleService.class) + public JpaEventScheduleService jpaEventScheduleService( EventPublisher eventPublisher, EventRecordRepository eventRecordRepository, EventJpaRepository eventJpaRepository, @@ -99,7 +86,7 @@ public JpaEventScheduleService eventScheduleService( String archiveLockerKey, EventScheduleProperties eventScheduleProperties, JdbcTemplate jdbcTemplate) { - scheduleService = new JpaEventScheduleService( + JpaEventScheduleService scheduleService = new JpaEventScheduleService( eventPublisher, eventRecordRepository, eventJpaRepository, @@ -114,9 +101,52 @@ public JpaEventScheduleService eventScheduleService( return scheduleService; } + /** + * 领域事件定时补偿任务 + */ + @RequiredArgsConstructor + @Service + @EnableScheduling + private static class __DomainEventScheduleLoader { + + private static final String CONFIG_KEY_4_COMPENSE_CRON = "${cap4j.ddd.domain.event.schedule.compenseCron:${cap4j.ddd.domain.event.schedule.compense-cron:0 */1 * * * ?}}"; + private static final String CONFIG_KEY_4_ARCHIVE_CRON = "${cap4j.ddd.domain.event.schedule.archiveCron:${cap4j.ddd.domain.event.schedule.archive-cron:0 0 2 * * ?}}"; + private static final String CONFIG_KEY_4_ADD_PARTITION_CRON = "${cap4j.ddd.domain.event.schedule.addPartitionCron:${cap4j.ddd.domain.event.schedule.add-partition-cron:0 0 0 * * ?}}"; + + + private final EventScheduleProperties eventScheduleProperties; + private final JpaEventScheduleService scheduleService = null; + + @Scheduled(cron = CONFIG_KEY_4_COMPENSE_CRON) + public void compensation() { + if (scheduleService == null) return; + scheduleService.compense( + eventScheduleProperties.getCompenseBatchSize(), + eventScheduleProperties.getCompenseMaxConcurrency(), + Duration.ofSeconds(eventScheduleProperties.getCompenseIntervalSeconds()), + Duration.ofSeconds(eventScheduleProperties.getCompenseMaxLockSeconds()) + ); + } + + @Scheduled(cron = CONFIG_KEY_4_ARCHIVE_CRON) + public void archive() { + if (scheduleService == null) return; + scheduleService.archive( + eventScheduleProperties.getArchiveExpireDays(), + eventScheduleProperties.getArchiveBatchSize(), + Duration.ofSeconds(eventScheduleProperties.getArchiveMaxLockSeconds()) + ); + } + + @Scheduled(cron = CONFIG_KEY_4_ADD_PARTITION_CRON) + public void addTablePartition() { + scheduleService.addPartition(); + } + } + @Bean @ConditionalOnMissingBean(EventPublisher.class) - public EventPublisher defaultEventPublisher( + public DefaultEventPublisher defaultEventPublisher( EventSubscriberManager eventSubscriberManager, List integrationEventPublisheres, EventRecordRepository eventRecordRepository, @@ -140,7 +170,7 @@ public EventPublisher defaultEventPublisher( @Bean @ConditionalOnMissingBean(EventSubscriberManager.class) - public EventSubscriberManager defaultEventSubscriberManager( + public DefaultEventSubscriberManager defaultEventSubscriberManager( List> subscribers, ApplicationEventPublisher applicationEventPublisher, EventProperties eventProperties @@ -156,30 +186,7 @@ public EventSubscriberManager defaultEventSubscriberManager( } @Bean - @Primary - public DefaultDomainEventSupervisor defaultDomainEventSupervisor( - EventRecordRepository eventRecordRepository, - DomainEventInterceptorManager domainEventInterceptorManager, - EventPublisher eventPublisher, - ApplicationEventPublisher applicationEventPublisher, - @Value(CONFIG_KEY_4_SVC_NAME) - String svcName - ) { - DefaultDomainEventSupervisor defaultDomainEventSupervisor = new DefaultDomainEventSupervisor( - eventRecordRepository, - domainEventInterceptorManager, - eventPublisher, - applicationEventPublisher, - svcName - ); - - DomainEventSupervisorSupport.configure((DomainEventSupervisor) defaultDomainEventSupervisor); - DomainEventSupervisorSupport.configure((DomainEventManager) defaultDomainEventSupervisor); - return defaultDomainEventSupervisor; - } - - @Bean - @Primary + @ConditionalOnMissingBean(DefaultEventInterceptorManager.class) public DefaultEventInterceptorManager defaultEventInterceptorManager( List eventMessageInterceptors, List interceptors @@ -190,5 +197,4 @@ public DefaultEventInterceptorManager defaultEventInterceptorManager( ); return defaultEventInterceptorManager; } - } \ No newline at end of file diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java index 0931bcf..d0fbada 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java @@ -2,7 +2,6 @@ import lombok.RequiredArgsConstructor; import org.apache.rocketmq.spring.core.RocketMQTemplate; -import org.netcorepal.cap4j.ddd.application.UnitOfWork; import org.netcorepal.cap4j.ddd.application.event.IntegrationEventManager; import org.netcorepal.cap4j.ddd.application.impl.JpaUnitOfWork; import org.netcorepal.cap4j.ddd.application.UnitOfWorkSupport; @@ -21,7 +20,6 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; import java.util.List; @@ -41,28 +39,29 @@ public class JpaRepositoryAutoConfiguration { @Bean - @ConditionalOnMissingBean(PersistListenerManager.class) - public PersistListenerManager defaultPersistListenerManager( - List> persistListeners, - EventProperties eventProperties - ) { - DefaultPersistListenerManager persistListenerManager = new DefaultPersistListenerManager( - persistListeners, - eventProperties.getEventScanPackage()); - persistListenerManager.init(); - return persistListenerManager; + public DefaultRepositorySupervisor defaultRepositorySupervisor( + List> repositories, + JpaUnitOfWork unitOfWork + ){ + DefaultRepositorySupervisor repositorySupervisor = new DefaultRepositorySupervisor(repositories, unitOfWork); + repositorySupervisor.init(); + RepositorySupervisorSupport.configure(repositorySupervisor); + return repositorySupervisor; } @Bean - @ConditionalOnMissingBean(SpecificationManager.class) - public SpecificationManager defaultSpecificationManager(List> specifications) { - DefaultSpecificationManager specificationManager = new DefaultSpecificationManager(specifications); - specificationManager.init(); - return specificationManager; + public DefaultAggregateFactorySupervisor defaultAggregateFactorySupervisor( + List> factories + ){ + DefaultAggregateFactorySupervisor aggregateFactorySupervisor = new DefaultAggregateFactorySupervisor( + factories + ); + aggregateFactorySupervisor.init(); + AggregateFactorySupervisorSupport.configure(aggregateFactorySupervisor); + return aggregateFactorySupervisor; } @Bean - @Primary public JpaUnitOfWork jpaUnitOfWork( DomainEventManager domainEventManager, IntegrationEventManager integrationEventManager, @@ -82,32 +81,6 @@ public JpaUnitOfWork jpaUnitOfWork( return unitOfWork; } - @Bean - @Primary - public RepositorySupervisor defaultRepositorySupervisor( - List> repositories, - UnitOfWork unitOfWork - ){ - DefaultRepositorySupervisor repositorySupervisor = new DefaultRepositorySupervisor(repositories, unitOfWork); - repositorySupervisor.init(); - RepositorySupervisorSupport.configure(repositorySupervisor); - return repositorySupervisor; - } - - @Bean - @Primary - public AggregateFactorySupervisor defaultAggregateFactorySupervisor( - List> factories - ){ - DefaultAggregateFactorySupervisor aggregateFactorySupervisor = new DefaultAggregateFactorySupervisor( - factories - ); - aggregateFactorySupervisor.init(); - AggregateFactorySupervisorSupport.configure(aggregateFactorySupervisor); - return aggregateFactorySupervisor; - } - - @Configuration private static class __JpaUnitOfWorkLoader { public __JpaUnitOfWorkLoader( @@ -130,4 +103,25 @@ public JpaEventRecordRepository jpaEventRecordRepository( ); return eventRecordRepository; } + + @Bean + @ConditionalOnMissingBean(PersistListenerManager.class) + public DefaultPersistListenerManager defaultPersistListenerManager( + List> persistListeners, + EventProperties eventProperties + ) { + DefaultPersistListenerManager persistListenerManager = new DefaultPersistListenerManager( + persistListeners, + eventProperties.getEventScanPackage()); + persistListenerManager.init(); + return persistListenerManager; + } + + @Bean + @ConditionalOnMissingBean(SpecificationManager.class) + public DefaultSpecificationManager defaultSpecificationManager(List> specifications) { + DefaultSpecificationManager specificationManager = new DefaultSpecificationManager(specifications); + specificationManager.init(); + return specificationManager; + } } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java index 43e2313..f887c03 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java @@ -24,8 +24,7 @@ public class DomainServiceAutoConfiguration { * @return */ @Bean - @ConditionalOnMissingBean(DomainServiceSupervisor.class) - public DomainServiceSupervisor defaultDomainServiceSupervisor(ApplicationContext applicationContext) { + public DefaultDomainServiceSupervisor defaultDomainServiceSupervisor(ApplicationContext applicationContext) { DefaultDomainServiceSupervisor domainServiceSupervisor = new DefaultDomainServiceSupervisor( applicationContext ); From 01f3bcf59efe9aa8c112b6c5cd3a7927a259f652 Mon Sep 17 00:00:00 2001 From: binking338 Date: Fri, 13 Sep 2024 15:35:44 +0800 Subject: [PATCH 30/62] =?UTF-8?q?=E5=88=86=E7=A6=BBgen-arch=20gen-design?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenArchMojo.java | 678 +----------------- .../cap4j/ddd/codegen/GenDesignMojo.java | 531 ++++++++++++++ .../cap4j/ddd/codegen/template/PathNode.java | 91 +++ .../cap4j/ddd/codegen/template/Template.java | 36 + .../ddd/codegen/template/TemplateNode.java | 34 + 5 files changed, 720 insertions(+), 650 deletions(-) create mode 100644 cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java create mode 100644 cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/PathNode.java create mode 100644 cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/Template.java create mode 100644 cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/TemplateNode.java diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index 97e070d..310aef0 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -1,17 +1,16 @@ package org.netcorepal.cap4j.ddd.codegen; import com.alibaba.fastjson.JSON; -import lombok.Data; -import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.FileUtils; -import org.netcorepal.cap4j.ddd.codegen.misc.NamingUtils; import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; -import org.netcorepal.cap4j.ddd.codegen.misc.TextUtils; +import org.netcorepal.cap4j.ddd.codegen.template.PathNode; +import org.netcorepal.cap4j.ddd.codegen.template.Template; +import org.netcorepal.cap4j.ddd.codegen.template.TemplateNode; import java.io.File; import java.io.IOException; @@ -19,9 +18,6 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; -import java.util.function.Function; -import java.util.regex.Pattern; -import java.util.stream.Collectors; /** * 生成项目目录结构 @@ -34,134 +30,6 @@ public class GenArchMojo extends MyAbstractMojo { public final String PATTERN_SPLITTER = "[\\,\\;]"; - /** - * 脚手架模板文件节点 - */ - @Data - public static class PathNode { - /** - * 节点类型:root|dir|file - */ - String type; - /** - * 节点标签:关联模板 - */ - String tag; - /** - * 节点名称 - */ - String name; - /** - * 模板源类型:raw|url - */ - String format = "raw"; - /** - * 模板数据数据 - */ - String data; - /** - * 冲突处理:skip|warn|overwrite - */ - String conflict = "skip"; - - /** - * 下级节点 - */ - List children; - - public PathNode clone() { - PathNode pathNode = JSON.parseObject(JSON.toJSONString(this), PathNode.class); - return pathNode; - } - - public PathNode resolve(Map context) throws IOException { - if (null != this.name) { - this.name = this.name.replace("${basePackage}", "${basePackage__as_path}"); - this.name = escape(this.name, context); - } - String rawData = ""; - switch (this.format) { - case "url": - if (null != this.data) { - rawData = SourceFileUtils.loadFileContent(this.data, context.get("archTemplateEncoding")); - } - break; - case "raw": - default: - rawData = this.data; - break; - } - this.data = escape(rawData, context); - this.format = "raw"; - if (null != this.children) { - for (PathNode child : this.children) { - child.resolve(context); - } - } - return this; - } - - protected String escape(String content, Map context) { - if (null == content) { - return ""; - } - String result = content; - for (Map.Entry kv : context.entrySet()) { - result = result.replace("${" + kv.getKey() + "}", kv.getValue() == null ? "" : kv.getValue()); - } - return result; - } - } - - /** - * 脚手架模板模板节点 - */ - @Data - public static class TemplateNode extends PathNode { - - /** - * 元素匹配正则 - */ - String pattern; - - public TemplateNode clone() { - TemplateNode templateNode = JSON.parseObject(JSON.toJSONString(this), TemplateNode.class); - return templateNode; - } - - @Override - public PathNode resolve(Map context) throws IOException { - super.resolve(context); - this.tag = ""; - return this; - } - } - - /** - * 模板 - */ - @Data - @NoArgsConstructor - public static class Template extends PathNode { - - /** - * 模板节点 - */ - List templates = null; - - /** - * 获取模板 - * - * @param tag - * @return - */ - public List select(String tag) { - if (this.templates == null) return null; - List nodes = templates.stream().filter(t -> Objects.equals(t.tag, tag)).collect(Collectors.toList()); - return nodes; - } - } - private String projectGroupId = ""; private String projectArtifactId = ""; private String projectVersion = ""; @@ -224,21 +92,21 @@ public void execute() throws MojoExecutionException, MojoFailureException { */ public String render(PathNode pathNode, String parentPath) throws IOException { String path = parentPath; - switch (pathNode.type) { + switch (pathNode.getType()) { case "file": path = renderFile(pathNode, parentPath); break; case "dir": path = renderDir(pathNode, parentPath); - if (pathNode.children != null) { - for (PathNode childPathNode : pathNode.children) { + if (pathNode.getChildren() != null) { + for (PathNode childPathNode : pathNode.getChildren()) { render(childPathNode, path); } } break; case "root": - if (pathNode.children != null) { - for (PathNode childPathNode : pathNode.children) { + if (pathNode.getChildren() != null) { + for (PathNode childPathNode : pathNode.getChildren()) { render(childPathNode, parentPath); } } @@ -247,507 +115,6 @@ public String render(PathNode pathNode, String parentPath) throws IOException { return path; } - public String alias4Design(String name) { - switch (name.toLowerCase()) { - case "commands": - case "command": - case "cmd": - return "command"; - case "queries": - case "query": - case "qry": - return "query"; - case "clients": - case "client": - case "cli": - return "client"; - case "integration_events": - case "integration_event": - case "events": - case "event": - case "evt": - case "i_e": - case "ie": - return "integration_event"; - case "integration_event_handlers": - case "integration_event_handler": - case "event_handlers": - case "event_handler": - case "evt_hdl": - case "i_e_h": - case "ieh": - return "integration_event_handler"; - case "repositories": - case "repository": - case "repos": - case "repo": - return "repository"; - case "factories": - case "factory": - case "fac": - return "factory"; - case "specifications": - case "specification": - case "specs": - case "spec": - return "specification"; - case "domain_events": - case "domain_event": - case "d_e": - case "de": - return "domain_event"; - case "domain_event_handlers": - case "domain_event_handler": - case "d_e_h": - case "deh": - return "domain_event_handler"; - case "domain_service": - case "service": - case "svc": - return "domain_service"; - default: - return name; - } - } - - /** - * 构建模型设计元素 - * - * @param templateNodes - * @param parentPath - * @throws IOException - */ - public void renderDesign(List templateNodes, String parentPath) throws IOException { - Map> designMap = Arrays.stream(this.design.split(PATTERN_SPLITTER)) - .map(item -> TextUtils.splitWithTrim(item, ":", 2)) - .filter(item -> item.length == 2) - .collect(Collectors.groupingBy(g -> alias4Design(g[0]), Collectors.mapping(g -> g[1].trim(), Collectors.toSet()))); - - - for (TemplateNode templateNode : templateNodes) { - switch (alias4Design(templateNode.tag)) { - case "command": - if (designMap.containsKey("command")) { - for (String literalCommand : designMap.get("command")) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - renderAppLayerCommand(literalCommand, parentPath, templateNode); - } - } - } - break; - case "query": - case "query_handler": - if (designMap.containsKey("query")) { - for (String literalCommand : designMap.get("query")) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - renderAppLayerQuery(literalCommand, parentPath, templateNode); - } - } - } - break; - case "client": - case "client_handler": - if (designMap.containsKey("client")) { - for (String literalCommand : designMap.get("client")) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - renderAppLayerClient(literalCommand, parentPath, templateNode); - } - } - } - break; - case "integration_event": - if (designMap.containsKey("integration_event")) { - for (String literalCommand : designMap.get("integration_event")) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - renderAppLayerIntegrationEvent("integration_event", literalCommand, parentPath, templateNode); - } - } - } - case "integration_event_handler": - if (designMap.containsKey("integration_event_handler")) { - for (String literalCommand : designMap.get("integration_event_handler")) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - renderAppLayerIntegrationEvent("integration_event_handler", literalCommand, parentPath, templateNode); - } - } - } - if (designMap.containsKey("integration_event")) { - for (String literalCommand : designMap.get("integration_event")) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - if (literalCommand.split(":").length >= 3) { - renderAppLayerIntegrationEvent("integration_event_handler", literalCommand, parentPath, templateNode); - } - } - } - } - break; - case "domain_event": - case "domain_event_handler": - if (designMap.containsKey("domain_event")) { - for (String literalCommand : designMap.get("domain_event")) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - renderDomainLayerDomainEvent(literalCommand, parentPath, templateNode); - } - } - } - break; - case "specification": - if (designMap.containsKey("specification")) { - for (String literalCommand : designMap.get("specification")) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - renderDomainLayerSpecificaton(literalCommand, parentPath, templateNode); - } - } - } - break; - case "factory": - if (designMap.containsKey("factory")) { - for (String literalCommand : designMap.get("factory")) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - renderDomainLayerAggregateFactory(literalCommand, parentPath, templateNode); - } - } - } - break; - case "domain_service": - if (designMap.containsKey("domain_service")) { - for (String literalCommand : designMap.get("domain_service")) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - renderDomainLayerDomainService(literalCommand, parentPath, templateNode); - } - } - } - break; - default: - if (designMap.containsKey(templateNode.tag)) { - for (String literalCommand : designMap.get(templateNode.tag)) { - if (StringUtils.isBlank(templateNode.pattern) || Pattern.compile(templateNode.pattern).asPredicate().test(literalCommand)) { - renderGenericDesign(literalCommand, parentPath, templateNode); - } - } - } - break; - } - } - } - - /** - * @param literalCommandDeclaration 文本化命令声明 CommandName - * @param templateNode 模板配置 - */ - public void renderAppLayerCommand(String literalCommandDeclaration, String parentPath, TemplateNode templateNode) throws IOException { - getLog().info("解析命令设计:" + literalCommandDeclaration); - String path = internalRenderGenericDesign(literalCommandDeclaration, parentPath, templateNode, context -> { - String Name = context.get("Name"); - if (!Name.endsWith("Cmd") && !Name.endsWith("Command")) { - Name += "Cmd"; - } - context.put("Name", Name); - context.put("Command", context.get("Name")); - context.put("command", context.get("Name").toLowerCase()); - context.put("Request", context.get("Command") + "Request"); - context.put("Response", context.get("Command") + "Response"); - - context.put("ReturnType", context.get("Response")); - - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 命令描述"); - context.put("comment", context.get("Comment")); - return context; - }); - if (StringUtils.isBlank(path)) { - getLog().info("模板不匹配:" + templateNode.pattern); - return; - } - getLog().info("生成命令代码:" + path); - } - - /** - * @param literalQueryDeclaration 文本化查询声明 QueryName - * @param templateNode 模板配置 - */ - public void renderAppLayerQuery(String literalQueryDeclaration, String parentPath, TemplateNode templateNode) throws IOException { - getLog().info("解析查询设计:" + literalQueryDeclaration); - String path = internalRenderGenericDesign(literalQueryDeclaration, parentPath, templateNode, context -> { - String Name = context.get("Name"); - if (!Name.endsWith("Qry") && !Name.endsWith("Query")) { - Name += "Qry"; - } - context.put("Name", Name); - context.put("Query", context.get("Name")); - context.put("query", context.get("Name").toLowerCase()); - context.put("Request", context.get("Query") + "Request"); - context.put("Response", context.get("Query") + "Response"); - - context.put("ReturnType", context.get("Response")); - - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 查询描述"); - context.put("comment", context.get("Comment")); - return context; - }); - if (StringUtils.isBlank(path)) { - getLog().info("模板不匹配:" + templateNode.pattern); - return; - } - getLog().info("生成查询代码:" + path); - } - - /** - * @param literalClientDeclaration 文本化防腐端声明 ClientName - * @param templateNode 模板配置 - */ - public void renderAppLayerClient(String literalClientDeclaration, String parentPath, TemplateNode templateNode) throws IOException { - getLog().info("解析防腐端设计:" + literalClientDeclaration); - String path = internalRenderGenericDesign(literalClientDeclaration, parentPath, templateNode, context -> { - String Name = context.get("Name"); - if (!Name.endsWith("Cli") && !Name.endsWith("Client")) { - Name += "Cli"; - } - context.put("Name", Name); - context.put("Client", context.get("Name")); - context.put("client", context.get("Name").toLowerCase()); - context.put("Request", context.get("Name") + "Request"); - context.put("Response", context.get("Name") + "Response"); - - context.put("ReturnType", context.get("Response")); - - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 防腐端描述"); - context.put("comment", context.get("Comment")); - return context; - }); - if (StringUtils.isBlank(path)) { - getLog().info("模板不匹配:" + templateNode.pattern); - return; - } - getLog().info("生成防腐端代码:" + path); - } - - /** - * @param literalType 设计类型 - * @param literalIntegrationEventDeclaration 文本化集成事件声明 IntegrationEventName[:mq-topic[:mq-consumer]] - * @param templateNode 模板配置 - */ - public void renderAppLayerIntegrationEvent(String literalType, String literalIntegrationEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { - getLog().info("解析集成事件设计:" + literalIntegrationEventDeclaration); - String path = internalRenderGenericDesign(literalIntegrationEventDeclaration, parentPath, templateNode, context -> { - String Name = context.get("Name"); - if (!Name.endsWith("Evt") && !Name.endsWith("Event")) { - Name += "IntegrationEvent"; - } - context.put("Name", Name); - context.put("IntegrationEvent", context.get("Name")); - context.put("Event", context.get("IntegrationEvent")); - context.put("INTEGRATION_EVENT", context.get("IntegrationEvent")); - context.put("IE", context.get("IntegrationEvent")); - context.put("I_E", context.get("IntegrationEvent")); - context.put("integration_event", context.get("Name").toLowerCase()); - context.put("event", context.get("integration_event")); - context.put("ie", context.get("integration_event")); - context.put("i_e", context.get("integration_event")); - context.put("MQ_TOPIC", context.containsKey("Val1") ? ("\"" + context.get("Val1") + "\"") : ("\"" + context.get("Val0") + "\"")); - if (Objects.equals(literalType, "integration_event_handler") && !context.containsKey("Val2")) { - context.put("MQ_CONSUMER", "IntegrationEvent.NONE_SUBSCRIBER"); - } else { - context.put("MQ_CONSUMER", context.containsKey("Val2") ? ("\"" + context.get("Val2") + "\"") : "\"${spring.application.name}\""); - } - - context.put("Comment", context.containsKey("Val3") ? context.get("Val3") : "todo: 集成事件描述"); - context.put("comment", context.get("Comment")); - return context; - }); - if (StringUtils.isBlank(path)) { - getLog().info("模板不匹配:" + templateNode.pattern); - return; - } - getLog().info("生成集成事件代码:" + path); - } - - /** - * @param literalDomainEventDeclaration 文本化领域事件声明 AggregateRootEntityName:DomainEventName - * @param templateNode 模板配置 - */ - public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { - getLog().info("解析领域事件设计:" + literalDomainEventDeclaration); - String path = internalRenderGenericDesign(literalDomainEventDeclaration, parentPath, templateNode, context -> { - String reletivePath = NamingUtils.parentPackageName(context.get("Val0")) - .replace(".", File.separator); - if (StringUtils.isNotBlank(reletivePath)) { - context.put("path", reletivePath); - context.put("package", StringUtils.isEmpty(reletivePath) ? "" : ("." + reletivePath.replace(File.separator, "."))); - } - if (!context.containsKey("Val1")) { - throw new RuntimeException("缺失领域事件名称,领域事件设计格式:AggregateRootEntityName:DomainEventName"); - } - String Name = NamingUtils.toUpperCamelCase(context.get("Val1")); - if (!Name.endsWith("Evt") && !Name.endsWith("Event")) { - Name += "DomainEvent"; - } - context.put("Name", Name); - context.put("DomainEvent", context.get("Name")); - context.put("DOMAIN_EVENT", context.get("DomainEvent")); - context.put("Event", context.get("DomainEvent")); - String entity = NamingUtils.toUpperCamelCase( - NamingUtils.getLastPackageName(context.get("Val0")) - ); - boolean persist = false; - if (context.containsKey("val2") && "`true`persist`1`".contains("`" + context.get("val2") + "`")) { - persist = true; - } - context.put("persist", persist ? "true" : "false"); - context.put("PERSIST", context.get("persist")); - context.put("Entity", entity); - context.put("ENTITY", context.get("Entity")); - context.put("AggregateRoot", context.get("Entity")); - context.put("AGGREGATE_ROOT", context.get("Entity")); - context.put("aggregate_root", context.get("Entity")); - context.put("Aggregate", context.get("package")); - context.put("aggregate", context.get("package")); - if (Objects.equals("domain_event_handler", alias4Design(templateNode.getTag()))) { - context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件订阅描述"); - } else { - context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件描述"); - } - context.put("comment", context.get("Comment")); - return context; - }); - if (StringUtils.isBlank(path)) { - getLog().info("模板不匹配:" + templateNode.pattern); - return; - } - getLog().info("生成领域事件代码:" + path); - } - - /** - * @param literalAggregateFactoryDeclaration 文本化聚合工厂声明 AggregateRootEntityName - * @param templateNode 模板配置 - */ - public void renderDomainLayerAggregateFactory(String literalAggregateFactoryDeclaration, String parentPath, TemplateNode templateNode) throws IOException { - getLog().info("解析聚合工厂设计:" + literalAggregateFactoryDeclaration); - String path = internalRenderGenericDesign(literalAggregateFactoryDeclaration, parentPath, templateNode, context -> { - String entity = context.get("Name"); - String Name = entity + "Factory"; - context.put("Name", Name); - context.put("name", Name.toLowerCase()); - context.put("Entity", entity); - context.put("entity", entity.toLowerCase()); - context.put("ENTITY", context.get("Entity")); - context.put("AggregateRoot", context.get("Entity")); - context.put("AGGREGATE_ROOT", context.get("Entity")); - context.put("aggregate_root", context.get("Entity")); - context.put("Aggregate", context.get("package")); - context.put("aggregate", context.get("package")); - context.put("Factory", context.get("Name")); - context.put("FACTORY", context.get("Factory")); - - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 聚合工厂描述"); - context.put("comment", context.get("Comment")); - return context; - }); - if (StringUtils.isBlank(path)) { - getLog().info("模板不匹配:" + templateNode.pattern); - return; - } - getLog().info("生成聚合工厂代码:" + path); - } - - /** - * @param literalSpecificationDeclaration 文本化聚合工厂声明 AggregateRootEntityName - * @param templateNode 模板配置 - */ - public void renderDomainLayerSpecificaton(String literalSpecificationDeclaration, String parentPath, TemplateNode templateNode) throws IOException { - getLog().info("解析实体规约设计:" + literalSpecificationDeclaration); - String path = internalRenderGenericDesign(literalSpecificationDeclaration, parentPath, templateNode, context -> { - String entity = context.get("Name"); - String Name = entity + "Specification"; - context.put("Name", Name); - context.put("name", Name.toLowerCase()); - context.put("Entity", entity); - context.put("entity", entity.toLowerCase()); - context.put("ENTITY", context.get("Entity")); - context.put("AggregateRoot", context.get("Entity")); - context.put("AGGREGATE_ROOT", context.get("Entity")); - context.put("aggregate_root", context.get("Entity")); - context.put("Aggregate", context.get("package")); - context.put("aggregate", context.get("package")); - context.put("Specification", context.get("Name")); - context.put("SPECIFICATION", context.get("Specification")); - - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 实体规约描述"); - context.put("comment", context.get("Comment")); - return context; - }); - getLog().info("生成实体规约代码:" + path); - } - - /** - * @param literalDomainServiceDeclaration 文本化领域服务声明 DomainServiceName - * @param templateNode 模板配置 - */ - public void renderDomainLayerDomainService(String literalDomainServiceDeclaration, String parentPath, TemplateNode templateNode) throws IOException { - getLog().info("解析领域服务设计:" + literalDomainServiceDeclaration); - String path = internalRenderGenericDesign(literalDomainServiceDeclaration, parentPath, templateNode, context -> { - String name = context.get("Name"); - if (!name.endsWith("Svc") && !name.endsWith("Service")) { - name += "DomainService"; - } - - context.put("Name", name); - context.put("DomainService", context.get("Name")); - context.put("DOMAIN_SERVICE", context.get("DomainService")); - - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 领域服务描述"); - context.put("comment", context.get("Comment")); - return context; - }); - if (StringUtils.isBlank(path)) { - getLog().info("模板不匹配:" + templateNode.pattern); - return; - } - getLog().info("生成领域服务代码:" + path); - } - - /** - * @param literalGenericDeclaration 文本化自定义元素声明 Val1[:Val2[:...]] - * @param templateNode 模板配置 - */ - public void renderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode) throws IOException { - getLog().info("解析自定义元素设计:" + literalGenericDeclaration); - String path = internalRenderGenericDesign(literalGenericDeclaration, parentPath, templateNode, null); - if (StringUtils.isBlank(path)) { - getLog().info("模板不匹配:" + templateNode.pattern); - return; - } - getLog().info("生成自定义元素代码:" + path); - } - - public String internalRenderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode, Function, Map> contextBuilder) throws IOException { - if (StringUtils.isNotBlank(templateNode.pattern)) { - if (!Pattern.compile(templateNode.pattern).asPredicate().test(literalGenericDeclaration)) { - return null; - } - } - String[] segments = TextUtils.splitWithTrim(literalGenericDeclaration, ":"); - Map context = new HashMap<>(getEscapeContext()); - for (int i = 0; i < segments.length; i++) { - context.put("Val" + i, segments[i]); - context.put("val" + i, segments[i].toLowerCase()); - } - - String name = segments[0].toLowerCase(); - String Name = NamingUtils.toUpperCamelCase(NamingUtils.getLastPackageName(segments[0])); - String path = NamingUtils.parentPackageName(segments[0]) - .replace(".", File.separator); - - context.put("Name", Name); - context.put("name", name); - context.put("path", path); - context.put("package", StringUtils.isEmpty(path) ? "" : ("." + path.replace(File.separator, "."))); - if (null != contextBuilder) { - context = contextBuilder.apply(context); - } - PathNode pathNode = templateNode.clone().resolve(context); - return render(pathNode, parentPath); - } - /** * @param pathNode * @param parentPath @@ -755,15 +122,15 @@ public String internalRenderGenericDesign(String literalGenericDeclaration, Stri * @throws IOException */ public String renderDir(PathNode pathNode, String parentPath) throws IOException { - if (!"dir".equalsIgnoreCase(pathNode.type)) { + if (!"dir".equalsIgnoreCase(pathNode.getType())) { throw new RuntimeException("节点类型必须是目录"); } - if (pathNode.name == null || pathNode.name.isEmpty()) { + if (pathNode.getName() == null || pathNode.getName().isEmpty()) { return parentPath; } - String path = parentPath + File.separator + pathNode.name; + String path = parentPath + File.separator + pathNode.getName(); if (FileUtils.fileExists(path)) { - switch (pathNode.conflict) { + switch (pathNode.getConflict()) { case "warn": getLog().warn("目录存在:" + path); break; @@ -794,23 +161,34 @@ public String renderDir(PathNode pathNode, String parentPath) throws IOException return path; } + /** + * 生成设计框架代码 + * + * @param templateNodes + * @param parentPath + * @throws IOException + */ + public void renderDesign(List templateNodes, String parentPath) throws IOException { + + } + /** * @param pathNode * @param parentPath * @return */ public String renderFile(PathNode pathNode, String parentPath) throws IOException { - if (!"file".equalsIgnoreCase(pathNode.type)) { + if (!"file".equalsIgnoreCase(pathNode.getType())) { throw new RuntimeException("节点类型必须是文件"); } - if (pathNode.name == null || pathNode.name.isEmpty()) { + if (pathNode.getName() == null || pathNode.getName().isEmpty()) { throw new RuntimeException("模板节点配置 name 不得为空 parentPath = " + parentPath); } - String path = parentPath + File.separator + pathNode.name; + String path = parentPath + File.separator + pathNode.getName(); - String content = pathNode.data; + String content = pathNode.getData(); if (FileUtils.fileExists(path)) { - switch (pathNode.conflict) { + switch (pathNode.getConflict()) { case "warn": getLog().warn("文件存在:" + path); break; diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java new file mode 100644 index 0000000..6778803 --- /dev/null +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java @@ -0,0 +1,531 @@ +package org.netcorepal.cap4j.ddd.codegen; + +import org.apache.commons.lang3.StringUtils; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.netcorepal.cap4j.ddd.codegen.misc.NamingUtils; +import org.netcorepal.cap4j.ddd.codegen.misc.TextUtils; +import org.netcorepal.cap4j.ddd.codegen.template.PathNode; +import org.netcorepal.cap4j.ddd.codegen.template.TemplateNode; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * 生成设计框架代码 + * + * @author binking338 + * @date 2024/9/13 + */ +@Mojo(name = "gen-design") +public class GenDesignMojo extends GenArchMojo { + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + super.execute(); + } + + /** + * 构建路径节点 + * + * @param pathNode + * @param parentPath + * @throws IOException + */ + @Override + public String render(PathNode pathNode, String parentPath) throws IOException { + String path = parentPath; + switch (pathNode.getType()) { + case "file": + // path = renderFile(pathNode, parentPath); + break; + case "dir": + path = renderDir(pathNode, parentPath); + if (pathNode.getChildren() != null) { + for (PathNode childPathNode : pathNode.getChildren()) { + render(childPathNode, path); + } + } + break; + case "root": + if (pathNode.getChildren() != null) { + for (PathNode childPathNode : pathNode.getChildren()) { + render(childPathNode, parentPath); + } + } + break; + } + return path; + } + + public String alias4Design(String name) { + switch (name.toLowerCase()) { + case "commands": + case "command": + case "cmd": + return "command"; + case "queries": + case "query": + case "qry": + return "query"; + case "clients": + case "client": + case "cli": + return "client"; + case "integration_events": + case "integration_event": + case "events": + case "event": + case "evt": + case "i_e": + case "ie": + return "integration_event"; + case "integration_event_handlers": + case "integration_event_handler": + case "event_handlers": + case "event_handler": + case "evt_hdl": + case "i_e_h": + case "ieh": + return "integration_event_handler"; + case "repositories": + case "repository": + case "repos": + case "repo": + return "repository"; + case "factories": + case "factory": + case "fac": + return "factory"; + case "specifications": + case "specification": + case "specs": + case "spec": + return "specification"; + case "domain_events": + case "domain_event": + case "d_e": + case "de": + return "domain_event"; + case "domain_event_handlers": + case "domain_event_handler": + case "d_e_h": + case "deh": + return "domain_event_handler"; + case "domain_service": + case "service": + case "svc": + return "domain_service"; + default: + return name; + } + } + + /** + * 构建模型设计元素 + * + * @param templateNodes + * @param parentPath + * @throws IOException + */ + @Override + public void renderDesign(List templateNodes, String parentPath) throws IOException { + Map> designMap = Arrays.stream(this.design.split(PATTERN_SPLITTER)) + .map(item -> TextUtils.splitWithTrim(item, ":", 2)) + .filter(item -> item.length == 2) + .collect(Collectors.groupingBy(g -> alias4Design(g[0]), Collectors.mapping(g -> g[1].trim(), Collectors.toSet()))); + + + for (TemplateNode templateNode : templateNodes) { + switch (alias4Design(templateNode.getTag())) { + case "command": + if (designMap.containsKey("command")) { + for (String literalDesign : designMap.get("command")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderAppLayerCommand(literalDesign, parentPath, templateNode); + } + } + } + break; + case "query": + case "query_handler": + if (designMap.containsKey("query")) { + for (String literalDesign : designMap.get("query")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderAppLayerQuery(literalDesign, parentPath, templateNode); + } + } + } + break; + case "client": + case "client_handler": + if (designMap.containsKey("client")) { + for (String literalDesign : designMap.get("client")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderAppLayerClient(literalDesign, parentPath, templateNode); + } + } + } + break; + case "integration_event": + if (designMap.containsKey("integration_event")) { + for (String literalDesign : designMap.get("integration_event")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderAppLayerIntegrationEvent("integration_event", literalDesign, parentPath, templateNode); + } + } + } + case "integration_event_handler": + if (designMap.containsKey("integration_event_handler")) { + for (String literalDesign : designMap.get("integration_event_handler")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderAppLayerIntegrationEvent("integration_event_handler", literalDesign, parentPath, templateNode); + } + } + } + if (designMap.containsKey("integration_event")) { + for (String literalDesign : designMap.get("integration_event")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + if (literalDesign.split(":").length >= 3) { + renderAppLayerIntegrationEvent("integration_event_handler", literalDesign, parentPath, templateNode); + } + } + } + } + break; + case "domain_event": + case "domain_event_handler": + if (designMap.containsKey("domain_event")) { + for (String literalDesign : designMap.get("domain_event")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderDomainLayerDomainEvent(literalDesign, parentPath, templateNode); + } + } + } + break; + case "specification": + if (designMap.containsKey("specification")) { + for (String literalDesign : designMap.get("specification")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderDomainLayerSpecificaton(literalDesign, parentPath, templateNode); + } + } + } + break; + case "factory": + if (designMap.containsKey("factory")) { + for (String literalDesign : designMap.get("factory")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderDomainLayerAggregateFactory(literalDesign, parentPath, templateNode); + } + } + } + break; + case "domain_service": + if (designMap.containsKey("domain_service")) { + for (String literalDesign : designMap.get("domain_service")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderDomainLayerDomainService(literalDesign, parentPath, templateNode); + } + } + } + break; + default: + if (designMap.containsKey(templateNode.getTag())) { + for (String literalDesign : designMap.get(templateNode.getTag())) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderGenericDesign(literalDesign, parentPath, templateNode); + } + } + } + break; + } + } + } + + /** + * @param literalCommandDeclaration 文本化命令声明 CommandName + * @param templateNode 模板配置 + */ + public void renderAppLayerCommand(String literalCommandDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析命令设计:" + literalCommandDeclaration); + String path = internalRenderGenericDesign(literalCommandDeclaration, parentPath, templateNode, context -> { + String Name = context.get("Name"); + if (!Name.endsWith("Cmd") && !Name.endsWith("Command")) { + Name += "Cmd"; + } + context.put("Name", Name); + context.put("Command", context.get("Name")); + context.put("command", context.get("Name").toLowerCase()); + context.put("Request", context.get("Command") + "Request"); + context.put("Response", context.get("Command") + "Response"); + + context.put("ReturnType", context.get("Response")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 命令描述"); + context.put("comment", context.get("Comment")); + return context; + }); + getLog().info("生成命令代码:" + path); + } + + /** + * @param literalQueryDeclaration 文本化查询声明 QueryName + * @param templateNode 模板配置 + */ + public void renderAppLayerQuery(String literalQueryDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析查询设计:" + literalQueryDeclaration); + String path = internalRenderGenericDesign(literalQueryDeclaration, parentPath, templateNode, context -> { + String Name = context.get("Name"); + if (!Name.endsWith("Qry") && !Name.endsWith("Query")) { + Name += "Qry"; + } + context.put("Name", Name); + context.put("Query", context.get("Name")); + context.put("query", context.get("Name").toLowerCase()); + context.put("Request", context.get("Query") + "Request"); + context.put("Response", context.get("Query") + "Response"); + + context.put("ReturnType", context.get("Response")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 查询描述"); + context.put("comment", context.get("Comment")); + return context; + }); + getLog().info("生成查询代码:" + path); + } + + /** + * @param literalClientDeclaration 文本化防腐端声明 ClientName + * @param templateNode 模板配置 + */ + public void renderAppLayerClient(String literalClientDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析防腐端设计:" + literalClientDeclaration); + String path = internalRenderGenericDesign(literalClientDeclaration, parentPath, templateNode, context -> { + String Name = context.get("Name"); + if (!Name.endsWith("Cli") && !Name.endsWith("Client")) { + Name += "Cli"; + } + context.put("Name", Name); + context.put("Client", context.get("Name")); + context.put("client", context.get("Name").toLowerCase()); + context.put("Request", context.get("Name") + "Request"); + context.put("Response", context.get("Name") + "Response"); + + context.put("ReturnType", context.get("Response")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 防腐端描述"); + context.put("comment", context.get("Comment")); + return context; + }); + getLog().info("生成防腐端代码:" + path); + } + + /** + * @param literalType 设计类型 + * @param literalIntegrationEventDeclaration 文本化集成事件声明 IntegrationEventName[:mq-topic[:mq-consumer]] + * @param templateNode 模板配置 + */ + public void renderAppLayerIntegrationEvent(String literalType, String literalIntegrationEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析集成事件设计:" + literalIntegrationEventDeclaration); + String path = internalRenderGenericDesign(literalIntegrationEventDeclaration, parentPath, templateNode, context -> { + String Name = context.get("Name"); + if (!Name.endsWith("Evt") && !Name.endsWith("Event")) { + Name += "IntegrationEvent"; + } + context.put("Name", Name); + context.put("IntegrationEvent", context.get("Name")); + context.put("Event", context.get("IntegrationEvent")); + context.put("INTEGRATION_EVENT", context.get("IntegrationEvent")); + context.put("IE", context.get("IntegrationEvent")); + context.put("I_E", context.get("IntegrationEvent")); + context.put("integration_event", context.get("Name").toLowerCase()); + context.put("event", context.get("integration_event")); + context.put("ie", context.get("integration_event")); + context.put("i_e", context.get("integration_event")); + context.put("MQ_TOPIC", context.containsKey("Val1") ? ("\"" + context.get("Val1") + "\"") : ("\"" + context.get("Val0") + "\"")); + if (Objects.equals(literalType, "integration_event_handler") && !context.containsKey("Val2")) { + context.put("MQ_CONSUMER", "IntegrationEvent.NONE_SUBSCRIBER"); + } else { + context.put("MQ_CONSUMER", context.containsKey("Val2") ? ("\"" + context.get("Val2") + "\"") : "\"${spring.application.name}\""); + } + + context.put("Comment", context.containsKey("Val3") ? context.get("Val3") : "todo: 集成事件描述"); + context.put("comment", context.get("Comment")); + return context; + }); + getLog().info("生成集成事件代码:" + path); + } + + /** + * @param literalDomainEventDeclaration 文本化领域事件声明 AggregateRootEntityName:DomainEventName + * @param templateNode 模板配置 + */ + public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析领域事件设计:" + literalDomainEventDeclaration); + String path = internalRenderGenericDesign(literalDomainEventDeclaration, parentPath, templateNode, context -> { + String reletivePath = NamingUtils.parentPackageName(context.get("Val0")) + .replace(".", File.separator); + if (StringUtils.isNotBlank(reletivePath)) { + context.put("path", reletivePath); + context.put("package", StringUtils.isEmpty(reletivePath) ? "" : ("." + reletivePath.replace(File.separator, "."))); + } + if (!context.containsKey("Val1")) { + throw new RuntimeException("缺失领域事件名称,领域事件设计格式:AggregateRootEntityName:DomainEventName"); + } + String Name = NamingUtils.toUpperCamelCase(context.get("Val1")); + if (!Name.endsWith("Evt") && !Name.endsWith("Event")) { + Name += "DomainEvent"; + } + context.put("Name", Name); + context.put("DomainEvent", context.get("Name")); + context.put("DOMAIN_EVENT", context.get("DomainEvent")); + context.put("Event", context.get("DomainEvent")); + String entity = NamingUtils.toUpperCamelCase( + NamingUtils.getLastPackageName(context.get("Val0")) + ); + boolean persist = false; + if (context.containsKey("val2") && "`true`persist`1`".contains("`" + context.get("val2") + "`")) { + persist = true; + } + context.put("persist", persist ? "true" : "false"); + context.put("PERSIST", context.get("persist")); + context.put("Entity", entity); + context.put("ENTITY", context.get("Entity")); + context.put("AggregateRoot", context.get("Entity")); + context.put("AGGREGATE_ROOT", context.get("Entity")); + context.put("aggregate_root", context.get("Entity")); + context.put("Aggregate", context.get("package")); + context.put("aggregate", context.get("package")); + if (Objects.equals("domain_event_handler", alias4Design(templateNode.getTag()))) { + context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件订阅描述"); + } else { + context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件描述"); + } + context.put("comment", context.get("Comment")); + return context; + }); + getLog().info("生成领域事件代码:" + path); + } + + /** + * @param literalAggregateFactoryDeclaration 文本化聚合工厂声明 AggregateRootEntityName + * @param templateNode 模板配置 + */ + public void renderDomainLayerAggregateFactory(String literalAggregateFactoryDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析聚合工厂设计:" + literalAggregateFactoryDeclaration); + String path = internalRenderGenericDesign(literalAggregateFactoryDeclaration, parentPath, templateNode, context -> { + String entity = context.get("Name"); + String Name = entity + "Factory"; + context.put("Name", Name); + context.put("name", Name.toLowerCase()); + context.put("Entity", entity); + context.put("entity", entity.toLowerCase()); + context.put("ENTITY", context.get("Entity")); + context.put("AggregateRoot", context.get("Entity")); + context.put("AGGREGATE_ROOT", context.get("Entity")); + context.put("aggregate_root", context.get("Entity")); + context.put("Aggregate", context.get("package")); + context.put("aggregate", context.get("package")); + context.put("Factory", context.get("Name")); + context.put("FACTORY", context.get("Factory")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 聚合工厂描述"); + context.put("comment", context.get("Comment")); + return context; + }); + getLog().info("生成聚合工厂代码:" + path); + } + + /** + * @param literalSpecificationDeclaration 文本化聚合工厂声明 AggregateRootEntityName + * @param templateNode 模板配置 + */ + public void renderDomainLayerSpecificaton(String literalSpecificationDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析实体规约设计:" + literalSpecificationDeclaration); + String path = internalRenderGenericDesign(literalSpecificationDeclaration, parentPath, templateNode, context -> { + String entity = context.get("Name"); + String Name = entity + "Specification"; + context.put("Name", Name); + context.put("name", Name.toLowerCase()); + context.put("Entity", entity); + context.put("entity", entity.toLowerCase()); + context.put("ENTITY", context.get("Entity")); + context.put("AggregateRoot", context.get("Entity")); + context.put("AGGREGATE_ROOT", context.get("Entity")); + context.put("aggregate_root", context.get("Entity")); + context.put("Aggregate", context.get("package")); + context.put("aggregate", context.get("package")); + context.put("Specification", context.get("Name")); + context.put("SPECIFICATION", context.get("Specification")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 实体规约描述"); + context.put("comment", context.get("Comment")); + return context; + }); + getLog().info("生成实体规约代码:" + path); + } + + /** + * @param literalDomainServiceDeclaration 文本化领域服务声明 DomainServiceName + * @param templateNode 模板配置 + */ + public void renderDomainLayerDomainService(String literalDomainServiceDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析领域服务设计:" + literalDomainServiceDeclaration); + String path = internalRenderGenericDesign(literalDomainServiceDeclaration, parentPath, templateNode, context -> { + String name = context.get("Name"); + if (!name.endsWith("Svc") && !name.endsWith("Service")) { + name += "DomainService"; + } + + context.put("Name", name); + context.put("DomainService", context.get("Name")); + context.put("DOMAIN_SERVICE", context.get("DomainService")); + + context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 领域服务描述"); + context.put("comment", context.get("Comment")); + return context; + }); + getLog().info("生成领域服务代码:" + path); + } + + /** + * @param literalGenericDeclaration 文本化自定义元素声明 Val1[:Val2[:...]] + * @param templateNode 模板配置 + */ + public void renderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + getLog().info("解析自定义元素设计:" + literalGenericDeclaration); + String path = internalRenderGenericDesign(literalGenericDeclaration, parentPath, templateNode, null); + getLog().info("生成自定义元素代码:" + path); + } + + public String internalRenderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode, Function, Map> contextBuilder) throws IOException { + String[] segments = TextUtils.splitWithTrim(literalGenericDeclaration, ":"); + Map context = new HashMap<>(getEscapeContext()); + for (int i = 0; i < segments.length; i++) { + context.put("Val" + i, segments[i]); + context.put("val" + i, segments[i].toLowerCase()); + } + + String name = segments[0].toLowerCase(); + String Name = NamingUtils.toUpperCamelCase(NamingUtils.getLastPackageName(segments[0])); + String path = NamingUtils.parentPackageName(segments[0]) + .replace(".", File.separator); + + context.put("Name", Name); + context.put("name", name); + context.put("path", path); + context.put("package", StringUtils.isEmpty(path) ? "" : ("." + path.replace(File.separator, "."))); + if (null != contextBuilder) { + context = contextBuilder.apply(context); + } + PathNode pathNode = templateNode.clone().resolve(context); + return render(pathNode, parentPath); + } + +} diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/PathNode.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/PathNode.java new file mode 100644 index 0000000..13e44c4 --- /dev/null +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/PathNode.java @@ -0,0 +1,91 @@ +package org.netcorepal.cap4j.ddd.codegen.template; + +import com.alibaba.fastjson.JSON; +import lombok.Data; +import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * 脚手架模板文件节点 + * + * @author binking338 + * @date 2024/9/13 + */ +@Data +public class PathNode { + /** + * 节点类型:root|dir|file + */ + String type; + /** + * 节点标签:关联模板 + */ + String tag; + /** + * 节点名称 + */ + String name; + /** + * 模板源类型:raw|url + */ + String format = "raw"; + /** + * 模板数据数据 + */ + String data; + /** + * 冲突处理:skip|warn|overwrite + */ + String conflict = "skip"; + + /** + * 下级节点 + */ + List children; + + public PathNode clone() { + PathNode pathNode = JSON.parseObject(JSON.toJSONString(this), PathNode.class); + return pathNode; + } + + public PathNode resolve(Map context) throws IOException { + if (null != this.name) { + this.name = this.name.replace("${basePackage}", "${basePackage__as_path}"); + this.name = escape(this.name, context); + } + String rawData = ""; + switch (this.format) { + case "url": + if (null != this.data) { + rawData = SourceFileUtils.loadFileContent(this.data, context.get("archTemplateEncoding")); + } + break; + case "raw": + default: + rawData = this.data; + break; + } + this.data = escape(rawData, context); + this.format = "raw"; + if (null != this.children) { + for (PathNode child : this.children) { + child.resolve(context); + } + } + return this; + } + + protected String escape(String content, Map context) { + if (null == content) { + return ""; + } + String result = content; + for (Map.Entry kv : context.entrySet()) { + result = result.replace("${" + kv.getKey() + "}", kv.getValue() == null ? "" : kv.getValue()); + } + return result; + } +} diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/Template.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/Template.java new file mode 100644 index 0000000..e0fc46b --- /dev/null +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/Template.java @@ -0,0 +1,36 @@ +package org.netcorepal.cap4j.ddd.codegen.template; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 模板 + * + * @author binking338 + * @date 2024/9/13 + */ +@Data +@NoArgsConstructor +public class Template extends PathNode { + + /** + * 模板节点 + */ + List templates = null; + + /** + * 获取模板 + * + * @param tag + * @return + */ + public List select(String tag) { + if (this.templates == null) return null; + List nodes = templates.stream().filter(t -> Objects.equals(t.tag, tag)).collect(Collectors.toList()); + return nodes; + } +} diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/TemplateNode.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/TemplateNode.java new file mode 100644 index 0000000..c5515c3 --- /dev/null +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/template/TemplateNode.java @@ -0,0 +1,34 @@ +package org.netcorepal.cap4j.ddd.codegen.template; + +import com.alibaba.fastjson.JSON; +import lombok.Data; + +import java.io.IOException; +import java.util.Map; + +/** + * 脚手架模板模板节点 + * + * @author binking338 + * @date 2024/9/13 + */ +@Data +public class TemplateNode extends PathNode { + + /** + * 元素匹配正则 + */ + String pattern; + + public TemplateNode clone() { + TemplateNode templateNode = JSON.parseObject(JSON.toJSONString(this), TemplateNode.class); + return templateNode; + } + + @Override + public PathNode resolve(Map context) throws IOException { + super.resolve(context); + this.tag = ""; + return this; + } +} From 670b06591448c9bf86f843f59b04d023fba4ee8f Mon Sep 17 00:00:00 2001 From: binking338 Date: Sat, 14 Sep 2024 01:20:20 +0800 Subject: [PATCH 31/62] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- .../cap4j/ddd/codegen/GenArchMojo.java | 32 ++- .../cap4j/ddd/codegen/GenDesignMojo.java | 77 +++++- .../cap4j/ddd/codegen/HelpMojo.java | 133 ++++++---- .../cap4j/ddd/codegen/MyAbstractMojo.java | 12 +- .../ddd/codegen/misc/SqlSchemaUtils.java | 2 +- cap4j-ddd-codegen-template.json | 2 +- ...23\346\236\204\344\273\213\347\273\215.md" | 3 + ...26\347\240\201\346\214\207\345\215\227.md" | 105 +++++++- ...26\347\240\201\346\214\207\345\215\227.md" | 243 +++++++++++++++--- 10 files changed, 496 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index e21b92f..710048c 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,8 @@ Mediator中介者模式、 cap4j-ddd-codegen-maven-plugin 1.0.0-alpha-2 - https://raw.githubusercontent.com/netcorepal/cap4j/main/cap4j-ddd-codegen-template.json org.netcorepal.cap4j.ddd.example + https://raw.githubusercontent.com/netcorepal/cap4j/main/cap4j-ddd-codegen-template.json false -adapter -domain @@ -63,11 +63,11 @@ Mediator中介者模式、 test
+ id version db_deleted db_created_at,db_updated_at - static org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisorSupport.events diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index 310aef0..daf073b 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -17,7 +17,9 @@ import java.nio.charset.Charset; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * 生成项目目录结构 @@ -30,12 +32,12 @@ public class GenArchMojo extends MyAbstractMojo { public final String PATTERN_SPLITTER = "[\\,\\;]"; - private String projectGroupId = ""; - private String projectArtifactId = ""; - private String projectVersion = ""; + protected String projectGroupId = ""; + protected String projectArtifactId = ""; + protected String projectVersion = ""; - private String projectDir = ""; - private Template template = null; + protected String projectDir = ""; + protected Template template = null; @Override public void execute() throws MojoExecutionException, MojoFailureException { @@ -76,8 +78,12 @@ public void execute() throws MojoExecutionException, MojoFailureException { } catch (IOException e) { getLog().error("模板文件加载失败!"); } + startRender(); + } + + protected void startRender(){ try { - render(template, projectDir); + render(template, projectDir, false); } catch (IOException e) { throw new RuntimeException(e); } @@ -90,24 +96,24 @@ public void execute() throws MojoExecutionException, MojoFailureException { * @param parentPath * @throws IOException */ - public String render(PathNode pathNode, String parentPath) throws IOException { + public String render(PathNode pathNode, String parentPath, boolean onlyRenderDir) throws IOException { String path = parentPath; switch (pathNode.getType()) { case "file": - path = renderFile(pathNode, parentPath); + path = renderFile(pathNode, parentPath, onlyRenderDir); break; case "dir": path = renderDir(pathNode, parentPath); if (pathNode.getChildren() != null) { for (PathNode childPathNode : pathNode.getChildren()) { - render(childPathNode, path); + render(childPathNode, path, onlyRenderDir); } } break; case "root": if (pathNode.getChildren() != null) { for (PathNode childPathNode : pathNode.getChildren()) { - render(childPathNode, parentPath); + render(childPathNode, parentPath, onlyRenderDir); } } break; @@ -177,7 +183,7 @@ public void renderDesign(List templateNodes, String parentPath) th * @param parentPath * @return */ - public String renderFile(PathNode pathNode, String parentPath) throws IOException { + public String renderFile(PathNode pathNode, String parentPath, boolean onlyRenderDir) throws IOException { if (!"file".equalsIgnoreCase(pathNode.getType())) { throw new RuntimeException("节点类型必须是文件"); } @@ -185,6 +191,7 @@ public String renderFile(PathNode pathNode, String parentPath) throws IOExceptio throw new RuntimeException("模板节点配置 name 不得为空 parentPath = " + parentPath); } String path = parentPath + File.separator + pathNode.getName(); + if (onlyRenderDir) return path; String content = pathNode.getData(); if (FileUtils.fileExists(path)) { @@ -216,6 +223,7 @@ public Map getEscapeContext() { context.put("version", projectVersion); context.put("archTemplate", archTemplate); context.put("archTemplateEncoding", archTemplateEncoding); + context.put("designFile", designFile); context.put("basePackage", basePackage); context.put("basePackage__as_path", basePackage.replace(".", File.separator)); context.put("multiModule", multiModule ? "true" : "false"); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java index 6778803..ddb69d5 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java @@ -4,6 +4,7 @@ import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; +import org.codehaus.plexus.util.FileUtils; import org.netcorepal.cap4j.ddd.codegen.misc.NamingUtils; import org.netcorepal.cap4j.ddd.codegen.misc.TextUtils; import org.netcorepal.cap4j.ddd.codegen.template.PathNode; @@ -24,12 +25,22 @@ */ @Mojo(name = "gen-design") public class GenDesignMojo extends GenArchMojo { + public final String DESIGN_PARAMS_SPLITTER = ":"; @Override public void execute() throws MojoExecutionException, MojoFailureException { super.execute(); } + @Override + protected void startRender() { + try { + render(template, projectDir, true); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** * 构建路径节点 * @@ -38,24 +49,24 @@ public void execute() throws MojoExecutionException, MojoFailureException { * @throws IOException */ @Override - public String render(PathNode pathNode, String parentPath) throws IOException { + public String render(PathNode pathNode, String parentPath, boolean onlyRenderDir) throws IOException { String path = parentPath; switch (pathNode.getType()) { case "file": - // path = renderFile(pathNode, parentPath); + path = renderFile(pathNode, parentPath, onlyRenderDir); break; case "dir": path = renderDir(pathNode, parentPath); if (pathNode.getChildren() != null) { for (PathNode childPathNode : pathNode.getChildren()) { - render(childPathNode, path); + render(childPathNode, path, onlyRenderDir); } } break; case "root": if (pathNode.getChildren() != null) { for (PathNode childPathNode : pathNode.getChildren()) { - render(childPathNode, parentPath); + render(childPathNode, parentPath, onlyRenderDir); } } break; @@ -92,6 +103,13 @@ public String alias4Design(String name) { case "evt_hdl": case "i_e_h": case "ieh": + case "integration_event_subscribers": + case "integration_event_subscriber": + case "event_subscribers": + case "event_subscriber": + case "evt_sub": + case "i_e_s": + case "ies": return "integration_event_handler"; case "repositories": case "repository": @@ -106,6 +124,7 @@ public String alias4Design(String name) { case "specification": case "specs": case "spec": + case "spe": return "specification"; case "domain_events": case "domain_event": @@ -116,6 +135,10 @@ public String alias4Design(String name) { case "domain_event_handler": case "d_e_h": case "deh": + case "domain_event_subscribers": + case "domain_event_subscriber": + case "d_e_s": + case "des": return "domain_event_handler"; case "domain_service": case "service": @@ -126,6 +149,16 @@ public String alias4Design(String name) { } } + public Map> resolveLiteralDesign(String design) { + if (StringUtils.isBlank(design)) { + return new HashMap<>(); + } + return Arrays.stream(design.split(PATTERN_SPLITTER)) + .map(item -> TextUtils.splitWithTrim(item, DESIGN_PARAMS_SPLITTER, 2)) + .filter(item -> item.length == 2) + .collect(Collectors.groupingBy(g -> alias4Design(g[0]), Collectors.mapping(g -> g[1].trim(), Collectors.toSet()))); + } + /** * 构建模型设计元素 * @@ -135,11 +168,14 @@ public String alias4Design(String name) { */ @Override public void renderDesign(List templateNodes, String parentPath) throws IOException { - Map> designMap = Arrays.stream(this.design.split(PATTERN_SPLITTER)) - .map(item -> TextUtils.splitWithTrim(item, ":", 2)) - .filter(item -> item.length == 2) - .collect(Collectors.groupingBy(g -> alias4Design(g[0]), Collectors.mapping(g -> g[1].trim(), Collectors.toSet()))); - + String design = ""; + if (StringUtils.isNotBlank(this.design)) { + design += this.design; + } + if (StringUtils.isNotBlank(this.designFile) && FileUtils.fileExists(this.designFile)) { + design += (DESIGN_PARAMS_SPLITTER + FileUtils.fileRead(this.designFile, this.archTemplateEncoding)); + } + Map> designMap = resolveLiteralDesign(design); for (TemplateNode templateNode : templateNodes) { switch (alias4Design(templateNode.getTag())) { @@ -191,7 +227,7 @@ public void renderDesign(List templateNodes, String parentPath) th if (designMap.containsKey("integration_event")) { for (String literalDesign : designMap.get("integration_event")) { if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { - if (literalDesign.split(":").length >= 3) { + if (literalDesign.split(DESIGN_PARAMS_SPLITTER).length >= 3) { renderAppLayerIntegrationEvent("integration_event_handler", literalDesign, parentPath, templateNode); } } @@ -199,7 +235,6 @@ public void renderDesign(List templateNodes, String parentPath) th } break; case "domain_event": - case "domain_event_handler": if (designMap.containsKey("domain_event")) { for (String literalDesign : designMap.get("domain_event")) { if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { @@ -207,6 +242,22 @@ public void renderDesign(List templateNodes, String parentPath) th } } } + if (designMap.containsKey("domain_event_handler")) { + for (String literalDesign : designMap.get("domain_event_handler")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderDomainLayerDomainEvent(literalDesign, parentPath, templateNode); + } + } + } + break; + case "domain_event_handler": + if (designMap.containsKey("domain_event_handler")) { + for (String literalDesign : designMap.get("domain_event_handler")) { + if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { + renderDomainLayerDomainEvent(literalDesign, parentPath, templateNode); + } + } + } break; case "specification": if (designMap.containsKey("specification")) { @@ -505,7 +556,7 @@ public void renderGenericDesign(String literalGenericDeclaration, String parentP } public String internalRenderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode, Function, Map> contextBuilder) throws IOException { - String[] segments = TextUtils.splitWithTrim(literalGenericDeclaration, ":"); + String[] segments = TextUtils.splitWithTrim(literalGenericDeclaration, DESIGN_PARAMS_SPLITTER); Map context = new HashMap<>(getEscapeContext()); for (int i = 0; i < segments.length; i++) { context.put("Val" + i, segments[i]); @@ -525,7 +576,7 @@ public String internalRenderGenericDesign(String literalGenericDeclaration, Stri context = contextBuilder.apply(context); } PathNode pathNode = templateNode.clone().resolve(context); - return render(pathNode, parentPath); + return render(pathNode, parentPath, false); } } diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java index ae00c13..fa7e354 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java @@ -16,103 +16,108 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info(""); getLog().info(""); getLog().info("plugins goals:"); - getLog().info(" gen-ddd:gen-arch 生成初始代码、包结构(支持自定义脚手架配置)"); - getLog().info(" gen-ddd:gen-entity 生成聚合实体"); - getLog().info(" gen-ddd:gen-repository 生成聚合根仓储"); - getLog().info(" gen-ddd:help 帮助"); + getLog().info(" cap4j-ddd-codegen:gen-arch 生成初始代码、包结构(支持自定义脚手架配置)"); + getLog().info(" cap4j-ddd-codegen:gen-design 生成设计元素(支持自定义脚手架配置)"); + getLog().info(" cap4j-ddd-codegen:gen-entity 生成聚合实体"); + getLog().info(" cap4j-ddd-codegen:gen-repository 生成聚合根仓储"); + getLog().info(" cap4j-ddd-codegen:help 帮助"); getLog().info(""); getLog().info(""); getLog().info("pom.xml插件配置"); getLog().info("--------------------------------------------------"); getLog().info("\n" + " \n" + - " \n" + - " https://raw.githubusercontent.com/netcorepal/cap4j/main/cap4j-ddd-codegen-template.json\n" + - " \n" + - " UTF-8\n" + " \n" + " org.netcorepal.cap4j.ddd.example\n" + - " \n" + + " \n" + + " https://raw.githubusercontent.com/netcorepal/cap4j/main/cap4j-ddd-codegen-template.json\n" + + " \n" + + " UTF-8\n" + + " \n" + " false\n" + - " \n" + + " \n" + " -adapter\n" + - " \n" + + " \n" + " -domain\n" + - " \n" + + " \n" + " -application\n" + - " \n" + + " \n" + " \n" + " \n" + " \n" + - " \n" + + " \n" + " root\n" + - " \n" + + " \n" + " 123456\n" + - " \n" + + " \n" + " test\n" + - " \n" + + " \n" + " \n" + "
\n" + - " \n" + - " \n" + + " \n" + " \n" + - " \n" + + " \n" + " id\n" + - " \n" + + " \n" + " version\n" + - " \n" + + " \n" + " db_deleted\n" + - " \n" + + " \n" + " \n" + " db_created_at,db_updated_at\n" + " \n" + - " \n" + + " \n" + " \n" + - " \n" + + " \n" + " \n" + - " \n" + + " \n" + " abs\n" + - " \n" + + " \n" + " domain._share.meta\n" + - " \n" + + " \n" + " SUBSELECT\n" + - " \n" + + " \n" + " EAGER\n" + - " \n" + + " \n" + " \n" + - " \n" + + " \n" + " code\n" + - " \n" + + " \n" + " name\n" + - " \n" + + " \n" + " true\n" + - " \n" + + " \n" + " java.time\n" + - " \n" + + " \n" + " \n" + - " \n" + + " \n" + " false\n" + - " \n" + + " \n" + " true\n" + - " \n" + + " \n" + " true\n" + - " \n" + + " \n" + " false\n" + - " \n" + + " \n" + " Long\n" + - " \n" + + " \n" + " \n" + - " \n" + + " \n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + - " \n" + + " \n" + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + "
"); getLog().info(""); getLog().info(""); - getLog().info("gen-ddd:gen-entity 支持如下【表注解】:"); + getLog().info("mvn cap4j-ddd-codegen:gen-entity"); + getLog().info("支持如下【表注解】:"); getLog().info("--------------------------------------------------"); getLog().info("[@AggregateRoot;]"); getLog().info("[@Root;]"); @@ -167,11 +172,13 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("--------------------------------------------------"); getLog().info("@Specification;"); getLog().info("@Spec;"); - getLog().info("功能:标注该表对应聚合需生成实体规约"); + getLog().info("@Spe;"); + getLog().info("功能:标注该表对应聚合需生成实体规格约束"); getLog().info("--------------------------------------------------"); getLog().info(""); getLog().info(""); - getLog().info("gen-ddd:gen-entity 支持如下【列注解】:"); + getLog().info("mvn cap4j-ddd-codegen:gen-entity"); + getLog().info("支持如下【列注解】:"); getLog().info("--------------------------------------------------"); getLog().info("@Reference[={table}];"); getLog().info("@Ref[={table}];"); @@ -211,5 +218,37 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("--------------------------------------------------"); getLog().info(""); getLog().info(""); + getLog().info("mvn cap4j-ddd-codegen:gen-design -Ddesign=literalDesign1;[literalDesign2;][...][literalDesignN;]"); + getLog().info("mvn cap4j-ddd-codegen:gen-design -DdesignFile=/path/to/designFile"); + getLog().info("literalDesignN; 支持如下设计元素:"); + getLog().info("--------------------------------------------------"); + getLog().info("cmd:{命令名称}[:命令说明]"); + getLog().info("功能:定义【应用层】设计元素【命令】,CQS中的C"); + getLog().info("--------------------------------------------------"); + getLog().info("qry:{查询名称}[:查询说明]"); + getLog().info("功能:定义【应用层】设计元素【查询】,CQS中的Q"); + getLog().info("--------------------------------------------------"); + getLog().info("cli:{防腐端名称}[:防腐端说明]"); + getLog().info("功能:定义【应用层】设计元素【防腐端】,作为外部服务接口防腐层,ACL"); + getLog().info("--------------------------------------------------"); + getLog().info("ie:{集成事件名称}[:mq-topic[:mq-consumer[:集成事件说明]]]"); + getLog().info("功能:定义【应用层】设计元素【集成事件】,Integration Event"); + getLog().info("--------------------------------------------------"); + getLog().info("ies:{集成事件名称}[:mq-topic[:mq-consumer[:集成事件说明]]]"); + getLog().info("ieh:{集成事件名称}[:mq-topic[:mq-consumer[:集成事件说明]]]"); + getLog().info("功能:定义【应用层】设计元素【集成事件订阅】,Integration Event Subscriber"); + getLog().info("--------------------------------------------------"); + getLog().info("de:{所在聚合根名称}:{领域事件名称}[:领域事件说明]"); + getLog().info("功能:定义【领域层】设计元素【领域事件】,并自动生成【领域事件订阅】,Domain Event & Domain Event Subscriber"); + getLog().info("--------------------------------------------------"); + getLog().info("fac:{所在聚合根名称}[:聚合工厂说明]"); + getLog().info("功能:定义【领域层】设计元素【领域模型工厂/聚合工厂】,Domain Model Factory / Aggregate Factory"); + getLog().info("--------------------------------------------------"); + getLog().info("spe:{所在聚合根名称}[:实体规格约束说明]"); + getLog().info("功能:定义【领域层】设计元素【实体规格约束】,Specification"); + getLog().info("--------------------------------------------------"); + getLog().info("svc:{领域服务名称}[:领域服务说明]"); + getLog().info("功能:定义【领域层】设计元素【领域服务】,慎用该元素,警惕模型贫血化,Domain Service"); + getLog().info("--------------------------------------------------"); } } diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java index 3d4e041..9f7198e 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java @@ -69,15 +69,21 @@ public abstract class MyAbstractMojo extends AbstractMojo { public String moduleNameSuffix4Application = "-application"; /** - * 添加应用层设计元素(命令cmd、查询qry、集成事件event、防腐客户端cli...) - * - * cmd:PlaceOrderCommand:Long, qry:GetOrderQuery, event:OrderPlacedIntegrationEvent, cli:GenerateBillClient + * 添加应用层或领域层设计元素(命令cmd、查询qry、集成事件event、防腐客户端cli...) * * @parameter expression="${design}" */ @Parameter(property = "design", defaultValue = "") public String design = ""; + /** + * 添加应用层或领域层设计元素(命令cmd、查询qry、集成事件event、防腐客户端cli...) + * + * @parameter expression="${designFile}" + */ + @Parameter(property = "designFile", defaultValue = "") + public String designFile = ""; + /** diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java index 19e939e..6285704 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java @@ -749,7 +749,7 @@ public static boolean hasFactory(Map table) { * @return */ public static boolean hasSpecification(Map table) { - return isAggregateRoot(table) && hasAnyAnnotation(table, Arrays.asList("Specification", "Spec")); + return isAggregateRoot(table) && hasAnyAnnotation(table, Arrays.asList("Specification", "Spec", "Spe")); } public static boolean hasDomainEvent(Map table) { diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index f41eb5a..f8ceb01 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -706,7 +706,7 @@ "name": "pom.xml", "format": "raw", "conflict": "overwrite", - "data": "\n 4.0.0\n\n ${groupId}\n ${artifactId}\n ${version}\n jar\n\n ddd-sample\n\n \n UTF-8\n UTF-8\n 1.8\n 1.0.0-alpha-2\n\n 1.18.16\n\n 31.1-jre\n 3.12.0\n 4.4\n 1.2.79\n\n 5.1.46\n 1.2.8\n\n 2.7.6\n 2021.0.5\n \n \n\n 1.6.14\n 3.0.3\n 2.2.7\n \n\n \n \n \n org.springframework.boot\n spring-boot-dependencies\n ${spring-boot.version}\n pom\n import\n \n \n org.springframework.cloud\n spring-cloud-dependencies\n ${spring-cloud.version}\n pom\n import\n \n \n \n\n \n \n org.projectlombok\n lombok\n true\n ${org.projectlombok.version}\n \n\n \n io.github.netcorepal\n cap4j-ddd-starter-jpa-rocketmq\n ${cap4j.version}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n provided\n \n\n \n \n org.springframework.boot\n spring-boot-starter-web\n \n \n org.springframework.boot\n spring-boot-starter-actuator\n \n \n org.springframework.boot\n spring-boot-starter-validation\n \n\n \n \n org.apache.commons\n commons-lang3\n ${commons.lang3.version}\n \n \n org.apache.commons\n commons-collections4\n ${commons.collections4.version}\n \n \n com.google.guava\n guava\n ${guava.version}\n \n \n com.alibaba\n fastjson\n ${fastjson.version}\n \n\n \n \n com.ctrip.framework.apollo\n apollo-client\n 1.7.0\n \n \n \n com.githup.liuyanggithup\n xxl-job-core\n 1.2.1\n \n \n \n org.springframework.cloud\n spring-cloud-starter-openfeign\n \n \n io.github.openfeign\n feign-okhttp\n 11.10\n \n\n \n \n org.springframework.boot\n spring-boot-starter-data-jpa\n \n \n \n org.mybatis.spring.boot\n mybatis-spring-boot-starter\n 2.1.3\n \n \n \n com.alibaba\n druid-spring-boot-starter\n ${spring-boot-druid.version}\n \n \n \n mysql\n mysql-connector-java\n ${mysql-connector.version}\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n org.springdoc\n springdoc-openapi-ui\n ${springdoc.version}\n \n \n com.github.xiaoymin\n knife4j-springdoc-ui\n ${knife4j.version}\n \n \n io.swagger.core.v3\n swagger-annotations\n ${swagger3.version}\n \n\n \n net.logstash.logback\n logstash-logback-encoder\n 6.6\n \n \n junit\n junit\n 3.8.1\n test\n \n \n \n ${project.artifactId}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n \n ${archTemplate}\n ${archTemplateEncoding}\n ${basePackage}\n ${multiModule}\n ${moduleNameSuffix4Adapter}\n ${moduleNameSuffix4Domain}\n ${moduleNameSuffix4Application}\n \n \n \n ${user}\n ${pwd}\n ${schema}\n ${table}
\n ${ignoreTable}\n ${idField}\n ${versionField}\n ${deletedField}\n ${readonlyFields}\n ${ignoreFields}\n ${entityBaseClass}\n ${entityClassExtraImports}\n ${entityMetaInfoClassOutputMode}\n ${entityMetaInfoClassOutputPackage}\n ${fetchMode}\n ${fetchType}\n ${idGenerator}\n ${enumValueField}\n ${enumNameField}\n ${enumUnmatchedThrowException}\n ${datePackage4Java}\n ${typeRemapping}\n ${generateDefault}\n ${generateDbType}\n ${generateSchema}\n ${generateBuild}\n ${aggregateIdentityClass}\n ${aggregateRootAnnotation}\n ${aggregateRepositoryBaseClass}\n ${aggregateRepositoryCustomerCode}\n ${ignoreAggregateRoots}\n
\n
\n \n org.springframework.boot\n spring-boot-maven-plugin\n 2.6.3\n \n \n \n \n repackage\n \n repackage\n \n \n ${basePackage}.StartApplication\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 1.8\n 1.8\n UTF-8\n \n \n \n org.apache.maven.plugins\n maven-archetype-plugin\n 3.2.0\n \n\n
\n
\n
" + "data": "\n 4.0.0\n\n ${groupId}\n ${artifactId}\n ${version}\n jar\n\n ddd-sample\n\n \n UTF-8\n UTF-8\n 1.8\n 1.0.0-alpha-2\n\n 1.18.16\n\n 31.1-jre\n 3.12.0\n 4.4\n 1.2.79\n\n 5.1.46\n 1.2.8\n\n 2.7.6\n 2021.0.5\n \n \n\n 1.6.14\n 3.0.3\n 2.2.7\n \n\n \n \n \n org.springframework.boot\n spring-boot-dependencies\n ${spring-boot.version}\n pom\n import\n \n \n org.springframework.cloud\n spring-cloud-dependencies\n ${spring-cloud.version}\n pom\n import\n \n \n \n\n \n \n org.projectlombok\n lombok\n true\n ${org.projectlombok.version}\n \n\n \n io.github.netcorepal\n cap4j-ddd-starter-jpa-rocketmq\n ${cap4j.version}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n provided\n \n\n \n \n org.springframework.boot\n spring-boot-starter-web\n \n \n org.springframework.boot\n spring-boot-starter-actuator\n \n \n org.springframework.boot\n spring-boot-starter-validation\n \n\n \n \n org.apache.commons\n commons-lang3\n ${commons.lang3.version}\n \n \n org.apache.commons\n commons-collections4\n ${commons.collections4.version}\n \n \n com.google.guava\n guava\n ${guava.version}\n \n \n com.alibaba\n fastjson\n ${fastjson.version}\n \n\n \n \n com.ctrip.framework.apollo\n apollo-client\n 1.7.0\n \n \n \n com.githup.liuyanggithup\n xxl-job-core\n 1.2.1\n \n \n \n org.springframework.cloud\n spring-cloud-starter-openfeign\n \n \n io.github.openfeign\n feign-okhttp\n 11.10\n \n\n \n \n org.springframework.boot\n spring-boot-starter-data-jpa\n \n \n \n org.mybatis.spring.boot\n mybatis-spring-boot-starter\n 2.1.3\n \n \n \n com.alibaba\n druid-spring-boot-starter\n ${spring-boot-druid.version}\n \n \n \n mysql\n mysql-connector-java\n ${mysql-connector.version}\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n org.springdoc\n springdoc-openapi-ui\n ${springdoc.version}\n \n \n com.github.xiaoymin\n knife4j-springdoc-ui\n ${knife4j.version}\n \n \n io.swagger.core.v3\n swagger-annotations\n ${swagger3.version}\n \n\n \n net.logstash.logback\n logstash-logback-encoder\n 6.6\n \n \n junit\n junit\n 3.8.1\n test\n \n \n \n ${project.artifactId}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n \n ${basePackage}\n ${archTemplate}\n ${archTemplateEncoding}\n ${designFile}\n ${multiModule}\n ${moduleNameSuffix4Adapter}\n ${moduleNameSuffix4Domain}\n ${moduleNameSuffix4Application}\n \n \n \n ${user}\n ${pwd}\n ${schema}\n ${table}
\n ${ignoreTable}\n ${ignoreFields}\n ${idField}\n ${versionField}\n ${deletedField}\n ${readonlyFields}\n ${entityBaseClass}\n ${entityClassExtraImports}\n ${entityMetaInfoClassOutputMode}\n ${entityMetaInfoClassOutputPackage}\n ${fetchMode}\n ${fetchType}\n ${idGenerator}\n ${enumValueField}\n ${enumNameField}\n ${enumUnmatchedThrowException}\n ${datePackage4Java}\n ${typeRemapping}\n ${generateDefault}\n ${generateDbType}\n ${generateSchema}\n ${generateBuild}\n ${aggregateIdentityClass}\n ${aggregateRootAnnotation}\n ${aggregateRepositoryBaseClass}\n ${aggregateRepositoryCustomerCode}\n ${ignoreAggregateRoots}\n
\n
\n \n org.springframework.boot\n spring-boot-maven-plugin\n 2.6.3\n \n \n \n \n repackage\n \n repackage\n \n \n ${basePackage}.StartApplication\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 1.8\n 1.8\n UTF-8\n \n \n \n org.apache.maven.plugins\n maven-archetype-plugin\n 3.2.0\n \n\n
\n
\n
" } ] } \ No newline at end of file diff --git "a/doc/00_\351\241\271\347\233\256\345\210\206\345\261\202\347\273\223\346\236\204\344\273\213\347\273\215.md" "b/doc/00_\351\241\271\347\233\256\345\210\206\345\261\202\347\273\223\346\236\204\344\273\213\347\273\215.md" index 465fa32..2e93562 100644 --- "a/doc/00_\351\241\271\347\233\256\345\210\206\345\261\202\347\273\223\346\236\204\344\273\213\347\273\215.md" +++ "b/doc/00_\351\241\271\347\233\256\345\210\206\345\261\202\347\273\223\346\236\204\344\273\213\347\273\215.md" @@ -3,6 +3,9 @@ ```xml org.netcorepal.cap4j.ddd.example ``` +```shell +mvn cap4j-ddd-codegen:gen-arch +``` 基于基础包路径配置,`cap4j-ddd-codegen`插件在maven项目源码目录`src/main/java/org/netcorepal/cap4j/ddd/example`下将会生成4个`package`。 > - `_share` 公共代码 > - `adapter` 适配层(Interface Adapter) diff --git "a/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" index bcdb971..1fa5df9 100644 --- "a/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" +++ "b/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -9,8 +9,10 @@ 所以,我们可以激进一点地认为,领域模型的设计关键,就是实体(或值对象)及其之间关系的设计。 为了能够在设计实现阶段方便地进行实体模型迭代,cap4j在`cap4j-ddd-codegen`中提供了两个命令来支持基于db-first模式的实体模型设计。 -- `gen-entity` -- `gen-repository` +```shell +mvn cap4j-ddd-codegen:gen-entity +mvn cap4j-ddd-codegen:gen-repository +``` ## 实体持久化映射 @@ -40,13 +42,13 @@ > `@P`=_parent_entity_table_; 支持表注解 > -> 指示插件将改变映射成聚合内普通实体,该实体在 _parent_entity_table_ 的实体中有一个引用属性。 +> 指示插件将该表映射成聚合内普通实体,该实体在 _parent_entity_table_ 的实体中有一个引用属性。 > `@T`=_JavaType_; 支持表注解、列注解 > -> 在表的注释中注解指示插件该表映射的实体类名不按默认规则生成,直接使用注解赋赋值的 _JavaType_ 作为类名。 +> 指示插件将该表映射的实体类名或字段类型不按默认规则生成,直接使用注解赋值的 _JavaType_ 。 -> `@E`=_0_:_ENUM_FIELD_:_枚举字段注释_; 支持列注解 +> `@E`=_整数编号_:_ENUM_FIELD_NAME_:_枚举字段注释_; 支持列注解 > > 指示插件映射该字段是,该字段需要映射成Java枚举类型,该注解需要配合`@T`一起工作。需要利用`@T`注解给枚举类命名。 @@ -451,9 +453,19 @@ cap4j的领域事件发布需配合`UnitOfWork`模式实现,因为领域事件 > - `消费方与订阅方同一事务` @DomainEvent 关于领域事件与集成事件 -> 集成事件指会对系统内其他服务发布的领域事件。通常如果要区分领域事件和集成事件,那么领域事件一般指的是不需要对外发布的业务事件,仅在内部聚合之间应用。 -> +> 集成事件指会对系统内其他服务发布的领域事件。通常如果要区分领域事件和集成事件,那么领域事件一般指的是没有外部服务关注的业务事件,仅在内部聚合之间应用。 + +#### 辅助代码生成 +方式一:数据库注解,在聚合根对应的表注释中使用注解声明领域事件 +```text +@DE=OrderPlaced; +``` +方式二:gen-design命令 +```text +mvn cap4j-ddd-codegen:gen-design -Ddesign=de:order.Order:OrderPlaced:下单领域事件 +``` +#### 代码示例 ```java package org.netcorepal.cap4j.ddd.example.domain.aggregates.events; @@ -523,7 +535,7 @@ import static org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisorSupport 以Outbox模式传播的领域事件内部会有重试机制来保障事务的最终一致性,以实现领域事件被各个订阅方至少消费一次的承诺。 但是为了保障系统不会因为传播链路的故障出现无限重试,这个`至少一次`也是有前提的,通过[@Retry](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/share/annotation/Retry.java)我们可以配置事件传播的失效以及重试次数。 -### 事件附加示例 +### 代码示例 ```java import static org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisorSupport.events; @@ -563,8 +575,9 @@ public class Order { ### 工厂方法模式 在聚合根中定义静态方法。优势:简单方便 -```java +#### 代码示例 +```java // 代码省略... @Builder public class Order { @@ -592,6 +605,16 @@ public class Order { 实现 [AggregateFactory](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/AggregateFactory.java)接口。并标记`@Service`注入`Spring IoC`容器。 +#### 辅助代码生成 +方式一:数据库注解,在聚合根对应的表注释中使用注解声明聚合工厂 +```text +@Fac; +``` +方式二:gen-design命令 +```text +mvn cap4j-ddd-codegen:gen-design -Ddesign=fac:order.Order:订单工厂 +``` +#### 代码示例 ```java package org.netcorepal.cap4j.ddd.example.domain.aggregates.order.factory; @@ -679,10 +702,74 @@ public class OrderFactory implements AggregateFactory { 实现规约接口[Specification](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/Specification.java) 并标记`@Service`注入`Spring IoC`容器,当对应聚合根持久化时会自动获取对应实体聚合类型的规约实例,并自动调用`specify`接口执行校验。 +#### 辅助代码生成 +方式一:数据库注解,在聚合根对应的表注释中使用注解声明实体规格约束 +```text +@Spe; +``` +方式二:gen-design命令 +```text +mvn cap4j-ddd-codegen:gen-design -Ddesign=spe:order.Order:订单规约 +``` +#### 代码示例 +```java +package org.netcorepal.cap4j.ddd.example.domain.aggregates.order.specs; + +import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.Order; +import org.netcorepal.cap4j.ddd.domain.aggregate.Specification; +import org.springframework.stereotype.Service; + +/** + * todo: 实体规格约束描述 + * + * @author cap4j-ddd-codegen + * @date 2024/09/09 + */ +@Service +public class OrderSpecification implements Specification { + @Override + public Result specify(Order entity) { + return Result.pass(); + } +} + +``` + + ## 领域服务 **代码位置规范**:`${basePackage}.domain.services` > `@DomainService`(name = "领域服务名称") > 领域服务需标记[@DomainService](../ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/annotation/DomainService.java)注解,并标记`@Service`注入`Spring IoC`容器。 +慎用领域服务,警惕导致领域模型贫血化。 + +#### 辅助代码生成 +方式一:数据库注解,在聚合根对应的表注释中使用注解声明实体规格约束 +```text +@Svc; +``` +方式二:gen-design命令 +```text +mvn cap4j-ddd-codegen:gen-design -Ddesign=svc:Some:领域服务说明 +``` +#### 代码示例 +```java +package org.netcorepal.cap4j.ddd.example.domain.services; + +import org.netcorepal.cap4j.ddd.domain.service.annotation.DomainService; +import org.springframework.stereotype.Service; + +/** + * todo: 领域服务描述 + * + * @author cap4j-ddd-codegen + * @date 2024/9/9 + */ +@DomainService +@Service +public class SomeDomainService { +} + +``` diff --git "a/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" index f47175b..f0bff23 100644 --- "a/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" +++ "b/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -1,8 +1,8 @@ # 应用层编码指南 **原则**:两件事情 -1. `CQS`:命令的定义与实现;查询的定义; -2. `事件`:领域事件的订阅;集成事件的发布与订阅; +1. `CQS`:命令的定义与实现;查询的定义;(流程编排式用例流程实现) +2. `事件`:领域事件的订阅;集成事件的发布与订阅;(发布/订阅式用例流程实现) ## 中介者 cap4j提供中介者模式来方便调用各个模型设计元素。 @@ -22,9 +22,13 @@ cap4j提供中介者模式来方便调用各个模型设计元素。 > > `请求中介者`: `Mediator`.`requests`().`send`(_request_); // 发出请求 -## 代码生成 -cap4j提供快速创建模型设计元素。 - +## 辅助代码生成 +cap4j插件cap4j-ddd-codegen提供快速创建模型设计元素框架代码。设计语法详见帮助:mvn cap4j-ddd-codegen:help。 +```text +mvn cap4j-ddd-codegen:gen-design -Ddesign=literalDesign1;[literalDesign2;][...][literalDesignN;] +mvn cap4j-ddd-codegen:gen-design -DdesignFile=/path/to/designFile +``` +依赖`cap4j-ddd-codegen:gen-design`,我们可以将更多的精力放到模型的面向对象设计上。 ## 命令 **代码位置规范**:`${basePackage}.application.commands` @@ -35,19 +39,39 @@ cap4j提供快速创建模型设计元素。 特别需慎用`领域服务中介者`,警惕领域模型贫血化问题。 -### -`Mediator`.`repositories`() - +### 辅助代码生成 +gen-design命令 +```text +mvn cap4j-ddd-codegen:gen-design -Ddesign=cmd:{命令名称}:{命令说明} +``` +### 代码示例 +命令参数类 +```java +package org.netcorepal.cap4j.ddd.example.application.commands.order; -## 查询 -基于CQS模式,查询可独立实现。不必依赖领域模型(领域层)实现,可直接依据DI原则,由适配层实现即可。 +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.netcorepal.cap4j.ddd.application.RequestParam; -应用层仅需提供查询(Query)出入参(Request\Response)声明定义。 +/** + * PlaceOrderCmd命令请求参数 + * todo: 命令描述 + * + * @author cap4j-ddd-codegen + * @date 2024/09/14 + */ +@Data +@Builder +public class PlaceOrderCmdRequest implements RequestParam { -### 查询响应类 +} +``` +命令响应类 ```java -package org.netcorepal.cap4j.ddd.example.application.queries.order; +package org.netcorepal.cap4j.ddd.example.application.commands.order; import lombok.AllArgsConstructor; import lombok.Builder; @@ -55,22 +79,66 @@ import lombok.Data; import lombok.NoArgsConstructor; /** - * todo: 查询响应描述 + * PlaceOrderCmd命令响应 + * todo: 命令描述 * * @author cap4j-ddd-codegen - * @date 2024/09/09 + * @date 2024/09/14 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class GetOrderQryResponse { - Long id; +public class PlaceOrderCmdResponse { + boolean success; +} + +``` +命令处理类 +```java +package org.netcorepal.cap4j.ddd.example.application.commands.order; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.netcorepal.cap4j.ddd.Mediator; +import org.netcorepal.cap4j.ddd.application.command.Command; +import org.springframework.stereotype.Service; + +/** + * PlaceOrderCmd命令请求实现 + * todo: 命令描述 + * + * @author cap4j-ddd-codegen + * @date 2024/09/14 + */ +@Service +@RequiredArgsConstructor +@Slf4j +public class PlaceOrderCmdHandler implements Command { + + @Override + public PlaceOrderCmdResponse exec(PlaceOrderCmdRequest cmd) { + Mediator.uow().save(); + + return null; + } } ``` -### 查询参数类 +## 查询 +基于CQS模式,查询可独立实现。不必依赖领域模型(领域层)实现,可直接依据DI原则,由适配层实现即可。 + +应用层仅需提供查询(Query)出入参(Request\Response)声明定义。 + +### 辅助代码生成 +gen-design命令 +```text +mvn cap4j-ddd-codegen:gen-design -Ddesign=qry:{查询名称}:{查询说明} +``` + +### 代码示例 +查询参数类 ```java package org.netcorepal.cap4j.ddd.example.application.queries.order; @@ -91,16 +159,76 @@ import org.netcorepal.cap4j.ddd.application.RequestParam; public class GetOrderQryRequest implements RequestParam { Long id; } +``` +查询响应类 +```java +package org.netcorepal.cap4j.ddd.example.application.queries.order; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * todo: 查询响应描述 + * + * @author cap4j-ddd-codegen + * @date 2024/09/09 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GetOrderQryResponse { + Long id; +} + ``` ## 集成事件声明 **代码位置规范**:`${basePackage}.application.distributed.events` 仅需标记@IntegrationEvent 注解,即可定义集成事件。 + +### 辅助代码生成 +gen-design命令 +```text +mvn cap4j-ddd-codegen:gen-design -Ddesign=ie:{集成事件名称}[:mq-topic[:mq-consumer[:集成事件说明]]] +``` + +### 代码示例 ```java -@IntegrationEvent(value = "some-integration-event-topic", subscriber = "${spring.application.name}") -public class SomeIntegrationEvent { - // 省略消息体字段定义... +package org.netcorepal.cap4j.ddd.example.application.distributed.events; + +import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.events.OrderClosedDomainEvent; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRelease; +import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; + +/** + * todo: 集成事件描述 + * + * @author cap4j-ddd-codegen + * @date 2024/09/09 + */ +@IntegrationEvent(value = "cap4j-example-order-placed", subscriber = "${spring.application.name}") +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class OrderPlacedIntegrationEvent implements Converter { + private Long id; + + @Override + public OrderPlacedIntegrationEvent convert(OrderPlacedDomainEvent source) { + return OrderPlacedIntegrationEvent.builder() + .id(source.getId()) + //... + .build(); + } } ``` @@ -110,10 +238,31 @@ public class SomeIntegrationEvent { **代码位置规范**:`${basePackage}.application.commands`包中的命令实现逻辑中。 +#### 代码示例 ```java -Mediator.events().attach(SomeIntegrationEvent.builder() - //... - .build()); +package org.netcorepal.cap4j.ddd.example.application.subscribers.domain; + +import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.events.OrderPlacedDomainEvent; +import lombok.RequiredArgsConstructor; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +/** + * todo: 领域事件描述 + */ +@Service +@RequiredArgsConstructor +public class OrderPlacedDomainEventSubscriber { + + @EventListener(OrderPlacedDomainEvent.class) + public void on(OrderPlacedDomainEvent event) { + Mediator.events().attach(OrderPlacedIntegrationEvent.builder() + .id(event.getId()) + //... + .build()); + } + +} ``` @@ -121,6 +270,7 @@ Mediator.events().attach(SomeIntegrationEvent.builder() 返回[IntegrationEventSupervisor](ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/event/IntegrationEventSupervisor.java)接口。 ### 声明式发布集成事件 集成事件声明上标记`@AutoRelease`注解。cap4j将会利用反射技术关联到对应领域事件的发布并自动触发集成事件的发布。 +#### 代码示例 ```java package org.netcorepal.cap4j.ddd.example.application.distributed.events; @@ -139,23 +289,22 @@ import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; * @date 2024/09/09 */ @IntegrationEvent(value = "cap4j-example-order-placed", subscriber = "${spring.application.name}") -@AutoRelease(sourceDomainEventClass = OrderClosedDomainEvent.class) +@AutoRelease(sourceDomainEventClass = OrderPlacedDomainEvent.class) @Data @Builder @AllArgsConstructor @NoArgsConstructor -public class OrderClosedIntegrationEvent implements Converter { +public class OrderPlacedIntegrationEvent implements Converter { private Long id; @Override - public OrderClosedIntegrationEvent convert(OrderClosedDomainEvent source) { - return OrderClosedIntegrationEvent.builder() + public OrderPlacedIntegrationEvent convert(OrderPlacedDomainEvent source) { + return OrderPlacedIntegrationEvent.builder() .id(source.getId()) //... .build(); } } - ``` ## 集成事件订阅 @@ -164,6 +313,36 @@ public class OrderClosedIntegrationEvent implements Converter 生成【领域事件】代码时,默认会生成【领域事件订阅】代码。所以一般在设计时领域事件订阅代码已经生成过。 ## SAGA 待实现... From 1f8df92976c82eec7f1755d79a4bed7b16dc9294 Mon Sep 17 00:00:00 2001 From: binking338 Date: Sat, 14 Sep 2024 09:25:28 +0800 Subject: [PATCH 32/62] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenDesignMojo.java | 10 +++- ...26\347\240\201\346\214\207\345\215\227.md" | 8 +-- ...26\347\240\201\346\214\207\345\215\227.md" | 53 ++++++++++++++----- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java index ddb69d5..422e3a7 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java @@ -399,11 +399,17 @@ public void renderAppLayerIntegrationEvent(String literalType, String literalInt context.put("event", context.get("integration_event")); context.put("ie", context.get("integration_event")); context.put("i_e", context.get("integration_event")); - context.put("MQ_TOPIC", context.containsKey("Val1") ? ("\"" + context.get("Val1") + "\"") : ("\"" + context.get("Val0") + "\"")); + if(context.containsKey("Val1")) { + context.put("MQ_TOPIC", ("\"" + context.get("Val1") + "\"")); + } else { + context.put("MQ_TOPIC", ("\"" + context.get("Val0") + "\"")); + } if (Objects.equals(literalType, "integration_event_handler") && !context.containsKey("Val2")) { context.put("MQ_CONSUMER", "IntegrationEvent.NONE_SUBSCRIBER"); + } else if(context.containsKey("Val2")) { + context.put("MQ_CONSUMER", ("\"" + context.get("Val2") + "\"")); } else { - context.put("MQ_CONSUMER", context.containsKey("Val2") ? ("\"" + context.get("Val2") + "\"") : "\"${spring.application.name}\""); + context.put("MQ_CONSUMER", "\"${spring.application.name}\""); } context.put("Comment", context.containsKey("Val3") ? context.get("Val3") : "todo: 集成事件描述"); diff --git "a/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" index 1fa5df9..31cd20c 100644 --- "a/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" +++ "b/doc/01_\351\242\206\345\237\237\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -461,7 +461,7 @@ cap4j的领域事件发布需配合`UnitOfWork`模式实现,因为领域事件 @DE=OrderPlaced; ``` 方式二:gen-design命令 -```text +```shell mvn cap4j-ddd-codegen:gen-design -Ddesign=de:order.Order:OrderPlaced:下单领域事件 ``` @@ -611,7 +611,7 @@ public class Order { @Fac; ``` 方式二:gen-design命令 -```text +```shell mvn cap4j-ddd-codegen:gen-design -Ddesign=fac:order.Order:订单工厂 ``` #### 代码示例 @@ -708,7 +708,7 @@ public class OrderFactory implements AggregateFactory { @Spe; ``` 方式二:gen-design命令 -```text +```shell mvn cap4j-ddd-codegen:gen-design -Ddesign=spe:order.Order:订单规约 ``` #### 代码示例 @@ -751,7 +751,7 @@ public class OrderSpecification implements Specification { @Svc; ``` 方式二:gen-design命令 -```text +```shell mvn cap4j-ddd-codegen:gen-design -Ddesign=svc:Some:领域服务说明 ``` #### 代码示例 diff --git "a/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" index f0bff23..6800906 100644 --- "a/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" +++ "b/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -4,6 +4,29 @@ 1. `CQS`:命令的定义与实现;查询的定义;(流程编排式用例流程实现) 2. `事件`:领域事件的订阅;集成事件的发布与订阅;(发布/订阅式用例流程实现) +## 辅助代码生成 +cap4j插件cap4j-ddd-codegen提供快速创建模型设计元素框架代码。设计元素支持如下: +> 应用层:查询 +> 应用层:命令 +> 应用层:防腐端 +> 应用层:集成事件 +> 应用层:集成事件订阅 +> 领域层:领域服务(警惕滥用导致领域模型贫血化) +> 领域层:聚合工厂 +> 领域层:领域事件&订阅 +> 领域层:实体规格约束 +> +设计语法详见帮助:mvn cap4j-ddd-codegen:help。后续根据需要,cap4j也会采纳并支持更多好的设计元素辅助代码生成。 +```shell +mvn cap4j-ddd-codegen:gen-design -Ddesign=literalDesign1;[literalDesign2;][...][literalDesignN;] +mvn cap4j-ddd-codegen:gen-design -DdesignFile=/path/to/designFile +``` +依赖`cap4j-ddd-codegen:gen-design`,我们可以将更多的精力放到模型的面向对象设计上。 +### 设计示例 +```text +待补充 +``` + ## 中介者 cap4j提供中介者模式来方便调用各个模型设计元素。 > `聚合工厂中介者`: `Mediator`.`factories`().`create`(_entityPayload_); // 创建聚合根实例 @@ -22,14 +45,6 @@ cap4j提供中介者模式来方便调用各个模型设计元素。 > > `请求中介者`: `Mediator`.`requests`().`send`(_request_); // 发出请求 -## 辅助代码生成 -cap4j插件cap4j-ddd-codegen提供快速创建模型设计元素框架代码。设计语法详见帮助:mvn cap4j-ddd-codegen:help。 -```text -mvn cap4j-ddd-codegen:gen-design -Ddesign=literalDesign1;[literalDesign2;][...][literalDesignN;] -mvn cap4j-ddd-codegen:gen-design -DdesignFile=/path/to/designFile -``` -依赖`cap4j-ddd-codegen:gen-design`,我们可以将更多的精力放到模型的面向对象设计上。 - ## 命令 **代码位置规范**:`${basePackage}.application.commands` @@ -41,11 +56,14 @@ mvn cap4j-ddd-codegen:gen-design -DdesignFile=/path/to/designFile ### 辅助代码生成 gen-design命令 -```text +```shell mvn cap4j-ddd-codegen:gen-design -Ddesign=cmd:{命令名称}:{命令说明} ``` ### 代码示例 +```shell +mvn cap4j-ddd-codegen:gen-design -Ddesign=cmd:order.PlaceOrder +``` 命令参数类 ```java package org.netcorepal.cap4j.ddd.example.application.commands.order; @@ -133,11 +151,14 @@ public class PlaceOrderCmdHandler implements Command 生成【领域事件】代码时,默认会生成【领域事件订阅】代码。所以一般在设计时领域事件订阅代码已经生成过。 From b2c55b84c2cb2a191e4e0dd432376288ba4e2962 Mon Sep 17 00:00:00 2001 From: binking338 Date: Sat, 14 Sep 2024 09:49:25 +0800 Subject: [PATCH 33/62] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...26\347\240\201\346\214\207\345\215\227.md" | 93 ++++++++++++++++++- ...26\347\240\201\346\214\207\345\215\227.md" | 12 ++- 2 files changed, 99 insertions(+), 6 deletions(-) diff --git "a/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" index 6800906..065267d 100644 --- "a/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" +++ "b/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -55,9 +55,8 @@ cap4j提供中介者模式来方便调用各个模型设计元素。 特别需慎用`领域服务中介者`,警惕领域模型贫血化问题。 ### 辅助代码生成 -gen-design命令 ```shell -mvn cap4j-ddd-codegen:gen-design -Ddesign=cmd:{命令名称}:{命令说明} +mvn cap4j-ddd-codegen:gen-design -Ddesign=cmd:{命令名称}[:{命令说明}] ``` ### 代码示例 @@ -152,7 +151,7 @@ public class PlaceOrderCmdHandler implements Command { + Long id; +} + +``` +响应类 +```java +package org.netcorepal.cap4j.ddd.example.application.distributed.clients; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * todo: 防腐端响应描述 + * + * @author cap4j-ddd-codegen + * @date 2024/9/9 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserCliResponse { + Long id; +} + +``` +请求处理类 +```java +package org.netcorepal.cap4j.ddd.example.adapter.application.distributed.clients; + +import org.netcorepal.cap4j.ddd.example.application.distributed.clients.UserCliRequest; +import org.netcorepal.cap4j.ddd.example.application.distributed.clients.UserCliResponse; +import org.netcorepal.cap4j.ddd.application.RequestHandler; +import org.springframework.stereotype.Service; + +/** + * todo: 防腐端描述 + * + * @author cap4j-ddd-codegen + * @date 2024/9/9 + */ +@Service +public class UserCliHandler implements RequestHandler { + @Override + public UserCliResponse exec(UserCliRequest userCliRequest) { + return null; + } +} + +``` + ## 集成事件声明 **代码位置规范**:`${basePackage}.application.distributed.events` 仅需标记@IntegrationEvent 注解,即可定义集成事件。 ### 辅助代码生成 -gen-design命令 ```shell mvn cap4j-ddd-codegen:gen-design -Ddesign=ie:{集成事件名称}[:mq-topic[:mq-consumer[:集成事件说明]]] ``` diff --git "a/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" index bb90d19..94a5f7d 100644 --- "a/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" +++ "b/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -7,12 +7,20 @@ ## 查询适配实现 **代码位置规范**:`${basePackage}.adapter.application.queries` -待补充... +### 代码示例 +代码由插件生成 +```java + +``` ## 防腐端适配实现 **代码位置规范**:`${basePackage}.adapter.application.queries` -待补充... +### 代码示例 +代码由插件生成 +```java + +``` ## MVC接口 **代码位置规范**:`${basePackage}.adapter.application.portal.api` From 36ad7f26ebe4193673f5dcfe35cc6c0fd145bb43 Mon Sep 17 00:00:00 2001 From: binking338 Date: Sat, 14 Sep 2024 22:02:33 +0800 Subject: [PATCH 34/62] =?UTF-8?q?literalDesign=E6=94=AF=E6=8C=81=E8=BD=AC?= =?UTF-8?q?=E4=B9=89=E7=AC=A6'\'=EF=BC=8C','=E3=80=81';'=E3=80=81':'?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E4=BD=BF=E7=94=A8'\,'=E3=80=81'\;'=E3=80=81'?= =?UTF-8?q?\:'=E6=94=AF=E6=8C=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenDesignMojo.java | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java index 422e3a7..b7180e8 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java @@ -25,7 +25,7 @@ */ @Mojo(name = "gen-design") public class GenDesignMojo extends GenArchMojo { - public final String DESIGN_PARAMS_SPLITTER = ":"; + public final String DESIGN_PARAMS_SPLITTER = "[\\:]"; @Override public void execute() throws MojoExecutionException, MojoFailureException { @@ -153,10 +153,16 @@ public Map> resolveLiteralDesign(String design) { if (StringUtils.isBlank(design)) { return new HashMap<>(); } - return Arrays.stream(design.split(PATTERN_SPLITTER)) + return Arrays.stream(escape(design).split(PATTERN_SPLITTER)) .map(item -> TextUtils.splitWithTrim(item, DESIGN_PARAMS_SPLITTER, 2)) .filter(item -> item.length == 2) - .collect(Collectors.groupingBy(g -> alias4Design(g[0]), Collectors.mapping(g -> g[1].trim(), Collectors.toSet()))); + .collect(Collectors.groupingBy( + g -> alias4Design(g[0]), + Collectors.mapping( + g -> g[1].trim(), + Collectors.toSet() + ) + )); } /** @@ -399,14 +405,14 @@ public void renderAppLayerIntegrationEvent(String literalType, String literalInt context.put("event", context.get("integration_event")); context.put("ie", context.get("integration_event")); context.put("i_e", context.get("integration_event")); - if(context.containsKey("Val1")) { + if (context.containsKey("Val1")) { context.put("MQ_TOPIC", ("\"" + context.get("Val1") + "\"")); } else { context.put("MQ_TOPIC", ("\"" + context.get("Val0") + "\"")); } if (Objects.equals(literalType, "integration_event_handler") && !context.containsKey("Val2")) { context.put("MQ_CONSUMER", "IntegrationEvent.NONE_SUBSCRIBER"); - } else if(context.containsKey("Val2")) { + } else if (context.containsKey("Val2")) { context.put("MQ_CONSUMER", ("\"" + context.get("Val2") + "\"")); } else { context.put("MQ_CONSUMER", "\"${spring.application.name}\""); @@ -561,8 +567,16 @@ public void renderGenericDesign(String literalGenericDeclaration, String parentP getLog().info("生成自定义元素代码:" + path); } - public String internalRenderGenericDesign(String literalGenericDeclaration, String parentPath, TemplateNode templateNode, Function, Map> contextBuilder) throws IOException { - String[] segments = TextUtils.splitWithTrim(literalGenericDeclaration, DESIGN_PARAMS_SPLITTER); + public String internalRenderGenericDesign( + String literalGenericDeclaration, + String parentPath, + TemplateNode templateNode, + Function, Map> contextBuilder + ) throws IOException { + String[] segments = TextUtils.splitWithTrim(escape(literalGenericDeclaration), DESIGN_PARAMS_SPLITTER); + for (int i = 0; i < segments.length; i++) { + segments[i] = unescape(segments[i]); + } Map context = new HashMap<>(getEscapeContext()); for (int i = 0; i < segments.length; i++) { context.put("Val" + i, segments[i]); @@ -585,4 +599,20 @@ public String internalRenderGenericDesign(String literalGenericDeclaration, Stri return render(pathNode, parentPath, false); } + public String escape(String content) { + return content + .replace("\\\\", "${symbol_escape}") + .replace("\\:", "${symbol_colon}") + .replace("\\,", "${symbol_comma}") + .replace("\\;", "${symbol_semicolon}"); + } + + public String unescape(String escape) { + return escape + .replace("${symbol_escape}", "\\") + .replace("${symbol_colon}", ":") + .replace("${symbol_comma}", ",") + .replace("${symbol_semicolon}", ";"); + } + } From 0266336aef58e3070ae99c47b0d7fa22b087971a Mon Sep 17 00:00:00 2001 From: binking338 Date: Sun, 15 Sep 2024 00:00:34 +0800 Subject: [PATCH 35/62] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E4=B8=8D=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cap4j-ddd-codegen-template.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index f8ceb01..392f5c6 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -137,13 +137,13 @@ "type": "file", "name": "${Client}Request.java", "format": "raw", - "data": "package ${basePackage}.application.distributed.clients;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n/**\n * ${Client}防腐端请求参数\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Client}Request implements RequestParam<${Client}Response> {\n Long id;\n}\n" + "data": "package ${basePackage}.application.distributed.clients${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n/**\n * ${Client}防腐端请求参数\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Client}Request implements RequestParam<${Client}Response> {\n Long id;\n}\n" }, { "type": "file", "name": "${Client}Response.java", "format": "raw", - "data": "package ${basePackage}.application.distributed.clients;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * ${Client}防腐端响应\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Client}Response {\n Long id;\n}\n" + "data": "package ${basePackage}.application.distributed.clients${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * ${Client}防腐端响应\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Client}Response {\n Long id;\n}\n" } ] }, @@ -355,7 +355,7 @@ "type": "file", "name": "MyIntegrationEventInterceptor.java", "format": "raw", - "data": "package ${basePackage}.adapter.application._share.configure;\n\nimport org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor;\nimport org.netcorepal.cap4j.ddd.domain.event.EventRecord;\nimport org.springframework.stereotype.Service;\n\nimport java.time.LocalDateTime;\n\n/**\n * 集成事件拦截器\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\npublic class MyIntegrationEventInterceptor implements IntegrationEventInterceptor {\n @Override\n public void onNotify(Object eventPayload, LocalDateTime schedule) {\n\n }\n\n @Override\n public void prePersist(EventRecord event) {\n\n }\n\n @Override\n public void postPersist(EventRecord event) {\n\n }\n\n @Override\n public void preRelease(EventRecord event) {\n\n }\n\n @Override\n public void postRelease(EventRecord event) {\n\n }\n\n @Override\n public void onException(Throwable throwable, EventRecord event) {\n\n }\n}\n" + "data": "package ${basePackage}.adapter.application._share.configure;\n\nimport org.netcorepal.cap4j.ddd.application.event.IntegrationEventInterceptor;\nimport org.netcorepal.cap4j.ddd.domain.event.EventRecord;\nimport org.springframework.stereotype.Service;\n\nimport java.time.LocalDateTime;\n\n/**\n * 集成事件拦截器\n *\n * @author cap4j-ddd-codegen\n * @date 2024/09/14\n */\n@Service\npublic class MyIntegrationEventInterceptor implements IntegrationEventInterceptor {\n\n @Override\n public void prePersist(EventRecord event) {\n\n }\n\n @Override\n public void postPersist(EventRecord event) {\n\n }\n\n @Override\n public void preRelease(EventRecord event) {\n\n }\n\n @Override\n public void postRelease(EventRecord event) {\n\n }\n\n @Override\n public void onException(Throwable throwable, EventRecord event) {\n\n }\n\n @Override\n public void onAttach(Object eventPayload, LocalDateTime schedule) {\n\n }\n\n @Override\n public void onDetach(Object eventPayload) {\n\n }\n}\n" } ] } From 6821ccfb8c99abd8d5763f6506930c151c79e4af Mon Sep 17 00:00:00 2001 From: binking338 Date: Sun, 15 Sep 2024 00:04:03 +0800 Subject: [PATCH 36/62] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20design=20designFile?= =?UTF-8?q?=20=E4=B9=8B=E9=97=B4=E4=BD=BF=E7=94=A8=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E7=AC=A6=E4=B8=B2=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenDesignMojo.java | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java index b7180e8..1d6e6a9 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java @@ -153,7 +153,7 @@ public Map> resolveLiteralDesign(String design) { if (StringUtils.isBlank(design)) { return new HashMap<>(); } - return Arrays.stream(escape(design).split(PATTERN_SPLITTER)) + return Arrays.stream(escape(design).replaceAll("\\r\\n|\\r|\\n", ";").split(PATTERN_SPLITTER)) .map(item -> TextUtils.splitWithTrim(item, DESIGN_PARAMS_SPLITTER, 2)) .filter(item -> item.length == 2) .collect(Collectors.groupingBy( @@ -179,7 +179,7 @@ public void renderDesign(List templateNodes, String parentPath) th design += this.design; } if (StringUtils.isNotBlank(this.designFile) && FileUtils.fileExists(this.designFile)) { - design += (DESIGN_PARAMS_SPLITTER + FileUtils.fileRead(this.designFile, this.archTemplateEncoding)); + design += (";" + FileUtils.fileRead(this.designFile, this.archTemplateEncoding)); } Map> designMap = resolveLiteralDesign(design); @@ -406,19 +406,26 @@ public void renderAppLayerIntegrationEvent(String literalType, String literalInt context.put("ie", context.get("integration_event")); context.put("i_e", context.get("integration_event")); if (context.containsKey("Val1")) { - context.put("MQ_TOPIC", ("\"" + context.get("Val1") + "\"")); + context.put("MQ_TOPIC", StringUtils.isBlank(context.get("Val1")) + ? ("\"" + context.get("Val0") + "\"") + : ("\"" + context.get("Val1") + "\"")); } else { context.put("MQ_TOPIC", ("\"" + context.get("Val0") + "\"")); } - if (Objects.equals(literalType, "integration_event_handler") && !context.containsKey("Val2")) { + if (Objects.equals(literalType, "integration_event")) { context.put("MQ_CONSUMER", "IntegrationEvent.NONE_SUBSCRIBER"); - } else if (context.containsKey("Val2")) { - context.put("MQ_CONSUMER", ("\"" + context.get("Val2") + "\"")); + context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 集成事件描述"); } else { - context.put("MQ_CONSUMER", "\"${spring.application.name}\""); + if (context.containsKey("Val2")) { + context.put("MQ_CONSUMER", StringUtils.isBlank(context.get("Val2")) + ? "\"${spring.application.name}\"" + : ("\"" + context.get("Val2") + "\"") + ); + } else { + context.put("MQ_CONSUMER", "\"${spring.application.name}\""); + } + context.put("Comment", context.containsKey("Val3") ? context.get("Val3") : "todo: 集成事件描述"); } - - context.put("Comment", context.containsKey("Val3") ? context.get("Val3") : "todo: 集成事件描述"); context.put("comment", context.get("Comment")); return context; }); From ce684bbcd046c7f734841fb54d821629772b8f6d Mon Sep 17 00:00:00 2001 From: binking338 Date: Sun, 15 Sep 2024 00:04:17 +0800 Subject: [PATCH 37/62] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B8=AE=E5=8A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/HelpMojo.java | 3 +- ...26\347\240\201\346\214\207\345\215\227.md" | 47 ++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java index fa7e354..4f9d533 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java @@ -221,6 +221,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("mvn cap4j-ddd-codegen:gen-design -Ddesign=literalDesign1;[literalDesign2;][...][literalDesignN;]"); getLog().info("mvn cap4j-ddd-codegen:gen-design -DdesignFile=/path/to/designFile"); getLog().info("literalDesignN; 支持如下设计元素:"); + getLog().info("(可使用转义符'\\'对 ';' ':' 进行转义)"); getLog().info("--------------------------------------------------"); getLog().info("cmd:{命令名称}[:命令说明]"); getLog().info("功能:定义【应用层】设计元素【命令】,CQS中的C"); @@ -231,7 +232,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("cli:{防腐端名称}[:防腐端说明]"); getLog().info("功能:定义【应用层】设计元素【防腐端】,作为外部服务接口防腐层,ACL"); getLog().info("--------------------------------------------------"); - getLog().info("ie:{集成事件名称}[:mq-topic[:mq-consumer[:集成事件说明]]]"); + getLog().info("ie:{集成事件名称}[:mq-topic[:集成事件说明]]"); getLog().info("功能:定义【应用层】设计元素【集成事件】,Integration Event"); getLog().info("--------------------------------------------------"); getLog().info("ies:{集成事件名称}[:mq-topic[:mq-consumer[:集成事件说明]]]"); diff --git "a/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" index 94a5f7d..876f076 100644 --- "a/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" +++ "b/doc/03_\351\200\202\351\205\215\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -10,15 +10,60 @@ ### 代码示例 代码由插件生成 ```java +package org.netcorepal.cap4j.ddd.example.adapter.application.queries; + +import org.netcorepal.cap4j.ddd.example.application.queries.order.GetOrderQryRequest; +import org.netcorepal.cap4j.ddd.example.application.queries.order.GetOrderQryResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.netcorepal.cap4j.ddd.application.query.Query; +import org.springframework.stereotype.Service; + +/** + * todo: 查询处理器描述 + * + * @author cap4j-ddd-codegen + * @date 2024/09/09 + */ +@Service +@RequiredArgsConstructor +@Slf4j +public class GetOrderQryHandler implements Query { + + @Override + public GetOrderQryResponse exec(GetOrderQryRequest request) { + return null; + } +} ``` ## 防腐端适配实现 -**代码位置规范**:`${basePackage}.adapter.application.queries` +**代码位置规范**:`${basePackage}.adapter.application.clients` ### 代码示例 代码由插件生成 ```java +package org.netcorepal.cap4j.ddd.example.adapter.application.distributed.clients; + +import org.netcorepal.cap4j.ddd.example.application.distributed.clients.UserCliRequest; +import org.netcorepal.cap4j.ddd.example.application.distributed.clients.UserCliResponse; +import org.netcorepal.cap4j.ddd.application.RequestHandler; +import org.springframework.stereotype.Service; + +/** + * todo: 防腐端描述 + * + * @author binking338 + * @date 2024/9/9 + */ +@Service +public class UserCliHandler implements RequestHandler { + @Override + public UserCliResponse exec(UserCliRequest userCliRequest) { + return null; + } +} ``` From fbf013d195761a2d1731b2fa046acdd21c18093a Mon Sep 17 00:00:00 2001 From: binking338 Date: Tue, 17 Sep 2024 23:33:34 +0800 Subject: [PATCH 38/62] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=80=BC=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenArchMojo.java | 1 - .../cap4j/ddd/codegen/GenEntityMojo.java | 39 ++++++++++++----- .../cap4j/ddd/codegen/GenRepositoryMojo.java | 43 ++++++++++++------- .../cap4j/ddd/codegen/HelpMojo.java | 2 + .../cap4j/ddd/codegen/MyAbstractMojo.java | 16 +++---- .../ddd/codegen/misc/SqlSchemaUtils.java | 23 +++++++++- .../aggregate/annotation/Aggregate.java | 10 ++++- .../repo/Md5HashIdentifierGenerator.java | 26 +++++++++++ 8 files changed, 120 insertions(+), 40 deletions(-) create mode 100644 ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Md5HashIdentifierGenerator.java diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index daf073b..19968c4 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -259,7 +259,6 @@ public Map getEscapeContext() { context.put("generateBuild", generateBuild ? "true" : "false"); context.put("aggregateRootAnnotation", aggregateRootAnnotation); context.put("aggregateRepositoryBaseClass", aggregateRepositoryBaseClass); - context.put("aggregateIdentityClass", aggregateIdentityClass); context.put("aggregateRepositoryCustomerCode", aggregateRepositoryCustomerCode); context.put("ignoreAggregateRoots", ignoreAggregateRoots); context.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index d2b8c8e..f8477d8 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -62,7 +62,6 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("忽略字段:" + ignoreFields); getLog().info(""); getLog().info("主键ID生成器:" + idGenerator); - getLog().info("聚合根主键映射Java类型:" + aggregateIdentityClass); getLog().info("日期类型映射Java包:" + datePackage4Java); getLog().info("枚举值Java字段名称:" + enumValueField); getLog().info("枚举名Java字段名称:" + enumNameField); @@ -597,11 +596,14 @@ public void processAnnotationLines(Map table, List 0); - } else { - SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + getEntityJavaType(tableName) + "\", type = Aggregate.TYPE_ENTITY, relevant = { \"" + getEntityJavaType(getParent(table)) + "\" }, description = \"" + getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\")", (list, line) -> 0); - } + SourceFileUtils.addIfNone(annotationLines, "@Aggregate\\(.*\\)", "@Aggregate(" + + "aggregate = \"" + getAggregateWithModule(tableName) + "\", " + + "name = \"" + getEntityJavaType(tableName) + "\", " + + "root = " + (isAggregateRoot(table) ? "true" : "false") + ", " + + "type = " + (isValueObject(table) ? "Aggregate.TYPE_VALUE_OBJECT" : "Aggregate.TYPE_ENTITY") + ", " + + (isAggregateRoot(table) ? "" : ("relevant = { \"" + getEntityJavaType(getParent(table)) + "\" }, ")) + + "description = \"" + getComment(table).replaceAll("[\\r\\n]", "\\\\n") + "\"" + + ")", (list, line) -> 0); if (StringUtils.isNotBlank(getAggregateRootAnnotation())) { if (isAggregateRoot(table)) { SourceFileUtils.addIfNone(annotationLines, getAggregateRootAnnotation() + "(\\(.*\\))?", getAggregateRootAnnotation()); @@ -735,6 +737,11 @@ public void writeEntitySourceFile(Map table, List getIdColumn(List> columns){ + return columns.stream().filter(column -> Objects.equals(idField, getColumnName(column))) + .findFirst().orElse(null); + } + private String generateEntityClassMainSource(Map table, List> columns, Map tablePackageMap, Map> relations, List enums, List annotationLines, List customerLines) throws IOException { String tableName = getTableName(table); String simpleClassName = getEntityJavaType(tableName); @@ -761,14 +768,24 @@ private String generateEntityClassMainSource(Map table, List column : columns) { writeColumnProperty(out, table, column, relations, enums); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java index 01d45cb..cf282f7 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenRepositoryMojo.java @@ -29,7 +29,6 @@ */ @Mojo(name = "gen-repository") public class GenRepositoryMojo extends MyAbstractMojo { - Map AggregateRoot2AggregateNameMap = new HashMap<>(); @Override @@ -99,12 +98,15 @@ public void execute() throws MojoExecutionException, MojoFailureException { this.getLog().info("发现聚合根: " + className); String simpleClassName = SourceFileUtils.resolveSimpleClassName(file.getAbsolutePath()); + + String identityClass = getIdType(content); + this.getLog().info("聚合根ID类型: " + identityClass); if (Arrays.stream(ignoreAggregateRoots.split("[\\,\\;]")) .anyMatch(i -> i.equalsIgnoreCase(simpleClassName))) { return; } try { - writeAggregateRepositorySourceFile(file.getAbsolutePath(), basePackage, adapterModulePath); + writeAggregateRepositorySourceFile(className, simpleClassName, identityClass, basePackage, adapterModulePath); } catch (IOException e) { e.printStackTrace(); } @@ -117,7 +119,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { this.getLog().info("结束生成仓储代码"); } - Pattern AGGREGATE_PATTERN = Pattern.compile("\\((aggregate\\s*=\\s*){1}\\\"(){1}\\\"\\)"); + static Pattern AGGREGATE_PATTERN = Pattern.compile("\\s*aggregate\\s*=\\s*\\\"([^\\\"]*)\\\""); private boolean isAggregateRoot(String content, String className) { return Arrays.stream(content.split("(\r)|(\n)|(\r\n)")) @@ -137,26 +139,38 @@ private boolean isAggregateRoot(String content, String className) { return hasAggregateRoot; } - boolean hasAggregate = - line.matches("@Aggregate\\s*\\(.*type\\s*=\\s*\\\"root\\\".*\\)") - || line.matches("@Aggregate\\s*\\(.*type\\s*=\\s*Aggregate.TYPE_ROOT.*\\)") - || line.matches("@Aggregate\\s*\\(.*type\\s*=\\s*TYPE_ROOT.*\\)"); - if (hasAggregate) { + boolean aggregateRoot = + line.matches("@Aggregate\\s*\\(.*root\\s*=\\s*true.*\\)"); + if (aggregateRoot) { Matcher matcher = AGGREGATE_PATTERN.matcher(line); - if (matcher.find() && matcher.groupCount() > 1) { - AggregateRoot2AggregateNameMap.put(className, matcher.group(2)); + if (matcher.find() && matcher.groupCount() == 1) { + AggregateRoot2AggregateNameMap.put(className, matcher.group(1)); } } - boolean aggregateRoot = hasAggregate; getLog().debug("annotationline: " + line); getLog().debug("hasAggregateRoot=" + hasAggregateRoot); - getLog().debug("hasAggregate=" + hasAggregate); + getLog().debug("aggregateRoot=" + aggregateRoot); return aggregateRoot; } ); } + private String getIdType(String content) { + Pattern ID_FIELD_PATTERN = Pattern.compile("^\\s*([_A-Za-z][_A-Za-z0-9]*)\\s*" + idField + "\\s*;$"); + String idFieldLine = Arrays.stream(content.split("(\r)|(\n)|(\r\n)")) + .filter(ID_FIELD_PATTERN.asPredicate()) + .findFirst() + .orElse(null); + if (null != idFieldLine) { + Matcher matcher = ID_FIELD_PATTERN.matcher(idFieldLine); + if (matcher.find() && matcher.groupCount() == 1) { + return matcher.group(1); + } + } + return "Long"; + } + /** * 返回能否从新生成实体 * @@ -231,11 +245,8 @@ public void renameAggregateRepositorySourceFiles(String basePackage, String base } } - public void writeAggregateRepositorySourceFile(String entitySourceFilePath, String basePackage, String baseDir) throws IOException { + public void writeAggregateRepositorySourceFile(String className, String simpleClassName, String aggregateIdentityClass, String basePackage, String baseDir) throws IOException { String packageName = basePackage + ".adapter.domain.repositories"; - - String simpleClassName = SourceFileUtils.resolveSimpleClassName(entitySourceFilePath); - String className = SourceFileUtils.resolveClassName(entitySourceFilePath); String aggregate = AggregateRoot2AggregateNameMap.getOrDefault(className, simpleClassName); new File(SourceFileUtils.resolveDirectory(baseDir, packageName)).mkdirs(); diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java index 4f9d533..0dbbdb2 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/HelpMojo.java @@ -80,6 +80,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { " EAGER\n" + " \n" + " \n" + + " \n" + + " \n" + " \n" + " code\n" + " \n" + diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java index 9f7198e..e1e72f7 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/MyAbstractMojo.java @@ -243,6 +243,14 @@ public List getEntityClassExtraImports() { */ @Parameter(property = "idGenerator", defaultValue = "") public String idGenerator = ""; + + /** + * 值对象主键生成器 默认md5哈希 + * + * @parameter expression="${idGenerator4ValueObject}" + */ + @Parameter(property = "idGenerator4ValueObject", defaultValue = "") + public String idGenerator4ValueObject = ""; /** * 枚举类型【值】字段名称 * @@ -310,14 +318,6 @@ public List getEntityClassExtraImports() { @Parameter(property = "generateBuild", defaultValue = "false") public Boolean generateBuild = false; - /** - * 聚合唯一标识类型 - * - * @parameter expression="${aggregateIdentityClass}" - */ - @Parameter(property = "aggregateIdentityClass", defaultValue = "Long") - public String aggregateIdentityClass = "Long"; - /** * 聚合根注解 * diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java index 6285704..44d018b 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java @@ -529,8 +529,27 @@ public static boolean isIgnore(Map tableOrColumn) { * @return */ public static boolean isAggregateRoot(Map table) { - return hasAnyAnnotation(table, Arrays.asList("AggregateRoot", "Root", "R")) - || !hasAnyAnnotation(table, Arrays.asList("Parent", "P")); + return !hasParent(table) || hasAnyAnnotation(table, Arrays.asList("AggregateRoot", "Root", "R")); + } + + /** + * 是否值对象 + * + * @param table + * @return + */ + public static boolean isValueObject(Map table) { + return hasAnyAnnotation(table, Arrays.asList("ValueObject", "VO")); + } + + /** + * 是否含聚合关系中的父表 + * + * @param table + * @return + */ + public static boolean hasParent(Map table) { + return hasAnyAnnotation(table, Arrays.asList("Parent", "P")); } /** diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java index 3e46994..c7a5948 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/annotation/Aggregate.java @@ -16,8 +16,8 @@ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Aggregate { - public static final String TYPE_ROOT = "root"; public static final String TYPE_ENTITY = "entity"; + public static final String TYPE_VALUE_OBJECT = "value-object"; public static final String TYPE_ENUM = "enum"; public static final String TYPE_REPOSITORY = "repository"; public static final String TYPE_DOMAIN_EVENT = "domain-event"; @@ -39,9 +39,15 @@ */ String name() default ""; + /** + * 是否聚合根 + * @return + */ + boolean root() default false; + /** * 元素类型 - * root、entity、domain-event、specification、repository + * entity、value-object、repository、factory、factory-payload、domain-event、specification、enum * * @return */ diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Md5HashIdentifierGenerator.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Md5HashIdentifierGenerator.java new file mode 100644 index 0000000..1a4ae38 --- /dev/null +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Md5HashIdentifierGenerator.java @@ -0,0 +1,26 @@ +package org.netcorepal.cap4j.ddd.domain.repo; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.id.IdentifierGenerator; +import org.springframework.util.DigestUtils; + +import java.io.Serializable; +import java.nio.charset.StandardCharsets; + +/** + * md5 + * + * @author binking338 + * @date 2024/9/17 + */ +public class Md5HashIdentifierGenerator implements IdentifierGenerator { + @Override + public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException { + String json = JSON.toJSONString(o, SerializerFeature.SortField); + String hash = DigestUtils.md5DigestAsHex(json.getBytes(StandardCharsets.UTF_8)); + return hash; + } +} From 2ce6932afab12ecdc9e6c418928a9e34ac2ec879 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 18 Sep 2024 10:07:46 +0800 Subject: [PATCH 39/62] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=80=BC=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenEntityMojo.java | 38 +++++++++++++------ .../repo/JpaRepositoryAutoConfiguration.java | 6 ++- .../ddd/domain/aggregate/ValueObject.java | 15 ++++++++ .../DefaultAggregateFactorySupervisor.java | 3 ++ .../cap4j/ddd/domain/repo/JpaPredicate.java | 15 +++++--- .../repo/Md5HashIdentifierGenerator.java | 13 ++++++- 6 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/ValueObject.java diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index f8477d8..ca31cf5 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -538,6 +538,13 @@ public void processImportLines(Map table, String basePackage, Li } List entityClassExtraImports = getEntityClassExtraImports(); + if (isValueObject(table)) { + if (entityClassExtraImports.indexOf("org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate") > 0) { + entityClassExtraImports.add(entityClassExtraImports.indexOf("org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate"), "org.netcorepal.cap4j.ddd.domain.aggregate.ValueObject"); + } else { + entityClassExtraImports.add("org.netcorepal.cap4j.ddd.domain.aggregate.ValueObject"); + } + } if (importEmpty) { boolean breakLine = false; for (String entityClassExtraImport : entityClassExtraImports) { @@ -737,7 +744,7 @@ public void writeEntitySourceFile(Map table, List getIdColumn(List> columns){ + private Map getIdColumn(List> columns) { return columns.stream().filter(column -> Objects.equals(idField, getColumnName(column))) .findFirst().orElse(null); } @@ -748,7 +755,7 @@ private String generateEntityClassMainSource(Map table, List writeLine(out, line)); - writeLine(out, "public class " + simpleClassName + (StringUtils.isNotBlank(entityBaseClass) ? " extends " + entityBaseClass : "") + " {"); + writeLine(out, "public class " + simpleClassName + (StringUtils.isNotBlank(entityBaseClass) ? " extends " + entityBaseClass : "") + (isValueObject(table) ? " implements ValueObject" : "") + " {"); if (customerLines.size() > 0) { customerLines.forEach(line -> writeLine(out, line)); } else { @@ -765,28 +772,37 @@ private String generateEntityClassMainSource(Map table, List column : columns) { writeColumnProperty(out, table, column, relations, enums); } diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java index d0fbada..4441b31 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaRepositoryAutoConfiguration.java @@ -51,10 +51,12 @@ public DefaultRepositorySupervisor defaultRepositorySupervisor( @Bean public DefaultAggregateFactorySupervisor defaultAggregateFactorySupervisor( - List> factories + List> factories, + JpaUnitOfWork jpaUnitOfWork ){ DefaultAggregateFactorySupervisor aggregateFactorySupervisor = new DefaultAggregateFactorySupervisor( - factories + factories, + jpaUnitOfWork ); aggregateFactorySupervisor.init(); AggregateFactorySupervisorSupport.configure(aggregateFactorySupervisor); diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/ValueObject.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/ValueObject.java new file mode 100644 index 0000000..c320e7c --- /dev/null +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/ValueObject.java @@ -0,0 +1,15 @@ +package org.netcorepal.cap4j.ddd.domain.aggregate; + +/** + * 值对象 + * + * @author binking338 + * @date 2024/9/18 + */ +public interface ValueObject { + /** + * 值对象哈希码 + * @return + */ + String hash(); +} diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java index 238554d..ec73781 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/aggregate/impl/DefaultAggregateFactorySupervisor.java @@ -1,6 +1,7 @@ package org.netcorepal.cap4j.ddd.domain.aggregate.impl; import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.application.UnitOfWork; import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory; import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactorySupervisor; import org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload; @@ -19,6 +20,7 @@ @RequiredArgsConstructor public class DefaultAggregateFactorySupervisor implements AggregateFactorySupervisor { private final List> factories; + private final UnitOfWork unitOfWork; private Map, AggregateFactory> factoryMap = null; @@ -51,6 +53,7 @@ public , ENTITY> ENTITY create(E return null; } ENTITY instance = ((AggregateFactory) factory).create(entityPayload); + unitOfWork.persist(instance); return instance; } } diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java index 9e00675..971a8b3 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/JpaPredicate.java @@ -1,11 +1,9 @@ package org.netcorepal.cap4j.ddd.domain.repo; import lombok.RequiredArgsConstructor; +import org.netcorepal.cap4j.ddd.domain.aggregate.ValueObject; import org.springframework.data.jpa.domain.Specification; -import java.util.Collection; -import java.util.List; - /** * Jpa仓储检索断言 * @@ -18,16 +16,21 @@ public class JpaPredicate implements Predicate { final Specification spec; final Object id; final Iterable ids; + final ValueObject valueObject; public static Predicate byId(Class entityClass, Object id) { - return new JpaPredicate<>(entityClass, null, id, null); + return new JpaPredicate<>(entityClass, null, id, null, null); } public static Predicate byIds(Class entityClass, Iterable ids) { - return new JpaPredicate<>(entityClass, null, null, ids); + return new JpaPredicate<>(entityClass, null, null, ids, null); + } + + public static Predicate byValueObject(VALUE_OBJECT valueObject){ + return new JpaPredicate<>((Class) valueObject.getClass(), null, valueObject.hash(), null, valueObject); } public static Predicate bySpecification(Class entityClass, Specification specification) { - return new JpaPredicate<>(entityClass, specification, null, null); + return new JpaPredicate<>(entityClass, specification, null, null, null); } } diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Md5HashIdentifierGenerator.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Md5HashIdentifierGenerator.java index 1a4ae38..eadce40 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Md5HashIdentifierGenerator.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/Md5HashIdentifierGenerator.java @@ -1,6 +1,7 @@ package org.netcorepal.cap4j.ddd.domain.repo; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -17,9 +18,19 @@ * @date 2024/9/17 */ public class Md5HashIdentifierGenerator implements IdentifierGenerator { + static Md5HashIdentifierGenerator instance = null; + public static Md5HashIdentifierGenerator getInstance(){ + if(instance == null){ + instance = new Md5HashIdentifierGenerator(); + } + return instance; + } + @Override public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException { - String json = JSON.toJSONString(o, SerializerFeature.SortField); + JSONObject jsonObject = (JSONObject) JSON.toJSON(o); + jsonObject.remove("id"); + String json = jsonObject.toString(SerializerFeature.SortField); String hash = DigestUtils.md5DigestAsHex(json.getBytes(StandardCharsets.UTF_8)); return hash; } From 7a1d152061661c8b71ed390ab59abc2c5712db34 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 18 Sep 2024 11:34:35 +0800 Subject: [PATCH 40/62] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=BA=9F=E5=BC=83?= =?UTF-8?q?=E5=8F=82=E6=95=B0aggregateIdentityClass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 - cap4j-ddd-codegen-template.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 710048c..50531f3 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,6 @@ Mediator中介者模式、 true true false - Long diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index 392f5c6..284dfe6 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -706,7 +706,7 @@ "name": "pom.xml", "format": "raw", "conflict": "overwrite", - "data": "\n 4.0.0\n\n ${groupId}\n ${artifactId}\n ${version}\n jar\n\n ddd-sample\n\n \n UTF-8\n UTF-8\n 1.8\n 1.0.0-alpha-2\n\n 1.18.16\n\n 31.1-jre\n 3.12.0\n 4.4\n 1.2.79\n\n 5.1.46\n 1.2.8\n\n 2.7.6\n 2021.0.5\n \n \n\n 1.6.14\n 3.0.3\n 2.2.7\n \n\n \n \n \n org.springframework.boot\n spring-boot-dependencies\n ${spring-boot.version}\n pom\n import\n \n \n org.springframework.cloud\n spring-cloud-dependencies\n ${spring-cloud.version}\n pom\n import\n \n \n \n\n \n \n org.projectlombok\n lombok\n true\n ${org.projectlombok.version}\n \n\n \n io.github.netcorepal\n cap4j-ddd-starter-jpa-rocketmq\n ${cap4j.version}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n provided\n \n\n \n \n org.springframework.boot\n spring-boot-starter-web\n \n \n org.springframework.boot\n spring-boot-starter-actuator\n \n \n org.springframework.boot\n spring-boot-starter-validation\n \n\n \n \n org.apache.commons\n commons-lang3\n ${commons.lang3.version}\n \n \n org.apache.commons\n commons-collections4\n ${commons.collections4.version}\n \n \n com.google.guava\n guava\n ${guava.version}\n \n \n com.alibaba\n fastjson\n ${fastjson.version}\n \n\n \n \n com.ctrip.framework.apollo\n apollo-client\n 1.7.0\n \n \n \n com.githup.liuyanggithup\n xxl-job-core\n 1.2.1\n \n \n \n org.springframework.cloud\n spring-cloud-starter-openfeign\n \n \n io.github.openfeign\n feign-okhttp\n 11.10\n \n\n \n \n org.springframework.boot\n spring-boot-starter-data-jpa\n \n \n \n org.mybatis.spring.boot\n mybatis-spring-boot-starter\n 2.1.3\n \n \n \n com.alibaba\n druid-spring-boot-starter\n ${spring-boot-druid.version}\n \n \n \n mysql\n mysql-connector-java\n ${mysql-connector.version}\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n org.springdoc\n springdoc-openapi-ui\n ${springdoc.version}\n \n \n com.github.xiaoymin\n knife4j-springdoc-ui\n ${knife4j.version}\n \n \n io.swagger.core.v3\n swagger-annotations\n ${swagger3.version}\n \n\n \n net.logstash.logback\n logstash-logback-encoder\n 6.6\n \n \n junit\n junit\n 3.8.1\n test\n \n \n \n ${project.artifactId}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n \n ${basePackage}\n ${archTemplate}\n ${archTemplateEncoding}\n ${designFile}\n ${multiModule}\n ${moduleNameSuffix4Adapter}\n ${moduleNameSuffix4Domain}\n ${moduleNameSuffix4Application}\n \n \n \n ${user}\n ${pwd}\n ${schema}\n ${table}
\n ${ignoreTable}\n ${ignoreFields}\n ${idField}\n ${versionField}\n ${deletedField}\n ${readonlyFields}\n ${entityBaseClass}\n ${entityClassExtraImports}\n ${entityMetaInfoClassOutputMode}\n ${entityMetaInfoClassOutputPackage}\n ${fetchMode}\n ${fetchType}\n ${idGenerator}\n ${enumValueField}\n ${enumNameField}\n ${enumUnmatchedThrowException}\n ${datePackage4Java}\n ${typeRemapping}\n ${generateDefault}\n ${generateDbType}\n ${generateSchema}\n ${generateBuild}\n ${aggregateIdentityClass}\n ${aggregateRootAnnotation}\n ${aggregateRepositoryBaseClass}\n ${aggregateRepositoryCustomerCode}\n ${ignoreAggregateRoots}\n
\n
\n \n org.springframework.boot\n spring-boot-maven-plugin\n 2.6.3\n \n \n \n \n repackage\n \n repackage\n \n \n ${basePackage}.StartApplication\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 1.8\n 1.8\n UTF-8\n \n \n \n org.apache.maven.plugins\n maven-archetype-plugin\n 3.2.0\n \n\n
\n
\n
" + "data": "\n 4.0.0\n\n ${groupId}\n ${artifactId}\n ${version}\n jar\n\n ddd-sample\n\n \n UTF-8\n UTF-8\n 1.8\n 1.0.0-alpha-2\n\n 1.18.16\n\n 31.1-jre\n 3.12.0\n 4.4\n 1.2.79\n\n 5.1.46\n 1.2.8\n\n 2.7.6\n 2021.0.5\n \n \n\n 1.6.14\n 3.0.3\n 2.2.7\n \n\n \n \n \n org.springframework.boot\n spring-boot-dependencies\n ${spring-boot.version}\n pom\n import\n \n \n org.springframework.cloud\n spring-cloud-dependencies\n ${spring-cloud.version}\n pom\n import\n \n \n \n\n \n \n org.projectlombok\n lombok\n true\n ${org.projectlombok.version}\n \n\n \n io.github.netcorepal\n cap4j-ddd-starter-jpa-rocketmq\n ${cap4j.version}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n provided\n \n\n \n \n org.springframework.boot\n spring-boot-starter-web\n \n \n org.springframework.boot\n spring-boot-starter-actuator\n \n \n org.springframework.boot\n spring-boot-starter-validation\n \n\n \n \n org.apache.commons\n commons-lang3\n ${commons.lang3.version}\n \n \n org.apache.commons\n commons-collections4\n ${commons.collections4.version}\n \n \n com.google.guava\n guava\n ${guava.version}\n \n \n com.alibaba\n fastjson\n ${fastjson.version}\n \n\n \n \n com.ctrip.framework.apollo\n apollo-client\n 1.7.0\n \n \n \n com.githup.liuyanggithup\n xxl-job-core\n 1.2.1\n \n \n \n org.springframework.cloud\n spring-cloud-starter-openfeign\n \n \n io.github.openfeign\n feign-okhttp\n 11.10\n \n\n \n \n org.springframework.boot\n spring-boot-starter-data-jpa\n \n \n \n org.mybatis.spring.boot\n mybatis-spring-boot-starter\n 2.1.3\n \n \n \n com.alibaba\n druid-spring-boot-starter\n ${spring-boot-druid.version}\n \n \n \n mysql\n mysql-connector-java\n ${mysql-connector.version}\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n org.springdoc\n springdoc-openapi-ui\n ${springdoc.version}\n \n \n com.github.xiaoymin\n knife4j-springdoc-ui\n ${knife4j.version}\n \n \n io.swagger.core.v3\n swagger-annotations\n ${swagger3.version}\n \n\n \n net.logstash.logback\n logstash-logback-encoder\n 6.6\n \n \n junit\n junit\n 3.8.1\n test\n \n \n \n ${project.artifactId}\n \n \n io.github.netcorepal\n cap4j-ddd-codegen-maven-plugin\n ${cap4j.version}\n \n ${basePackage}\n ${archTemplate}\n ${archTemplateEncoding}\n ${designFile}\n ${multiModule}\n ${moduleNameSuffix4Adapter}\n ${moduleNameSuffix4Domain}\n ${moduleNameSuffix4Application}\n \n \n \n ${user}\n ${pwd}\n ${schema}\n ${table}
\n ${ignoreTable}\n ${ignoreFields}\n ${idField}\n ${versionField}\n ${deletedField}\n ${readonlyFields}\n ${entityBaseClass}\n ${entityClassExtraImports}\n ${entityMetaInfoClassOutputMode}\n ${entityMetaInfoClassOutputPackage}\n ${fetchMode}\n ${fetchType}\n ${idGenerator}\n ${enumValueField}\n ${enumNameField}\n ${enumUnmatchedThrowException}\n ${datePackage4Java}\n ${typeRemapping}\n ${generateDefault}\n ${generateDbType}\n ${generateSchema}\n ${generateBuild}\n ${aggregateRootAnnotation}\n ${aggregateRepositoryBaseClass}\n ${aggregateRepositoryCustomerCode}\n ${ignoreAggregateRoots}\n
\n
\n \n org.springframework.boot\n spring-boot-maven-plugin\n 2.6.3\n \n \n \n \n repackage\n \n repackage\n \n \n ${basePackage}.StartApplication\n \n \n \n \n \n \n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 1.8\n 1.8\n UTF-8\n \n \n \n org.apache.maven.plugins\n maven-archetype-plugin\n 3.2.0\n \n\n
\n
\n
" } ] } \ No newline at end of file From c3ecf187c4d61892cd97c980104c01826ae568d4 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 18 Sep 2024 12:23:06 +0800 Subject: [PATCH 41/62] =?UTF-8?q?=E8=A1=A5=E5=85=A8AutoRelease=20=E7=9A=84?= =?UTF-8?q?=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...26\347\240\201\346\214\207\345\215\227.md" | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git "a/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" "b/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" index 065267d..cdb5d4f 100644 --- "a/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" +++ "b/doc/02_\345\272\224\347\224\250\345\261\202\347\274\226\347\240\201\346\214\207\345\215\227.md" @@ -309,13 +309,13 @@ mvn cap4j-ddd-codegen:gen-design -Ddesign=ie:OrderPlaced:cap4j-example-order-pla ```java package org.netcorepal.cap4j.ddd.example.application.distributed.events; +import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRelease; +import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.events.OrderClosedDomainEvent; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRelease; -import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; /** * todo: 集成事件描述 @@ -328,16 +328,8 @@ import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; @Builder @AllArgsConstructor @NoArgsConstructor -public class OrderPlacedIntegrationEvent implements Converter { +public class OrderPlacedIntegrationEvent { private Long id; - - @Override - public OrderPlacedIntegrationEvent convert(OrderPlacedDomainEvent source) { - return OrderPlacedIntegrationEvent.builder() - .id(source.getId()) - //... - .build(); - } } ``` @@ -383,13 +375,14 @@ public class OrderPlacedDomainEventSubscriber { ```java package org.netcorepal.cap4j.ddd.example.application.distributed.events; +import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRelease; +import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.events.OrderClosedDomainEvent; +import org.springframework.core.convert.converter.Converter; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRelease; -import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent; /** * todo: 集成事件描述 From f9fe4cef3516091c2ee3cd053221ab69fc29de13 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 18 Sep 2024 21:17:34 +0800 Subject: [PATCH 42/62] =?UTF-8?q?=E6=94=AF=E6=8C=81postgresql=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E7=9A=84gen-entity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- cap4j-ddd-codegen-maven-plugin/pom.xml | 5 + .../cap4j/ddd/codegen/GenEntityMojo.java | 37 ++- .../ddd/codegen/misc/SqlSchemaUtils.java | 41 ++- .../codegen/misc/SqlSchemaUtils4Mysql.java | 11 +- .../misc/SqlSchemaUtils4Postgresql.java | 271 ++++++++++++++++++ 6 files changed, 340 insertions(+), 29 deletions(-) create mode 100644 cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Postgresql.java diff --git a/README.md b/README.md index 50531f3..d9568f9 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ Mediator中介者模式、 123456 test
- - + __% + db_% id version db_deleted diff --git a/cap4j-ddd-codegen-maven-plugin/pom.xml b/cap4j-ddd-codegen-maven-plugin/pom.xml index 14cb0e6..53fc6c1 100644 --- a/cap4j-ddd-codegen-maven-plugin/pom.xml +++ b/cap4j-ddd-codegen-maven-plugin/pom.xml @@ -29,6 +29,11 @@ mysql-connector-java 5.1.46 + + org.postgresql + postgresql + 42.5.0 + com.alibaba fastjson diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index ca31cf5..667ac01 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -346,8 +346,7 @@ public String getEntityPackage(String tableName) { public boolean isReservedColumn(Map column) { String columnName = getColumnName(column).toLowerCase(); boolean isReserved = idField.equalsIgnoreCase(columnName) - || versionField.equalsIgnoreCase(columnName) - || columnName.startsWith("db_"); + || versionField.equalsIgnoreCase(columnName); return isReserved; } @@ -627,19 +626,19 @@ public void processAnnotationLines(Map table, List table, List table, List table, M writeLine(out, " @Convert(converter = " + columnJavaType + ".Converter.class)"); } if (!updatable || !insertable) { - writeLine(out, " @Column(name = \"" + LEFT_QUOTES_4_ID_ALIAS + columnName + RIGHT_QUOTES_4_ID_ALIAS + "\", insertable = " + (insertable ? "true" : "false") + ", updatable = " + (updatable ? "true" : "false") + ")"); + writeLine(out, " @Column(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + columnName + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\", insertable = " + (insertable ? "true" : "false") + ", updatable = " + (updatable ? "true" : "false") + ")"); } else { - writeLine(out, " @Column(name = \"" + LEFT_QUOTES_4_ID_ALIAS + columnName + RIGHT_QUOTES_4_ID_ALIAS + "\")"); + writeLine(out, " @Column(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + columnName + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\")"); } if (generateDefault) { String defaultJavaLiteral = getColumnDefaultJavaLiteral(column); @@ -1044,7 +1043,7 @@ public void writeRelationProperty(BufferedWriter out, Map table, switch (refInfos[0]) { case "OneToMany": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY, orphanRemoval = true)"); - writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\", nullable = false)"); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\", nullable = false)"); boolean countIsOne = countIsOne(navTable); if (countIsOne) { writeLine(out, " @Getter(lombok.AccessLevel.PROTECTED)"); @@ -1060,12 +1059,12 @@ public void writeRelationProperty(BufferedWriter out, Map table, break; case "ManyToOne": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)"); - writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")"); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\")"); writeLine(out, " private " + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + " " + toLowerCamelCase(getEntityJavaType(entry.getKey())) + ";"); break; case "OneToOne": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)"); - writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")"); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\")"); writeLine(out, " private " + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + " " + toLowerCamelCase(getEntityJavaType(entry.getKey())) + ";"); break; case "*OneToMany": @@ -1078,7 +1077,7 @@ public void writeRelationProperty(BufferedWriter out, Map table, break; case "ManyToMany": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)"); - writeLine(out, " @JoinTable(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[3] + RIGHT_QUOTES_4_ID_ALIAS + "\", joinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")}, inverseJoinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[2] + RIGHT_QUOTES_4_ID_ALIAS + "\")})"); + writeLine(out, " @JoinTable(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[3] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\", joinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\")}, inverseJoinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[2] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\")})"); writeLine(out, " private java.util.List<" + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + "> " + Inflector.getInstance().pluralize(toLowerCamelCase(getEntityJavaType(entry.getKey()))) + ";"); break; case "*ManyToMany": @@ -1094,7 +1093,7 @@ public void writeRelationProperty(BufferedWriter out, Map table, switch (refInfos[0]) { case "OneToMany": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true) @Fetch(FetchMode." + fetchMode + ")"); - writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\", nullable = false)"); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\", nullable = false)"); boolean countIsOne = countIsOne(navTable); if (countIsOne) { writeLine(out, " @Getter(lombok.AccessLevel.PROTECTED)"); @@ -1110,12 +1109,12 @@ public void writeRelationProperty(BufferedWriter out, Map table, break; case "ManyToOne": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) @Fetch(FetchMode." + fetchMode + ")"); - writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")"); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\")"); writeLine(out, " private " + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + " " + toLowerCamelCase(getEntityJavaType(entry.getKey())) + ";"); break; case "OneToOne": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) @Fetch(FetchMode." + fetchMode + ")"); - writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")"); + writeLine(out, " @JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\")"); writeLine(out, " private " + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + " " + toLowerCamelCase(getEntityJavaType(entry.getKey())) + ";"); break; case "*OneToMany": @@ -1128,7 +1127,7 @@ public void writeRelationProperty(BufferedWriter out, Map table, break; case "ManyToMany": writeLine(out, " @" + refInfos[0].replace("*", "") + "(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) @Fetch(FetchMode." + fetchMode + ")"); - writeLine(out, " @JoinTable(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[3] + RIGHT_QUOTES_4_ID_ALIAS + "\", joinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS + "\")}, inverseJoinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS + refInfos[2] + RIGHT_QUOTES_4_ID_ALIAS + "\")})"); + writeLine(out, " @JoinTable(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[3] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\", joinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[1] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\")}, inverseJoinColumns = {@JoinColumn(name = \"" + LEFT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + refInfos[2] + RIGHT_QUOTES_4_ID_ALIAS.replace("\"", "\\\"") + "\")})"); writeLine(out, " private java.util.List<" + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + "> " + Inflector.getInstance().pluralize(toLowerCamelCase(getEntityJavaType(entry.getKey()))) + ";"); break; case "*ManyToMany": diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java index 44d018b..37bab08 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils.java @@ -99,6 +99,9 @@ static List> executeQuery(String sql, String connectionStrin case DB_TYPE_MYSQL: Class.forName("com.mysql.jdbc.Driver"); break; + case DB_TYPE_POSTGRESQL: + Class.forName("org.postgresql.Driver"); + break; } //2.获得数据库链接 Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); @@ -142,6 +145,8 @@ public static List> resolveTables(String connectionString, S default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.resolveTables(connectionString, user, pwd); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.resolveTables(connectionString, user, pwd); } } @@ -159,6 +164,8 @@ public static List> resolveColumns(String connectionString, default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.resolveColumns(connectionString, user, pwd); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.resolveColumns(connectionString, user, pwd); } } @@ -181,6 +188,8 @@ public static String getColumnJavaType(Map column) { default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.getColumnJavaType(column); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.getColumnJavaType(column); } } @@ -195,6 +204,8 @@ public static String getColumnDefaultJavaLiteral(Map column) { default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.getColumnDefaultJavaLiteral(column); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.getColumnDefaultJavaLiteral(column); } } @@ -209,6 +220,8 @@ public static boolean isAutoUpdateDateColumn(Map column) { default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.isAutoUpdateDateColumn(column); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.isAutoUpdateDateColumn(column); } } @@ -223,6 +236,8 @@ public static boolean isAutoInsertDateColumn(Map column) { default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.isAutoInsertDateColumn(column); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.isAutoInsertDateColumn(column); } } @@ -238,6 +253,8 @@ public static boolean isColumnInTable(Map column, Map column) { default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.getOridinalPosition(column); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.getOridinalPosition(column); } } @@ -267,6 +286,8 @@ public static boolean hasColumn(String columnName, List> col default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.hasColumn(columnName, columns); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.hasColumn(columnName, columns); } } @@ -281,6 +302,8 @@ public static String getName(Map tableOrColumn) { default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.getName(tableOrColumn); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.getName(tableOrColumn); } } @@ -295,20 +318,24 @@ public static String getColumnName(Map column) { default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.getColumnName(column); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.getColumnName(column); } } /** * 获取表名 * - * @param tableOrColumn + * @param table * @return */ - public static String getTableName(Map tableOrColumn) { + public static String getTableName(Map table) { switch (mojo.dbType) { default: case DB_TYPE_MYSQL: - return SqlSchemaUtils4Mysql.getTableName(tableOrColumn); + return SqlSchemaUtils4Mysql.getTableName(table); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.getTableName(table); } } @@ -323,6 +350,8 @@ public static String getColumnDbType(Map column) { default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.getColumnDbType(column); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.getColumnDbType(column); } } @@ -337,6 +366,8 @@ public static String getColumnDbDataType(Map column) { default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.getColumnDbDataType(column); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.getColumnDbDataType(column); } } @@ -351,6 +382,8 @@ public static boolean isColumnNullable(Map column) { default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.isColumnNullable(column); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.isColumnNullable(column); } } @@ -366,6 +399,8 @@ public static String getComment(Map tableOrColumn, boolean clean default: case DB_TYPE_MYSQL: return SqlSchemaUtils4Mysql.getComment(tableOrColumn, cleanAnnotations); + case DB_TYPE_POSTGRESQL: + return SqlSchemaUtils4Postgresql.getComment(tableOrColumn, cleanAnnotations); } } diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Mysql.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Mysql.java index 4909ee3..a724ec6 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Mysql.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Mysql.java @@ -27,6 +27,7 @@ public static List> resolveTables(String connectionString, S String whereClause = String.join(" or ", Arrays.stream(mojo.ignoreTable.split(mojo.PATTERN_SPLITTER)).map(t -> "table_name like " + LEFT_QUOTES_4_LITERAL_STRING + t + RIGHT_QUOTES_4_LITERAL_STRING).collect(Collectors.toList())); tableSql += " and not (" + whereClause + ")"; } + mojo.getLog().debug(tableSql); return executeQuery(tableSql, connectionString, user, pwd); } public static List> resolveColumns(String connectionString, String user, String pwd){ @@ -39,6 +40,7 @@ public static List> resolveColumns(String connectionString, String whereClause = String.join(" or ", Arrays.stream(mojo.ignoreTable.split(mojo.PATTERN_SPLITTER)).map(t -> "table_name like " + LEFT_QUOTES_4_LITERAL_STRING + t + RIGHT_QUOTES_4_LITERAL_STRING).collect(Collectors.toList())); columnSql += " and not (" + whereClause + ")"; } + mojo.getLog().debug(columnSql); return executeQuery(columnSql, connectionString, user, pwd); } @@ -143,9 +145,9 @@ public static String getColumnDefaultJavaLiteral(Map column) { case "Boolean": if (StringUtils.isNotEmpty(columnDefault)) { if (columnDefault.trim().equalsIgnoreCase("b'1'")) { - return "false"; - } else if (columnDefault.trim().equalsIgnoreCase("b'0'")) { return "true"; + } else if (columnDefault.trim().equalsIgnoreCase("b'0'")) { + return "false"; } return "" + (columnDefault.trim().equalsIgnoreCase("0") ? "false" : "true"); } else { @@ -164,7 +166,6 @@ public static String getColumnDefaultJavaLiteral(Map column) { } else { return "java.math.BigDecimal.ZERO"; } - case "java.util.Date": default: break; } @@ -208,8 +209,8 @@ public static String getColumnName(Map column) { return column.get("COLUMN_NAME").toString(); } - public static String getTableName(Map tableOrColumn) { - return tableOrColumn.get("TABLE_NAME").toString(); + public static String getTableName(Map table) { + return table.get("TABLE_NAME").toString(); } public static String getColumnDbType(Map column) { diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Postgresql.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Postgresql.java new file mode 100644 index 0000000..a6e290b --- /dev/null +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/SqlSchemaUtils4Postgresql.java @@ -0,0 +1,271 @@ +package org.netcorepal.cap4j.ddd.codegen.misc; + +import org.codehaus.plexus.util.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.netcorepal.cap4j.ddd.codegen.misc.SqlSchemaUtils.*; + +/** + * todo: 类描述 + * + * @author binking338 + * @date 2024/9/18 + */ +public class SqlSchemaUtils4Postgresql { + public static List> resolveTables(String connectionString, String user, String pwd) { + String tableSql = "SELECT rel.relname AS table_name, obj_description(rel.oid) AS table_comment\n" + + "FROM pg_class rel\n" + + "WHERE rel.relkind = 'r' AND rel.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = '" + mojo.schema + "')"; + if (StringUtils.isNotBlank(mojo.table)) { + String whereClause = String.join(" or ", Arrays.stream(mojo.table.split(mojo.PATTERN_SPLITTER)).map(t -> "rel.relname like " + LEFT_QUOTES_4_LITERAL_STRING + t + RIGHT_QUOTES_4_LITERAL_STRING).collect(Collectors.toList())); + tableSql += " and (" + whereClause + ")"; + } + if (StringUtils.isNotBlank(mojo.ignoreTable)) { + String whereClause = String.join(" or ", Arrays.stream(mojo.ignoreTable.split(mojo.PATTERN_SPLITTER)).map(t -> "rel.relname like " + LEFT_QUOTES_4_LITERAL_STRING + t + RIGHT_QUOTES_4_LITERAL_STRING).collect(Collectors.toList())); + tableSql += " and not (" + whereClause + ")"; + } + mojo.getLog().debug(tableSql); + return executeQuery(tableSql, connectionString, user, pwd); + } + + public static List> resolveColumns(String connectionString, String user, String pwd) { + String columnSql = "SELECT\n" + + " att.attnum AS num, -- 列序号\n" + + " tbl.relname AS table_name, -- 列序号\n" + + " att.attname AS column_name, -- 列名\n" + + " typ.typname AS data_type, -- 数据类型\n" + + "\n" + + " CASE when\n" + + " SUBSTRING ( format_type ( att.atttypid, att.atttypmod ) FROM '\\(.*\\)' ) isNUll\n" + + " then '0'\n" + + " else\n" + + " substr(SUBSTRING ( format_type ( att.atttypid, att.atttypmod ) FROM '\\(.*\\)' ),2,CHAR_LENGTH(SUBSTRING ( format_type ( att.atttypid, att.atttypmod ) FROM '\\(.*\\)' ))-2)\n" + + " end as data_len, -- 数据长度\n" + + " att.attnotnull AS not_null, -- 非空\n" + + " att.attidentity AS identity, -- 自增主键\n" + + " format_type(att.atttypid, att.atttypmod) AS column_type, -- 列数据类型\n" + + " col.column_default AS column_default, -- 列默认值\n" + + " pgd.description AS column_comment -- 列注释\n" + + "FROM\n" + + " pg_attribute att\n" + + " JOIN\n" + + " pg_class tbl ON att.attrelid = tbl.oid\n" + + " JOIN\n" + + " pg_type typ ON typ.oid = att.atttypid\n" + + " JOIN\n" + + " pg_namespace ns ON tbl.relnamespace = ns.oid\n" + + " LEFT JOIN\n" + + " pg_description pgd ON pgd.objoid = att.attrelid AND pgd.objsubid = att.attnum\n" + + " LEFT JOIN\n" + + " information_schema.columns col ON col.table_name = tbl.relname AND col.column_name = att.attname\n" + + "WHERE\n" + + " ns.nspname = '" + mojo.schema + "' -- 模式名,替换为你的模式名(例如 public)\n" + + " AND att.attnum > 0 -- 排除系统列\n" + + " AND NOT att.attisdropped"; + if (StringUtils.isNotBlank(mojo.table)) { + String whereClause = String.join(" or ", Arrays.stream(mojo.table.split(mojo.PATTERN_SPLITTER)).map(t -> "tbl.relname like " + LEFT_QUOTES_4_LITERAL_STRING + t + RIGHT_QUOTES_4_LITERAL_STRING).collect(Collectors.toList())); + columnSql += " and (" + whereClause + ")"; + } + if (StringUtils.isNotBlank(mojo.ignoreTable)) { + String whereClause = String.join(" or ", Arrays.stream(mojo.ignoreTable.split(mojo.PATTERN_SPLITTER)).map(t -> "tbl.relname like " + LEFT_QUOTES_4_LITERAL_STRING + t + RIGHT_QUOTES_4_LITERAL_STRING).collect(Collectors.toList())); + columnSql += " and not (" + whereClause + ")"; + } + mojo.getLog().debug(columnSql); + return executeQuery(columnSql, connectionString, user, pwd); + } + + /** + * 获取列的Java映射类型(Mysql) + * + * @param column + * @return + */ + public static String getColumnJavaType(Map column) { + String dataType = column.get("data_type").toString().toLowerCase(); + String columnType = column.get("column_type").toString().toLowerCase(); + String comment = SqlSchemaUtils.getComment(column); + String columnName = SqlSchemaUtils.getColumnName(column).toLowerCase(); + if (mojo.typeRemapping != null && mojo.typeRemapping.containsKey(dataType)) { + // 类型重映射 + return mojo.typeRemapping.get(dataType); + } + switch (dataType) { + case "char": + case "varchar": + case "text": + case "name": + case "character": + case "nchar": + return "String"; + case "timestamp": + case "timestamptz": + if ("java.time".equalsIgnoreCase(mojo.datePackage4Java)) { + return "java.time.LocalDateTime"; + } else { + return "java.util.Date"; + } + case "date": + if ("java.time".equalsIgnoreCase(mojo.datePackage4Java)) { + return "java.time.LocalDate"; + } else { + return "java.util.Date"; + } + case "time": + case "timetz": + if ("java.time".equalsIgnoreCase(mojo.datePackage4Java)) { + return "java.time.LocalTime"; + } else { + return "java.util.Date"; + } + case "int4": + case "int": + case "integer": + case "serial": + return "Integer"; + case "int8": + return "Long"; + case "int2": + case "smallint": + return "Short"; + case "bool": + case "boolean": + return "Boolean"; + case "float4": + return "Float"; + case "float8": + return "Double"; + case "decimal": + case "money": + case "numeric": + return "java.math.BigDecimal"; + default: + break; + } + throw new RuntimeException("包含未支持字段类型!" + dataType); + } + + public static String getColumnDefaultJavaLiteral(Map column) { + String columnDefault = column.get("column_default") == null ? null : column.get("column_default").toString(); + switch (SqlSchemaUtils.getColumnJavaType(column)) { + case "String": + if (StringUtils.isNotEmpty(columnDefault)) { + String defaultString = columnDefault.split(":")[0]; + if (defaultString.startsWith("'") && defaultString.endsWith("'")) { + return "\"" + defaultString.substring(1, defaultString.length() - 2).replace("\"", "\\\"") + "\""; + } else { + return "\"" + defaultString.replace("\"", "\\\"") + "\""; + } + } else { + return "\"\""; + } + case "Integer": + case "Short": + case "Byte": + if (StringUtils.isNotEmpty(columnDefault)) { + return "" + columnDefault; + } else { + return "0"; + } + case "Long": + if (StringUtils.isNotEmpty(columnDefault)) { + return "" + columnDefault + "L"; + } else { + return "0L"; + } + case "Boolean": + if (StringUtils.isNotEmpty(columnDefault)) { + if (columnDefault.trim().equalsIgnoreCase("false")) { + return "false"; + } else if (columnDefault.trim().equalsIgnoreCase("true")) { + return "true"; + } + return "" + (columnDefault.trim().equalsIgnoreCase("0") ? "false" : "true"); + } else { + return "false"; + } + case "Float": + case "Double": + if (StringUtils.isNotEmpty(columnDefault)) { + return "" + columnDefault; + } else { + return "0"; + } + case "java.math.BigDecimal": + if (StringUtils.isNotEmpty(columnDefault)) { + return "java.math.BigDecimal.valueOf(" + columnDefault + ")"; + } else { + return "java.math.BigDecimal.ZERO"; + } + default: + break; + } + return ""; // = "" + } + + public static boolean isAutoUpdateDateColumn(Map column) { + return false; + } + + public static boolean isAutoInsertDateColumn(Map column) { + String defaultData = column.get("column_default") == null ? "" : column.get("column_default").toString(); + if ("CURRENT_TIMESTAMP".equalsIgnoreCase(defaultData)) { + return true; + } + return false; + } + + public static boolean isColumnInTable(Map column, Map table) { + return column.get("table_name").toString().equalsIgnoreCase(table.get("table_name").toString()); + } + + public static int getOridinalPosition(Map column) { + return Integer.parseInt(column.get("num").toString()); + } + + + public static boolean hasColumn(String columnName, List> columns) { + return columns.stream().anyMatch(col -> col.get("column_name").toString().equalsIgnoreCase(columnName)); + } + + public static String getName(Map tableOrColumn) { + return tableOrColumn.containsKey("column_name") ? getColumnName(tableOrColumn) : getTableName(tableOrColumn); + } + + public static String getColumnName(Map column) { + return column.get("column_name").toString(); + } + + public static String getTableName(Map table) { + return table.get("table_name").toString(); + } + + public static String getColumnDbType(Map column) { + return column.get("column_type").toString(); + } + + public static String getColumnDbDataType(Map column) { + return column.get("data_type").toString(); + } + + public static boolean isColumnNullable(Map column) { + return "true".equalsIgnoreCase(column.get("not_null").toString()); + } + + public static String getComment(Map tableOrColumn, boolean cleanAnnotations) { + String comment = ""; + if (tableOrColumn.containsKey("table_comment")) { + comment = tableOrColumn.get("table_comment") == null ? "" : tableOrColumn.get("table_comment").toString(); + } else if (tableOrColumn.containsKey("column_comment")) { + comment = tableOrColumn.get("column_comment") == null ? "" : tableOrColumn.get("column_comment").toString(); + } + + if (cleanAnnotations) { + comment = SqlSchemaUtils.ANNOTATION_PATTERN.matcher(comment).replaceAll(""); + } + return comment.trim(); + } +} From 33b88a34ffacfcee9f7adec9a673c6d20f6bb881 Mon Sep 17 00:00:00 2001 From: binking338 Date: Sun, 22 Sep 2024 23:31:56 +0800 Subject: [PATCH 43/62] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=B0=8F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/GenEntityMojo.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index 667ac01..fcf2041 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -14,8 +14,7 @@ import java.util.*; import java.util.stream.Collectors; -import static org.netcorepal.cap4j.ddd.codegen.misc.NamingUtils.toLowerCamelCase; -import static org.netcorepal.cap4j.ddd.codegen.misc.NamingUtils.toUpperCamelCase; +import static org.netcorepal.cap4j.ddd.codegen.misc.NamingUtils.*; import static org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils.writeLine; import static org.netcorepal.cap4j.ddd.codegen.misc.SqlSchemaUtils.*; @@ -250,11 +249,18 @@ public String getModule(String tableName) { String module = SqlSchemaUtils.getModule(table); getLog().info("尝试解析模块:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[缺失]" : module)); while (!isAggregateRoot(table) && StringUtils.isBlank(module)) { - table = TableMap.get(getParent(table)); + String parent = getParent(table); + if (StringUtils.isBlank(parent)) { + break; + } + table = TableMap.get(parent); + if (table == null) { + getLog().error("表 " + tableName + " @Parent 注解值填写错误,不存在表名为 " + parent + " 的表"); + } module = SqlSchemaUtils.getModule(table); getLog().info("尝试父表模块:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[缺失]" : module)); } - getLog().info("模块解析结果:" + getTableName(table) + " " + (StringUtils.isBlank(module) ? "[无]" : module)); + getLog().info("模块解析结果:" + tableName + " " + (StringUtils.isBlank(module) ? "[无]" : module)); return module; }); } @@ -280,7 +286,7 @@ public String getAggregate(String tableName) { getLog().info("尝试父表聚合:" + getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); } if (StringUtils.isBlank(aggregate)) { - aggregate = getEntityJavaType(getTableName(table)).toLowerCase(); + aggregate = toSnakeCase(getEntityJavaType(getTableName(table))); } getLog().info("聚合解析结果:" + tableName + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); return aggregate; @@ -1155,8 +1161,10 @@ public void writeFactorySourceFile(Map table, String baseDir) th "package " + entityPackage + ".factory;\n" + "\n" + "import " + entityPackage + "." + simpleClassName + ";\n" + + "import lombok.AllArgsConstructor;\n" + "import lombok.Builder;\n" + "import lombok.Data;\n" + + "import lombok.NoArgsConstructor;\n" + "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + "import org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload;\n" + "\n" + @@ -1170,6 +1178,8 @@ public void writeFactorySourceFile(Map table, String baseDir) th "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + simpleClassName + "Payload\", type = Aggregate.TYPE_FACTORY_PAYLOAD, description = \"\")\n" + "@Data\n" + "@Builder\n" + + "@NoArgsConstructor\n" + + "@AllArgsConstructor\n" + "public class " + simpleClassName + "Payload implements AggregatePayload<" + simpleClassName + "> {\n" + " private Long id;\n" + "}" From b47bdfb860604cd0f125cb908425a48ef961d461 Mon Sep 17 00:00:00 2001 From: binking338 Date: Sun, 22 Sep 2024 23:32:08 +0800 Subject: [PATCH 44/62] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cap4j-ddd-codegen-template.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cap4j-ddd-codegen-template.json b/cap4j-ddd-codegen-template.json index 284dfe6..1fc77fb 100644 --- a/cap4j-ddd-codegen-template.json +++ b/cap4j-ddd-codegen-template.json @@ -10,7 +10,7 @@ "type": "file", "name": "${Command}Request.java", "format": "raw", - "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n/**\n * ${Command}命令请求参数\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\npublic class ${Command}Request implements RequestParam<${ReturnType}> {\n\n}" + "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.application.RequestParam;\n\n/**\n * ${Command}命令请求参数\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Command}Request implements RequestParam<${ReturnType}> {\n String param;\n\n}" }, { "type": "file", @@ -22,7 +22,7 @@ "type": "file", "name": "${Command}Handler.java", "format": "raw", - "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.netcorepal.cap4j.ddd.Mediator;\nimport org.netcorepal.cap4j.ddd.application.command.Command;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${Command}命令请求实现\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class ${Command}Handler implements Command<${Command}Request, ${Command}Response> {\n\n @Override\n public ${Command}Response exec(${Command}Request cmd) {\n Mediator.uow().save();\n \n return null;\n }\n}\n" + "data": "package ${basePackage}.application.commands${package};\n\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.netcorepal.cap4j.ddd.Mediator;\nimport org.netcorepal.cap4j.ddd.application.command.Command;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${Command}命令请求实现\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class ${Command}Handler implements Command<${Command}Request, ${Command}Response> {\n\n @Override\n public ${Command}Response exec(${Command}Request cmd) {\n Mediator.uow().save();\n \n return ${Command}Response.builder()\n .success(true)\n .build();\n }\n}\n" } ] }, @@ -221,7 +221,7 @@ "type": "file", "name": "${Entity}Payload.java", "format": "raw", - "data": "package ${basePackage}.domain.aggregates${package}.factory;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport lombok.Builder;\nimport lombok.Data;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload;\n\n/**\n * ${Entity}工厂负载\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Payload\", type = Aggregate.TYPE_FACTORY_PAYLOAD, description = \"\")\n@Data\n@Builder\npublic class ${Entity}Payload implements AggregatePayload<${Entity}> {\n \n}\n" + "data": "package ${basePackage}.domain.aggregates${package}.factory;\n\nimport ${basePackage}.domain.aggregates${package}.${Entity};\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\nimport org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload;\n\n/**\n * ${Entity}工厂负载\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@Aggregate(aggregate = \"${Aggreagate}\", name = \"${Entity}Payload\", type = Aggregate.TYPE_FACTORY_PAYLOAD, description = \"\")\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ${Entity}Payload implements AggregatePayload<${Entity}> {\n \n}\n" }, { "type": "file", @@ -247,13 +247,13 @@ { "type": "dir", "tag": "domain_service", - "name": "", + "name": "${path}", "children": [ { "type": "file", "name": "${Name}.java", "format": "raw", - "data": "package ${basePackage}.domain.services;\n\nimport org.netcorepal.cap4j.ddd.domain.service.annotation.DomainService;\nimport org.springframework.stereotype.Service;\n\n/**\n * todo: 领域服务描述\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@DomainService\n@Service\npublic class ${Name} {\n}\n" + "data": "package ${basePackage}.domain.services${package};\n\nimport org.netcorepal.cap4j.ddd.domain.service.annotation.DomainService;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${Comment}\n *\n * @author cap4j-ddd-codegen\n * @date ${date}\n */\n@DomainService\n@Service\npublic class ${Name} {\n}\n" } ] } From a8b470d3386e248b9e441fdb96cf57a8fe81e340 Mon Sep 17 00:00:00 2001 From: binking338 Date: Sun, 22 Sep 2024 23:32:37 +0800 Subject: [PATCH 45/62] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E9=A3=8E=E6=A0=BC=E5=8F=98=E6=8D=A2=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cap4j/ddd/codegen/misc/NamingUtils.java | 124 +++++++++++++----- 1 file changed, 90 insertions(+), 34 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/NamingUtils.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/NamingUtils.java index 78e14a0..4fb8880 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/NamingUtils.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/misc/NamingUtils.java @@ -1,5 +1,10 @@ package org.netcorepal.cap4j.ddd.codegen.misc; +import org.codehaus.plexus.util.StringUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + /** * @author binking338 * @date 2022-02-17 @@ -10,35 +15,40 @@ public class NamingUtils { * user_name ----> userName * userName ---> userName * - * @param underlineStr 带有下划线的字符串 + * @param someCase 带有下划线的字符串 * @return 驼峰字符串 */ - public static String toLowerCamelCase(String underlineStr) { - if (underlineStr == null) { + public static String toLowerCamelCase(String someCase) { + if (someCase == null) { return null; } - // 分成数组 - char[] charArray = underlineStr.toCharArray(); - // 判断上次循环的字符是否是"_" - boolean underlineBefore = false; - StringBuffer buffer = new StringBuffer(); - for (int i = 0, l = charArray.length; i < l; i++) { - if (i == 0 && Character.isUpperCase(charArray[0])) { - buffer.append(Character.toLowerCase(charArray[i])); - } - // 判断当前字符是否是"_",如果跳出本次循环 - else if (charArray[i] == 95) { - underlineBefore = true; - } else if (underlineBefore) { - // 如果为true,代表上次的字符是"_",当前字符需要转成大写 - buffer.append(charArray[i] -= 32); - underlineBefore = false; - } else { - // 不是"_"后的字符就直接追加 - buffer.append(charArray[i]); - } - } - return buffer.toString(); + String camel = toUpperCamelCase(someCase); + return camel.substring(0, 1).toLowerCase() + camel.substring(1); +// if (someCase == null) { +// return null; +// } +// // 分成数组 +// char[] charArray = someCase.toCharArray(); +// // 判断上次循环的字符是否是"_" +// boolean underlineBefore = false; +// StringBuffer buffer = new StringBuffer(); +// for (int i = 0, l = charArray.length; i < l; i++) { +// if (i == 0 && Character.isUpperCase(charArray[0])) { +// buffer.append(Character.toLowerCase(charArray[i])); +// } +// // 判断当前字符是否是"_",如果跳出本次循环 +// else if (charArray[i] == 95) { +// underlineBefore = true; +// } else if (underlineBefore) { +// // 如果为true,代表上次的字符是"_",当前字符需要转成大写 +// buffer.append(charArray[i] -= 32); +// underlineBefore = false; +// } else { +// // 不是"_"后的字符就直接追加 +// buffer.append(charArray[i]); +// } +// } +// return buffer.toString(); } /** @@ -46,12 +56,58 @@ else if (charArray[i] == 95) { * user_name ----> UserName * userName ---> UserName * - * @param underlineStr 带有下划线的字符串 + * @param someCase 带有下划线的字符串 * @return 驼峰字符串 */ - public static String toUpperCamelCase(String underlineStr) { - String camel = toLowerCamelCase(underlineStr); - return camel.substring(0, 1).toUpperCase() + camel.substring(1); + public static String toUpperCamelCase(String someCase) { + return String.join( + "", + Arrays.stream(someCase.split("(?=[A-Z])|[^a-zA-Z0-9]")) + .map(s -> s.replaceAll("[^a-zA-Z0-9]", "")) + .filter(s -> StringUtils.isNotBlank(s)) + .map(s -> s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase()) + .collect(Collectors.toList()) + ); +// String camel = toLowerCamelCase(someCase); +// return camel.substring(0, 1).toUpperCase() + camel.substring(1); + } + + /** + * 转蛇形风格命名(snake_case) + * + * @param someCase + * @return + */ + public static String toSnakeCase(String someCase) { + if (someCase == null) { + return null; + } + return String.join( + "_", + Arrays.stream(someCase.split("(?=[A-Z])|[^a-zA-Z0-9_]")) + .filter(s -> StringUtils.isNotBlank(s)) + .map(s -> s.toLowerCase()) + .collect(Collectors.toList()) + ); + } + + /** + * 转土耳其烤肉风格命名(kebab-case) + * + * @param someCase + * @return + */ + public static String toKebabCase(String someCase) { + if (someCase == null) { + return someCase; + } + return String.join( + "-", + Arrays.stream(someCase.split("(?=[A-Z])|[^a-zA-Z0-9\\-]")) + .filter(s -> StringUtils.isNotBlank(s)) + .map(s -> s.toLowerCase()) + .collect(Collectors.toList()) + ); } /** @@ -60,11 +116,11 @@ public static String toUpperCamelCase(String underlineStr) { * @param packageName * @return */ - public static String getLastPackageName(String packageName){ - if(null == packageName || packageName.isEmpty()){ + public static String getLastPackageName(String packageName) { + if (null == packageName || packageName.isEmpty()) { return ""; } - if(!packageName.contains(".")){ + if (!packageName.contains(".")) { return packageName; } return packageName.substring(packageName.lastIndexOf(".") + 1); @@ -76,9 +132,9 @@ public static String getLastPackageName(String packageName){ * @param packageName * @return */ - public static String parentPackageName(String packageName){ + public static String parentPackageName(String packageName) { String lastPackageName = getLastPackageName(packageName); - if(packageName.length() == lastPackageName.length()){ + if (packageName.length() == lastPackageName.length()) { return ""; } return packageName.substring(0, packageName.length() - lastPackageName.length() - 1); From f7d8bb263bf88d623452779ee3dd06659cbe38a0 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 25 Sep 2024 08:55:39 +0800 Subject: [PATCH 46/62] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E6=A0=87=E8=AF=86=E7=AC=A6=E5=8F=8D=E5=BC=95=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../distributed/configure/JdbcLockerProperties.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/JdbcLockerProperties.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/JdbcLockerProperties.java index 8704b1c..5368aab 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/JdbcLockerProperties.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/application/distributed/configure/JdbcLockerProperties.java @@ -17,21 +17,21 @@ public class JdbcLockerProperties { /** * 锁表名 */ - String table = "`__locker`"; + String table = "__locker"; /** * 锁名称字段名 */ - String fieldName = "`name`"; + String fieldName = "name"; /** * 锁密码字段名 */ - String fieldPwd = "`pwd`"; + String fieldPwd = "pwd"; /** * 锁获取时间字段名 */ - String fieldLockAt = "`lock_at`"; + String fieldLockAt = "lock_at"; /** * 锁释放时间字段名 */ - String fieldUnlockAt = "`unlock_at`"; + String fieldUnlockAt = "unlock_at"; } From 095b94324f8e6fcf6b7aef1a673950fe49bf7437 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 25 Sep 2024 19:11:31 +0800 Subject: [PATCH 47/62] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=AF=94=E8=BE=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddd/domain/repo/impl/DefaultPersistListenerManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java index e31e6fb..db6d5dc 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultPersistListenerManager.java @@ -107,7 +107,7 @@ public void onChange(Entity entity, PersistType type) { } } } - if(!Object.class.equals(entity)){ + if(!Object.class.equals(entity.getClass())){ listeners = persistListenersMap.get(Object.class); if (listeners != null) { for (PersistListener listener : From 99b8ec6e1c63f28c6bdcf773f1bc20b3d35a6675 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 25 Sep 2024 19:12:39 +0800 Subject: [PATCH 48/62] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A1=AB=E5=85=85?= =?UTF-8?q?=E9=A2=86=E5=9F=9F=E6=9C=8D=E5=8A=A1=E9=BB=98=E8=AE=A4=E5=8D=95?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddd/domain/service/DomainServiceAutoConfiguration.java | 6 +++--- .../cap4j/ddd/domain/service/DomainServiceSupervisor.java | 2 +- ...nfiguration.java => DomainServiceSupervisorSupport.java} | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/{DomainServiceSupervisorConfiguration.java => DomainServiceSupervisorSupport.java} (73%) diff --git a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java index f887c03..9b80e42 100644 --- a/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java +++ b/cap4j-ddd-starter-jpa-rocketmq/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceAutoConfiguration.java @@ -2,7 +2,6 @@ import lombok.RequiredArgsConstructor; import org.netcorepal.cap4j.ddd.domain.service.impl.DefaultDomainServiceSupervisor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -25,9 +24,10 @@ public class DomainServiceAutoConfiguration { */ @Bean public DefaultDomainServiceSupervisor defaultDomainServiceSupervisor(ApplicationContext applicationContext) { - DefaultDomainServiceSupervisor domainServiceSupervisor = new DefaultDomainServiceSupervisor( + DefaultDomainServiceSupervisor defaultDomainServiceSupervisor = new DefaultDomainServiceSupervisor( applicationContext ); - return domainServiceSupervisor; + DomainServiceSupervisorSupport.configure(defaultDomainServiceSupervisor); + return defaultDomainServiceSupervisor; } } diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisor.java index 681fd4a..fc5cba9 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisor.java @@ -9,7 +9,7 @@ public interface DomainServiceSupervisor { public static DomainServiceSupervisor getInstance(){ - return DomainServiceSupervisorConfiguration.instance; + return DomainServiceSupervisorSupport.instance; } /** diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisorConfiguration.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisorSupport.java similarity index 73% rename from ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisorConfiguration.java rename to ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisorSupport.java index cc4c58d..e20eed4 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisorConfiguration.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/service/DomainServiceSupervisorSupport.java @@ -6,10 +6,10 @@ * @author binking338 * @date 2024/9/4 */ -public class DomainServiceSupervisorConfiguration { +public class DomainServiceSupervisorSupport { static DomainServiceSupervisor instance; public static void configure(DomainServiceSupervisor domainServiceSupervisor){ - domainServiceSupervisor = instance; + instance = domainServiceSupervisor; } } From efdf3b927cdc0805c3344b012a54f51e34e0af6f Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 25 Sep 2024 19:16:55 +0800 Subject: [PATCH 49/62] =?UTF-8?q?=E9=80=9A=E8=BF=87=E9=87=8D=E8=BD=BD4Upda?= =?UTF-8?q?te=E6=96=B9=E6=B3=95=E5=AE=9E=E7=8E=B0=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E9=99=84=E5=8A=A0UniOfWork=E4=B8=8A=E4=B8=8B=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddd/domain/repo/RepositorySupervisor.java | 44 +++++++++++++++++++ .../cap4j/ddd/impl/DefaultMediator.java | 15 +++++++ .../impl/DefaultRepositorySupervisor.java | 18 ++++++++ 3 files changed, 77 insertions(+) diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java index 1ec4856..8f2b51a 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java @@ -38,6 +38,18 @@ default List find(Predicate predicate) { return find(predicate, null); } + /** + * 根据条件获取实体列表 + * 自动调用 UnitOfWork::persist + * + * @param predicate + * @param + * @return + */ + default List find4Update(Predicate predicate) { + return find4Update(predicate, null); + } + /** * 根据条件获取实体列表 * @@ -48,6 +60,17 @@ default List find(Predicate predicate) { */ List find(Predicate predicate, List orders); + /** + * 根据条件获取实体列表 + * 自动调用 UnitOfWork::persist + * + * @param predicate + * @param orders + * @param + * @return + */ + List find4Update(Predicate predicate, List orders); + /** * 根据条件获取单个实体 * @@ -57,6 +80,16 @@ default List find(Predicate predicate) { */ Optional findOne(Predicate predicate); + /** + * 根据条件获取单个实体 + * 自动调用 UnitOfWork::persist + * + * @param predicate + * @param + * @return + */ + Optional findOne4Update(Predicate predicate); + /** * 根据条件获取实体分页列表 * @@ -67,6 +100,17 @@ default List find(Predicate predicate) { */ PageData findPage(Predicate predicate, PageParam pageParam); + /** + * 根据条件获取实体分页列表 + * 自动调用 UnitOfWork::persist + * + * @param predicate + * @param pageParam + * @param + * @return + */ + PageData findPage4Update(Predicate predicate, PageParam pageParam); + /** * 根据条件删除实体 * diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java index ad60511..8032512 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java @@ -43,16 +43,31 @@ public List find(Predicate predicate, List o return RepositorySupervisor.getInstance().find(predicate, orders); } + @Override + public List find4Update(Predicate predicate, List orders) { + return RepositorySupervisor.getInstance().find4Update(predicate, orders); + } + @Override public Optional findOne(Predicate predicate) { return RepositorySupervisor.getInstance().findOne(predicate); } + @Override + public Optional findOne4Update(Predicate predicate) { + return RepositorySupervisor.getInstance().findOne4Update(predicate); + } + @Override public PageData findPage(Predicate predicate, PageParam pageParam) { return RepositorySupervisor.getInstance().findPage(predicate, pageParam); } + @Override + public PageData findPage4Update(Predicate predicate, PageParam pageParam) { + return RepositorySupervisor.getInstance().findPage4Update(predicate, pageParam); + } + @Override public long count(Predicate predicate) { return RepositorySupervisor.getInstance().count(predicate); diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java index e9d4b43..0ed40ef 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java @@ -59,6 +59,12 @@ public Repository repo(Class entityClass) { public List find(Predicate predicate, List orders) { Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); List entities = repo(entityClass).find(predicate, orders); + return entities; + } + + @Override + public List find4Update(Predicate predicate, List orders) { + List entities = find(predicate, orders); if (entities != null) { entities.forEach(unitOfWork::persist); } @@ -69,6 +75,12 @@ public List find(Predicate predicate, List o public Optional findOne(Predicate predicate) { Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); Optional entity = repo(entityClass).findOne(predicate); + return entity; + } + + @Override + public Optional findOne4Update(Predicate predicate) { + Optional entity = findOne(predicate); entity.ifPresent(unitOfWork::persist); return entity; } @@ -77,6 +89,12 @@ public Optional findOne(Predicate predicate) { public PageData findPage(Predicate predicate, PageParam pageParam) { Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); PageData page = repo(entityClass).findPage(predicate, pageParam); + return page; + } + + @Override + public PageData findPage4Update(Predicate predicate, PageParam pageParam) { + PageData page = findPage(predicate, pageParam); if (page.getList() != null) { page.getList().forEach(unitOfWork::persist); } From c8d0b910a2cd536875d4ba30960657d3add7bc73 Mon Sep 17 00:00:00 2001 From: binking338 Date: Wed, 25 Sep 2024 19:17:45 +0800 Subject: [PATCH 50/62] =?UTF-8?q?=E5=A4=84=E7=90=86UnitOfWork::save?= =?UTF-8?q?=E7=9A=84=E5=B5=8C=E5=A5=97=E8=B0=83=E7=94=A8=E5=BC=95=E5=8F=91?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddd/application/impl/JpaUnitOfWork.java | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java index 56007b8..e6de3d3 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java @@ -56,6 +56,7 @@ public static void fixAopWrapper(JpaUnitOfWork unitOfWork) { private static ThreadLocal> persistedEntitiesThreadLocal = new ThreadLocal<>(); private static ThreadLocal> removedEntitiesThreadLocal = new ThreadLocal<>(); + private static ThreadLocal> processedPersistenceContextEntitiesThreadLocal = new ThreadLocal<>(); private static ThreadLocal entityPersisttedEventThreadLocal = new ThreadLocal<>(); @@ -81,11 +82,35 @@ public void save() { save(Propagation.REQUIRED); } + private boolean pushProcessedPersistenceContextEntities(Object entity, Set currentProcessedPersistenceContextEntities) { + if (processedPersistenceContextEntitiesThreadLocal.get() == null) { + processedPersistenceContextEntitiesThreadLocal.set(new HashSet<>()); + processedPersistenceContextEntitiesThreadLocal.get().add(entity); + currentProcessedPersistenceContextEntities.add(entity); + return true; + } else if(!processedPersistenceContextEntitiesThreadLocal.get().contains(entity)){ + processedPersistenceContextEntitiesThreadLocal.get().add(entity); + currentProcessedPersistenceContextEntities.add(entity); + return true; + } + return false; + } + + private boolean popProcessedPersistenceContextEntities(Set currentProcessedPersistenceContextEntities) { + if (processedPersistenceContextEntitiesThreadLocal.get() != null && currentProcessedPersistenceContextEntities != null) { + return processedPersistenceContextEntitiesThreadLocal.get().removeAll(currentProcessedPersistenceContextEntities); + } + return true; + } + public void save(Propagation propagation) { + Set currentProcessedPersistenceContextEntities = new HashSet<>(); + Set persistEntityList = null; if (persistedEntitiesThreadLocal.get() != null) { persistEntityList = new HashSet<>(persistedEntitiesThreadLocal.get()); persistedEntitiesThreadLocal.get().clear(); + persistEntityList.forEach(e -> pushProcessedPersistenceContextEntities(e, currentProcessedPersistenceContextEntities)); } else { persistEntityList = new HashSet<>(); } @@ -93,13 +118,16 @@ public void save(Propagation propagation) { if (removedEntitiesThreadLocal.get() != null) { deleteEntityList = new HashSet<>(removedEntitiesThreadLocal.get()); removedEntitiesThreadLocal.get().clear(); + deleteEntityList.forEach(e -> pushProcessedPersistenceContextEntities(e, currentProcessedPersistenceContextEntities)); } else { deleteEntityList = new HashSet<>(); } for (Object entity : persistenceContextEntities()) { - // 如果不在删除列表中,则加入保存列表 - if (!deleteEntityList.contains(entity)) { - persistEntityList.add(entity); + if (pushProcessedPersistenceContextEntities(entity, currentProcessedPersistenceContextEntities)) { + // 如果不在删除列表中,则加入保存列表 + if (!deleteEntityList.contains(entity)) { + persistEntityList.add(entity); + } } } if (!persistEntityList.isEmpty() || !deleteEntityList.isEmpty()) { @@ -169,11 +197,13 @@ public void save(Propagation propagation) { integrationEventManager.release(); return null; }, saveAndDeleteEntityList, propagation); + popProcessedPersistenceContextEntities(currentProcessedPersistenceContextEntities); } public static void reset() { persistedEntitiesThreadLocal.remove(); removedEntitiesThreadLocal.remove(); + processedPersistenceContextEntitiesThreadLocal.remove(); entityPersisttedEventThreadLocal.remove(); } @@ -440,10 +470,6 @@ public EntityPersisttedEvent(Object source, Set createdEntities, Set(createdEntities), new HashSet<>(updatedEntities), new HashSet<>(deletedEntities)); - } } @EventListener(classes = EntityPersisttedEvent.class) From cac7e6aeb88956c3d9b588f39ed513b5fac0d01a Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 26 Sep 2024 10:55:27 +0800 Subject: [PATCH 51/62] =?UTF-8?q?=E7=A7=BB=E9=99=A4Schema=E5=AF=B9org.apac?= =?UTF-8?q?he.commons.collections4=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index fcf2041..6355436 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -1442,7 +1442,6 @@ public void writeSchemaSourceFile(Map table, List table, List> builders) {\n" + - " if(CollectionUtils.isEmpty(builders)) {\n" + + " if(null == builders || builders.isEmpty()) {\n" + " return Sort.unsorted();\n" + " }\n" + " return Sort.by(builders.stream()\n" + From a893ff7c56bd9a5cdfe8dc20090b445dc83a3a9b Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 26 Sep 2024 11:28:31 +0800 Subject: [PATCH 52/62] =?UTF-8?q?=E4=BF=AE=E5=A4=8DSchema=E7=94=9F?= =?UTF-8?q?=E6=88=90=E5=9C=A8=E5=A4=9A=E6=A8=A1=E5=9D=97=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E4=B8=8B=EF=BC=8C=E7=9B=AE=E6=A0=87=E6=A8=A1=E5=9D=97=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index 6355436..98bb6e1 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -208,7 +208,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info(""); if (generateSchema) { try { - writeBaseSechemaSourceFile(basePackage, applicationModulePath); + writeBaseSechemaSourceFile(basePackage, domainModulePath); } catch (IOException e) { e.printStackTrace(); getLog().error(e); @@ -745,7 +745,7 @@ public void writeEntitySourceFile(Map table, List Date: Thu, 26 Sep 2024 12:58:36 +0800 Subject: [PATCH 53/62] =?UTF-8?q?=E4=BB=93=E5=82=A8=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=90=8D=E7=A7=B0=E9=87=8D=E6=9E=84=EF=BC=8C?= =?UTF-8?q?=E4=BE=BF=E4=BA=8E=E9=BB=98=E8=AE=A4UnitOfWork::persist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddd/domain/repo/RepositorySupervisor.java | 20 +++++++++---------- .../cap4j/ddd/impl/DefaultMediator.java | 18 ++++++++--------- .../impl/DefaultRepositorySupervisor.java | 18 ++++++++--------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java index 8f2b51a..d0f695b 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/RepositorySupervisor.java @@ -34,8 +34,8 @@ static RepositorySupervisor getInstance() { * @param * @return */ - default List find(Predicate predicate) { - return find(predicate, null); + default List findWithoutPersist(Predicate predicate) { + return findWithoutPersist(predicate, null); } /** @@ -46,8 +46,8 @@ default List find(Predicate predicate) { * @param * @return */ - default List find4Update(Predicate predicate) { - return find4Update(predicate, null); + default List find(Predicate predicate) { + return find(predicate, null); } /** @@ -58,7 +58,7 @@ default List find4Update(Predicate predicate) { * @param * @return */ - List find(Predicate predicate, List orders); + List findWithoutPersist(Predicate predicate, List orders); /** * 根据条件获取实体列表 @@ -69,7 +69,7 @@ default List find4Update(Predicate predicate) { * @param * @return */ - List find4Update(Predicate predicate, List orders); + List find(Predicate predicate, List orders); /** * 根据条件获取单个实体 @@ -78,7 +78,7 @@ default List find4Update(Predicate predicate) { * @param * @return */ - Optional findOne(Predicate predicate); + Optional findOneWithoutPersist(Predicate predicate); /** * 根据条件获取单个实体 @@ -88,7 +88,7 @@ default List find4Update(Predicate predicate) { * @param * @return */ - Optional findOne4Update(Predicate predicate); + Optional findOne(Predicate predicate); /** * 根据条件获取实体分页列表 @@ -98,7 +98,7 @@ default List find4Update(Predicate predicate) { * @param * @return */ - PageData findPage(Predicate predicate, PageParam pageParam); + PageData findPageWithoutPersist(Predicate predicate, PageParam pageParam); /** * 根据条件获取实体分页列表 @@ -109,7 +109,7 @@ default List find4Update(Predicate predicate) { * @param * @return */ - PageData findPage4Update(Predicate predicate, PageParam pageParam); + PageData findPage(Predicate predicate, PageParam pageParam); /** * 根据条件删除实体 diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java index 8032512..9b308c1 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/impl/DefaultMediator.java @@ -38,14 +38,19 @@ public Repository repo(Class entityClass) { return RepositorySupervisor.getInstance().repo(entityClass); } + @Override + public List findWithoutPersist(Predicate predicate, List orders) { + return RepositorySupervisor.getInstance().findWithoutPersist(predicate, orders); + } + @Override public List find(Predicate predicate, List orders) { return RepositorySupervisor.getInstance().find(predicate, orders); } @Override - public List find4Update(Predicate predicate, List orders) { - return RepositorySupervisor.getInstance().find4Update(predicate, orders); + public Optional findOneWithoutPersist(Predicate predicate) { + return RepositorySupervisor.getInstance().findOneWithoutPersist(predicate); } @Override @@ -54,8 +59,8 @@ public Optional findOne(Predicate predicate) { } @Override - public Optional findOne4Update(Predicate predicate) { - return RepositorySupervisor.getInstance().findOne4Update(predicate); + public PageData findPageWithoutPersist(Predicate predicate, PageParam pageParam) { + return RepositorySupervisor.getInstance().findPageWithoutPersist(predicate, pageParam); } @Override @@ -63,11 +68,6 @@ public PageData findPage(Predicate predicate, PageParam return RepositorySupervisor.getInstance().findPage(predicate, pageParam); } - @Override - public PageData findPage4Update(Predicate predicate, PageParam pageParam) { - return RepositorySupervisor.getInstance().findPage4Update(predicate, pageParam); - } - @Override public long count(Predicate predicate) { return RepositorySupervisor.getInstance().count(predicate); diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java index 0ed40ef..2625923 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/domain/repo/impl/DefaultRepositorySupervisor.java @@ -56,15 +56,15 @@ public Repository repo(Class entityClass) { } @Override - public List find(Predicate predicate, List orders) { + public List findWithoutPersist(Predicate predicate, List orders) { Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); List entities = repo(entityClass).find(predicate, orders); return entities; } @Override - public List find4Update(Predicate predicate, List orders) { - List entities = find(predicate, orders); + public List find(Predicate predicate, List orders) { + List entities = findWithoutPersist(predicate, orders); if (entities != null) { entities.forEach(unitOfWork::persist); } @@ -72,29 +72,29 @@ public List find4Update(Predicate predicate, List Optional findOne(Predicate predicate) { + public Optional findOneWithoutPersist(Predicate predicate) { Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); Optional entity = repo(entityClass).findOne(predicate); return entity; } @Override - public Optional findOne4Update(Predicate predicate) { - Optional entity = findOne(predicate); + public Optional findOne(Predicate predicate) { + Optional entity = findOneWithoutPersist(predicate); entity.ifPresent(unitOfWork::persist); return entity; } @Override - public PageData findPage(Predicate predicate, PageParam pageParam) { + public PageData findPageWithoutPersist(Predicate predicate, PageParam pageParam) { Class entityClass = JpaPredicateSupport.reflectEntityClass(predicate); PageData page = repo(entityClass).findPage(predicate, pageParam); return page; } @Override - public PageData findPage4Update(Predicate predicate, PageParam pageParam) { - PageData page = findPage(predicate, pageParam); + public PageData findPage(Predicate predicate, PageParam pageParam) { + PageData page = findPageWithoutPersist(predicate, pageParam); if (page.getList() != null) { page.getList().forEach(unitOfWork::persist); } From 1a0318ee3174ad3884026a7c9e96d8e4249b1a84 Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 26 Sep 2024 12:59:40 +0800 Subject: [PATCH 54/62] =?UTF-8?q?=E6=9B=B4=E6=96=B0UntOfWork=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/netcorepal/cap4j/ddd/application/UnitOfWork.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java index f8165bd..dc6432d 100644 --- a/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java +++ b/ddd-core/src/main/java/org/netcorepal/cap4j/ddd/application/UnitOfWork.java @@ -14,26 +14,26 @@ static UnitOfWork getInstance() { } /** - * 新增或更新持久化记录 + * 提交新增或更新实体持久化记录意图到UnitOfWork上下文 * * @param entity 实体对象 */ void persist(Object entity); /** - * 移除持久化记录 + * 提交移除实体持久化记录意图到UnitOfWork上下文 * * @param entity 实体对象 */ void remove(Object entity); /** - * 提交事务 + * 将持久化意图转换成持久化指令,并提交事务 */ void save(); /** - * 提交事务 + * 将持久化意图转换成持久化指令,并提交事务 * * @param propagation 事务传播特性 */ From 3444c3a05956451a65c7631296bf6a239b6090a4 Mon Sep 17 00:00:00 2001 From: binking338 Date: Thu, 26 Sep 2024 16:32:46 +0800 Subject: [PATCH 55/62] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=9D=9E=E8=81=9A?= =?UTF-8?q?=E5=90=88=E6=A0=B9=E5=AE=9E=E4=BD=93=E4=BA=8B=E4=BB=B6=E9=87=8A?= =?UTF-8?q?=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddd/application/impl/JpaUnitOfWork.java | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java index e6de3d3..fbcfafe 100644 --- a/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java +++ b/ddd-domain-repo-jpa/src/main/java/org/netcorepal/cap4j/ddd/application/impl/JpaUnitOfWork.java @@ -8,6 +8,7 @@ import org.netcorepal.cap4j.ddd.application.event.IntegrationEventManager; import org.netcorepal.cap4j.ddd.domain.aggregate.Specification; import org.netcorepal.cap4j.ddd.domain.aggregate.SpecificationManager; +import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate; import org.netcorepal.cap4j.ddd.domain.event.DomainEventManager; import org.netcorepal.cap4j.ddd.domain.repo.PersistListenerManager; import org.netcorepal.cap4j.ddd.domain.repo.PersistType; @@ -56,7 +57,7 @@ public static void fixAopWrapper(JpaUnitOfWork unitOfWork) { private static ThreadLocal> persistedEntitiesThreadLocal = new ThreadLocal<>(); private static ThreadLocal> removedEntitiesThreadLocal = new ThreadLocal<>(); - private static ThreadLocal> processedPersistenceContextEntitiesThreadLocal = new ThreadLocal<>(); + private static ThreadLocal> processingThreadLocal = new ThreadLocal<>(); private static ThreadLocal entityPersisttedEventThreadLocal = new ThreadLocal<>(); @@ -82,35 +83,38 @@ public void save() { save(Propagation.REQUIRED); } - private boolean pushProcessedPersistenceContextEntities(Object entity, Set currentProcessedPersistenceContextEntities) { - if (processedPersistenceContextEntitiesThreadLocal.get() == null) { - processedPersistenceContextEntitiesThreadLocal.set(new HashSet<>()); - processedPersistenceContextEntitiesThreadLocal.get().add(entity); + private boolean pushProcessingEntities(Object entity, Set currentProcessedPersistenceContextEntities) { + if (null == entity) { + return false; + } + if (processingThreadLocal.get() == null) { + processingThreadLocal.set(new HashSet<>()); + processingThreadLocal.get().add(entity); currentProcessedPersistenceContextEntities.add(entity); return true; - } else if(!processedPersistenceContextEntitiesThreadLocal.get().contains(entity)){ - processedPersistenceContextEntitiesThreadLocal.get().add(entity); + } else if (!processingThreadLocal.get().contains(entity)) { + processingThreadLocal.get().add(entity); currentProcessedPersistenceContextEntities.add(entity); return true; } return false; } - private boolean popProcessedPersistenceContextEntities(Set currentProcessedPersistenceContextEntities) { - if (processedPersistenceContextEntitiesThreadLocal.get() != null && currentProcessedPersistenceContextEntities != null) { - return processedPersistenceContextEntitiesThreadLocal.get().removeAll(currentProcessedPersistenceContextEntities); + private boolean popProcessingEntities(Set currentProcessedPersistenceContextEntities) { + if (processingThreadLocal.get() != null && currentProcessedPersistenceContextEntities != null) { + return processingThreadLocal.get().removeAll(currentProcessedPersistenceContextEntities); } return true; } public void save(Propagation propagation) { - Set currentProcessedPersistenceContextEntities = new HashSet<>(); + Set currentProcessedEntities = new HashSet<>(); Set persistEntityList = null; if (persistedEntitiesThreadLocal.get() != null) { persistEntityList = new HashSet<>(persistedEntitiesThreadLocal.get()); persistedEntitiesThreadLocal.get().clear(); - persistEntityList.forEach(e -> pushProcessedPersistenceContextEntities(e, currentProcessedPersistenceContextEntities)); + persistEntityList.forEach(e -> pushProcessingEntities(e, currentProcessedEntities)); } else { persistEntityList = new HashSet<>(); } @@ -118,25 +122,15 @@ public void save(Propagation propagation) { if (removedEntitiesThreadLocal.get() != null) { deleteEntityList = new HashSet<>(removedEntitiesThreadLocal.get()); removedEntitiesThreadLocal.get().clear(); - deleteEntityList.forEach(e -> pushProcessedPersistenceContextEntities(e, currentProcessedPersistenceContextEntities)); + deleteEntityList.forEach(e -> pushProcessingEntities(e, currentProcessedEntities)); } else { deleteEntityList = new HashSet<>(); } - for (Object entity : persistenceContextEntities()) { - if (pushProcessedPersistenceContextEntities(entity, currentProcessedPersistenceContextEntities)) { - // 如果不在删除列表中,则加入保存列表 - if (!deleteEntityList.contains(entity)) { - persistEntityList.add(entity); - } - } - } - if (!persistEntityList.isEmpty() || !deleteEntityList.isEmpty()) { - if (null == entityPersisttedEventThreadLocal.get()) { - entityPersisttedEventThreadLocal.set(new EntityPersisttedEvent(this, new HashSet<>(), new HashSet<>(), new HashSet<>())); - } + if (null == entityPersisttedEventThreadLocal.get()) { + entityPersisttedEventThreadLocal.set(new EntityPersisttedEvent(this, new HashSet<>(), new HashSet<>(), new HashSet<>())); } specifyEntitesBeforeTransaction(persistEntityList); - Set[] saveAndDeleteEntityList = new Set[]{persistEntityList, deleteEntityList}; + Set[] saveAndDeleteEntityList = new Set[]{persistEntityList, deleteEntityList, currentProcessedEntities}; save(input -> { Set persistEntities = input[0]; Set deleteEntities = input[1]; @@ -175,6 +169,7 @@ public void save(Propagation propagation) { entityPersisttedEventThreadLocal.get().getDeletedEntities().add(entity); } } + if (flush) { getEntityManager().flush(); if (refreshEntityList != null && !refreshEntityList.isEmpty()) { @@ -193,17 +188,21 @@ public void save(Propagation propagation) { if (deleteEntities != null && !deleteEntities.isEmpty()) { entities.addAll(deleteEntities); } + for (Object entity : persistenceContextEntities()) { + pushProcessingEntities(entity, currentProcessedEntities); + } + entities.addAll(currentProcessedEntities); domainEventManager.release(entities); integrationEventManager.release(); return null; }, saveAndDeleteEntityList, propagation); - popProcessedPersistenceContextEntities(currentProcessedPersistenceContextEntities); + popProcessingEntities(currentProcessedEntities); } public static void reset() { persistedEntitiesThreadLocal.remove(); removedEntitiesThreadLocal.remove(); - processedPersistenceContextEntitiesThreadLocal.remove(); + processingThreadLocal.remove(); entityPersisttedEventThreadLocal.remove(); } From 8fbbf5d191d8b5d8beee916b2a9924102e1ed518 Mon Sep 17 00:00:00 2001 From: binking338 Date: Tue, 1 Oct 2024 01:23:04 +0800 Subject: [PATCH 56/62] =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E7=94=9F=E4=BA=A7=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=BE=93=E5=87=BA=E6=96=87=E4=BB=B6=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E8=AE=BE=E7=BD=AE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +- .../cap4j/ddd/codegen/GenArchMojo.java | 252 +-- .../cap4j/ddd/codegen/GenDesignMojo.java | 265 +-- .../cap4j/ddd/codegen/GenEntityMojo.java | 1782 ++++++++++------- .../cap4j/ddd/codegen/GenRepositoryMojo.java | 341 ++-- .../cap4j/ddd/codegen/HelpMojo.java | 12 +- .../cap4j/ddd/codegen/MyAbstractMojo.java | 915 ++++++++- .../ddd/codegen/misc/SourceFileUtils.java | 158 +- .../cap4j/ddd/codegen/template/PathNode.java | 2 +- cap4j-ddd-codegen-template.json | 18 +- 10 files changed, 2393 insertions(+), 1362 deletions(-) diff --git a/README.md b/README.md index d9568f9..cdfab6b 100644 --- a/README.md +++ b/README.md @@ -69,11 +69,9 @@ Mediator中介者模式、 db_deleted db_created_at,db_updated_at - - static org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisorSupport.events - - ref - domain._share.meta + static org.netcorepal.cap4j.ddd.domain.event.DomainEventSupervisorSupport.events + ref + domain._share.meta SUBSELECT EAGER org.netcorepal.cap4j.ddd.domain.distributed.SnowflakeIdentifierGenerator @@ -85,10 +83,8 @@ Mediator中介者模式、 false true true - false - diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java index 19968c4..69e836c 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenArchMojo.java @@ -1,25 +1,15 @@ package org.netcorepal.cap4j.ddd.codegen; import com.alibaba.fastjson.JSON; -import org.apache.commons.lang3.StringUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.util.FileUtils; import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; -import org.netcorepal.cap4j.ddd.codegen.template.PathNode; import org.netcorepal.cap4j.ddd.codegen.template.Template; -import org.netcorepal.cap4j.ddd.codegen.template.TemplateNode; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.List; -import java.util.Map; /** * 生成项目目录结构 @@ -30,20 +20,18 @@ @Mojo(name = "gen-arch") public class GenArchMojo extends MyAbstractMojo { - public final String PATTERN_SPLITTER = "[\\,\\;]"; - - protected String projectGroupId = ""; - protected String projectArtifactId = ""; - protected String projectVersion = ""; - - protected String projectDir = ""; - protected Template template = null; - @Override public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("当前系统默认编码:" + Charset.defaultCharset().name()); getLog().info("设置模板读取编码:" + archTemplateEncoding + " (from archTemplateEncoding)"); - + getLog().info("设置输出文件编码:" + outputEncoding + " (from outputEncoding)"); + getLog().info("基础包名:" + basePackage); + getLog().info(multiModule ? "多模块项目" : "单模块项目"); + getLog().info("项目目录:" + getProjectDir()); + getLog().info("适配层目录:" + getAdapterModulePath()); + getLog().info("应用层目录:" + getApplicationModulePath()); + getLog().info("领域层目录:" + getDomainModulePath()); + this.renderFileSwitch = true; String templateContent = ""; try { if (null == archTemplate || archTemplate.isEmpty()) { @@ -61,236 +49,18 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().warn("请设置(basePackage)参数"); return; } - MavenProject mavenProject = ((MavenProject) getPluginContext().get("project")); - if (mavenProject != null) { - projectGroupId = mavenProject.getGroupId(); - projectArtifactId = mavenProject.getArtifactId(); - projectVersion = mavenProject.getVersion(); - } - - // 项目结构解析 - projectDir = new File("").getAbsolutePath(); - getLog().info("项目目录:" + projectDir); template = JSON.parseObject(templateContent, Template.class); try { template.resolve(getEscapeContext()); } catch (IOException e) { - getLog().error("模板文件加载失败!"); + getLog().error("模板文件加载失败!", e); } - startRender(); - } - protected void startRender(){ try { - render(template, projectDir, false); + render(template, getProjectDir()); } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * 构建路径节点 - * - * @param pathNode - * @param parentPath - * @throws IOException - */ - public String render(PathNode pathNode, String parentPath, boolean onlyRenderDir) throws IOException { - String path = parentPath; - switch (pathNode.getType()) { - case "file": - path = renderFile(pathNode, parentPath, onlyRenderDir); - break; - case "dir": - path = renderDir(pathNode, parentPath); - if (pathNode.getChildren() != null) { - for (PathNode childPathNode : pathNode.getChildren()) { - render(childPathNode, path, onlyRenderDir); - } - } - break; - case "root": - if (pathNode.getChildren() != null) { - for (PathNode childPathNode : pathNode.getChildren()) { - render(childPathNode, parentPath, onlyRenderDir); - } - } - break; - } - return path; - } - - /** - * @param pathNode - * @param parentPath - * @return - * @throws IOException - */ - public String renderDir(PathNode pathNode, String parentPath) throws IOException { - if (!"dir".equalsIgnoreCase(pathNode.getType())) { - throw new RuntimeException("节点类型必须是目录"); - } - if (pathNode.getName() == null || pathNode.getName().isEmpty()) { - return parentPath; - } - String path = parentPath + File.separator + pathNode.getName(); - if (FileUtils.fileExists(path)) { - switch (pathNode.getConflict()) { - case "warn": - getLog().warn("目录存在:" + path); - break; - case "overwrite": - getLog().info("目录覆盖:" + path); - FileUtils.deleteDirectory(path); - FileUtils.mkdir(path); - break; - case "skip": -// getLog().info("目录存在:" + path); - break; - } - } else { - getLog().info("目录创建:" + path); - FileUtils.mkdir(path); - } - - if (StringUtils.isNotBlank(pathNode.getTag())) { - String[] tags = pathNode.getTag().split(PATTERN_SPLITTER); - for (String tag : tags) { - List templateNodes = template.select(tag); - if (null != templateNodes) { - renderDesign(templateNodes, path); - } - } - } - - return path; - } - - /** - * 生成设计框架代码 - * - * @param templateNodes - * @param parentPath - * @throws IOException - */ - public void renderDesign(List templateNodes, String parentPath) throws IOException { - - } - - /** - * @param pathNode - * @param parentPath - * @return - */ - public String renderFile(PathNode pathNode, String parentPath, boolean onlyRenderDir) throws IOException { - if (!"file".equalsIgnoreCase(pathNode.getType())) { - throw new RuntimeException("节点类型必须是文件"); - } - if (pathNode.getName() == null || pathNode.getName().isEmpty()) { - throw new RuntimeException("模板节点配置 name 不得为空 parentPath = " + parentPath); - } - String path = parentPath + File.separator + pathNode.getName(); - if (onlyRenderDir) return path; - - String content = pathNode.getData(); - if (FileUtils.fileExists(path)) { - switch (pathNode.getConflict()) { - case "warn": - getLog().warn("文件存在:" + path); - break; - case "overwrite": - getLog().info("文件覆盖:" + path); - FileUtils.fileDelete(path); - FileUtils.fileWrite(path, content); - break; - case "skip": - default: -// getLog().info("文件存在:" + path); - break; - } - } else { - getLog().info("文件创建:" + path); - FileUtils.fileWrite(path, content); - } - return path; - } - - public Map getEscapeContext() { - Map context = new HashMap<>(); - context.put("groupId", projectGroupId); - context.put("artifactId", projectArtifactId); - context.put("version", projectVersion); - context.put("archTemplate", archTemplate); - context.put("archTemplateEncoding", archTemplateEncoding); - context.put("designFile", designFile); - context.put("basePackage", basePackage); - context.put("basePackage__as_path", basePackage.replace(".", File.separator)); - context.put("multiModule", multiModule ? "true" : "false"); - context.put("moduleNameSuffix4Adapter", moduleNameSuffix4Adapter); - context.put("moduleNameSuffix4Domain", moduleNameSuffix4Domain); - context.put("moduleNameSuffix4Application", moduleNameSuffix4Application); - context.put("connectionString", connectionString); - context.put("user", user); - context.put("pwd", pwd); - context.put("schema", schema); - context.put("table", table); - context.put("ignoreTable", ignoreTable); - context.put("idField", idField); - context.put("versionField", versionField); - context.put("deletedField", deletedField); - context.put("readonlyFields", readonlyFields); - context.put("ignoreFields", ignoreFields); - context.put("entityBaseClass", entityBaseClass); - context.put("entityClassExtraImports", stringfyEntityClassImportPackages()); - context.put("entityMetaInfoClassOutputPackage", entityMetaInfoClassOutputPackage); - context.put("entityMetaInfoClassOutputMode", entityMetaInfoClassOutputMode); - context.put("idGenerator", idGenerator); - context.put("fetchType", fetchType); - context.put("fetchMode", fetchMode); - context.put("enumValueField", enumValueField); - context.put("enumNameField", enumNameField); - context.put("enumUnmatchedThrowException", enumUnmatchedThrowException ? "true" : "false"); - context.put("datePackage4Java", datePackage4Java); - context.put("typeRemapping", stringfyTypeRemapping()); - context.put("generateDefault", generateDefault ? "true" : "false"); - context.put("generateDbType", generateDbType ? "true" : "false"); - context.put("generateSchema", generateSchema ? "true" : "false"); - context.put("generateBuild", generateBuild ? "true" : "false"); - context.put("aggregateRootAnnotation", aggregateRootAnnotation); - context.put("aggregateRepositoryBaseClass", aggregateRepositoryBaseClass); - context.put("aggregateRepositoryCustomerCode", aggregateRepositoryCustomerCode); - context.put("ignoreAggregateRoots", ignoreAggregateRoots); - context.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); - context.put("SEPARATOR", File.separator); - context.put("separator", File.separator); - context.put("symbol_pound", "#"); - context.put("symbol_escape", "\\"); - context.put("symbol_dollar", "$"); - return context; - } - - private String stringfyTypeRemapping() { - if (typeRemapping == null || typeRemapping.isEmpty()) { - return ""; - } - String result = ""; - for (Map.Entry kv : - typeRemapping.entrySet()) { - result += "<" + kv.getKey() + ">" + kv.getValue() + ""; - } - return result; - } - - private String stringfyEntityClassImportPackages() { - if (entityClassExtraImports == null || entityClassExtraImports.isEmpty()) { - return ""; - } - String result = "\n"; - for (String entityClassExtraImport : entityClassExtraImports) { - result += " " + entityClassExtraImport + "\n"; + getLog().error("模板文件写入失败!", e); } - result += " "; - return result; } } diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java index 1d6e6a9..dc0e019 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenDesignMojo.java @@ -17,6 +17,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import static org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils.refPackage; + /** * 生成设计框架代码 * @@ -25,55 +27,12 @@ */ @Mojo(name = "gen-design") public class GenDesignMojo extends GenArchMojo { - public final String DESIGN_PARAMS_SPLITTER = "[\\:]"; @Override public void execute() throws MojoExecutionException, MojoFailureException { super.execute(); } - @Override - protected void startRender() { - try { - render(template, projectDir, true); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * 构建路径节点 - * - * @param pathNode - * @param parentPath - * @throws IOException - */ - @Override - public String render(PathNode pathNode, String parentPath, boolean onlyRenderDir) throws IOException { - String path = parentPath; - switch (pathNode.getType()) { - case "file": - path = renderFile(pathNode, parentPath, onlyRenderDir); - break; - case "dir": - path = renderDir(pathNode, parentPath); - if (pathNode.getChildren() != null) { - for (PathNode childPathNode : pathNode.getChildren()) { - render(childPathNode, path, onlyRenderDir); - } - } - break; - case "root": - if (pathNode.getChildren() != null) { - for (PathNode childPathNode : pathNode.getChildren()) { - render(childPathNode, parentPath, onlyRenderDir); - } - } - break; - } - return path; - } - public String alias4Design(String name) { switch (name.toLowerCase()) { case "commands": @@ -154,7 +113,7 @@ public Map> resolveLiteralDesign(String design) { return new HashMap<>(); } return Arrays.stream(escape(design).replaceAll("\\r\\n|\\r|\\n", ";").split(PATTERN_SPLITTER)) - .map(item -> TextUtils.splitWithTrim(item, DESIGN_PARAMS_SPLITTER, 2)) + .map(item -> TextUtils.splitWithTrim(item, PATTERN_DESIGN_PARAMS_SPLITTER, 2)) .filter(item -> item.length == 2) .collect(Collectors.groupingBy( g -> alias4Design(g[0]), @@ -173,7 +132,7 @@ public Map> resolveLiteralDesign(String design) { * @throws IOException */ @Override - public void renderDesign(List templateNodes, String parentPath) throws IOException { + public void renderTemplate(List templateNodes, String parentPath) throws IOException { String design = ""; if (StringUtils.isNotBlank(this.design)) { design += this.design; @@ -233,7 +192,7 @@ public void renderDesign(List templateNodes, String parentPath) th if (designMap.containsKey("integration_event")) { for (String literalDesign : designMap.get("integration_event")) { if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { - if (literalDesign.split(DESIGN_PARAMS_SPLITTER).length >= 3) { + if (literalDesign.split(PATTERN_DESIGN_PARAMS_SPLITTER).length >= 3) { renderAppLayerIntegrationEvent("integration_event_handler", literalDesign, parentPath, templateNode); } } @@ -269,7 +228,7 @@ public void renderDesign(List templateNodes, String parentPath) th if (designMap.containsKey("specification")) { for (String literalDesign : designMap.get("specification")) { if (StringUtils.isBlank(templateNode.getPattern()) || Pattern.compile(templateNode.getPattern()).asPredicate().test(literalDesign)) { - renderDomainLayerSpecificaton(literalDesign, parentPath, templateNode); + renderDomainLayerSpecification(literalDesign, parentPath, templateNode); } } } @@ -316,16 +275,13 @@ public void renderAppLayerCommand(String literalCommandDeclaration, String paren if (!Name.endsWith("Cmd") && !Name.endsWith("Command")) { Name += "Cmd"; } - context.put("Name", Name); - context.put("Command", context.get("Name")); - context.put("command", context.get("Name").toLowerCase()); - context.put("Request", context.get("Command") + "Request"); - context.put("Response", context.get("Command") + "Response"); - - context.put("ReturnType", context.get("Response")); + putContext(templateNode.getTag(), "Name", Name, context); + putContext(templateNode.getTag(), "Command", context.get("Name"), context); + putContext(templateNode.getTag(), "Request", context.get("Command") + "Request", context); + putContext(templateNode.getTag(), "Response", context.get("Command") + "Response", context); - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 命令描述"); - context.put("comment", context.get("Comment")); + putContext(templateNode.getTag(), "Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 命令描述", context); + putContext(templateNode.getTag(), "CommentEscaped", context.get("Comment").replace(PATTERN_LINE_BREAK, " "), context); return context; }); getLog().info("生成命令代码:" + path); @@ -342,16 +298,13 @@ public void renderAppLayerQuery(String literalQueryDeclaration, String parentPat if (!Name.endsWith("Qry") && !Name.endsWith("Query")) { Name += "Qry"; } - context.put("Name", Name); - context.put("Query", context.get("Name")); - context.put("query", context.get("Name").toLowerCase()); - context.put("Request", context.get("Query") + "Request"); - context.put("Response", context.get("Query") + "Response"); + putContext(templateNode.getTag(), "Name", Name, context); + putContext(templateNode.getTag(), "Query", context.get("Name"), context); + putContext(templateNode.getTag(), "Request", context.get("Query") + "Request", context); + putContext(templateNode.getTag(), "Response", context.get("Query") + "Response", context); - context.put("ReturnType", context.get("Response")); - - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 查询描述"); - context.put("comment", context.get("Comment")); + putContext(templateNode.getTag(), "Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 查询描述", context); + putContext(templateNode.getTag(), "CommentEscaped", context.get("Comment").replace(PATTERN_LINE_BREAK, " "), context); return context; }); getLog().info("生成查询代码:" + path); @@ -368,16 +321,13 @@ public void renderAppLayerClient(String literalClientDeclaration, String parentP if (!Name.endsWith("Cli") && !Name.endsWith("Client")) { Name += "Cli"; } - context.put("Name", Name); - context.put("Client", context.get("Name")); - context.put("client", context.get("Name").toLowerCase()); - context.put("Request", context.get("Name") + "Request"); - context.put("Response", context.get("Name") + "Response"); - - context.put("ReturnType", context.get("Response")); + putContext(templateNode.getTag(), "Name", Name, context); + putContext(templateNode.getTag(), "Client", context.get("Name"), context); + putContext(templateNode.getTag(), "Request", context.get("Name") + "Request", context); + putContext(templateNode.getTag(), "Response", context.get("Name") + "Response", context); - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 防腐端描述"); - context.put("comment", context.get("Comment")); + putContext(templateNode.getTag(), "Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 防腐端描述", context); + putContext(templateNode.getTag(), "CommentEscaped", context.get("Comment").replace(PATTERN_LINE_BREAK, " "), context); return context; }); getLog().info("生成防腐端代码:" + path); @@ -395,38 +345,33 @@ public void renderAppLayerIntegrationEvent(String literalType, String literalInt if (!Name.endsWith("Evt") && !Name.endsWith("Event")) { Name += "IntegrationEvent"; } - context.put("Name", Name); - context.put("IntegrationEvent", context.get("Name")); - context.put("Event", context.get("IntegrationEvent")); - context.put("INTEGRATION_EVENT", context.get("IntegrationEvent")); - context.put("IE", context.get("IntegrationEvent")); - context.put("I_E", context.get("IntegrationEvent")); - context.put("integration_event", context.get("Name").toLowerCase()); - context.put("event", context.get("integration_event")); - context.put("ie", context.get("integration_event")); - context.put("i_e", context.get("integration_event")); + putContext(templateNode.getTag(), "Name", Name, context); + putContext(templateNode.getTag(), "IntegrationEvent", context.get("Name"), context); if (context.containsKey("Val1")) { - context.put("MQ_TOPIC", StringUtils.isBlank(context.get("Val1")) - ? ("\"" + context.get("Val0") + "\"") - : ("\"" + context.get("Val1") + "\"")); + putContext(templateNode.getTag(), "MQ_TOPIC", StringUtils.isBlank(context.get("Val1")) + ? ("\"" + context.get("Val0") + "\"") + : ("\"" + context.get("Val1") + "\""), + context + ); } else { - context.put("MQ_TOPIC", ("\"" + context.get("Val0") + "\"")); + putContext(templateNode.getTag(), "MQ_TOPIC", ("\"" + context.get("Val0") + "\""), context); } if (Objects.equals(literalType, "integration_event")) { - context.put("MQ_CONSUMER", "IntegrationEvent.NONE_SUBSCRIBER"); - context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 集成事件描述"); + putContext(templateNode.getTag(), "MQ_CONSUMER", "IntegrationEvent.NONE_SUBSCRIBER", context); + putContext(templateNode.getTag(), "Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 集成事件描述", context); } else { if (context.containsKey("Val2")) { - context.put("MQ_CONSUMER", StringUtils.isBlank(context.get("Val2")) - ? "\"${spring.application.name}\"" - : ("\"" + context.get("Val2") + "\"") + putContext(templateNode.getTag(), "MQ_CONSUMER", StringUtils.isBlank(context.get("Val2")) + ? "\"${spring.application.name}\"" + : ("\"" + context.get("Val2") + "\""), + context ); } else { - context.put("MQ_CONSUMER", "\"${spring.application.name}\""); + putContext(templateNode.getTag(), "MQ_CONSUMER", "\"${spring.application.name}\"", context); } - context.put("Comment", context.containsKey("Val3") ? context.get("Val3") : "todo: 集成事件描述"); + putContext(templateNode.getTag(), "Comment", context.containsKey("Val3") ? context.get("Val3") : "todo: 集成事件描述", context); } - context.put("comment", context.get("Comment")); + putContext(templateNode.getTag(), "CommentEscaped", context.get("Comment").replace(PATTERN_LINE_BREAK, " "), context); return context; }); getLog().info("生成集成事件代码:" + path); @@ -442,8 +387,8 @@ public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, S String reletivePath = NamingUtils.parentPackageName(context.get("Val0")) .replace(".", File.separator); if (StringUtils.isNotBlank(reletivePath)) { - context.put("path", reletivePath); - context.put("package", StringUtils.isEmpty(reletivePath) ? "" : ("." + reletivePath.replace(File.separator, "."))); + putContext(templateNode.getTag(), "path", reletivePath, context); + putContext(templateNode.getTag(), "package", StringUtils.isEmpty(reletivePath) ? "" : ("." + reletivePath.replace(File.separator, ".")), context); } if (!context.containsKey("Val1")) { throw new RuntimeException("缺失领域事件名称,领域事件设计格式:AggregateRootEntityName:DomainEventName"); @@ -452,10 +397,6 @@ public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, S if (!Name.endsWith("Evt") && !Name.endsWith("Event")) { Name += "DomainEvent"; } - context.put("Name", Name); - context.put("DomainEvent", context.get("Name")); - context.put("DOMAIN_EVENT", context.get("DomainEvent")); - context.put("Event", context.get("DomainEvent")); String entity = NamingUtils.toUpperCamelCase( NamingUtils.getLastPackageName(context.get("Val0")) ); @@ -463,21 +404,20 @@ public void renderDomainLayerDomainEvent(String literalDomainEventDeclaration, S if (context.containsKey("val2") && "`true`persist`1`".contains("`" + context.get("val2") + "`")) { persist = true; } - context.put("persist", persist ? "true" : "false"); - context.put("PERSIST", context.get("persist")); - context.put("Entity", entity); - context.put("ENTITY", context.get("Entity")); - context.put("AggregateRoot", context.get("Entity")); - context.put("AGGREGATE_ROOT", context.get("Entity")); - context.put("aggregate_root", context.get("Entity")); - context.put("Aggregate", context.get("package")); - context.put("aggregate", context.get("package")); + putContext(templateNode.getTag(), "Name", Name, context); + putContext(templateNode.getTag(), "DomainEvent", context.get("Name"), context); + putContext(templateNode.getTag(), "persist", persist ? "true" : "false", context); + putContext(templateNode.getTag(), "Aggregate", context.get("package"), context); + putContext(templateNode.getTag(), "Entity", entity, context); + putContext(templateNode.getTag(), "EntityVar", NamingUtils.toLowerCamelCase(entity), context); + putContext(templateNode.getTag(), "AggregateRoot", context.get("Entity"), context); + if (Objects.equals("domain_event_handler", alias4Design(templateNode.getTag()))) { - context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件订阅描述"); + putContext(templateNode.getTag(), "Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件订阅描述", context); } else { - context.put("Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件描述"); + putContext(templateNode.getTag(), "Comment", context.containsKey("Val2") ? context.get("Val2") : "todo: 领域事件描述", context); } - context.put("comment", context.get("Comment")); + putContext(templateNode.getTag(), "CommentEscaped", context.get("Comment").replace(PATTERN_LINE_BREAK, " "), context); return context; }); getLog().info("生成领域事件代码:" + path); @@ -492,21 +432,15 @@ public void renderDomainLayerAggregateFactory(String literalAggregateFactoryDecl String path = internalRenderGenericDesign(literalAggregateFactoryDeclaration, parentPath, templateNode, context -> { String entity = context.get("Name"); String Name = entity + "Factory"; - context.put("Name", Name); - context.put("name", Name.toLowerCase()); - context.put("Entity", entity); - context.put("entity", entity.toLowerCase()); - context.put("ENTITY", context.get("Entity")); - context.put("AggregateRoot", context.get("Entity")); - context.put("AGGREGATE_ROOT", context.get("Entity")); - context.put("aggregate_root", context.get("Entity")); - context.put("Aggregate", context.get("package")); - context.put("aggregate", context.get("package")); - context.put("Factory", context.get("Name")); - context.put("FACTORY", context.get("Factory")); - - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 聚合工厂描述"); - context.put("comment", context.get("Comment")); + putContext(templateNode.getTag(), "Name", Name, context); + putContext(templateNode.getTag(), "Factory", context.get("Name"), context); + putContext(templateNode.getTag(), "Aggregate", context.get("package"), context); + putContext(templateNode.getTag(), "Entity", entity, context); + putContext(templateNode.getTag(), "EntityVar", NamingUtils.toLowerCamelCase(entity), context); + putContext(templateNode.getTag(), "AggregateRoot", context.get("Entity"), context); + + putContext(templateNode.getTag(), "Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 聚合工厂描述", context); + putContext(templateNode.getTag(), "CommentEscaped", context.get("Comment").replace(PATTERN_LINE_BREAK, " "), context); return context; }); getLog().info("生成聚合工厂代码:" + path); @@ -516,26 +450,20 @@ public void renderDomainLayerAggregateFactory(String literalAggregateFactoryDecl * @param literalSpecificationDeclaration 文本化聚合工厂声明 AggregateRootEntityName * @param templateNode 模板配置 */ - public void renderDomainLayerSpecificaton(String literalSpecificationDeclaration, String parentPath, TemplateNode templateNode) throws IOException { + public void renderDomainLayerSpecification(String literalSpecificationDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析实体规约设计:" + literalSpecificationDeclaration); String path = internalRenderGenericDesign(literalSpecificationDeclaration, parentPath, templateNode, context -> { String entity = context.get("Name"); String Name = entity + "Specification"; - context.put("Name", Name); - context.put("name", Name.toLowerCase()); - context.put("Entity", entity); - context.put("entity", entity.toLowerCase()); - context.put("ENTITY", context.get("Entity")); - context.put("AggregateRoot", context.get("Entity")); - context.put("AGGREGATE_ROOT", context.get("Entity")); - context.put("aggregate_root", context.get("Entity")); - context.put("Aggregate", context.get("package")); - context.put("aggregate", context.get("package")); - context.put("Specification", context.get("Name")); - context.put("SPECIFICATION", context.get("Specification")); - - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 实体规约描述"); - context.put("comment", context.get("Comment")); + putContext(templateNode.getTag(), "Name", Name, context); + putContext(templateNode.getTag(), "Specification", context.get("Name"), context); + putContext(templateNode.getTag(), "Aggregate", context.get("package"), context); + putContext(templateNode.getTag(), "Entity", entity, context); + putContext(templateNode.getTag(), "EntityVar", NamingUtils.toLowerCamelCase(entity), context); + putContext(templateNode.getTag(), "AggregateRoot", context.get("Entity"), context); + + putContext(templateNode.getTag(), "Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 实体规约描述", context); + putContext(templateNode.getTag(), "CommentEscaped", context.get("Comment").replace(PATTERN_LINE_BREAK, " "), context); return context; }); getLog().info("生成实体规约代码:" + path); @@ -548,17 +476,14 @@ public void renderDomainLayerSpecificaton(String literalSpecificationDeclaration public void renderDomainLayerDomainService(String literalDomainServiceDeclaration, String parentPath, TemplateNode templateNode) throws IOException { getLog().info("解析领域服务设计:" + literalDomainServiceDeclaration); String path = internalRenderGenericDesign(literalDomainServiceDeclaration, parentPath, templateNode, context -> { - String name = context.get("Name"); - if (!name.endsWith("Svc") && !name.endsWith("Service")) { - name += "DomainService"; - } + String name = generateDomainServiceName(context.get("Name")); - context.put("Name", name); - context.put("DomainService", context.get("Name")); - context.put("DOMAIN_SERVICE", context.get("DomainService")); - context.put("Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 领域服务描述"); - context.put("comment", context.get("Comment")); + putContext(templateNode.getTag(), "Name", name, context); + putContext(templateNode.getTag(), "DomainService", context.get("Name"), context); + + putContext(templateNode.getTag(), "Comment", context.containsKey("Val1") ? context.get("Val1") : "todo: 领域服务描述", context); + putContext(templateNode.getTag(), "CommentEscaped", context.get("Comment").replace(PATTERN_LINE_BREAK, " "), context); return context; }); getLog().info("生成领域服务代码:" + path); @@ -580,14 +505,14 @@ public String internalRenderGenericDesign( TemplateNode templateNode, Function, Map> contextBuilder ) throws IOException { - String[] segments = TextUtils.splitWithTrim(escape(literalGenericDeclaration), DESIGN_PARAMS_SPLITTER); + String[] segments = TextUtils.splitWithTrim(escape(literalGenericDeclaration), PATTERN_DESIGN_PARAMS_SPLITTER); for (int i = 0; i < segments.length; i++) { segments[i] = unescape(segments[i]); } Map context = new HashMap<>(getEscapeContext()); for (int i = 0; i < segments.length; i++) { - context.put("Val" + i, segments[i]); - context.put("val" + i, segments[i].toLowerCase()); + putContext(templateNode.getTag(), "Val" + i, segments[i], context); + putContext(templateNode.getTag(), "val" + i, segments[i].toLowerCase(), context); } String name = segments[0].toLowerCase(); @@ -595,31 +520,15 @@ public String internalRenderGenericDesign( String path = NamingUtils.parentPackageName(segments[0]) .replace(".", File.separator); - context.put("Name", Name); - context.put("name", name); - context.put("path", path); - context.put("package", StringUtils.isEmpty(path) ? "" : ("." + path.replace(File.separator, "."))); + putContext(templateNode.getTag(), "Name", Name, context); + putContext(templateNode.getTag(), "name", name, context); + putContext(templateNode.getTag(), "path", path, context); + putContext(templateNode.getTag(), "package", StringUtils.isEmpty(path) ? "" : ("." + path.replace(File.separator, ".")), context); if (null != contextBuilder) { context = contextBuilder.apply(context); } PathNode pathNode = templateNode.clone().resolve(context); - return render(pathNode, parentPath, false); - } - - public String escape(String content) { - return content - .replace("\\\\", "${symbol_escape}") - .replace("\\:", "${symbol_colon}") - .replace("\\,", "${symbol_comma}") - .replace("\\;", "${symbol_semicolon}"); - } - - public String unescape(String escape) { - return escape - .replace("${symbol_escape}", "\\") - .replace("${symbol_colon}", ":") - .replace("${symbol_comma}", ",") - .replace("${symbol_semicolon}", ";"); + return forceRender(pathNode, parentPath); } } diff --git a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java index 98bb6e1..af9f04b 100644 --- a/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java +++ b/cap4j-ddd-codegen-maven-plugin/src/main/java/org/netcorepal/cap4j/ddd/codegen/GenEntityMojo.java @@ -1,21 +1,29 @@ package org.netcorepal.cap4j.ddd.codegen; +import com.alibaba.fastjson.JSON; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; -import org.netcorepal.cap4j.ddd.codegen.misc.*; - -import java.io.*; -import java.nio.charset.Charset; +import org.netcorepal.cap4j.ddd.codegen.misc.Inflector; +import org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils; +import org.netcorepal.cap4j.ddd.codegen.misc.SqlSchemaUtils; +import org.netcorepal.cap4j.ddd.codegen.misc.TextUtils; +import org.netcorepal.cap4j.ddd.codegen.template.PathNode; +import org.netcorepal.cap4j.ddd.codegen.template.TemplateNode; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; import static org.netcorepal.cap4j.ddd.codegen.misc.NamingUtils.*; -import static org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils.writeLine; +import static org.netcorepal.cap4j.ddd.codegen.misc.SourceFileUtils.*; import static org.netcorepal.cap4j.ddd.codegen.misc.SqlSchemaUtils.*; /** @@ -25,7 +33,13 @@ * @date 2022-02-16 */ @Mojo(name = "gen-entity") -public class GenEntityMojo extends MyAbstractMojo { +public class GenEntityMojo extends GenArchMojo { + static final String DEFAULT_SCHEMA_PACKAGE = "meta"; + static final String DEFAULT_SPEC_PACKAGE = "specs"; + static final String DEFAULT_FAC_PACKAGE = "factory"; + static final String DEFAULT_ENUM_PACKAGE = "enums"; + static final String DEFAULT_DOMAIN_EVENT_PACKAGE = "events"; + static final String DEFAULT_SCHEMA_BASE_CLASS_NAME = "Schema"; public Map> TableMap = new HashMap<>(); public Map TableModuleMap = new HashMap<>(); @@ -42,12 +56,90 @@ public class GenEntityMojo extends MyAbstractMojo { public String dbType = "mysql"; - public final String PATTERN_SPLITTER = "[\\,\\;]"; + public String aggregatesPath = ""; + public String schemaPath = ""; + public String subscriberPath = ""; + + public Map> templateNodeMap = new HashMap<>(); @Override public void execute() throws MojoExecutionException, MojoFailureException { - getLog().info("当前默认编码:" + Charset.defaultCharset().name()); + super.execute(); + genEntity(); + } + + public String alias4Design(String name) { + switch (name.toLowerCase()) { + case "entity": + case "aggregate": + case "entities": + case "aggregates": + return "aggregate"; + case "schema": + case "schemas": + return "schema"; + case "enum": + case "enums": + return "enum"; + case "enumitem": + case "enum_item": + return "enum_item"; + case "factories": + case "factory": + case "fac": + return "factory"; + case "specifications": + case "specification": + case "specs": + case "spec": + case "spe": + return "specification"; + case "domain_events": + case "domain_event": + case "d_e": + case "de": + return "domain_event"; + case "domain_event_handlers": + case "domain_event_handler": + case "d_e_h": + case "deh": + case "domain_event_subscribers": + case "domain_event_subscriber": + case "d_e_s": + case "des": + return "domain_event_handler"; + case "domain_service": + case "service": + case "svc": + return "domain_service"; + default: + return name; + } + } + + @Override + public void renderTemplate(List templateNodes, String parentPath) throws IOException { + + for (TemplateNode templateNode : + templateNodes) { + String alias = alias4Design(templateNode.getTag()); + if ("aggregate".equals(alias)) { + aggregatesPath = parentPath; + } else if ("schema_base".equals(alias)) { + schemaPath = parentPath; + } else if ("domain_event_handler".equals(alias)) { + subscriberPath = parentPath; + } + if (!templateNodeMap.containsKey(alias)) { + templateNodeMap.put(alias, new ArrayList<>()); + } + templateNodeMap.get(alias).add(templateNode); + } + } + + public void genEntity() { + SqlSchemaUtils.mojo = this; getLog().info("数据库连接:" + connectionString); getLog().info("数据库账号:" + user); getLog().info("数据库密码:" + pwd); @@ -60,7 +152,13 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("只读字段:" + readonlyFields); getLog().info("忽略字段:" + ignoreFields); getLog().info(""); - getLog().info("主键ID生成器:" + idGenerator); + // 项目结构解析 + this.basePackage = StringUtils.isNotBlank(this.basePackage) + ? this.basePackage + : SourceFileUtils.resolveDefaultBasePackage(getDomainModulePath()); + getLog().info("实体基类:" + entityBaseClass); + getLog().info("聚合根标注注解:" + getAggregateRootAnnotation()); + getLog().info("主键ID生成器:" + (StringUtils.isBlank(idGenerator) ? "自增" : idGenerator)); getLog().info("日期类型映射Java包:" + datePackage4Java); getLog().info("枚举值Java字段名称:" + enumValueField); getLog().info("枚举名Java字段名称:" + enumNameField); @@ -70,58 +168,28 @@ public void execute() throws MojoExecutionException, MojoFailureException { typeRemapping.entrySet()) { getLog().info(" " + entry.getKey() + " <-> " + entry.getValue()); } - getLog().info(""); - this.getLog().info("开始生成实体代码"); - SqlSchemaUtils.mojo = this; - this.dbType = recognizeDbType(connectionString); - processSqlDialet(this.dbType); - - // 项目结构解析 - String absoluteCurrentDir, projectDir, domainModulePath, applicationModulePath, adapterModulePath; - absoluteCurrentDir = new File("").getAbsolutePath(); - if (multiModule) { - projectDir = new File(absoluteCurrentDir + File.separator + "pom.xml").exists() - ? absoluteCurrentDir - : new File(absoluteCurrentDir).getParent(); - - domainModulePath = Arrays.stream(new File(projectDir).listFiles()) - .filter(path -> path.getAbsolutePath().endsWith(moduleNameSuffix4Domain)) - .findFirst().get().getAbsolutePath(); - applicationModulePath = Arrays.stream(new File(projectDir).listFiles()) - .filter(path -> path.getAbsolutePath().endsWith(moduleNameSuffix4Application)) - .findFirst().get().getAbsolutePath(); - adapterModulePath = Arrays.stream(new File(projectDir).listFiles()) - .filter(path -> path.getAbsolutePath().endsWith(moduleNameSuffix4Adapter)) - .findFirst().get().getAbsolutePath(); - } else { - projectDir = absoluteCurrentDir; - domainModulePath = absoluteCurrentDir; - applicationModulePath = absoluteCurrentDir; - adapterModulePath = absoluteCurrentDir; - } - String basePackage = StringUtils.isNotBlank(this.basePackage) - ? this.basePackage - : SourceFileUtils.resolveDefaultBasePackage(domainModulePath); - getLog().info(multiModule ? "多模块项目" : "单模块项目"); - getLog().info("项目目录:" + projectDir); - getLog().info("适配层目录:" + adapterModulePath); - getLog().info("应用层目录:" + applicationModulePath); - getLog().info("领域层目录:" + domainModulePath); - getLog().info("基础包名:" + basePackage); - getLog().info("实体基类:" + entityBaseClass); - getLog().info("聚合根标注注解:" + getAggregateRootAnnotation()); - - if (StringUtils.isBlank(entityMetaInfoClassOutputPackage)) { - entityMetaInfoClassOutputPackage = "domain._share.meta"; + getLog().info("生成Schema:" + (generateSchema ? "是" : "否")); + if (generateSchema) { + getLog().info(" 输出模式:" + getEntitySchemaOutputMode()); + getLog().info(" 输出路径:" + getEntitySchemaOutputPackage()); } + getLog().info(""); + getLog().info(""); // 数据库解析 + this.dbType = recognizeDbType(connectionString); + processSqlDialet(this.dbType); List> tables = resolveTables(connectionString, user, pwd); List> columns = resolveColumns(connectionString, user, pwd); Map> relations = new HashMap<>(); Map tablePackageMap = new HashMap<>(); + getLog().info("----------------解析数据库表----------------"); getLog().info(""); - getLog().info("待解析数据库表:"); + getLog().info("数据库类型:" + this.dbType); + int maxTableNameLength = tables.stream() + .map(table -> getTableName(table).length()) + .max(Comparator.naturalOrder()) + .orElse(20); for (Map table : tables) { List> tableColumns = columns.stream().filter(column -> isColumnInTable(column, table)) @@ -129,8 +197,10 @@ public void execute() throws MojoExecutionException, MojoFailureException { .collect(Collectors.toList()); TableMap.put(getTableName(table), table); ColumnsMap.put(getTableName(table), tableColumns); - - getLog().info(String.format("%20s : (%s)", + getLog().info(String.format("%" + maxTableNameLength + "s %s", + "", + getComment(table))); + getLog().info(String.format("%" + maxTableNameLength + "s : (%s)", getTableName(table), String.join(", ", tableColumns.stream() .map(column -> @@ -161,7 +231,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { relations.get(entry.getKey()).putAll(entry.getValue()); } } - tablePackageMap.put(getTableName(table), resolvePackage(table, basePackage, domainModulePath)); + tablePackageMap.put(getTableName(table), resolveEntityFullPackage(table, basePackage, getDomainModulePath())); getLog().info("结束解析表关系:" + getTableName(table)); getLog().info(""); } @@ -174,11 +244,17 @@ public void execute() throws MojoExecutionException, MojoFailureException { List> tableColumns = ColumnsMap.get(getTableName(table)); for (Map column : tableColumns) { - if (hasEnum(column)) { + if (hasEnum(column) && !isIgnoreColumn(column)) { Map enumConfig = getEnum(column); if (enumConfig.size() > 0) { EnumConfigMap.put(getType(column), enumConfig); - EnumPackageMap.put(getType(column), basePackage + "." + getEntityPackage(getTableName(table)) + ".enums"); + String enumPackage = templateNodeMap.containsKey("enum") && templateNodeMap.get("enum").size() > 0 + ? templateNodeMap.get("enum").get(0).getName() + : "enums"; + if (StringUtils.isNotBlank(enumPackage)) { + enumPackage = "." + enumPackage; + } + EnumPackageMap.put(getType(column), basePackage + "." + getEntityPackage(getTableName(table)) + enumPackage); EnumTableNameMap.put(getType(column), getTableName(table)); } } @@ -193,7 +269,13 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info(""); for (Map.Entry> entry : EnumConfigMap.entrySet()) { try { - writeEnumSourceFile(entry.getValue(), entry.getKey(), EnumPackageMap.get(entry.getKey()), domainModulePath, enumValueField, enumNameField); + writeEnumSourceFile( + entry.getValue(), + entry.getKey(), + enumValueField, + enumNameField, + tablePackageMap, + getDomainModulePath()); } catch (IOException e) { e.printStackTrace(); getLog().error(e); @@ -208,7 +290,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info(""); if (generateSchema) { try { - writeBaseSechemaSourceFile(basePackage, domainModulePath); + writeSchemaBaseSourceFile(getDomainModulePath()); } catch (IOException e) { e.printStackTrace(); getLog().error(e); @@ -218,15 +300,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { TableMap.values()) { List> tableColumns = ColumnsMap.get(getTableName(table)); try { - writeEntitySourceFile(table, tableColumns, tablePackageMap, relations, basePackage, domainModulePath); - } catch (IOException e) { - e.printStackTrace(); - getLog().error(e); - } - } - if (generateBuild) { - try { - writeEntityBuilderSourceFile(basePackage, applicationModulePath, tablePackageMap, relations); + buildEntitySourceFile(table, tableColumns, tablePackageMap, relations, basePackage, getDomainModulePath()); } catch (IOException e) { e.printStackTrace(); getLog().error(e); @@ -236,6 +310,80 @@ public void execute() throws MojoExecutionException, MojoFailureException { getLog().info(""); } + /** + * 获取聚合根文件目录 + * + * @return + */ + public String getAggregatesPath() { + if (StringUtils.isNotBlank(aggregatesPath)) { + return aggregatesPath; + } + return resolveDirectory( + getDomainModulePath(), + basePackage + "." + AGGREGATE_PACKAGE + ); + } + + /** + * 获取聚合根包名,不包含basePackage + * + * @return + */ + public String getAggregatesPackage() { + return SourceFileUtils.resolvePackage(getAggregatesPath() + File.separator + "X.java") + .substring(basePackage.length() + 1); + } + + /** + * 获取实体schema文件目录 + * + * @return + */ + public String getSchemaPath() { + if (StringUtils.isNotBlank(schemaPath)) { + return schemaPath; + } + return resolveDirectory( + getDomainModulePath(), + basePackage + "." + getEntitySchemaOutputPackage() + ); + } + + /** + * 获取schema包名,不包含basePackage + * + * @return + */ + public String getSchemaPackage() { + return SourceFileUtils.resolvePackage(getSchemaPath() + File.separator + "X.java") + .substring(basePackage.length() + 1); + } + + /** + * 获取领域事件订阅者文件目录 + * + * @return + */ + public String getSubscriberPath() { + if (StringUtils.isNotBlank(subscriberPath)) { + return subscriberPath; + } + return resolveDirectory( + getApplicationModulePath(), + basePackage + "." + DOMAIN_EVENT_SUBSCRIBER_PACKAGE + ); + } + + /** + * 获取领域事件订阅者包名,不包含basePackage + * + * @return + */ + public String getSubscriberPackage() { + return SourceFileUtils.resolvePackage(getSubscriberPath() + File.separator + "X.java") + .substring(basePackage.length() + 1); + } /** * 获取模块 @@ -286,7 +434,7 @@ public String getAggregate(String tableName) { getLog().info("尝试父表聚合:" + getTableName(table) + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); } if (StringUtils.isBlank(aggregate)) { - aggregate = toSnakeCase(getEntityJavaType(getTableName(table))); + aggregate = toSnakeCase(getEntityJavaType(getTableName(table))); } getLog().info("聚合解析结果:" + tableName + " " + (StringUtils.isBlank(aggregate) ? "[缺失]" : aggregate)); return aggregate; @@ -303,7 +451,7 @@ public String getAggregate(String tableName) { public String getAggregateWithModule(String tableName) { String module = getModule(tableName); if (StringUtils.isNotBlank(module)) { - return module + "." + getAggregate(tableName); + return SourceFileUtils.concatPackage(module, getAggregate(tableName)); } else { return getAggregate(tableName); } @@ -333,7 +481,7 @@ public String getEntityJavaType(String tableName) { } /** - * 获取实体类 package + * 获取实体类所在包,不包含basePackage * * @param tableName * @return @@ -341,9 +489,10 @@ public String getEntityJavaType(String tableName) { public String getEntityPackage(String tableName) { String module = getModule(tableName); String aggregate = getAggregate(tableName); - String packageName = ("domain.aggregates" - + (StringUtils.isNotBlank(module) ? "." + module : "") - + (StringUtils.isNotBlank(aggregate) ? "." + aggregate.toLowerCase() : "") + String packageName = SourceFileUtils.concatPackage( + getAggregatesPackage() + , module + , aggregate.toLowerCase() ); return packageName; } @@ -390,6 +539,115 @@ public boolean isIgnoreColumn(Map column) { return false; } + /** + * 判断是否需要生成实体字段 + * + * @param table + * @param column + * @param relations + * @return + */ + public boolean isColumnNeedGenerate(Map table, Map column, Map> relations) { + + String tableName = getTableName(table); + String columnName = getColumnName(column); + if (isIgnoreColumn(column)) { + return false; + } + if (isReservedColumn(column)) { + return false; + } + + if (!isAggregateRoot(table)) { + if (columnName.equalsIgnoreCase(getParent(table) + "_id")) { + return false; + } + } + + if (relations.containsKey(tableName)) { + boolean skip = false; + for (Map.Entry entry : relations.get(tableName).entrySet()) { + String[] refInfos = entry.getValue().split(";"); + if (("ManyToOne".equalsIgnoreCase(refInfos[0]) || "OneToOne".equalsIgnoreCase(refInfos[0])) && columnName.equalsIgnoreCase(refInfos[1])) { + skip = true; + break; + } + } + if (skip) { + return false; + } + } + return true; + } + + /** + * 获取Id列 + * + * @param columns + * @return + */ + private Map getIdColumn(List> columns) { + return columns.stream().filter(column -> Objects.equals(idField, getColumnName(column))) + .findFirst().orElse(null); + } + + /** + * 生成字段注释 + * + * @param column + * @return + */ + public List generateFieldComment(Map column) { + List comments = new ArrayList<>(); + String fieldName = getColumnName(column); + String fieldType = getColumnJavaType(column); + comments.add("/**"); + for (String comment : getComment(column).split(PATTERN_LINE_BREAK)) { + if (StringUtils.isEmpty(comment)) { + continue; + } + comments.add(" * " + comment); + if (hasEnum(column)) { + getLog().info("获取枚举java类型:" + fieldName + " -> " + fieldType); + Map enumMap = EnumConfigMap.get(fieldType); + if (enumMap == null) { + enumMap = EnumConfigMap.get(getType(column)); + } + if (enumMap != null) { + comments.addAll(enumMap.entrySet().stream() + .map(c -> " * " + c.getKey() + ":" + c.getValue()[0] + ":" + c.getValue()[1]) + .collect(Collectors.toList()) + ); + } + } + } + if (generateDbType) { + comments.add(" * " + getColumnDbType(column)); + } + comments.add(" */"); + return comments; + } + + /** + * 解析实体类全路径包名,含basePackage + * + * @param table + * @param basePackage + * @param baseDir + * @return + */ + public String resolveEntityFullPackage(Map table, String basePackage, String baseDir) { + String tableName = getTableName(table); + String simpleClassName = getEntityJavaType(tableName); + String packageName = SourceFileUtils.concatPackage(basePackage, getEntityPackage(tableName)); + +// Optional existFilePath = SourceFileUtils.findJavaFileBySimpleClassName(baseDir, simpleClassName); +// if (existFilePath.isPresent()) { +// packageName = SourceFileUtils.resolvePackage(existFilePath.get().getAbsolutePath()); +// } + return packageName; + } + /** * 关系格式 table1 table2 relation;join_column;[inverse_join_column;][table;][LAZY;] *

@@ -487,9 +745,8 @@ public Map> resolveRelationTable(Map return result; } - public boolean readCustomerSourceFile(String filePath, List importLines, List annotationLines, List customerLines) throws IOException { + public boolean readEntityCustomerSourceFile(String filePath, List importLines, List annotationLines, List customerLines) throws IOException { if (FileUtils.fileExists(filePath)) { - String simpleClassName = SourceFileUtils.resolveSimpleClassName(filePath); String content = FileUtils.fileRead(filePath); List lines = Arrays.asList(content.replace("\r\n", "\n").split("\n")); @@ -536,7 +793,7 @@ public boolean readCustomerSourceFile(String filePath, List importLines, return true; } - public void processImportLines(Map table, String basePackage, List importLines, String content) { + public void processImportLines(Map table, List importLines, String content) { boolean importEmpty = importLines.size() == 0; if (importEmpty) { importLines.add(""); @@ -544,8 +801,9 @@ public void processImportLines(Map table, String basePackage, Li List entityClassExtraImports = getEntityClassExtraImports(); if (isValueObject(table)) { - if (entityClassExtraImports.indexOf("org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate") > 0) { - entityClassExtraImports.add(entityClassExtraImports.indexOf("org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate"), "org.netcorepal.cap4j.ddd.domain.aggregate.ValueObject"); + int idx = entityClassExtraImports.indexOf("org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate"); + if (idx > 0) { + entityClassExtraImports.add(idx, "org.netcorepal.cap4j.ddd.domain.aggregate.ValueObject"); } else { entityClassExtraImports.add("org.netcorepal.cap4j.ddd.domain.aggregate.ValueObject"); } @@ -561,14 +819,14 @@ public void processImportLines(Map table, String basePackage, Li } importLines.add(""); importLines.add("/**"); - for (String comment : getComment(table).split("[\\r\\n]")) { + for (String comment : getComment(table).split(PATTERN_LINE_BREAK)) { if (StringUtils.isEmpty(comment)) { continue; } importLines.add(" * " + comment); } importLines.add(" *"); - // importLines.add(" * " + getComment(table).replaceAll("[\\r\\n]", " ")); + // importLines.add(" * " + getComment(table).replaceAll(PATTERN_LINE_BREAK, " ")); importLines.add(" * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); importLines.add(" * 警告:请勿手工修改该文件的字段声明,重新生成会覆盖字段声明"); importLines.add(" * @author cap4j-ddd-codegen"); @@ -614,7 +872,7 @@ public void processAnnotationLines(Map table, List 0); if (StringUtils.isNotBlank(getAggregateRootAnnotation())) { if (isAggregateRoot(table)) { @@ -624,13 +882,7 @@ public void processAnnotationLines(Map table, List table, List table, String basePackage, String baseDir) { - String tableName = getTableName(table); - String simpleClassName = getEntityJavaType(tableName); - String packageName = basePackage + "." + getEntityPackage(tableName); - - Optional existFilePath = SourceFileUtils.findJavaFileBySimpleClassName(baseDir, simpleClassName); - if (existFilePath.isPresent()) { - packageName = SourceFileUtils.resolvePackage(existFilePath.get().getAbsolutePath()); - } - return packageName; - } - - public void writeEntitySourceFile(Map table, List> columns, Map tablePackageMap, Map> relations, String basePackage, String baseDir) throws IOException { + public void buildEntitySourceFile(Map table, List> columns, Map tablePackageMap, Map> relations, String basePackage, String baseDir) throws IOException { String tableName = getTableName(table); if (isIgnoreTable(table)) { getLog().info("跳过忽略表:" + tableName); @@ -686,21 +926,41 @@ public void writeEntitySourceFile(Map table, List enums = new ArrayList<>(); + List importLines = new ArrayList<>(); + List annotationLines = new ArrayList<>(); + List customerLines = new ArrayList<>(); + if (!readEntityCustomerSourceFile(filePath, importLines, annotationLines, customerLines)) { + getLog().warn("文件被改动,无法自动更新!" + filePath); + return; + } + + processAnnotationLines(table, columns, annotationLines); + String mainSource = writeEntityClass(table, columns, tablePackageMap, relations, enums, annotationLines, customerLines); + processImportLines(table, importLines, mainSource); + + getLog().info("开始生成实体文件:" + filePath); + FileUtils.fileWrite(filePath, outputEncoding, + "package " + entityFullPackage + ";\n" + + String.join("\n", importLines) + "\n" + + mainSource + "\n" + ); + if (generateSchema) { + writeSchemaSourceFile(table, columns, tablePackageMap, relations, basePackage, baseDir); + } if (isAggregateRoot(table)) { - new File(SourceFileUtils.resolveDirectory(baseDir, packageName + ".enums")).mkdirs(); - new File(SourceFileUtils.resolveDirectory(baseDir, packageName + ".events")).mkdirs(); - new File(SourceFileUtils.resolveDirectory(baseDir, packageName + ".schemas")).mkdirs(); - new File(SourceFileUtils.resolveDirectory(baseDir, packageName + ".specs")).mkdirs(); - new File(SourceFileUtils.resolveDirectory(baseDir, packageName + ".factory")).mkdirs(); if (hasFactory(table)) { - writeFactorySourceFile(table, baseDir); + writeFactorySourceFile(table, tablePackageMap, baseDir); } if (hasSpecification(table)) { - writeSpecificationSourceFile(table, baseDir); + writeSpecificationSourceFile(table, tablePackageMap, baseDir); } if (hasDomainEvent(table)) { List domainEvents = getDomainEvent(table); @@ -709,58 +969,23 @@ public void writeEntitySourceFile(Map table, List 1 ? segments[1] : "todo: 领域事件说明"; - try { - writeDomainEventSourceFile(table, domainEventClassName, domainEventDescription, baseDir); - } catch (IOException ex) { - getLog().error("领域事件" + domainEventClassName + "代码生成异常"); - throw ex; - } + writeDomainEventSourceFile(table, tablePackageMap, domainEventClassName, domainEventDescription, baseDir); } } } - String filePath = SourceFileUtils.resolveSourceFile(baseDir, packageName, simpleClassName); - - List enums = new ArrayList<>(); - List importLines = new ArrayList<>(); - List annotationLines = new ArrayList<>(); - List customerLines = new ArrayList<>(); - if (!readCustomerSourceFile(filePath, importLines, annotationLines, customerLines)) { - getLog().warn("文件被改动,无法自动更新!" + filePath); - return; - } - - getLog().info("开始生成实体文件:" + filePath); - processAnnotationLines(table, columns, annotationLines); - String mainSource = generateEntityClassMainSource(table, columns, tablePackageMap, relations, enums, annotationLines, customerLines); - processImportLines(table, basePackage, importLines, mainSource); - - BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); - writeLine(out, "package " + packageName + ";"); - importLines.forEach(line -> writeLine(out, line)); - writeLine(out, mainSource); - out.close(); - if (generateSchema) { - writeSchemaSourceFile(table, columns, tablePackageMap, relations, basePackage, baseDir); - } - } - - private Map getIdColumn(List> columns) { - return columns.stream().filter(column -> Objects.equals(idField, getColumnName(column))) - .findFirst().orElse(null); } - private String generateEntityClassMainSource(Map table, List> columns, Map tablePackageMap, Map> relations, List enums, List annotationLines, List customerLines) throws IOException { + private String writeEntityClass(Map table, List> columns, Map tablePackageMap, Map> relations, List enums, List annotationLines, List customerLines) throws IOException { String tableName = getTableName(table); - String simpleClassName = getEntityJavaType(tableName); + String entityType = getEntityJavaType(tableName); StringWriter stringWriter = new StringWriter(); BufferedWriter out = new BufferedWriter(stringWriter); annotationLines.forEach(line -> writeLine(out, line)); - writeLine(out, "public class " + simpleClassName + (StringUtils.isNotBlank(entityBaseClass) ? " extends " + entityBaseClass : "") + (isValueObject(table) ? " implements ValueObject" : "") + " {"); + writeLine(out, "public class " + entityType + + (StringUtils.isNotBlank(entityBaseClass) ? " extends " + entityBaseClass : "") + + (isValueObject(table) ? " implements ValueObject" : "") + " {"); if (customerLines.size() > 0) { customerLines.forEach(line -> writeLine(out, line)); } else { @@ -784,18 +1009,19 @@ private String generateEntityClassMainSource(Map table, List table, List table, Map column, Map> relations) { - - String tableName = getTableName(table); - String columnName = getColumnName(column); - if (isIgnoreColumn(column)) { - return false; - } - if (isReservedColumn(column)) { - return false; - } - - if (!isAggregateRoot(table)) { - if (columnName.equalsIgnoreCase(getParent(table) + "_id")) { - return false; - } - } - - if (relations.containsKey(tableName)) { - boolean skip = false; - for (Map.Entry entry : relations.get(tableName).entrySet()) { - String[] refInfos = entry.getValue().split(";"); - if (("ManyToOne".equalsIgnoreCase(refInfos[0]) || "OneToOne".equalsIgnoreCase(refInfos[0])) && columnName.equalsIgnoreCase(refInfos[1])) { - skip = true; - break; - } - } - if (skip) { - return false; - } - } - return true; - } - - public void writeEntityBuilderSourceFile(String basePackage, String baseDir, Map tablePackageMap, Map> relations) throws IOException { - String packageName = basePackage + "." + entityMetaInfoClassOutputPackage; - - new File(SourceFileUtils.resolveDirectory(baseDir, packageName)).mkdirs(); - String filePath = SourceFileUtils.resolveSourceFile(baseDir, packageName, "EntityBuilder"); - - BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); - writeLine(out, "package " + packageName + ";"); - for (Map.Entry> tableEntry : TableMap.entrySet()) { - Map table = tableEntry.getValue(); - if (isIgnoreTable(table)) { - continue; - } - if (hasRelation(table)) { - continue; - } - String tableName = getTableName(table); - String simpleClassName = getEntityJavaType(tableName); - writeLine(out, "import " + tablePackageMap.get(tableName) + "." + simpleClassName + ";"); - } - writeLine(out, ""); - writeLine(out, "/**"); - writeLine(out, " * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); - writeLine(out, " * 警告:请勿手工修改该文件,重新生成会覆盖该文件"); - writeLine(out, " * @author cap4j-ddd-codegen"); - writeLine(out, " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); - writeLine(out, " */"); - writeLine(out, "public class EntityBuilder {"); - for (Map.Entry> tableEntry : TableMap.entrySet()) { - Map table = tableEntry.getValue(); - if (isIgnoreTable(table)) { - continue; - } - if (hasRelation(table)) { - continue; - } - writeBuildEntityMethod(out, table, ColumnsMap.get(tableEntry.getKey()), relations); - } - writeLine(out, ""); - writeLine(out, "}"); - out.flush(); - out.close(); - } - - public void writeBuildEntityMethod(BufferedWriter out, Map table, List> columns, Map> relations) { - - String tableName = getTableName(table); - String simpleClassName = getEntityJavaType(tableName); - writeLine(out, ""); - writeLine(out, " /**"); - for (String comment : getComment(table).split("[\\r\\n]")) { - if (StringUtils.isEmpty(comment)) { - continue; - } - writeLine(out, " * " + comment); - } - writeLine(out, " * @param fieldFillNull 字段默认值是否为空"); - writeLine(out, " * @return"); - writeLine(out, " */"); - writeLine(out, " public static " + simpleClassName + "." + simpleClassName + "Builder build" + simpleClassName + "(boolean fieldFillNull) {"); - writeLine(out, " return " + simpleClassName + ".builder()"); - writeLine(out, " ." + idField + "(null)"); - for (Map column : columns) { - if (!needGenerateField(table, column, relations)) { - continue; - } - String defaultJavaLiteral = getColumnDefaultJavaLiteral(column); - if (StringUtils.isBlank(defaultJavaLiteral)) { - defaultJavaLiteral = "null"; - } - if ("Byte".equalsIgnoreCase(getColumnJavaType(column))) { - defaultJavaLiteral = "(byte)" + defaultJavaLiteral; - } - writeLine(out, " ." + toLowerCamelCase(getColumnName(column)) + "(fieldFillNull ? null : " + defaultJavaLiteral + ")"); - - } - if (hasColumn(versionField, columns)) { - writeLine(out, " ." + toLowerCamelCase(versionField) + "(fieldFillNull ? null : 0)"); - } - writeLine(out, " ;"); - writeLine(out, " }"); - } - public void writeColumnProperty(BufferedWriter out, Map table, Map column, Map> relations, List enums) { String columnName = getColumnName(column); String columnJavaType = getColumnJavaType(column); - if (!needGenerateField(table, column, relations)) { + if (!isColumnNeedGenerate(table, column, relations)) { return; } @@ -977,7 +1087,7 @@ public void writeColumnProperty(BufferedWriter out, Map table, M } writeLine(out, ""); - writeColumnComment(out, column); + writeFieldComment(out, column); if (hasEnum(column)) { enums.add(columnJavaType); writeLine(out, " @Convert(converter = " + columnJavaType + ".Converter.class)"); @@ -998,31 +1108,10 @@ public void writeColumnProperty(BufferedWriter out, Map table, M } } - public void writeColumnComment(BufferedWriter out, Map column) { - String columnName = getColumnName(column); - String columnJavaType = getColumnJavaType(column); - writeLine(out, " /**"); - for (String comment : getComment(column).split("[\\r\\n]")) { - if (StringUtils.isEmpty(comment)) { - continue; - } - writeLine(out, " * " + comment); - if (hasEnum(column)) { - getLog().info("获取枚举java类型:" + columnName + " -> " + columnJavaType); - Map enumMap = EnumConfigMap.get(columnJavaType); - if (enumMap == null) { - enumMap = EnumConfigMap.get(getType(column)); - } - if (enumMap != null) { - writeLine(out, " * " + String.join(";", enumMap.entrySet().stream() - .map(c -> c.getKey() + ":" + c.getValue()[0] + ":" + c.getValue()[1]).collect(Collectors.toList()))); - } - } - } - if (generateDbType) { - writeLine(out, " * " + getColumnDbType(column)); + public void writeFieldComment(BufferedWriter out, Map column) { + for (String line : generateFieldComment(column)) { + writeLine(out, " " + line); } - writeLine(out, " */"); } public void writeRelationProperty(BufferedWriter out, Map table, Map> relations, Map tablePackageMap) { @@ -1149,298 +1238,673 @@ public void writeRelationProperty(BufferedWriter out, Map table, } } - public void writeFactorySourceFile(Map table, String baseDir) throws IOException { + public void writeFactorySourceFile(Map table, Map tablePackageMap, String baseDir) throws IOException { + String tag = "factory"; String tableName = getTableName(table); - String entityPackage = basePackage + "." + getEntityPackage(tableName); - String simpleClassName = getEntityJavaType(tableName); - String filePath = SourceFileUtils.resolveSourceFile(baseDir, entityPackage + ".factory", simpleClassName + "Payload"); - if (!FileUtils.fileExists(filePath)) { - getLog().info("开始生成工厂负载:" + filePath); - BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); - writeLine(out, - "package " + entityPackage + ".factory;\n" + - "\n" + - "import " + entityPackage + "." + simpleClassName + ";\n" + - "import lombok.AllArgsConstructor;\n" + - "import lombok.Builder;\n" + - "import lombok.Data;\n" + - "import lombok.NoArgsConstructor;\n" + - "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + - "import org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload;\n" + - "\n" + - "/**\n" + - " * " + simpleClassName + "工厂负载\n" + - " * todo: 工厂负载描述\n" + - " *\n" + - " * @author cap4j-ddd-codegen\n" + - " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) + "\n" + - " */\n" + - "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + simpleClassName + "Payload\", type = Aggregate.TYPE_FACTORY_PAYLOAD, description = \"\")\n" + - "@Data\n" + - "@Builder\n" + - "@NoArgsConstructor\n" + - "@AllArgsConstructor\n" + - "public class " + simpleClassName + "Payload implements AggregatePayload<" + simpleClassName + "> {\n" + - " private Long id;\n" + - "}" - ); - out.flush(); - out.close(); - } - - filePath = SourceFileUtils.resolveSourceFile(baseDir, entityPackage + ".factory", simpleClassName + "Factory"); - if (!FileUtils.fileExists(filePath)) { - getLog().info("开始生成聚合工厂:" + filePath); - BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); - writeLine(out, - "package " + entityPackage + ".factory;\n" + - "\n" + - "import " + entityPackage + "." + simpleClassName + ";\n" + - "import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory;\n" + - "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + - "import org.springframework.stereotype.Service;\n" + - "\n" + - "/**\n" + - " * " + simpleClassName + "聚合工厂\n" + - " * todo: 聚合工厂描述\n" + - " *\n" + - " * @author cap4j-ddd-codegen\n" + - " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) + "\n" + - " */\n" + - "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + simpleClassName + "Factory\", type = Aggregate.TYPE_FACTORY, description = \"\")\n" + - "@Service\n" + - "public class " + simpleClassName + "Factory implements AggregateFactory<" + simpleClassName + "Payload, " + simpleClassName + "> {\n" + - "\n" + - " @Override\n" + - " public " + simpleClassName + " create(" + simpleClassName + "Payload payload) {\n" + - "\n" + - " return " + simpleClassName + ".builder()\n" + - "\n" + - " .build();\n" + - " }\n" + - "}" - ); - out.flush(); - out.close(); + String aggregate = getAggregateWithModule(tableName); + + String entityFullPackage = tablePackageMap.get(tableName); + String entityType = getEntityJavaType(tableName); + String entityVar = toLowerCamelCase(entityType); + + + Map context = getEscapeContext(); + putContext(tag, "Name", entityType + "Factory", context); + putContext(tag, "Factory", context.get("Name"), context); + putContext(tag, "templatePackage", refPackage(getAggregatesPackage()), context); + putContext(tag, "package", refPackage(aggregate), context); + putContext(tag, "path", aggregate.replace(".", File.separator), context); + putContext(tag, "Aggregate", aggregate, context); + putContext(tag, "Comment", "", context); + putContext(tag, "CommentEscaped", "", context); + putContext(tag, "entityPackage", SourceFileUtils.refPackage(entityFullPackage, basePackage), context); + putContext(tag, "Entity", entityType, context); + putContext(tag, "AggregateRoot", context.get("Entity"), context); + putContext(tag, "EntityVar", entityVar, context); + + List factoryTemplateNodes = templateNodeMap.containsKey(tag) + ? templateNodeMap.get(tag) + : Arrays.asList(getDefaultFactoryPayloadTemplateNode(), getDefaultFactoryTemplateNode()); + try { + for (TemplateNode templateNode : factoryTemplateNodes) { + PathNode pathNode = templateNode.clone().resolve(context); + String path = forceRender( + pathNode, + SourceFileUtils.resolveDirectory( + baseDir, + concatPackage(basePackage, context.get("templatePackage")) + ) + ); + getLog().info(SourceFileUtils.resolveDirectory( + baseDir, + concatPackage(basePackage, context.get("templatePackage")) + )); + getLog().info("开始生成聚合工厂:" + path); + } + } catch (IOException e) { + getLog().error("聚合工厂模板文件写入失败!", e); } } - public void writeSpecificationSourceFile(Map table, String baseDir) throws IOException { + public void writeSpecificationSourceFile(Map table, Map tablePackageMap, String baseDir) throws IOException { + String tag = "specification"; String tableName = getTableName(table); - String entityPackage = basePackage + "." + getEntityPackage(tableName); - String simpleClassName = getEntityJavaType(tableName); - String filePath = SourceFileUtils.resolveSourceFile(baseDir, entityPackage + ".specs", simpleClassName + "Specification"); - if (FileUtils.fileExists(filePath)) { - return; + String aggregate = getAggregateWithModule(tableName); + + String entityFullPackage = tablePackageMap.get(tableName); + String entityType = getEntityJavaType(tableName); + String entityVar = toLowerCamelCase(entityType); + + + Map context = getEscapeContext(); + putContext(tag, "Name", entityType + "Specification", context); + putContext(tag, "Specification", context.get("Name"), context); + putContext(tag, "templatePackage", refPackage(getAggregatesPackage()), context); + putContext(tag, "package", refPackage(aggregate), context); + putContext(tag, "path", aggregate.replace(".", File.separator), context); + putContext(tag, "Aggregate", aggregate, context); + putContext(tag, "Comment", "", context); + putContext(tag, "CommentEscaped", "", context); + putContext(tag, "entityPackage", SourceFileUtils.refPackage(entityFullPackage, basePackage), context); + putContext(tag, "Entity", entityType, context); + putContext(tag, "AggregateRoot", context.get("Entity"), context); + putContext(tag, "EntityVar", entityVar, context); + + List domainEventTemplateNodes = templateNodeMap.containsKey(tag) + ? templateNodeMap.get(tag) + : Arrays.asList(getDefaultSpecificationTemplateNode()); + try { + for (TemplateNode templateNode : domainEventTemplateNodes) { + PathNode pathNode = templateNode.clone().resolve(context); + String path = forceRender( + pathNode, + SourceFileUtils.resolveDirectory( + baseDir, + concatPackage(basePackage, context.get("templatePackage")) + ) + ); + getLog().info("开始生成实体规约:" + path); + } + } catch (IOException e) { + getLog().error("实体规约模板文件写入失败!", e); } - getLog().info("开始生成实体规约:" + filePath); - BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); - writeLine(out, - "package " + entityPackage + ".specs;\n" + - "\n" + - "import " + entityPackage + "." + simpleClassName + ";\n" + - "import org.netcorepal.cap4j.ddd.domain.aggregate.Specification;\n" + - "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + - "import org.springframework.stereotype.Service;\n" + - "\n" + - "/**\n" + - " * " + simpleClassName + "规格约束\n" + - " * todo: 规格约束描述\n" + - " *\n" + - " * @author cap4j-ddd-codegen\n" + - " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) + "\n" + - " */\n" + - "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + simpleClassName + "Specification\", type = Aggregate.TYPE_SPECIFICATION, description = \"\")\n" + - "@Service\n" + - "public class " + simpleClassName + "Specification implements Specification<" + simpleClassName + "> {\n" + - " @Override\n" + - " public Result specify(" + simpleClassName + " entity) {\n" + - " return Result.fail(\"未实现\");\n" + - " }\n" + - "}" - ); - out.flush(); - out.close(); } - public void writeDomainEventSourceFile(Map table, String domainEventClassName, String domainEventDescription, String baseDir) throws IOException { + public void writeDomainEventSourceFile(Map table, Map tablePackageMap, String domainEventClassName, String domainEventDesc, String baseDir) { + String tag = "domain_event"; + String handlerTag = "domain_event_handler"; String tableName = getTableName(table); - String entityPackage = basePackage + "." + getEntityPackage(tableName); - String simpleClassName = getEntityJavaType(tableName); - String filePath = SourceFileUtils.resolveSourceFile(baseDir, entityPackage + ".events", domainEventClassName); - if (!FileUtils.fileExists(filePath)) { - getLog().info("开始生成领域事件:" + filePath); - BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); - writeLine(out, - "package " + entityPackage + ".events;\n" + - "\n" + -// "import " + entityPackage + "." + simpleClassName + ";\n" + - "import lombok.AllArgsConstructor;\n" + - "import lombok.Builder;\n" + - "import lombok.Data;\n" + - "import lombok.NoArgsConstructor;\n" + - "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + - "import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent;\n" + - "\n" + - "/**\n" + - " * " + simpleClassName + "领域事件\n" + - " * " + domainEventDescription + "\n" + - " *\n" + - " * @author cap4j-ddd-codegen\n" + - " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) + "\n" + - " */\n" + - "@DomainEvent(persist = false)\n" + - "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + domainEventClassName + "\", type = Aggregate.TYPE_DOMAIN_EVENT, description = \"" + domainEventDescription.replaceAll("(\\r\\n)|(\\r)|(\\n)", "\\n") + "\")\n" + - "@Data\n" + - "@Builder\n" + - "@AllArgsConstructor\n" + - "@NoArgsConstructor\n" + - "public class " + domainEventClassName + " {\n" + - " Long id;\n" + - "}" - ); - out.flush(); - out.close(); - } - - String subscriberPackage = basePackage + ".application.subscribers.domain"; - filePath = SourceFileUtils.resolveSourceFile(baseDir, subscriberPackage, domainEventClassName + "Subscriber"); - if (!FileUtils.fileExists(filePath)) { - - getLog().info("开始生成领域事件订阅:" + filePath); - BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); - writeLine(out, - "package " + subscriberPackage + ";\n" + - "\n" + - "import " + entityPackage + ".events." + domainEventClassName + ";\n" + - "import lombok.RequiredArgsConstructor;\n" + - "import org.springframework.context.event.EventListener;\n" + - "import org.springframework.stereotype.Service;\n" + - "\n" + - "/**\n" + - " * " + simpleClassName + "领域事件订阅\n" + - " * todo: 领域事件订阅描述\n" + - " */\n" + - "@Service\n" + - "@RequiredArgsConstructor\n" + - "public class " + domainEventClassName + "Subscriber {\n" + - "\n" + - " @EventListener(" + domainEventClassName + ".class)\n" + - " public void on(" + domainEventClassName + " event) {\n" + - "\n" + - " }\n" + - "\n" + - "}" - ); - out.flush(); - out.close(); + String aggregate = getAggregateWithModule(tableName); + + String entityFullPackage = tablePackageMap.get(tableName); + String entityType = getEntityJavaType(tableName); + String entityVar = toLowerCamelCase(entityType); + + String domainEventDescEscaped = domainEventDesc.replaceAll("(\\r\\n)|(\\r)|(\\n)", "\\n"); + + Map context = getEscapeContext(); + putContext(tag, "Name", domainEventClassName, context); + putContext(tag, "DomainEvent", context.get("Name"), context); + putContext(tag, "domainEventPackage", refPackage(getAggregatesPackage()), context); + putContext(tag, "domainEventHandlerPackage", refPackage(getSubscriberPackage()), context); + putContext(tag, "package", refPackage(aggregate), context); + putContext(tag, "path", aggregate.replace(".", File.separator), context); + putContext(tag, "persist", "false", context); + putContext(tag, "Aggregate", aggregate, context); + putContext(tag, "Comment", domainEventDescEscaped, context); + putContext(tag, "CommentEscaped", domainEventDescEscaped, context); + putContext(tag, "entityPackage", SourceFileUtils.refPackage(entityFullPackage, basePackage), context); + putContext(tag, "Entity", entityType, context); + putContext(tag, "AggregateRoot", context.get("Entity"), context); + putContext(tag, "EntityVar", entityVar, context); + + putContext(tag, "templatePackage", context.get("domainEventPackage"), context); + List domainEventTemplateNodes = templateNodeMap.containsKey(tag) + ? templateNodeMap.get(tag) + : Arrays.asList(getDefaultDomainEventTemplateNode()); + try { + for (TemplateNode templateNode : domainEventTemplateNodes) { + PathNode pathNode = templateNode.clone().resolve(context); + String path = forceRender( + pathNode, + SourceFileUtils.resolveDirectory( + baseDir, + concatPackage(basePackage, context.get("templatePackage")) + ) + ); + getLog().info("开始生成领域事件文件:" + path); + } + } catch (IOException e) { + getLog().error("领域事件模板文件写入失败!", e); + } + + putContext(tag, "templatePackage", context.get("domainEventHandlerPackage"), context); + List domainEventHandlerTemplateNodes = templateNodeMap.containsKey(handlerTag) + ? templateNodeMap.get(handlerTag) + : Arrays.asList(getDefaultDomainEventHandlerTemplateNode()); + try { + for (TemplateNode templateNode : domainEventHandlerTemplateNodes) { + PathNode pathNode = templateNode.clone().resolve(context); + String path = forceRender( + pathNode, + SourceFileUtils.resolveDirectory( + baseDir, + concatPackage(basePackage, context.get("templatePackage")) + ) + ); + getLog().info("开始生成领域事件订阅:" + path); + } + } catch (IOException e) { + getLog().error("领域事件订阅模板文件写入失败!", e); } } - public void writeEnumSourceFile(Map enumConfigs, String enumType, String enumPackage, String baseDir, String enumValueField, String enumNameField) throws IOException { - String tableName = EnumTableNameMap.get(enumType); - - new File(SourceFileUtils.resolveDirectory(baseDir, enumPackage)).mkdirs(); - String filePath = SourceFileUtils.resolveSourceFile(baseDir, enumPackage, enumType); - - BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); - getLog().info("开始生成枚举文件:" + filePath); - - writeLine(out, "package " + enumPackage + ";"); - writeLine(out, ""); - writeLine(out, "import lombok.Getter;"); - writeLine(out, "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;"); - writeLine(out, ""); - writeLine(out, "import javax.persistence.*;"); - writeLine(out, "import java.util.HashMap;"); - writeLine(out, "import java.util.Map;"); - writeLine(out, ""); - writeLine(out, "/**"); - writeLine(out, " * 本文件由[cap4j-ddd-codegen-maven-plugin]生成"); - writeLine(out, " * 警告:请勿手工修改该文件,重新生成会覆盖该文件"); - writeLine(out, " * @author cap4j-ddd-codegen"); - writeLine(out, " * @date " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))); - writeLine(out, " */"); - writeLine(out, "@Aggregate(aggregate = \"" + getAggregateWithModule(tableName) + "\", name = \"" + enumType + "\", type = \"enum\", description = \"\")"); - writeLine(out, "public enum " + enumType + " {"); - writeLine(out, ""); + public void writeEnumSourceFile(Map enumConfigs, String enumClassName, String enumValueField, String enumNameField, Map tablePackageMap, String baseDir) throws IOException { + String tag = "enum"; + String itemTag = "enum_item"; + String tableName = EnumTableNameMap.get(enumClassName); + String aggregate = getAggregateWithModule(tableName); + + String entityFullPackage = tablePackageMap.get(tableName); + String entityType = getEntityJavaType(tableName); + String entityVar = toLowerCamelCase(entityType); + + Map context = getEscapeContext(); + putContext(tag, "templatePackage", refPackage(getAggregatesPackage()), context); + putContext(tag, "package", refPackage(aggregate), context); + putContext(tag, "path", aggregate.replace(".", File.separator), context); + putContext(tag, "Aggregate", aggregate, context); + putContext(tag, "Comment", "", context); + putContext(tag, "CommentEscaped", "", context); + putContext(tag, "entityPackage", SourceFileUtils.refPackage(entityFullPackage, basePackage), context); + putContext(tag, "Entity", entityType, context); + putContext(tag, "AggregateRoot", context.get("Entity"), context); + putContext(tag, "EntityVar", entityVar, context); + putContext(tag, "Enum", enumClassName, context); + putContext(tag, "EnumValueField", enumValueField, context); + putContext(tag, "EnumNameField", enumNameField, context); + String enumItems = ""; for (Map.Entry entry : enumConfigs.entrySet()) { - getLog().info(" " + entry.getValue()[1] + " : " + entry.getValue()[0] + " = " + entry.getKey()); - writeLine(out, " /**"); - writeLine(out, " * " + entry.getValue()[1]); - writeLine(out, " */"); - writeLine(out, " " + entry.getValue()[0] + "(" + entry.getKey() + ", \"" + entry.getValue()[1] + "\"),"); + String itemValue = entry.getKey().toString(); + String itemName = entry.getValue()[0]; + String itemDesc = entry.getValue()[1]; + getLog().info(" " + itemDesc + " : " + itemName + " = " + entry.getKey()); + Map itemContext = new HashMap<>(context); + putContext(itemTag, "itemName", itemName, itemContext); + putContext(itemTag, "itemValue", itemValue, itemContext); + putContext(itemTag, "itemDesc", itemDesc, itemContext); + PathNode enumItemsPathNode = ((templateNodeMap.containsKey(itemTag) && templateNodeMap.get(itemTag).size() > 0) + ? templateNodeMap.get(itemTag).get(templateNodeMap.get(itemTag).size() - 1) + : getDefaultEnumItemTemplateNode() + ).clone().resolve(itemContext); + enumItems += enumItemsPathNode.getData(); + } + putContext(tag, "ENUM_ITEMS", enumItems, context); + List enumTemplateNodes = templateNodeMap.containsKey(tag) + ? templateNodeMap.get(tag) + : Arrays.asList(getDefaultEnumTemplateNode()); + try { + for (TemplateNode templateNode : enumTemplateNodes) { + PathNode pathNode = templateNode.clone().resolve(context); + String path = forceRender( + pathNode, + SourceFileUtils.resolveDirectory( + baseDir, + concatPackage(basePackage, context.get("templatePackage")) + ) + ); + getLog().info(JSON.toJSONString(context)); + getLog().info("开始生成枚举文件:" + path); + } + } catch (IOException e) { + getLog().error("枚举模板文件写入失败!", e); } - writeLine(out, ";"); - writeLine(out, " @Getter"); - writeLine(out, " private int " + enumValueField + ";"); - writeLine(out, " @Getter"); - writeLine(out, " private String " + enumNameField + ";"); - writeLine(out, ""); - writeLine(out, " " + enumType + "(Integer " + enumValueField + ", String " + enumNameField + "){"); - writeLine(out, " this." + enumValueField + " = " + enumValueField + ";"); - writeLine(out, " this." + enumNameField + " = " + enumNameField + ";"); - writeLine(out, " }"); - writeLine(out, ""); - writeLine(out, "" + - " private static Map enums = null;\n" + - " public static " + enumType + " valueOf(Integer " + enumValueField + ") {\n" + - " if(enums == null) {\n" + - " enums = new HashMap<>();\n" + - " for (" + enumType + " val : " + enumType + ".values()) {\n" + - " enums.put(val." + enumValueField + ", val);\n" + - " }\n" + - " }\n" + - " if(enums.containsKey(" + enumValueField + ")){\n" + - " return enums.get(" + enumValueField + ");\n" + - " }\n" + - (this.enumUnmatchedThrowException - ? " throw new RuntimeException(\"枚举类型" + enumType + "枚举值转换异常,不存在的值\" + " + enumValueField + ");\n" - : " return null;") + - " }"); - writeLine(out, ""); - writeLine(out, " /**"); - writeLine(out, " * JPA转换器"); - writeLine(out, " */"); - writeLine(out, " public static class Converter implements AttributeConverter<" + enumType + ", Integer>{"); - writeLine(out, "" + - " @Override\n" + - " public Integer convertToDatabaseColumn(" + enumType + " val) {\n" + - " return val." + enumValueField + ";\n" + - " }\n" + - "\n" + - " @Override\n" + - " public " + enumType + " convertToEntityAttribute(Integer " + enumValueField + ") {\n" + - " return " + enumType + ".valueOf(" + enumValueField + ");\n" + - " }"); - writeLine(out, " }"); - writeLine(out, "}"); - writeLine(out, ""); - out.flush(); - out.close(); } public void writeSchemaSourceFile(Map table, List> columns, Map tablePackageMap, Map> relations, String basePackage, String baseDir) throws IOException { + String tag = "schema"; + String fieldTag = "schema_field"; + String joinTag = "schema_join"; String tableName = getTableName(table); - String packageName = null; - if ("abs".equalsIgnoreCase(entityMetaInfoClassOutputMode)) { - packageName = basePackage + "." + entityMetaInfoClassOutputPackage + ".schemas"; + String aggregate = getAggregateWithModule(tableName); + String schemaPackage = null; + if ("abs".equalsIgnoreCase(entitySchemaOutputMode)) { + schemaPackage = getSchemaPackage(); } else { - packageName = tablePackageMap.get(tableName) + ".schemas"; + schemaPackage = getAggregatesPackage(); } - String simpleClassName = getEntityJavaType(tableName); - new File(SourceFileUtils.resolveDirectory(baseDir, packageName)).mkdirs(); - String filePath = SourceFileUtils.resolveSourceFile(baseDir, packageName, simpleClassName + "Schema"); + String entityFullPackage = tablePackageMap.get(tableName); + String entityType = getEntityJavaType(tableName); + String entityVar = toLowerCamelCase(entityType); + + String comment = getComment(table).replaceAll(PATTERN_LINE_BREAK, " "); + + String schemaBaseFullPackage = StringUtils.isNotBlank(schemaPath) + ? SourceFileUtils.resolvePackage(schemaPath) + : SourceFileUtils.concatPackage(basePackage, entitySchemaOutputPackage); - BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); - getLog().info("开始生成Schema文件:" + filePath); + Map context = getEscapeContext(); - writeLine(out, "package " + packageName + ";"); - writeLine(out, "\n" + - "import " + basePackage + "." + entityMetaInfoClassOutputPackage + ".Schema;\n" + - "import " + tablePackageMap.get(tableName) + "." + simpleClassName + ";\n" + + putContext(tag, "templatePackage", refPackage(schemaPackage), context); + putContext(tag, "package", refPackage(aggregate), context); + putContext(tag, "path", aggregate.replace(".", File.separator), context); + putContext(tag, "Aggregate", aggregate, context); + putContext(tag, "Comment", comment, context); + putContext(tag, "CommentEscaped", comment.replaceAll(PATTERN_LINE_BREAK, " "), context); + putContext(tag, "entityPackage", SourceFileUtils.refPackage(entityFullPackage, basePackage), context); + putContext(tag, "Entity", entityType, context); + putContext(tag, "EntityVar", entityVar, context); + putContext(tag, "schemaBasePackage", SourceFileUtils.refPackage(schemaBaseFullPackage, basePackage), context); + putContext(tag, "SchemaBase", DEFAULT_SCHEMA_BASE_CLASS_NAME, context); + putContext(tag, "IdField", idField, context); + + String fieldItems = ""; + for (Map column : columns) { + if (!isColumnNeedGenerate(table, column, relations)) { + continue; + } + String fieldType = getColumnJavaType(column); + String fieldName = toLowerCamelCase(getColumnName(column)); + String fieldComment = generateFieldComment(column).stream().reduce((a, b) -> a + "\n " + b).orElse(""); + Map itemContext = new HashMap<>(context); + putContext(fieldTag, "fieldType", fieldType, itemContext); + putContext(fieldTag, "fieldName", fieldName, itemContext); + putContext(fieldTag, "fieldComment", fieldComment, itemContext); + fieldItems += ( + templateNodeMap.containsKey(fieldTag) && templateNodeMap.get(fieldTag).size() > 0 + ? templateNodeMap.get(fieldTag).get(templateNodeMap.get(fieldTag).size() - 1) + : getDefaultSchemaFieldTemplateNode().clone() + ).resolve(itemContext).getData(); + } + putContext(tag, "FIELD_ITEMS", fieldItems, context); + + String joinItems = ""; + if (relations.containsKey(tableName)) { + for (Map.Entry entry : relations.get(tableName).entrySet()) { + String[] refInfos = entry.getValue().split(";"); + switch (refInfos[0]) { + case "OneToMany": + case "*OneToMany": + Map joinContext = new HashMap<>(context); + putContext(joinTag, "joinEntityPackage", tablePackageMap.get(entry.getKey()), joinContext); + putContext(joinTag, "joinEntityType", getEntityJavaType(entry.getKey()), joinContext); + putContext(joinTag, "joinEntityVars", Inflector.getInstance().pluralize(toLowerCamelCase(getEntityJavaType(entry.getKey()))), joinContext); + PathNode joinItemTemplateNode = ( + (templateNodeMap.containsKey(joinTag) && templateNodeMap.get(joinTag).size() > 0) + ? templateNodeMap.get(joinTag).get(templateNodeMap.get(joinTag).size() - 1) + : getDefaultSchemaJoinTemplateNode() + ).clone().resolve(joinContext); + joinItems += joinItemTemplateNode.getData(); + break; + default: + // 暂不支持 + break; + } + + } + } + putContext(tag, "JOIN_ITEMS", joinItems, context); + + List schemaTemplateNodes = templateNodeMap.containsKey(tag) + ? templateNodeMap.get(tag) + : Arrays.asList(getDefaultSchemaTemplateNode()); + try { + for (TemplateNode templateNode : schemaTemplateNodes) { + PathNode pathNode = templateNode.clone().resolve(context); + String path = forceRender( + pathNode, + SourceFileUtils.resolveDirectory( + baseDir, + concatPackage(basePackage, context.get("templatePackage")) + ) + ); + getLog().info("开始生成Schema文件:" + path); + } + } catch (IOException e) { + getLog().error("Schema模板文件写入失败!", e); + } + } + + public void writeSchemaBaseSourceFile(String baseDir) throws IOException { + String tag = "schema_base"; + String schemaFullPackage = concatPackage(basePackage, getSchemaPackage()); + + List schemaBaseTemplateNodes = templateNodeMap.containsKey(tag) + ? templateNodeMap.get(tag) + : Arrays.asList(getDefaultSchemaBaseTemplateNode()); + Map context = getEscapeContext(); + putContext(tag, "templatePackage", SourceFileUtils.refPackage(schemaFullPackage, basePackage), context); + putContext(tag, "SchemaBase", DEFAULT_SCHEMA_BASE_CLASS_NAME, context); + try { + for (TemplateNode templateNode : schemaBaseTemplateNodes) { + PathNode pathNode = templateNode.clone().resolve(context); + forceRender( + pathNode, + SourceFileUtils.resolveDirectory( + baseDir, + concatPackage(basePackage, context.get("templatePackage")) + ) + ); + } + } catch (IOException e) { + getLog().error("模板文件写入失败!", e); + } + } + + public TemplateNode getDefaultFactoryTemplateNode() { + String template = "package ${basePackage}${templatePackage}${package}.factory;\n" + + "\n" + + "import ${basePackage}${entityPackage}${package}.${Entity};\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.AggregateFactory;\n" + + "import org.springframework.stereotype.Service;\n" + + "\n" + + "/**\n" + + " * ${Entity}聚合工厂\n" + + " * ${Comment}\n" + + " *\n" + + " * @author cap4j-ddd-codegen\n" + + " * @date ${date}\n" + + " */\n" + + "@Aggregate(aggregate = \"${Aggregate}\", name = \"${Entity}Factory\", type = Aggregate.TYPE_FACTORY, description = \"${CommentEscaped}\")\n" + + "@Service\n" + + "public class ${Entity}Factory implements AggregateFactory<${Entity}Payload, ${Entity}> {\n" + + "\n" + + " @Override\n" + + " public ${Entity} create(${Entity}Payload payload) {\n" + + "\n" + + " return ${Entity}.builder()\n" + + "\n" + + " .build();\n" + + " }\n" + + "}\n"; + TemplateNode templateNode = new TemplateNode(); + templateNode.setType("file"); + templateNode.setTag("factory"); + templateNode.setName("${path}${SEPARATOR}factory${SEPARATOR}${Entity}Factory.java"); + templateNode.setFormat("raw"); + templateNode.setData(template); + templateNode.setConflict("skip"); + return templateNode; + } + + public TemplateNode getDefaultFactoryPayloadTemplateNode() { + String template = "package ${basePackage}${templatePackage}${package}.factory;\n" + + "\n" + + "import ${basePackage}${entityPackage}${package}.${Entity};\n" + + "import lombok.AllArgsConstructor;\n" + + "import lombok.Builder;\n" + + "import lombok.Data;\n" + + "import lombok.NoArgsConstructor;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.AggregatePayload;\n" + + "\n" + + "/**\n" + + " * ${Entity}工厂负载\n" + + " * ${Comment}\n" + + " *\n" + + " * @author cap4j-ddd-codegen\n" + + " * @date ${date}\n" + + " */\n" + + "@Aggregate(aggregate = \"${Aggregate}\", name = \"${Entity}Payload\", type = Aggregate.TYPE_FACTORY_PAYLOAD, description = \"${CommentEscaped}\")\n" + + "@Data\n" + + "@Builder\n" + + "@NoArgsConstructor\n" + + "@AllArgsConstructor\n" + + "public class ${Entity}Payload implements AggregatePayload<${Entity}> {\n" + + " String name;\n" + + "}\n"; + TemplateNode templateNode = new TemplateNode(); + templateNode.setType("file"); + templateNode.setTag("factory"); + templateNode.setName("${path}${SEPARATOR}factory${SEPARATOR}${Entity}Payload.java"); + templateNode.setFormat("raw"); + templateNode.setData(template); + templateNode.setConflict("skip"); + return templateNode; + } + + public TemplateNode getDefaultSpecificationTemplateNode() { + String template = "package ${basePackage}${templatePackage}${package}.specs;\n" + + "\n" + + "import ${basePackage}${entityPackage}${package}.${Entity};\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.Specification;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + + "import org.springframework.stereotype.Service;\n" + + "\n" + + "/**\n" + + " * ${Entity}规格约束\n" + + " * ${Comment}\n" + + " *\n" + + " * @author cap4j-ddd-codegen\n" + + " * @date ${date}\n" + + " */\n" + + "@Aggregate(aggregate = \"${Aggregate}\", name = \"${Entity}Specification\", type = Aggregate.TYPE_SPECIFICATION, description = \"${CommentEscaped}\")\n" + + "@Service\n" + + "public class ${Entity}Specification implements Specification<${Entity}> {\n" + + " @Override\n" + + " public Result specify(${Entity} entity) {\n" + + " return Result.fail(\"未实现\");\n" + + " }\n" + + "}"; + TemplateNode templateNode = new TemplateNode(); + templateNode.setType("file"); + templateNode.setTag("specification"); + templateNode.setName("${path}${SEPARATOR}specs${SEPARATOR}${Entity}Specification.java"); + templateNode.setFormat("raw"); + templateNode.setData(template); + templateNode.setConflict("skip"); + return templateNode; + } + + public TemplateNode getDefaultDomainEventHandlerTemplateNode() { + String template = "package ${basePackage}${templatePackage};\n" + + "\n" + + "import ${basePackage}${domainEventPackage}${package}.${DomainEvent};\n" + + "import lombok.RequiredArgsConstructor;\n" + + "import org.springframework.context.event.EventListener;\n" + + "import org.springframework.stereotype.Service;\n" + + "\n" + + "/**\n" + + " * ${Entity}.${DomainEvent}领域事件订阅\n" + + " * todo: 领域事件订阅描述\n" + + " */\n" + + "@Service\n" + + "@RequiredArgsConstructor\n" + + "public class ${DomainEvent}Subscriber {\n" + + "\n" + + " @EventListener(${DomainEvent}.class)\n" + + " public void on(${DomainEvent} event) {\n" + + "\n" + + " }\n" + + "\n" + + "}"; + + TemplateNode templateNode = new TemplateNode(); + templateNode.setType("file"); + templateNode.setTag("domain_event_handler"); + templateNode.setName("${DomainEvent}Subscriber.java"); + templateNode.setFormat("raw"); + templateNode.setData(template); + templateNode.setConflict("skip"); + return templateNode; + } + + public TemplateNode getDefaultDomainEventTemplateNode() { + String template = "package ${basePackage}${templatePackage}${package}.events;\n" + + "\n" + + "import lombok.AllArgsConstructor;\n" + + "import lombok.Builder;\n" + + "import lombok.Data;\n" + + "import lombok.NoArgsConstructor;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + + "import org.netcorepal.cap4j.ddd.domain.event.annotation.DomainEvent;\n" + + "\n" + + "/**\n" + + " * ${Entity}.${DomainEvent}领域事件\n" + + " * ${Comment}\n" + + " *\n" + + " * @author cap4j-ddd-codegen\n" + + " * @date ${date}\n" + + " */\n" + + "@DomainEvent(persist = false)\n" + + "@Aggregate(aggregate = \"${Aggregate}\", name = \"${DomainEvent}\", type = Aggregate.TYPE_DOMAIN_EVENT, description = \"${CommentEscaped}\")\n" + + "@Data\n" + + "@Builder\n" + + "@AllArgsConstructor\n" + + "@NoArgsConstructor\n" + + "public class ${DomainEvent} {\n" + + " Long id;\n" + + "}"; + + TemplateNode templateNode = new TemplateNode(); + templateNode.setType("file"); + templateNode.setTag("domain_event"); + templateNode.setName("${path}${SEPARATOR}events${SEPARATOR}${DomainEvent}.java"); + templateNode.setFormat("raw"); + templateNode.setData(template); + templateNode.setConflict("skip"); + return templateNode; + } + + public TemplateNode getDefaultEnumTemplateNode() { + String template = + "package ${basePackage}${templatePackage}${package}.enums;\n" + + "\n" + + "import lombok.Getter;\n" + + "import org.netcorepal.cap4j.ddd.domain.aggregate.annotation.Aggregate;\n" + + "\n" + + "import javax.persistence.*;\n" + + "import java.util.HashMap;\n" + + "import java.util.Map;\n" + + "\n" + + "/**\n" + + " * 本文件由[cap4j-ddd-codegen-maven-plugin]生成\n" + + " * 警告:请勿手工修改该文件,重新生成会覆盖该文件\n" + + " * @author cap4j-ddd-codegen\n" + + " * @date ${date}\n" + + " */\n" + + "@Aggregate(aggregate = \"${Aggregate}\", name = \"${EnumType}\", type = \"enum\", description = \"${CommentEscaped}\")\n" + + "public enum ${EnumType} {\n" + + "\n" + + "${ENUM_ITEMS}\n" + + ";\n" + + " @Getter\n" + + " private int ${EnumValueField};\n" + + " @Getter\n" + + " private String ${EnumNameField};\n" + + "\n" + + " ${EnumType}(Integer ${EnumValueField}, String ${EnumNameField}){\n" + + " this.${EnumValueField} = ${EnumValueField};\n" + + " this.${EnumNameField} = ${EnumNameField};\n" + + " }\n" + + "\n" + + "\n" + + " private static Map enums = null;\n" + + " public static ${EnumType} valueOf(Integer ${EnumValueField}) {\n" + + " if(enums == null) {\n" + + " enums = new HashMap<>();\n" + + " for (${EnumType} val : ${EnumType}.values()) {\n" + + " enums.put(val.${EnumValueField}, val);\n" + + " }\n" + + " }\n" + + " if(enums.containsKey(${EnumValueField})){\n" + + " return enums.get(${EnumValueField});\n" + + " }\n" + + (this.enumUnmatchedThrowException + ? " throw new RuntimeException(\"枚举类型${EnumType}枚举值转换异常,不存在的值\" + ${EnumValueField});\n" + : " return null;\n" + ) + + " }\n" + + "\n" + + " /**\n" + + " * JPA转换器\n" + + " */\n" + + " public static class Converter implements AttributeConverter<${EnumType}, Integer>{\n" + + "\n" + + " @Override\n" + + " public Integer convertToDatabaseColumn(${EnumType} val) {\n" + + " return val.${EnumValueField};\n" + + " }\n" + + "\n" + + " @Override\n" + + " public ${EnumType} convertToEntityAttribute(Integer ${EnumValueField}) {\n" + + " return ${EnumType}.valueOf(${EnumValueField});\n" + + " }\n" + + " }\n" + + "}\n"; + TemplateNode templateNode = new TemplateNode(); + templateNode.setType("file"); + templateNode.setTag("enum"); + templateNode.setName("${path}${SEPARATOR}enums${SEPARATOR}${EnumType}.java"); + templateNode.setFormat("raw"); + templateNode.setData(template); + templateNode.setConflict("skip"); + return templateNode; + } + + public TemplateNode getDefaultEnumItemTemplateNode() { + String template = " /**\n" + + " * ${itemDesc}\n" + + " */\n" + + " ${itemName}(${itemValue}, \"${itemDesc}\"),\n"; + TemplateNode templateNode = new TemplateNode(); + templateNode.setType("segment"); + templateNode.setTag("enum_item"); + templateNode.setName(""); + templateNode.setFormat("raw"); + templateNode.setData(template); + templateNode.setConflict("skip"); + return templateNode; + } + + public TemplateNode getDefaultSchemaFieldTemplateNode() { + String template = "\n" + + " ${fieldComment}\n" + + " public ${SchemaBase}.Field<${fieldType}> ${fieldName}() {\n" + + " return root == null ? new ${SchemaBase}.Field<>(\"${fieldName}\") : new ${SchemaBase}.Field<>(root.get(\"${fieldName}\"));\n" + + " }\n"; + TemplateNode templateNode = new TemplateNode(); + templateNode.setType("segment"); + templateNode.setTag("schema_field"); + templateNode.setName(""); + templateNode.setFormat("raw"); + templateNode.setData(template); + templateNode.setConflict("skip"); + return templateNode; + } + + public TemplateNode getDefaultSchemaJoinTemplateNode() { + String template = "\n" + + " /**\n" + + " * ${joinEntityType} 关联查询条件定义\n" + + " *\n" + + " * @param joinType\n" + + " * @return\n" + + " */\n" + + " public ${joinEntityType}Schema join${joinEntityType}(${SchemaBase}.JoinType joinType) {\n" + + " JoinType type = joinType.toJpaJoinType();\n" + + " Join<${Entity}, ${joinEntityPackage}.${joinEntityType}> join = ((Root<${Entity}>) root).join(\"${joinEntityVars}\", type);\n" + + " ${joinEntityType}Schema schema = new ${joinEntityType}Schema(join, criteriaBuilder);\n" + + " return schema;\n" + + " }"; + TemplateNode templateNode = new TemplateNode(); + templateNode.setType("segment"); + templateNode.setTag("schema_join"); + templateNode.setName(""); + templateNode.setFormat("raw"); + templateNode.setData(template); + templateNode.setConflict("skip"); + return templateNode; + } + + public TemplateNode getDefaultSchemaTemplateNode() { + String template = "package ${basePackage}${templatePackage}${package}.meta;\n" + + "\n" + + "import ${basePackage}${schemaBasePackage}.${SchemaBase};\n" + + "import ${basePackage}${entityPackage}.${Entity};\n" + "import lombok.RequiredArgsConstructor;\n" + "import org.springframework.data.domain.Sort;\n" + "import org.springframework.data.jpa.domain.Specification;\n" + @@ -1448,39 +1912,29 @@ public void writeSchemaSourceFile(Map table, List root;"); - writeLine(out, " private final CriteriaBuilder criteriaBuilder;\n" + + "import java.util.stream.Collectors;\n" + + "\n" + + "/**\n" + + " * ${Comment}\n" + + " * 本文件由[cap4j-ddd-codegen-maven-plugin]生成\n" + + " * 警告:请勿手工修改该文件,重新生成会覆盖该文件\n" + + " * @author cap4j-ddd-codegen\n" + + " * @date ${date}\n" + + " */\n" + + "@RequiredArgsConstructor\n" + + "public class ${Entity}Schema {\n" + + " private final Path<${Entity}> root;\n" + + " private final CriteriaBuilder criteriaBuilder;\n" + "\n" + " public CriteriaBuilder criteriaBuilder() {\n" + " return criteriaBuilder;\n" + - " }"); - writeLine(out, "\n" + - " public Schema.Field " + idField + "() {\n" + - " return root == null ? new Schema.Field<>(\"" + idField + "\") : new Schema.Field<>(root.get(\"" + idField + "\"));\n" + - " }"); - for (Map column : columns) { - if (!needGenerateField(table, column, relations)) { - continue; - } - writeLine(out, ""); - writeColumnComment(out, column); - writeLine(out, " public Schema.Field<" + getColumnJavaType(column) + "> " + toLowerCamelCase(getColumnName(column)) + "() {\n" + - " return root == null ? new Schema.Field<>(\"" + toLowerCamelCase(getColumnName(column)) + "\") : new Schema.Field<>(root.get(\"" + toLowerCamelCase(getColumnName(column)) + "\"));\n" + - " }"); - } - writeLine(out, ""); - writeLine(out, "" + + " }\n" + + "\n" + + " public ${SchemaBase}.Field ${IdField}() {\n" + + " return root == null ? new ${SchemaBase}.Field<>(\"${IdField}\") : new ${SchemaBase}.Field<>(root.get(\"${IdField}\"));\n" + + " }\n" + + "${FIELD_ITEMS}\n" + + "\n" + " /**\n" + " * 满足所有条件\n" + " * @param restrictions\n" + @@ -1504,21 +1958,21 @@ public void writeSchemaSourceFile(Map table, List builder){\n" + + " public Predicate spec(${SchemaBase}.PredicateBuilder<${Entity}Schema> builder){\n" + " return builder.build(this);\n" + - " }"); - writeJoinEntities(out, table, relations, tablePackageMap); - writeLine(out, ""); - writeLine(out, " /**\n" + + " }\n" + + "${JOIN_ITEMS}\n" + + "\n" + + " /**\n" + " * 构建查询条件\n" + " * @param builder\n" + " * @param distinct\n" + " * @return\n" + " */\n" + - " public static Specification<" + simpleClassName + "> specify(Schema.PredicateBuilder<" + simpleClassName + "Schema> builder, boolean distinct) {\n" + + " public static Specification<${Entity}> specify(${SchemaBase}.PredicateBuilder<${Entity}Schema> builder, boolean distinct) {\n" + " return (root, criteriaQuery, criteriaBuilder) -> {\n" + - " " + simpleClassName + "Schema " + toLowerCamelCase(simpleClassName) + " = new " + simpleClassName + "Schema(root, criteriaBuilder);\n" + - " criteriaQuery.where(builder.build(" + toLowerCamelCase(simpleClassName) + "));\n" + + " ${Entity}Schema ${EntityVar} = new ${Entity}Schema(root, criteriaBuilder);\n" + + " criteriaQuery.where(builder.build(${EntityVar}));\n" + " criteriaQuery.distinct(distinct);\n" + " return null;\n" + " };\n" + @@ -1529,10 +1983,10 @@ public void writeSchemaSourceFile(Map table, List specify(Schema.PredicateBuilder<" + simpleClassName + "Schema> builder) {\n" + + " public static Specification<${Entity}> specify(${SchemaBase}.PredicateBuilder<${Entity}Schema> builder) {\n" + " return (root, criteriaQuery, criteriaBuilder) -> {\n" + - " " + simpleClassName + "Schema " + toLowerCamelCase(simpleClassName) + " = new " + simpleClassName + "Schema(root, criteriaBuilder);\n" + - " criteriaQuery.where(builder.build(" + toLowerCamelCase(simpleClassName) + "));\n" + + " ${Entity}Schema ${EntityVar} = new ${Entity}Schema(root, criteriaBuilder);\n" + + " criteriaQuery.where(builder.build(${EntityVar}));\n" + " return null;\n" + " };\n" + " }\n" + @@ -1542,7 +1996,7 @@ public void writeSchemaSourceFile(Map table, List... builders) {\n" + + " public static Sort orderBy(${SchemaBase}.OrderBuilder<${Entity}Schema>... builders) {\n" + " return orderBy(Arrays.asList(builders));\n" + " }\n" + "\n" + @@ -1552,80 +2006,30 @@ public void writeSchemaSourceFile(Map table, List> builders) {\n" + + " public static Sort orderBy(Collection<${SchemaBase}.OrderBuilder<${Entity}Schema>> builders) {\n" + " if(null == builders || builders.isEmpty()) {\n" + " return Sort.unsorted();\n" + " }\n" + " return Sort.by(builders.stream()\n" + - " .map(builder -> builder.build(new " + simpleClassName + "Schema(null, null)))\n" + + " .map(builder -> builder.build(new ${Entity}Schema(null, null)))\n" + " .collect(Collectors.toList())\n" + " );\n" + - " }"); - writeLine(out, ""); - writeLine(out, "}"); - out.flush(); - out.close(); - } - - public void writeJoinEntities(BufferedWriter out, Map table, Map> relations, Map tablePackageMap) { - String tableName = getTableName(table); - int count = 0; - if (relations.containsKey(tableName)) { - for (Map.Entry entry : relations.get(tableName).entrySet()) { - String[] refInfos = entry.getValue().split(";"); - switch (refInfos[0]) { - case "OneToMany": - case "*OneToMany": - writeLine(out, - "\n" + - " /**\n" + - " * " + getEntityJavaType(entry.getKey()) + " 关联查询条件定义\n" + - " *\n" + - " * @param joinType\n" + - " * @return\n" + - " */\n" + - " public " + getEntityJavaType(entry.getKey()) + "Schema join" + getEntityJavaType(entry.getKey()) + "(Schema.JoinType joinType) {\n" + - " JoinType type = transformJoinType(joinType);\n" + - " Join<" + getEntityJavaType(tableName) + ", " + tablePackageMap.get(entry.getKey()) + "." + getEntityJavaType(entry.getKey()) + "> join = ((Root<" + getEntityJavaType(tableName) + ">) root).join(\"" + Inflector.getInstance().pluralize(toLowerCamelCase(getEntityJavaType(entry.getKey()))) + "\", type);\n" + - " " + getEntityJavaType(entry.getKey()) + "Schema schema = new " + getEntityJavaType(entry.getKey()) + "Schema(join, criteriaBuilder);\n" + - " return schema;\n" + - " }"); - count++; - break; - default: - // 暂不支持 - break; - } - - } - - if (count > 0) { - writeLine(out, "\n" + - "\n" + - " private JoinType transformJoinType(Schema.JoinType joinType){\n" + - " if(joinType == Schema.JoinType.INNER){\n" + - " return JoinType.INNER;\n" + - " } else if(joinType == Schema.JoinType.LEFT){\n" + - " return JoinType.LEFT;\n" + - " } else if(joinType == Schema.JoinType.RIGHT){\n" + - " return JoinType.RIGHT;\n" + - " }\n" + - " return JoinType.LEFT;\n" + - " }"); - } - } + " }\n" + + "\n" + + "}\n"; + TemplateNode templateNode = new TemplateNode(); + templateNode.setType("file"); + templateNode.setTag("schema"); + templateNode.setName("${path}${SEPARATOR}meta${SEPARATOR}${Entity}Schema.java"); + templateNode.setFormat("raw"); + templateNode.setData(template); + templateNode.setConflict("skip"); + return templateNode; } - public void writeBaseSechemaSourceFile(String basePackage, String baseDir) throws IOException { - String packageName = basePackage + "." + entityMetaInfoClassOutputPackage; - String simpleClassName = "Schema"; - - new File(SourceFileUtils.resolveDirectory(baseDir, packageName)).mkdirs(); - String filePath = SourceFileUtils.resolveSourceFile(baseDir, packageName, simpleClassName); - - BufferedWriter out = new BufferedWriter(new FileWriter(filePath)); - writeLine(out, "package " + packageName + ";"); - writeLine(out, "\n" + + public TemplateNode getDefaultSchemaBaseTemplateNode() { + String template = "package ${basePackage}${templatePackage};\n" + + "\n" + "import com.google.common.collect.Lists;\n" + "import org.hibernate.query.criteria.internal.path.SingularAttributePath;\n" + "import org.springframework.data.domain.Sort;\n" + @@ -1639,10 +2043,9 @@ public void writeBaseSechemaSourceFile(String basePackage, String baseDir) throw "/**\n" + " * Schema\n" + " *\n" + - " * @author