635 lines
20 KiB
Go
635 lines
20 KiB
Go
// package system
|
|
// @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 system
|
|
|
|
import (
|
|
"context"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/event"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/mcache"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rolepolicy"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rolesync"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/do"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility"
|
|
"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/os/gtime"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
type sSysRole struct {
|
|
}
|
|
|
|
func NewSysRole() *sSysRole {
|
|
return &sSysRole{}
|
|
}
|
|
|
|
func init() {
|
|
service.RegisterSysRole(NewSysRole())
|
|
}
|
|
|
|
func (l *sSysRole) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
|
if len(option) == 0 {
|
|
option = append(option, &handler.Option{
|
|
FilterTenant: true,
|
|
FilterAuth: false, // 角色表不验证数据权限
|
|
})
|
|
}
|
|
return handler.Model(dao.SysRole.Ctx(ctx).Master(), option...)
|
|
}
|
|
|
|
func (l *sSysRole) ModelQuery(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
|
if len(option) == 0 {
|
|
option = append(option, &handler.Option{
|
|
FilterTenant: true,
|
|
FilterAuth: false, // 角色表不验证数据权限
|
|
})
|
|
}
|
|
return handler.Model(dao.SysRole.Ctx(ctx).Master(), option...)
|
|
}
|
|
|
|
// 获取租户下角色列表
|
|
func (s *sSysRole) List(ctx context.Context, param *model.SysRoleListParam) (res []*model.SysRoleListModel, total int, err error) {
|
|
db := s.ModelQuery(ctx)
|
|
if param.RoleName != "" {
|
|
db = db.WhereLike(dao.SysRole.Columns().RoleName, "%"+param.RoleName+"%")
|
|
}
|
|
if param.Status != "" {
|
|
db = db.Where(dao.SysRole.Columns().Status, param.Status)
|
|
}
|
|
if param.DeptId != nil {
|
|
db = db.Where(dao.SysRole.Columns().DeptId, *param.DeptId)
|
|
}
|
|
if len(param.CreatedAt) == 2 {
|
|
start, end := gtime.NewFromStr(param.CreatedAt[0]), gtime.NewFromStr(param.CreatedAt[1])
|
|
db = db.WhereBetween(dao.SysRole.Columns().CreatedAt, start, end.EndOfDay())
|
|
}
|
|
if len(param.RoleIds) > 0 {
|
|
db = db.WhereIn(dao.SysRole.Columns().RoleId, param.RoleIds)
|
|
}
|
|
total, err = db.Count()
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if total == 0 {
|
|
return nil, 0, nil
|
|
}
|
|
|
|
companyMap, err := service.SysDept().GetDeptCompanyMap(ctx)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
g.Log().Debugf(ctx, "sSysRole.List companyMap: %v", companyMap)
|
|
err = db.Page(param.Page, param.PageSize).Scan(&res)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
for _, role := range res {
|
|
role.DeptName = companyMap[role.DeptId]
|
|
}
|
|
return
|
|
}
|
|
|
|
// 获取角色详情
|
|
func (s *sSysRole) View(ctx context.Context, param *model.SysRoleViewParam) (res *model.SysRoleViewModel, err error) {
|
|
err = s.ModelQuery(ctx).Where(dao.SysRole.Columns().RoleId, param.RoleId).Scan(&res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if res == nil {
|
|
return nil, gerror.New("角色不存在或已删除")
|
|
}
|
|
template, err := rolepolicy.TemplateForRoles(ctx, res.RoleId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res.IsTemplateRole = template != nil
|
|
defaultRoleCount, err := dao.SysDept.Ctx(ctx).Master().Where("tenant_id", res.TenantId).Where("dept_id", res.DeptId).Where("dept_role", res.RoleId).Count()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res.IsDeptDefaultRole = defaultRoleCount > 0
|
|
// 获取角色菜单
|
|
roleMenus, err := s.getRoleMenuRelations(ctx, param.RoleId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
menuIdList := make([]int64, 0)
|
|
roleMenuDataScopes := make(map[int64]string)
|
|
for _, menu := range roleMenus {
|
|
menuIdList = append(menuIdList, menu.MenuId)
|
|
roleMenuDataScopes[menu.MenuId] = menu.DataScope
|
|
}
|
|
res.MenuIds = menuIdList
|
|
res.RoleMenuDataScopes = roleMenuDataScopes
|
|
// 获取角色部门
|
|
roleDepts, err := s.GetRoleDept(ctx, param.RoleId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deptIdList := make([]int64, 0)
|
|
for _, dept := range roleDepts {
|
|
deptIdList = append(deptIdList, dept.DeptId)
|
|
}
|
|
res.DeptIds = deptIdList
|
|
return
|
|
}
|
|
|
|
func (s *sSysRole) getRoleMenuRelations(ctx context.Context, id int64) (res []*entity.SysRoleMenu, err error) {
|
|
err = dao.SysRoleMenu.Ctx(ctx).Master().
|
|
Where(dao.SysRoleMenu.Columns().TenantId, contexts.GetTenantId(ctx)).
|
|
Where(dao.SysRoleMenu.Columns().RoleId, id).Scan(&res)
|
|
return
|
|
}
|
|
|
|
// 获取角色菜单
|
|
func (s *sSysRole) GetRoleMenu(ctx context.Context, id int64) (res []*entity.SysMenu, err error) {
|
|
err = dao.SysRoleMenu.Ctx(ctx).Master().
|
|
Where(dao.SysRoleMenu.Columns().TenantId, contexts.GetTenantId(ctx)).
|
|
Where(dao.SysRoleMenu.Columns().RoleId, id).Scan(&res)
|
|
return
|
|
}
|
|
|
|
// 获取角色部门
|
|
func (s *sSysRole) GetRoleDept(ctx context.Context, id int64) (res []*entity.SysDept, err error) {
|
|
err = dao.SysRoleDept.Ctx(ctx).Master().
|
|
Where(dao.SysRoleDept.Columns().TenantId, contexts.GetTenantId(ctx)).
|
|
Where(dao.SysRoleDept.Columns().RoleId, id).Scan(&res)
|
|
return
|
|
}
|
|
|
|
// 获取角色列表对应菜单
|
|
func (s *sSysRole) GetRoleListMenu(ctx context.Context, ids []int64) (res []*entity.SysRoleMenu, err error) {
|
|
res = make([]*entity.SysRoleMenu, 0)
|
|
if slices.Contains(ids, consts.SuperAdminRoleId) {
|
|
menus, _, err := service.SysMenu().List(ctx, &model.SysMenuListParam{
|
|
Status: consts.SysMenuStatusNormal,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, menu := range menus {
|
|
res = append(res, &entity.SysRoleMenu{
|
|
TenantId: contexts.GetTenantId(ctx),
|
|
RoleId: consts.SuperAdminRoleId,
|
|
MenuId: menu.MenuId,
|
|
})
|
|
}
|
|
return res, nil
|
|
}
|
|
err = dao.SysRoleMenu.Ctx(ctx).Master().
|
|
Where(dao.SysRoleMenu.Columns().TenantId, contexts.GetTenantId(ctx)).
|
|
WhereIn(dao.SysRoleMenu.Columns().RoleId, ids).Scan(&res)
|
|
return
|
|
}
|
|
|
|
// 新增角色
|
|
func (s *sSysRole) Add(ctx context.Context, param *model.SysRoleAddParam) (role *model.SysRoleAddModel, err error) {
|
|
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
var saveErr error
|
|
role, saveErr = s.addRoleAtomic(ctx, param)
|
|
if saveErr != nil {
|
|
return saveErr
|
|
}
|
|
return nil
|
|
})
|
|
if err == nil && role != nil {
|
|
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysRoleCreate, role.RoleId)
|
|
err = rolesync.EnqueueRole(ctx, role.RoleId, contexts.GetUserId(ctx))
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *sSysRole) addRoleAtomic(ctx context.Context, param *model.SysRoleAddParam) (role *model.SysRoleAddModel, err error) {
|
|
user := contexts.GetUser(ctx)
|
|
if user == nil {
|
|
return nil, gerror.New("登录已过期")
|
|
}
|
|
data := do.SysRole{}
|
|
gconv.Struct(param, &data)
|
|
if data.DataScope == nil || data.DataScope == "" {
|
|
data.DataScope = consts.SysRoleDataScopePersonal
|
|
}
|
|
data.CreatedBy = user.ID
|
|
data.CreatedAt = gtime.Now()
|
|
data.CreatedDept = user.DeptId
|
|
data.TenantId = user.TenantId
|
|
data.UpdatedBy = user.ID
|
|
data.UpdatedAt = gtime.Now()
|
|
|
|
roleId, err := s.Model(ctx).Data(data).OmitNil().InsertAndGetId()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
role = &model.SysRoleAddModel{
|
|
RoleId: roleId,
|
|
}
|
|
// 新增角色菜单
|
|
err = s.RoleMenu(ctx, roleId, param.MenuIds, param.RoleMenuDataScopes)
|
|
if err != nil {
|
|
return role, err
|
|
}
|
|
return
|
|
}
|
|
|
|
// 编辑角色
|
|
func (s *sSysRole) Edit(ctx context.Context, param *model.SysRoleEditParam) (role *model.SysRoleEditModel, err error) {
|
|
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
if _, err := tx.Ctx(ctx).Model(dao.SysRole.Table()).Where("role_id", param.RoleId).Where("tenant_id", contexts.GetTenantId(ctx)).Data(do.SysRole{UpdatedBy: contexts.GetUserId(ctx)}).Update(); err != nil {
|
|
return err
|
|
}
|
|
var saveErr error
|
|
role, saveErr = s.editRoleAtomic(ctx, param)
|
|
if saveErr != nil {
|
|
return saveErr
|
|
}
|
|
return nil
|
|
})
|
|
if err == nil {
|
|
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysRoleUpdate, param.RoleId)
|
|
err = rolesync.EnqueueRole(ctx, param.RoleId, contexts.GetUserId(ctx))
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *sSysRole) editRoleAtomic(ctx context.Context, param *model.SysRoleEditParam) (role *model.SysRoleEditModel, err error) {
|
|
role = &model.SysRoleEditModel{
|
|
RoleId: param.RoleId,
|
|
}
|
|
if err = s.ensureRolesNotInherited(ctx, []int64{param.RoleId}); err != nil {
|
|
return role, err
|
|
}
|
|
user := contexts.GetUser(ctx)
|
|
if user == nil {
|
|
return role, gerror.New("登录已过期")
|
|
}
|
|
template, templateErr := rolepolicy.TemplateForRoles(ctx, param.RoleId)
|
|
if templateErr != nil {
|
|
return role, templateErr
|
|
}
|
|
if template != nil && template.RoleId == param.RoleId {
|
|
if !contexts.IsSuperAdmin(ctx) {
|
|
return role, gerror.New("仅平台超级管理员可修改代理模板")
|
|
}
|
|
if param.Status != nil && *param.Status != "0" {
|
|
return role, gerror.New("代理模板不能停用,请先切换模板角色")
|
|
}
|
|
if param.DeptId != nil && *param.DeptId != template.DeptId {
|
|
return role, gerror.New("代理模板不能变更所属机构")
|
|
}
|
|
}
|
|
data := do.SysRole{}
|
|
gconv.Struct(param, &data)
|
|
data.UpdatedBy = user.ID
|
|
data.UpdatedAt = gtime.Now()
|
|
|
|
_, err = s.Model(ctx).Data(data).Where(dao.SysRole.Columns().RoleId, param.RoleId).OmitNil().Update()
|
|
if err != nil {
|
|
return role, err
|
|
}
|
|
// 更新角色菜单
|
|
err = s.RoleMenu(ctx, param.RoleId, param.MenuIds, param.RoleMenuDataScopes)
|
|
if err != nil {
|
|
return role, err
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// 删除角色
|
|
func (s *sSysRole) Delete(ctx context.Context, param *model.SysRoleDeleteParam) (role *model.SysRoleDeleteModel, err error) {
|
|
targetIDs := []int64{param.RoleId}
|
|
if param.RoleId == 0 {
|
|
targetIDs = param.RoleIds
|
|
}
|
|
template, templateErr := rolepolicy.TemplateForRoles(ctx, targetIDs...)
|
|
if templateErr != nil {
|
|
return nil, templateErr
|
|
}
|
|
if template != nil && (slices.Contains(param.RoleIds, template.RoleId) || param.RoleId == template.RoleId) {
|
|
return nil, gerror.New("代理模板不能删除,请先切换模板角色")
|
|
}
|
|
role = &model.SysRoleDeleteModel{
|
|
RoleId: param.RoleId,
|
|
RoleIds: param.RoleIds,
|
|
}
|
|
roleIds := make([]int64, 0)
|
|
if param.RoleId != 0 {
|
|
roleIds = append(roleIds, param.RoleId)
|
|
} else {
|
|
roleIds = param.RoleIds
|
|
}
|
|
if len(roleIds) == 0 {
|
|
return role, gerror.New("请选择要删除的角色")
|
|
}
|
|
if err = s.ensureRolesNotInherited(ctx, roleIds); err != nil {
|
|
return role, err
|
|
}
|
|
if err = s.ensureRolesHaveNoInheritedChildren(ctx, roleIds); err != nil {
|
|
return role, err
|
|
}
|
|
user := contexts.GetUser(ctx)
|
|
if user == nil {
|
|
return role, gerror.New("登录已过期")
|
|
}
|
|
data := do.SysRole{}
|
|
data.DeletedBy = user.ID
|
|
err = g.DB().Transaction(ctx, func(ctx context.Context, _ gdb.TX) error {
|
|
if _, deleteErr := s.Model(ctx).WhereIn(dao.SysRole.Columns().RoleId, roleIds).Data(data).OmitNil().Update(); deleteErr != nil {
|
|
return deleteErr
|
|
}
|
|
if _, deleteErr := s.Model(ctx).WhereIn(dao.SysRole.Columns().RoleId, roleIds).Delete(); deleteErr != nil {
|
|
return deleteErr
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return role, err
|
|
}
|
|
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysRoleDelete, roleIds)
|
|
clearRoleUsersAccessCache(ctx, roleIds...)
|
|
return
|
|
}
|
|
|
|
func (s *sSysRole) RoleMenu(ctx context.Context, roleId int64, menuIds []int64, dataScopes map[int64]string) (err error) {
|
|
template, templateErr := rolepolicy.TemplateForRoles(ctx, roleId)
|
|
if templateErr != nil {
|
|
return templateErr
|
|
}
|
|
if template != nil && template.RoleId == roleId {
|
|
if !contexts.IsSuperAdmin(ctx) {
|
|
return gerror.New("仅平台超级管理员可修改代理模板")
|
|
}
|
|
for _, scope := range dataScopes {
|
|
if scope == consts.SysRoleDataScopeAll || scope == consts.SysRoleDataScopeCustom {
|
|
return gerror.New("模板按钮不允许全部或自定义数据范围")
|
|
}
|
|
}
|
|
}
|
|
tenantId := contexts.GetTenantId(ctx)
|
|
if !contexts.IsSuperAdmin(ctx) {
|
|
allowed, err := rolepolicy.NormalizeAssignableMenus(ctx, tenantId, contexts.GetDeptId(ctx), contexts.GetUserId(ctx), menuIds)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
menuIds = allowed
|
|
for _, scope := range dataScopes {
|
|
if scope == consts.SysRoleDataScopeAll || scope == consts.SysRoleDataScopeCustom {
|
|
return gerror.New("不允许授予全部或自定义数据范围")
|
|
}
|
|
}
|
|
}
|
|
// 查询角色菜单
|
|
roleMenu := make([]*entity.SysRoleMenu, 0)
|
|
err = dao.SysRoleMenu.Ctx(ctx).Master().Where(dao.SysRoleMenu.Columns().TenantId, tenantId).Where(dao.SysRoleMenu.Columns().RoleId, roleId).Scan(&roleMenu)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
currMenuIds := make([]int64, 0)
|
|
for _, menu := range roleMenu {
|
|
currMenuIds = append(currMenuIds, menu.MenuId)
|
|
}
|
|
|
|
delMenuIds := utility.ArrayRightDiff(currMenuIds, menuIds)
|
|
addMenuIds := utility.ArrayRightDiff(menuIds, currMenuIds)
|
|
updateMenuIds := utility.ArrayRightDiff(menuIds, addMenuIds)
|
|
|
|
// 删除角色菜单
|
|
if len(delMenuIds) > 0 {
|
|
_, err = dao.SysRoleMenu.Ctx(ctx).Where(dao.SysRoleMenu.Columns().TenantId, tenantId).Where(dao.SysRoleMenu.Columns().RoleId, roleId).WhereIn(dao.SysRoleMenu.Columns().MenuId, delMenuIds).Delete()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// 新增角色菜单
|
|
if len(addMenuIds) > 0 {
|
|
addData := make([]*do.SysRoleMenu, 0)
|
|
for _, menuId := range addMenuIds {
|
|
addData = append(addData, &do.SysRoleMenu{
|
|
TenantId: tenantId,
|
|
RoleId: roleId,
|
|
MenuId: menuId,
|
|
DataScope: normalizeRoleMenuDataScope(dataScopes[menuId]),
|
|
})
|
|
}
|
|
_, err = dao.SysRoleMenu.Ctx(ctx).Data(addData).Insert()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, menuId := range updateMenuIds {
|
|
_, err = dao.SysRoleMenu.Ctx(ctx).Master().
|
|
Where(dao.SysRoleMenu.Columns().TenantId, tenantId).
|
|
Where(dao.SysRoleMenu.Columns().RoleId, roleId).
|
|
Where(dao.SysRoleMenu.Columns().MenuId, menuId).
|
|
Data(do.SysRoleMenu{
|
|
DataScope: normalizeRoleMenuDataScope(dataScopes[menuId]),
|
|
}).Update()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
clearRoleUsersAccessCache(ctx, roleId)
|
|
return
|
|
}
|
|
|
|
func normalizeRoleMenuDataScope(dataScope string) string {
|
|
switch dataScope {
|
|
case consts.SysRoleDataScopeAll,
|
|
consts.SysRoleDataScopeDept,
|
|
consts.SysRoleDataScopeDeptAndBelow,
|
|
consts.SysRoleDataScopePersonal,
|
|
consts.SysRoleDataScopeDeptAndBelowOrPersonal,
|
|
consts.SysRoleDataScopeOrgAndNextLevel,
|
|
consts.SysRoleDataScopeOrgNextLevel:
|
|
return dataScope
|
|
default:
|
|
return consts.SysRoleMenuDataScopeRole
|
|
}
|
|
}
|
|
|
|
// clearRoleUsersAccessCache removes stale permission caches after role changes.
|
|
func clearRoleUsersAccessCache(ctx context.Context, roleIds ...int64) {
|
|
if len(roleIds) == 0 {
|
|
return
|
|
}
|
|
relations := make([]*entity.SysUserRole, 0)
|
|
if err := dao.SysUserRole.Ctx(ctx).Master().
|
|
Fields(dao.SysUserRole.Columns().UserId).
|
|
Where(dao.SysUserRole.Columns().TenantId, contexts.GetTenantId(ctx)).
|
|
WhereIn(dao.SysUserRole.Columns().RoleId, roleIds).
|
|
Scan(&relations); err != nil {
|
|
g.Log().Warningf(ctx, "clear role users access cache failed, roleIds:%v, err:%+v", roleIds, err)
|
|
return
|
|
}
|
|
seen := make(map[int64]struct{}, len(relations))
|
|
for _, relation := range relations {
|
|
if relation == nil || relation.UserId <= 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[relation.UserId]; ok {
|
|
continue
|
|
}
|
|
seen[relation.UserId] = struct{}{}
|
|
if err := mcache.RemoveUserAccessCodeList(ctx, relation.UserId); err != nil {
|
|
g.Log().Warningf(ctx, "remove user access code cache failed, userId:%d, err:%+v", relation.UserId, err)
|
|
}
|
|
if err := mcache.RemoveUserRoleDataAccessCodeList(ctx, relation.UserId); err != nil {
|
|
g.Log().Warningf(ctx, "remove user role data access code cache failed, userId:%d, err:%+v", relation.UserId, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 自定义角色部门数据权限
|
|
func (s *sSysRole) RoleDept(ctx context.Context, roleId int64, deptIds []int64) (err error) {
|
|
tenantId := contexts.GetTenantId(ctx)
|
|
// 查询角色部门
|
|
roleDept := make([]*entity.SysRoleDept, 0)
|
|
err = dao.SysRoleDept.Ctx(ctx).Master().Where(dao.SysRoleDept.Columns().TenantId, tenantId).Where(dao.SysRoleDept.Columns().RoleId, roleId).Scan(&roleDept)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
currDeptIds := make([]int64, 0)
|
|
for _, dept := range roleDept {
|
|
currDeptIds = append(currDeptIds, dept.DeptId)
|
|
}
|
|
|
|
delDeptIds := utility.ArrayRightDiff(currDeptIds, deptIds)
|
|
addDeptIds := utility.ArrayRightDiff(deptIds, currDeptIds)
|
|
|
|
// 删除角色部门
|
|
if len(delDeptIds) > 0 {
|
|
_, err = dao.SysRoleDept.Ctx(ctx).Where(dao.SysRoleDept.Columns().TenantId, tenantId).Where(dao.SysRoleDept.Columns().RoleId, roleId).WhereIn(dao.SysRoleDept.Columns().DeptId, delDeptIds).Delete()
|
|
}
|
|
|
|
// 新增角色部门
|
|
if len(addDeptIds) > 0 {
|
|
addData := make([]map[string]interface{}, 0)
|
|
for _, deptId := range addDeptIds {
|
|
addData = append(addData, map[string]interface{}{
|
|
dao.SysRoleDept.Columns().TenantId: tenantId,
|
|
dao.SysRoleDept.Columns().RoleId: roleId,
|
|
dao.SysRoleDept.Columns().DeptId: deptId,
|
|
})
|
|
}
|
|
_, err = dao.SysRoleDept.Ctx(ctx).Data(addData).Insert()
|
|
}
|
|
if err == nil {
|
|
clearRoleUsersAccessCache(ctx, roleId)
|
|
}
|
|
return
|
|
}
|
|
|
|
// 编辑角色数据权限
|
|
func (s *sSysRole) EditRoleDataScope(ctx context.Context, model *model.SysRoleDataScopeEditParam) (err error) {
|
|
template, templateErr := rolepolicy.TemplateForRoles(ctx, model.RoleId)
|
|
if templateErr != nil {
|
|
return templateErr
|
|
}
|
|
if template != nil && !contexts.IsSuperAdmin(ctx) {
|
|
return gerror.New("仅平台超级管理员可修改代理模板")
|
|
}
|
|
if !contexts.IsSuperAdmin(ctx) || (template != nil && template.RoleId == model.RoleId) {
|
|
if model.DataScope == consts.SysRoleDataScopeAll || model.DataScope == consts.SysRoleDataScopeCustom || model.DataScope == "" {
|
|
return gerror.New("不允许全部、自定义或空数据权限")
|
|
}
|
|
}
|
|
if err = s.ensureRolesNotInherited(ctx, []int64{model.RoleId}); err != nil {
|
|
return err
|
|
}
|
|
|
|
data := do.SysRole{}
|
|
gconv.Struct(model, &data)
|
|
data.UpdatedBy = contexts.GetUserId(ctx)
|
|
data.UpdatedAt = gtime.Now()
|
|
|
|
if template != nil && template.RoleId == model.RoleId && model.DataScope != consts.SysRoleDataScopeDeptAndBelow {
|
|
return gerror.New("代理模板数据范围必须为本机构及下级")
|
|
}
|
|
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
if _, updateErr := s.Model(ctx).Data(data).Where(dao.SysRole.Columns().RoleId, model.RoleId).OmitNil().Update(); updateErr != nil {
|
|
return updateErr
|
|
}
|
|
if err := s.RoleDept(ctx, model.RoleId, model.DeptIds); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysRoleUpdate, model.RoleId)
|
|
err = rolesync.EnqueueRole(ctx, model.RoleId, contexts.GetUserId(ctx))
|
|
return
|
|
}
|
|
|
|
func (s *sSysRole) ensureRolesNotInherited(ctx context.Context, roleIds []int64) error {
|
|
if len(roleIds) == 0 {
|
|
return nil
|
|
}
|
|
if contexts.IsSuperAdmin(ctx) {
|
|
return nil
|
|
}
|
|
count, err := g.Model(dao.SysRole.Table()+" r").Ctx(ctx).
|
|
LeftJoin(dao.SysDept.Table()+" d", "d.dept_id = r.dept_id AND d.tenant_id = r.tenant_id").
|
|
WhereIn("r.role_id", roleIds).
|
|
Where("r.deleted_at IS NULL").
|
|
Where("d.deleted_at IS NULL").
|
|
Where("r.created_dept > 0").
|
|
Where("r.dept_id <> r.created_dept").
|
|
Where("d.parent_id = r.created_dept").
|
|
Where("r.tenant_id", contexts.GetTenantId(ctx)).
|
|
Count()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return gerror.New("上级下发角色由上级统一维护,当前组织只能查看和使用")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *sSysRole) ensureRolesHaveNoInheritedChildren(ctx context.Context, roleIds []int64) error {
|
|
if len(roleIds) == 0 {
|
|
return nil
|
|
}
|
|
configured, err := dao.SysConfig.Ctx(ctx).Master().Where("tenant_id", consts.DefaultSystemTenantCode).Where("config_key", rolepolicy.TemplateConfigKey).Count()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if configured > 0 {
|
|
return nil
|
|
}
|
|
count, err := g.Model(dao.SysRole.Table()+" r").Ctx(ctx).
|
|
LeftJoin(dao.SysDept.Table()+" child_dept", "child_dept.dept_id = r.dept_id AND child_dept.tenant_id = r.tenant_id").
|
|
Where("r.created_dept IN (SELECT dept_id FROM sys_role WHERE role_id IN (?) AND is_pass = 1 AND deleted_at IS NULL)", roleIds).
|
|
Where("r.deleted_at IS NULL").
|
|
Where("child_dept.deleted_at IS NULL").
|
|
Where("r.created_dept > 0").
|
|
Where("r.dept_id <> r.created_dept").
|
|
Where("child_dept.parent_id = r.created_dept").
|
|
Where("r.tenant_id", contexts.GetTenantId(ctx)).
|
|
Count()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return gerror.New("该角色已下发给下级,不能删除;可关闭后续下发或停用角色")
|
|
}
|
|
return nil
|
|
}
|