backend/tmp/reborn/usecase/permission_usecase.go

240 lines
5.4 KiB
Go
Raw Normal View History

2025-10-07 09:29:47 +00:00
package usecase
import (
"context"
"permission/reborn/domain/entity"
"permission/reborn/domain/errors"
"permission/reborn/domain/repository"
"permission/reborn/domain/usecase"
"sync"
)
type permissionUseCase struct {
permRepo repository.PermissionRepository
rolePermRepo repository.RolePermissionRepository
roleRepo repository.RoleRepository
userRoleRepo repository.UserRoleRepository
cache repository.CacheRepository
// 權限樹快取 (in-memory)
treeMutex sync.RWMutex
tree *PermissionTree
}
// NewPermissionUseCase 建立權限 UseCase
func NewPermissionUseCase(
permRepo repository.PermissionRepository,
rolePermRepo repository.RolePermissionRepository,
roleRepo repository.RoleRepository,
userRoleRepo repository.UserRoleRepository,
cache repository.CacheRepository,
) usecase.PermissionUseCase {
return &permissionUseCase{
permRepo: permRepo,
rolePermRepo: rolePermRepo,
roleRepo: roleRepo,
userRoleRepo: userRoleRepo,
cache: cache,
}
}
func (uc *permissionUseCase) GetAll(ctx context.Context) ([]*usecase.PermissionResponse, error) {
perms, err := uc.permRepo.ListActive(ctx)
if err != nil {
return nil, err
}
result := make([]*usecase.PermissionResponse, 0, len(perms))
for _, perm := range perms {
result = append(result, uc.toResponse(perm))
}
return result, nil
}
func (uc *permissionUseCase) GetTree(ctx context.Context) (*usecase.PermissionTreeNode, error) {
tree, err := uc.getOrBuildTree(ctx)
if err != nil {
return nil, err
}
roots := tree.ToTree()
if len(roots) == 0 {
return nil, errors.ErrPermissionNotFound
}
// 如果有多個根節點,包裝成一個虛擬根節點
if len(roots) == 1 {
return roots[0], nil
}
return &usecase.PermissionTreeNode{
PermissionResponse: &usecase.PermissionResponse{
Name: "root",
},
Children: roots,
}, nil
}
func (uc *permissionUseCase) GetByHTTP(ctx context.Context, path, method string) (*usecase.PermissionResponse, error) {
perm, err := uc.permRepo.GetByHTTP(ctx, path, method)
if err != nil {
return nil, err
}
return uc.toResponse(perm), nil
}
func (uc *permissionUseCase) ExpandPermissions(ctx context.Context, permissions entity.Permissions) (entity.Permissions, error) {
tree, err := uc.getOrBuildTree(ctx)
if err != nil {
return nil, err
}
return tree.ExpandPermissions(permissions)
}
func (uc *permissionUseCase) GetUsersByPermission(ctx context.Context, permissionNames []string) ([]string, error) {
// 取得權限
perms, err := uc.permRepo.GetByNames(ctx, permissionNames)
if err != nil {
return nil, err
}
if len(perms) == 0 {
return []string{}, nil
}
// 取得權限 ID
permIDs := make([]int64, len(perms))
for i, perm := range perms {
permIDs[i] = perm.ID
}
// 取得擁有這些權限的角色
rolePerms, err := uc.rolePermRepo.GetByPermissionIDs(ctx, permIDs)
if err != nil {
return nil, err
}
// 取得角色 ID
roleIDMap := make(map[int64]bool)
for _, rp := range rolePerms {
roleIDMap[rp.RoleID] = true
}
roleIDs := make([]int64, 0, len(roleIDMap))
for roleID := range roleIDMap {
roleIDs = append(roleIDs, roleID)
}
// 批量取得角色
roles, err := uc.roleRepo.List(ctx, repository.RoleFilter{})
if err != nil {
return nil, err
}
roleUIDMap := make(map[int64]string)
for _, role := range roles {
if roleIDMap[role.ID] {
roleUIDMap[role.ID] = role.UID
}
}
// 取得使用這些角色的使用者
userUIDs := make([]string, 0)
for _, roleUID := range roleUIDMap {
userRoles, err := uc.userRoleRepo.GetByRoleID(ctx, roleUID)
if err != nil {
continue
}
for _, ur := range userRoles {
userUIDs = append(userUIDs, ur.UID)
}
}
return userUIDs, nil
}
// getOrBuildTree 取得或建立權限樹 (帶快取)
func (uc *permissionUseCase) getOrBuildTree(ctx context.Context) (*PermissionTree, error) {
// 先檢查 in-memory 快取
uc.treeMutex.RLock()
if uc.tree != nil {
uc.treeMutex.RUnlock()
return uc.tree, nil
}
uc.treeMutex.RUnlock()
// 嘗試從 Redis 快取取得
if uc.cache != nil {
var perms []*entity.Permission
err := uc.cache.GetObject(ctx, repository.CacheKeyPermissionTree, &perms)
if err == nil && len(perms) > 0 {
tree := NewPermissionTree(perms)
// 更新 in-memory 快取
uc.treeMutex.Lock()
uc.tree = tree
uc.treeMutex.Unlock()
return tree, nil
}
}
// 從資料庫建立
perms, err := uc.permRepo.ListActive(ctx)
if err != nil {
return nil, err
}
tree := NewPermissionTree(perms)
// 檢測循環依賴
if err := tree.DetectCircularDependency(); err != nil {
return nil, err
}
// 更新快取
uc.treeMutex.Lock()
uc.tree = tree
uc.treeMutex.Unlock()
if uc.cache != nil {
_ = uc.cache.SetObject(ctx, repository.CacheKeyPermissionTree, perms, 0)
}
return tree, nil
}
// InvalidateTreeCache 清除權限樹快取
func (uc *permissionUseCase) InvalidateTreeCache(ctx context.Context) error {
uc.treeMutex.Lock()
uc.tree = nil
uc.treeMutex.Unlock()
if uc.cache != nil {
return uc.cache.Delete(ctx, repository.CacheKeyPermissionTree, repository.CacheKeyPermissionList)
}
return nil
}
func (uc *permissionUseCase) toResponse(perm *entity.Permission) *usecase.PermissionResponse {
status := entity.PermissionOpen
if !perm.IsActive() {
status = entity.PermissionClose
}
return &usecase.PermissionResponse{
ID: perm.ID,
ParentID: perm.ParentID,
Name: perm.Name,
HTTPPath: perm.HTTPPath,
HTTPMethod: perm.HTTPMethod,
Status: status,
Type: perm.Type,
}
}