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, } }