Commit df3cd34b30a2990b8b8c65656c7e1fe4e5dad6b8
1 parent
2bcc5505
扩展组件
Showing
26 changed files
with
1097 additions
and
0 deletions
yudao-framework/pom.xml
| @@ -28,6 +28,7 @@ | @@ -28,6 +28,7 @@ | ||
| 28 | <module>yudao-spring-boot-starter-biz-operatelog</module> | 28 | <module>yudao-spring-boot-starter-biz-operatelog</module> |
| 29 | <module>yudao-spring-boot-starter-biz-dict</module> | 29 | <module>yudao-spring-boot-starter-biz-dict</module> |
| 30 | <module>yudao-spring-boot-starter-biz-sms</module> | 30 | <module>yudao-spring-boot-starter-biz-sms</module> |
| 31 | + <module>yudao-spring-boot-starter-extension</module> | ||
| 31 | </modules> | 32 | </modules> |
| 32 | 33 | ||
| 33 | <artifactId>yudao-framework</artifactId> | 34 | <artifactId>yudao-framework</artifactId> |
yudao-framework/yudao-spring-boot-starter-extension/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <parent> | ||
| 6 | + <artifactId>yudao-framework</artifactId> | ||
| 7 | + <groupId>cn.iocoder.boot</groupId> | ||
| 8 | + <version>${revision}</version> | ||
| 9 | + </parent> | ||
| 10 | + <modelVersion>4.0.0</modelVersion> | ||
| 11 | + | ||
| 12 | + <artifactId>yudao-spring-boot-starter-extension</artifactId> | ||
| 13 | + <packaging>jar</packaging> | ||
| 14 | + | ||
| 15 | + <name>${artifactId}</name> | ||
| 16 | + <description>扩展点组件</description> | ||
| 17 | + <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | ||
| 18 | + | ||
| 19 | + <properties> | ||
| 20 | + | ||
| 21 | + </properties> | ||
| 22 | + | ||
| 23 | + <dependencies> | ||
| 24 | + <dependency> | ||
| 25 | + <groupId>cn.iocoder.boot</groupId> | ||
| 26 | + <artifactId>yudao-common</artifactId> | ||
| 27 | + </dependency> | ||
| 28 | + | ||
| 29 | + <!-- Spring 核心 --> | ||
| 30 | + <dependency> | ||
| 31 | + <groupId>org.springframework</groupId> | ||
| 32 | + <artifactId>spring-context</artifactId> | ||
| 33 | + <scope>provided</scope> | ||
| 34 | + </dependency> | ||
| 35 | + | ||
| 36 | + <dependency> | ||
| 37 | + <groupId>org.springframework</groupId> | ||
| 38 | + <artifactId>spring-beans</artifactId> | ||
| 39 | + <scope>provided</scope> | ||
| 40 | + </dependency> | ||
| 41 | + | ||
| 42 | + <!-- Spring 核心 --> | ||
| 43 | + <dependency> | ||
| 44 | + <groupId>org.springframework.boot</groupId> | ||
| 45 | + <artifactId>spring-boot-starter-aop</artifactId> | ||
| 46 | + </dependency> | ||
| 47 | + | ||
| 48 | + <!-- 测试包 --> | ||
| 49 | + <dependency> | ||
| 50 | + <groupId>org.springframework.boot</groupId> | ||
| 51 | + <artifactId>spring-boot-starter-test</artifactId> | ||
| 52 | + <scope>test</scope> | ||
| 53 | + </dependency> | ||
| 54 | + | ||
| 55 | + <!-- 测试包 --> | ||
| 56 | + <dependency> | ||
| 57 | + <groupId>junit</groupId> | ||
| 58 | + <artifactId>junit</artifactId> | ||
| 59 | + <scope>test</scope> | ||
| 60 | + </dependency> | ||
| 61 | + | ||
| 62 | + <!-- 工具类相关 --> | ||
| 63 | + <dependency> | ||
| 64 | + <groupId>jakarta.validation</groupId> | ||
| 65 | + <artifactId>jakarta.validation-api</artifactId> | ||
| 66 | + </dependency> | ||
| 67 | + </dependencies> | ||
| 68 | +</project> | ||
| 0 | \ No newline at end of file | 69 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/config/YudaoExtensionAutoConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.config; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.ExtensionBootstrap; | ||
| 4 | +import cn.iocoder.yudao.framework.extension.core.context.ExtensionContext; | ||
| 5 | +import cn.iocoder.yudao.framework.extension.core.context.ExtensionContextHolder; | ||
| 6 | +import cn.iocoder.yudao.framework.extension.core.context.ExtensionExecutor; | ||
| 7 | +import cn.iocoder.yudao.framework.extension.core.factory.ExtensionFactory; | ||
| 8 | +import cn.iocoder.yudao.framework.extension.core.factory.ExtensionRegisterFactory; | ||
| 9 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
| 10 | +import org.springframework.context.annotation.Bean; | ||
| 11 | +import org.springframework.context.annotation.Configuration; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * @description 扩展点组件自动装配 | ||
| 15 | + * @author Qingchen | ||
| 16 | + * @version 1.0.0 | ||
| 17 | + * @date 2021-08-28 21:50 | ||
| 18 | + * @class cn.iocoder.yudao.framework.extension.config.YudaoExtensionAutoConfiguration.java | ||
| 19 | + */ | ||
| 20 | +@Configuration | ||
| 21 | +public class YudaoExtensionAutoConfiguration { | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 组件初始化 | ||
| 25 | + * @return | ||
| 26 | + */ | ||
| 27 | + @Bean(initMethod = "init") | ||
| 28 | + @ConditionalOnMissingBean(ExtensionBootstrap.class) | ||
| 29 | + public ExtensionBootstrap bootstrap() { | ||
| 30 | + return new ExtensionBootstrap(); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 扩展点工厂 | ||
| 35 | + * @return | ||
| 36 | + */ | ||
| 37 | + @Bean | ||
| 38 | + @ConditionalOnMissingBean({ExtensionRegisterFactory.class, ExtensionFactory.class}) | ||
| 39 | + public ExtensionRegisterFactory registerFactory() { | ||
| 40 | + return new ExtensionRegisterFactory(); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 扩展组件上下文对象 | ||
| 45 | + * @return | ||
| 46 | + */ | ||
| 47 | + @Bean | ||
| 48 | + @ConditionalOnMissingBean({ExtensionContextHolder.class, ExtensionContext.class}) | ||
| 49 | + public ExtensionContextHolder context() { | ||
| 50 | + return new ExtensionContextHolder(); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 扩展组件执行器 | ||
| 55 | + * @return | ||
| 56 | + */ | ||
| 57 | + @Bean | ||
| 58 | + @ConditionalOnMissingBean(ExtensionExecutor.class) | ||
| 59 | + public ExtensionExecutor executor() { | ||
| 60 | + return new ExtensionExecutor(); | ||
| 61 | + } | ||
| 62 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/BusinessScenario.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core; | ||
| 2 | + | ||
| 3 | +import javax.validation.constraints.NotNull; | ||
| 4 | +import java.io.Serializable; | ||
| 5 | +import java.util.StringJoiner; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * @description 业务场景 = businessId + useCase + scenario, 用来标识系统中唯一的一个场景<br/> | ||
| 9 | + * @author Qingchen | ||
| 10 | + * @version 1.0.0 | ||
| 11 | + * @date 2021-08-28 22:19 | ||
| 12 | + * @class cn.iocoder.yudao.framework.extension.core.BusinessScenario.java | ||
| 13 | + */ | ||
| 14 | +public class BusinessScenario implements Serializable { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 默认业务id | ||
| 18 | + */ | ||
| 19 | + public final static String DEFAULT_BUSINESS_ID = "#defaultBusinessId#"; | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 默认用例 | ||
| 23 | + */ | ||
| 24 | + public final static String DEFAULT_USECASE = "#defaultUseCase#"; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 默认场景 | ||
| 28 | + */ | ||
| 29 | + public final static String DEFAULT_SCENARIO = "#defaultScenario#"; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 分隔符 | ||
| 33 | + */ | ||
| 34 | + private final static String DOT_SEPARATOR = "."; | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 业务Id | ||
| 38 | + */ | ||
| 39 | + private String businessId; | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 用例 | ||
| 43 | + */ | ||
| 44 | + private String useCase; | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * 场景 | ||
| 48 | + */ | ||
| 49 | + private String scenario; | ||
| 50 | + | ||
| 51 | + public BusinessScenario() { | ||
| 52 | + this.businessId = DEFAULT_BUSINESS_ID; | ||
| 53 | + this.useCase = DEFAULT_USECASE; | ||
| 54 | + this.scenario = DEFAULT_SCENARIO; | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + public BusinessScenario(@NotNull String businessId, @NotNull String useCase, @NotNull String scenario) { | ||
| 58 | + this.businessId = businessId; | ||
| 59 | + this.useCase = useCase; | ||
| 60 | + this.scenario = scenario; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + public BusinessScenario(@NotNull String scenario) { | ||
| 64 | + this(); | ||
| 65 | + this.scenario = scenario; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + public BusinessScenario(@NotNull String useCase, @NotNull String scenario) { | ||
| 69 | + this(DEFAULT_BUSINESS_ID, useCase, scenario); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + public String getBusinessId() { | ||
| 73 | + return businessId; | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + public void setBusinessId(String businessId) { | ||
| 77 | + this.businessId = businessId; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + public String getUseCase() { | ||
| 81 | + return useCase; | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + public void setUseCase(String useCase) { | ||
| 85 | + this.useCase = useCase; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + public String getScenario() { | ||
| 89 | + return scenario; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + public void setScenario(String scenario) { | ||
| 93 | + this.scenario = scenario; | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + /** | ||
| 97 | + * 构建业务场景 | ||
| 98 | + * @param businessId | ||
| 99 | + * @param useCase | ||
| 100 | + * @param scenario | ||
| 101 | + * @return | ||
| 102 | + */ | ||
| 103 | + public static BusinessScenario valueOf(@NotNull String businessId, @NotNull String useCase, @NotNull String scenario) { | ||
| 104 | + return new BusinessScenario(businessId, useCase, scenario); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + /** | ||
| 108 | + * 构建业务场景 | ||
| 109 | + * @param useCase | ||
| 110 | + * @param scenario | ||
| 111 | + * @return | ||
| 112 | + */ | ||
| 113 | + public static BusinessScenario valueOf(@NotNull String useCase, @NotNull String scenario) { | ||
| 114 | + return new BusinessScenario(useCase, scenario); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + /** | ||
| 118 | + * 构建业务场景 | ||
| 119 | + * @param scenario | ||
| 120 | + * @return | ||
| 121 | + */ | ||
| 122 | + public static BusinessScenario valueOf(@NotNull String scenario) { | ||
| 123 | + return new BusinessScenario(scenario); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + /** | ||
| 127 | + * 业务场景唯一标识 | ||
| 128 | + * @return | ||
| 129 | + */ | ||
| 130 | + public String getUniqueIdentity(){ | ||
| 131 | + return new StringJoiner(DOT_SEPARATOR).add(businessId).add(useCase).add(scenario).toString(); | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + @Override | ||
| 135 | + public String toString() { | ||
| 136 | + return "BusinessScenario{" + | ||
| 137 | + "businessId='" + businessId + '\'' + | ||
| 138 | + ", useCase='" + useCase + '\'' + | ||
| 139 | + ", scenario='" + scenario + '\'' + | ||
| 140 | + '}'; | ||
| 141 | + } | ||
| 142 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/ExtensionBootstrap.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.factory.ExtensionRegisterFactory; | ||
| 4 | +import org.springframework.beans.BeansException; | ||
| 5 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 6 | +import org.springframework.context.ApplicationContext; | ||
| 7 | +import org.springframework.context.ApplicationContextAware; | ||
| 8 | + | ||
| 9 | +import javax.annotation.PostConstruct; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * @description | ||
| 13 | + * @author Qingchen | ||
| 14 | + * @version 1.0.0 | ||
| 15 | + * @date 2021-08-29 00:18 | ||
| 16 | + * @class cn.iocoder.yudao.framework.extension.core.ExtensionBootstrap.java | ||
| 17 | + */ | ||
| 18 | +public class ExtensionBootstrap implements ApplicationContextAware { | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * spring 容器 | ||
| 22 | + */ | ||
| 23 | + private ApplicationContext applicationContext; | ||
| 24 | + | ||
| 25 | + @Autowired | ||
| 26 | + private ExtensionRegisterFactory registerFactory; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 初始化 | ||
| 30 | + */ | ||
| 31 | + @PostConstruct | ||
| 32 | + public void init() { | ||
| 33 | + registerFactory.setApplicationContext(applicationContext); | ||
| 34 | + registerFactory.register(null); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @Override | ||
| 38 | + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { | ||
| 39 | + this.applicationContext = applicationContext; | ||
| 40 | + } | ||
| 41 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/AbstractComponentExecutor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core.context; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.BusinessScenario; | ||
| 4 | +import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint; | ||
| 5 | + | ||
| 6 | +import java.util.function.Consumer; | ||
| 7 | +import java.util.function.Function; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * @description 执行器通用方法 | ||
| 11 | + * @author Qingchen | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @date 2021-08-29 00:38 | ||
| 14 | + * @class cn.iocoder.yudao.framework.extension.core.context.AbstractComponentExecutor.java | ||
| 15 | + */ | ||
| 16 | +public abstract class AbstractComponentExecutor { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * ("业务" + "用例" + "场景")执行扩展组件,并返回执行结果 | ||
| 20 | + * @param targetClazz | ||
| 21 | + * @param businessId | ||
| 22 | + * @param useCase | ||
| 23 | + * @param scenario | ||
| 24 | + * @param function | ||
| 25 | + * @param <R> | ||
| 26 | + * @param <T> | ||
| 27 | + * @return | ||
| 28 | + */ | ||
| 29 | + public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, String businessId, String useCase, String scenario, Function<T, R> function) { | ||
| 30 | + return execute(targetClazz, BusinessScenario.valueOf(businessId, useCase, scenario), function); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * ("用例" + "场景")执行扩展组件,并返回执行结果 | ||
| 36 | + * @param targetClazz | ||
| 37 | + * @param useCase | ||
| 38 | + * @param scenario | ||
| 39 | + * @param function | ||
| 40 | + * @param <R> | ||
| 41 | + * @param <T> | ||
| 42 | + * @return | ||
| 43 | + */ | ||
| 44 | + public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, String useCase, String scenario, Function<T, R> function) { | ||
| 45 | + return execute(targetClazz, BusinessScenario.valueOf(useCase, scenario), function); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * ("场景")执行扩展组件,并返回执行结果 | ||
| 50 | + * @param targetClazz | ||
| 51 | + * @param scenario | ||
| 52 | + * @param function | ||
| 53 | + * @param <R> | ||
| 54 | + * @param <T> | ||
| 55 | + * @return | ||
| 56 | + */ | ||
| 57 | + public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, String scenario, Function<T, R> function) { | ||
| 58 | + return execute(targetClazz, BusinessScenario.valueOf(scenario), function); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 执行扩展组件,并返回执行结果 | ||
| 63 | + * @param targetClazz | ||
| 64 | + * @param businessScenario | ||
| 65 | + * @param function | ||
| 66 | + * @param <R> Response Type | ||
| 67 | + * @param <T> Parameter Type | ||
| 68 | + * @return | ||
| 69 | + */ | ||
| 70 | + public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, BusinessScenario businessScenario, Function<T, R> function) { | ||
| 71 | + T component = locateComponent(targetClazz, businessScenario); | ||
| 72 | + return function.apply(component); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + /** | ||
| 76 | + * ("业务" + "用例" + "场景")执行扩展组件,适用于无返回值的业务 | ||
| 77 | + * @param targetClazz | ||
| 78 | + * @param businessId | ||
| 79 | + * @param useCase | ||
| 80 | + * @param scenario | ||
| 81 | + * @param consumer | ||
| 82 | + * @param <T> | ||
| 83 | + */ | ||
| 84 | + public <T extends ExtensionPoint> void accept(Class<T> targetClazz, String businessId, String useCase, String scenario, Consumer<T> consumer) { | ||
| 85 | + accept(targetClazz, BusinessScenario.valueOf(businessId, useCase, scenario), consumer); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + /** | ||
| 89 | + * ("场景")执行扩展组件,适用于无返回值的业务 | ||
| 90 | + * @param targetClazz | ||
| 91 | + * @param useCase | ||
| 92 | + * @param scenario | ||
| 93 | + * @param consumer | ||
| 94 | + * @param <T> | ||
| 95 | + */ | ||
| 96 | + public <T extends ExtensionPoint> void accept(Class<T> targetClazz, String useCase, String scenario, Consumer<T> consumer) { | ||
| 97 | + accept(targetClazz, BusinessScenario.valueOf(useCase, scenario), consumer); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + /** | ||
| 101 | + * ("场景")执行扩展组件,适用于无返回值的业务 | ||
| 102 | + * @param targetClazz | ||
| 103 | + * @param scenario | ||
| 104 | + * @param consumer | ||
| 105 | + * @param <T> | ||
| 106 | + */ | ||
| 107 | + public <T extends ExtensionPoint> void accept(Class<T> targetClazz, String scenario, Consumer<T> consumer) { | ||
| 108 | + accept(targetClazz, BusinessScenario.valueOf(scenario), consumer); | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + /** | ||
| 112 | + * 执行扩展组件,适用于无返回值的业务 | ||
| 113 | + * @param targetClazz | ||
| 114 | + * @param businessScenario | ||
| 115 | + * @param consumer | ||
| 116 | + * @param <T> Parameter Type | ||
| 117 | + */ | ||
| 118 | + public <T extends ExtensionPoint> void accept(Class<T> targetClazz, BusinessScenario businessScenario, Consumer<T> consumer) { | ||
| 119 | + T component = locateComponent(targetClazz, businessScenario); | ||
| 120 | + consumer.accept(component); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + /** | ||
| 124 | + * 获取/定位扩展点组件 | ||
| 125 | + * @param targetClazz | ||
| 126 | + * @param businessScenario | ||
| 127 | + * @param <C> | ||
| 128 | + * @return | ||
| 129 | + */ | ||
| 130 | + protected abstract <C extends ExtensionPoint> C locateComponent(Class<C> targetClazz, BusinessScenario businessScenario); | ||
| 131 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/ExtensionContext.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core.context; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.BusinessScenario; | ||
| 4 | +import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * @description 上下文,包含各个扩展点的相关操作 | ||
| 8 | + * @author Qingchen | ||
| 9 | + * @version 1.0.0 | ||
| 10 | + * @date 2021-08-28 22:15 | ||
| 11 | + * @class cn.iocoder.yudao.framework.extension.core.context.ExtensionContext.java | ||
| 12 | + */ | ||
| 13 | +public interface ExtensionContext { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 根据业务场景唯一标识获取扩展点组件实现类 | ||
| 17 | + * @param businessId | ||
| 18 | + * @param useCase | ||
| 19 | + * @param scenario | ||
| 20 | + * @param clazz | ||
| 21 | + * @param <T> | ||
| 22 | + * @return | ||
| 23 | + */ | ||
| 24 | + <T extends ExtensionPoint> T getPoint(String businessId, String useCase, String scenario, Class<T> clazz); | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 根据("实例" + "场景")获取扩展点组件实现类,其中:业务id(businessId)= {@linkplain cn.iocoder.yudao.framework.extension.core.BusinessScenario.DEFAULT_BUSINESS_ID} | ||
| 28 | + * @param useCase | ||
| 29 | + * @param scenario | ||
| 30 | + * @param clazz | ||
| 31 | + * @param <T> | ||
| 32 | + * @return | ||
| 33 | + */ | ||
| 34 | + <T extends ExtensionPoint> T getPoint(String useCase, String scenario, Class<T> clazz); | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 根据("场景")获取扩展点组件实现类 <br/> | ||
| 38 | + * 其中: | ||
| 39 | + * 业务id(businessId)= {@linkplain cn.iocoder.yudao.framework.extension.core.BusinessScenario.DEFAULT_BUSINESS_ID} | ||
| 40 | + * 实例(useCase)= {@linkplain cn.iocoder.yudao.framework.extension.core.BusinessScenario.DEFAULT_USECASE} | ||
| 41 | + * @param scenario | ||
| 42 | + * @param clazz | ||
| 43 | + * @param <T> | ||
| 44 | + * @return | ||
| 45 | + */ | ||
| 46 | + <T extends ExtensionPoint> T getPoint(String scenario, Class<T> clazz); | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 根据业务场景唯一标识获取扩展点组件实现类 | ||
| 50 | + * @param businessScenario | ||
| 51 | + * @param clazz | ||
| 52 | + * @param <T> | ||
| 53 | + * @return | ||
| 54 | + */ | ||
| 55 | + <T extends ExtensionPoint> T getPoint(BusinessScenario businessScenario, Class<T> clazz); | ||
| 56 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/ExtensionContextHolder.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core.context; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.BusinessScenario; | ||
| 4 | +import cn.iocoder.yudao.framework.extension.core.factory.ExtensionFactory; | ||
| 5 | +import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint; | ||
| 6 | +import lombok.extern.slf4j.Slf4j; | ||
| 7 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 8 | +import org.springframework.stereotype.Component; | ||
| 9 | + | ||
| 10 | +import javax.validation.constraints.NotNull; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * @description 上下文及扩展点组件工厂的持有类 | ||
| 14 | + * @author Qingchen | ||
| 15 | + * @version 1.0.0 | ||
| 16 | + * @date 2021-08-29 00:29 | ||
| 17 | + * @class cn.iocoder.yudao.framework.extension.core.context.ExtensionContextHolder.java | ||
| 18 | + */ | ||
| 19 | +@Component | ||
| 20 | +@Slf4j | ||
| 21 | +public class ExtensionContextHolder implements ExtensionContext{ | ||
| 22 | + | ||
| 23 | + @Autowired | ||
| 24 | + private ExtensionFactory factory; | ||
| 25 | + | ||
| 26 | + @Override | ||
| 27 | + public <T extends ExtensionPoint> T getPoint(@NotNull String businessId, @NotNull String useCase, @NotNull String scenario, Class<T> clazz) { | ||
| 28 | + return getPoint(BusinessScenario.valueOf(businessId, useCase, scenario), clazz); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + @Override | ||
| 32 | + public <T extends ExtensionPoint> T getPoint(@NotNull String useCase, String scenario, Class<T> clazz) { | ||
| 33 | + return getPoint(BusinessScenario.valueOf(useCase, scenario), clazz); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + @Override | ||
| 37 | + public <T extends ExtensionPoint> T getPoint(@NotNull String scenario, Class<T> clazz) { | ||
| 38 | + return getPoint(BusinessScenario.valueOf(scenario), clazz); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + @Override | ||
| 42 | + public <T extends ExtensionPoint> T getPoint(@NotNull BusinessScenario businessScenario, Class<T> clazz) { | ||
| 43 | + return factory.get(businessScenario, clazz); | ||
| 44 | + } | ||
| 45 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/context/ExtensionExecutor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core.context; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.BusinessScenario; | ||
| 4 | +import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint; | ||
| 5 | +import lombok.extern.slf4j.Slf4j; | ||
| 6 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 7 | +import org.springframework.stereotype.Component; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * @description 扩展组件执行器 | ||
| 11 | + * @author Qingchen | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @date 2021-08-29 00:32 | ||
| 14 | + * @class cn.iocoder.yudao.framework.extension.core.context.ExtensionExecutor.java | ||
| 15 | + */ | ||
| 16 | +@Component | ||
| 17 | +@Slf4j | ||
| 18 | +public class ExtensionExecutor extends AbstractComponentExecutor{ | ||
| 19 | + | ||
| 20 | + @Autowired | ||
| 21 | + private ExtensionContextHolder contextHolder; | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + @Override | ||
| 25 | + protected <C extends ExtensionPoint> C locateComponent(Class<C> targetClazz, BusinessScenario businessScenario) { | ||
| 26 | + return contextHolder.getPoint(businessScenario, targetClazz); | ||
| 27 | + } | ||
| 28 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/factory/ExtensionDefinition.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core.factory; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.BusinessScenario; | ||
| 4 | +import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint; | ||
| 5 | +import lombok.Getter; | ||
| 6 | +import lombok.Setter; | ||
| 7 | + | ||
| 8 | +import javax.validation.constraints.NotNull; | ||
| 9 | +import java.io.Serializable; | ||
| 10 | +import java.util.Objects; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * @description 扩展定义(扩展坐标),标识唯一一个业务场景实现 | ||
| 14 | + * @author Qingchen | ||
| 15 | + * @version 1.0.0 | ||
| 16 | + * @date 2021-08-28 23:14 | ||
| 17 | + * @class cn.iocoder.yudao.framework.extension.core.factory.ExtensionDefinition.java | ||
| 18 | + */ | ||
| 19 | +@Setter | ||
| 20 | +@Getter | ||
| 21 | +public class ExtensionDefinition implements Serializable { | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 业务场景唯一标识(id) | ||
| 25 | + */ | ||
| 26 | + private String uniqueIdentify; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 扩展点实现类名称 | ||
| 30 | + */ | ||
| 31 | + private String extensionPointName; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 业务场景 | ||
| 35 | + */ | ||
| 36 | + private BusinessScenario businessScenario; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 扩展点实现类 | ||
| 40 | + */ | ||
| 41 | + private ExtensionPoint extensionPoint; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * class | ||
| 45 | + */ | ||
| 46 | + private Class extensionPointClass; | ||
| 47 | + | ||
| 48 | + public ExtensionDefinition() { | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + public ExtensionDefinition(@NotNull BusinessScenario businessScenario, @NotNull ExtensionPoint extensionPoint) { | ||
| 52 | + this.businessScenario = businessScenario; | ||
| 53 | + this.extensionPoint = extensionPoint; | ||
| 54 | + this.uniqueIdentify = this.businessScenario.getUniqueIdentity(); | ||
| 55 | + this.extensionPointClass = this.extensionPoint.getClass(); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 构建definition | ||
| 60 | + * @param businessScenario | ||
| 61 | + * @param point | ||
| 62 | + * @return | ||
| 63 | + */ | ||
| 64 | + public static ExtensionDefinition valueOf(@NotNull BusinessScenario businessScenario, @NotNull ExtensionPoint point) { | ||
| 65 | + return new ExtensionDefinition(businessScenario, point); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + @Override | ||
| 69 | + public boolean equals(Object o) { | ||
| 70 | + if (this == o) { | ||
| 71 | + return true; | ||
| 72 | + } | ||
| 73 | + if (o == null || getClass() != o.getClass()) { | ||
| 74 | + return false; | ||
| 75 | + } | ||
| 76 | + ExtensionDefinition that = (ExtensionDefinition) o; | ||
| 77 | + return Objects.equals(uniqueIdentify, that.uniqueIdentify) && Objects.equals(extensionPointName, that.extensionPointName); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + @Override | ||
| 81 | + public int hashCode() { | ||
| 82 | + final int prime = 31; | ||
| 83 | + int result = 1; | ||
| 84 | + result = prime * result + ((uniqueIdentify == null) ? 0 : uniqueIdentify.hashCode()); | ||
| 85 | + result = prime * result + ((extensionPointName == null) ? 0 : extensionPointName.hashCode()); | ||
| 86 | + return result; | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + @Override | ||
| 90 | + public String toString() { | ||
| 91 | + return "ExtensionDefinition{" + | ||
| 92 | + "uniqueIdentify='" + uniqueIdentify + '\'' + | ||
| 93 | + ", extensionPointName='" + extensionPointName + '\'' + | ||
| 94 | + '}'; | ||
| 95 | + } | ||
| 96 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/factory/ExtensionFactory.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core.factory; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.BusinessScenario; | ||
| 4 | +import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * @description 扩展点工厂 | ||
| 8 | + * @author Qingchen | ||
| 9 | + * @version 1.0.0 | ||
| 10 | + * @date 2021-08-28 23:04 | ||
| 11 | + * @class cn.iocoder.yudao.framework.extension.core.factory.ExtensionFactory.java | ||
| 12 | + */ | ||
| 13 | +public interface ExtensionFactory { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 注册所有扩展点实现类 | ||
| 17 | + * @param basePackage | ||
| 18 | + */ | ||
| 19 | + void register(String basePackage); | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 根据业务场景获取指定类型的扩展点 | ||
| 23 | + * @param businessScenario | ||
| 24 | + * @param clazz | ||
| 25 | + * @param <T> | ||
| 26 | + * @return | ||
| 27 | + */ | ||
| 28 | + <T extends ExtensionPoint> T get(BusinessScenario businessScenario, Class<T> clazz); | ||
| 29 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/factory/ExtensionRegisterFactory.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core.factory; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.BusinessScenario; | ||
| 4 | +import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint; | ||
| 5 | +import cn.iocoder.yudao.framework.extension.core.stereotype.Extension; | ||
| 6 | +import lombok.extern.slf4j.Slf4j; | ||
| 7 | +import org.springframework.aop.support.AopUtils; | ||
| 8 | +import org.springframework.context.ApplicationContext; | ||
| 9 | +import org.springframework.core.annotation.AnnotationUtils; | ||
| 10 | +import org.springframework.stereotype.Component; | ||
| 11 | +import org.springframework.util.ClassUtils; | ||
| 12 | + | ||
| 13 | +import javax.validation.constraints.NotNull; | ||
| 14 | +import java.util.Map; | ||
| 15 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 16 | + | ||
| 17 | +/** | ||
| 18 | + * @description 注册工厂 | ||
| 19 | + * @author Qingchen | ||
| 20 | + * @version 1.0.0 | ||
| 21 | + * @date 2021-08-28 23:07 | ||
| 22 | + * @class cn.iocoder.yudao.framework.extension.core.factory.ExtensionRegisterFactory.java | ||
| 23 | + */ | ||
| 24 | +@Component | ||
| 25 | +@Slf4j | ||
| 26 | +public class ExtensionRegisterFactory implements ExtensionFactory { | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * spring ApplicationContext | ||
| 30 | + */ | ||
| 31 | + private ApplicationContext applicationContext; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 扩展点实现类集合 | ||
| 35 | + */ | ||
| 36 | + private Map<String, ExtensionDefinition> registerExtensionBeans = new ConcurrentHashMap<>(); | ||
| 37 | + | ||
| 38 | + @Override | ||
| 39 | + public void register(String basePackage) { | ||
| 40 | + final Map<String, Object> beans = applicationContext.getBeansWithAnnotation(Extension.class); | ||
| 41 | + if(beans == null || beans.isEmpty()) { | ||
| 42 | + return; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + beans.values().forEach(point -> doRegister((ExtensionPoint) point)); | ||
| 46 | + log.info("业务场景相关扩展点注册完成,注册数量: {}", registerExtensionBeans.size()); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + @Override | ||
| 50 | + public <T extends ExtensionPoint> T get(BusinessScenario businessScenario, Class<T> clazz) { | ||
| 51 | + | ||
| 52 | + final ExtensionDefinition definition = registerExtensionBeans.get(businessScenario.getUniqueIdentity()); | ||
| 53 | + if(definition == null) { | ||
| 54 | + log.error("获取业务场景扩展点实现失败,失败原因:尚未定义该业务场景相关扩展点。{}", businessScenario); | ||
| 55 | + throw new RuntimeException("尚未定义该业务场景相关扩展点 [" + businessScenario + "]"); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + return (T) definition.getExtensionPoint(); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 注册扩展点 | ||
| 63 | + * @param point | ||
| 64 | + */ | ||
| 65 | + private void doRegister(@NotNull ExtensionPoint point) { | ||
| 66 | + Class<?> extensionClazz = point.getClass(); | ||
| 67 | + | ||
| 68 | + if (AopUtils.isAopProxy(point)) { | ||
| 69 | + extensionClazz = ClassUtils.getUserClass(point); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + Extension extension = AnnotationUtils.findAnnotation(extensionClazz, Extension.class); | ||
| 73 | + final BusinessScenario businessScenario = BusinessScenario.valueOf(extension.businessId(), extension.useCase(), extension.scenario()); | ||
| 74 | + final ExtensionDefinition definition = ExtensionDefinition.valueOf(businessScenario, point); | ||
| 75 | + final ExtensionDefinition exist = registerExtensionBeans.get(businessScenario.getUniqueIdentity()); | ||
| 76 | + if(exist != null && !exist.equals(definition)) { | ||
| 77 | + throw new RuntimeException("相同的业务场景重复注册了不同类型的扩展点实现 :【" + definition + "】【" + exist + "】"); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + registerExtensionBeans.put(businessScenario.getUniqueIdentity(), definition); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + public void setApplicationContext(ApplicationContext applicationContext) { | ||
| 84 | + this.applicationContext = applicationContext; | ||
| 85 | + } | ||
| 86 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/package-info.java
0 → 100644
| 1 | +/** | ||
| 2 | + * @description core 核心 | ||
| 3 | + * @author Qingchen | ||
| 4 | + * @version 1.0.0 | ||
| 5 | + * @date 2021-08-28 21:54 | ||
| 6 | + * @class cn.iocoder.yudao.framework.extension.core.package-info.java | ||
| 7 | + */ | ||
| 8 | +package cn.iocoder.yudao.framework.extension.core; | ||
| 0 | \ No newline at end of file | 9 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/point/ExtensionPoint.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core.point; | ||
| 2 | +/** | ||
| 3 | + * @description 扩展点 <br/> | ||
| 4 | + * 表示一块逻辑在不同的业务有不同的实现,使用扩展点做接口申明,然后用{@linkplain cn.iocoder.yudao.framework.extension.core.stereotype.Extension}(扩展)去实现扩展点。 | ||
| 5 | + * @author Qingchen | ||
| 6 | + * @version 1.0.0 | ||
| 7 | + * @date 2021-08-28 22:06 | ||
| 8 | + * @class cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint.java | ||
| 9 | + */ | ||
| 10 | +public interface ExtensionPoint { | ||
| 11 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/core/stereotype/Extension.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.core.stereotype; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.BusinessScenario; | ||
| 4 | +import org.springframework.stereotype.Component; | ||
| 5 | + | ||
| 6 | +import java.lang.annotation.*; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * @description 表示带注释的类是“扩展组件” | ||
| 10 | + * @author Qingchen | ||
| 11 | + * @version 1.0.0 | ||
| 12 | + * @date 2021-08-28 21:59 | ||
| 13 | + * @class cn.iocoder.yudao.framework.extension.core.stereotype.Extension.java | ||
| 14 | + */ | ||
| 15 | +@Inherited | ||
| 16 | +@Retention(RetentionPolicy.RUNTIME) | ||
| 17 | +@Target({ElementType.TYPE}) | ||
| 18 | +@Component | ||
| 19 | +public @interface Extension { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 业务 <br/> | ||
| 23 | + * 一个自负盈亏的财务主体,比如tmall、淘宝和零售通就是三个不同的业务 | ||
| 24 | + * @return | ||
| 25 | + */ | ||
| 26 | + String businessId() default BusinessScenario.DEFAULT_BUSINESS_ID; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 用例 <br/> | ||
| 30 | + * 描述了用户和系统之间的互动,每个用例提供了一个或多个场景。比如,支付订单就是一个典型的用例。 | ||
| 31 | + * @return | ||
| 32 | + */ | ||
| 33 | + String useCase() default BusinessScenario.DEFAULT_USECASE; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 场景 <br/> | ||
| 37 | + * 场景也被称为用例的实例(Instance),包括用例所有的可能情况(正常的和异常的)。比如对于"订单支付"这个用例,就有“支付宝支付”、“银行卡支付”、"微信支付"等多个场景 | ||
| 38 | + * @return | ||
| 39 | + */ | ||
| 40 | + String scenario() default BusinessScenario.DEFAULT_SCENARIO; | ||
| 41 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/main/java/cn/iocoder/yudao/framework/extension/package-info.java
0 → 100644
yudao-framework/yudao-spring-boot-starter-extension/src/main/resources/META-INF/spring.factories
0 → 100644
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/Application.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension; | ||
| 2 | + | ||
| 3 | +import org.springframework.boot.SpringApplication; | ||
| 4 | +import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * @description Application | ||
| 8 | + * @author Qingchen | ||
| 9 | + * @version 1.0.0 | ||
| 10 | + * @date 2021-08-30 10:32 | ||
| 11 | + * @class cn.iocoder.yudao.framework.extension.Application.java | ||
| 12 | + */ | ||
| 13 | +@SpringBootApplication | ||
| 14 | +public class Application { | ||
| 15 | + | ||
| 16 | + public static void main(String[] args) { | ||
| 17 | + SpringApplication.run(Application.class, args); | ||
| 18 | + } | ||
| 19 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/ExtensionTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.IdUtil; | ||
| 4 | +import cn.hutool.json.JSONUtil; | ||
| 5 | +import cn.iocoder.yudao.framework.extension.core.BusinessScenario; | ||
| 6 | +import cn.iocoder.yudao.framework.extension.core.context.ExtensionExecutor; | ||
| 7 | +import cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint; | ||
| 8 | +import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand; | ||
| 9 | +import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult; | ||
| 10 | +import lombok.extern.slf4j.Slf4j; | ||
| 11 | +import org.junit.Assert; | ||
| 12 | +import org.junit.Test; | ||
| 13 | +import org.junit.runner.RunWith; | ||
| 14 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 15 | +import org.springframework.boot.test.context.SpringBootTest; | ||
| 16 | +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; | ||
| 17 | + | ||
| 18 | +import java.math.BigDecimal; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * @description | ||
| 22 | + * @author Qingchen | ||
| 23 | + * @version 1.0.0 | ||
| 24 | + * @date 2021-08-30 10:30 | ||
| 25 | + * @class cn.iocoder.yudao.framework.extension.ExtensionTest.java | ||
| 26 | + */ | ||
| 27 | +@RunWith(SpringJUnit4ClassRunner.class) | ||
| 28 | +@SpringBootTest(classes = Application.class) | ||
| 29 | +@Slf4j | ||
| 30 | +public class ExtensionTest { | ||
| 31 | + | ||
| 32 | + @Autowired | ||
| 33 | + private ExtensionExecutor extensionExecutor; | ||
| 34 | + | ||
| 35 | + @Test | ||
| 36 | + public void unifiedOrder() { | ||
| 37 | + final BusinessScenario scenario = BusinessScenario.valueOf("pay", "jsapi", "wechat"); | ||
| 38 | + final TransactionsCommand command = new TransactionsCommand(IdUtil.objectId(), new BigDecimal(105), "Image形象店-深圳腾大-QQ公仔", "https://www.weixin.qq.com/wxpay/pay.php"); | ||
| 39 | + final TransactionsResult result = extensionExecutor.execute(PayExtensionPoint.class, scenario, extension -> extension.unifiedOrder(command)); | ||
| 40 | + log.info("result is: {}", JSONUtil.toJsonStr(result)); | ||
| 41 | + Assert.assertSame("wechat", result.getChannel()); | ||
| 42 | + } | ||
| 43 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/package-info.java
0 → 100644
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/PayExtensionPoint.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.pay; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint; | ||
| 4 | +import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand; | ||
| 5 | +import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * @description 支付操作接口 | ||
| 9 | + * @author Qingchen | ||
| 10 | + * @version 1.0.0 | ||
| 11 | + * @date 2021-08-30 10:35 | ||
| 12 | + * @class cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint.java | ||
| 13 | + */ | ||
| 14 | +public interface PayExtensionPoint extends ExtensionPoint { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 统一下单:获取"预支付交易会话标识" | ||
| 18 | + * @param command | ||
| 19 | + * @return | ||
| 20 | + */ | ||
| 21 | + TransactionsResult unifiedOrder(TransactionsCommand command); | ||
| 22 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/command/TransactionsCommand.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.pay.command; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.NoArgsConstructor; | ||
| 6 | + | ||
| 7 | +import java.io.Serializable; | ||
| 8 | +import java.math.BigDecimal; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * @description 下单请求 | ||
| 12 | + * @author Qingchen | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @date 2021-08-30 10:48 | ||
| 15 | + * @class cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand.java | ||
| 16 | + */ | ||
| 17 | +@Data | ||
| 18 | +@NoArgsConstructor | ||
| 19 | +@AllArgsConstructor | ||
| 20 | +public class TransactionsCommand implements Serializable { | ||
| 21 | + /** | ||
| 22 | + * 订单编号 | ||
| 23 | + */ | ||
| 24 | + private String orderNo; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 支付金额 | ||
| 28 | + */ | ||
| 29 | + private BigDecimal amount; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 商品描述 | ||
| 33 | + */ | ||
| 34 | + private String productDescription; | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 通知地址 | ||
| 38 | + */ | ||
| 39 | + private String notifyUrl; | ||
| 40 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/domain/TransactionsResult.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.pay.domain; | ||
| 2 | + | ||
| 3 | +import lombok.*; | ||
| 4 | + | ||
| 5 | +import java.io.Serializable; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * @description 下单: 预支付交易单返回结果 | ||
| 9 | + * @author Qingchen | ||
| 10 | + * @version 1.0.0 | ||
| 11 | + * @date 2021-08-30 10:43 | ||
| 12 | + * @class cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult.java | ||
| 13 | + */ | ||
| 14 | +@Data | ||
| 15 | +@AllArgsConstructor | ||
| 16 | +public class TransactionsResult implements Serializable { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 预支付交易会话标识 | ||
| 20 | + */ | ||
| 21 | + private String prepayId; | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 订单编号 | ||
| 25 | + */ | ||
| 26 | + private String orderNo; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 系统内部支付单号 | ||
| 30 | + */ | ||
| 31 | + private String paymentNo; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 支付渠道:微信 or 支付宝 | ||
| 35 | + */ | ||
| 36 | + private String channel; | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/impl/AlipayService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.pay.impl; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.IdUtil; | ||
| 4 | +import cn.hutool.json.JSONUtil; | ||
| 5 | +import cn.iocoder.yudao.framework.extension.core.stereotype.Extension; | ||
| 6 | +import cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint; | ||
| 7 | +import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand; | ||
| 8 | +import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult; | ||
| 9 | +import lombok.extern.slf4j.Slf4j; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * @description 微信 JSAPI 支付 | ||
| 13 | + * @author Qingchen | ||
| 14 | + * @version 1.0.0 | ||
| 15 | + * @date 2021-08-30 10:38 | ||
| 16 | + * @class cn.iocoder.yudao.framework.extension.pay.impl.AlipayService.java | ||
| 17 | + */ | ||
| 18 | +@Extension(businessId = "pay", useCase = "jsapi", scenario = "alipay") | ||
| 19 | +@Slf4j | ||
| 20 | +public class AlipayService implements PayExtensionPoint { | ||
| 21 | + @Override | ||
| 22 | + public TransactionsResult unifiedOrder(TransactionsCommand command) { | ||
| 23 | + log.info("微信 JSAPI 支付:{}", JSONUtil.toJsonStr(command)); | ||
| 24 | + return new TransactionsResult("alipay26112221580621e9b071c00d9e093b0000", command.getOrderNo(), IdUtil.objectId(), "alipay"); | ||
| 25 | + } | ||
| 26 | +} |
yudao-framework/yudao-spring-boot-starter-extension/src/test/java/cn/iocoder/yudao/framework/extension/pay/impl/WechatPayService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.extension.pay.impl; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.IdUtil; | ||
| 4 | +import cn.hutool.json.JSONUtil; | ||
| 5 | +import cn.iocoder.yudao.framework.extension.core.stereotype.Extension; | ||
| 6 | +import cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint; | ||
| 7 | +import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand; | ||
| 8 | +import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult; | ||
| 9 | +import lombok.extern.slf4j.Slf4j; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * @description 微信 JSAPI 支付 | ||
| 13 | + * @author Qingchen | ||
| 14 | + * @version 1.0.0 | ||
| 15 | + * @date 2021-08-30 10:37 | ||
| 16 | + * @class cn.iocoder.yudao.framework.extension.pay.impl.WechatPayService.java | ||
| 17 | + */ | ||
| 18 | +@Extension(businessId = "pay", useCase = "jsapi", scenario = "wechat") | ||
| 19 | +@Slf4j | ||
| 20 | +public class WechatPayService implements PayExtensionPoint { | ||
| 21 | + @Override | ||
| 22 | + public TransactionsResult unifiedOrder(TransactionsCommand command) { | ||
| 23 | + log.info("微信 JSAPI 支付:{}", JSONUtil.toJsonStr(command)); | ||
| 24 | + return new TransactionsResult("wx26112221580621e9b071c00d9e093b0000", command.getOrderNo(), IdUtil.objectId(), "wechat"); | ||
| 25 | + } | ||
| 26 | +} |
yudao-framework/yudao-spring-boot-starter-extension/《芋道 Spring Boot 扩展点组件》.md
0 → 100644
| 1 | +### 作用 | ||
| 2 | + | ||
| 3 | + 为了解决同一个流程不同业务有不同处理逻辑而产生,减少代码中 if else 逻辑,降低代码的耦合性,通过统一的扩展形式来支撑业务的变化。 | ||
| 4 | + | ||
| 5 | +### 原理 | ||
| 6 | + | ||
| 7 | + https://blog.csdn.net/significantfrank/article/details/100074716 | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | +### 使用介绍 | ||
| 12 | + | ||
| 13 | +参考测试代码 `cn.iocoder.yudao.framework.extension.ExtensionTest` | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + |