package data import ( "context" "errors" "strconv" "strings" "kra/internal/biz" "gorm.io/gorm" ) func (r *accessRepo) strictAuthorityIDs(ctx context.Context, actorID uint) (map[uint]bool, error) { allowed := make(map[uint]bool) if actorID == 0 { return allowed, nil } var actor authorityPO if err := r.data.gormDB.WithContext(ctx).Where("authority_id = ?", actorID).First(&actor).Error; err != nil { return nil, err } var authorities []authorityPO if err := r.data.gormDB.WithContext(ctx).Find(&authorities).Error; err != nil { return nil, err } children := make(map[uint][]uint) for _, authority := range authorities { if authority.ParentID != nil { children[*authority.ParentID] = append(children[*authority.ParentID], authority.AuthorityID) } } var walk func(uint) walk = func(id uint) { for _, child := range children[id] { if !allowed[child] { allowed[child] = true walk(child) } } } if actor.ParentID == nil || *actor.ParentID == 0 { allowed[actorID] = true } walk(actorID) return allowed, nil } func (r *accessRepo) checkAuthorityIDAuth(ctx context.Context, targetID uint) error { config := r.data.runtime.Admin() if config == nil || config.System == nil || !config.System.UseStrictAuth { return nil } actor, ok := biz.ActorFromContext(ctx) if !ok { return errors.New("您提交的角色ID不合法") } allowed, err := r.strictAuthorityIDs(ctx, actor.AuthorityID) if err != nil { return err } if !allowed[targetID] { return errors.New("您提交的角色ID不合法") } return nil } func (r *accessRepo) CreateAuthority(ctx context.Context, value *biz.Authority) error { config := r.data.runtime.Admin() if config != nil && config.System != nil && config.System.UseStrictAuth && (value.ParentID == nil || *value.ParentID == 0) { if actor, ok := biz.ActorFromContext(ctx); ok { value.ParentID = &actor.AuthorityID } } return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { var count int64 if err := tx.Model(&authorityPO{}).Where("authority_id = ?", value.AuthorityID).Count(&count).Error; err != nil { return err } if count > 0 { return errors.New("存在相同角色id") } if value.DefaultRouter == "" { value.DefaultRouter = "dashboard" } if err := tx.Create(&authorityPO{AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}).Error; err != nil { return err } var dashboard menuPO if err := tx.Where("name = ?", "dashboard").First(&dashboard).Error; err == nil { if err = tx.Create(&authorityMenuPO{SysAuthorityAuthorityID: value.AuthorityID, SysBaseMenuID: dashboard.ID}).Error; err != nil { return err } } defaults := []struct{ path, method string }{{"/menu/getMenu", "POST"}, {"/jwt/jsonInBlacklist", "POST"}, {"/user/changePassword", "POST"}, {"/user/setUserAuthority", "POST"}, {"/user/getUserInfo", "GET"}, {"/user/setSelfInfo", "PUT"}, {"/fileUploadAndDownload/upload", "POST"}, {"/sysDictionary/findSysDictionary", "GET"}} for _, item := range defaults { var api apiPO if err := tx.Where("path = ? AND method = ?", item.path, item.method).First(&api).Error; err == nil { if err = tx.Create(&authorityAPIPO{AuthorityID: value.AuthorityID, APIID: api.ID}).Error; err != nil { return err } } } return nil }) } func (r *accessRepo) CopyAuthority(ctx context.Context, sourceID uint, value *biz.Authority) error { return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { config := r.data.runtime.Admin() actor, hasActor := biz.ActorFromContext(ctx) strict := hasActor && actor.AuthorityID != 888 && config != nil && config.System != nil && config.System.UseStrictAuth if strict { allowed, err := r.strictAuthorityIDs(ctx, actor.AuthorityID) if err != nil { return err } if value.ParentID == nil || !allowed[*value.ParentID] { return errors.New("严格角色模式下只能在自己的角色树中复制角色") } } var count int64 if err := tx.Model(&authorityPO{}).Where("authority_id = ?", value.AuthorityID).Count(&count).Error; err != nil { return err } if count != 0 { return errors.New("存在相同角色id") } if err := tx.Create(&authorityPO{AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}).Error; err != nil { return err } copyLinks := func(table string, destination any, columns map[string]any) error { return tx.Table(table).Where(columns).Find(destination).Error } var menus []authorityMenuPO if err := copyLinks("sys_authority_menus", &menus, map[string]any{"sys_authority_authority_id": sourceID}); err != nil { return err } for i := range menus { menus[i].SysAuthorityAuthorityID = value.AuthorityID } if len(menus) > 0 { if err := tx.Create(&menus).Error; err != nil { return err } } var apis []authorityAPIPO if err := copyLinks("sys_authority_apis", &apis, map[string]any{"authority_id": sourceID}); err != nil { return err } for i := range apis { if strict { var allowedCount int64 if err := tx.Model(&authorityAPIPO{}).Where("authority_id = ? AND api_id = ?", actor.AuthorityID, apis[i].APIID).Count(&allowedCount).Error; err != nil { return err } if allowedCount == 0 { return errors.New("严格角色模式下不能复制当前角色未拥有的 API") } } apis[i].AuthorityID = value.AuthorityID } if len(apis) > 0 { if err := tx.Create(&apis).Error; err != nil { return err } } var buttons []authorityButtonPO if err := copyLinks("sys_authority_btns", &buttons, map[string]any{"authority_id": sourceID}); err != nil { return err } for i := range buttons { buttons[i].AuthorityID = value.AuthorityID } if len(buttons) > 0 { if err := tx.Create(&buttons).Error; err != nil { return err } } return nil }) } func (r *accessRepo) UpdateAuthority(ctx context.Context, value *biz.Authority) error { return r.data.gormDB.WithContext(ctx).Model(&authorityPO{}).Where("authority_id = ?", value.AuthorityID).Updates(map[string]any{"authority_name": value.AuthorityName, "parent_id": value.ParentID, "data_scope": value.DataScope, "default_router": value.DefaultRouter}).Error } func (r *accessRepo) DeleteAuthority(ctx context.Context, id uint) error { if id == 888 { return errors.New("不能删除超级管理员") } return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { var users, children int64 if err := tx.Model(&userAuthorityPO{}).Where("sys_authority_authority_id = ?", id).Count(&users).Error; err != nil { return err } if users > 0 { return errors.New("此角色有用户正在使用禁止删除") } if err := tx.Model(&userPO{}).Where("authority_id = ?", id).Count(&users).Error; err != nil { return err } if users > 0 { return errors.New("此角色有用户正在使用禁止删除") } if err := tx.Model(&authorityPO{}).Where("parent_id = ?", id).Count(&children).Error; err != nil { return err } if children > 0 { return errors.New("此角色存在子角色不允许删除") } if err := tx.Where("sys_authority_authority_id = ?", id).Delete(&authorityMenuPO{}).Error; err != nil { return err } if err := tx.Where("authority_id = ?", id).Delete(&authorityAPIPO{}).Error; err != nil { return err } if err := tx.Where("authority_id = ?", id).Delete(&authorityButtonPO{}).Error; err != nil { return err } if err := tx.Where("authority_id = ?", id).Delete(&authorityDepartmentPO{}).Error; err != nil { return err } return tx.Delete(&authorityPO{}, "authority_id = ?", id).Error }) } func (r *accessRepo) ListAuthorities(ctx context.Context) ([]*biz.Authority, error) { var pos []authorityPO if err := r.data.gormDB.WithContext(ctx).Order("authority_id").Find(&pos).Error; err != nil { return nil, err } var allowed map[uint]bool config := r.data.runtime.Admin() if actor, ok := biz.ActorFromContext(ctx); ok && config != nil && config.System != nil && config.System.UseStrictAuth { var err error allowed, err = r.strictAuthorityIDs(ctx, actor.AuthorityID) if err != nil { return nil, err } } out := make([]*biz.Authority, 0, len(pos)) for _, po := range pos { if allowed != nil && !allowed[po.AuthorityID] { continue } v := toBizAuthority(po) out = append(out, &v) } return out, nil } func (r *accessRepo) SetAuthorityUsers(ctx context.Context, id uint, ids []uint) error { return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { var oldIDs []uint if err := tx.Model(&userAuthorityPO{}).Where("sys_authority_authority_id = ?", id).Pluck("sys_user_id", &oldIDs).Error; err != nil { return err } if err := tx.Where("sys_authority_authority_id = ?", id).Delete(&userAuthorityPO{}).Error; err != nil { return err } links := make([]userAuthorityPO, 0, len(ids)) for _, uid := range ids { links = append(links, userAuthorityPO{SysUserID: uid, SysAuthorityAuthorityID: id}) } if len(links) > 0 { if err := tx.Create(&links).Error; err != nil { return err } } selected := map[uint]bool{} for _, uid := range ids { selected[uid] = true } for _, uid := range oldIDs { if selected[uid] { continue } var user userPO if err := tx.First(&user, uid).Error; err == nil && user.AuthorityID == id { var another userAuthorityPO if err = tx.Where("sys_user_id = ?", uid).First(&another).Error; errors.Is(err, gorm.ErrRecordNotFound) { continue } else if err != nil { return err } if err = tx.Model(&user).Update("authority_id", another.SysAuthorityAuthorityID).Error; err != nil { return err } } } return nil }) } func (r *accessRepo) AuthorityUserIDs(ctx context.Context, id uint) ([]uint, error) { var ids []uint err := r.data.gormDB.WithContext(ctx).Model(&userAuthorityPO{}).Where("sys_authority_authority_id = ?", id).Pluck("sys_user_id", &ids).Error return ids, err } func (r *accessRepo) SetDataScope(ctx context.Context, id uint, scope int, deptIDs []uint) error { return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Model(&authorityPO{}).Where("authority_id = ?", id).Update("data_scope", scope).Error; err != nil { return err } if err := tx.Where("authority_id = ?", id).Delete(&authorityDepartmentPO{}).Error; err != nil { return err } if scope == 5 { links := make([]authorityDepartmentPO, 0, len(deptIDs)) for _, did := range deptIDs { links = append(links, authorityDepartmentPO{AuthorityID: id, DepartmentID: did}) } if len(links) > 0 { return tx.Create(&links).Error } } return nil }) } func (r *accessRepo) DataScopeDepartmentIDs(ctx context.Context, id uint) ([]uint, error) { var ids []uint err := r.data.gormDB.WithContext(ctx).Model(&authorityDepartmentPO{}).Where("authority_id = ?", id).Pluck("department_id", &ids).Error return ids, err } func (r *accessRepo) ResolveDataScope(ctx context.Context, authorityID, userID uint) (biz.DataScope, error) { identity := biz.DataScope{UserID: userID, AuthorityID: authorityID} var user userPO _ = r.data.gormDB.WithContext(ctx).Select("id", "dept_id").First(&user, userID).Error identity.PrimaryDeptID = user.DeptID var authority authorityPO _ = r.data.gormDB.WithContext(ctx).Select("authority_id", "data_scope").First(&authority, "authority_id = ?", authorityID).Error identity.Scope = authority.DataScope if identity.Scope == 0 { identity.Scope = 1 } identity.All = identity.Scope == 1 if identity.Scope == 4 { identity.OwnerUserID = userID } var ids []uint _ = r.data.gormDB.WithContext(ctx).Model(&userDepartmentPO{}).Where("sys_user_id = ?", userID).Pluck("sys_department_id", &ids).Error selected := make(map[uint]bool, len(ids)+1) for _, id := range ids { selected[id] = true } if user.DeptID != 0 { selected[user.DeptID] = true } ids = ids[:0] for id := range selected { ids = append(ids, id) } if identity.Scope == 2 && len(ids) > 0 { var departments []departmentPO _ = r.data.gormDB.WithContext(ctx).Find(&departments).Error for _, department := range departments { for _, part := range strings.Split(department.Ancestors, ",") { value, _ := strconv.ParseUint(part, 10, 64) if selected[uint(value)] { selected[department.ID] = true break } } } ids = ids[:0] for id := range selected { ids = append(ids, id) } } else if identity.Scope == 5 { ids, _ = r.DataScopeDepartmentIDs(ctx, authorityID) } identity.DepartmentIDs = ids return identity, nil }