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