backend/pkg/permission/usecase/permission_usecase.go

239 lines
5.4 KiB
Go

package usecase
import (
"backend/pkg/permission/domain/entity"
"backend/pkg/permission/domain/permission"
"backend/pkg/permission/domain/repository"
"backend/pkg/permission/domain/usecase"
"context"
"fmt"
"sync"
"time"
"go.mongodb.org/mongo-driver/v2/bson"
)
type PermissionUseCaseParam struct {
PermRepo repository.PermissionRepository
RolePermRepo repository.RolePermissionRepository
RoleRepo repository.RoleRepository
UserRoleRepo repository.UserRoleRepository
}
type permissionUseCase struct {
PermissionUseCaseParam
// 權限樹快取 (in-memory)
treeMutex sync.RWMutex
tree *PermissionTree
}
// NewPermissionUseCase 建立權限 UseCase
func NewPermissionUseCase(param PermissionUseCaseParam) usecase.PermissionUseCase {
return &permissionUseCase{
PermissionUseCaseParam: param,
}
}
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, fmt.Errorf("no permissions found")
}
// 如果有多個根節點,包裝成一個虛擬根節點
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.FindByHTTP(ctx, path, method)
if err != nil {
return nil, err
}
return uc.toResponse(perm), nil
}
func (uc *permissionUseCase) ExpandPermissions(ctx context.Context, permissions permission.Permissions) (permission.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 {
// Convert ObjectID to int64 (timestamp-based)
permIDs[i] = perm.ID.Timestamp().Unix()
}
// 取得擁有這些權限的角色
rolePerms, err := uc.RolePermRepo.GetByPermissionIDs(ctx, permIDs)
if err != nil {
return nil, err
}
// 取得角色 ID
roleIDMap := make(map[int64]bool)
for _, rp := range rolePerms {
// Convert ObjectID to int64
roleID := rp.RoleID.Timestamp().Unix()
roleIDMap[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 {
roleID := role.ID.Timestamp().Unix()
if roleIDMap[roleID] {
roleUIDMap[roleID] = 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()
// 從資料庫建立
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()
return tree, nil
}
// InvalidateTreeCache 清除權限樹快取
func (uc *permissionUseCase) InvalidateTreeCache(ctx context.Context) error {
uc.treeMutex.Lock()
uc.tree = nil
uc.treeMutex.Unlock()
return nil
}
func (uc *permissionUseCase) toResponse(perm *entity.Permission) *usecase.PermissionResponse {
status := permission.Open
if !perm.State.IsActive() {
status = permission.Close
}
parentID := ""
if !perm.ParentID.IsZero() {
parentID = perm.ParentID.Hex()
}
return &usecase.PermissionResponse{
ID: perm.ID.Hex(),
ParentID: parentID,
Name: perm.Name,
HTTPPath: perm.HTTPPath,
HTTPMethod: perm.HTTPMethod,
Status: status,
Type: perm.Type,
}
}
// ConvertOIDToInt64 輔助函數:將 ObjectID 轉換為 int64
func ConvertOIDToInt64(oid bson.ObjectID) int64 {
if oid.IsZero() {
return 0
}
return oid.Timestamp().Unix()
}
// ConvertInt64ToOID 輔助函數:將 int64 轉換為 ObjectID (基於時間戳)
func ConvertInt64ToOID(id int64) bson.ObjectID {
if id == 0 {
return bson.ObjectID{}
}
return bson.NewObjectIDFromTimestamp(time.Unix(id, 0))
}