package system import ( "context" "errors" "strconv" ) var ( ErrAuthorityExists = errors.New("存在相同角色id") ErrAuthorityNotFound = errors.New("该角色不存在") ErrAuthorityHasUsers = errors.New("此角色有用户正在使用禁止删除") ErrAuthorityHasChildren = errors.New("此角色存在子角色不允许删除") ErrAuthorityIDInvalid = errors.New("您提交的角色ID不合法") ) // AuthorityFull 完整角色实体(包含关联) type AuthorityFull struct { Authority DataAuthorityId []*Authority Children []*AuthorityFull SysBaseMenus []*BaseMenu } // CasbinInfo Casbin规则信息 type CasbinInfo struct { Path string Method string } // DefaultCasbin 默认Casbin规则 func DefaultCasbin() []CasbinInfo { return []CasbinInfo{ {Path: "/menu/getMenu", Method: "POST"}, {Path: "/jwt/jsonInBlacklist", Method: "POST"}, {Path: "/base/login", Method: "POST"}, {Path: "/user/changePassword", Method: "POST"}, {Path: "/user/setUserAuthority", Method: "POST"}, {Path: "/user/getUserInfo", Method: "GET"}, {Path: "/user/setSelfInfo", Method: "PUT"}, {Path: "/fileUploadAndDownload/upload", Method: "POST"}, {Path: "/sysDictionary/findSysDictionary", Method: "GET"}, } } // DefaultMenu 默认菜单 func DefaultMenu() []*BaseMenu { return []*BaseMenu{{ ID: 1, ParentId: 0, Path: "dashboard", Name: "dashboard", Component: "view/dashboard/index.vue", Sort: 1, Title: "仪表盘", Icon: "setting", }} } // AuthorityRepo 角色仓储接口 type AuthorityRepo interface { Create(ctx context.Context, auth *Authority) error Update(ctx context.Context, auth *Authority) error Delete(ctx context.Context, authorityId uint) error FindByID(ctx context.Context, authorityId uint) (*Authority, error) FindByIDWithDataAuthority(ctx context.Context, authorityId uint) (*AuthorityFull, error) FindByParentID(ctx context.Context, parentId uint) ([]*AuthorityFull, error) FindChildren(ctx context.Context, authorityId uint) ([]*AuthorityFull, error) HasUsers(ctx context.Context, authorityId uint) (bool, error) HasChildren(ctx context.Context, authorityId uint) (bool, error) GetParentAuthorityID(ctx context.Context, authorityId uint) (uint, error) SetDataAuthority(ctx context.Context, authorityId uint, dataAuthorityIds []uint) error SetMenuAuthority(ctx context.Context, authorityId uint, menuIds []uint) error GetMenuIds(ctx context.Context, authorityId uint) ([]uint, error) CopyAuthorityBtns(ctx context.Context, oldAuthorityId, newAuthorityId uint) error DeleteAuthorityRelations(ctx context.Context, authorityId uint) error } // AuthorityUsecase 角色用例 type AuthorityUsecase struct { repo AuthorityRepo casbinRepo CasbinRepo useStrictAuth bool } // NewAuthorityUsecase 创建角色用例 func NewAuthorityUsecase(repo AuthorityRepo, casbinRepo CasbinRepo, useStrictAuth bool) *AuthorityUsecase { return &AuthorityUsecase{ repo: repo, casbinRepo: casbinRepo, useStrictAuth: useStrictAuth, } } // CreateAuthority 创建角色 func (uc *AuthorityUsecase) CreateAuthority(ctx context.Context, auth *Authority) (*Authority, error) { // 检查是否存在相同角色ID existing, _ := uc.repo.FindByID(ctx, auth.AuthorityId) if existing != nil { return nil, ErrAuthorityExists } // 创建角色 if err := uc.repo.Create(ctx, auth); err != nil { return nil, err } // 设置默认菜单 defaultMenus := DefaultMenu() menuIds := make([]uint, len(defaultMenus)) for i, m := range defaultMenus { menuIds[i] = m.ID } if err := uc.repo.SetMenuAuthority(ctx, auth.AuthorityId, menuIds); err != nil { return nil, err } // 添加默认Casbin规则 casbinInfos := DefaultCasbin() rules := make([][]string, len(casbinInfos)) for i, v := range casbinInfos { rules[i] = []string{uintToStr(auth.AuthorityId), v.Path, v.Method} } if err := uc.casbinRepo.AddPolicies(rules); err != nil { return nil, err } return auth, nil } // CopyAuthority 复制角色 func (uc *AuthorityUsecase) CopyAuthority(ctx context.Context, adminAuthorityID, oldAuthorityId uint, newAuth *Authority) (*Authority, error) { // 检查新角色ID是否已存在 existing, _ := uc.repo.FindByID(ctx, newAuth.AuthorityId) if existing != nil { return nil, ErrAuthorityExists } // 获取旧角色的菜单 menuIds, err := uc.repo.GetMenuIds(ctx, oldAuthorityId) if err != nil { return nil, err } // 创建新角色 if err := uc.repo.Create(ctx, newAuth); err != nil { return nil, err } // 复制菜单权限 if err := uc.repo.SetMenuAuthority(ctx, newAuth.AuthorityId, menuIds); err != nil { return nil, err } // 复制按钮权限 if err := uc.repo.CopyAuthorityBtns(ctx, oldAuthorityId, newAuth.AuthorityId); err != nil { return nil, err } // 复制Casbin规则 paths := uc.casbinRepo.GetPolicyPathByAuthorityId(oldAuthorityId) if err := uc.casbinRepo.UpdateCasbin(adminAuthorityID, newAuth.AuthorityId, paths); err != nil { _ = uc.DeleteAuthority(ctx, newAuth.AuthorityId) return nil, err } return newAuth, nil } // UpdateAuthority 更新角色 func (uc *AuthorityUsecase) UpdateAuthority(ctx context.Context, auth *Authority) (*Authority, error) { existing, err := uc.repo.FindByID(ctx, auth.AuthorityId) if err != nil { return nil, errors.New("查询角色数据失败") } if existing == nil { return nil, ErrAuthorityNotFound } if err := uc.repo.Update(ctx, auth); err != nil { return nil, err } return auth, nil } // DeleteAuthority 删除角色 func (uc *AuthorityUsecase) DeleteAuthority(ctx context.Context, authorityId uint) error { // 检查角色是否存在 existing, err := uc.repo.FindByID(ctx, authorityId) if err != nil || existing == nil { return ErrAuthorityNotFound } // 检查是否有用户使用此角色 hasUsers, err := uc.repo.HasUsers(ctx, authorityId) if err != nil { return err } if hasUsers { return ErrAuthorityHasUsers } // 检查是否有子角色 hasChildren, err := uc.repo.HasChildren(ctx, authorityId) if err != nil { return err } if hasChildren { return ErrAuthorityHasChildren } // 删除关联关系 if err := uc.repo.DeleteAuthorityRelations(ctx, authorityId); err != nil { return err } // 删除角色 if err := uc.repo.Delete(ctx, authorityId); err != nil { return err } // 删除Casbin规则 uc.casbinRepo.RemoveFilteredPolicy(uintToStr(authorityId)) return nil } // GetAuthorityInfoList 获取角色列表(树形) func (uc *AuthorityUsecase) GetAuthorityInfoList(ctx context.Context, authorityID uint) ([]*AuthorityFull, error) { auth, err := uc.repo.FindByID(ctx, authorityID) if err != nil { return nil, err } var authorities []*AuthorityFull if uc.useStrictAuth { if auth.ParentId == nil || *auth.ParentId == 0 { // 顶级角色可以修改自己的权限和以下权限 authFull, err := uc.repo.FindByIDWithDataAuthority(ctx, authorityID) if err != nil { return nil, err } authorities = []*AuthorityFull{authFull} } else { // 非顶级角色只能修改以下权限 authorities, err = uc.repo.FindByParentID(ctx, authorityID) if err != nil { return nil, err } } } else { authorities, err = uc.repo.FindByParentID(ctx, 0) if err != nil { return nil, err } } // 递归获取子角色 for i := range authorities { if err := uc.findChildrenAuthority(ctx, authorities[i]); err != nil { return nil, err } } return authorities, nil } func (uc *AuthorityUsecase) findChildrenAuthority(ctx context.Context, authority *AuthorityFull) error { children, err := uc.repo.FindChildren(ctx, authority.AuthorityId) if err != nil { return err } authority.Children = children for i := range authority.Children { if err := uc.findChildrenAuthority(ctx, authority.Children[i]); err != nil { return err } } return nil } // GetStructAuthorityList 获取角色ID列表(包含子角色) func (uc *AuthorityUsecase) GetStructAuthorityList(ctx context.Context, authorityID uint) ([]uint, error) { auth, err := uc.repo.FindByID(ctx, authorityID) if err != nil { return nil, err } var list []uint children, err := uc.repo.FindChildren(ctx, authorityID) if err != nil { return nil, err } for _, child := range children { list = append(list, child.AuthorityId) childList, err := uc.GetStructAuthorityList(ctx, child.AuthorityId) if err == nil { list = append(list, childList...) } } if auth.ParentId == nil || *auth.ParentId == 0 { list = append(list, authorityID) } return list, nil } // CheckAuthorityIDAuth 检查角色ID权限 func (uc *AuthorityUsecase) CheckAuthorityIDAuth(ctx context.Context, authorityID, targetID uint) error { if !uc.useStrictAuth { return nil } authIDs, err := uc.GetStructAuthorityList(ctx, authorityID) if err != nil { return err } for _, id := range authIDs { if id == targetID { return nil } } return ErrAuthorityIDInvalid } // GetAuthorityInfo 获取角色信息 func (uc *AuthorityUsecase) GetAuthorityInfo(ctx context.Context, authorityId uint) (*AuthorityFull, error) { return uc.repo.FindByIDWithDataAuthority(ctx, authorityId) } // SetDataAuthority 设置角色资源权限 func (uc *AuthorityUsecase) SetDataAuthority(ctx context.Context, adminAuthorityID uint, authorityId uint, dataAuthorityIds []uint) error { // 检查权限 checkIDs := append([]uint{authorityId}, dataAuthorityIds...) for _, id := range checkIDs { if err := uc.CheckAuthorityIDAuth(ctx, adminAuthorityID, id); err != nil { return err } } return uc.repo.SetDataAuthority(ctx, authorityId, dataAuthorityIds) } // SetMenuAuthority 设置角色菜单权限 func (uc *AuthorityUsecase) SetMenuAuthority(ctx context.Context, authorityId uint, menuIds []uint) error { return uc.repo.SetMenuAuthority(ctx, authorityId, menuIds) } // GetParentAuthorityID 获取父角色ID func (uc *AuthorityUsecase) GetParentAuthorityID(ctx context.Context, authorityId uint) (uint, error) { return uc.repo.GetParentAuthorityID(ctx, authorityId) } // 辅助函数 func uintToStr(n uint) string { return strconv.FormatUint(uint64(n), 10) }