Files
EasyGoAdmin_GoFrame_EleVue/internal/logic/data_scope.go
T
2026-10-20 11:37:25 +08:00

89 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logic
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"easygoadmin/internal/consts"
"easygoadmin/internal/dao"
)
// ============================== 角色数据权限(行级过滤) ==============================
//
// 档位(role.data_scope,多角色在身份加载层取最宽松):
// 1-全部 租户隔离之内不过滤
// 2-本部门及以下 dept_id IN (自身部门 + 全部子孙部门)
// 3-本部门 dept_id = 自身部门
// 4-仅本人 id = 自身用户ID
//
// 生效范围:挂接本函数的模块其 Page/List/导出(applyFilters 链)自动生效;
// Detail/Update/Delete 不过滤,由 permission 节点与租户隔离兜底。
// 租户隔离、软删过滤由 dao.Ctx(hooks.ApplyCtx)先于本链注入,两层独立叠加。
// ApplyDataScope 数据权限过滤工厂
//
// 返回可挂到 ModuleSpec.DataScope 的过滤函数;deptColumn 为行上的部门列名,
// 无部门归属的行表(如内容模块)后续可按需提供按创建人过滤的变体。
func ApplyDataScope(deptColumn string) DataScopeFunc {
return func(ctx context.Context, m *gdb.Model, req any) *gdb.Model {
if dataScopeBypass(ctx) {
return m
}
scope := consts.GetDataScope(ctx)
if scope <= consts.DataScopeAll {
return m
}
uid := consts.GetUserID(ctx)
deptId := consts.GetDeptID(ctx)
switch {
case scope >= consts.DataScopeSelf || deptId <= 0:
// 仅本人;未分配部门的用户挂部门档时降级为仅本人(安全兜底)
return m.Where("id", uid)
case scope == consts.DataScopeDept:
return m.Where(deptColumn, deptId)
case scope == consts.DataScopeDeptAndBelow:
return m.WhereIn(deptColumn, deptSubtreeIds(ctx, deptId))
}
return m
}
}
// dataScopeBypass 数据范围豁免判断
// 平台级(超管)或持有超管角色编码时全量可见,与权限通配逻辑对齐
func dataScopeBypass(ctx context.Context) bool {
if consts.IsPlatform(ctx) {
return true
}
for _, r := range consts.GetRoles(ctx) {
if r == consts.RoleCodeSuperAdmin {
return true
}
}
return false
}
// deptSubtreeIds 收集部门自身及全部子孙部门ID
//
// 部门为 parent_id 树且体量小,直接全量拉取后 BFS;
// 查询经 dao.Ctx(租户隔离+软删),后续如成热点可加短时缓存
func deptSubtreeIds(ctx context.Context, deptId int) []int {
rows, err := dao.Dept.Ctx(ctx).Fields("id, parent_id").All()
if err != nil || len(rows) == 0 {
return []int{deptId}
}
children := make(map[int][]int, len(rows))
for _, r := range rows {
pid := r["parent_id"].Int()
children[pid] = append(children[pid], r["id"].Int())
}
ids := []int{deptId}
for queue := []int{deptId}; len(queue) > 0; queue = queue[1:] {
for _, cid := range children[queue[0]] {
ids = append(ids, cid)
queue = append(queue, cid)
}
}
return ids
}