mirror of
				https://github.moeyy.xyz/https://github.com/GeWuYou/forgeboot
				synced 2025-10-31 10:14:21 +08:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
			446b859a8a
			...
			8a449467ab
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8a449467ab | |||
| 96469ac1e3 | |||
| 05a8d3d409 | |||
| 2258f26544 | |||
| c3f4b6973c | |||
| 6c30120016 | |||
| 806cefd248 | |||
| 6942de1320 | |||
| 69c59aeba5 | |||
| 37309f5b15 | |||
| 0e74483168 | |||
| b90048a57d | |||
| ebe801f6bb | 
| @ -7,6 +7,12 @@ | |||||||
|  */ |  */ | ||||||
| object Modules { | object Modules { | ||||||
| 
 | 
 | ||||||
|  |     object Context{ | ||||||
|  |         const val STARTER = ":forgeboot-context-spring-boot-starter" | ||||||
|  |         const val API = ":forgeboot-context-spring-boot-starter:forgeboot-context-api" | ||||||
|  |         const val IMPL = ":forgeboot-context-spring-boot-starter:forgeboot-context-impl" | ||||||
|  |         const val AUTOCONFIGURE = ":forgeboot-context-spring-boot-starter:forgeboot-context-autoconfigure" | ||||||
|  |     } | ||||||
|     object Webmvc { |     object Webmvc { | ||||||
|         const val STARTER = ":forgeboot-webmvc-spring-boot-starter" |         const val STARTER = ":forgeboot-webmvc-spring-boot-starter" | ||||||
|         const val DTO = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-dto" |         const val DTO = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-dto" | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								forgeboot-context/forgeboot-context-api/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								forgeboot-context/forgeboot-context-api/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | /gradlew text eol=lf | ||||||
|  | *.bat text eol=crlf | ||||||
|  | *.jar binary | ||||||
							
								
								
									
										40
									
								
								forgeboot-context/forgeboot-context-api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								forgeboot-context/forgeboot-context-api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  | HELP.md | ||||||
|  | .gradle | ||||||
|  | build/ | ||||||
|  | !gradle/wrapper/gradle-wrapper.jar | ||||||
|  | !**/src/main/**/build/ | ||||||
|  | !**/src/test/**/build/ | ||||||
|  | 
 | ||||||
|  | ### STS ### | ||||||
|  | .apt_generated | ||||||
|  | .classpath | ||||||
|  | .factorypath | ||||||
|  | .project | ||||||
|  | .settings | ||||||
|  | .springBeans | ||||||
|  | .sts4-cache | ||||||
|  | bin/ | ||||||
|  | !**/src/main/**/bin/ | ||||||
|  | !**/src/test/**/bin/ | ||||||
|  | 
 | ||||||
|  | ### IntelliJ IDEA ### | ||||||
|  | .idea | ||||||
|  | *.iws | ||||||
|  | *.iml | ||||||
|  | *.ipr | ||||||
|  | out/ | ||||||
|  | !**/src/main/**/out/ | ||||||
|  | !**/src/test/**/out/ | ||||||
|  | 
 | ||||||
|  | ### NetBeans ### | ||||||
|  | /nbproject/private/ | ||||||
|  | /nbbuild/ | ||||||
|  | /dist/ | ||||||
|  | /nbdist/ | ||||||
|  | /.nb-gradle/ | ||||||
|  | 
 | ||||||
|  | ### VS Code ### | ||||||
|  | .vscode/ | ||||||
|  | 
 | ||||||
|  | ### Kotlin ### | ||||||
|  | .kotlin | ||||||
							
								
								
									
										4
									
								
								forgeboot-context/forgeboot-context-api/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								forgeboot-context/forgeboot-context-api/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | 
 | ||||||
|  | dependencies { | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,57 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.api | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  *抽象上下文 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 13:36:54 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | abstract class AbstractContext<K, V>:  Context<K, V> { | ||||||
|  |     private val local= ThreadLocal.withInitial { mutableMapOf<K,V>() } | ||||||
|  |     /** | ||||||
|  |      * 将指定的键值对存入上下文中。 | ||||||
|  |      * | ||||||
|  |      * @param key   要存储的键 | ||||||
|  |      * @param value 要存储的值,可以为 null | ||||||
|  |      */ | ||||||
|  |     override fun put(key: K, value: V?) { | ||||||
|  |         value?.let { | ||||||
|  |             local.get()[key] = it | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 根据指定的键从上下文中获取对应的值。 | ||||||
|  |      * | ||||||
|  |      * @param key 要查找的键 | ||||||
|  |      * @return 对应的值,如果不存在则返回 null | ||||||
|  |      */ | ||||||
|  |     override fun get(key: K): V? { | ||||||
|  |         return local.get()[key] | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 获取当前上下文的一个快照,包含所有键值对。 | ||||||
|  |      * | ||||||
|  |      * @return 一个 Map,表示当前上下文中的所有键值对 | ||||||
|  |      */ | ||||||
|  |     override fun snapshot(): Map<K, V> { | ||||||
|  |         return HashMap(local.get()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 清除上下文中的所有键值对。 | ||||||
|  |      */ | ||||||
|  |     override fun clear() { | ||||||
|  |         local.remove() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 从上下文中移除指定的键值对。 | ||||||
|  |      * | ||||||
|  |      * @param key 要移除的键 | ||||||
|  |      */ | ||||||
|  |     override fun remove(key: K) { | ||||||
|  |         local.get().remove(key) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,44 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.api | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 上下文接口,用于管理键值对的存储、获取和清理操作。 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 13:34:04 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | interface Context<K, V> { | ||||||
|  |     /** | ||||||
|  |      * 将指定的键值对存入上下文中。 | ||||||
|  |      * | ||||||
|  |      * @param key   要存储的键 | ||||||
|  |      * @param value 要存储的值,可以为 null | ||||||
|  |      */ | ||||||
|  |     fun put(key: K, value: V?) | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 根据指定的键从上下文中获取对应的值。 | ||||||
|  |      * | ||||||
|  |      * @param key 要查找的键 | ||||||
|  |      * @return 对应的值,如果不存在则返回 null | ||||||
|  |      */ | ||||||
|  |     fun get(key: K): V? | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 获取当前上下文的一个快照,包含所有键值对。 | ||||||
|  |      * | ||||||
|  |      * @return 一个 Map,表示当前上下文中的所有键值对 | ||||||
|  |      */ | ||||||
|  |     fun snapshot(): Map<K, V?> | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 清除上下文中的所有键值对。 | ||||||
|  |      */ | ||||||
|  |     fun clear() | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 从上下文中移除指定的键值对。 | ||||||
|  |      * | ||||||
|  |      * @param key 要移除的键 | ||||||
|  |      */ | ||||||
|  |     fun remove(key: K) | ||||||
|  | } | ||||||
| @ -0,0 +1,20 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.api | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.entities.FieldDef | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 上下文字段贡献者接口,用于定义上下文所需字段的契约。 | ||||||
|  |  * | ||||||
|  |  * 实现此接口的类应提供一组上下文字段定义([FieldDef]),用于描述当前上下文的数据结构。 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 13:32:39 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | fun interface ContextFieldContributor { | ||||||
|  |     /** | ||||||
|  |      * 提供上下文字段定义集合。 | ||||||
|  |      * | ||||||
|  |      * @return 返回一个不可变的[Set]集合,包含当前上下文所需的所有字段定义对象[FieldDef]。 | ||||||
|  |      */ | ||||||
|  |     fun fields(): Set<FieldDef> | ||||||
|  | } | ||||||
| @ -0,0 +1,53 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.api | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 上下文处理器 | ||||||
|  |  * | ||||||
|  |  * 用于定义上下文的处理逻辑,包括顺序控制、上下文提取与注入。 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 15:07:13 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | interface ContextProcessor : Comparable<ContextProcessor> { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 获取当前处理器的执行顺序优先级。 | ||||||
|  |      * | ||||||
|  |      * 默认实现返回0,数值越小优先级越高。 | ||||||
|  |      * | ||||||
|  |      * @return Int 表示当前处理器的顺序值 | ||||||
|  |      */ | ||||||
|  |     fun order(): Int = 0 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 从给定的载体中提取上下文信息,并填充到上下文对象中。 | ||||||
|  |      * | ||||||
|  |      * 默认实现为空方法,子类可根据需要重写此方法。 | ||||||
|  |      * | ||||||
|  |      * @param carrier 载体对象,通常包含上下文数据 | ||||||
|  |      * @param ctx 可变映射,用于存储提取出的上下文键值对 | ||||||
|  |      */ | ||||||
|  |     fun extract(carrier: Any, ctx: MutableMap<String, String>) { | ||||||
|  |         // do nothing | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 将上下文信息注入到给定的载体中。 | ||||||
|  |      * | ||||||
|  |      * 默认实现为空方法,子类可根据需要重写此方法。 | ||||||
|  |      * | ||||||
|  |      * @param carrier 载体对象,将上下文数据注入其中 | ||||||
|  |      * @param ctx 包含上下文键值对的映射 | ||||||
|  |      */ | ||||||
|  |     fun inject(carrier: Any, ctx: MutableMap<String, String>) { | ||||||
|  |         // do nothing | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 根据处理器的执行顺序进行比较,实现Comparable接口的方法。 | ||||||
|  |      * | ||||||
|  |      * @param other 待比较的另一个上下文处理器 | ||||||
|  |      * @return Int 当前对象与other对象的顺序差值,用于排序 | ||||||
|  |      */ | ||||||
|  |     override fun compareTo(other: ContextProcessor) = order() - other.order() | ||||||
|  | } | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.api | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.entities.FieldDef | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 字段注册表接口 | ||||||
|  |  * | ||||||
|  |  * 该接口定义了字段注册表的基本操作,包括获取所有字段定义和通过字段头查询字段定义。 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 14:44:40 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | interface FieldRegistry { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 获取所有字段定义的集合 | ||||||
|  |      * | ||||||
|  |      * @return 返回包含所有字段定义的集合 | ||||||
|  |      */ | ||||||
|  |     fun all(): Collection<FieldDef> | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 根据字段头查找对应的字段定义 | ||||||
|  |      * | ||||||
|  |      * @param header 字段头信息 | ||||||
|  |      * @return 如果找到匹配的字段定义则返回,否则返回 null | ||||||
|  |      */ | ||||||
|  |     fun findByHeader(header: String): FieldDef? | ||||||
|  | } | ||||||
| @ -0,0 +1,21 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.api.entities | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.enums.Scope | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 字段定义,用于描述数据结构中的字段属性。 | ||||||
|  |  * | ||||||
|  |  * @property header 字段的显示名称,通常用于界面展示。 | ||||||
|  |  * @property key 字段的唯一标识符,用于数据映射和识别。 | ||||||
|  |  * @property generator 生成字段值的函数,默认为 null,表示不使用动态生成。 | ||||||
|  |  * @property scopes 定义该字段适用的上下文范围,默认包括 HEADER 和 MDC。 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 13:31:32 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | data class FieldDef( | ||||||
|  |     val header: String, | ||||||
|  |     val key: String, | ||||||
|  |     val generator: (() -> String)? = null, | ||||||
|  |     val scopes: Set<Scope> = setOf(Scope.HEADER, Scope.MDC) | ||||||
|  | ) | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.api.enums | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 范围枚举类,定义了不同上下文信息的存储位置 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 13:29:57 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | enum class Scope { | ||||||
|  |     /** | ||||||
|  |      * 存储在请求头(Header)中 | ||||||
|  |      */ | ||||||
|  |     HEADER, | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 存储在 MDC(Mapped Diagnostic Context)中,用于日志跟踪 | ||||||
|  |      */ | ||||||
|  |     MDC, | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 存储在 Reactor 上下文中 | ||||||
|  |      */ | ||||||
|  |     REACTOR, | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 存储在 Baggage 中,用于分布式追踪 | ||||||
|  |      */ | ||||||
|  |     BAGGAGE | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								forgeboot-context/forgeboot-context-autoconfigure/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								forgeboot-context/forgeboot-context-autoconfigure/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | /gradlew text eol=lf | ||||||
|  | *.bat text eol=crlf | ||||||
|  | *.jar binary | ||||||
							
								
								
									
										40
									
								
								forgeboot-context/forgeboot-context-autoconfigure/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								forgeboot-context/forgeboot-context-autoconfigure/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  | HELP.md | ||||||
|  | .gradle | ||||||
|  | build/ | ||||||
|  | !gradle/wrapper/gradle-wrapper.jar | ||||||
|  | !**/src/main/**/build/ | ||||||
|  | !**/src/test/**/build/ | ||||||
|  | 
 | ||||||
|  | ### STS ### | ||||||
|  | .apt_generated | ||||||
|  | .classpath | ||||||
|  | .factorypath | ||||||
|  | .project | ||||||
|  | .settings | ||||||
|  | .springBeans | ||||||
|  | .sts4-cache | ||||||
|  | bin/ | ||||||
|  | !**/src/main/**/bin/ | ||||||
|  | !**/src/test/**/bin/ | ||||||
|  | 
 | ||||||
|  | ### IntelliJ IDEA ### | ||||||
|  | .idea | ||||||
|  | *.iws | ||||||
|  | *.iml | ||||||
|  | *.ipr | ||||||
|  | out/ | ||||||
|  | !**/src/main/**/out/ | ||||||
|  | !**/src/test/**/out/ | ||||||
|  | 
 | ||||||
|  | ### NetBeans ### | ||||||
|  | /nbproject/private/ | ||||||
|  | /nbbuild/ | ||||||
|  | /dist/ | ||||||
|  | /nbdist/ | ||||||
|  | /.nb-gradle/ | ||||||
|  | 
 | ||||||
|  | ### VS Code ### | ||||||
|  | .vscode/ | ||||||
|  | 
 | ||||||
|  | ### Kotlin ### | ||||||
|  | .kotlin | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | plugins{ | ||||||
|  |     alias(libs.plugins.kotlin.plugin.spring) | ||||||
|  | } | ||||||
|  | dependencies { | ||||||
|  |     implementation(platform(libs.springBootDependencies.bom)) | ||||||
|  |     implementation(libs.springBoot.autoconfigure) | ||||||
|  |     compileOnly(project(Modules.Context.API)) | ||||||
|  |     compileOnly(project(Modules.Context.IMPL)) | ||||||
|  |     compileOnly(libs.springCloudDependencies.bom) | ||||||
|  |     compileOnly(libs.springBootStarter.web) | ||||||
|  |     compileOnly(libs.springBootStarter.webflux) | ||||||
|  |     compileOnly(libs.springCloudStarter.openfeign) | ||||||
|  | } | ||||||
| @ -0,0 +1,253 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.autoconfigure | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.* | ||||||
|  | import com.gewuyou.forgeboot.context.impl.* | ||||||
|  | import com.gewuyou.forgeboot.context.impl.filter.ContextServletFilter | ||||||
|  | import com.gewuyou.forgeboot.context.impl.filter.ContextWebFilter | ||||||
|  | import com.gewuyou.forgeboot.context.impl.processor.* | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | import org.springframework.context.annotation.Bean | ||||||
|  | import org.springframework.context.annotation.Configuration | ||||||
|  | import org.springframework.boot.autoconfigure.condition.* | ||||||
|  | import org.springframework.boot.web.reactive.function.client.WebClientCustomizer | ||||||
|  | import org.springframework.core.task.TaskDecorator | ||||||
|  | import org.springframework.web.reactive.function.client.ClientRequest | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 配置类,用于自动配置上下文相关的 Bean。 | ||||||
|  |  * | ||||||
|  |  * 该配置类根据不同的运行时依赖和配置条件,定义了一系列的 Bean, | ||||||
|  |  * 实现了上下文字段在不同场景下的传播与管理机制。 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 11:48:01 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | @Configuration | ||||||
|  | class ForgeContextAutoConfiguration { | ||||||
|  |     /* ─────────────────────────────────────────────────────────────── | ||||||
|  |        0️⃣ 通用 Bean:不依赖 Web / Feign / Reactor 等外部包 | ||||||
|  |     ─────────────────────────────────────────────────────────────── */ | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 创建 FieldRegistry Bean,用于注册上下文中所有字段定义。 | ||||||
|  |      * | ||||||
|  |      * FieldRegistry 是上下文字段的核心注册中心,聚合所有 ContextFieldContributor 提供的字段定义。 | ||||||
|  |      * | ||||||
|  |      * @param contributors 提供字段定义的贡献者列表 | ||||||
|  |      * @return 构建完成的 FieldRegistry 实例 | ||||||
|  |      */ | ||||||
|  |     @Bean | ||||||
|  |     @ConditionalOnMissingBean | ||||||
|  |     fun fieldRegistry(contributors: List<ContextFieldContributor>): FieldRegistry = | ||||||
|  |         DefaultFieldRegistry(contributors.flatMap { it.fields() }.toSet()) | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 创建 HeaderProcessor Bean,用于处理请求头中的上下文字段。 | ||||||
|  |      * | ||||||
|  |      * HeaderProcessor 负责从请求头中提取上下文字段并注入到当前线程上下文中。 | ||||||
|  |      * | ||||||
|  |      * @param reg 字段注册表 | ||||||
|  |      * @return 构建完成的 HeaderProcessor 实例 | ||||||
|  |      */ | ||||||
|  |     @Bean("headerProcessor") | ||||||
|  |     fun headerProcessor(reg: FieldRegistry) = HeaderProcessor(reg) | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 创建 GeneratorProcessor Bean,用于生成上下文字段值。 | ||||||
|  |      * | ||||||
|  |      * GeneratorProcessor 根据字段定义生成默认值(如 traceId、spanId 等),适用于首次进入系统的情况。 | ||||||
|  |      * | ||||||
|  |      * @param reg 字段注册表 | ||||||
|  |      * @return 构建完成的 GeneratorProcessor 实例 | ||||||
|  |      */ | ||||||
|  |     @Bean("generatorProcessor") | ||||||
|  |     fun generatorProcessor(reg: FieldRegistry) = GeneratorProcessor(reg) | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 创建 MdcProcessor Bean,用于将上下文字段写入 MDC(Mapped Diagnostic Context)。 | ||||||
|  |      * | ||||||
|  |      * MdcProcessor 使得日志框架(如 Logback)可以访问当前上下文字段,便于日志追踪。 | ||||||
|  |      * | ||||||
|  |      * @param reg 字段注册表 | ||||||
|  |      * @return 构建完成的 MdcProcessor 实例 | ||||||
|  |      */ | ||||||
|  |     @Bean("mdcProcessor") | ||||||
|  |     fun mdcProcessor(reg: FieldRegistry) = MdcProcessor(reg) | ||||||
|  | 
 | ||||||
|  |     /* ─────────────────────────────────────────────────────────────── | ||||||
|  |        1️⃣ Reactor 支持(只有 classpath 有 Reactor 时才激活) | ||||||
|  |     ─────────────────────────────────────────────────────────────── */ | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 配置类,提供对 Reactor 上下文传播的支持。 | ||||||
|  |      */ | ||||||
|  |     @Configuration(proxyBeanMethods = false) | ||||||
|  |     @ConditionalOnClass(name = ["reactor.util.context.Context"]) | ||||||
|  |     class ReactorSupport { | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 创建 ReactorProcessor Bean,用于在 Reactor 上下文中传播上下文字段。 | ||||||
|  |          * | ||||||
|  |          * ReactorProcessor 适配了 Reactor 的 Context 接口,确保上下文字段在响应式流中正确传递。 | ||||||
|  |          * | ||||||
|  |          * @param reg 字段注册表 | ||||||
|  |          * @return 构建完成的 ReactorProcessor 实例 | ||||||
|  |          */ | ||||||
|  |         @Bean("reactorProcessor") | ||||||
|  |         fun reactorProcessor(reg: FieldRegistry) = ReactorProcessor(reg) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* ─────────────────────────────────────────────────────────────── | ||||||
|  |        2️⃣ WebFlux 过滤器(依赖 WebFlux + Reactor) | ||||||
|  |     ─────────────────────────────────────────────────────────────── */ | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 配置类,注册 WebFlux 环境下的上下文传播过滤器。 | ||||||
|  |      */ | ||||||
|  |     @Configuration(proxyBeanMethods = false) | ||||||
|  |     @ConditionalOnProperty(name = ["spring.main.web-application-type"], havingValue = "reactive") | ||||||
|  |     @ConditionalOnClass(name = ["org.springframework.web.server.WebFilter"]) | ||||||
|  |     class WebFluxPart { | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 注册 ContextWebFilter Bean,用于在 WebFlux 请求链中传播上下文字段。 | ||||||
|  |          * | ||||||
|  |          * 该过滤器利用 ReactorProcessor 在 WebFlux 的过滤器链中维护上下文一致性。 | ||||||
|  |          * | ||||||
|  |          * @param chain 处理器链 | ||||||
|  |          * @param reactorProcessor ReactorProcessor 实例 | ||||||
|  |          * @return 构建完成的 ContextWebFilter 实例 | ||||||
|  |          */ | ||||||
|  |         @Bean | ||||||
|  |         @ConditionalOnMissingBean | ||||||
|  |         fun contextWebFilter( | ||||||
|  |             chain: List<ContextProcessor>, | ||||||
|  |             reactorProcessor: ReactorProcessor, | ||||||
|  |         ) = ContextWebFilter(chain, reactorProcessor) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* ─────────────────────────────────────────────────────────────── | ||||||
|  |        3️⃣ Servlet 过滤器(依赖 Servlet API) | ||||||
|  |     ─────────────────────────────────────────────────────────────── */ | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 配置类,注册 Servlet 环境下的上下文传播过滤器。 | ||||||
|  |      */ | ||||||
|  |     @Configuration(proxyBeanMethods = false) | ||||||
|  |     @ConditionalOnProperty(name = ["spring.main.web-application-type"], havingValue = "servlet", matchIfMissing = true) | ||||||
|  |     @ConditionalOnClass(name = ["jakarta.servlet.Filter"]) | ||||||
|  |     class ServletPart { | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 注册 ContextServletFilter Bean,用于在 Servlet 请求链中传播上下文字段。 | ||||||
|  |          * | ||||||
|  |          * 该过滤器负责在同步阻塞的 Servlet 请求链中维护上下文一致性。 | ||||||
|  |          * | ||||||
|  |          * @param chain 处理器链 | ||||||
|  |          * @return 构建完成的 ContextServletFilter 实例 | ||||||
|  |          */ | ||||||
|  |         @Bean | ||||||
|  |         @ConditionalOnMissingBean | ||||||
|  |         fun contextServletFilter(chain: List<ContextProcessor>) = | ||||||
|  |             ContextServletFilter(chain) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* ─────────────────────────────────────────────────────────────── | ||||||
|  |        4️⃣ Feign 请求拦截器(依赖 OpenFeign) | ||||||
|  |     ─────────────────────────────────────────────────────────────── */ | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 配置类,注册 Feign 客户端的请求拦截器。 | ||||||
|  |      */ | ||||||
|  |     @Configuration(proxyBeanMethods = false) | ||||||
|  |     @ConditionalOnClass(name = ["feign.RequestInterceptor"]) | ||||||
|  |     class FeignPart { | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 注册 Feign 请求拦截器,用于在 Feign 调用中传播上下文字段。 | ||||||
|  |          * | ||||||
|  |          * 拦截器会在每次 Feign 请求发起前,将当前上下文字段写入 HTTP 请求头。 | ||||||
|  |          * | ||||||
|  |          * @param registry 字段注册表 | ||||||
|  |          * @return 构建完成的 feign.RequestInterceptor 实例 | ||||||
|  |          */ | ||||||
|  |         @Bean | ||||||
|  |         @ConditionalOnMissingBean | ||||||
|  |         fun feignInterceptor(registry: FieldRegistry) = | ||||||
|  |             feign.RequestInterceptor { tpl -> | ||||||
|  |                 val ctx = StringContextHolder.snapshot() | ||||||
|  |                 registry.all().forEach { def -> | ||||||
|  |                     ctx[def.key]?.let { tpl.header(def.header, it) } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* ─────────────────────────────────────────────────────────────── | ||||||
|  |        5️⃣ 线程池 TaskDecorator(纯 Spring,安全通用) | ||||||
|  |     ─────────────────────────────────────────────────────────────── */ | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 配置类,注册异步执行上下文保持支持。 | ||||||
|  |      */ | ||||||
|  |     @Configuration(proxyBeanMethods = false) | ||||||
|  |     class TaskDecoratorPart { | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 创建 TaskDecorator Bean,用于在异步执行中保持上下文一致性。 | ||||||
|  |          * | ||||||
|  |          * 通过装饰线程池任务,确保异步任务继承调用线程的上下文状态。 | ||||||
|  |          * | ||||||
|  |          * @param processors 所有处理器列表 | ||||||
|  |          * @return 构建完成的 TaskDecorator 实例 | ||||||
|  |          */ | ||||||
|  |         @Bean | ||||||
|  |         fun contextTaskDecorator(processors: List<ContextProcessor>) = | ||||||
|  |             TaskDecorator { delegate -> | ||||||
|  |                 val snap = StringContextHolder.snapshot() | ||||||
|  |                 Runnable { | ||||||
|  |                     try { | ||||||
|  |                         snap.forEach(StringContextHolder::put) | ||||||
|  |                         processors.forEach { it.inject(Unit, snap.toMutableMap()) } | ||||||
|  |                         delegate.run() | ||||||
|  |                     } finally { | ||||||
|  |                         processors.forEach { it.inject(Unit, mutableMapOf()) } | ||||||
|  |                         StringContextHolder.clear() | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* ─────────────────────────────────────────────────────────────── | ||||||
|  |        6️⃣ WebClient 过滤器 | ||||||
|  |     ─────────────────────────────────────────────────────────────── */ | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 配置类,注册 WebClient 自定义器以支持上下文传播。 | ||||||
|  |      */ | ||||||
|  |     @Configuration(proxyBeanMethods = false) | ||||||
|  |     @ConditionalOnClass(name = ["org.springframework.web.reactive.function.client.WebClient"]) | ||||||
|  |     class WebClientPart(private val registry: FieldRegistry) { | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 注册 WebClientCustomizer,用于定制 WebClient 的请求行为。 | ||||||
|  |          * | ||||||
|  |          * 在每次请求发出前,将当前上下文字段写入 HTTP 请求头。 | ||||||
|  |          * | ||||||
|  |          * @return 构建完成的 WebClientCustomizer 实例 | ||||||
|  |          */ | ||||||
|  |         @Bean | ||||||
|  |         fun contextWebClientCustomizer() = WebClientCustomizer { builder -> | ||||||
|  |             builder.filter { req, next -> | ||||||
|  |                 val ctx = StringContextHolder.snapshot() | ||||||
|  |                 val mutated = ClientRequest.from(req).apply { | ||||||
|  |                     registry.all().forEach { def -> | ||||||
|  |                         ctx[def.key]?.let { value -> header(def.header, value) } | ||||||
|  |                     } | ||||||
|  |                 }.build() | ||||||
|  |                 next.exchange(mutated) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1 @@ | |||||||
|  | com.gewuyou.forgeboot.context.autoconfigure.ForgeContextAutoConfiguration | ||||||
							
								
								
									
										3
									
								
								forgeboot-context/forgeboot-context-impl/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								forgeboot-context/forgeboot-context-impl/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | /gradlew text eol=lf | ||||||
|  | *.bat text eol=crlf | ||||||
|  | *.jar binary | ||||||
							
								
								
									
										40
									
								
								forgeboot-context/forgeboot-context-impl/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								forgeboot-context/forgeboot-context-impl/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  | HELP.md | ||||||
|  | .gradle | ||||||
|  | build/ | ||||||
|  | !gradle/wrapper/gradle-wrapper.jar | ||||||
|  | !**/src/main/**/build/ | ||||||
|  | !**/src/test/**/build/ | ||||||
|  | 
 | ||||||
|  | ### STS ### | ||||||
|  | .apt_generated | ||||||
|  | .classpath | ||||||
|  | .factorypath | ||||||
|  | .project | ||||||
|  | .settings | ||||||
|  | .springBeans | ||||||
|  | .sts4-cache | ||||||
|  | bin/ | ||||||
|  | !**/src/main/**/bin/ | ||||||
|  | !**/src/test/**/bin/ | ||||||
|  | 
 | ||||||
|  | ### IntelliJ IDEA ### | ||||||
|  | .idea | ||||||
|  | *.iws | ||||||
|  | *.iml | ||||||
|  | *.ipr | ||||||
|  | out/ | ||||||
|  | !**/src/main/**/out/ | ||||||
|  | !**/src/test/**/out/ | ||||||
|  | 
 | ||||||
|  | ### NetBeans ### | ||||||
|  | /nbproject/private/ | ||||||
|  | /nbbuild/ | ||||||
|  | /dist/ | ||||||
|  | /nbdist/ | ||||||
|  | /.nb-gradle/ | ||||||
|  | 
 | ||||||
|  | ### VS Code ### | ||||||
|  | .vscode/ | ||||||
|  | 
 | ||||||
|  | ### Kotlin ### | ||||||
|  | .kotlin | ||||||
| @ -0,0 +1,8 @@ | |||||||
|  | 
 | ||||||
|  | dependencies { | ||||||
|  |     compileOnly(project(Modules.Context.API)) | ||||||
|  |     compileOnly(libs.springBootStarter.web) | ||||||
|  |     compileOnly(libs.springBootStarter.webflux) | ||||||
|  |     implementation(platform(libs.springBootDependencies.bom)) | ||||||
|  |     implementation(libs.springBoot.autoconfigure) | ||||||
|  | } | ||||||
| @ -0,0 +1,45 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.impl | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.FieldRegistry | ||||||
|  | import com.gewuyou.forgeboot.context.api.entities.FieldDef | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 默认字段注册表 | ||||||
|  |  * | ||||||
|  |  * 该类实现了字段的注册和查找功能,通过字段的 key 和 header 建立索引, | ||||||
|  |  * 提供了基于 header 的快速查找能力。 | ||||||
|  |  * | ||||||
|  |  * @param defs 初始化字段定义集合,用于构建注册表 | ||||||
|  |  * @since 2025-06-04 14:51:39 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | class DefaultFieldRegistry(defs: Set<FieldDef>) : FieldRegistry { | ||||||
|  |     /** | ||||||
|  |      * 按字段 key 构建的有序映射表,用于通过 key 快速访问字段定义。 | ||||||
|  |      * 保持字段注册顺序的一致性,适用于需要按注册顺序遍历的场景。 | ||||||
|  |      */ | ||||||
|  |     private val byKey: Map<String, FieldDef> = | ||||||
|  |         LinkedHashMap<String, FieldDef>().apply { | ||||||
|  |             defs.forEach { put(it.key, it) } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 按字段 header(小写形式)构建的映射表,用于通过 header 快速查找字段定义。 | ||||||
|  |      */ | ||||||
|  |     private val byHeader = defs.associateBy { it.header.lowercase() } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 获取注册表中所有的字段定义。 | ||||||
|  |      * | ||||||
|  |      * @return 字段定义的集合 | ||||||
|  |      */ | ||||||
|  |     override fun all() = byKey.values | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 根据字段 header 查找对应的字段定义。 | ||||||
|  |      * | ||||||
|  |      * @param header 要查找的字段 header(不区分大小写) | ||||||
|  |      * @return 匹配到的字段定义,若未找到则返回 null | ||||||
|  |      */ | ||||||
|  |     override fun findByHeader(header: String) = byHeader[header.lowercase()] | ||||||
|  | } | ||||||
| @ -0,0 +1,11 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.impl | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.AbstractContext | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  *字符串上下文容器 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 15:05:43 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | object StringContextHolder: AbstractContext<String, String>() | ||||||
| @ -0,0 +1,62 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.impl.filter | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.ContextProcessor | ||||||
|  | import com.gewuyou.forgeboot.context.impl.StringContextHolder | ||||||
|  | import jakarta.servlet.FilterChain | ||||||
|  | import jakarta.servlet.http.HttpServletRequest | ||||||
|  | import jakarta.servlet.http.HttpServletResponse | ||||||
|  | import org.springframework.web.filter.OncePerRequestFilter | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 上下文 Servlet 过滤器 | ||||||
|  |  * | ||||||
|  |  * 该过滤器用于在 HTTP 请求处理过程中提取和注入上下文信息, | ||||||
|  |  * 确保请求链中各组件可以共享上下文数据。 | ||||||
|  |  * | ||||||
|  |  * @property chain 处理上下文的处理器链表,按顺序依次执行 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 16:08:33 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | class ContextServletFilter( | ||||||
|  |     private val chain: List<ContextProcessor>, | ||||||
|  | ) : OncePerRequestFilter() { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 执行内部过滤逻辑 | ||||||
|  |      * | ||||||
|  |      * 在请求进入业务逻辑前,从请求中提取上下文信息并存储到上下文持有者中; | ||||||
|  |      * 在请求完成后,清理上下文以避免内存泄漏或上下文污染。 | ||||||
|  |      * | ||||||
|  |      * @param request 当前 HTTP 请求对象 | ||||||
|  |      * @param response 当前 HTTP 响应对象 | ||||||
|  |      * @param filterChain 过滤器链,用于继续执行后续过滤器或目标处理逻辑 | ||||||
|  |      */ | ||||||
|  |     override fun doFilterInternal( | ||||||
|  |         request: HttpServletRequest, | ||||||
|  |         response: HttpServletResponse, | ||||||
|  |         filterChain: FilterChain, | ||||||
|  |     ) { | ||||||
|  |         // 创建当前线程上下文快照的可变副本,确保后续操作不影响原始上下文 | ||||||
|  |         val ctx = StringContextHolder.snapshot().toMutableMap() | ||||||
|  | 
 | ||||||
|  |         // 遍历上下文处理器链,依次从请求中提取上下文信息并更新临时上下文容器 | ||||||
|  |         chain.forEach { it.extract(request, ctx) } | ||||||
|  | 
 | ||||||
|  |         // 将提取后的上下文写入当前线程的上下文持有者,供后续组件访问 | ||||||
|  |         ctx.forEach(StringContextHolder::put) | ||||||
|  | 
 | ||||||
|  |         // 调用下一个过滤器或最终的目标处理器 | ||||||
|  |         chain.forEach { it.inject(request, ctx) } | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             filterChain.doFilter(request, response) | ||||||
|  |         } finally { | ||||||
|  |             // 确保在请求结束时清理所有上下文资源 | ||||||
|  |             // 向处理器链注入空上下文,触发清理操作(如有) | ||||||
|  |             chain.forEach { it.inject(Unit, mutableMapOf()) } | ||||||
|  |             // 显式清除当前线程的上下文持有者,防止上下文泄露 | ||||||
|  |             StringContextHolder.clear() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,66 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.impl.filter | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.ContextProcessor | ||||||
|  | import com.gewuyou.forgeboot.context.impl.StringContextHolder | ||||||
|  | import com.gewuyou.forgeboot.context.impl.processor.ReactorProcessor | ||||||
|  | import org.springframework.web.server.ServerWebExchange | ||||||
|  | import org.springframework.web.server.WebFilter | ||||||
|  | import org.springframework.web.server.WebFilterChain | ||||||
|  | import reactor.core.publisher.Mono | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 上下文 Web 过滤器 | ||||||
|  |  * | ||||||
|  |  * 用于在 Web 请求处理过程中维护和传递上下文信息,包括 ThreadLocal、MDC 和 Reactor Context。 | ||||||
|  |  * 通过一系列的 [ContextProcessor] 实现对上下文的提取、注入和清理操作。 | ||||||
|  |  * | ||||||
|  |  * @property contextProcessors 上下文处理器列表,用于处理上下文的提取与注入逻辑。 | ||||||
|  |  * @property reactorProc 反应式上下文处理器,用于将上下文写入 Reactor 的 Context 中。 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 15:54:43 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | class ContextWebFilter( | ||||||
|  |     private val contextProcessors: List<ContextProcessor>, | ||||||
|  |     private val reactorProc: ReactorProcessor | ||||||
|  | ) : WebFilter { | ||||||
|  |     /** | ||||||
|  |      * 执行过滤逻辑,在请求链中插入上下文管理操作。 | ||||||
|  |      * | ||||||
|  |      * 此方法依次完成以下步骤: | ||||||
|  |      * 1. 提取当前上下文并构建新的上下文数据; | ||||||
|  |      * 2. 将上下文填充到 ThreadLocal 和 MDC; | ||||||
|  |      * 3. 将上下文注入到请求头中以构建新地请求; | ||||||
|  |      * 4. 继续执行请求链,并将上下文写入 Reactor Context; | ||||||
|  |      * 5. 最终清理 ThreadLocal、MDC 中的上下文。 | ||||||
|  |      * | ||||||
|  |      * @param exchange 当前的 ServerWebExchange 对象,代表 HTTP 请求交换。 | ||||||
|  |      * @param chain Web 过滤器链,用于继续执行后续的过滤器或目标处理逻辑。 | ||||||
|  |      * @return 返回一个 Mono<Void> 表示异步完成的操作。 | ||||||
|  |      */ | ||||||
|  |     override fun filter( | ||||||
|  |         exchange: ServerWebExchange, | ||||||
|  |         chain: WebFilterChain, | ||||||
|  |     ): Mono<Void> { | ||||||
|  |         // 从 StringContextHolder 快照获取当前上下文并转换为可变 Map | ||||||
|  |         val ctx = StringContextHolder.snapshot().toMutableMap() | ||||||
|  |         // 遍历所有 ContextProcessor,从请求中提取上下文信息到 ctx | ||||||
|  |         contextProcessors.forEach { it.extract(exchange, ctx) } | ||||||
|  |         // 将上下文写入 StringContextHolder(ThreadLocal) | ||||||
|  |         ctx.forEach(StringContextHolder::put) | ||||||
|  |         // 使用 MdcProcessor 将上下文注入到 MDC 中 | ||||||
|  |         contextProcessors.forEach { it.inject(Unit, ctx) } | ||||||
|  |         // 构建新的 ServerWebExchange 实例 | ||||||
|  |         val mutated = exchange.mutate() | ||||||
|  |         // 注入上下文到请求头中 | ||||||
|  |         contextProcessors.forEach { it.inject(mutated, ctx) } | ||||||
|  |         // 继续执行过滤器链,同时将上下文写入 Reactor Context | ||||||
|  |         return chain.filter(mutated.build()) | ||||||
|  |             .contextWrite(reactorProc.injectToReactor(ctx)) | ||||||
|  |             .doFinally { | ||||||
|  |                 // 清理 ThreadLocal + MDC 上下文 | ||||||
|  |                 contextProcessors.forEach { it.inject(Unit, mutableMapOf()) } | ||||||
|  |                 StringContextHolder.clear() | ||||||
|  |             } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,47 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.impl.processor | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.ContextProcessor | ||||||
|  | import com.gewuyou.forgeboot.context.api.FieldRegistry | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 生成器处理器 | ||||||
|  |  * | ||||||
|  |  * 用于通过字段注册表(FieldRegistry)动态填充上下文中的空白字段。 | ||||||
|  |  * 当前类实现了 ContextProcessor 接口,提供了一种自动化的上下文字段填充机制。 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 15:35:11 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | class GeneratorProcessor( | ||||||
|  |     private val reg: FieldRegistry | ||||||
|  | ) : ContextProcessor { | ||||||
|  |     /** | ||||||
|  |      * 获取当前处理器的执行顺序优先级。 | ||||||
|  |      * | ||||||
|  |      * 在多个 ContextProcessor 实现中,该方法决定本处理器的执行顺序。 | ||||||
|  |      * 数值越小,优先级越高,在上下文处理流程中就越早被调用。 | ||||||
|  |      * | ||||||
|  |      * @return Int 表示当前处理器的顺序值,默认为20 | ||||||
|  |      */ | ||||||
|  |     override fun order(): Int { | ||||||
|  |         return 20 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 从给定的载体中提取上下文信息,并填充到上下文对象中。 | ||||||
|  |      * | ||||||
|  |      * 遍历 FieldRegistry 中注册的所有字段定义: | ||||||
|  |      * - 如果当前字段在上下文中不存在或为空白,则尝试使用其关联的生成器函数进行填充。 | ||||||
|  |      * - 生成器函数非空时会被调用,并将结果存入上下文映射中。 | ||||||
|  |      * | ||||||
|  |      * @param carrier 载体对象,通常包含上下文数据(未使用于当前实现) | ||||||
|  |      * @param ctx 可变映射,用于存储提取出的上下文键值对 | ||||||
|  |      */ | ||||||
|  |     override fun extract(carrier: Any, ctx: MutableMap<String, String>) { | ||||||
|  |         reg.all().forEach { def -> | ||||||
|  |             if (ctx[def.key].isNullOrBlank()) { | ||||||
|  |                 def.generator?.invoke()?.let { ctx[def.key] = it } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,72 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.impl.processor | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.ContextProcessor | ||||||
|  | import com.gewuyou.forgeboot.context.api.FieldRegistry | ||||||
|  | import jakarta.servlet.http.HttpServletRequest | ||||||
|  | import jakarta.servlet.http.HttpServletResponse | ||||||
|  | import org.springframework.web.server.ServerWebExchange | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 请求头处理器 | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 15:14:57 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | class HeaderProcessor(private val reg: FieldRegistry) : ContextProcessor { | ||||||
|  |     /** | ||||||
|  |      * 获取当前处理器的执行顺序优先级。 | ||||||
|  |      * | ||||||
|  |      * 默认实现返回0,数值越小优先级越高。 | ||||||
|  |      * | ||||||
|  |      * @return Int 表示当前处理器的顺序值 | ||||||
|  |      */ | ||||||
|  |     override fun order(): Int { | ||||||
|  |         return 10 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 从给定的载体中提取上下文信息,并填充到上下文对象中。 | ||||||
|  |      * | ||||||
|  |      * 默认实现为空方法,子类可根据需要重写此方法。 | ||||||
|  |      * | ||||||
|  |      * @param carrier 载体对象,通常包含上下文数据 | ||||||
|  |      * @param ctx 可变映射,用于存储提取出的上下文键值对 | ||||||
|  |      */ | ||||||
|  |     override fun extract(carrier: Any, ctx: MutableMap<String, String>) { | ||||||
|  |         when (carrier) { | ||||||
|  |             is ServerWebExchange -> reg.all().forEach { def -> | ||||||
|  |                 // 从ServerWebExchange请求头中提取指定字段并存入上下文 | ||||||
|  |                 carrier.request.headers[def.header]?.firstOrNull()?.let { ctx[def.key] = it } | ||||||
|  |             } | ||||||
|  |             is HttpServletRequest -> reg.all().forEach { def -> | ||||||
|  |                 // 从HttpServletRequest请求头中提取指定字段并存入上下文 | ||||||
|  |                 carrier.getHeader(def.header)?.let { ctx[def.key] = it } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 将上下文信息注入到给定的载体中。 | ||||||
|  |      * | ||||||
|  |      * 默认实现为空方法,子类可根据需要重写此方法。 | ||||||
|  |      * | ||||||
|  |      * @param carrier 载体对象,将上下文数据注入其中 | ||||||
|  |      * @param ctx 包含上下文键值对的映射 | ||||||
|  |      */ | ||||||
|  |     override fun inject(carrier: Any, ctx: MutableMap<String, String>) { | ||||||
|  |         when (carrier) { | ||||||
|  |             is ServerWebExchange.Builder -> reg.all().forEach { def -> | ||||||
|  |                 // 向ServerWebExchange构建器中注入请求头字段 | ||||||
|  |                 ctx[def.key]?.let { value -> | ||||||
|  |                     carrier.request { reqBuilder -> | ||||||
|  |                         reqBuilder.header(def.header, value) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             is HttpServletResponse -> reg.all().forEach { def -> | ||||||
|  |                 // 向HttpServletResponse中设置对应的响应头字段 | ||||||
|  |                 ctx[def.key]?.let { carrier.setHeader(def.header, it) } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,48 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.impl.processor | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.ContextProcessor | ||||||
|  | import com.gewuyou.forgeboot.context.api.FieldRegistry | ||||||
|  | import org.slf4j.MDC | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * MDC 处理器 | ||||||
|  |  * 把 ctx 写入 SLF4J MDC,方便日志打印;请求结束或线程任务结束时清理。 | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * @since 2025-06-04 15:39:35 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | class MdcProcessor( | ||||||
|  |     private val reg: FieldRegistry, | ||||||
|  | ) : ContextProcessor { | ||||||
|  |     /** | ||||||
|  |      * 获取当前处理器的执行顺序优先级。 | ||||||
|  |      * | ||||||
|  |      * 默认实现返回0,数值越小优先级越高。 | ||||||
|  |      * | ||||||
|  |      * @return Int 表示当前处理器的顺序值 | ||||||
|  |      */ | ||||||
|  |     override fun order(): Int { | ||||||
|  |         return 30 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 将上下文信息注入到给定的载体中。 | ||||||
|  |      * | ||||||
|  |      * 默认实现为空方法,子类可根据需要重写此方法。 | ||||||
|  |      * | ||||||
|  |      * @param carrier 载体对象,将上下文数据注入其中 | ||||||
|  |      * @param ctx 包含上下文键值对的映射 | ||||||
|  |      */ | ||||||
|  |     override fun inject(carrier: Any, ctx: MutableMap<String, String>) { | ||||||
|  |         if (ctx.isEmpty()) { | ||||||
|  |             // 视为空 ctx → 清理 | ||||||
|  |             reg.all().forEach { def -> MDC.remove(def.key) } | ||||||
|  |         } else { | ||||||
|  |             // 正常写入 | ||||||
|  |             reg.all().forEach { def -> | ||||||
|  |                 ctx[def.key]?.let { MDC.put(def.key, it) } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,49 @@ | |||||||
|  | package com.gewuyou.forgeboot.context.impl.processor | ||||||
|  | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.ContextProcessor | ||||||
|  | import com.gewuyou.forgeboot.context.api.FieldRegistry | ||||||
|  | import reactor.util.context.Context | ||||||
|  | import java.util.function.Function | ||||||
|  | /** | ||||||
|  |  * 反应器处理器 | ||||||
|  |  * | ||||||
|  |  * 用于在 WebFlux/Reactor 流中透传字段。当进入异步链时,将字段写入 reactor.util.context.Context。 | ||||||
|  |  * Context 在链尾无需手动清理,会自动复制和管理。 | ||||||
|  |  * | ||||||
|  |  * @property reg 字段注册表,用于获取所有需要处理的字段定义 | ||||||
|  |  * @since 2025-06-04 15:43:20 | ||||||
|  |  * @author gewuyou | ||||||
|  |  */ | ||||||
|  | class ReactorProcessor( | ||||||
|  |     private val reg: FieldRegistry | ||||||
|  | ) : ContextProcessor { | ||||||
|  |     /** | ||||||
|  |      * 获取当前处理器的执行顺序优先级。 | ||||||
|  |      * | ||||||
|  |      * 默认实现返回0,数值越小优先级越高。 | ||||||
|  |      * | ||||||
|  |      * @return Int 表示当前处理器的顺序值 | ||||||
|  |      */ | ||||||
|  |     override fun order(): Int { | ||||||
|  |         return 40 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 创建一个函数,用于将给定的上下文数据注入到 Reactor 的 Context 中。 | ||||||
|  |      * | ||||||
|  |      * 遍历字段注册表中的所有字段定义,并将对应的值从输入上下文中取出, | ||||||
|  |      * 然后放入 Reactor 的 Context 中。 | ||||||
|  |      * | ||||||
|  |      * @param ctx 包含要注入字段的映射表(key-value) | ||||||
|  |      * @return Function<Context, Context> 返回一个函数,该函数接受原始的 Reactor Context 并返回更新后的 Context | ||||||
|  |      */ | ||||||
|  |     fun injectToReactor(ctx: Map<String, String>): Function<Context, Context> = | ||||||
|  |         Function { rCtx -> | ||||||
|  |             var updated = rCtx | ||||||
|  |             // 遍历所有字段定义,并将对应的值注入到 Reactor Context 中 | ||||||
|  |             reg.all().forEach { def -> | ||||||
|  |                 ctx[def.key]?.let { value -> updated = updated.put(def.key, value) } | ||||||
|  |             } | ||||||
|  |             updated | ||||||
|  |         } | ||||||
|  | } | ||||||
| @ -1,10 +1,11 @@ | |||||||
| 
 | 
 | ||||||
| dependencies { | dependencies { | ||||||
|     compileOnly(platform(libs.springBootDependencies.bom)) |     implementation(project(Modules.Core.EXTENSION)) | ||||||
|  |     implementation(platform(libs.springBootDependencies.bom)) | ||||||
|  |     implementation(libs.springBoot.autoconfigure) | ||||||
|     compileOnly(platform(libs.springCloudDependencies.bom)) |     compileOnly(platform(libs.springCloudDependencies.bom)) | ||||||
|     compileOnly(libs.springBootStarter.web) |     compileOnly(libs.springBootStarter.web) | ||||||
|     compileOnly(libs.springBootStarter.webflux) |     compileOnly(libs.springBootStarter.webflux) | ||||||
|     compileOnly(project(Modules.I18n.API)) |     compileOnly(project(Modules.I18n.API)) | ||||||
|     compileOnly(project(Modules.I18n.IMPL)) |     compileOnly(project(Modules.I18n.IMPL)) | ||||||
|     implementation(project(Modules.Core.EXTENSION)) |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -21,10 +21,4 @@ class TraceProperties { | |||||||
|      * MDC(Mapped Diagnostic Context)中用于存储请求ID的键名,默认为"requestId"。 |      * MDC(Mapped Diagnostic Context)中用于存储请求ID的键名,默认为"requestId"。 | ||||||
|      */ |      */ | ||||||
|     var requestIdMdcKey: String = "requestId" |     var requestIdMdcKey: String = "requestId" | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 配置忽略日志记录的路径模式,通常用于静态资源文件, |  | ||||||
|      * 默认忽略以.css、.js、.png等结尾的静态资源请求。 |  | ||||||
|      */ |  | ||||||
|     var ignorePatten = arrayOf(".*\\.(css|js|png|jpg|jpeg|gif|svg)") |  | ||||||
| } | } | ||||||
| @ -1,7 +1,9 @@ | |||||||
| 
 | plugins { | ||||||
|  |     alias(libs.plugins.kotlin.plugin.spring) | ||||||
|  | } | ||||||
| dependencies { | dependencies { | ||||||
|     implementation(project(Modules.Core.EXTENSION)) |     implementation(platform(libs.springBootDependencies.bom)) | ||||||
|     compileOnly(platform(libs.springBootDependencies.bom)) |     implementation(libs.springBoot.autoconfigure) | ||||||
|     compileOnly(platform(libs.springCloudDependencies.bom)) |     compileOnly(platform(libs.springCloudDependencies.bom)) | ||||||
|     compileOnly(libs.springBootStarter.web) |     compileOnly(libs.springBootStarter.web) | ||||||
|     compileOnly(libs.springBootStarter.webflux) |     compileOnly(libs.springBootStarter.webflux) | ||||||
|  | |||||||
| @ -1,34 +0,0 @@ | |||||||
| package com.gewuyou.forgeboot.trace.autoconfig |  | ||||||
| 
 |  | ||||||
| import com.gewuyou.forgeboot.core.extension.log |  | ||||||
| import com.gewuyou.forgeboot.trace.api.config.TraceProperties |  | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnClass |  | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean |  | ||||||
| import org.springframework.context.annotation.Bean |  | ||||||
| import org.springframework.context.annotation.Configuration |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  *Feign跟踪自动配置 |  | ||||||
|  * |  | ||||||
|  * @since 2025-05-31 22:02:56 |  | ||||||
|  * @author gewuyou |  | ||||||
|  */ |  | ||||||
| @Configuration |  | ||||||
| @ConditionalOnClass(name = ["feign.RequestInterceptor"]) |  | ||||||
| open class FeignTraceAutoConfiguration { |  | ||||||
|     /** |  | ||||||
|      * Feign 拦截器(仅当 Feign 存在时生效) |  | ||||||
|      * |  | ||||||
|      * 该拦截器用于在Feign客户端中传递请求ID |  | ||||||
|      * @param traceProperties 跟踪配置属性 |  | ||||||
|      * @return FeignRequestIdInterceptor实例 |  | ||||||
|      */ |  | ||||||
|     @Bean |  | ||||||
|     @ConditionalOnMissingBean(name = ["feignRequestIdInterceptor"]) |  | ||||||
|     open fun feignRequestIdInterceptor(traceProperties: TraceProperties): Any { |  | ||||||
|         val clazz = Class.forName("com.gewuyou.forgeboot.trace.impl.interceptor.FeignRequestIdInterceptor") |  | ||||||
|         val constructor = clazz.getConstructor(TraceProperties::class.java) |  | ||||||
|         log.info( "创建FeignRequestIdInterceptor实例") |  | ||||||
|         return constructor.newInstance(traceProperties) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,18 +1,18 @@ | |||||||
| package com.gewuyou.forgeboot.trace.autoconfig | package com.gewuyou.forgeboot.trace.autoconfig | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.api.ContextFieldContributor | ||||||
|  | import com.gewuyou.forgeboot.context.api.entities.FieldDef | ||||||
|  | import com.gewuyou.forgeboot.context.api.enums.Scope | ||||||
| import com.gewuyou.forgeboot.core.extension.log | import com.gewuyou.forgeboot.core.extension.log | ||||||
| import com.gewuyou.forgeboot.trace.api.RequestIdProvider | import com.gewuyou.forgeboot.trace.api.RequestIdProvider | ||||||
| import com.gewuyou.forgeboot.trace.api.config.TraceProperties | import com.gewuyou.forgeboot.trace.api.config.TraceProperties | ||||||
| import com.gewuyou.forgeboot.trace.impl.decorator.RequestIdTaskDecorator |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.filter.ReactiveRequestIdFilter |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.filter.RequestIdFilter |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.provider.TraceRequestIdProvider | import com.gewuyou.forgeboot.trace.impl.provider.TraceRequestIdProvider | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty |  | ||||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties | import org.springframework.boot.context.properties.EnableConfigurationProperties | ||||||
| import org.springframework.context.annotation.Bean | import org.springframework.context.annotation.Bean | ||||||
| import org.springframework.context.annotation.Configuration | import org.springframework.context.annotation.Configuration | ||||||
|  | import java.util.UUID | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 跟踪自动配置 |  * 跟踪自动配置 | ||||||
| @ -23,37 +23,9 @@ import org.springframework.context.annotation.Configuration | |||||||
|  */ |  */ | ||||||
| @Configuration | @Configuration | ||||||
| @EnableConfigurationProperties(TraceProperties::class) | @EnableConfigurationProperties(TraceProperties::class) | ||||||
| open class TraceAutoConfiguration { | class TraceAutoConfiguration( | ||||||
|     /** |     private val traceProperties: TraceProperties | ||||||
|      * Spring MVC 过滤器(仅当 Spring MVC 存在时生效) | ) { | ||||||
|      * |  | ||||||
|      * 该过滤器用于在Spring MVC应用中生成和传递请求ID |  | ||||||
|      * @param traceProperties 跟踪配置属性 |  | ||||||
|      * @return RequestIdFilter实例 |  | ||||||
|      */ |  | ||||||
|     @Bean |  | ||||||
|     @ConditionalOnProperty(name = ["spring.main.web-application-type"], havingValue = "servlet", matchIfMissing = true) |  | ||||||
|     @ConditionalOnMissingBean |  | ||||||
|     open fun requestIdFilter(traceProperties: TraceProperties): RequestIdFilter { |  | ||||||
|         log.info("RequestIdFilter 已创建!") |  | ||||||
|         return RequestIdFilter(traceProperties) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Spring WebFlux 过滤器(仅当 Spring WebFlux 存在时生效) |  | ||||||
|      * |  | ||||||
|      * 该过滤器用于在Spring WebFlux应用中生成和传递请求ID |  | ||||||
|      * @param traceProperties 跟踪配置属性 |  | ||||||
|      * @return ReactiveRequestIdFilter实例 |  | ||||||
|      */ |  | ||||||
|     @Bean |  | ||||||
|     @ConditionalOnProperty(name = ["spring.main.web-application-type"], havingValue = "reactive") |  | ||||||
|     @ConditionalOnMissingBean |  | ||||||
|     open fun reactiveRequestIdFilter(traceProperties: TraceProperties): ReactiveRequestIdFilter { |  | ||||||
|         log.info("ReactiveRequestIdFilter 已创建!") |  | ||||||
|         return ReactiveRequestIdFilter(traceProperties) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * 请求ID提供者(用于生成请求ID) |      * 请求ID提供者(用于生成请求ID) | ||||||
|      * |      * | ||||||
| @ -62,23 +34,19 @@ open class TraceAutoConfiguration { | |||||||
|      */ |      */ | ||||||
|     @Bean |     @Bean | ||||||
|     @ConditionalOnMissingBean(RequestIdProvider::class) |     @ConditionalOnMissingBean(RequestIdProvider::class) | ||||||
|     open fun traceRequestIdProvider(): TraceRequestIdProvider { |     fun traceRequestIdProvider(): TraceRequestIdProvider { | ||||||
|         log.info("TraceRequestIdProvider 已创建!") |         log.info("TraceRequestIdProvider 已创建!") | ||||||
|         return TraceRequestIdProvider() |         return TraceRequestIdProvider(traceProperties) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 线程池装饰器(用于 @Async) |  | ||||||
|      * |  | ||||||
|      * 该装饰器用于在异步线程池中传递请求ID,确保异步执行的任务能够携带正确地请求信息 |  | ||||||
|      * @param traceProperties 跟踪配置属性 |  | ||||||
|      * @return RequestIdTaskDecorator实例 |  | ||||||
|      */ |  | ||||||
|     @Bean |     @Bean | ||||||
|     @ConditionalOnMissingBean |     fun requestContributor() = ContextFieldContributor { | ||||||
|     open fun requestIdTaskDecorator(traceProperties: TraceProperties): RequestIdTaskDecorator { |         setOf( | ||||||
|         log.info("RequestIdTaskDecorator 已创建!") |             FieldDef( | ||||||
|         return RequestIdTaskDecorator(traceProperties) |                 header = traceProperties.requestIdHeaderName,          // 请求-响应头名 | ||||||
|  |                 key = traceProperties.requestIdMdcKey,             // ctx/MDC 键 | ||||||
|  |                 generator = { UUID.randomUUID().toString() }, // 如果前端没带,用这个生成 | ||||||
|  |                 scopes = setOf(Scope.HEADER, Scope.MDC, Scope.REACTOR) | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,43 +0,0 @@ | |||||||
| package com.gewuyou.forgeboot.trace.autoconfig |  | ||||||
| 
 |  | ||||||
| import com.gewuyou.forgeboot.core.extension.log |  | ||||||
| import com.gewuyou.forgeboot.trace.api.config.TraceProperties |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.filter.WebClientRequestIdFilter |  | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnBean |  | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnClass |  | ||||||
| import org.springframework.context.annotation.Bean |  | ||||||
| import org.springframework.context.annotation.Configuration |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Web客户端跟踪自动配置 |  | ||||||
|  * |  | ||||||
|  * 该配置类用于自动配置Web客户端的请求ID过滤器,以实现请求跟踪功能 |  | ||||||
|  * 它依赖于特定条件,如类的存在和Bean的定义,以确保在适当的时候进行配置 |  | ||||||
|  * |  | ||||||
|  * @since 2025-05-31 21:59:02 |  | ||||||
|  * @author gewuyou |  | ||||||
|  */ |  | ||||||
| @Configuration |  | ||||||
| @ConditionalOnClass(name = ["org.springframework.web.reactive.function.client.WebClient\$Builder"]) |  | ||||||
| open class WebClientTraceAutoConfiguration { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 配置Web客户端的请求ID过滤器 |  | ||||||
|      * |  | ||||||
|      * 该方法在满足条件时被调用,它获取WebClient.Builder实例并应用请求ID过滤器, |  | ||||||
|      * 以便在发出请求时添加请求ID信息这对于跟踪请求跨多个服务非常有用 |  | ||||||
|      * |  | ||||||
|      * @param webClientBuilder Web客户端构建器,用于配置过滤器 |  | ||||||
|      * @param traceProperties 跟踪属性配置,用于定制跟踪行为 |  | ||||||
|      * @return 配置后的WebClient.Builder实例 |  | ||||||
|      */ |  | ||||||
|     @Bean |  | ||||||
|     @ConditionalOnBean(name = ["webClientBuilder"]) |  | ||||||
|     open fun webClientRequestIdFilter( |  | ||||||
|         webClientBuilder: org.springframework.web.reactive.function.client.WebClient.Builder, |  | ||||||
|         traceProperties: TraceProperties |  | ||||||
|     ): org.springframework.web.reactive.function.client.WebClient.Builder { |  | ||||||
|         log .info("配置Web客户端的请求ID过滤器") |  | ||||||
|         return webClientBuilder.filter(WebClientRequestIdFilter(traceProperties)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,3 +1 @@ | |||||||
| com.gewuyou.forgeboot.trace.autoconfig.TraceAutoConfiguration | com.gewuyou.forgeboot.trace.autoconfig.TraceAutoConfiguration | ||||||
| com.gewuyou.forgeboot.trace.autoconfig.WebClientTraceAutoConfiguration |  | ||||||
| com.gewuyou.forgeboot.trace.autoconfig.FeignTraceAutoConfiguration |  | ||||||
| @ -1,7 +1,8 @@ | |||||||
| dependencies { | dependencies { | ||||||
|     implementation(project(Modules.Core.EXTENSION)) |     implementation(platform(libs.springBootDependencies.bom)) | ||||||
|  |     api(project(Modules.Core.EXTENSION)) | ||||||
|  |     api(project(Modules.Context.STARTER)) | ||||||
|     compileOnly(project(Modules.TRACE.API)) |     compileOnly(project(Modules.TRACE.API)) | ||||||
|     compileOnly(platform(libs.springBootDependencies.bom)) |  | ||||||
|     compileOnly(platform(libs.springCloudDependencies.bom)) |     compileOnly(platform(libs.springCloudDependencies.bom)) | ||||||
|     compileOnly(libs.springBootStarter.webflux) |     compileOnly(libs.springBootStarter.webflux) | ||||||
|     compileOnly(libs.springBootStarter.web) |     compileOnly(libs.springBootStarter.web) | ||||||
|  | |||||||
| @ -1,29 +0,0 @@ | |||||||
| package com.gewuyou.forgeboot.trace.impl.decorator |  | ||||||
| 
 |  | ||||||
| import com.gewuyou.forgeboot.trace.api.config.TraceProperties |  | ||||||
| import org.slf4j.MDC |  | ||||||
| import org.springframework.core.task.TaskDecorator |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  *请求ID任务装饰器 |  | ||||||
|  * |  | ||||||
|  * @since 2025-03-17 16:57:54 |  | ||||||
|  * @author gewuyou |  | ||||||
|  */ |  | ||||||
| class RequestIdTaskDecorator( |  | ||||||
|     private val traceProperties: TraceProperties |  | ||||||
| ) : TaskDecorator { |  | ||||||
|     override fun decorate(task: Runnable): Runnable { |  | ||||||
|         val requestIdMdcKey = traceProperties.requestIdMdcKey |  | ||||||
|         // 获取主线程 requestId |  | ||||||
|         val requestId = MDC.get(requestIdMdcKey) |  | ||||||
|         return Runnable { |  | ||||||
|             try { |  | ||||||
|                 MDC.put(requestIdMdcKey, requestId) |  | ||||||
|                 task.run() |  | ||||||
|             } finally { |  | ||||||
|                 MDC.remove(requestIdMdcKey) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,60 +0,0 @@ | |||||||
| package com.gewuyou.forgeboot.trace.impl.extension |  | ||||||
| 
 |  | ||||||
| import jakarta.servlet.http.HttpServletRequest |  | ||||||
| import org.springframework.http.HttpMethod |  | ||||||
| import org.springframework.http.server.reactive.ServerHttpRequest |  | ||||||
| import org.springframework.web.reactive.function.client.ClientRequest |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 请求扩展 |  | ||||||
|  * |  | ||||||
|  * @since 2025-05-02 21:59:26 |  | ||||||
|  * @author gewuyou |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 判断是否跳过请求 |  | ||||||
|  * @apiNote 这个方法是给反应式请求id过滤器使用的 |  | ||||||
|  * @return true: 跳过请求; false: 不跳过请求 |  | ||||||
|  */ |  | ||||||
| fun ServerHttpRequest.isSkipRequest(ignorePaths: Array<String>): Boolean { |  | ||||||
|     return isSkipRequest(this.method.name(), this.uri.path, ignorePaths) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 判断是否跳过请求 |  | ||||||
|  * @apiNote 这个方法是给请求id过滤器使用的 |  | ||||||
|  * @return true: 跳过请求; false: 不跳过请求 |  | ||||||
|  */ |  | ||||||
| fun HttpServletRequest.isSkipRequest(ignorePaths: Array<String>): Boolean { |  | ||||||
|     return isSkipRequest(this.method, this.requestURI, ignorePaths) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 判断是否跳过请求 |  | ||||||
|  * @apiNote 这个方法是给请求id过滤器使用的 |  | ||||||
|  * @return true: 跳过请求; false: 不跳过请求 |  | ||||||
|  */ |  | ||||||
| fun ClientRequest.isSkipRequest(ignorePaths: Array<String>): Boolean { |  | ||||||
|     return isSkipRequest(this.method().name(), this.url().path, ignorePaths) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 判断是否跳过请求 |  | ||||||
|  * @param method 请求方法 |  | ||||||
|  * @param uri 请求路径 |  | ||||||
|  * @return true: 跳过请求; false: 不跳过请求 |  | ||||||
|  */ |  | ||||||
| fun isSkipRequest(method: String, uri: String, ignorePaths: Array<String>): Boolean { |  | ||||||
|     return when { |  | ||||||
|         // 跳过 OPTIONS 请求 |  | ||||||
|         HttpMethod.OPTIONS.name() == method -> true |  | ||||||
|         // 跳过 HEAD 请求 |  | ||||||
|         HttpMethod.HEAD.name() == method -> true |  | ||||||
|         // 跳过 TRACE 请求 |  | ||||||
|         HttpMethod.TRACE.name() == method -> true |  | ||||||
|         // 跳过模式匹配 |  | ||||||
|         ignorePaths.any { uri.matches(Regex(it)) } -> true |  | ||||||
|         else -> false |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,65 +0,0 @@ | |||||||
| package com.gewuyou.forgeboot.trace.impl.filter |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| import com.gewuyou.forgeboot.core.extension.log |  | ||||||
| import com.gewuyou.forgeboot.trace.api.config.TraceProperties |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.extension.isSkipRequest |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil |  | ||||||
| 
 |  | ||||||
| import org.slf4j.MDC |  | ||||||
| import org.springframework.web.server.ServerWebExchange |  | ||||||
| import org.springframework.web.server.WebFilter |  | ||||||
| import org.springframework.web.server.WebFilterChain |  | ||||||
| import reactor.core.publisher.Mono |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 反应式请求 ID 过滤器 |  | ||||||
|  * |  | ||||||
|  * 该类的主要作用是在每个请求开始时生成或获取一个唯一的请求 ID,并将其设置到日志上下文中, |  | ||||||
|  * 以便在后续的日志记录中能够追踪到该请求。它还支持基于特定模式跳过某些请求的处理。 |  | ||||||
|  * |  | ||||||
|  * @param traceProperties 配置属性,包含请求 ID 的头名称和 MDK 关键等信息 |  | ||||||
|  * @since 2025-02-09 02:14:49 |  | ||||||
|  * @author gewuyou |  | ||||||
|  */ |  | ||||||
| class ReactiveRequestIdFilter( |  | ||||||
|     private val traceProperties: TraceProperties |  | ||||||
| )  : WebFilter { |  | ||||||
|     override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> { |  | ||||||
|         val request = exchange.request |  | ||||||
|         // 检测请求是否需要跳过 |  | ||||||
|         if (request.isSkipRequest(traceProperties.ignorePatten)) { |  | ||||||
|             return chain.filter(exchange) |  | ||||||
|         } |  | ||||||
|         // 获取请求头中请求 ID 的名称和 MDK 中的键 |  | ||||||
|         val requestIdHeader = traceProperties.requestIdHeaderName |  | ||||||
|         val requestIdMdcKey = traceProperties.requestIdMdcKey |  | ||||||
|         // 尝试从请求头中获取 requestId,如果存在则设置到 RequestIdUtil 中,否则生成一个新的 requestId |  | ||||||
|         request.headers[requestIdHeader]?.let { |  | ||||||
|             it.firstOrNull()?.let(RequestIdUtil::requestId::set) ?: RequestIdUtil.generateRequestId() |  | ||||||
|         } ?: RequestIdUtil.generateRequestId() |  | ||||||
|         // 获取当前的 requestId |  | ||||||
|         val currentRequestId = RequestIdUtil.requestId |  | ||||||
|         // 将 requestId 设置到日志中 |  | ||||||
|         MDC.put(requestIdMdcKey, currentRequestId) |  | ||||||
|         log.info("设置 Request id: $currentRequestId") |  | ||||||
|         // ✅ **创建新的 request 并更新 exchange** |  | ||||||
|         // 更新请求头,确保后续的请求处理中包含 requestId |  | ||||||
|         val mutatedRequest = request.mutate() |  | ||||||
|             .header(requestIdHeader, currentRequestId) |  | ||||||
|             .build() |  | ||||||
|         val mutatedExchange = exchange.mutate().request(mutatedRequest).build() |  | ||||||
|         // 放行请求 |  | ||||||
|         return chain.filter(mutatedExchange) |  | ||||||
|             // ✅ 让 Reactor 线程也能获取 requestId |  | ||||||
|             // 将 requestId 写入 Reactor 的上下文中,以便在异步处理中也能访问 |  | ||||||
|             .contextWrite { ctx -> ctx.put(requestIdMdcKey, currentRequestId!!) } |  | ||||||
|             .doFinally { |  | ||||||
|                 // 清理 MDC 中的 requestId,避免内存泄漏 |  | ||||||
|                 MDC.remove(requestIdMdcKey) |  | ||||||
|                 // 将 requestId 清除 |  | ||||||
|                 RequestIdUtil.removeRequestId() |  | ||||||
|             } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,64 +0,0 @@ | |||||||
| package com.gewuyou.forgeboot.trace.impl.filter |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| import com.gewuyou.forgeboot.core.extension.log |  | ||||||
| import com.gewuyou.forgeboot.trace.api.config.TraceProperties |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.extension.isSkipRequest |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil |  | ||||||
| 
 |  | ||||||
| import jakarta.servlet.FilterChain |  | ||||||
| import jakarta.servlet.http.HttpServletRequest |  | ||||||
| import jakarta.servlet.http.HttpServletResponse |  | ||||||
| import org.slf4j.MDC |  | ||||||
| import org.springframework.web.filter.OncePerRequestFilter |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 请求过滤器 |  | ||||||
|  * |  | ||||||
|  * 该过滤器用于在请求处理过程中添加和管理请求ID(RequestId),以便于日志追踪和调试 |  | ||||||
|  * 它基于Spring的OncePerRequestFilter,确保每个请求只被过滤一次 |  | ||||||
|  * |  | ||||||
|  * @param traceProperties Trace属性配置,包含请求ID的相关配置信息 |  | ||||||
|  * @since 2025-01-02 14:31:07 |  | ||||||
|  * @author gewuyou |  | ||||||
|  */ |  | ||||||
| class RequestIdFilter( |  | ||||||
|     private val traceProperties: TraceProperties |  | ||||||
| ) : OncePerRequestFilter() { |  | ||||||
|     override fun doFilterInternal( |  | ||||||
|         request: HttpServletRequest, |  | ||||||
|         response: HttpServletResponse, |  | ||||||
|         chain: FilterChain |  | ||||||
|     ) { |  | ||||||
|         // 检查请求是否需要跳过 |  | ||||||
|         if (request.isSkipRequest(traceProperties.ignorePatten)) { |  | ||||||
|             return chain.doFilter(request, response) |  | ||||||
|         } |  | ||||||
|         // 获取请求头中 requestId 的名称和 MDC 中的键 |  | ||||||
|         val requestIdHeader = traceProperties.requestIdHeaderName |  | ||||||
|         val requestIdMdcKey = traceProperties.requestIdMdcKey |  | ||||||
|         try { |  | ||||||
|             // 尝试从请求头中获取 requestId |  | ||||||
|             request.getHeader(requestIdHeader)?.also( |  | ||||||
|                 RequestIdUtil::requestId::set |  | ||||||
|             ) ?: run { |  | ||||||
|                 // 如果没有,则生成新的 requestId |  | ||||||
|                 RequestIdUtil.generateRequestId() |  | ||||||
|             } |  | ||||||
|             // 获取 requestId |  | ||||||
|             val requestId = RequestIdUtil.requestId |  | ||||||
|             // 将requestId 设置到日志中 |  | ||||||
|             MDC.put(requestIdMdcKey, requestId) |  | ||||||
|             log.info("设置 Request id: $requestId") |  | ||||||
|             // 将 requestId 设置到响应头中 |  | ||||||
|             response.setHeader(requestIdHeader, requestId) |  | ||||||
|             // 继续处理请求 |  | ||||||
|             chain.doFilter(request, response) |  | ||||||
|         } finally { |  | ||||||
|             // 移除 MDC 中的 requestId |  | ||||||
|             MDC.remove(requestIdMdcKey) |  | ||||||
|             // 清理当前线程的 RequestId,防止内存泄漏 |  | ||||||
|             RequestIdUtil.removeRequestId() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,58 +0,0 @@ | |||||||
| package com.gewuyou.forgeboot.trace.impl.filter |  | ||||||
| 
 |  | ||||||
| import com.gewuyou.forgeboot.core.extension.log |  | ||||||
| import com.gewuyou.forgeboot.trace.api.config.TraceProperties |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.extension.isSkipRequest |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil |  | ||||||
| 
 |  | ||||||
| import org.slf4j.MDC |  | ||||||
| import org.springframework.web.reactive.function.client.ClientRequest |  | ||||||
| import org.springframework.web.reactive.function.client.ClientResponse |  | ||||||
| import org.springframework.web.reactive.function.client.ExchangeFilterFunction |  | ||||||
| import org.springframework.web.reactive.function.client.ExchangeFunction |  | ||||||
| import reactor.core.publisher.Mono |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Web客户端请求ID过滤器 |  | ||||||
|  * |  | ||||||
|  * 该类的主要作用是在发出HTTP请求时,确保请求ID的正确传递和记录 |  | ||||||
|  * 如果请求被忽略,则直接传递不做处理;否则,会尝试从请求头中获取请求ID, |  | ||||||
|  * 如果获取不到则生成新的请求ID,并将其设置到请求头中以及日志中,以便于追踪请求 |  | ||||||
|  * |  | ||||||
|  * @param traceProperties 追踪属性配置,包括忽略模式、请求ID头名称和MDK中的键 |  | ||||||
|  * @since 2025-05-02 22:18:06 |  | ||||||
|  * @author gewuyou |  | ||||||
|  */ |  | ||||||
| class WebClientRequestIdFilter( |  | ||||||
|     private val traceProperties: TraceProperties |  | ||||||
| ) : ExchangeFilterFunction { |  | ||||||
|     override fun filter(request: ClientRequest, next: ExchangeFunction): Mono<ClientResponse> { |  | ||||||
|         // 检查请求是否被忽略,如果被忽略,则直接执行请求 |  | ||||||
|         if (request.isSkipRequest(traceProperties.ignorePatten)) { |  | ||||||
|             return next.exchange(request) |  | ||||||
|         } |  | ||||||
|         // 获取请求头中请求 ID 的名称和 MDK 中的键 |  | ||||||
|         val requestIdHeader = traceProperties.requestIdHeaderName |  | ||||||
|         val requestIdMdcKey = traceProperties.requestIdMdcKey |  | ||||||
|         // 尝试从请求头中获取 requestId,如果存在则设置到 RequestIdUtil 中,否则生成一个新的 requestId |  | ||||||
|         request.headers()[requestIdHeader]?.let { |  | ||||||
|             it.firstOrNull()?.let(RequestIdUtil::requestId::set) ?: RequestIdUtil.generateRequestId() |  | ||||||
|         } ?: RequestIdUtil.generateRequestId() |  | ||||||
|         // 获取当前的 requestId |  | ||||||
|         val currentRequestId = RequestIdUtil.requestId |  | ||||||
|         // 将 requestId 设置到日志中 |  | ||||||
|         MDC.put(requestIdMdcKey, currentRequestId) |  | ||||||
|         log.info("设置 Request id: $currentRequestId") |  | ||||||
|         // 创建一个新的请求,包含 requestId 头 |  | ||||||
|         val mutatedRequest = ClientRequest.from(request) |  | ||||||
|             .header(requestIdHeader, currentRequestId) |  | ||||||
|             .build() |  | ||||||
|         // 执行请求,并在请求完成后清除 MDC 和 RequestIdUtil 中的 requestId |  | ||||||
|         return next.exchange(mutatedRequest) |  | ||||||
|             .doFinally { |  | ||||||
|                 MDC.remove(requestIdMdcKey) |  | ||||||
|                 RequestIdUtil.removeRequestId() |  | ||||||
|             } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -1,30 +0,0 @@ | |||||||
| package com.gewuyou.forgeboot.trace.impl.interceptor |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| import com.gewuyou.forgeboot.core.extension.log |  | ||||||
| import com.gewuyou.forgeboot.trace.api.config.TraceProperties |  | ||||||
| import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil |  | ||||||
| import feign.RequestInterceptor |  | ||||||
| import feign.RequestTemplate |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Feign请求ID 拦截器 |  | ||||||
|  * |  | ||||||
|  * @since 2025-03-17 16:42:50 |  | ||||||
|  * @author gewuyou |  | ||||||
|  */ |  | ||||||
| class FeignRequestIdInterceptor( |  | ||||||
|     private val traceProperties: TraceProperties |  | ||||||
| ) : RequestInterceptor { |  | ||||||
|     override fun apply(template: RequestTemplate) { |  | ||||||
|         // 尝试获取当前请求的请求id |  | ||||||
|         val requestId = RequestIdUtil.requestId |  | ||||||
|         requestId?.let { |  | ||||||
|             // 如果请求id存在,则添加到请求头中 |  | ||||||
|             template.header(traceProperties.requestIdHeaderName, requestId) |  | ||||||
|         } ?: run { |  | ||||||
|             log.warn("请求ID为null,请检查您是否已在过滤链中添加了请求filter。") |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,7 +1,8 @@ | |||||||
| package com.gewuyou.forgeboot.trace.impl.provider | package com.gewuyou.forgeboot.trace.impl.provider | ||||||
| 
 | 
 | ||||||
|  | import com.gewuyou.forgeboot.context.impl.StringContextHolder | ||||||
| import com.gewuyou.forgeboot.trace.api.RequestIdProvider | import com.gewuyou.forgeboot.trace.api.RequestIdProvider | ||||||
| import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil | import com.gewuyou.forgeboot.trace.api.config.TraceProperties | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -10,7 +11,9 @@ import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil | |||||||
|  * @since 2025-05-03 17:26:46 |  * @since 2025-05-03 17:26:46 | ||||||
|  * @author gewuyou |  * @author gewuyou | ||||||
|  */ |  */ | ||||||
| class TraceRequestIdProvider: RequestIdProvider { | class TraceRequestIdProvider( | ||||||
|  |     private val traceProperties: TraceProperties | ||||||
|  | ): RequestIdProvider { | ||||||
|     /** |     /** | ||||||
|      * 获取请求ID |      * 获取请求ID | ||||||
|      * |      * | ||||||
| @ -19,6 +22,6 @@ class TraceRequestIdProvider: RequestIdProvider { | |||||||
|      * @return 请求ID的字符串表示 |      * @return 请求ID的字符串表示 | ||||||
|      */ |      */ | ||||||
|     override fun getRequestId(): String { |     override fun getRequestId(): String { | ||||||
|         return RequestIdUtil.requestId?:throw RuntimeException("requestId is null") |         return StringContextHolder.get(traceProperties.requestIdMdcKey) ?:throw RuntimeException("requestId is null") | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -1,26 +0,0 @@ | |||||||
| package com.gewuyou.forgeboot.trace.impl.util |  | ||||||
| 
 |  | ||||||
| import java.util.* |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 请求 ID Util |  | ||||||
|  * 这个类需配合 RequestIdFilter 使用,用于生成请求 ID,并将其绑定到线程变量中,供后续可能需要的地方使用。 |  | ||||||
|  * @author gewuyou |  | ||||||
|  * @since 2025-01-02 14:27:45 |  | ||||||
|  */ |  | ||||||
|  object RequestIdUtil { |  | ||||||
|     private val REQUEST_ID_HOLDER = ThreadLocal<String>() |  | ||||||
|     fun generateRequestId() { |  | ||||||
|         REQUEST_ID_HOLDER.set(UUID.randomUUID().toString()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     var requestId: String? |  | ||||||
|         get() = REQUEST_ID_HOLDER.get() |  | ||||||
|         set(uuid) { |  | ||||||
|             REQUEST_ID_HOLDER.set(uuid) |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     fun removeRequestId() { |  | ||||||
|         REQUEST_ID_HOLDER.remove() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -32,4 +32,8 @@ open class BaseResult<T>( | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun toFlatMap(): Map<String, Any?> = toMutableFlatMap().toMap() |     fun toFlatMap(): Map<String, Any?> = toMutableFlatMap().toMap() | ||||||
|  | 
 | ||||||
|  |     override fun toString(): String { | ||||||
|  |         return "BaseResult(code=$code, success=$success, message='$message', data=$data, requestId=$requestId, extra=$extra)" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -109,4 +109,7 @@ open class PageResult<T> { | |||||||
|          */ |          */ | ||||||
|         fun <T> empty(): PageResult<T> = PageResult() |         fun <T> empty(): PageResult<T> = PageResult() | ||||||
|     } |     } | ||||||
|  |     override fun toString(): String { | ||||||
|  |         return "PageResult(records=$records, totalRecords=$totalRecords, totalPages=$totalPages, currentPage=$currentPage, pageSize=$pageSize, hasPrevious=$hasPrevious, hasNext=$hasNext)" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -85,4 +85,9 @@ open class PageQueryReq<T> { | |||||||
|     fun isDateRangeValid(): Boolean { |     fun isDateRangeValid(): Boolean { | ||||||
|         return startDate == null || endDate == null || !startDate!!.isAfter(endDate) |         return startDate == null || endDate == null || !startDate!!.isAfter(endDate) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     override fun toString(): String { | ||||||
|  |         return "PageQueryReq(currentPage=$currentPage, pageSize=$pageSize, sortBy='$sortBy', sortDirection=$sortDirection, sortConditions=$sortConditions, keyword=$keyword, filter=$filter, startDate=$startDate, endDate=$endDate, enabled=$enabled, deleted=$deleted)" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
| @ -4,6 +4,8 @@ plugins{ | |||||||
| } | } | ||||||
| dependencies { | dependencies { | ||||||
|     implementation(project(Modules.Core.EXTENSION)) |     implementation(project(Modules.Core.EXTENSION)) | ||||||
|  |     implementation(platform(libs.springBootDependencies.bom)) | ||||||
|  |     implementation(libs.springBoot.autoconfigure) | ||||||
|     api(project(Modules.I18n.STARTER)) |     api(project(Modules.I18n.STARTER)) | ||||||
|     api(project(Modules.TRACE.STARTER)) |     api(project(Modules.TRACE.STARTER)) | ||||||
|     implementation(project(Modules.Webmvc.DTO)) |     implementation(project(Modules.Webmvc.DTO)) | ||||||
|  | |||||||
| @ -3,8 +3,10 @@ plugins{ | |||||||
| } | } | ||||||
| dependencies { | dependencies { | ||||||
|     implementation(project(Modules.Core.EXTENSION)) |     implementation(project(Modules.Core.EXTENSION)) | ||||||
|     api(project(Modules.TRACE.STARTER)) |  | ||||||
|     implementation(project(Modules.Webmvc.DTO)) |     implementation(project(Modules.Webmvc.DTO)) | ||||||
|  |     implementation(platform(libs.springBootDependencies.bom)) | ||||||
|  |     implementation(libs.springBoot.autoconfigure) | ||||||
|  |     api(project(Modules.TRACE.STARTER)) | ||||||
|     compileOnly(libs.springBootStarter.validation) |     compileOnly(libs.springBootStarter.validation) | ||||||
|     compileOnly(libs.springBootStarter.web) |     compileOnly(libs.springBootStarter.web) | ||||||
|     kapt(libs.springBoot.configuration.processor) |     kapt(libs.springBoot.configuration.processor) | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| dependencies { | dependencies { | ||||||
|     implementation(project(Modules.Core.EXTENSION)) |     implementation(project(Modules.Core.EXTENSION)) | ||||||
|     implementation(libs.springBootStarter.aop) |     implementation(libs.springBootStarter.aop) | ||||||
| 
 |  | ||||||
|     implementation(libs.kotlinxCoroutines.reactor) |     implementation(libs.kotlinxCoroutines.reactor) | ||||||
| 
 |     implementation(platform(libs.springBootDependencies.bom)) | ||||||
|  |     implementation(libs.springBoot.autoconfigure) | ||||||
|     compileOnly(libs.springBootStarter.web) |     compileOnly(libs.springBootStarter.web) | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| package com.gewuyou.forgeboot.webmvc.spec.service.impl | package com.gewuyou.forgeboot.webmvc.spec.service.impl | ||||||
| 
 | 
 | ||||||
| import com.gewuyou.forgeboot.core.extension.log |  | ||||||
| import com.gewuyou.forgeboot.webmvc.dto.PageResult | import com.gewuyou.forgeboot.webmvc.dto.PageResult | ||||||
| import com.gewuyou.forgeboot.webmvc.dto.extension.map | import com.gewuyou.forgeboot.webmvc.dto.extension.map | ||||||
| import com.gewuyou.forgeboot.webmvc.dto.extension.toPageResult | import com.gewuyou.forgeboot.webmvc.dto.extension.toPageResult | ||||||
| @ -227,6 +226,6 @@ abstract class CrudServiceImplSpec<Entity : Any, Id : Any, Filter : Any>( | |||||||
|         exist?.let { |         exist?.let { | ||||||
|             setDeleted(it) |             setDeleted(it) | ||||||
|             update(it) |             update(it) | ||||||
|         } ?: log.error("删除失败,找不到该租户") |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -1,5 +1,7 @@ | |||||||
| dependencies { | dependencies { | ||||||
|     implementation(project(Modules.Core.EXTENSION)) |     implementation(project(Modules.Core.EXTENSION)) | ||||||
|  |     implementation(platform(libs.springBootDependencies.bom)) | ||||||
|  |     implementation(libs.springBoot.autoconfigure) | ||||||
|     kapt(libs.springBoot.configuration.processor) |     kapt(libs.springBoot.configuration.processor) | ||||||
|     compileOnly(libs.springBootStarter.web) |     compileOnly(libs.springBootStarter.web) | ||||||
| } | } | ||||||
|  | |||||||
| @ -31,6 +31,7 @@ springBootStarter-webflux = { group = "org.springframework.boot", name = "spring | |||||||
| springBootStarter-jpa = { group = "org.springframework.boot", name = "spring-boot-starter-data-jpa" } | springBootStarter-jpa = { group = "org.springframework.boot", name = "spring-boot-starter-data-jpa" } | ||||||
| springBootStarter-validation = { group = "org.springframework.boot", name = "spring-boot-starter-validation" } | springBootStarter-validation = { group = "org.springframework.boot", name = "spring-boot-starter-validation" } | ||||||
| springBoot-configuration-processor = { group = "org.springframework.boot", name = "spring-boot-configuration-processor", version.ref = "spring-boot-version" } | springBoot-configuration-processor = { group = "org.springframework.boot", name = "spring-boot-configuration-processor", version.ref = "spring-boot-version" } | ||||||
|  | springBoot-autoconfigure = { group = "org.springframework.boot", name = "spring-boot-autoconfigure" } | ||||||
| 
 | 
 | ||||||
| springCloudDependencies-bom = { module = "org.springframework.cloud:spring-cloud-dependencies", version.ref = "spring-cloud-version" } | springCloudDependencies-bom = { module = "org.springframework.cloud:spring-cloud-dependencies", version.ref = "spring-cloud-version" } | ||||||
| springCloudStarter-openfeign = { group = "org.springframework.cloud", name = "spring-cloud-starter-openfeign" } | springCloudStarter-openfeign = { group = "org.springframework.cloud", name = "spring-cloud-starter-openfeign" } | ||||||
|  | |||||||
| @ -1,41 +1,73 @@ | |||||||
| // The settings file is the entry point of every Gradle build. |  | ||||||
| // Its primary purpose is to define the subprojects. |  | ||||||
| // It is also used for some aspects of project-wide configuration, like managing plugins, dependencies, etc. |  | ||||||
| // https://docs.gradle.org/current/userguide/settings_file_basics.html |  | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * This settings.gradle.kts file configures the Gradle build for the forgeboot project. | ||||||
|  |  * It sets up dependency resolution, plugins, and includes all relevant subprojects. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | // Configures the dependency resolution management across all subprojects | ||||||
| dependencyResolutionManagement { | dependencyResolutionManagement { | ||||||
|     // Use Maven Central as the default repository (where Gradle will download dependencies) in all subprojects. |     /** | ||||||
|  |      * Use Maven Central as the default repository (where Gradle will download dependencies) | ||||||
|  |      * The @Suppress annotation is used to bypass warnings about unstable API usage. | ||||||
|  |      */ | ||||||
|     @Suppress("UnstableApiUsage") |     @Suppress("UnstableApiUsage") | ||||||
|     repositories { |     repositories { | ||||||
|         mavenCentral() |         mavenCentral() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Applies necessary plugins for the build process | ||||||
| plugins { | plugins { | ||||||
|     // Use the Foojay Toolchains plugin to automatically download JDKs required by subprojects. |     /** | ||||||
|  |      * Use the Foojay Toolchains plugin to automatically download JDKs required by subprojects. | ||||||
|  |      * This ensures consistent Java versions across different environments. | ||||||
|  |      */ | ||||||
|     id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" |     id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Include the `app` and `utils` subprojects in the build. | // Sets the root project name | ||||||
| // If there are changes in only one of the projects, Gradle will rebuild only the one that has changed. |  | ||||||
| // Learn more about structuring projects with Gradle - https://docs.gradle.org/8.7/userguide/multi_project_builds.html |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| rootProject.name = "forgeboot" | rootProject.name = "forgeboot" | ||||||
|  | 
 | ||||||
|  | //region module context | ||||||
|  | /** | ||||||
|  |  * Includes and configures projects related to 'forgeboot-context' | ||||||
|  |  * This module appears to be focused on contextual functionality within the application. | ||||||
|  |  */ | ||||||
|  | include( | ||||||
|  |     "forgeboot-context", | ||||||
|  |     ":forgeboot-context:forgeboot-context-api", | ||||||
|  |     ":forgeboot-context:forgeboot-context-impl", | ||||||
|  |     ":forgeboot-context:forgeboot-context-autoconfigure", | ||||||
|  | ) | ||||||
|  | project(":forgeboot-context").name = "forgeboot-context-spring-boot-starter" | ||||||
|  | project(":forgeboot-context:forgeboot-context-api").name = "forgeboot-context-api" | ||||||
|  | project(":forgeboot-context:forgeboot-context-impl").name = "forgeboot-context-impl" | ||||||
|  | project(":forgeboot-context:forgeboot-context-autoconfigure").name = "forgeboot-context-autoconfigure" | ||||||
|  | //endregion | ||||||
|  | 
 | ||||||
| //region module banner | //region module banner | ||||||
|  | /** | ||||||
|  |  * Includes and configures projects related to 'forgeboot-banner' | ||||||
|  |  * This module likely deals with banners or startup messages in the application. | ||||||
|  |  */ | ||||||
| include( | include( | ||||||
|     "forgeboot-banner", |     "forgeboot-banner", | ||||||
|     ":forgeboot-banner:forgeboot-banner-api", |     ":forgeboot-banner:forgeboot-banner-api", | ||||||
|     ":forgeboot-banner:forgeboot-banner-impl", |     ":forgeboot-banner:forgeboot-banner-impl", | ||||||
|     ":forgeboot-banner:forgeboot-banner-launcher", |     ":forgeboot-banner:forgeboot-banner-launcher", | ||||||
| ) | ) | ||||||
|  project(":forgeboot-banner").name = "forgeboot-banner" | project(":forgeboot-banner").name = "forgeboot-banner" | ||||||
|  project(":forgeboot-banner:forgeboot-banner-api").name = "forgeboot-banner-api" | project(":forgeboot-banner:forgeboot-banner-api").name = "forgeboot-banner-api" | ||||||
|  project(":forgeboot-banner:forgeboot-banner-impl").name = "forgeboot-banner-impl" | project(":forgeboot-banner:forgeboot-banner-impl").name = "forgeboot-banner-impl" | ||||||
|  project(":forgeboot-banner:forgeboot-banner-launcher").name = "forgeboot-banner-launcher" | project(":forgeboot-banner:forgeboot-banner-launcher").name = "forgeboot-banner-launcher" | ||||||
| //endregion | //endregion | ||||||
| 
 | 
 | ||||||
| //region module webmvc | //region module webmvc | ||||||
|  | /** | ||||||
|  |  * Includes and configures projects related to 'forgeboot-webmvc' | ||||||
|  |  * This module seems to handle Spring WebMVC-related functionalities like logging, | ||||||
|  |  * exceptions, DTO handling, validation, etc. | ||||||
|  |  */ | ||||||
| include( | include( | ||||||
|     "forgeboot-webmvc", |     "forgeboot-webmvc", | ||||||
|     ":forgeboot-webmvc:version", |     ":forgeboot-webmvc:version", | ||||||
| @ -57,6 +89,10 @@ project(":forgeboot-webmvc:spec").name = "forgeboot-webmvc-spec" | |||||||
| //endregion | //endregion | ||||||
| 
 | 
 | ||||||
| //region module core | //region module core | ||||||
|  | /** | ||||||
|  |  * Includes and configures projects related to 'forgeboot-core' | ||||||
|  |  * This module represents foundational components of the application. | ||||||
|  |  */ | ||||||
| include( | include( | ||||||
|     "forgeboot-core", |     "forgeboot-core", | ||||||
|     ":forgeboot-core:forgeboot-core-extension" |     ":forgeboot-core:forgeboot-core-extension" | ||||||
| @ -66,6 +102,10 @@ project(":forgeboot-core:forgeboot-core-extension").name = "forgeboot-core-exten | |||||||
| //endregion | //endregion | ||||||
| 
 | 
 | ||||||
| //region module i18n | //region module i18n | ||||||
|  | /** | ||||||
|  |  * Includes and configures projects related to 'forgeboot-i18n' | ||||||
|  |  * This module handles internationalization (i18n) support. | ||||||
|  |  */ | ||||||
| include( | include( | ||||||
|     "forgeboot-i18n", |     "forgeboot-i18n", | ||||||
|     ":forgeboot-i18n:forgeboot-i18n-api", |     ":forgeboot-i18n:forgeboot-i18n-api", | ||||||
| @ -78,14 +118,12 @@ project(":forgeboot-i18n:forgeboot-i18n-impl").name = "forgeboot-i18n-impl" | |||||||
| project(":forgeboot-i18n:forgeboot-i18n-autoconfigure").name = "forgeboot-i18n-autoconfigure" | project(":forgeboot-i18n:forgeboot-i18n-autoconfigure").name = "forgeboot-i18n-autoconfigure" | ||||||
| //endregion | //endregion | ||||||
| 
 | 
 | ||||||
| //region module webflux |  | ||||||
| //include( |  | ||||||
| //    "forgeboot-webflux", |  | ||||||
| //) |  | ||||||
| //project(":forgeboot-webflux").name = "forgeboot-webflux-spring-boot-starter" |  | ||||||
| //endregion |  | ||||||
| 
 | 
 | ||||||
| //region module trace | //region module trace | ||||||
|  | /** | ||||||
|  |  * Includes and configures projects related to 'forgeboot-trace' | ||||||
|  |  * This module handles distributed tracing functionality. | ||||||
|  |  */ | ||||||
| include( | include( | ||||||
|     "forgeboot-trace", |     "forgeboot-trace", | ||||||
|     ":forgeboot-trace:forgeboot-trace-api", |     ":forgeboot-trace:forgeboot-trace-api", | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user