Compare commits

..

No commits in common. "446b859a8ae666f627d0a7bd674178849b898047" and "58593f7b218de8bc3f1c49a5e017f59f300f5325" have entirely different histories.

43 changed files with 40 additions and 490 deletions

View File

@ -41,6 +41,7 @@ version = scmVersion.version
// 配置目录路径
val configDir = "$rootDir/config/"
val tasksDir = "$configDir/tasks/"
// 全局项目配置
allprojects {
@ -188,14 +189,7 @@ subprojects {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
tasks.named<Jar>("jar") {
manifest {
attributes(
"Implementation-Title" to project.name,
"Implementation-Version" to project.version
)
}
}
// 任务配置
tasks.withType<Jar> {
isEnabled = true

View File

@ -30,10 +30,4 @@ object Modules {
const val IMPL = ":forgeboot-trace-spring-boot-starter:forgeboot-trace-impl"
const val AUTOCONFIGURE = ":forgeboot-trace-spring-boot-starter:forgeboot-trace-autoconfigure"
}
object Banner {
const val STARTER = ":forgeboot-banner"
const val API = ":forgeboot-banner:forgeboot-banner-api"
const val IMPL = ":forgeboot-banner:forgeboot-banner-impl"
const val AUTOCONFIGURE = ":forgeboot-banner:forgeboot-banner-autoconfigure"
}
}

View File

@ -1,3 +0,0 @@
/gradlew text eol=lf
*.bat text eol=crlf
*.jar binary

View File

@ -1,40 +0,0 @@
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

View File

@ -1,6 +0,0 @@
extra{
setProperty(ProjectFlags.IS_ROOT_MODULE,true)
}
dependencies {
}

View File

@ -1,3 +0,0 @@
/gradlew text eol=lf
*.bat text eol=crlf
*.jar binary

View File

@ -1,40 +0,0 @@
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

View File

@ -1,6 +0,0 @@
dependencies {
compileOnly(platform(libs.springBootDependencies.bom))
compileOnly(libs.springBootStarter.web)
kapt(libs.springBoot.configuration.processor)
}

View File

@ -1,16 +0,0 @@
package com.gewuyou.forgeboot.banner.api.config.entities
import com.gewuyou.forgeboot.banner.api.enums.BannerStrategy
import org.springframework.boot.context.properties.ConfigurationProperties
/**
*横幅配置
*
* @since 2025-06-03 12:55:18
* @author gewuyou
*/
@ConfigurationProperties("forgeboot.banner")
class BannerProperties {
var path: String = "banners/"
var strategy: BannerStrategy = BannerStrategy.Random
}

View File

@ -1,13 +0,0 @@
package com.gewuyou.forgeboot.banner.api.enums
/**
*横幅策略
*
* @since 2025-06-03 13:04:30
* @author gewuyou
*/
enum class BannerStrategy {
First,
Random,
All
}

View File

@ -1,18 +0,0 @@
package com.gewuyou.forgeboot.banner.api.provider
import java.io.PrintStream
/**
* 横幅提供商接口用于定义横幅输出行为
*
* @since 2025-06-03 12:17:19
* @author gewuyou
*/
fun interface BannerProvider {
/**
* 输出横幅内容到指定的输出流
*
* @param out 输出流对象
*/
fun printBanner(out: PrintStream)
}

View File

@ -1,3 +0,0 @@
/gradlew text eol=lf
*.bat text eol=crlf
*.jar binary

View File

@ -1,40 +0,0 @@
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

View File

@ -1,6 +0,0 @@
dependencies {
compileOnly(platform(libs.springBootDependencies.bom))
compileOnly(libs.springBootStarter.web)
compileOnly(project(Modules.Banner.API))
}

View File

@ -1,75 +0,0 @@
package com.gewuyou.forgeboot.banner.impl
import com.gewuyou.forgeboot.banner.api.enums.BannerStrategy
import com.gewuyou.forgeboot.banner.api.provider.BannerProvider
import com.gewuyou.forgeboot.banner.api.config.entities.BannerProperties
import org.springframework.core.io.Resource
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
import java.io.PrintStream
import java.util.Random
import java.util.Scanner
/**
* 可配置横幅提供者类根据配置属性输出横幅
*
* @param properties 横幅属性配置对象
*/
class ConfigurableBannerProvider(
private val properties: BannerProperties,
): BannerProvider {
/**
* 输出横幅内容到指定的输出流
*
* @param out 输出流对象可为空
*/
override fun printBanner(out: PrintStream) {
try {
// 根据属性配置获取横幅资源路径模式
val pattern = "classpath*:" + properties.path + "*.txt"
// 使用路径匹配获取所有符合条件的资源
val resources: Array<Resource?> = PathMatchingResourcePatternResolver().getResources(pattern)
// 如果没有找到任何资源,则输出提示信息并返回
if (resources.isEmpty()) {
out.println("No banner found in: " + properties.path)
return
}
// 根据配置的策略选择横幅输出方式
when (properties.strategy) {
BannerStrategy.First-> resources[0]?.let { printResource(it, out) }
BannerStrategy.Random -> resources[Random().nextInt(resources.size)]?.let { printResource(it, out) }
BannerStrategy.All -> {
for (resource in resources) {
resource?.let { printResource(it, out) }
out.println()
}
}
}
} catch (e: Exception) {
// 异常处理:输出错误信息
out.println("Error loading banners: " + e.message)
}
}
/**
* 打印资源内容到指定的输出流
*
* @param resource 要打印的资源对象
* @param out 输出流对象
*/
private fun printResource(resource: Resource, out: PrintStream) {
try {
// 使用输入流读取资源内容并输出
resource.inputStream.use { `is` ->
Scanner(`is`).useDelimiter("\\A").use { scanner ->
while (scanner.hasNext()) {
out.println(scanner.next())
}
}
}
} catch (e: java.lang.Exception) {
// 异常处理:输出错误信息
out.println("Failed to print banner: " + e.message)
}
}
}

View File

@ -1,3 +0,0 @@
/gradlew text eol=lf
*.bat text eol=crlf
*.jar binary

View File

@ -1,40 +0,0 @@
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

View File

@ -1,8 +0,0 @@
dependencies {
compileOnly(platform(libs.springBootDependencies.bom))
compileOnly(libs.springBootStarter.web)
compileOnly(project(Modules.Banner.IMPL))
compileOnly(project(Modules.Banner.API))
implementation(project(Modules.Core.EXTENSION))
}

View File

@ -1,34 +0,0 @@
package com.gewuyou.forgeboot.banner.launcher
import com.gewuyou.forgeboot.banner.api.config.entities.BannerProperties
import com.gewuyou.forgeboot.banner.impl.ConfigurableBannerProvider
import org.springframework.boot.Banner
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.context.properties.bind.Binder
import org.springframework.core.env.StandardEnvironment
/**
* 使用Forge横幅运行Spring应用的函数
* 该函数主要用于简化Spring应用的启动过程特别是当需要自定义横幅时
* 它通过反射启动一个Spring应用并允许开发者在启动时自定义横幅
*
* @param T Spring应用的主类类型必须是Any的子类
* @param args 命令行参数传递给Spring应用
*/
inline fun <reified T : Any> runApplicationWithForgeBanner(vararg args: String) {
// 创建并配置Spring应用的环境
val env = StandardEnvironment()
// 获取配置属性绑定器
val binder = Binder.get(env)
// 从配置中绑定BannerProperties属性如果没有找到则使用默认值
val props = binder.bind("banner", BannerProperties::class.java).orElse(BannerProperties())
// 创建一个自定义的Banner实例
val banner = Banner { _, _, out -> ConfigurableBannerProvider(props).printBanner(out) }
// 构建并运行Spring应用
SpringApplicationBuilder()
.sources(T::class.java)
.banner(banner)
.environment(env)
.run(*args)
}

View File

@ -1,7 +1,8 @@
dependencies {
compileOnly(platform(libs.springBootDependencies.bom))
// compileOnly(platform(libs.springCloudDependencies.bom))
// compileOnly(libs.springBootStarter.web)
compileOnly(libs.springBootStarter.webflux)
kapt(libs.springBoot.configuration.processor)
}

View File

@ -0,0 +1 @@
spring.application.name=forgeboot-i18n-api

View File

@ -3,7 +3,7 @@ package com.gewuyou.forgeboot.i18n.autoconfigure
import com.gewuyou.forgeboot.core.extension.log
import com.gewuyou.forgeboot.i18n.api.MessageResolver
import com.gewuyou.forgeboot.i18n.api.config.I18nProperties
import com.gewuyou.forgeboot.i18n.impl.config.I18nProperties
import com.gewuyou.forgeboot.i18n.impl.filter.ReactiveLocaleResolver
import com.gewuyou.forgeboot.i18n.impl.resolver.I18nMessageResolver
import jakarta.servlet.http.HttpServletRequest

View File

@ -1,4 +1,4 @@
package com.gewuyou.forgeboot.i18n.api.config
package com.gewuyou.forgeboot.i18n.impl.config
import org.springframework.boot.context.properties.ConfigurationProperties

View File

@ -2,7 +2,7 @@ package com.gewuyou.forgeboot.i18n.impl.filter
import com.gewuyou.forgeboot.i18n.api.WebFluxLocaleResolver
import com.gewuyou.forgeboot.i18n.api.config.I18nProperties
import com.gewuyou.forgeboot.i18n.impl.config.I18nProperties
import org.slf4j.LoggerFactory
import org.springframework.context.i18n.LocaleContextHolder

View File

@ -0,0 +1 @@
spring.application.name=forgeboot-i18n-impl-starter

View File

@ -1,5 +1,3 @@
dependencies {
compileOnly(platform(libs.springBootDependencies.bom))
compileOnly(libs.springBootStarter.web)
kapt(libs.springBoot.configuration.processor)
}

View File

@ -0,0 +1 @@
spring.application.name=api

View File

@ -1,7 +1,7 @@
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.config.TraceProperties
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean

View File

@ -3,7 +3,7 @@ package com.gewuyou.forgeboot.trace.autoconfig
import com.gewuyou.forgeboot.core.extension.log
import com.gewuyou.forgeboot.trace.api.RequestIdProvider
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
import com.gewuyou.forgeboot.trace.impl.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

View File

@ -1,7 +1,7 @@
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.config.TraceProperties
import com.gewuyou.forgeboot.trace.impl.filter.WebClientRequestIdFilter
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass

View File

@ -1,4 +1,4 @@
package com.gewuyou.forgeboot.trace.api.config
package com.gewuyou.forgeboot.trace.impl.config
import org.springframework.boot.context.properties.ConfigurationProperties

View File

@ -1,6 +1,6 @@
package com.gewuyou.forgeboot.trace.impl.decorator
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
import com.gewuyou.forgeboot.trace.impl.config.TraceProperties
import org.slf4j.MDC
import org.springframework.core.task.TaskDecorator

View File

@ -3,7 +3,7 @@ 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.config.TraceProperties
import com.gewuyou.forgeboot.trace.impl.extension.isSkipRequest
import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil

View File

@ -2,7 +2,7 @@ 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.config.TraceProperties
import com.gewuyou.forgeboot.trace.impl.extension.isSkipRequest
import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil

View File

@ -1,7 +1,7 @@
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.config.TraceProperties
import com.gewuyou.forgeboot.trace.impl.extension.isSkipRequest
import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil

View File

@ -3,7 +3,7 @@ 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.config.TraceProperties
import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil
import feign.RequestInterceptor
import feign.RequestTemplate

View File

@ -0,0 +1 @@
spring.application.name=impl

View File

@ -5,45 +5,25 @@ import org.mapstruct.MappingTarget
import org.mapstruct.NullValuePropertyMappingStrategy
/**
* Base Mapper基础映射器
* 提供基础的对象映射操作定义包含合并覆盖合并单个对象拷贝及列表拷贝的方法
*Base Mapper 基础映射器
*
* @since 2025-05-30 22:50:18
* @author gewuyou
*/
interface BaseMapper<S, T> {
interface BaseMapper<T,S> {
/**
* source 对象中的非 null 属性合并到 target 对象中
* 注意null 值的属性不会覆盖 target 中已有的值
*
* @param target 目标对象将被更新
* @param source 源对象提供需要合并的数据
* 合并 source target忽略 null
*/
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
fun mergeIgnoreNull(@MappingTarget target: T, source: S)
/**
* 全量覆盖合并 source target
* 注意即使 source 中的字段为 null也会覆盖 target 中对应的字段
*
* @param target 目标对象将被更新
* @param source 源对象提供需要合并的数据
* 全量覆盖合并source 字段即使为 null 也覆盖 target
*/
fun overwriteMerge(@MappingTarget target: T, source: S)
/**
* source 对象的内容拷贝到一个新的 T 类型对象中
*
* @param source 源对象提供数据
* @return 返回一个新的目标类型对象
* 拷贝 source 到新对象
*/
fun copy(source: S): T
/**
* 将源对象列表中的每个元素拷贝为新的目标类型对象生成一个目标对象列表
*
* @param sources 源对象列表
* @return 返回目标类型对象的列表
*/
fun copyList(sources: List<S>): List<T>
}

View File

@ -1,15 +1,10 @@
package com.gewuyou.forgeboot.webmvc.dto.mapper
import org.mapstruct.BeanMapping
import org.mapstruct.MappingTarget
import org.mapstruct.NullValuePropertyMappingStrategy
package com.gewuyou.forgeboot.webmvc.dto.dto.mapper
/**
* 转换映射器接口
* 转换 映射器
*
* 提供通用的实体类(Entity)与数据传输对象(DTO)之间的双向转换能力
* 该接口定义了基础的数据转换方法包括单个对象和集合对象的转换
* 并支持部分更新操作忽略空值属性
* 定义了一个转换映射器接口用于在实体类和数据传输对象DTO之间进行转换
* 这个接口定义了四个基本转换方法实体到DTODTO到实体实体列表到DTO列表和DTO列表到实体列表
*
* @param <Entity> 实体类类型
* @param <Dto> 数据传输对象类型
@ -17,47 +12,36 @@ import org.mapstruct.NullValuePropertyMappingStrategy
* @since 2025-05-30 22:53:35
* @author gewuyou
*/
interface ConversionMapper<Entity, Dto> {
interface ConversionMapper<Entity, Dto>{
/**
* 将实体对象转换为对应的DTO对象
* 将实体对象转换为DTO对象
*
* @param entity 需要转换的实体对象
* @param entity 实体对象
* @return 转换后的DTO对象
*/
fun toDto(entity: Entity): Dto
/**
* 将DTO对象转换为对应的实体对象
* 将DTO对象转换为实体对象
*
* @param dto 需要转换的DTO对象
* @param dto DTO对象
* @return 转换后的实体对象
*/
fun toEntity(dto: Dto): Entity
/**
* 将实体对象列表转换为对应的DTO对象列表
* 将实体对象列表转换为DTO对象列表
*
* @param entityList 需要转换的实体对象列表
* @param entityList 实体对象列表
* @return 转换后的DTO对象列表
*/
fun toDtoList(entityList: List<Entity>): List<Dto>
/**
* 将DTO对象列表转换为对应的实体对象列表
* 将DTO对象列表转换为实体对象列表
*
* @param dtoList 需要转换的DTO对象列表
* @param dtoList DTO对象列表
* @return 转换后的实体对象列表
*/
fun toEntityList(dtoList: List<Dto>): List<Entity>
/**
* 使用非空属性对实体进行部分更新
*
* 注意此操作不会覆盖实体中为空的属性
*
* @param dto 需要用于更新的DTO对象
* @param entity 需要被更新的实体对象
*/
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
fun partialUpdate(dto: Dto, @MappingTarget entity: Entity)
}

View File

@ -2,5 +2,4 @@
dependencies {
compileOnly(libs.springBootStarter.jpa)
implementation(project(Modules.Webmvc.DTO))
implementation(project(Modules.Core.EXTENSION))
}

View File

@ -74,16 +74,6 @@ interface CrudServiceSpec<Entity: Any, Id: Any, Filter: Any> {
*/
fun deleteByAll(entities: List<Entity>)
/**
* 软删除
*
* 本函数用于标记实体为删除状态而不是真正从数据库中移除
* 这种方法可以保留历史数据同时避免数据泄露
*
* @param id 实体的唯一标识符
*/
fun softDelete(id: Id)
/**
* 根据ID检查实体是否存在
*

View File

@ -1,6 +1,5 @@
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.extension.map
import com.gewuyou.forgeboot.webmvc.dto.extension.toPageResult
@ -202,31 +201,4 @@ abstract class CrudServiceImplSpec<Entity : Any, Id : Any, Filter : Any>(
override fun saveAll(entities: List<Entity>): List<Entity> {
return repository.saveAll(entities)
}
/**
* 标记实体为软删除状态
*
* 此方法应在子类中实现用于定义如何将实体标记为已删除例如设置一个 deleted 字段
* 软删除不会从数据库中物理移除记录而是将其标记为已删除状态以便保留历史数据
*
* @param entity 实体对象表示要标记为删除状态的对象
*/
protected abstract fun setDeleted(entity: Entity)
/**
* 执行软删除操作
*
* 该方法会根据提供的 ID 查找实体如果找到该实体则调用 [setDeleted] 方法将其标记为删除状态
* 然后通过 [update] 方法保存更改如果没有找到实体则记录一条错误日志
*
* 软删除机制可以保留历史数据同时避免敏感信息的直接删除确保数据可追溯且安全
*
* @param id 实体的唯一标识符用于查找需要删除的实体
*/
override fun softDelete(id: Id) {
val exist: Entity? = findById(id)
exist?.let {
setDeleted(it)
update(it)
} ?: log.error("删除失败,找不到该租户")
}
}

View File

@ -22,18 +22,6 @@ plugins {
rootProject.name = "forgeboot"
//region module banner
include(
"forgeboot-banner",
":forgeboot-banner:forgeboot-banner-api",
":forgeboot-banner:forgeboot-banner-impl",
":forgeboot-banner:forgeboot-banner-launcher",
)
project(":forgeboot-banner").name = "forgeboot-banner"
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-launcher").name = "forgeboot-banner-launcher"
//endregion
//region module webmvc
include(
@ -50,7 +38,7 @@ project(":forgeboot-webmvc").name = "forgeboot-webmvc-spring-boot-starter"
project(":forgeboot-webmvc:version").name = "forgeboot-webmvc-version-spring-boot-starter"
project(":forgeboot-webmvc:logger").name = "forgeboot-webmvc-logger-spring-boot-starter"
project(":forgeboot-webmvc:exception").name = "forgeboot-webmvc-exception-spring-boot-starter"
project(":forgeboot-webmvc:exception-i18n").name = "forgeboot-webmvc-exception-i18n-spring-boot-starter"
project(":forgeboot-webmvc:exception-i18n").name = "forgeboot-webmvc-exception-i18n-spring-boot-starter"
project(":forgeboot-webmvc:dto").name = "forgeboot-webmvc-dto"
project(":forgeboot-webmvc:validation").name = "forgeboot-webmvc-validation"
project(":forgeboot-webmvc:spec").name = "forgeboot-webmvc-spec"