314 lines
7.7 KiB
Go
314 lines
7.7 KiB
Go
package system
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"strings"
|
||
)
|
||
|
||
// Api API实体
|
||
type Api struct {
|
||
ID uint
|
||
Path string
|
||
Description string
|
||
ApiGroup string
|
||
Method string
|
||
}
|
||
|
||
// IgnoreApi 忽略的API
|
||
type IgnoreApi struct {
|
||
Path string
|
||
Method string
|
||
}
|
||
|
||
// CasbinRule Casbin规则
|
||
type CasbinRule struct {
|
||
Path string
|
||
Method string
|
||
}
|
||
|
||
// ApiRepo API仓储接口
|
||
type ApiRepo interface {
|
||
Create(ctx context.Context, api *Api) error
|
||
Update(ctx context.Context, api *Api) error
|
||
Delete(ctx context.Context, id uint) error
|
||
DeleteByIds(ctx context.Context, ids []uint) error
|
||
FindByID(ctx context.Context, id uint) (*Api, error)
|
||
FindByPathMethod(ctx context.Context, path, method string) (*Api, error)
|
||
List(ctx context.Context, page, pageSize int, filters map[string]string, orderKey string, desc bool) ([]*Api, int64, error)
|
||
FindAll(ctx context.Context) ([]*Api, error)
|
||
FindByIds(ctx context.Context, ids []uint) ([]*Api, error)
|
||
GetApiGroups(ctx context.Context) ([]string, map[string]string, error)
|
||
CreateIgnoreApi(ctx context.Context, path, method string) error
|
||
DeleteIgnoreApi(ctx context.Context, path, method string) error
|
||
FindAllIgnoreApis(ctx context.Context) ([]*IgnoreApi, error)
|
||
}
|
||
|
||
// CasbinRepo Casbin仓储接口
|
||
type CasbinRepo interface {
|
||
ClearCasbin(v int, p ...string) bool
|
||
UpdateCasbinApi(oldPath, newPath, oldMethod, newMethod string) error
|
||
GetPolicyPathByAuthorityId(authorityId uint) []CasbinRule
|
||
FreshCasbin() error
|
||
AddPolicies(rules [][]string) error
|
||
UpdateCasbin(adminAuthorityID, authorityId uint, casbinInfos []CasbinRule) error
|
||
RemoveFilteredPolicy(authorityId string) error
|
||
}
|
||
|
||
// ApiUsecase API用例
|
||
type ApiUsecase struct {
|
||
repo ApiRepo
|
||
casbinRepo CasbinRepo
|
||
authRepo AuthorityRepo
|
||
useStrictAuth bool
|
||
routers []RouterInfo // 内存中的路由信息
|
||
}
|
||
|
||
// RouterInfo 路由信息
|
||
type RouterInfo struct {
|
||
Path string
|
||
Method string
|
||
}
|
||
|
||
// NewApiUsecase 创建API用例
|
||
func NewApiUsecase(repo ApiRepo, casbinRepo CasbinRepo, authRepo AuthorityRepo, useStrictAuth bool) *ApiUsecase {
|
||
return &ApiUsecase{
|
||
repo: repo,
|
||
casbinRepo: casbinRepo,
|
||
authRepo: authRepo,
|
||
useStrictAuth: useStrictAuth,
|
||
routers: make([]RouterInfo, 0),
|
||
}
|
||
}
|
||
|
||
// SetRouters 设置内存中的路由信息
|
||
func (uc *ApiUsecase) SetRouters(routers []RouterInfo) {
|
||
uc.routers = routers
|
||
}
|
||
|
||
// CreateApi 创建API
|
||
func (uc *ApiUsecase) CreateApi(ctx context.Context, api *Api) error {
|
||
return uc.repo.Create(ctx, api)
|
||
}
|
||
|
||
// UpdateApi 更新API
|
||
func (uc *ApiUsecase) UpdateApi(ctx context.Context, api *Api) error {
|
||
// 获取旧记录
|
||
old, err := uc.repo.FindByID(ctx, api.ID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 如果路径或方法变更,更新Casbin
|
||
if old.Path != api.Path || old.Method != api.Method {
|
||
if err := uc.casbinRepo.UpdateCasbinApi(old.Path, api.Path, old.Method, api.Method); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return uc.repo.Update(ctx, api)
|
||
}
|
||
|
||
// DeleteApi 删除API
|
||
func (uc *ApiUsecase) DeleteApi(ctx context.Context, id uint) error {
|
||
api, err := uc.repo.FindByID(ctx, id)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
if err := uc.repo.Delete(ctx, id); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 清除Casbin规则
|
||
uc.casbinRepo.ClearCasbin(1, api.Path, api.Method)
|
||
return nil
|
||
}
|
||
|
||
// DeleteApisByIds 批量删除API
|
||
func (uc *ApiUsecase) DeleteApisByIds(ctx context.Context, ids []uint) error {
|
||
apis, err := uc.repo.FindByIds(ctx, ids)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
if err := uc.repo.DeleteByIds(ctx, ids); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 清除Casbin规则
|
||
for _, api := range apis {
|
||
uc.casbinRepo.ClearCasbin(1, api.Path, api.Method)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// GetApiById 根据ID获取API
|
||
func (uc *ApiUsecase) GetApiById(ctx context.Context, id uint) (*Api, error) {
|
||
return uc.repo.FindByID(ctx, id)
|
||
}
|
||
|
||
// GetAPIInfoList 分页获取API列表
|
||
func (uc *ApiUsecase) GetAPIInfoList(ctx context.Context, page, pageSize int, filters map[string]string, orderKey string, desc bool) ([]*Api, int64, error) {
|
||
// 验证排序字段
|
||
if orderKey != "" {
|
||
orderMap := map[string]bool{"id": true, "path": true, "api_group": true, "description": true, "method": true}
|
||
if !orderMap[orderKey] {
|
||
return nil, 0, errors.New("非法的排序字段: " + orderKey)
|
||
}
|
||
}
|
||
return uc.repo.List(ctx, page, pageSize, filters, orderKey, desc)
|
||
}
|
||
|
||
// GetAllApis 获取所有API(根据权限过滤)
|
||
func (uc *ApiUsecase) GetAllApis(ctx context.Context, authorityID uint) ([]*Api, error) {
|
||
parentAuthorityID, err := uc.authRepo.GetParentAuthorityID(ctx, authorityID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
apis, err := uc.repo.FindAll(ctx)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 如果是顶级角色或不使用严格权限,返回所有API
|
||
if parentAuthorityID == 0 || !uc.useStrictAuth {
|
||
return apis, nil
|
||
}
|
||
|
||
// 根据Casbin规则过滤
|
||
paths := uc.casbinRepo.GetPolicyPathByAuthorityId(authorityID)
|
||
var authApis []*Api
|
||
for _, api := range apis {
|
||
for _, rule := range paths {
|
||
if rule.Path == api.Path && rule.Method == api.Method {
|
||
authApis = append(authApis, api)
|
||
break
|
||
}
|
||
}
|
||
}
|
||
return authApis, nil
|
||
}
|
||
|
||
// GetApiGroups 获取API分组
|
||
func (uc *ApiUsecase) GetApiGroups(ctx context.Context) ([]string, map[string]string, error) {
|
||
return uc.repo.GetApiGroups(ctx)
|
||
}
|
||
|
||
// SyncApi 同步API
|
||
func (uc *ApiUsecase) SyncApi(ctx context.Context) (newApis, deleteApis, ignoreApis []*Api, err error) {
|
||
newApis = make([]*Api, 0)
|
||
deleteApis = make([]*Api, 0)
|
||
ignoreApis = make([]*Api, 0)
|
||
|
||
// 获取数据库中的API
|
||
apis, err := uc.repo.FindAll(ctx)
|
||
if err != nil {
|
||
return nil, nil, nil, err
|
||
}
|
||
|
||
// 获取忽略的API
|
||
ignores, err := uc.repo.FindAllIgnoreApis(ctx)
|
||
if err != nil {
|
||
return nil, nil, nil, err
|
||
}
|
||
|
||
for _, ig := range ignores {
|
||
ignoreApis = append(ignoreApis, &Api{
|
||
Path: ig.Path,
|
||
Method: ig.Method,
|
||
})
|
||
}
|
||
|
||
// 过滤掉忽略的路由
|
||
var cacheApis []*Api
|
||
for _, r := range uc.routers {
|
||
ignored := false
|
||
for _, ig := range ignores {
|
||
if ig.Path == r.Path && ig.Method == r.Method {
|
||
ignored = true
|
||
break
|
||
}
|
||
}
|
||
if !ignored {
|
||
cacheApis = append(cacheApis, &Api{
|
||
Path: r.Path,
|
||
Method: r.Method,
|
||
})
|
||
}
|
||
}
|
||
|
||
// 对比:内存中有但数据库没有的 -> 新增
|
||
for _, cache := range cacheApis {
|
||
found := false
|
||
for _, api := range apis {
|
||
if cache.Path == api.Path && cache.Method == api.Method {
|
||
found = true
|
||
break
|
||
}
|
||
}
|
||
if !found {
|
||
newApis = append(newApis, &Api{
|
||
Path: cache.Path,
|
||
Method: cache.Method,
|
||
})
|
||
}
|
||
}
|
||
|
||
// 对比:数据库有但内存没有的 -> 删除
|
||
for _, api := range apis {
|
||
found := false
|
||
for _, cache := range cacheApis {
|
||
if cache.Path == api.Path && cache.Method == api.Method {
|
||
found = true
|
||
break
|
||
}
|
||
}
|
||
if !found {
|
||
deleteApis = append(deleteApis, api)
|
||
}
|
||
}
|
||
|
||
return newApis, deleteApis, ignoreApis, nil
|
||
}
|
||
|
||
// IgnoreApi 忽略/取消忽略API
|
||
func (uc *ApiUsecase) IgnoreApi(ctx context.Context, path, method string, flag bool) error {
|
||
if flag {
|
||
return uc.repo.CreateIgnoreApi(ctx, path, method)
|
||
}
|
||
return uc.repo.DeleteIgnoreApi(ctx, path, method)
|
||
}
|
||
|
||
// EnterSyncApi 确认同步API
|
||
func (uc *ApiUsecase) EnterSyncApi(ctx context.Context, newApis, deleteApis []*Api) error {
|
||
// 创建新API
|
||
for _, api := range newApis {
|
||
if err := uc.repo.Create(ctx, api); err != nil {
|
||
// 忽略重复错误
|
||
if !strings.Contains(err.Error(), "存在相同api") {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
|
||
// 删除旧API
|
||
for _, api := range deleteApis {
|
||
uc.casbinRepo.ClearCasbin(1, api.Path, api.Method)
|
||
existing, err := uc.repo.FindByPathMethod(ctx, api.Path, api.Method)
|
||
if err == nil && existing != nil {
|
||
if err := uc.repo.Delete(ctx, existing.ID); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// FreshCasbin 刷新Casbin缓存
|
||
func (uc *ApiUsecase) FreshCasbin() error {
|
||
return uc.casbinRepo.FreshCasbin()
|
||
}
|