239 lines
5.4 KiB
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))
|
|
}
|
|
|