kra/internal/biz/system/api.go

314 lines
7.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()
}