chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)

This commit is contained in:
MiFaZhan
2026-11-02 13:27:59 +08:00
1250 changed files with 154111 additions and 0 deletions
@@ -0,0 +1,197 @@
// package xgorm
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package xgorm
import (
"context"
"fmt"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility"
"slices"
"reflect"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/text/gstr"
)
var (
descTags = []string{"description", "dc", "json"} // 实体描述标签
fieldTags = []string{"json"} // 实体字段名称映射
)
// GetFieldsToSlice 获取dao实例中的所有字段
func GetFieldsToSlice(ctx context.Context, dao daoInstance) ([]string, error) {
fields, err := dao.Ctx(ctx).TableFields(dao.Table())
if err != nil {
return nil, err
}
if len(fields) == 0 {
return nil, gerror.New("field not found")
}
var keys []string
for _, field := range fields {
keys = append(keys, field.Name)
}
return keys, nil
}
// GetPkField 获取dao实例中的主键名称
func GetPkField(ctx context.Context, dao daoInstance) (string, error) {
fields, err := dao.Ctx(ctx).TableFields(dao.Table())
if err != nil {
return "", err
}
if len(fields) == 0 {
return "", gerror.New("field not found")
}
for _, field := range fields {
if field.Key == "PRI" {
return field.Name, nil
}
}
return "", gerror.New("no primary key")
}
// IsUnique 是否唯一
func IsUnique(ctx context.Context, dao daoInstance, where g.Map, message string, pkId ...interface{}) error {
if len(where) == 0 {
return gerror.New("where condition cannot be empty")
}
m := dao.Ctx(ctx).Where(where)
if len(pkId) > 0 {
field, err := GetPkField(ctx, dao)
if err != nil {
return err
}
m = m.WhereNot(field, pkId[0])
}
count, err := m.Count(1)
if err != nil {
return err
}
if count > 0 {
if message == "" {
for k := range where {
message = fmt.Sprintf("in the table:%s, %v not uniqued", dao.Table(), where[k])
break
}
}
return gerror.New(message)
}
return nil
}
func JoinFields(ctx context.Context, entity interface{}, dao daoInstance, as string) (fs string) {
entityFs, err := GetEntityFieldTags(entity)
if err != nil {
g.Log().Errorf(ctx, "JoinFields err: %v", err)
return
}
if len(entityFs) == 0 {
g.Log().Errorf(ctx, "JoinFields entityFs is empty")
return ""
}
fields, err := dao.Ctx(ctx).TableFields(dao.Table())
if err != nil {
g.Log().Errorf(ctx, "JoinFields err: %v", err)
return
}
var columns []string
for _, v := range entityFs {
if !gstr.HasPrefix(v, as) {
g.Log().Infof(ctx, "JoinFields gstr.HasPrefix: %+v, %+v", v, as)
continue
}
g.Log().Infof(ctx, "JoinFields gstr.HasPrefix2: %+v, %+v", v, as)
field := gstr.CaseSnakeFirstUpper(gstr.StrEx(v, as))
if _, ok := fields[field]; ok {
columns = append(columns, fmt.Sprintf("`%s`.`%s` as `%s`", dao.Table(), field, v))
}
g.Log().Infof(ctx, "JoinFields field: %+v fields: %+v", field, fields)
}
if len(columns) > 0 {
g.Log().Infof(ctx, "JoinFields columns: %+v", columns)
return gstr.Implode(",", utility.UniqueSlice(columns))
}
return
}
// GetEntityFieldTags 获取实体中的字段名称
func GetEntityFieldTags(entity interface{}) (tags []string, err error) {
var formRef = reflect.TypeOf(entity)
for i := 0; i < formRef.NumField(); i++ {
field := formRef.Field(i)
if field.Type.Kind() == reflect.Struct {
addTags, err := reflectTag(field.Type, fieldTags, []string{})
if err != nil {
return nil, err
}
tags = append(tags, addTags...)
continue
}
tags = append(tags, reflectTagName(field, fieldTags, true))
}
return
}
// reflectTag 层级递增解析tag
func reflectTag(reflectType reflect.Type, filterTags []string, tags []string) ([]string, error) {
if reflectType.Kind() == reflect.Ptr {
return nil, gerror.Newf("reflect type do not support reflect.Ptr yet, reflectType:%+v", reflectType)
}
if reflectType.Kind() != reflect.Struct {
return nil, nil
}
for i := 0; i < reflectType.NumField(); i++ {
tag := reflectTagName(reflectType.Field(i), filterTags, false)
if tag == "" {
addTags, err := reflectTag(reflectType.Field(i).Type, filterTags, tags)
if err != nil {
return nil, err
}
tags = append(tags, addTags...)
continue
}
tags = append(tags, tag)
}
return tags, nil
}
// reflectTagName 解析实体中的描述标签,优先级:description > dc > json > Name
func reflectTagName(field reflect.StructField, filterTags []string, isDef bool) string {
if slices.Contains(filterTags, "description") {
if description, ok := field.Tag.Lookup("description"); ok && description != "" {
return description
}
}
if slices.Contains(filterTags, "dc") {
if dc, ok := field.Tag.Lookup("dc"); ok && dc != "" {
return dc
}
}
if slices.Contains(filterTags, "json") {
if jsonName, ok := field.Tag.Lookup("json"); ok && jsonName != "" {
return jsonName
}
}
if !isDef {
return ""
}
return field.Name
}
@@ -0,0 +1,195 @@
// package xgorm
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package xgorm
import (
"context"
gormmodel "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/gorm_model"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/tree"
"slices"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
const (
idDefaulField = "id"
pidDefaulField = "pid"
levelDefaulField = "level"
treeDefaulField = "tree"
)
type TreeFiledOption struct {
IdField string
PidField string
LevelField string
TreeField string
}
type daoInstance interface {
Table() string
Ctx(ctx context.Context) *gdb.Model
}
func CustomTreeFiledToDefault(old map[string]interface{}, option *TreeFiledOption) *gormmodel.DefaultTree {
if option == nil {
option = &TreeFiledOption{
IdField: idDefaulField,
PidField: pidDefaulField,
LevelField: levelDefaulField,
TreeField: treeDefaulField,
}
}
pd := &gormmodel.DefaultTree{}
if id, ok := old[option.IdField]; ok {
pd.Id = gconv.Int64(id)
}
if pid, ok := old[option.PidField]; ok {
pd.Pid = gconv.Int64(pid)
}
if level, ok := old[option.LevelField]; ok {
pd.Level = gconv.Int(level)
}
if tree, ok := old[option.TreeField]; ok {
pd.Tree = gconv.String(tree)
}
return pd
}
// AutoUpdateTree 自动更新关系树
func AutoUpdateTree(ctx context.Context, dao daoInstance, id, pid int64, option *TreeFiledOption) (newPid int64, newLevel int, newTree string, err error) {
if option == nil {
option = &TreeFiledOption{
IdField: idDefaulField,
PidField: pidDefaulField,
LevelField: levelDefaulField,
TreeField: treeDefaulField,
}
}
if err = CheckTreeTable(ctx, dao, option); err != nil {
return
}
if pid <= 0 {
newPid = 0
newLevel = 1
newTree = ""
} else {
var pdMap map[string]interface{}
row, err := dao.Ctx(ctx).Fields(option.IdField, option.PidField, option.LevelField, option.TreeField).WherePri(pid).One()
if err != nil {
return 0, 0, "", err
}
if row != nil {
pdMap = row.Map()
}
if len(pdMap) == 0 {
return 0, 0, "", gerror.New("未查询到树表上级信息,请检查!")
}
pd := CustomTreeFiledToDefault(pdMap, option)
if id > 0 && slices.Contains(tree.GetIds(pd.Tree), id) {
return 0, 0, "", gerror.New("上级不能设为自己当前的子级!")
}
newPid = pid
newLevel = pd.Level + 1
newTree = tree.GenLabel(pd.Tree, pid)
}
if id > 0 {
if pid == id {
return 0, 0, "", gerror.New("上级不能是自己!")
}
var models *gormmodel.DefaultTree
if err = dao.Ctx(ctx).WherePri(id).Scan(&models); err != nil {
return 0, 0, "", err
}
if models == nil {
return 0, 0, "", gerror.New("树表信息不存在,请检查!")
}
// g.Log().Infof(ctx, "models: %+v, pid: %v, newPid: %v, newLevel: %v, newTree: %v", models, pid, newPid, newLevel, newTree)
// 上级发生变化时,遍历修改其所有的下级关系树
if models.Pid != pid {
if err = updateChildrenTree(ctx, dao, models.Id, newLevel, newTree, option); err != nil {
return
}
}
}
return
}
// CheckTreeTable 检查树表
func CheckTreeTable(ctx context.Context, dao daoInstance, option *TreeFiledOption) (err error) {
fields, err := GetFieldsToSlice(ctx, dao)
if err != nil {
return err
}
if !slices.Contains(fields, option.PidField) {
return gerror.New("树表必须包含`pid`字段")
}
if !slices.Contains(fields, option.LevelField) {
return gerror.New("树表必须包含`level`字段")
}
if !slices.Contains(fields, option.TreeField) {
return gerror.New("树表必须包含`tree`字段")
}
return
}
// updateChildrenTree 更新下级关系树
func updateChildrenTree(ctx context.Context, dao daoInstance, pid int64, pLevel int, pTree string, option *TreeFiledOption) (err error) {
var list []*gormmodel.DefaultTree
// var pdMaps []map[string]interface{}
pdMaps, err := dao.Ctx(ctx).Fields(option.IdField, option.PidField, option.LevelField, option.TreeField).Where(option.PidField, pid).All()
if err != nil {
return err
}
for _, v := range pdMaps {
if v == nil {
continue
}
pb := CustomTreeFiledToDefault(v.Map(), option)
if pb.Id == 0 {
g.Log().Warningf(ctx, "updateChildrenTree pb.Id is 0, pb: %+v, option: %+v, v: %+v, pdMaps: %+v", pb, option, v, pdMaps)
continue
}
// g.Log().Debugf(ctx, "updateChildrenTree pb: %+v", pb)
list = append(list, pb)
}
if len(list) == 0 {
return
}
newLevel := pLevel + 1
newTree := tree.GenLabel(pTree, pid)
var updateIds []int64
for _, v := range list {
updateIds = append(updateIds, v.Id)
if err = updateChildrenTree(ctx, dao, v.Id, newLevel, newTree, option); err != nil {
return
}
}
if len(updateIds) > 0 {
update := g.Map{
option.LevelField: newLevel,
option.TreeField: newTree,
}
_, err = dao.Ctx(ctx).WhereIn(option.IdField, updateIds).Data(update).Update()
}
return
}
@@ -0,0 +1,14 @@
// package gormmodel
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package gormmodel
// DefaultTree 默认树表字段
type DefaultTree struct {
Id int64 `json:"id" description:"ID"`
Pid int64 `json:"pid" description:"父ID"`
Level int `json:"level" description:"关系树等级"`
Tree string `json:"tree" description:"关系树"`
}
@@ -0,0 +1,231 @@
// package handler
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package handler
import (
"context"
"fmt"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
// FilterAuth 过滤数据权限
// 通过上下文中的用户角色权限和表中是否含有需要过滤的字段附加查询条件
func FilterAuth(m *gdb.Model) *gdb.Model {
var (
ctx = m.GetCtx()
fields = convert.EscapeFieldsToSlice(m.GetFieldsStr())
)
// 超管拥有全部权限
if contexts.IsSuperAdmin(ctx) {
return m
}
// 获取用户信息
user := contexts.GetUser(ctx)
if user == nil {
return m
}
if len(user.Roles) == 0 {
return m.Where("1 = 0")
}
deptIds := make([]int64, 0)
userIds := make([]int64, 0)
// 是否进入了非「全部数据」的受限范围;解析结果为空时必须拒绝,不能漏过滤
hasRestrictedScope := false
currentRoleId := ""
currentRoleDataScope := ""
currentRoleIdIf := contexts.GetDataValue(ctx, "currentRoleId")
if currentRoleIdIf != nil {
currentRoleId = currentRoleIdIf.(string)
}
currentRoleDataScopeIf := contexts.GetDataValue(ctx, "currentRoleDataScope")
if currentRoleDataScopeIf != nil {
currentRoleDataScope = currentRoleDataScopeIf.(string)
}
if len(currentRoleDataScope) > 0 && len(currentRoleId) > 0 {
dataScope := currentRoleDataScope
roleId := gconv.Int64(currentRoleId)
g.Log().Infof(ctx, "uid:%d, currentRoleId: %d, currentRoleDataScope: %s", user.ID, roleId, dataScope)
if dataScope == consts.SysRoleDataScopeAll { // 角色数据范围: 1全部数据权限
return m
}
hasRestrictedScope = true
if dataScope == consts.SysRoleDataScopeCustom { // 角色数据范围: 2自定数据权限
var roleDepts []*entity.SysRoleDept
err := g.Model("sys_role_dept").Where("tenant_id", contexts.GetTenantId(ctx)).Where("role_id", roleId).Scan(&roleDepts)
if err != nil {
g.Log().Panicf(ctx, "failed to get role dept data err:%+v", err)
}
for _, roleDept := range roleDepts {
deptIds = append(deptIds, roleDept.DeptId)
}
} else if dataScope == consts.SysRoleDataScopeDept { // 角色数据范围: 3本部门数据权限
deptIds = append(deptIds, user.DeptId)
} else if dataScope == consts.SysRoleDataScopeDeptAndBelow { // 角色数据范围: 4本部门及以下数据权限
deptIds = append(deptIds, GetDeptAndSub(ctx, user.DeptId)...)
} else if dataScope == consts.SysRoleDataScopePersonal { // 角色数据范围: 5仅本人数据权限
userIds = append(userIds, user.ID)
} else if dataScope == consts.SysRoleDataScopeDeptAndBelowOrPersonal { // 角色数据范围: 6本部门及以下或本人数据权限
deptIds = append(deptIds, GetDeptAndSub(ctx, user.DeptId)...)
userIds = append(userIds, user.ID)
} else if dataScope == consts.SysRoleDataScopeOrgAndNextLevel { // 角色数据范围: 7本组织及本组织下一级数据权限
deptIds = append(deptIds, GetOrgAndNextLevelDeptIds(ctx, user.DeptId)...)
} else if dataScope == consts.SysRoleDataScopeOrgNextLevel { // 角色数据范围: 8本组织下一级数据权限
deptIds = append(deptIds, GetOrgNextLevelDeptIds(ctx, user.DeptId)...)
}
} else {
for _, role := range user.Roles {
dataScope := role.DataScope
roleId := role.RoleId
if dataScope == consts.SysRoleDataScopeAll { // 角色数据范围: 1全部数据权限
return m
} else if dataScope == consts.SysRoleDataScopeCustom { // 角色数据范围: 2自定数据权限
hasRestrictedScope = true
var roleDepts []*entity.SysRoleDept
err := g.Model("sys_role_dept").Where("tenant_id", contexts.GetTenantId(ctx)).Where("role_id", roleId).Scan(&roleDepts)
if err != nil {
g.Log().Panicf(ctx, "failed to get role dept data err:%+v", err)
}
for _, roleDept := range roleDepts {
deptIds = append(deptIds, roleDept.DeptId)
}
} else if dataScope == consts.SysRoleDataScopeDept { // 角色数据范围: 3本部门数据权限
hasRestrictedScope = true
deptIds = append(deptIds, user.DeptId)
} else if dataScope == consts.SysRoleDataScopeDeptAndBelow { // 角色数据范围: 4本部门及以下数据权限
hasRestrictedScope = true
deptIds = append(deptIds, GetDeptAndSub(ctx, user.DeptId)...)
} else if dataScope == consts.SysRoleDataScopePersonal { // 角色数据范围: 5仅本人数据权限
hasRestrictedScope = true
userIds = append(userIds, user.ID)
} else if dataScope == consts.SysRoleDataScopeDeptAndBelowOrPersonal { // 角色数据范围: 6本部门及以下或本人数据权限
hasRestrictedScope = true
deptIds = append(deptIds, GetDeptAndSub(ctx, user.DeptId)...)
userIds = append(userIds, user.ID)
} else if dataScope == consts.SysRoleDataScopeOrgAndNextLevel { // 角色数据范围: 7本组织及本组织下一级数据权限
hasRestrictedScope = true
deptIds = append(deptIds, GetOrgAndNextLevelDeptIds(ctx, user.DeptId)...)
} else if dataScope == consts.SysRoleDataScopeOrgNextLevel { // 角色数据范围: 8本组织下一级数据权限
hasRestrictedScope = true
deptIds = append(deptIds, GetOrgNextLevelDeptIds(ctx, user.DeptId)...)
}
}
}
deptFilterField := ""
if gstr.InArray(fields, "dept_id") {
deptFilterField = "dept_id"
} else if gstr.InArray(fields, "created_dept") {
deptFilterField = "created_dept"
}
userFilterField := ""
if gstr.InArray(fields, "user_id") {
userFilterField = "user_id"
} else if gstr.InArray(fields, "created_by") {
userFilterField = "created_by"
}
if len(deptIds) > 0 && len(userIds) > 0 {
if deptFilterField != "" && userFilterField != "" {
m = m.Where(deptFilterField+" IN (?) OR "+userFilterField+" IN (?)", deptIds, userIds)
} else if deptFilterField != "" {
m = m.WhereIn(deptFilterField, deptIds)
} else if userFilterField != "" {
m = m.WhereIn(userFilterField, userIds)
}
} else if len(deptIds) > 0 && deptFilterField != "" {
m = m.WhereIn(deptFilterField, deptIds)
} else if len(userIds) > 0 && userFilterField != "" {
m = m.WhereIn(userFilterField, userIds)
} else if hasRestrictedScope && (deptFilterField != "" || userFilterField != "") {
// 例:范围8无下级部门、范围2未配自定义部门 → 可见集合为空,禁止放行全表
g.Log().Warningf(ctx, "FilterAuth empty scope result, deny all data. uid:%d, deptIds:%v, userIds:%v", user.ID, deptIds, userIds)
m = m.Where("1 = 0")
}
return m
}
// GetParentCompanyDeptId 获取用户所属上级公司组织ID
func GetParentCompanyDeptId(ctx context.Context, deptId int64) int64 {
if deptId <= 0 {
return deptId
}
var dept entity.SysDept
err := g.Model("sys_dept").Where("dept_id", deptId).Scan(&dept)
if err != nil || dept.DeptId == 0 {
return deptId
}
if dept.ParentId == 0 || dept.DeptType == 1 {
return dept.DeptId
}
return GetParentCompanyDeptId(ctx, dept.ParentId)
}
// GetDeptDirectSub 获取指定部门的直接下级部门ID
func GetDeptDirectSub(ctx context.Context, deptId int64) (ids []int64) {
array, err := g.Model("sys_dept").
Where("parent_id", deptId).
Fields("dept_id").
Array()
if err != nil {
g.Log().Panicf(ctx, "GetDeptDirectSub err:%+v", err)
return
}
for _, v := range array {
ids = append(ids, v.Int64())
}
return
}
// GetOrgAndNextLevelDeptIds 获取本组织及本组织下一级数据权限范围:所属公司组织及直接下级
func GetOrgAndNextLevelDeptIds(ctx context.Context, deptId int64) (ids []int64) {
orgDeptId := GetParentCompanyDeptId(ctx, deptId)
ids = append(ids, orgDeptId)
ids = append(ids, GetDeptDirectSub(ctx, orgDeptId)...)
return
}
// GetOrgNextLevelDeptIds 获取本组织下一级数据权限范围:所属公司组织的直接下级
func GetOrgNextLevelDeptIds(ctx context.Context, deptId int64) (ids []int64) {
orgDeptId := GetParentCompanyDeptId(ctx, deptId)
return GetDeptDirectSub(ctx, orgDeptId)
}
// GetDeptAndSub 获取指定部门的所有下级,含本部门
//
// ancestors 的平台约定格式以逗号结尾(如 "0,1,172,"),但历史数据里存在
// 由代理建企业路径写入的缺尾逗号形态(如 "0,1,172")。两种都要匹配,
// 否则这些机构的下级会在「本机构及以下」数据权限展开时被整体漏掉,
// 表现为上级机构看不到下级(例:一级代理选不到划拨目标客户)。
func GetDeptAndSub(ctx context.Context, deptId int64) (ids []int64) {
// likeTail = "%,172"(机构在祖先链末尾)、likeInner = "%,172,%"(机构在祖先链中间)
likeTail := fmt.Sprintf("%%,%d", deptId)
array, err := g.Model("sys_dept").
Where("(ancestors LIKE ? OR ancestors LIKE ?)", likeTail+",%", likeTail).
Fields("dept_id").
Array()
if err != nil {
g.Log().Panicf(ctx, "GetDeptAndSub err:%+v", err)
return
}
for _, v := range array {
ids = append(ids, v.Int64())
}
ids = append(ids, deptId)
return
}
@@ -0,0 +1,13 @@
// package handler
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package handler
import "github.com/gogf/gf/v2/database/gdb"
// ForceCache 强制缓存
func ForceCache(m *gdb.Model) *gdb.Model {
return m.Cache(gdb.CacheOption{Duration: 0, Force: true})
}
@@ -0,0 +1,40 @@
// package handler
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package handler
import "github.com/gogf/gf/v2/database/gdb"
// Option 预处理选项
type Option struct {
FilterAuth bool // 过滤权限
ForceCache bool // 强制缓存
FilterTenant bool // 过滤多租户权限
}
// DefaultOption 默认预处理选项
var DefaultOption = &Option{
FilterAuth: true,
FilterTenant: true,
}
func Model(m *gdb.Model, opt ...*Option) *gdb.Model {
var option *Option
if len(opt) > 0 {
option = opt[0]
} else {
option = DefaultOption
}
if option.FilterAuth {
m = m.Handler(FilterAuth)
}
if option.ForceCache {
m = m.Handler(ForceCache)
}
if option.FilterTenant {
m = m.Handler(FilterTenant)
}
return m
}
@@ -0,0 +1,27 @@
// package handler
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package handler
import (
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/text/gstr"
)
// FilterTenant 过滤多租户数据权限
func FilterTenant(m *gdb.Model) *gdb.Model {
var (
ctx = m.GetCtx()
fields = convert.EscapeFieldsToSlice(m.GetFieldsStr())
)
// 如果包含tenant_id字段,则过滤多租户数据权限
if gstr.InArray(fields, "tenant_id") {
m = m.Where("tenant_id", contexts.GetTenantId(ctx))
}
return m
}
@@ -0,0 +1,118 @@
// package hook
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package hook
import (
"context"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
type MemberSumma struct {
UserId int64 `json:"userId" orm:"user_id" description:"用户ID"`
UserName string `json:"userName" orm:"user_name" description:"用户账号"`
NickName string `json:"nickName" orm:"nick_name" description:"用户昵称"`
Avatar string `json:"avatar" orm:"avatar" description:"头像地址"`
TenantId string `json:"tenantId" orm:"tenant_id" description:"租户编号"`
DeptId int64 `json:"deptId" orm:"dept_id" description:"部门ID"`
}
// MemberSummary 操作人摘要信息
var MemberSummary = gdb.HookHandler{
Select: func(ctx context.Context, in *gdb.HookSelectInput) (result gdb.Result, err error) {
result, err = in.Next(ctx)
if err != nil {
return
}
var (
createdByIds []int64
updatedByIds []int64
deletedByIds []int64
memberIds []int64
)
for _, record := range result {
if record["created_by"].Int64() > 0 {
createdByIds = append(createdByIds, record["created_by"].Int64())
}
if record["updated_by"].Int64() > 0 {
updatedByIds = append(updatedByIds, record["updated_by"].Int64())
}
if record["deleted_by"].Int64() > 0 {
deletedByIds = append(deletedByIds, record["deleted_by"].Int64())
}
if record["user_id"].Int64() > 0 {
memberIds = append(memberIds, record["user_id"].Int64())
}
}
memberIds = append(memberIds, createdByIds...)
memberIds = append(memberIds, updatedByIds...)
memberIds = append(memberIds, deletedByIds...)
memberIds = utility.UniqueSlice(memberIds)
if len(memberIds) == 0 {
return
}
var members []*MemberSumma
if err = g.Model("sys_user").Ctx(ctx).WhereIn("user_id", memberIds).Scan(&members); err != nil {
g.Log().Errorf(ctx, "[MemberSummary] 查询用户信息失败: %v", err)
return nil, err
}
if len(members) == 0 {
g.Log().Warningf(ctx, "[MemberSummary] 没有找到任何用户信息,用户IDs: %v", memberIds)
return
}
findMember := func(id *gvar.Var) *MemberSumma {
userId := id.Int64()
for _, v := range members {
if v.UserId == userId {
return v
}
}
// 只在找不到用户时记录警告(避免日志过多)
g.Log().Warningf(ctx, "[MemberSummary] 未找到用户ID: %d", userId)
return nil
}
for _, record := range result {
if record["created_by"].Int64() > 0 {
member := findMember(record["created_by"])
if member != nil {
record["createdBySumma"] = gvar.New(member)
}
}
if record["updated_by"].Int64() > 0 {
member := findMember(record["updated_by"])
if member != nil {
record["updatedBySumma"] = gvar.New(member)
}
}
if record["deleted_by"].Int64() > 0 {
member := findMember(record["deleted_by"])
if member != nil {
record["deletedBySumma"] = gvar.New(member)
}
}
if record["user_id"].Int64() > 0 {
member := findMember(record["user_id"])
if member != nil {
record["memberBySumma"] = gvar.New(member)
}
}
}
return
},
}