backend/pkg/permission/usecase/role_usecase.go

280 lines
6.7 KiB
Go

package usecase
import (
"backend/pkg/permission/domain"
"backend/pkg/permission/domain/entity"
"backend/pkg/permission/domain/permission"
"backend/pkg/permission/domain/repository"
"backend/pkg/permission/domain/usecase"
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/v2/bson"
)
type RoleUseCaseConfig struct {
AdminRoleUID string // 管理員角色 UID
UIDPrefix string // UID 前綴 (e.g., "ROLE")
UIDLength int // UID 長度 (不含前綴)
}
type RoleUseCaseParam struct {
RoleRepo repository.RoleRepository
UserRoleRepo repository.UserRoleRepository
RolePermUseCase usecase.RolePermissionUseCase
Config RoleUseCaseConfig
}
type roleUseCase struct {
RoleUseCaseParam
}
// NewRoleUseCase 建立角色 UseCase
func NewRoleUseCase(param RoleUseCaseParam) usecase.RoleUseCase {
// 設定預設值
if param.Config.UIDPrefix == "" {
param.Config.UIDPrefix = "ROLE"
}
if param.Config.UIDLength == 0 {
param.Config.UIDLength = 10
}
return &roleUseCase{
RoleUseCaseParam: param,
}
}
func (uc *roleUseCase) Create(ctx context.Context, req usecase.CreateRoleRequest) (*usecase.RoleResponse, error) {
// 生成 UID
nextID, err := uc.RoleRepo.NextID(ctx)
if err != nil {
return nil, fmt.Errorf("failed to generate role id: %w", err)
}
uid := fmt.Sprintf("%s%0*d", uc.Config.UIDPrefix, uc.Config.UIDLength, nextID)
// 建立角色
role := &entity.Role{
ID: bson.NewObjectID(),
UID: uid,
ClientID: req.ClientID,
Name: req.Name,
Status: domain.RecordActive,
}
role.CreateTime = time.Now().Unix()
role.UpdateTime = role.CreateTime
if err := uc.RoleRepo.Create(ctx, role); err != nil {
return nil, err
}
// 設定權限
if len(req.Permissions) > 0 && uc.RolePermUseCase != nil {
if err := uc.RolePermUseCase.UpdateRolePermissions(ctx, uid, req.Permissions); err != nil {
return nil, err
}
}
// 查詢完整角色資訊
return uc.Get(ctx, uid)
}
func (uc *roleUseCase) Update(ctx context.Context, uid string, req usecase.UpdateRoleRequest) (*usecase.RoleResponse, error) {
// 檢查角色是否存在
role, err := uc.RoleRepo.GetByUID(ctx, uid)
if err != nil {
return nil, err
}
// 更新欄位
if req.Name != nil {
role.Name = *req.Name
}
if req.Status != nil {
role.Status = *req.Status
}
role.UpdateTime = time.Now().Unix()
if err := uc.RoleRepo.Update(ctx, role); err != nil {
return nil, err
}
// 更新權限
if req.Permissions != nil && uc.RolePermUseCase != nil {
if err := uc.RolePermUseCase.UpdateRolePermissions(ctx, uid, req.Permissions); err != nil {
return nil, err
}
}
return uc.Get(ctx, uid)
}
func (uc *roleUseCase) Delete(ctx context.Context, uid string) error {
// 檢查角色是否存在
_, err := uc.RoleRepo.GetByUID(ctx, uid)
if err != nil {
return err
}
// 檢查是否有使用者使用此角色
users, err := uc.UserRoleRepo.GetByRoleID(ctx, uid)
if err != nil {
return err
}
if len(users) > 0 {
return fmt.Errorf("role has %d users, cannot delete", len(users))
}
// 刪除角色
return uc.RoleRepo.Delete(ctx, uid)
}
func (uc *roleUseCase) Get(ctx context.Context, uid string) (*usecase.RoleResponse, error) {
role, err := uc.RoleRepo.GetByUID(ctx, uid)
if err != nil {
return nil, err
}
// 取得權限
var permissions permission.Permissions
if uc.RolePermUseCase != nil {
permissions, _ = uc.RolePermUseCase.GetByRoleUID(ctx, uid)
}
if permissions == nil {
permissions = make(permission.Permissions)
}
return uc.toResponse(role, permissions), nil
}
func (uc *roleUseCase) List(ctx context.Context, filter usecase.RoleFilterRequest) ([]*usecase.RoleResponse, error) {
repoFilter := repository.RoleFilter{
ClientID: filter.ClientID,
Name: filter.Name,
Status: filter.Status,
}
roles, err := uc.RoleRepo.List(ctx, repoFilter)
if err != nil {
return nil, err
}
return uc.toResponseList(ctx, roles, filter.Permissions), nil
}
func (uc *roleUseCase) Page(ctx context.Context, filter usecase.RoleFilterRequest, page, size int) (*usecase.RolePageResponse, error) {
repoFilter := repository.RoleFilter{
ClientID: filter.ClientID,
Name: filter.Name,
Status: filter.Status,
}
roles, total, err := uc.RoleRepo.Page(ctx, repoFilter, page, size)
if err != nil {
return nil, err
}
// 取得所有角色的使用者數量 (批量查詢,避免 N+1)
roleUIDs := make([]string, len(roles))
for i, role := range roles {
roleUIDs[i] = role.UID
}
userCounts, err := uc.UserRoleRepo.CountByRoleID(ctx, roleUIDs)
if err != nil {
return nil, err
}
// 組裝回應
list := make([]*usecase.RoleWithUserCountResponse, 0, len(roles))
for _, role := range roles {
// 取得權限
var permissions permission.Permissions
if uc.RolePermUseCase != nil {
permissions, _ = uc.RolePermUseCase.GetByRoleUID(ctx, role.UID)
}
if permissions == nil {
permissions = make(permission.Permissions)
}
// 權限過濾 (如果有指定)
if len(filter.Permissions) > 0 {
hasPermission := false
for _, reqPerm := range filter.Permissions {
if permissions.HasPermission(reqPerm) {
hasPermission = true
break
}
}
if !hasPermission {
continue
}
}
resp := &usecase.RoleWithUserCountResponse{
RoleResponse: *uc.toResponse(role, permissions),
UserCount: userCounts[role.UID],
}
list = append(list, resp)
}
return &usecase.RolePageResponse{
List: list,
Total: total,
Page: page,
Size: size,
}, nil
}
func (uc *roleUseCase) toResponse(role *entity.Role, permissions permission.Permissions) *usecase.RoleResponse {
if permissions == nil {
permissions = make(permission.Permissions)
}
return &usecase.RoleResponse{
ID: role.ID.Hex(),
UID: role.UID,
ClientID: role.ClientID,
Name: role.Name,
Status: role.Status,
Permissions: permissions,
CreateTime: time.Unix(role.CreateTime, 0).UTC().Format(time.RFC3339),
UpdateTime: time.Unix(role.UpdateTime, 0).UTC().Format(time.RFC3339),
}
}
func (uc *roleUseCase) toResponseList(ctx context.Context, roles []*entity.Role, permFilter []string) []*usecase.RoleResponse {
result := make([]*usecase.RoleResponse, 0, len(roles))
for _, role := range roles {
var permissions permission.Permissions
if uc.RolePermUseCase != nil {
permissions, _ = uc.RolePermUseCase.GetByRoleUID(ctx, role.UID)
}
if permissions == nil {
permissions = make(permission.Permissions)
}
// 權限過濾
if len(permFilter) > 0 {
hasPermission := false
for _, reqPerm := range permFilter {
if permissions.HasPermission(reqPerm) {
hasPermission = true
break
}
}
if !hasPermission {
continue
}
}
result = append(result, uc.toResponse(role, permissions))
}
return result
}