186 lines
7.0 KiB
Go
186 lines
7.0 KiB
Go
package rolesync
|
|
|
|
import (
|
|
"context"
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/dao"
|
|
|
|
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/library/rolepolicy"
|
|
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model"
|
|
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model/do"
|
|
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model/entity"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func syncTemplateRoles(ctx context.Context, template *entity.SysRole, message *model.AgentRoleSyncMessage) error {
|
|
departments, err := templateDescendants(ctx, template)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
retrying := len(message.ChildDeptIds) > 0
|
|
if !retrying {
|
|
message.TotalCount = len(departments)
|
|
message.SuccessCount = 0
|
|
}
|
|
pending := make(map[int64]bool, len(message.ChildDeptIds))
|
|
for _, deptID := range message.ChildDeptIds {
|
|
pending[deptID] = true
|
|
}
|
|
failed := make(map[int64]bool)
|
|
failedDeptIDs := make([]int64, 0)
|
|
var firstErr error
|
|
for _, child := range departments {
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
if retrying && !pending[child.DeptId] {
|
|
continue
|
|
}
|
|
delete(pending, child.DeptId)
|
|
var syncErr error
|
|
if failed[child.ParentId] {
|
|
syncErr = gerror.Newf("上级机构%d尚未同步成功", child.ParentId)
|
|
} else {
|
|
syncErr = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
return syncTemplateChildTx(ctx, tx, template, child, message.ActorId)
|
|
})
|
|
}
|
|
if syncErr != nil {
|
|
failed[child.DeptId] = true
|
|
failedDeptIDs = append(failedDeptIDs, child.DeptId)
|
|
if firstErr == nil {
|
|
firstErr = syncErr
|
|
}
|
|
g.Log().Errorf(ctx, "agent template sync child failed, deptId:%d, err:%v", child.DeptId, syncErr)
|
|
continue
|
|
}
|
|
message.SuccessCount++
|
|
}
|
|
for deptID := range pending {
|
|
failedDeptIDs = append(failedDeptIDs, deptID)
|
|
if firstErr == nil {
|
|
firstErr = gerror.Newf("待同步机构%d已不在当前模板的下级范围,请核对机构关系", deptID)
|
|
}
|
|
}
|
|
slices.Sort(failedDeptIDs)
|
|
message.ChildDeptIds = failedDeptIDs
|
|
message.FailedCount = len(failedDeptIDs)
|
|
if firstErr != nil {
|
|
return gerror.Wrapf(firstErr, "代理模板同步失败企业数:%d", len(failedDeptIDs))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func templateDescendants(ctx context.Context, template *entity.SysRole) ([]*entity.SysDept, error) {
|
|
queue := []int64{template.DeptId}
|
|
seen := map[int64]bool{template.DeptId: true}
|
|
departments := make([]*entity.SysDept, 0)
|
|
for len(queue) > 0 {
|
|
if err := ctx.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
parentID := queue[0]
|
|
queue = queue[1:]
|
|
var children []*entity.SysDept
|
|
if err := dao.SysDept.Ctx(ctx).Master().Where("tenant_id", template.TenantId).Where("parent_id", parentID).Where("dept_type", 1).OrderAsc("dept_id").Scan(&children); err != nil {
|
|
return nil, err
|
|
}
|
|
for _, child := range children {
|
|
if seen[child.DeptId] {
|
|
return nil, gerror.New("机构上下级存在循环")
|
|
}
|
|
seen[child.DeptId] = true
|
|
queue = append(queue, child.DeptId)
|
|
departments = append(departments, child)
|
|
}
|
|
}
|
|
return departments, nil
|
|
}
|
|
|
|
func syncTemplateChildTx(ctx context.Context, tx gdb.TX, template *entity.SysRole, child *entity.SysDept, actorID int64) error {
|
|
if child.DeptRole <= 0 {
|
|
return gerror.New("下级机构缺失默认角色")
|
|
}
|
|
lock := tx.Ctx(ctx).Model(dao.SysDept.Table()).Where("dept_id", child.DeptId).Where("tenant_id", template.TenantId)
|
|
if !strings.EqualFold(g.DB().GetConfig().Type, "sqlite") {
|
|
lock = lock.LockUpdate()
|
|
}
|
|
var locked *entity.SysDept
|
|
if err := lock.Scan(&locked); err != nil {
|
|
return err
|
|
}
|
|
if locked == nil || locked.DeptRole != child.DeptRole || locked.ParentId != child.ParentId || locked.DeptType != 1 {
|
|
return gerror.New("下级机构角色绑定已变化")
|
|
}
|
|
currentLock := tx.Ctx(ctx).Model(dao.SysRole.Table()).Where("role_id", child.DeptRole).Where("tenant_id", template.TenantId).Where("dept_id", child.DeptId)
|
|
if !strings.EqualFold(g.DB().GetConfig().Type, "sqlite") {
|
|
currentLock = currentLock.LockUpdate()
|
|
}
|
|
var current *entity.SysRole
|
|
if err := currentLock.Scan(¤t); err != nil {
|
|
return err
|
|
}
|
|
if current == nil || current.Status != "0" {
|
|
return gerror.New("下级机构默认角色不存在或已删除")
|
|
}
|
|
if current.CreatedDept != child.ParentId {
|
|
return gerror.New("下级机构默认角色非直属上级继承角色,请核对角色来源")
|
|
}
|
|
configLock := tx.Ctx(ctx).Model(dao.SysConfig.Table()).Where("tenant_id", template.TenantId).Where("config_key", rolepolicy.TemplateConfigKey)
|
|
if !strings.EqualFold(g.DB().GetConfig().Type, "sqlite") {
|
|
configLock = configLock.LockUpdate()
|
|
}
|
|
var config *entity.SysConfig
|
|
if err := configLock.Scan(&config); err != nil {
|
|
return err
|
|
}
|
|
if config == nil || strings.TrimSpace(config.ConfigValue) != strconv.FormatInt(template.RoleId, 10) {
|
|
return gerror.New("模板已切换,请重新同步")
|
|
}
|
|
templateLock := tx.Ctx(ctx).Model(dao.SysRole.Table()).Where("role_id", template.RoleId)
|
|
if !strings.EqualFold(g.DB().GetConfig().Type, "sqlite") {
|
|
templateLock = templateLock.LockUpdate()
|
|
}
|
|
var latest *entity.SysRole
|
|
if err := templateLock.Scan(&latest); err != nil {
|
|
return err
|
|
}
|
|
if latest == nil || latest.Status != "0" || latest.TenantId != template.TenantId || latest.DeptId != template.DeptId || latest.DataScope != consts.SysRoleDataScopeDeptAndBelow || (latest.CreatedDept > 0 && latest.CreatedDept != latest.DeptId) {
|
|
return gerror.New("模板不可用")
|
|
}
|
|
menuLock := tx.Ctx(ctx).Model(dao.SysRoleMenu.Table()).Where("tenant_id", template.TenantId).Where("role_id", template.RoleId)
|
|
if !strings.EqualFold(g.DB().GetConfig().Type, "sqlite") {
|
|
menuLock = menuLock.LockUpdate()
|
|
}
|
|
var menus []*entity.SysRoleMenu
|
|
if err := menuLock.Scan(&menus); err != nil {
|
|
return err
|
|
}
|
|
// Every linked default role receives the same template snapshot. Individual
|
|
// changes are intentionally replaced; unrelated roles are never updated.
|
|
if _, err := tx.Ctx(ctx).Model(dao.SysRole.Table()).Where("tenant_id", template.TenantId).Where("role_id", current.RoleId).Data(do.SysRole{DataScope: consts.SysRoleDataScopeDeptAndBelow, UpdatedBy: actorID}).Update(); err != nil {
|
|
return err
|
|
}
|
|
if _, err := tx.Ctx(ctx).Model(dao.SysRoleMenu.Table()).Where("tenant_id", template.TenantId).Where("role_id", current.RoleId).Delete(); err != nil {
|
|
return err
|
|
}
|
|
menuData := make([]*do.SysRoleMenu, 0, len(menus))
|
|
for _, menu := range menus {
|
|
if menu.DataScope == consts.SysRoleDataScopeAll || menu.DataScope == consts.SysRoleDataScopeCustom {
|
|
return gerror.New("模板按钮数据范围已变化,请重新同步")
|
|
}
|
|
menuData = append(menuData, &do.SysRoleMenu{TenantId: template.TenantId, RoleId: current.RoleId, MenuId: menu.MenuId, DataScope: consts.SysRoleMenuDataScopeRole})
|
|
}
|
|
if len(menuData) > 0 {
|
|
if _, err := tx.Ctx(ctx).Model(dao.SysRoleMenu.Table()).Data(menuData).Batch(200).Insert(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|