RoleServiceImpl.kt
package io.github.lishangbu.avalon.auth.service.impl
import io.github.lishangbu.avalon.auth.entity.Role
import io.github.lishangbu.avalon.auth.entity.addBy
import io.github.lishangbu.avalon.auth.entity.dto.RoleSpecification
import io.github.lishangbu.avalon.auth.entity.dto.RoleView
import io.github.lishangbu.avalon.auth.entity.dto.SaveRoleInput
import io.github.lishangbu.avalon.auth.entity.dto.UpdateRoleInput
import io.github.lishangbu.avalon.auth.repository.AuthorizationFetchers
import io.github.lishangbu.avalon.auth.repository.MenuRepository
import io.github.lishangbu.avalon.auth.repository.PermissionRepository
import io.github.lishangbu.avalon.auth.repository.RoleRepository
import io.github.lishangbu.avalon.auth.service.RoleService
import io.github.lishangbu.avalon.jimmer.support.readOrNull
import org.babyfish.jimmer.Page
import org.babyfish.jimmer.sql.ast.mutation.SaveMode
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
/** 角色服务实现。 */
@Service
class RoleServiceImpl(
private val roleRepository: RoleRepository,
private val menuRepository: MenuRepository,
private val permissionRepository: PermissionRepository,
) : RoleService {
override fun getPageByCondition(
specification: RoleSpecification,
pageable: Pageable,
): Page<RoleView> = roleRepository.pageViews(specification, pageable)
override fun listByCondition(specification: RoleSpecification): List<RoleView> = roleRepository.listViews(specification)
override fun getById(id: Long): RoleView? = roleRepository.loadViewById(id)
@Transactional(rollbackFor = [Exception::class])
override fun save(command: SaveRoleInput): RoleView {
val prepared = bindAssociations(command.toEntity(), false)
return roleRepository.save(prepared, SaveMode.INSERT_ONLY).let(::reloadView)
}
@Transactional(rollbackFor = [Exception::class])
override fun update(command: UpdateRoleInput): RoleView {
val prepared = bindAssociations(command.toEntity(), true)
return roleRepository.save(prepared).let(::reloadView)
}
@Transactional(rollbackFor = [Exception::class])
override fun removeById(id: Long) {
roleRepository.deleteById(id)
}
private fun bindAssociations(
role: Role,
preserveWhenNull: Boolean,
): Role {
val existing =
if (preserveWhenNull) {
role.readOrNull { id }?.let { roleId ->
roleRepository.findNullable(roleId, AuthorizationFetchers.ROLE_WITH_BINDINGS)
}
} else {
null
}
val currentMenus = role.readOrNull { menus }
val menuIds = currentMenus?.mapNotNull { it.readOrNull { id } }?.toCollection(LinkedHashSet())
val shouldLoadMenus = currentMenus != null
val boundMenus =
when {
currentMenus != null && !menuIds.isNullOrEmpty() -> menuRepository.findAllById(menuIds)
currentMenus != null -> emptyList()
preserveWhenNull -> existing?.readOrNull { menus } ?: emptyList()
else -> emptyList()
}
val currentPermissions = role.readOrNull { permissions }
val permissionIds =
currentPermissions?.mapNotNull { it.readOrNull { id } }?.toCollection(LinkedHashSet())
val shouldLoadPermissions = currentPermissions != null
val boundPermissions =
when {
currentPermissions != null && !permissionIds.isNullOrEmpty() -> permissionRepository.findAllById(permissionIds)
currentPermissions != null -> emptyList()
preserveWhenNull -> existing?.readOrNull { permissions } ?: emptyList()
else -> emptyList()
}
return Role {
role.readOrNull { id }?.let { id = it }
code = role.readOrNull { code } ?: existing?.readOrNull { code }
name = role.readOrNull { name } ?: existing?.readOrNull { name }
enabled = role.readOrNull { enabled } ?: existing?.readOrNull { enabled }
if (shouldLoadMenus) {
menus()
}
boundMenus.forEach { boundMenu -> menus().addBy(boundMenu) }
if (shouldLoadPermissions) {
permissions()
}
boundPermissions.forEach { boundPermission -> permissions().addBy(boundPermission) }
}
}
private fun reloadView(role: Role): RoleView =
requireNotNull(roleRepository.loadViewById(role.id)) {
"未找到 ID=${role.id} 对应的角色"
}
}