240 lines
5.4 KiB
Go
240 lines
5.4 KiB
Go
|
|
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,
|
||
|
|
}
|
||
|
|
}
|