dataperm

package
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 27, 2026 License: MIT Imports: 3 Imported by: 0

README

数据权限过滤器使用指南

快速开始

1. 导入包
import "leeforge-backend/pkg/dataperm"
2. 在 Service 中使用
方式一:使用全局方法(推荐)
// internal/modules/user/invitation_service.go

// 查询列表 - 自动过滤
func (s *InvitationService) List(ctx context.Context) ([]*ent.InvitationToken, error) {
    query := s.client.InvitationToken.Query()

    // 一行代码处理数据权限
    if needFilter, userID := dataperm.ShouldFilterByCreator(ctx); needFilter {
        query = query.Where(invitationtoken.CreatedByID(userID))
    }

    return query.All(ctx)
}

// 创建 - 不需要手动设置 created_by_id(由 Hook 自动处理)
func (s *InvitationService) Create(ctx context.Context, email string) (*ent.InvitationToken, error) {
    return s.client.InvitationToken.
        Create().
        SetToken(generateToken()).
        SetEmail(email).
        Save(ctx)  // created_by_id 由 DataPermissionHook 自动设置
}

// 更新 - 自动设置 updated_by_id
func (s *InvitationService) Update(ctx context.Context, id uuid.UUID, email string) (*ent.InvitationToken, error) {
    query := s.client.InvitationToken.UpdateOneID(id)

    // 非管理员只能更新自己创建的
    if needFilter, userID := dataperm.ShouldFilterByCreator(ctx); needFilter {
        query = query.Where(invitationtoken.CreatedByID(userID))
    }

    return query.
        SetEmail(email).
        Save(ctx)  // updated_by_id 自动设置
}

// 删除 - 自动设置 deleted_by_id
func (s *InvitationService) Delete(ctx context.Context, id uuid.UUID) error {
    query := s.client.InvitationToken.UpdateOneID(id)

    // 非管理员只能删除自己创建的
    if needFilter, userID := dataperm.ShouldFilterByCreator(ctx); needFilter {
        query = query.Where(invitationtoken.CreatedByID(userID))
    }

    return query.
        SetDeletedAt(time.Now()).
        Exec(ctx)  // deleted_by_id 自动设置
}

// 根据权限返回不同数据
func (s *InvitationService) GetStatistics(ctx context.Context) (*Statistics, error) {
    if dataperm.IsAdmin(ctx) {
        // 管理员看到所有统计
        return s.getAllStatistics(ctx)
    }

    // 普通用户只看到自己的
    userID := dataperm.GetCreatorID(ctx)
    return s.getUserStatistics(ctx, userID)
}
方式二:使用自定义过滤器实例
type InvitationService struct {
    client *ent.Client
    filter *dataperm.Filter  // 自定义过滤器
}

func NewInvitationService(client *ent.Client) *InvitationService {
    return &InvitationService{
        client: client,
        filter: dataperm.NewFilter().
            WithSuperAdminRoles([]string{"super_admin", "admin", "god"}).
            WithBypassRoles([]string{"system", "service"}),
    }
}

func (s *InvitationService) List(ctx context.Context) ([]*ent.InvitationToken, error) {
    query := s.client.InvitationToken.Query()

    // 使用自定义过滤器
    if needFilter, userID := s.filter.ShouldFilterByCreator(ctx); needFilter {
        query = query.Where(invitationtoken.CreatedByID(userID))
    }

    return query.All(ctx)
}

API 说明

全局方法
dataperm.ShouldFilterByCreator(ctx) (bool, uuid.UUID)

判断是否需要按创建者过滤数据。

  • 返回(needFilter bool, creatorID uuid.UUID)
  • 使用场景:查询、更新、删除时判断是否需要添加 created_by_id 条件
// 示例
if needFilter, userID := dataperm.ShouldFilterByCreator(ctx); needFilter {
    query = query.Where(entity.CreatedByID(userID))
}
dataperm.GetCreatorID(ctx) uuid.UUID

