mirror of
https://github.moeyy.xyz/https://github.com/GeWuYou/forgeboot
synced 2025-10-28 02:24:21 +08:00
Merge branch 'refactor/8-mvc-spec-core-jpa-refactoring-the-core-and-jpa-modules-of-spec-in-mvc' into 'main'
refactor(spec): 重构规范模块以提高可维护性和扩展性- 重新组织代码结构,将通用逻辑移至 spec-core 模块 Closes #8 See merge request gewuyou/forgeboot!13
This commit is contained in:
commit
75496b27ec
@ -1,3 +1,4 @@
|
|||||||
dependencies {
|
dependencies {
|
||||||
|
api(project(Modules.Webmvc.DTO))
|
||||||
|
compileOnly(libs.springFramework.data.commons)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package com.gewuyou.webmvc.spec.jpa.extension
|
package com.gewuyou.webmvc.spec.core.extension
|
||||||
|
|
||||||
import com.gewuyou.forgeboot.webmvc.dto.PageResult
|
import com.gewuyou.forgeboot.webmvc.dto.PageResult
|
||||||
import org.springframework.data.domain.Page
|
import org.springframework.data.domain.Page
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.gewuyou.webmvc.spec.jpa.extension
|
package com.gewuyou.webmvc.spec.core.extension
|
||||||
|
|
||||||
import com.gewuyou.forgeboot.webmvc.dto.PageResult
|
import com.gewuyou.forgeboot.webmvc.dto.PageResult
|
||||||
|
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.gewuyou.webmvc.spec.core.extension
|
||||||
|
|
||||||
|
import com.gewuyou.webmvc.spec.core.page.Pageable
|
||||||
|
import com.gewuyou.webmvc.spec.core.page.QueryComponent
|
||||||
|
import com.gewuyou.webmvc.spec.core.page.Sortable
|
||||||
|
import org.springframework.data.domain.PageRequest
|
||||||
|
import org.springframework.data.domain.Sort
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 QueryComponent 转换为 Spring Data 的 PageRequest 对象
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return PageRequest 分页请求对象,包含分页和排序信息
|
||||||
|
*
|
||||||
|
* 重要逻辑说明:
|
||||||
|
* 1. currentPage 从 1 开始计数,转换为 Spring Data 从 0 开始的页码
|
||||||
|
* 2. pageSize 保持不变,直接用于分页请求
|
||||||
|
* 3. 如果对象实现了 Sortable 接口,则使用其排序条件;否则使用未排序状态
|
||||||
|
* 4. 如果对象未实现 Pageable 接口,则使用默认分页参数(第一页,每页10条)
|
||||||
|
*/
|
||||||
|
fun QueryComponent.toPageRequest(): PageRequest {
|
||||||
|
val pageable = this as? Pageable
|
||||||
|
val sortable = this as? Sortable
|
||||||
|
// 默认分页参数
|
||||||
|
val page = pageable?.let { (it.currentPage - 1).coerceAtLeast(0) } ?: 0
|
||||||
|
val size = pageable?.pageSize ?: 10
|
||||||
|
// 排序规则
|
||||||
|
val sort = sortable?.let { Sort.by(it.toSortOrders()) } ?: Sort.unsorted()
|
||||||
|
return PageRequest.of(page, size, sort)
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package com.gewuyou.webmvc.spec.core.extension
|
||||||
|
|
||||||
|
import com.gewuyou.webmvc.spec.core.enums.SortDirection
|
||||||
|
import org.springframework.data.domain.Sort
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将自定义的排序方向枚举转换为 Spring Data的排序方向
|
||||||
|
*
|
||||||
|
* @return 对应的 Spring Data Sort.Direction 枚举值
|
||||||
|
*/
|
||||||
|
fun SortDirection.toSpringDirection(): Sort.Direction =
|
||||||
|
if (this == SortDirection.DESC) Sort.Direction.DESC else Sort.Direction.ASC
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.gewuyou.webmvc.spec.core.extension
|
||||||
|
|
||||||
|
import com.gewuyou.webmvc.spec.core.page.Sortable
|
||||||
|
import org.springframework.data.domain.Sort
|
||||||
|
import kotlin.collections.map
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将当前排序配置转换为可用于 Spring Data Pageable 的排序条件列表
|
||||||
|
*
|
||||||
|
* @param defaultSort 当没有其他排序条件时使用的默认排序,默认为按 createdAt 降序
|
||||||
|
* @return 适用于 Pageable 的排序条件列表
|
||||||
|
*
|
||||||
|
* 处理逻辑优先级:
|
||||||
|
* 1. 如果存在多个排序条件,使用 sortConditions 构建排序列表
|
||||||
|
* 2. 如果指定了单一排序字段,使用 sortBy 和 sortDirection 构建排序条件
|
||||||
|
* 3. 如果没有指定任何排序条件,使用默认排序
|
||||||
|
*/
|
||||||
|
fun Sortable.toSortOrders(defaultSort: Sort.Order = Sort.Order.desc("createdAt")): List<Sort.Order> {
|
||||||
|
return when {
|
||||||
|
sortConditions.isNotEmpty() -> {
|
||||||
|
// 使用 map 将每个 SortCondition 转换为 Spring 的 Sort.Order 对象
|
||||||
|
sortConditions.map {
|
||||||
|
Sort.Order(
|
||||||
|
it.direction.toSpringDirection(),
|
||||||
|
it.property
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sortBy.isNotBlank() -> {
|
||||||
|
// 当存在单一排序字段时,创建对应的排序条件列表
|
||||||
|
listOf(Sort.Order(sortDirection.toSpringDirection(), sortBy))
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> listOf(defaultSort)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,7 +11,6 @@ import com.gewuyou.webmvc.spec.core.page.StatusFilterable
|
|||||||
*
|
*
|
||||||
* @property currentPage 当前页码,从1开始计数的分页参数
|
* @property currentPage 当前页码,从1开始计数的分页参数
|
||||||
* @property pageSize 每页记录数量,控制分页大小
|
* @property pageSize 每页记录数量,控制分页大小
|
||||||
* @property statusConditions 状态条件映射,键值对形式表示的状态过滤条件
|
|
||||||
*
|
*
|
||||||
* @since 2025-07-19 09:15:12
|
* @since 2025-07-19 09:15:12
|
||||||
* @author gewuyou
|
* @author gewuyou
|
||||||
|
|||||||
@ -0,0 +1,133 @@
|
|||||||
|
package com.gewuyou.webmvc.spec.core.service
|
||||||
|
|
||||||
|
/**
|
||||||
|
*CRUD服务规范
|
||||||
|
*
|
||||||
|
* @since 2025-07-25 11:12:32
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface CrudServiceSpec <Entity: Any, Id: Any> {
|
||||||
|
/**
|
||||||
|
* 根据ID获取实体
|
||||||
|
*
|
||||||
|
* @param id 实体的唯一标识符
|
||||||
|
* @return 返回实体,如果不存在则返回null
|
||||||
|
*/
|
||||||
|
fun findById(id: Id): Entity?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有实体列表
|
||||||
|
*
|
||||||
|
* @return 返回实体列表
|
||||||
|
*/
|
||||||
|
fun list(): List<Entity>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存一个实体
|
||||||
|
*
|
||||||
|
* @param entity 要保存的实体
|
||||||
|
* @return 返回保存后的实体
|
||||||
|
*/
|
||||||
|
fun save(entity: Entity): Entity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新一个实体
|
||||||
|
*
|
||||||
|
* @param entity 要更新的实体
|
||||||
|
* @return 返回更新后的实体
|
||||||
|
*/
|
||||||
|
fun update(entity: Entity): Entity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除一个实体
|
||||||
|
*
|
||||||
|
* @param id 要删除的实体的ID
|
||||||
|
*/
|
||||||
|
fun deleteById(id: Id)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除实体
|
||||||
|
*
|
||||||
|
* @param ids 要删除的实体的ID列表
|
||||||
|
*/
|
||||||
|
fun deleteByIds(ids: List<Id>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除一个实体
|
||||||
|
*
|
||||||
|
* @param entity 要删除的实体
|
||||||
|
*/
|
||||||
|
fun deleteByOne(entity: Entity)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除实体
|
||||||
|
*
|
||||||
|
* @param entities 要删除的实体列表
|
||||||
|
*/
|
||||||
|
fun deleteByAll(entities: List<Entity>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 软删除
|
||||||
|
*
|
||||||
|
* 本函数用于标记实体为删除状态,而不是真正从数据库中移除
|
||||||
|
* 这种方法可以保留历史数据,同时避免数据泄露
|
||||||
|
*
|
||||||
|
* @param id 实体的唯一标识符
|
||||||
|
*/
|
||||||
|
fun softDelete(id: Id)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量软删除
|
||||||
|
*
|
||||||
|
* @param ids 要软删除的实体ID列表
|
||||||
|
*/
|
||||||
|
fun softDeleteByIds(ids: List<Id>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消软删除(恢复已删除实体)
|
||||||
|
*
|
||||||
|
* @param id 要恢复的实体ID
|
||||||
|
*/
|
||||||
|
fun restore(id: Id)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量取消软删除
|
||||||
|
*
|
||||||
|
* @param ids 要恢复的实体ID列表
|
||||||
|
*/
|
||||||
|
fun restoreByIds(ids: List<Id>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断实体是否已被软删除
|
||||||
|
*
|
||||||
|
* @param id 实体ID
|
||||||
|
* @return 如果是软删除状态返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
fun isSoftDeleted(id: Id): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID检查实体是否存在
|
||||||
|
*
|
||||||
|
* @param id 实体的ID
|
||||||
|
* @return 如果实体存在返回true,否则返回false
|
||||||
|
*/
|
||||||
|
fun existsById(id: Id): Boolean
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存实体
|
||||||
|
*
|
||||||
|
* @param entities 要保存的实体列表
|
||||||
|
* @return 返回保存后的实体列表
|
||||||
|
*/
|
||||||
|
fun saveAll(entities: List<Entity>): List<Entity>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询记录总数
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return 返回记录总数
|
||||||
|
*/
|
||||||
|
fun count(): Long
|
||||||
|
}
|
||||||
@ -1,64 +1,18 @@
|
|||||||
package com.gewuyou.webmvc.spec.jpa.extension
|
package com.gewuyou.webmvc.spec.jpa.extension
|
||||||
|
|
||||||
|
|
||||||
import com.gewuyou.webmvc.spec.core.enums.SortDirection
|
import com.gewuyou.webmvc.spec.core.extension.toPageRequest
|
||||||
import com.gewuyou.webmvc.spec.core.page.DateRangeFilterable
|
import com.gewuyou.webmvc.spec.core.page.*
|
||||||
import com.gewuyou.webmvc.spec.core.page.KeywordSearchable
|
|
||||||
import com.gewuyou.webmvc.spec.core.page.Pageable
|
|
||||||
import com.gewuyou.webmvc.spec.core.page.QueryComponent
|
|
||||||
import com.gewuyou.webmvc.spec.core.page.Sortable
|
|
||||||
import com.gewuyou.webmvc.spec.jpa.page.JpaFilterable
|
import com.gewuyou.webmvc.spec.jpa.page.JpaFilterable
|
||||||
import com.gewuyou.webmvc.spec.jpa.page.JpaStatusFilterable
|
import com.gewuyou.webmvc.spec.jpa.page.JpaStatusFilterable
|
||||||
import jakarta.persistence.criteria.CriteriaBuilder
|
import jakarta.persistence.criteria.CriteriaBuilder
|
||||||
import jakarta.persistence.criteria.Predicate
|
import jakarta.persistence.criteria.Predicate
|
||||||
import jakarta.persistence.criteria.Root
|
import jakarta.persistence.criteria.Root
|
||||||
|
|
||||||
import org.springframework.data.domain.PageRequest
|
import org.springframework.data.domain.PageRequest
|
||||||
import org.springframework.data.domain.Sort
|
|
||||||
import org.springframework.data.jpa.domain.Specification
|
import org.springframework.data.jpa.domain.Specification
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将自定义的排序方向枚举转换为 Spring Data JPA 的排序方向
|
|
||||||
*
|
|
||||||
* @return 对应的 Spring Data Sort.Direction 枚举值
|
|
||||||
*/
|
|
||||||
fun SortDirection.toSpringDirection(): Sort.Direction =
|
|
||||||
if (this == SortDirection.DESC) Sort.Direction.DESC else Sort.Direction.ASC
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将当前排序配置转换为可用于 Spring Data Pageable 的排序条件列表
|
|
||||||
*
|
|
||||||
* @param defaultSort 当没有其他排序条件时使用的默认排序,默认为按 createdAt 降序
|
|
||||||
* @return 适用于 Pageable 的排序条件列表
|
|
||||||
*
|
|
||||||
* 处理逻辑优先级:
|
|
||||||
* 1. 如果存在多个排序条件,使用 sortConditions 构建排序列表
|
|
||||||
* 2. 如果指定了单一排序字段,使用 sortBy 和 sortDirection 构建排序条件
|
|
||||||
* 3. 如果没有指定任何排序条件,使用默认排序
|
|
||||||
*/
|
|
||||||
fun Sortable.toSortOrders(defaultSort: Sort.Order = Sort.Order.desc("createdAt")): List<Sort.Order> {
|
|
||||||
return when {
|
|
||||||
sortConditions.isNotEmpty() -> {
|
|
||||||
// 使用 map 将每个 SortCondition 转换为 Spring 的 Sort.Order 对象
|
|
||||||
sortConditions.map {
|
|
||||||
Sort.Order(
|
|
||||||
it.direction.toSpringDirection(),
|
|
||||||
it.property
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sortBy.isNotBlank() -> {
|
|
||||||
// 当存在单一排序字段时,创建对应的排序条件列表
|
|
||||||
listOf(Sort.Order(sortDirection.toSpringDirection(), sortBy))
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> listOf(defaultSort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建关键字搜索对应的 JPA Predicate 条件列表
|
* 构建关键字搜索对应的 JPA Predicate 条件列表
|
||||||
*
|
*
|
||||||
@ -179,21 +133,6 @@ fun <T> QueryComponent.toSpecification(): Specification<T> {
|
|||||||
* @return 返回一个 Pair,包含以下两个元素:
|
* @return 返回一个 Pair,包含以下两个元素:
|
||||||
* - Specification<T>:JPA 查询条件规范
|
* - Specification<T>:JPA 查询条件规范
|
||||||
* - PageRequest:包含分页和排序信息的请求对象
|
* - PageRequest:包含分页和排序信息的请求对象
|
||||||
*
|
|
||||||
* 重要逻辑说明:
|
|
||||||
* 1. currentPage 从 1 开始计数,转换为 Spring Data 从 0 开始的页码
|
|
||||||
* 2. pageSize 保持不变,直接用于分页请求
|
|
||||||
* 3. 如果对象实现了 Sortable 接口,则使用其排序条件;否则使用未排序状态
|
|
||||||
* 4. 如果对象未实现 Pageable 接口,则使用默认分页参数(第一页,每页10条)
|
|
||||||
*/
|
*/
|
||||||
fun <T> QueryComponent.toJpaQuery(): Pair<Specification<T>, PageRequest> {
|
fun <T> QueryComponent.toJpaQuery(): Pair<Specification<T>, PageRequest> =
|
||||||
val specification = this.toSpecification<T>()
|
this.toSpecification<T>() to this.toPageRequest()
|
||||||
val pageable = this as? Pageable
|
|
||||||
val sortable = this as? Sortable
|
|
||||||
// 默认分页参数
|
|
||||||
val page = pageable?.let { (it.currentPage - 1).coerceAtLeast(0) } ?: 0
|
|
||||||
val size = pageable?.pageSize ?: 10
|
|
||||||
// 排序规则
|
|
||||||
val sort = sortable?.let { Sort.by(it.toSortOrders()) } ?: Sort.unsorted()
|
|
||||||
return specification to PageRequest.of(page, size, sort)
|
|
||||||
}
|
|
||||||
@ -2,106 +2,15 @@ package com.gewuyou.webmvc.spec.jpa.service
|
|||||||
|
|
||||||
import com.gewuyou.forgeboot.webmvc.dto.PageResult
|
import com.gewuyou.forgeboot.webmvc.dto.PageResult
|
||||||
import com.gewuyou.webmvc.spec.core.page.QueryComponent
|
import com.gewuyou.webmvc.spec.core.page.QueryComponent
|
||||||
|
import com.gewuyou.webmvc.spec.core.service.CrudServiceSpec
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jpa CRUD 服务规范
|
*JPA Crud服务规范
|
||||||
*
|
*
|
||||||
* 定义了对实体进行基本操作的服务接口,包括增删查改和分页查询等功能
|
* @since 2025-07-25 12:41:39
|
||||||
*
|
|
||||||
* @param Entity 实体类型
|
|
||||||
* @param Id 实体标识符类型
|
|
||||||
* @since 2025-05-29 20:18:22
|
|
||||||
* @author gewuyou
|
* @author gewuyou
|
||||||
*/
|
*/
|
||||||
interface JpaCrudServiceSpec<Entity: Any, Id: Any> {
|
interface JpaCrudServiceSpec<Entity: Any, Id: Any>: CrudServiceSpec<Entity, Id> {
|
||||||
/**
|
|
||||||
* 根据ID获取实体
|
|
||||||
*
|
|
||||||
* @param id 实体的唯一标识符
|
|
||||||
* @return 返回实体,如果不存在则返回null
|
|
||||||
*/
|
|
||||||
fun findById(id: Id): Entity?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有实体列表
|
|
||||||
*
|
|
||||||
* @return 返回实体列表
|
|
||||||
*/
|
|
||||||
fun list(): List<Entity>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存一个实体
|
|
||||||
*
|
|
||||||
* @param entity 要保存的实体
|
|
||||||
* @return 返回保存后的实体
|
|
||||||
*/
|
|
||||||
fun save(entity: Entity): Entity
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新一个实体
|
|
||||||
*
|
|
||||||
* @param entity 要更新的实体
|
|
||||||
* @return 返回更新后的实体
|
|
||||||
*/
|
|
||||||
fun update(entity: Entity): Entity
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除一个实体
|
|
||||||
*
|
|
||||||
* @param id 要删除的实体的ID
|
|
||||||
*/
|
|
||||||
fun deleteById(id: Id)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量删除实体
|
|
||||||
*
|
|
||||||
* @param ids 要删除的实体的ID列表
|
|
||||||
*/
|
|
||||||
fun deleteByIds(ids: List<Id>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除一个实体
|
|
||||||
*
|
|
||||||
* @param entity 要删除的实体
|
|
||||||
*/
|
|
||||||
fun deleteByOne(entity: Entity)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量删除实体
|
|
||||||
*
|
|
||||||
* @param entities 要删除的实体列表
|
|
||||||
*/
|
|
||||||
fun deleteByAll(entities: List<Entity>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 软删除
|
|
||||||
*
|
|
||||||
* 本函数用于标记实体为删除状态,而不是真正从数据库中移除
|
|
||||||
* 这种方法可以保留历史数据,同时避免数据泄露
|
|
||||||
*
|
|
||||||
* @param id 实体的唯一标识符
|
|
||||||
*/
|
|
||||||
fun softDelete(id: Id)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据ID检查实体是否存在
|
|
||||||
*
|
|
||||||
* @param id 实体的ID
|
|
||||||
* @return 如果实体存在返回true,否则返回false
|
|
||||||
*/
|
|
||||||
fun existsById(id: Id): Boolean
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量保存实体
|
|
||||||
*
|
|
||||||
* @param entities 要保存的实体列表
|
|
||||||
* @return 返回保存后的实体列表
|
|
||||||
*/
|
|
||||||
fun saveAll(entities: List<Entity>): List<Entity>
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询实体列表
|
* 分页查询实体列表
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1,16 +1,14 @@
|
|||||||
package com.gewuyou.webmvc.spec.jpa.service.impl
|
package com.gewuyou.webmvc.spec.jpa.service.impl
|
||||||
|
|
||||||
import com.gewuyou.forgeboot.webmvc.dto.PageResult
|
import com.gewuyou.forgeboot.webmvc.dto.PageResult
|
||||||
|
import com.gewuyou.webmvc.spec.core.extension.map
|
||||||
|
import com.gewuyou.webmvc.spec.core.extension.toPageResult
|
||||||
import com.gewuyou.webmvc.spec.core.page.QueryComponent
|
import com.gewuyou.webmvc.spec.core.page.QueryComponent
|
||||||
import com.gewuyou.webmvc.spec.jpa.extension.map
|
|
||||||
import com.gewuyou.webmvc.spec.jpa.extension.toJpaQuery
|
import com.gewuyou.webmvc.spec.jpa.extension.toJpaQuery
|
||||||
import com.gewuyou.webmvc.spec.jpa.extension.toPageResult
|
|
||||||
import com.gewuyou.webmvc.spec.jpa.extension.toSpecification
|
import com.gewuyou.webmvc.spec.jpa.extension.toSpecification
|
||||||
import com.gewuyou.webmvc.spec.jpa.repository.JpaCrudRepositorySpec
|
import com.gewuyou.webmvc.spec.jpa.repository.JpaCrudRepositorySpec
|
||||||
import com.gewuyou.webmvc.spec.jpa.service.JpaCrudServiceSpec
|
import com.gewuyou.webmvc.spec.jpa.service.JpaCrudServiceSpec
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jpa CRUD 服务实现的抽象类,提供通用的数据访问操作。
|
* Jpa CRUD 服务实现的抽象类,提供通用的数据访问操作。
|
||||||
*
|
*
|
||||||
@ -195,6 +193,24 @@ abstract class JpaCrudServiceImplSpec<Entity : Any, Id : Any>(
|
|||||||
*/
|
*/
|
||||||
protected abstract fun setDeleted(entity: Entity)
|
protected abstract fun setDeleted(entity: Entity)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将实体标记为已恢复状态。
|
||||||
|
*
|
||||||
|
* 此方法应在子类中实现,用于定义如何将实体从软删除状态恢复为正常状态(例如清除 deleted 字段或设置为 false)。
|
||||||
|
* 该机制允许在不物理删除数据的情况下,灵活地控制记录的可见性与状态。
|
||||||
|
*
|
||||||
|
* @param entity 实体对象,表示要恢复的实体
|
||||||
|
*/
|
||||||
|
protected abstract fun setRestored(entity: Entity)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断实体是否已被软删除
|
||||||
|
*
|
||||||
|
* @param entity 实体对象,用于检查其删除状态
|
||||||
|
* @return 如果实体已被标记为软删除状态返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
protected abstract fun isSoftDeletedByEntity(entity: Entity): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行软删除操作。
|
* 执行软删除操作。
|
||||||
*
|
*
|
||||||
@ -212,4 +228,60 @@ abstract class JpaCrudServiceImplSpec<Entity : Any, Id : Any>(
|
|||||||
update(it)
|
update(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询记录总数
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return 返回记录总数
|
||||||
|
*/
|
||||||
|
override fun count(): Long {
|
||||||
|
return repository.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量软删除
|
||||||
|
*
|
||||||
|
* @param ids 要软删除的实体ID列表
|
||||||
|
*/
|
||||||
|
override fun softDeleteByIds(ids: List<Id>) {
|
||||||
|
ids.forEach {
|
||||||
|
softDelete( it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消软删除(恢复已删除实体)
|
||||||
|
*
|
||||||
|
* @param id 要恢复的实体ID
|
||||||
|
*/
|
||||||
|
override fun restore(id: Id) {
|
||||||
|
val exist: Entity? = findById(id)
|
||||||
|
exist?.let {
|
||||||
|
setRestored(exist)
|
||||||
|
update(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量取消软删除
|
||||||
|
*
|
||||||
|
* @param ids 要恢复的实体ID列表
|
||||||
|
*/
|
||||||
|
override fun restoreByIds(ids: List<Id>) {
|
||||||
|
ids.forEach {
|
||||||
|
restore(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断实体是否已被软删除
|
||||||
|
*
|
||||||
|
* @param id 实体ID
|
||||||
|
* @return 如果是软删除状态返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
override fun isSoftDeleted(id: Id): Boolean {
|
||||||
|
val entity = findById(id)?:return false
|
||||||
|
return isSoftDeletedByEntity(entity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
package com.gewuyou.webmvc.spec.jpa.service.impl
|
||||||
|
|
||||||
|
import com.gewuyou.webmvc.spec.jpa.repository.JpaCrudRepositorySpec
|
||||||
|
|
||||||
|
/**
|
||||||
|
*简单的JPA Crud服务实现规范,请注意该抽象实现类不实现软删除
|
||||||
|
*
|
||||||
|
* @since 2025-07-25 14:24:19
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
abstract class SimpleJpaCrudServiceImplSpec<Entity : Any, Id : Any>(
|
||||||
|
private val repository: JpaCrudRepositorySpec<Entity, Id>,
|
||||||
|
): JpaCrudServiceImplSpec<Entity,Id>(repository) {
|
||||||
|
/**
|
||||||
|
* 标记实体为软删除状态。
|
||||||
|
*
|
||||||
|
* 此方法应在子类中实现,用于定义如何将实体标记为已删除(例如设置一个 deleted 字段)。
|
||||||
|
* 软删除不会从数据库中物理移除记录,而是将其标记为已删除状态,以便保留历史数据。
|
||||||
|
*
|
||||||
|
* @param entity 实体对象,表示要标记为删除状态的对象
|
||||||
|
*/
|
||||||
|
override fun setDeleted(entity: Entity) {
|
||||||
|
// 不实现
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将实体标记为已恢复状态。
|
||||||
|
*
|
||||||
|
* 此方法应在子类中实现,用于定义如何将实体从软删除状态恢复为正常状态(例如清除 deleted 字段或设置为 false)。
|
||||||
|
* 该机制允许在不物理删除数据的情况下,灵活地控制记录的可见性与状态。
|
||||||
|
*
|
||||||
|
* @param entity 实体对象,表示要恢复的实体
|
||||||
|
*/
|
||||||
|
override fun setRestored(entity: Entity) {
|
||||||
|
// 不实现
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断实体是否已被软删除
|
||||||
|
*
|
||||||
|
* @param entity 实体对象,用于检查其删除状态
|
||||||
|
* @return 如果实体已被标记为软删除状态返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
override fun isSoftDeletedByEntity(entity: Entity): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
[versions]
|
[versions]
|
||||||
jjwt-version = "0.12.6"
|
jjwt-version = "0.12.6"
|
||||||
gradleMavenPublishPlugin-version="0.32.0"
|
gradleMavenPublishPlugin-version = "0.32.0"
|
||||||
kotlin-version = "2.0.0"
|
kotlin-version = "2.0.0"
|
||||||
kotlinxDatetime-version = "0.6.1"
|
kotlinxDatetime-version = "0.6.1"
|
||||||
kotlinxSerializationJSON-version = "1.7.3"
|
kotlinxSerializationJSON-version = "1.7.3"
|
||||||
@ -12,15 +12,12 @@ kotlinxSerializationJSON-version = "1.7.3"
|
|||||||
axion-release-version = "1.18.7"
|
axion-release-version = "1.18.7"
|
||||||
spring-cloud-version = "2024.0.1"
|
spring-cloud-version = "2024.0.1"
|
||||||
spring-boot-version = "3.4.4"
|
spring-boot-version = "3.4.4"
|
||||||
spring-framework-version = "6.2.5"
|
|
||||||
slf4j-version = "2.0.17"
|
slf4j-version = "2.0.17"
|
||||||
map-struct-version="1.6.3"
|
caffeine-version = "3.2.1"
|
||||||
caffeine-version="3.2.1"
|
redisson-version = "3.50.0"
|
||||||
redisson-version="3.50.0"
|
|
||||||
org-pf4j-version = "3.13.0"
|
org-pf4j-version = "3.13.0"
|
||||||
org-pf4j-spring-version = "0.10.0"
|
org-pf4j-spring-version = "0.10.0"
|
||||||
org-yaml-snakeyaml-version = "2.4"
|
org-yaml-snakeyaml-version = "2.4"
|
||||||
org-yaml-snakeyaml-engine-version = "2.9"
|
|
||||||
[libraries]
|
[libraries]
|
||||||
kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin-version" }
|
kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin-version" }
|
||||||
kotlinxDatetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime-version" }
|
kotlinxDatetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime-version" }
|
||||||
@ -36,18 +33,17 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j-version" }
|
|||||||
springBootDependencies-bom = { module = "org.springframework.boot:spring-boot-dependencies", version.ref = "spring-boot-version" }
|
springBootDependencies-bom = { module = "org.springframework.boot:spring-boot-dependencies", version.ref = "spring-boot-version" }
|
||||||
springBootStarter-aop = { group = "org.springframework.boot", name = "spring-boot-starter-aop" }
|
springBootStarter-aop = { group = "org.springframework.boot", name = "spring-boot-starter-aop" }
|
||||||
springBootStarter-web = { group = "org.springframework.boot", name = "spring-boot-starter-web" }
|
springBootStarter-web = { group = "org.springframework.boot", name = "spring-boot-starter-web" }
|
||||||
springBootStarter-security = { group = "org.springframework.boot", name = "spring-boot-starter-security" }
|
|
||||||
springBootStarter-webflux = { group = "org.springframework.boot", name = "spring-boot-starter-webflux" }
|
springBootStarter-webflux = { group = "org.springframework.boot", name = "spring-boot-starter-webflux" }
|
||||||
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" }
|
||||||
springBootStarter-redis = { group = "org.springframework.boot", name = "spring-boot-starter-data-redis" }
|
springBootStarter-redis = { group = "org.springframework.boot", name = "spring-boot-starter-data-redis" }
|
||||||
|
|
||||||
redisson-springBootStarter= { group = "org.redisson", name = "redisson-spring-boot-starter", version.ref = "redisson-version" }
|
|
||||||
|
redisson-springBootStarter = { group = "org.redisson", name = "redisson-spring-boot-starter", version.ref = "redisson-version" }
|
||||||
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" }
|
springBoot-autoconfigure = { group = "org.springframework.boot", name = "spring-boot-autoconfigure" }
|
||||||
springBoot-starter = { group = "org.springframework.boot", name = "spring-boot-starter" }
|
springBoot-starter = { group = "org.springframework.boot", name = "spring-boot-starter" }
|
||||||
springExpression = { group = "org.springframework", name = "spring-expression", version.ref = "spring-framework-version" }
|
springFramework-data-commons = { group = "org.springframework.data", name = "spring-data-commons" }
|
||||||
|
|
||||||
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" }
|
||||||
|
|
||||||
@ -60,8 +56,6 @@ jackson-module-kotlin = { group = "com.fasterxml.jackson.module", name = "jackso
|
|||||||
|
|
||||||
reactor-core = { group = "io.projectreactor", name = "reactor-core" }
|
reactor-core = { group = "io.projectreactor", name = "reactor-core" }
|
||||||
#org
|
#org
|
||||||
org-mapstruct = { group = "org.mapstruct", name = "mapstruct", version.ref = "map-struct-version" }
|
|
||||||
org-snakeyaml-snakeyamlEngine = { group = "org.snakeyaml", name = "snakeyaml-engine", version.ref = "org-yaml-snakeyaml-engine-version" }
|
|
||||||
org-yaml-snakeyaml = { group = "org.yaml", name = "snakeyaml", version.ref = "org-yaml-snakeyaml-version" }
|
org-yaml-snakeyaml = { group = "org.yaml", name = "snakeyaml", version.ref = "org-yaml-snakeyaml-version" }
|
||||||
org-pf4j = { group = "org.pf4j", name = "pf4j", version.ref = "org-pf4j-version" }
|
org-pf4j = { group = "org.pf4j", name = "pf4j", version.ref = "org-pf4j-version" }
|
||||||
org-pf4jSpring = { group = "org.pf4j", name = "pf4j-spring", version.ref = "org-pf4j-spring-version" }
|
org-pf4jSpring = { group = "org.pf4j", name = "pf4j-spring", version.ref = "org-pf4j-spring-version" }
|
||||||
@ -71,7 +65,7 @@ jjwt-api = { module = "io.jsonwebtoken:jjwt-api", version.ref = "jjwt-version" }
|
|||||||
jjwt-impl = { module = "io.jsonwebtoken:jjwt-impl", version.ref = "jjwt-version" }
|
jjwt-impl = { module = "io.jsonwebtoken:jjwt-impl", version.ref = "jjwt-version" }
|
||||||
jjwt-jackson = { module = "io.jsonwebtoken:jjwt-jackson", version.ref = "jjwt-version" }
|
jjwt-jackson = { module = "io.jsonwebtoken:jjwt-jackson", version.ref = "jjwt-version" }
|
||||||
# com
|
# com
|
||||||
com-github-benManes-caffeine = { module = "com.github.ben-manes.caffeine:caffeine",version.ref = "caffeine-version" }
|
com-github-benManes-caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version.ref = "caffeine-version" }
|
||||||
# io
|
# io
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
@ -100,5 +94,5 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-version" }
|
|||||||
kotlin-plugin-spring = { id = "org.jetbrains.kotlin.plugin.spring", version.ref = "kotlin-version" }
|
kotlin-plugin-spring = { id = "org.jetbrains.kotlin.plugin.spring", version.ref = "kotlin-version" }
|
||||||
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt" }
|
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt" }
|
||||||
forgeboot-i18n-keygen = { id = "i18n-key-gen" }
|
forgeboot-i18n-keygen = { id = "i18n-key-gen" }
|
||||||
gradleMavenPublishPlugin={id="com.vanniktech.maven.publish", version.ref="gradleMavenPublishPlugin-version"}
|
gradleMavenPublishPlugin = { id = "com.vanniktech.maven.publish", version.ref = "gradleMavenPublishPlugin-version" }
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user