获取当前用户ID。

  • 返回uuid.UUID(如果没有身份信息返回 uuid.Nil
  • 使用场景:需要显式获取当前用户ID时
// 示例
userID := dataperm.GetCreatorID(ctx)
if userID != uuid.Nil {
    // 使用 userID
}
dataperm.IsAdmin(ctx) bool

判断当前用户是否是管理员。

  • 返回bool
  • 使用场景:根据权限返回不同数据或执行不同逻辑
// 示例
if dataperm.IsAdmin(ctx) {
    // 管理员逻辑
} else {
    // 普通用户逻辑
}
dataperm.HasRole(ctx, role string) bool

判断当前用户是否有指定角色。

  • 参数role string - 角色名称
  • 返回bool
  • 使用场景:细粒度的权限控制
// 示例
if dataperm.HasRole(ctx, "editor") {
    // 编辑者可以执行的操作
}

配置说明

默认配置
  • 超级管理员角色["super_admin", "admin"]
  • 绕过角色[](空)
自定义配置
filter := dataperm.NewFilter().
    WithSuperAdminRoles([]string{"super_admin", "admin", "god"}).
    WithBypassRoles([]string{"system", "service"})

权限规则

用户类型 查询 创建 更新 删除
超级管理员 所有数据 所有数据 所有数据
普通用户 自己创建的 自己创建的 自己创建的
系统调用(无身份) 所有数据 所有数据 所有数据

完整示例

package invitation

import (
    "context"
    "time"

    "github.com/google/uuid"

    "leeforge-backend/ent"
    "leeforge-backend/ent/invitationtoken"
    "leeforge-backend/pkg/dataperm"
)

type Service struct {
    client *ent.Client
}

func NewService(client *ent.Client) *Service {
    return &Service{client: client}
}

// List 查询列表
func (s *Service) List(ctx context.Context) ([]*ent.InvitationToken, error) {
    query := s.client.InvitationToken.Query()

    // 数据权限过滤
    if needFilter, userID := dataperm.ShouldFilterByCreator(ctx); needFilter {
        query = query.Where(invitationtoken.CreatedByID(userID))
    }

    return query.
        Order(ent.Desc(invitationtoken.FieldCreatedAt)).
        All(ctx)
}

// Get 查询单个
func (s *Service) Get(ctx context.Context, id uuid.UUID) (*ent.InvitationToken, error) {
    query := s.client.InvitationToken.Query().
        Where(invitationtoken.ID(id))

    // 数据权限过滤
    if needFilter, userID := dataperm.ShouldFilterByCreator(ctx); needFilter {
        query = query.Where(invitationtoken.CreatedByID(userID))
    }

    return query.Only(ctx)
}

// Create 创建
func (s *Service) Create(ctx context.Context, email string) (*ent.InvitationToken, error) {
    // created_by_id 由 DataPermissionHook 自动设置
    return s.client.InvitationToken.
        Create().
        SetToken(generateToken()).
        SetEmail(email).
        SetExpiresAt(time.Now().Add(7 * 24 * time.Hour)).
        Save(ctx)
}

// Update 更新
func (s *Service) Update(ctx context.Context, id uuid.UUID, email string) (*ent.InvitationToken, error) {
    query := s.client.InvitationToken.UpdateOneID(id)

    // 数据权限过滤
    if needFilter, userID := dataperm.ShouldFilterByCreator(ctx); needFilter {
        query = query.Where(invitationtoken.CreatedByID(userID))
    }

    // updated_by_id 由 DataPermissionHook 自动设置
    return query.
        SetEmail(email).
        Save(ctx)
}

// Delete 软删除
func (s *Service) Delete(ctx context.Context, id uuid.UUID) error {
    query := s.client.InvitationToken.UpdateOneID(id)

    // 数据权限过滤
    if needFilter, userID := dataperm.ShouldFilterByCreator(ctx); needFilter {
        query = query.Where(invitationtoken.CreatedByID(userID))
    }

    // deleted_by_id 由 DataPermissionHook 自动设置
    return query.
        SetDeletedAt(time.Now()).
        Exec(ctx)
}

// Statistics 统计数据
func (s *Service) Statistics(ctx context.Context) (int, error) {
    query := s.client.InvitationToken.Query()

    // 根据权限返回不同的统计
    if dataperm.IsAdmin(ctx) {
        // 管理员看到所有
        return query.Count(ctx)
    }

    // 普通用户只看到自己的
    userID := dataperm.GetCreatorID(ctx)
    return query.
        Where(invitationtoken.CreatedByID(userID)).
        Count(ctx)
}

func generateToken() string {
    // 生成随机 token
    return uuid.New().String()
}

注意事项

  1. Context 必须包含 Identity:通过认证中间件自动注入
  2. Hook 必须注册:在 Ent Client 初始化时注册 DataPermissionHook
  3. 手动设置优先:如果手动调用 SetCreatedByID(),会覆盖自动设置
  4. 系统调用不需要过滤:后台任务、定时任务等可以不包含 Identity
  5. 查询性能:添加 created_by_id 索引以提升查询性能

与 Hook 的关系

  • HookDataPermissionHook):自动设置审计字段(创建/更新/删除时)
  • Filterdataperm 包):判断是否需要过滤查询(查询时)

两者配合使用,实现完整的数据权限控制:

  • 写操作:Hook 自动设置 created_by_idupdated_by_iddeleted_by_id
  • 读操作:Filter 帮助判断是否需要过滤,Service 层根据结果添加 Where 条件

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetCreatorID

func GetCreatorID(ctx context.Context) uuid.UUID

GetCreatorID 全局方法:获取当前创建者ID

func HasRole

func HasRole(ctx context.Context, role string) bool

HasRole 全局方法:判断当前用户是否有指定角色

func IsAdmin

func IsAdmin(ctx context.Context) bool

IsAdmin 全局方法:判断当前用户是否是管理员

func ShouldFilterByCreator

func ShouldFilterByCreator(ctx context.Context) (bool, uuid.UUID)

ShouldFilterByCreator 全局方法:判断是否需要按创建者过滤

Types

type Filter

type Filter struct {
	// contains filtered or unexported fields
}

Filter 数据权限过滤器

func NewFilter

func NewFilter() *Filter

NewFilter 创建过滤器

func (*Filter) GetCreatorID

func (f *Filter) GetCreatorID(ctx context.Context) uuid.UUID

GetCreatorID 获取当前创建者ID(用于创建时设置) 如果没有身份信息,返回 uuid.Nil

func (*Filter) HasRole

func (f *Filter) HasRole(ctx context.Context, targetRole string) bool

HasRole 判断当前用户是否有指定角色

func (*Filter) IsAdmin

func (f *Filter) IsAdmin(ctx context.Context) bool

IsAdmin 判断当前用户是否是管理员

func (*Filter) ShouldFilterByCreator

func (f *Filter) ShouldFilterByCreator(ctx context.Context) (bool, uuid.UUID)

ShouldFilterByCreator 判断是否需要按创建者过滤 返回 (needFilter bool, creatorID uuid.UUID)

使用示例:

if needFilter, userID := filter.ShouldFilterByCreator(ctx); needFilter {
    query = query.Where(entity.CreatedByID(userID))
}

func (*Filter) WithBypassRoles

func (f *Filter) WithBypassRoles(roles []string) *Filter

WithBypassRoles 设置绕过角色

func (*Filter) WithSuperAdminRoles

func (f *Filter) WithSuperAdminRoles(roles []string) *Filter

WithSuperAdminRoles 设置超级管理员角色

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL