任务三
This commit is contained in:
parent
a01fecdc11
commit
7df0450b1e
|
|
@ -0,0 +1,310 @@
|
|||
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
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// BaseMenu 基础菜单实体
|
||||
type BaseMenu struct {
|
||||
ID uint
|
||||
ParentId uint
|
||||
Path string
|
||||
Name string
|
||||
Hidden bool
|
||||
Component string
|
||||
Sort int
|
||||
ActiveName string
|
||||
KeepAlive bool
|
||||
DefaultMenu bool
|
||||
Title string
|
||||
Icon string
|
||||
CloseTab bool
|
||||
TransitionType string
|
||||
Parameters []*MenuParameter
|
||||
MenuBtn []*MenuBtn
|
||||
Children []*BaseMenu
|
||||
}
|
||||
|
||||
// MenuParameter 菜单参数
|
||||
type MenuParameter struct {
|
||||
ID uint
|
||||
SysBaseMenuID uint
|
||||
Type string
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
// MenuBtn 菜单按钮
|
||||
type MenuBtn struct {
|
||||
ID uint
|
||||
SysBaseMenuID uint
|
||||
Name string
|
||||
Desc string
|
||||
}
|
||||
|
||||
// Menu 菜单(带权限)
|
||||
type Menu struct {
|
||||
BaseMenu
|
||||
AuthorityId uint
|
||||
MenuId uint
|
||||
Btns map[string]uint
|
||||
Children []*Menu
|
||||
}
|
||||
|
||||
// AuthorityBtn 角色按钮权限
|
||||
type AuthorityBtn struct {
|
||||
AuthorityId uint
|
||||
SysMenuID uint
|
||||
SysBaseMenuBtnID uint
|
||||
BtnName string
|
||||
}
|
||||
|
||||
// MenuRepo 菜单仓储接口
|
||||
type MenuRepo interface {
|
||||
CreateBaseMenu(ctx context.Context, menu *BaseMenu) error
|
||||
UpdateBaseMenu(ctx context.Context, menu *BaseMenu) error
|
||||
DeleteBaseMenu(ctx context.Context, id uint) error
|
||||
FindBaseMenuByID(ctx context.Context, id uint) (*BaseMenu, error)
|
||||
FindAllBaseMenus(ctx context.Context) ([]*BaseMenu, error)
|
||||
FindBaseMenusByIds(ctx context.Context, ids []string) ([]*BaseMenu, error)
|
||||
FindAuthorityMenuIds(ctx context.Context, authorityId uint) ([]string, error)
|
||||
FindAuthorityBtns(ctx context.Context, authorityId uint) ([]*AuthorityBtn, error)
|
||||
SetMenuAuthority(ctx context.Context, authorityId uint, menuIds []uint) error
|
||||
}
|
||||
|
||||
// MenuUsecase 菜单用例
|
||||
type MenuUsecase struct {
|
||||
repo MenuRepo
|
||||
authRepo AuthorityRepo
|
||||
useStrictAuth bool
|
||||
}
|
||||
|
||||
// NewMenuUsecase 创建菜单用例
|
||||
func NewMenuUsecase(repo MenuRepo, authRepo AuthorityRepo, useStrictAuth bool) *MenuUsecase {
|
||||
return &MenuUsecase{
|
||||
repo: repo,
|
||||
authRepo: authRepo,
|
||||
useStrictAuth: useStrictAuth,
|
||||
}
|
||||
}
|
||||
|
||||
// AddBaseMenu 添加基础菜单
|
||||
func (uc *MenuUsecase) AddBaseMenu(ctx context.Context, menu *BaseMenu) error {
|
||||
return uc.repo.CreateBaseMenu(ctx, menu)
|
||||
}
|
||||
|
||||
// UpdateBaseMenu 更新基础菜单
|
||||
func (uc *MenuUsecase) UpdateBaseMenu(ctx context.Context, menu *BaseMenu) error {
|
||||
return uc.repo.UpdateBaseMenu(ctx, menu)
|
||||
}
|
||||
|
||||
// DeleteBaseMenu 删除基础菜单
|
||||
func (uc *MenuUsecase) DeleteBaseMenu(ctx context.Context, id uint) error {
|
||||
return uc.repo.DeleteBaseMenu(ctx, id)
|
||||
}
|
||||
|
||||
// GetBaseMenuById 根据ID获取基础菜单
|
||||
func (uc *MenuUsecase) GetBaseMenuById(ctx context.Context, id uint) (*BaseMenu, error) {
|
||||
return uc.repo.FindBaseMenuByID(ctx, id)
|
||||
}
|
||||
|
||||
// GetMenuTree 获取动态菜单树
|
||||
func (uc *MenuUsecase) GetMenuTree(ctx context.Context, authorityId uint) ([]*Menu, error) {
|
||||
treeMap, err := uc.getMenuTreeMap(ctx, authorityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
menus := treeMap[0]
|
||||
for i := range menus {
|
||||
uc.getChildrenList(menus[i], treeMap)
|
||||
}
|
||||
return menus, nil
|
||||
}
|
||||
|
||||
func (uc *MenuUsecase) getMenuTreeMap(ctx context.Context, authorityId uint) (map[uint][]*Menu, error) {
|
||||
treeMap := make(map[uint][]*Menu)
|
||||
|
||||
// 获取角色的菜单ID
|
||||
menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, authorityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取基础菜单
|
||||
baseMenus, err := uc.repo.FindBaseMenusByIds(ctx, menuIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取按钮权限
|
||||
btns, err := uc.repo.FindAuthorityBtns(ctx, authorityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 构建按钮map
|
||||
btnMap := make(map[uint]map[string]uint)
|
||||
for _, b := range btns {
|
||||
if btnMap[b.SysMenuID] == nil {
|
||||
btnMap[b.SysMenuID] = make(map[string]uint)
|
||||
}
|
||||
btnMap[b.SysMenuID][b.BtnName] = authorityId
|
||||
}
|
||||
|
||||
// 构建菜单树
|
||||
for _, base := range baseMenus {
|
||||
menu := &Menu{
|
||||
BaseMenu: *base,
|
||||
AuthorityId: authorityId,
|
||||
MenuId: base.ID,
|
||||
Btns: btnMap[base.ID],
|
||||
}
|
||||
treeMap[base.ParentId] = append(treeMap[base.ParentId], menu)
|
||||
}
|
||||
|
||||
return treeMap, nil
|
||||
}
|
||||
|
||||
func (uc *MenuUsecase) getChildrenList(menu *Menu, treeMap map[uint][]*Menu) {
|
||||
menu.Children = treeMap[menu.MenuId]
|
||||
for i := range menu.Children {
|
||||
uc.getChildrenList(menu.Children[i], treeMap)
|
||||
}
|
||||
}
|
||||
|
||||
// GetBaseMenuTree 获取基础菜单树
|
||||
func (uc *MenuUsecase) GetBaseMenuTree(ctx context.Context, authorityID uint) ([]*BaseMenu, error) {
|
||||
treeMap, err := uc.getBaseMenuTreeMap(ctx, authorityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
menus := treeMap[0]
|
||||
for i := range menus {
|
||||
uc.getBaseChildrenList(menus[i], treeMap)
|
||||
}
|
||||
return menus, nil
|
||||
}
|
||||
|
||||
func (uc *MenuUsecase) getBaseMenuTreeMap(ctx context.Context, authorityID uint) (map[uint][]*BaseMenu, error) {
|
||||
parentAuthorityID, err := uc.authRepo.GetParentAuthorityID(ctx, authorityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var allMenus []*BaseMenu
|
||||
treeMap := make(map[uint][]*BaseMenu)
|
||||
|
||||
// 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选
|
||||
if uc.useStrictAuth && parentAuthorityID != 0 {
|
||||
menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, authorityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allMenus, err = uc.repo.FindBaseMenusByIds(ctx, menuIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
allMenus, err = uc.repo.FindAllBaseMenus(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range allMenus {
|
||||
treeMap[m.ParentId] = append(treeMap[m.ParentId], m)
|
||||
}
|
||||
return treeMap, nil
|
||||
}
|
||||
|
||||
func (uc *MenuUsecase) getBaseChildrenList(menu *BaseMenu, treeMap map[uint][]*BaseMenu) {
|
||||
menu.Children = treeMap[menu.ID]
|
||||
for i := range menu.Children {
|
||||
uc.getBaseChildrenList(menu.Children[i], treeMap)
|
||||
}
|
||||
}
|
||||
|
||||
// GetInfoList 获取菜单列表(树形)
|
||||
func (uc *MenuUsecase) GetInfoList(ctx context.Context, authorityID uint) ([]*BaseMenu, error) {
|
||||
return uc.GetBaseMenuTree(ctx, authorityID)
|
||||
}
|
||||
|
||||
// AddMenuAuthority 为角色增加菜单权限
|
||||
func (uc *MenuUsecase) AddMenuAuthority(ctx context.Context, menus []*BaseMenu, adminAuthorityID, authorityId uint) error {
|
||||
// 检查权限
|
||||
parentAuthorityID, err := uc.authRepo.GetParentAuthorityID(ctx, adminAuthorityID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选
|
||||
if uc.useStrictAuth && parentAuthorityID != 0 {
|
||||
menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, adminAuthorityID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, menu := range menus {
|
||||
hasMenu := false
|
||||
idStr := strconv.Itoa(int(menu.ID))
|
||||
for _, mid := range menuIds {
|
||||
if idStr == mid {
|
||||
hasMenu = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasMenu {
|
||||
return errors.New("添加失败,请勿跨级操作")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置菜单权限
|
||||
menuIds := make([]uint, len(menus))
|
||||
for i, m := range menus {
|
||||
menuIds[i] = m.ID
|
||||
}
|
||||
return uc.repo.SetMenuAuthority(ctx, authorityId, menuIds)
|
||||
}
|
||||
|
||||
// GetMenuAuthority 获取角色的菜单权限
|
||||
func (uc *MenuUsecase) GetMenuAuthority(ctx context.Context, authorityId uint) ([]*Menu, error) {
|
||||
menuIds, err := uc.repo.FindAuthorityMenuIds(ctx, authorityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseMenus, err := uc.repo.FindBaseMenusByIds(ctx, menuIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
menus := make([]*Menu, len(baseMenus))
|
||||
for i, base := range baseMenus {
|
||||
menus[i] = &Menu{
|
||||
BaseMenu: *base,
|
||||
AuthorityId: authorityId,
|
||||
MenuId: base.ID,
|
||||
}
|
||||
}
|
||||
return menus, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"kra/internal/biz/system"
|
||||
"kra/internal/data/model"
|
||||
"kra/internal/data/query"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type apiRepo struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewApiRepo 创建API仓储
|
||||
func NewApiRepo(db *gorm.DB) system.ApiRepo {
|
||||
return &apiRepo{db: db}
|
||||
}
|
||||
|
||||
func (r *apiRepo) Create(ctx context.Context, api *system.Api) error {
|
||||
_, err := query.SysAPI.WithContext(ctx).Where(
|
||||
query.SysAPI.Path.Eq(api.Path),
|
||||
query.SysAPI.Method.Eq(api.Method),
|
||||
).First()
|
||||
if err == nil {
|
||||
return errors.New("存在相同api")
|
||||
}
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
m := toModelAPI(api)
|
||||
return query.SysAPI.WithContext(ctx).Create(m)
|
||||
}
|
||||
|
||||
func (r *apiRepo) Update(ctx context.Context, api *system.Api) error {
|
||||
old, err := query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.Eq(int64(api.ID))).First()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if safeString(old.Path) != api.Path || safeString(old.Method) != api.Method {
|
||||
dup, ferr := query.SysAPI.WithContext(ctx).Where(
|
||||
query.SysAPI.Path.Eq(api.Path),
|
||||
query.SysAPI.Method.Eq(api.Method),
|
||||
).First()
|
||||
if ferr == nil && dup.ID != int64(api.ID) {
|
||||
return errors.New("存在相同api路径")
|
||||
}
|
||||
if ferr != nil && !errors.Is(ferr, gorm.ErrRecordNotFound) {
|
||||
return ferr
|
||||
}
|
||||
}
|
||||
m := toModelAPI(api)
|
||||
_, err = query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.Eq(int64(api.ID))).Updates(m)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *apiRepo) Delete(ctx context.Context, id uint) error {
|
||||
_, err := query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.Eq(int64(id))).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *apiRepo) DeleteByIds(ctx context.Context, ids []uint) error {
|
||||
int64Ids := make([]int64, len(ids))
|
||||
for i, id := range ids {
|
||||
int64Ids[i] = int64(id)
|
||||
}
|
||||
_, err := query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.In(int64Ids...)).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *apiRepo) FindByID(ctx context.Context, id uint) (*system.Api, error) {
|
||||
m, err := query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.Eq(int64(id))).First()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toBizAPI(m), nil
|
||||
}
|
||||
|
||||
func (r *apiRepo) FindByPathMethod(ctx context.Context, path, method string) (*system.Api, error) {
|
||||
m, err := query.SysAPI.WithContext(ctx).Where(
|
||||
query.SysAPI.Path.Eq(path),
|
||||
query.SysAPI.Method.Eq(method),
|
||||
).First()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toBizAPI(m), nil
|
||||
}
|
||||
|
||||
func (r *apiRepo) List(ctx context.Context, page, pageSize int, filters map[string]string, orderKey string, desc bool) ([]*system.Api, int64, error) {
|
||||
q := query.SysAPI.WithContext(ctx)
|
||||
if v, ok := filters["path"]; ok && v != "" {
|
||||
q = q.Where(query.SysAPI.Path.Like("%" + v + "%"))
|
||||
}
|
||||
if v, ok := filters["description"]; ok && v != "" {
|
||||
q = q.Where(query.SysAPI.Description.Like("%" + v + "%"))
|
||||
}
|
||||
if v, ok := filters["method"]; ok && v != "" {
|
||||
q = q.Where(query.SysAPI.Method.Eq(v))
|
||||
}
|
||||
if v, ok := filters["api_group"]; ok && v != "" {
|
||||
q = q.Where(query.SysAPI.APIGroup.Eq(v))
|
||||
}
|
||||
|
||||
total, err := q.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
orderMap := map[string]bool{"id": true, "path": true, "api_group": true, "description": true, "method": true}
|
||||
if orderKey != "" && orderMap[orderKey] {
|
||||
switch orderKey {
|
||||
case "id":
|
||||
if desc {
|
||||
q = q.Order(query.SysAPI.ID.Desc())
|
||||
} else {
|
||||
q = q.Order(query.SysAPI.ID)
|
||||
}
|
||||
case "path":
|
||||
if desc {
|
||||
q = q.Order(query.SysAPI.Path.Desc())
|
||||
} else {
|
||||
q = q.Order(query.SysAPI.Path)
|
||||
}
|
||||
case "api_group":
|
||||
if desc {
|
||||
q = q.Order(query.SysAPI.APIGroup.Desc())
|
||||
} else {
|
||||
q = q.Order(query.SysAPI.APIGroup)
|
||||
}
|
||||
case "description":
|
||||
if desc {
|
||||
q = q.Order(query.SysAPI.Description.Desc())
|
||||
} else {
|
||||
q = q.Order(query.SysAPI.Description)
|
||||
}
|
||||
case "method":
|
||||
if desc {
|
||||
q = q.Order(query.SysAPI.Method.Desc())
|
||||
} else {
|
||||
q = q.Order(query.SysAPI.Method)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
q = q.Order(query.SysAPI.ID.Desc())
|
||||
}
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
list, err := q.Offset(offset).Limit(pageSize).Find()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
apis := make([]*system.Api, len(list))
|
||||
for i, m := range list {
|
||||
apis[i] = toBizAPI(m)
|
||||
}
|
||||
return apis, total, nil
|
||||
}
|
||||
|
||||
func (r *apiRepo) FindAll(ctx context.Context) ([]*system.Api, error) {
|
||||
list, err := query.SysAPI.WithContext(ctx).Order(query.SysAPI.ID.Desc()).Find()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apis := make([]*system.Api, len(list))
|
||||
for i, m := range list {
|
||||
apis[i] = toBizAPI(m)
|
||||
}
|
||||
return apis, nil
|
||||
}
|
||||
|
||||
func (r *apiRepo) FindByIds(ctx context.Context, ids []uint) ([]*system.Api, error) {
|
||||
int64Ids := make([]int64, len(ids))
|
||||
for i, id := range ids {
|
||||
int64Ids[i] = int64(id)
|
||||
}
|
||||
list, err := query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.In(int64Ids...)).Find()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apis := make([]*system.Api, len(list))
|
||||
for i, m := range list {
|
||||
apis[i] = toBizAPI(m)
|
||||
}
|
||||
return apis, nil
|
||||
}
|
||||
|
||||
func (r *apiRepo) GetApiGroups(ctx context.Context) ([]string, map[string]string, error) {
|
||||
list, err := query.SysAPI.WithContext(ctx).Find()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
groupMap := make(map[string]bool)
|
||||
apiGroupMap := make(map[string]string)
|
||||
var groups []string
|
||||
|
||||
for _, m := range list {
|
||||
path := safeString(m.Path)
|
||||
apiGroup := safeString(m.APIGroup)
|
||||
|
||||
if !groupMap[apiGroup] {
|
||||
groupMap[apiGroup] = true
|
||||
groups = append(groups, apiGroup)
|
||||
}
|
||||
|
||||
if len(path) > 1 {
|
||||
parts := strings.Split(path, "/")
|
||||
if len(parts) > 1 {
|
||||
apiGroupMap[parts[1]] = apiGroup
|
||||
}
|
||||
}
|
||||
}
|
||||
return groups, apiGroupMap, nil
|
||||
}
|
||||
|
||||
func (r *apiRepo) CreateIgnoreApi(ctx context.Context, path, method string) error {
|
||||
m := &model.SysIgnoreAPI{
|
||||
Path: &path,
|
||||
Method: &method,
|
||||
}
|
||||
return r.db.WithContext(ctx).Create(m).Error
|
||||
}
|
||||
|
||||
func (r *apiRepo) DeleteIgnoreApi(ctx context.Context, path, method string) error {
|
||||
return r.db.WithContext(ctx).Unscoped().Where("path = ? AND method = ?", path, method).Delete(&model.SysIgnoreAPI{}).Error
|
||||
}
|
||||
|
||||
func (r *apiRepo) FindAllIgnoreApis(ctx context.Context) ([]*system.IgnoreApi, error) {
|
||||
var list []model.SysIgnoreAPI
|
||||
if err := r.db.WithContext(ctx).Find(&list).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*system.IgnoreApi, len(list))
|
||||
for i, m := range list {
|
||||
result[i] = &system.IgnoreApi{
|
||||
Path: safeString(m.Path),
|
||||
Method: safeString(m.Method),
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 转换函数
|
||||
func toModelAPI(a *system.Api) *model.SysAPI {
|
||||
return &model.SysAPI{
|
||||
ID: int64(a.ID),
|
||||
Path: &a.Path,
|
||||
Description: &a.Description,
|
||||
APIGroup: &a.ApiGroup,
|
||||
Method: &a.Method,
|
||||
}
|
||||
}
|
||||
|
||||
func toBizAPI(m *model.SysAPI) *system.Api {
|
||||
return &system.Api{
|
||||
ID: uint(m.ID),
|
||||
Path: safeString(m.Path),
|
||||
Description: safeString(m.Description),
|
||||
ApiGroup: safeString(m.APIGroup),
|
||||
Method: safeString(m.Method),
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,485 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"kra/internal/biz/system"
|
||||
"kra/internal/data/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type menuRepo struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewMenuRepo 创建菜单仓储
|
||||
func NewMenuRepo(db *gorm.DB) system.MenuRepo {
|
||||
return &menuRepo{db: db}
|
||||
}
|
||||
|
||||
// CreateBaseMenu 创建基础菜单
|
||||
func (r *menuRepo) CreateBaseMenu(ctx context.Context, menu *system.BaseMenu) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// 检查name是否重复
|
||||
var count int64
|
||||
if err := tx.Model(&model.SysBaseMenu{}).Where("name = ?", menu.Name).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("存在重复name,请修改name")
|
||||
}
|
||||
|
||||
if menu.ParentId != 0 {
|
||||
// 检查父菜单是否存在
|
||||
var parentMenu model.SysBaseMenu
|
||||
if err := tx.First(&parentMenu, menu.ParentId).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("父菜单不存在")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查父菜单下现有子菜单数量
|
||||
var existingChildrenCount int64
|
||||
if err := tx.Model(&model.SysBaseMenu{}).Where("parent_id = ?", menu.ParentId).Count(&existingChildrenCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 如果父菜单原本是叶子菜单,需要清空其权限分配
|
||||
if existingChildrenCount == 0 {
|
||||
// 检查父菜单是否被其他角色设置为首页
|
||||
var defaultRouterCount int64
|
||||
if err := tx.Model(&model.SysAuthority{}).Where("default_router = ?", parentMenu.Name).Count(&defaultRouterCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if defaultRouterCount > 0 {
|
||||
return errors.New("父菜单已被其他角色的首页占用,请先释放父菜单的首页权限")
|
||||
}
|
||||
|
||||
// 清空父菜单的所有权限分配
|
||||
if err := tx.Where("sys_base_menu_id = ?", menu.ParentId).Delete(&model.SysAuthorityMenu{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建菜单
|
||||
m := toModelBaseMenu(menu)
|
||||
if err := tx.Create(m).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建参数
|
||||
if len(menu.Parameters) > 0 {
|
||||
for _, p := range menu.Parameters {
|
||||
param := &model.SysBaseMenuParameter{
|
||||
SysBaseMenuID: ptrInt64(int64(m.ID)),
|
||||
Type: &p.Type,
|
||||
Key: &p.Key,
|
||||
Value: &p.Value,
|
||||
}
|
||||
if err := tx.Create(param).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建按钮
|
||||
if len(menu.MenuBtn) > 0 {
|
||||
for _, b := range menu.MenuBtn {
|
||||
btn := &model.SysBaseMenuBtn{
|
||||
SysBaseMenuID: ptrInt64(int64(m.ID)),
|
||||
Name: &b.Name,
|
||||
Desc: &b.Desc,
|
||||
}
|
||||
if err := tx.Create(btn).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu.ID = uint(m.ID)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateBaseMenu 更新基础菜单
|
||||
func (r *menuRepo) UpdateBaseMenu(ctx context.Context, menu *system.BaseMenu) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var oldMenu model.SysBaseMenu
|
||||
if err := tx.Where("id = ?", menu.ID).First(&oldMenu).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查name是否重复
|
||||
if safeString(oldMenu.Name) != menu.Name {
|
||||
var count int64
|
||||
if err := tx.Model(&model.SysBaseMenu{}).Where("id <> ? AND name = ?", menu.ID, menu.Name).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("存在相同name修改失败")
|
||||
}
|
||||
}
|
||||
|
||||
// 删除旧参数和按钮
|
||||
if err := tx.Unscoped().Where("sys_base_menu_id = ?", menu.ID).Delete(&model.SysBaseMenuParameter{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Unscoped().Where("sys_base_menu_id = ?", menu.ID).Delete(&model.SysBaseMenuBtn{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建新参数
|
||||
if len(menu.Parameters) > 0 {
|
||||
for _, p := range menu.Parameters {
|
||||
param := &model.SysBaseMenuParameter{
|
||||
SysBaseMenuID: ptrInt64(int64(menu.ID)),
|
||||
Type: &p.Type,
|
||||
Key: &p.Key,
|
||||
Value: &p.Value,
|
||||
}
|
||||
if err := tx.Create(param).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新按钮
|
||||
if len(menu.MenuBtn) > 0 {
|
||||
for _, b := range menu.MenuBtn {
|
||||
btn := &model.SysBaseMenuBtn{
|
||||
SysBaseMenuID: ptrInt64(int64(menu.ID)),
|
||||
Name: &b.Name,
|
||||
Desc: &b.Desc,
|
||||
}
|
||||
if err := tx.Create(btn).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新菜单
|
||||
updates := map[string]any{
|
||||
"keep_alive": menu.KeepAlive,
|
||||
"close_tab": menu.CloseTab,
|
||||
"default_menu": menu.DefaultMenu,
|
||||
"parent_id": menu.ParentId,
|
||||
"path": menu.Path,
|
||||
"name": menu.Name,
|
||||
"hidden": menu.Hidden,
|
||||
"component": menu.Component,
|
||||
"title": menu.Title,
|
||||
"active_name": menu.ActiveName,
|
||||
"icon": menu.Icon,
|
||||
"sort": menu.Sort,
|
||||
"transition_type": menu.TransitionType,
|
||||
}
|
||||
return tx.Model(&oldMenu).Updates(updates).Error
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteBaseMenu 删除基础菜单
|
||||
func (r *menuRepo) DeleteBaseMenu(ctx context.Context, id uint) error {
|
||||
// 检查是否有子菜单
|
||||
var childCount int64
|
||||
if err := r.db.WithContext(ctx).Model(&model.SysBaseMenu{}).Where("parent_id = ?", id).Count(&childCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if childCount > 0 {
|
||||
return errors.New("此菜单存在子菜单不可删除")
|
||||
}
|
||||
|
||||
// 获取菜单信息
|
||||
var menu model.SysBaseMenu
|
||||
if err := r.db.WithContext(ctx).First(&menu, id).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("记录不存在")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查是否有角色将此菜单作为首页
|
||||
var defaultRouterCount int64
|
||||
if err := r.db.WithContext(ctx).Model(&model.SysAuthority{}).Where("default_router = ?", menu.Name).Count(&defaultRouterCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if defaultRouterCount > 0 {
|
||||
return errors.New("此菜单有角色正在作为首页,不可删除")
|
||||
}
|
||||
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Delete(&model.SysBaseMenu{}, "id = ?", id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Delete(&model.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Delete(&model.SysBaseMenuBtn{}, "sys_base_menu_id = ?", id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Delete(&model.SysAuthorityBtn{}, "sys_menu_id = ?", id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Delete(&model.SysAuthorityMenu{}, "sys_base_menu_id = ?", id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// FindBaseMenuByID 根据ID获取基础菜单
|
||||
func (r *menuRepo) FindBaseMenuByID(ctx context.Context, id uint) (*system.BaseMenu, error) {
|
||||
var menu model.SysBaseMenu
|
||||
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&menu).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取参数
|
||||
var params []model.SysBaseMenuParameter
|
||||
r.db.WithContext(ctx).Where("sys_base_menu_id = ?", id).Find(¶ms)
|
||||
|
||||
// 获取按钮
|
||||
var btns []model.SysBaseMenuBtn
|
||||
r.db.WithContext(ctx).Where("sys_base_menu_id = ?", id).Find(&btns)
|
||||
|
||||
return toBizBaseMenu(&menu, params, btns), nil
|
||||
}
|
||||
|
||||
// FindAllBaseMenus 获取所有基础菜单
|
||||
func (r *menuRepo) FindAllBaseMenus(ctx context.Context) ([]*system.BaseMenu, error) {
|
||||
var menus []model.SysBaseMenu
|
||||
if err := r.db.WithContext(ctx).Order("sort").Find(&menus).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取所有参数
|
||||
var allParams []model.SysBaseMenuParameter
|
||||
r.db.WithContext(ctx).Find(&allParams)
|
||||
paramMap := make(map[int64][]model.SysBaseMenuParameter)
|
||||
for _, p := range allParams {
|
||||
if p.SysBaseMenuID != nil {
|
||||
paramMap[*p.SysBaseMenuID] = append(paramMap[*p.SysBaseMenuID], p)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有按钮
|
||||
var allBtns []model.SysBaseMenuBtn
|
||||
r.db.WithContext(ctx).Find(&allBtns)
|
||||
btnMap := make(map[int64][]model.SysBaseMenuBtn)
|
||||
for _, b := range allBtns {
|
||||
if b.SysBaseMenuID != nil {
|
||||
btnMap[*b.SysBaseMenuID] = append(btnMap[*b.SysBaseMenuID], b)
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]*system.BaseMenu, len(menus))
|
||||
for i, m := range menus {
|
||||
result[i] = toBizBaseMenu(&m, paramMap[m.ID], btnMap[m.ID])
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// FindBaseMenusByIds 根据ID列表获取基础菜单
|
||||
func (r *menuRepo) FindBaseMenusByIds(ctx context.Context, ids []string) ([]*system.BaseMenu, error) {
|
||||
var menus []model.SysBaseMenu
|
||||
if err := r.db.WithContext(ctx).Where("id in (?)", ids).Order("sort").Find(&menus).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取参数
|
||||
var allParams []model.SysBaseMenuParameter
|
||||
r.db.WithContext(ctx).Where("sys_base_menu_id in (?)", ids).Find(&allParams)
|
||||
paramMap := make(map[int64][]model.SysBaseMenuParameter)
|
||||
for _, p := range allParams {
|
||||
if p.SysBaseMenuID != nil {
|
||||
paramMap[*p.SysBaseMenuID] = append(paramMap[*p.SysBaseMenuID], p)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取按钮
|
||||
var allBtns []model.SysBaseMenuBtn
|
||||
r.db.WithContext(ctx).Where("sys_base_menu_id in (?)", ids).Find(&allBtns)
|
||||
btnMap := make(map[int64][]model.SysBaseMenuBtn)
|
||||
for _, b := range allBtns {
|
||||
if b.SysBaseMenuID != nil {
|
||||
btnMap[*b.SysBaseMenuID] = append(btnMap[*b.SysBaseMenuID], b)
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]*system.BaseMenu, len(menus))
|
||||
for i, m := range menus {
|
||||
result[i] = toBizBaseMenu(&m, paramMap[m.ID], btnMap[m.ID])
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// FindAuthorityMenuIds 获取角色的菜单ID列表
|
||||
func (r *menuRepo) FindAuthorityMenuIds(ctx context.Context, authorityId uint) ([]string, error) {
|
||||
var authorityMenus []model.SysAuthorityMenu
|
||||
if err := r.db.WithContext(ctx).Where("sys_authority_authority_id = ?", authorityId).Find(&authorityMenus).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
menuIds := make([]string, len(authorityMenus))
|
||||
for i, am := range authorityMenus {
|
||||
menuIds[i] = strconv.FormatInt(am.SysBaseMenuID, 10)
|
||||
}
|
||||
return menuIds, nil
|
||||
}
|
||||
|
||||
// FindAuthorityBtns 获取角色的按钮权限
|
||||
func (r *menuRepo) FindAuthorityBtns(ctx context.Context, authorityId uint) ([]*system.AuthorityBtn, error) {
|
||||
var btns []model.SysAuthorityBtn
|
||||
if err := r.db.WithContext(ctx).Where("authority_id = ?", authorityId).Find(&btns).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取按钮名称
|
||||
var btnIds []int64
|
||||
for _, b := range btns {
|
||||
if b.SysBaseMenuBtnID != nil {
|
||||
btnIds = append(btnIds, *b.SysBaseMenuBtnID)
|
||||
}
|
||||
}
|
||||
|
||||
btnNameMap := make(map[int64]string)
|
||||
if len(btnIds) > 0 {
|
||||
var menuBtns []model.SysBaseMenuBtn
|
||||
r.db.WithContext(ctx).Where("id in (?)", btnIds).Find(&menuBtns)
|
||||
for _, mb := range menuBtns {
|
||||
btnNameMap[mb.ID] = safeString(mb.Name)
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]*system.AuthorityBtn, len(btns))
|
||||
for i, b := range btns {
|
||||
result[i] = &system.AuthorityBtn{}
|
||||
if b.AuthorityID != nil {
|
||||
result[i].AuthorityId = uint(*b.AuthorityID)
|
||||
}
|
||||
if b.SysMenuID != nil {
|
||||
result[i].SysMenuID = uint(*b.SysMenuID)
|
||||
}
|
||||
if b.SysBaseMenuBtnID != nil {
|
||||
result[i].SysBaseMenuBtnID = uint(*b.SysBaseMenuBtnID)
|
||||
result[i].BtnName = btnNameMap[*b.SysBaseMenuBtnID]
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SetMenuAuthority 设置角色菜单权限
|
||||
func (r *menuRepo) SetMenuAuthority(ctx context.Context, authorityId uint, menuIds []uint) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// 删除旧权限
|
||||
if err := tx.Where("sys_authority_authority_id = ?", authorityId).Delete(&model.SysAuthorityMenu{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建新权限
|
||||
for _, menuId := range menuIds {
|
||||
am := &model.SysAuthorityMenu{
|
||||
SysAuthorityAuthorityID: int64(authorityId),
|
||||
SysBaseMenuID: int64(menuId),
|
||||
}
|
||||
if err := tx.Create(am).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// 转换函数
|
||||
func toModelBaseMenu(m *system.BaseMenu) *model.SysBaseMenu {
|
||||
parentId := int64(m.ParentId)
|
||||
sort := int64(m.Sort)
|
||||
|
||||
return &model.SysBaseMenu{
|
||||
ID: int64(m.ID),
|
||||
ParentID: &parentId,
|
||||
Path: &m.Path,
|
||||
Name: &m.Name,
|
||||
Hidden: &m.Hidden,
|
||||
Component: &m.Component,
|
||||
Sort: &sort,
|
||||
ActiveName: &m.ActiveName,
|
||||
KeepAlive: &m.KeepAlive,
|
||||
DefaultMenu: &m.DefaultMenu,
|
||||
Title: &m.Title,
|
||||
Icon: &m.Icon,
|
||||
CloseTab: &m.CloseTab,
|
||||
TransitionType: &m.TransitionType,
|
||||
}
|
||||
}
|
||||
|
||||
func toBizBaseMenu(m *model.SysBaseMenu, params []model.SysBaseMenuParameter, btns []model.SysBaseMenuBtn) *system.BaseMenu {
|
||||
menu := &system.BaseMenu{
|
||||
ID: uint(m.ID),
|
||||
Path: safeString(m.Path),
|
||||
Name: safeString(m.Name),
|
||||
Component: safeString(m.Component),
|
||||
ActiveName: safeString(m.ActiveName),
|
||||
Title: safeString(m.Title),
|
||||
Icon: safeString(m.Icon),
|
||||
TransitionType: safeString(m.TransitionType),
|
||||
}
|
||||
if m.ParentID != nil {
|
||||
menu.ParentId = uint(*m.ParentID)
|
||||
}
|
||||
if m.Sort != nil {
|
||||
menu.Sort = int(*m.Sort)
|
||||
}
|
||||
if m.Hidden != nil {
|
||||
menu.Hidden = *m.Hidden
|
||||
}
|
||||
if m.KeepAlive != nil {
|
||||
menu.KeepAlive = *m.KeepAlive
|
||||
}
|
||||
if m.DefaultMenu != nil {
|
||||
menu.DefaultMenu = *m.DefaultMenu
|
||||
}
|
||||
if m.CloseTab != nil {
|
||||
menu.CloseTab = *m.CloseTab
|
||||
}
|
||||
|
||||
// 转换参数
|
||||
if len(params) > 0 {
|
||||
menu.Parameters = make([]*system.MenuParameter, len(params))
|
||||
for i, p := range params {
|
||||
menu.Parameters[i] = &system.MenuParameter{
|
||||
ID: uint(p.ID),
|
||||
Type: safeString(p.Type),
|
||||
Key: safeString(p.Key),
|
||||
Value: safeString(p.Value),
|
||||
}
|
||||
if p.SysBaseMenuID != nil {
|
||||
menu.Parameters[i].SysBaseMenuID = uint(*p.SysBaseMenuID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 转换按钮
|
||||
if len(btns) > 0 {
|
||||
menu.MenuBtn = make([]*system.MenuBtn, len(btns))
|
||||
for i, b := range btns {
|
||||
menu.MenuBtn[i] = &system.MenuBtn{
|
||||
ID: uint(b.ID),
|
||||
Name: safeString(b.Name),
|
||||
Desc: safeString(b.Desc),
|
||||
}
|
||||
if b.SysBaseMenuID != nil {
|
||||
menu.MenuBtn[i].SysBaseMenuID = uint(*b.SysBaseMenuID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return menu
|
||||
}
|
||||
|
||||
func ptrInt64(v int64) *int64 {
|
||||
return &v
|
||||
}
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"kra/internal/biz/system"
|
||||
"kra/internal/server/middleware"
|
||||
|
||||
"github.com/go-kratos/kratos/v2/errors"
|
||||
"github.com/go-kratos/kratos/v2/transport/http"
|
||||
)
|
||||
|
||||
// ApiService API服务
|
||||
type ApiService struct {
|
||||
uc *system.ApiUsecase
|
||||
}
|
||||
|
||||
// NewApiService 创建API服务
|
||||
func NewApiService(uc *system.ApiUsecase) *ApiService {
|
||||
return &ApiService{uc: uc}
|
||||
}
|
||||
|
||||
// ApiInfo API信息
|
||||
type ApiInfo struct {
|
||||
ID uint `json:"ID"`
|
||||
Path string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
ApiGroup string `json:"apiGroup"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
// CreateApiRequest 创建API请求
|
||||
type CreateApiRequest struct {
|
||||
Path string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
ApiGroup string `json:"apiGroup"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
// CreateApi 创建API
|
||||
func (s *ApiService) CreateApi(ctx context.Context, req *CreateApiRequest) error {
|
||||
api := &system.Api{
|
||||
Path: req.Path,
|
||||
Description: req.Description,
|
||||
ApiGroup: req.ApiGroup,
|
||||
Method: req.Method,
|
||||
}
|
||||
return s.uc.CreateApi(ctx, api)
|
||||
}
|
||||
|
||||
// UpdateApiRequest 更新API请求
|
||||
type UpdateApiRequest struct {
|
||||
ID uint `json:"ID"`
|
||||
Path string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
ApiGroup string `json:"apiGroup"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
// UpdateApi 更新API
|
||||
func (s *ApiService) UpdateApi(ctx context.Context, req *UpdateApiRequest) error {
|
||||
api := &system.Api{
|
||||
ID: req.ID,
|
||||
Path: req.Path,
|
||||
Description: req.Description,
|
||||
ApiGroup: req.ApiGroup,
|
||||
Method: req.Method,
|
||||
}
|
||||
return s.uc.UpdateApi(ctx, api)
|
||||
}
|
||||
|
||||
// DeleteApi 删除API
|
||||
func (s *ApiService) DeleteApi(ctx context.Context, id uint) error {
|
||||
return s.uc.DeleteApi(ctx, id)
|
||||
}
|
||||
|
||||
// DeleteApisByIdsRequest 批量删除API请求
|
||||
type DeleteApisByIdsRequest struct {
|
||||
Ids []uint `json:"ids"`
|
||||
}
|
||||
|
||||
// DeleteApisByIds 批量删除API
|
||||
func (s *ApiService) DeleteApisByIds(ctx context.Context, req *DeleteApisByIdsRequest) error {
|
||||
return s.uc.DeleteApisByIds(ctx, req.Ids)
|
||||
}
|
||||
|
||||
// GetApiById 根据ID获取API
|
||||
func (s *ApiService) GetApiById(ctx context.Context, id uint) (*ApiInfo, error) {
|
||||
api, err := s.uc.GetApiById(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toApiInfo(api), nil
|
||||
}
|
||||
|
||||
// GetApiListRequest 获取API列表请求
|
||||
type GetApiListRequest struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
Path string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
Method string `json:"method"`
|
||||
ApiGroup string `json:"apiGroup"`
|
||||
OrderKey string `json:"orderKey"`
|
||||
Desc bool `json:"desc"`
|
||||
}
|
||||
|
||||
// GetApiListResponse 获取API列表响应
|
||||
type GetApiListResponse struct {
|
||||
List []*ApiInfo `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
|
||||
// GetApiList 获取API列表
|
||||
func (s *ApiService) GetApiList(ctx context.Context, req *GetApiListRequest) (*GetApiListResponse, error) {
|
||||
filters := make(map[string]string)
|
||||
if req.Path != "" {
|
||||
filters["path"] = req.Path
|
||||
}
|
||||
if req.Description != "" {
|
||||
filters["description"] = req.Description
|
||||
}
|
||||
if req.Method != "" {
|
||||
filters["method"] = req.Method
|
||||
}
|
||||
if req.ApiGroup != "" {
|
||||
filters["api_group"] = req.ApiGroup
|
||||
}
|
||||
|
||||
apis, total, err := s.uc.GetAPIInfoList(ctx, req.Page, req.PageSize, filters, req.OrderKey, req.Desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := make([]*ApiInfo, len(apis))
|
||||
for i, api := range apis {
|
||||
list[i] = toApiInfo(api)
|
||||
}
|
||||
|
||||
return &GetApiListResponse{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAllApis 获取所有API
|
||||
func (s *ApiService) GetAllApis(ctx context.Context, authorityID uint) ([]*ApiInfo, error) {
|
||||
apis, err := s.uc.GetAllApis(ctx, authorityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := make([]*ApiInfo, len(apis))
|
||||
for i, api := range apis {
|
||||
list[i] = toApiInfo(api)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// GetApiGroups 获取API分组
|
||||
func (s *ApiService) GetApiGroups(ctx context.Context) ([]string, map[string]string, error) {
|
||||
return s.uc.GetApiGroups(ctx)
|
||||
}
|
||||
|
||||
// SyncApiResponse 同步API响应
|
||||
type SyncApiResponse struct {
|
||||
NewApis []*ApiInfo `json:"newApis"`
|
||||
DeleteApis []*ApiInfo `json:"deleteApis"`
|
||||
IgnoreApis []*ApiInfo `json:"ignoreApis"`
|
||||
}
|
||||
|
||||
// SyncApi 同步API
|
||||
func (s *ApiService) SyncApi(ctx context.Context) (*SyncApiResponse, error) {
|
||||
newApis, deleteApis, ignoreApis, err := s.uc.SyncApi(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &SyncApiResponse{
|
||||
NewApis: make([]*ApiInfo, len(newApis)),
|
||||
DeleteApis: make([]*ApiInfo, len(deleteApis)),
|
||||
IgnoreApis: make([]*ApiInfo, len(ignoreApis)),
|
||||
}
|
||||
for i, api := range newApis {
|
||||
resp.NewApis[i] = toApiInfo(api)
|
||||
}
|
||||
for i, api := range deleteApis {
|
||||
resp.DeleteApis[i] = toApiInfo(api)
|
||||
}
|
||||
for i, api := range ignoreApis {
|
||||
resp.IgnoreApis[i] = toApiInfo(api)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// IgnoreApiRequest 忽略API请求
|
||||
type IgnoreApiRequest struct {
|
||||
Path string `json:"path"`
|
||||
Method string `json:"method"`
|
||||
Flag bool `json:"flag"`
|
||||
}
|
||||
|
||||
// IgnoreApi 忽略API
|
||||
func (s *ApiService) IgnoreApi(ctx context.Context, req *IgnoreApiRequest) error {
|
||||
return s.uc.IgnoreApi(ctx, req.Path, req.Method, req.Flag)
|
||||
}
|
||||
|
||||
// EnterSyncApiRequest 确认同步API请求
|
||||
type EnterSyncApiRequest struct {
|
||||
NewApis []*ApiInfo `json:"newApis"`
|
||||
DeleteApis []*ApiInfo `json:"deleteApis"`
|
||||
}
|
||||
|
||||
// EnterSyncApi 确认同步API
|
||||
func (s *ApiService) EnterSyncApi(ctx context.Context, req *EnterSyncApiRequest) error {
|
||||
newApis := make([]*system.Api, len(req.NewApis))
|
||||
for i, api := range req.NewApis {
|
||||
newApis[i] = &system.Api{
|
||||
Path: api.Path,
|
||||
Description: api.Description,
|
||||
ApiGroup: api.ApiGroup,
|
||||
Method: api.Method,
|
||||
}
|
||||
}
|
||||
deleteApis := make([]*system.Api, len(req.DeleteApis))
|
||||
for i, api := range req.DeleteApis {
|
||||
deleteApis[i] = &system.Api{
|
||||
ID: api.ID,
|
||||
Path: api.Path,
|
||||
Method: api.Method,
|
||||
}
|
||||
}
|
||||
return s.uc.EnterSyncApi(ctx, newApis, deleteApis)
|
||||
}
|
||||
|
||||
// FreshCasbin 刷新Casbin缓存
|
||||
func (s *ApiService) FreshCasbin() error {
|
||||
return s.uc.FreshCasbin()
|
||||
}
|
||||
|
||||
// 转换函数
|
||||
func toApiInfo(api *system.Api) *ApiInfo {
|
||||
return &ApiInfo{
|
||||
ID: api.ID,
|
||||
Path: api.Path,
|
||||
Description: api.Description,
|
||||
ApiGroup: api.ApiGroup,
|
||||
Method: api.Method,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoutes 注册路由
|
||||
func (s *ApiService) RegisterRoutes(srv *http.Server) {
|
||||
r := srv.Route("/")
|
||||
|
||||
r.POST("/api/createApi", s.handleCreateApi)
|
||||
r.POST("/api/updateApi", s.handleUpdateApi)
|
||||
r.POST("/api/deleteApi", s.handleDeleteApi)
|
||||
r.DELETE("/api/deleteApisByIds", s.handleDeleteApisByIds)
|
||||
r.POST("/api/getApiList", s.handleGetApiList)
|
||||
r.POST("/api/getApiById", s.handleGetApiById)
|
||||
r.POST("/api/getAllApis", s.handleGetAllApis)
|
||||
r.GET("/api/getApiGroups", s.handleGetApiGroups)
|
||||
r.GET("/api/syncApi", s.handleSyncApi)
|
||||
r.POST("/api/ignoreApi", s.handleIgnoreApi)
|
||||
r.POST("/api/enterSyncApi", s.handleEnterSyncApi)
|
||||
r.GET("/api/freshCasbin", s.handleFreshCasbin)
|
||||
}
|
||||
|
||||
// HTTP Handlers
|
||||
func (s *ApiService) handleCreateApi(ctx http.Context) error {
|
||||
var req CreateApiRequest
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.CreateApi(ctx, &req); err != nil {
|
||||
return errors.BadRequest("CREATE_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "创建成功"})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleUpdateApi(ctx http.Context) error {
|
||||
var req UpdateApiRequest
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.UpdateApi(ctx, &req); err != nil {
|
||||
return errors.BadRequest("UPDATE_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "修改成功"})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleDeleteApi(ctx http.Context) error {
|
||||
var req struct {
|
||||
ID uint `json:"ID"`
|
||||
}
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.DeleteApi(ctx, req.ID); err != nil {
|
||||
return errors.BadRequest("DELETE_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "删除成功"})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleDeleteApisByIds(ctx http.Context) error {
|
||||
var req DeleteApisByIdsRequest
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.DeleteApisByIds(ctx, &req); err != nil {
|
||||
return errors.BadRequest("DELETE_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "删除成功"})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleGetApiList(ctx http.Context) error {
|
||||
var req GetApiListRequest
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := s.GetApiList(ctx, &req)
|
||||
if err != nil {
|
||||
return errors.InternalServer("LIST_ERROR", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "获取成功", "data": resp})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleGetApiById(ctx http.Context) error {
|
||||
var req struct {
|
||||
ID uint `json:"ID"`
|
||||
}
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := s.GetApiById(ctx, req.ID)
|
||||
if err != nil {
|
||||
return errors.NotFound("NOT_FOUND", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "获取成功", "data": map[string]any{"api": resp}})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleGetAllApis(ctx http.Context) error {
|
||||
authorityID := middleware.GetAuthorityID(ctx)
|
||||
resp, err := s.GetAllApis(ctx, authorityID)
|
||||
if err != nil {
|
||||
return errors.InternalServer("LIST_ERROR", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "获取成功", "data": map[string]any{"apis": resp}})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleGetApiGroups(ctx http.Context) error {
|
||||
groups, apiGroupMap, err := s.GetApiGroups(ctx)
|
||||
if err != nil {
|
||||
return errors.InternalServer("LIST_ERROR", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "获取成功", "data": map[string]any{"groups": groups, "apiGroupMap": apiGroupMap}})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleSyncApi(ctx http.Context) error {
|
||||
resp, err := s.SyncApi(ctx)
|
||||
if err != nil {
|
||||
return errors.InternalServer("SYNC_ERROR", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "data": resp})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleIgnoreApi(ctx http.Context) error {
|
||||
var req IgnoreApiRequest
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.IgnoreApi(ctx, &req); err != nil {
|
||||
return errors.BadRequest("IGNORE_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "操作成功"})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleEnterSyncApi(ctx http.Context) error {
|
||||
var req EnterSyncApiRequest
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.EnterSyncApi(ctx, &req); err != nil {
|
||||
return errors.BadRequest("SYNC_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "同步成功"})
|
||||
}
|
||||
|
||||
func (s *ApiService) handleFreshCasbin(ctx http.Context) error {
|
||||
if err := s.FreshCasbin(); err != nil {
|
||||
return errors.InternalServer("FRESH_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "刷新成功"})
|
||||
}
|
||||
|
|
@ -0,0 +1,440 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"kra/internal/biz/system"
|
||||
"kra/internal/server/middleware"
|
||||
|
||||
"github.com/go-kratos/kratos/v2/errors"
|
||||
"github.com/go-kratos/kratos/v2/transport/http"
|
||||
)
|
||||
|
||||
// MenuService 菜单服务
|
||||
type MenuService struct {
|
||||
uc *system.MenuUsecase
|
||||
}
|
||||
|
||||
// NewMenuService 创建菜单服务
|
||||
func NewMenuService(uc *system.MenuUsecase) *MenuService {
|
||||
return &MenuService{uc: uc}
|
||||
}
|
||||
|
||||
// MenuInfo 菜单信息
|
||||
type MenuInfo struct {
|
||||
ID uint `json:"ID"`
|
||||
ParentId uint `json:"parentId"`
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Hidden bool `json:"hidden"`
|
||||
Component string `json:"component"`
|
||||
Sort int `json:"sort"`
|
||||
Meta MenuMeta `json:"meta"`
|
||||
AuthorityId uint `json:"authorityId,omitempty"`
|
||||
MenuId uint `json:"menuId,omitempty"`
|
||||
Parameters []*MenuParamInfo `json:"parameters,omitempty"`
|
||||
MenuBtn []*MenuBtnInfo `json:"menuBtn,omitempty"`
|
||||
Btns map[string]uint `json:"btns,omitempty"`
|
||||
Children []*MenuInfo `json:"children,omitempty"`
|
||||
}
|
||||
|
||||
// MenuMeta 菜单元信息
|
||||
type MenuMeta struct {
|
||||
ActiveName string `json:"activeName"`
|
||||
KeepAlive bool `json:"keepAlive"`
|
||||
DefaultMenu bool `json:"defaultMenu"`
|
||||
Title string `json:"title"`
|
||||
Icon string `json:"icon"`
|
||||
CloseTab bool `json:"closeTab"`
|
||||
TransitionType string `json:"transitionType"`
|
||||
}
|
||||
|
||||
// MenuParamInfo 菜单参数信息
|
||||
type MenuParamInfo struct {
|
||||
ID uint `json:"ID"`
|
||||
SysBaseMenuID uint `json:"sysBaseMenuId"`
|
||||
Type string `json:"type"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// MenuBtnInfo 菜单按钮信息
|
||||
type MenuBtnInfo struct {
|
||||
ID uint `json:"ID"`
|
||||
SysBaseMenuID uint `json:"sysBaseMenuId"`
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
}
|
||||
|
||||
// BaseMenuRequest 基础菜单请求
|
||||
type BaseMenuRequest struct {
|
||||
ID uint `json:"ID"`
|
||||
ParentId uint `json:"parentId"`
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Hidden bool `json:"hidden"`
|
||||
Component string `json:"component"`
|
||||
Sort int `json:"sort"`
|
||||
Meta MenuMeta `json:"meta"`
|
||||
Parameters []*MenuParamInfo `json:"parameters"`
|
||||
MenuBtn []*MenuBtnInfo `json:"menuBtn"`
|
||||
}
|
||||
|
||||
// GetMenu 获取用户动态路由
|
||||
func (s *MenuService) GetMenu(ctx context.Context, authorityId uint) ([]*MenuInfo, error) {
|
||||
menus, err := s.uc.GetMenuTree(ctx, authorityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toMenuInfoList(menus), nil
|
||||
}
|
||||
|
||||
// GetBaseMenuTree 获取基础菜单树
|
||||
func (s *MenuService) GetBaseMenuTree(ctx context.Context, authorityId uint) ([]*MenuInfo, error) {
|
||||
menus, err := s.uc.GetBaseMenuTree(ctx, authorityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toBaseMenuInfoList(menus), nil
|
||||
}
|
||||
|
||||
// GetMenuList 获取菜单列表
|
||||
func (s *MenuService) GetMenuList(ctx context.Context, authorityId uint) ([]*MenuInfo, error) {
|
||||
menus, err := s.uc.GetInfoList(ctx, authorityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toBaseMenuInfoList(menus), nil
|
||||
}
|
||||
|
||||
// AddBaseMenu 添加基础菜单
|
||||
func (s *MenuService) AddBaseMenu(ctx context.Context, req *BaseMenuRequest) error {
|
||||
menu := toBaseMenu(req)
|
||||
return s.uc.AddBaseMenu(ctx, menu)
|
||||
}
|
||||
|
||||
// UpdateBaseMenu 更新基础菜单
|
||||
func (s *MenuService) UpdateBaseMenu(ctx context.Context, req *BaseMenuRequest) error {
|
||||
menu := toBaseMenu(req)
|
||||
return s.uc.UpdateBaseMenu(ctx, menu)
|
||||
}
|
||||
|
||||
// DeleteBaseMenu 删除基础菜单
|
||||
func (s *MenuService) DeleteBaseMenu(ctx context.Context, id uint) error {
|
||||
return s.uc.DeleteBaseMenu(ctx, id)
|
||||
}
|
||||
|
||||
// GetBaseMenuById 根据ID获取基础菜单
|
||||
func (s *MenuService) GetBaseMenuById(ctx context.Context, id uint) (*MenuInfo, error) {
|
||||
menu, err := s.uc.GetBaseMenuById(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toBaseMenuInfo(menu), nil
|
||||
}
|
||||
|
||||
// AddMenuAuthorityRequest 添加菜单权限请求
|
||||
type AddMenuAuthorityRequest struct {
|
||||
AuthorityId uint `json:"authorityId"`
|
||||
Menus []*BaseMenuRequest `json:"menus"`
|
||||
}
|
||||
|
||||
// AddMenuAuthority 添加菜单权限
|
||||
func (s *MenuService) AddMenuAuthority(ctx context.Context, adminAuthorityID uint, req *AddMenuAuthorityRequest) error {
|
||||
menus := make([]*system.BaseMenu, len(req.Menus))
|
||||
for i, m := range req.Menus {
|
||||
menus[i] = toBaseMenu(m)
|
||||
}
|
||||
return s.uc.AddMenuAuthority(ctx, menus, adminAuthorityID, req.AuthorityId)
|
||||
}
|
||||
|
||||
// GetMenuAuthority 获取角色菜单权限
|
||||
func (s *MenuService) GetMenuAuthority(ctx context.Context, authorityId uint) ([]*MenuInfo, error) {
|
||||
menus, err := s.uc.GetMenuAuthority(ctx, authorityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toMenuInfoList(menus), nil
|
||||
}
|
||||
|
||||
// 转换函数
|
||||
func toBaseMenu(req *BaseMenuRequest) *system.BaseMenu {
|
||||
menu := &system.BaseMenu{
|
||||
ID: req.ID,
|
||||
ParentId: req.ParentId,
|
||||
Path: req.Path,
|
||||
Name: req.Name,
|
||||
Hidden: req.Hidden,
|
||||
Component: req.Component,
|
||||
Sort: req.Sort,
|
||||
ActiveName: req.Meta.ActiveName,
|
||||
KeepAlive: req.Meta.KeepAlive,
|
||||
DefaultMenu: req.Meta.DefaultMenu,
|
||||
Title: req.Meta.Title,
|
||||
Icon: req.Meta.Icon,
|
||||
CloseTab: req.Meta.CloseTab,
|
||||
TransitionType: req.Meta.TransitionType,
|
||||
}
|
||||
|
||||
if len(req.Parameters) > 0 {
|
||||
menu.Parameters = make([]*system.MenuParameter, len(req.Parameters))
|
||||
for i, p := range req.Parameters {
|
||||
menu.Parameters[i] = &system.MenuParameter{
|
||||
ID: p.ID,
|
||||
SysBaseMenuID: p.SysBaseMenuID,
|
||||
Type: p.Type,
|
||||
Key: p.Key,
|
||||
Value: p.Value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(req.MenuBtn) > 0 {
|
||||
menu.MenuBtn = make([]*system.MenuBtn, len(req.MenuBtn))
|
||||
for i, b := range req.MenuBtn {
|
||||
menu.MenuBtn[i] = &system.MenuBtn{
|
||||
ID: b.ID,
|
||||
SysBaseMenuID: b.SysBaseMenuID,
|
||||
Name: b.Name,
|
||||
Desc: b.Desc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return menu
|
||||
}
|
||||
|
||||
func toMenuInfoList(menus []*system.Menu) []*MenuInfo {
|
||||
if menus == nil {
|
||||
return []*MenuInfo{}
|
||||
}
|
||||
result := make([]*MenuInfo, len(menus))
|
||||
for i, m := range menus {
|
||||
result[i] = toMenuInfo(m)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func toMenuInfo(m *system.Menu) *MenuInfo {
|
||||
info := &MenuInfo{
|
||||
ID: m.ID,
|
||||
ParentId: m.ParentId,
|
||||
Path: m.Path,
|
||||
Name: m.Name,
|
||||
Hidden: m.Hidden,
|
||||
Component: m.Component,
|
||||
Sort: m.Sort,
|
||||
AuthorityId: m.AuthorityId,
|
||||
MenuId: m.MenuId,
|
||||
Btns: m.Btns,
|
||||
Meta: MenuMeta{
|
||||
ActiveName: m.ActiveName,
|
||||
KeepAlive: m.KeepAlive,
|
||||
DefaultMenu: m.DefaultMenu,
|
||||
Title: m.Title,
|
||||
Icon: m.Icon,
|
||||
CloseTab: m.CloseTab,
|
||||
TransitionType: m.TransitionType,
|
||||
},
|
||||
}
|
||||
|
||||
if len(m.Parameters) > 0 {
|
||||
info.Parameters = make([]*MenuParamInfo, len(m.Parameters))
|
||||
for i, p := range m.Parameters {
|
||||
info.Parameters[i] = &MenuParamInfo{
|
||||
ID: p.ID,
|
||||
SysBaseMenuID: p.SysBaseMenuID,
|
||||
Type: p.Type,
|
||||
Key: p.Key,
|
||||
Value: p.Value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.Children) > 0 {
|
||||
info.Children = toMenuInfoList(m.Children)
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func toBaseMenuInfoList(menus []*system.BaseMenu) []*MenuInfo {
|
||||
if menus == nil {
|
||||
return []*MenuInfo{}
|
||||
}
|
||||
result := make([]*MenuInfo, len(menus))
|
||||
for i, m := range menus {
|
||||
result[i] = toBaseMenuInfo(m)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func toBaseMenuInfo(m *system.BaseMenu) *MenuInfo {
|
||||
info := &MenuInfo{
|
||||
ID: m.ID,
|
||||
ParentId: m.ParentId,
|
||||
Path: m.Path,
|
||||
Name: m.Name,
|
||||
Hidden: m.Hidden,
|
||||
Component: m.Component,
|
||||
Sort: m.Sort,
|
||||
Meta: MenuMeta{
|
||||
ActiveName: m.ActiveName,
|
||||
KeepAlive: m.KeepAlive,
|
||||
DefaultMenu: m.DefaultMenu,
|
||||
Title: m.Title,
|
||||
Icon: m.Icon,
|
||||
CloseTab: m.CloseTab,
|
||||
TransitionType: m.TransitionType,
|
||||
},
|
||||
}
|
||||
|
||||
if len(m.Parameters) > 0 {
|
||||
info.Parameters = make([]*MenuParamInfo, len(m.Parameters))
|
||||
for i, p := range m.Parameters {
|
||||
info.Parameters[i] = &MenuParamInfo{
|
||||
ID: p.ID,
|
||||
SysBaseMenuID: p.SysBaseMenuID,
|
||||
Type: p.Type,
|
||||
Key: p.Key,
|
||||
Value: p.Value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.MenuBtn) > 0 {
|
||||
info.MenuBtn = make([]*MenuBtnInfo, len(m.MenuBtn))
|
||||
for i, b := range m.MenuBtn {
|
||||
info.MenuBtn[i] = &MenuBtnInfo{
|
||||
ID: b.ID,
|
||||
SysBaseMenuID: b.SysBaseMenuID,
|
||||
Name: b.Name,
|
||||
Desc: b.Desc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.Children) > 0 {
|
||||
info.Children = toBaseMenuInfoList(m.Children)
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
// RegisterRoutes 注册路由
|
||||
func (s *MenuService) RegisterRoutes(srv *http.Server) {
|
||||
r := srv.Route("/")
|
||||
|
||||
r.POST("/menu/getMenu", s.handleGetMenu)
|
||||
r.POST("/menu/getBaseMenuTree", s.handleGetBaseMenuTree)
|
||||
r.POST("/menu/getMenuList", s.handleGetMenuList)
|
||||
r.POST("/menu/addBaseMenu", s.handleAddBaseMenu)
|
||||
r.POST("/menu/updateBaseMenu", s.handleUpdateBaseMenu)
|
||||
r.POST("/menu/deleteBaseMenu", s.handleDeleteBaseMenu)
|
||||
r.POST("/menu/getBaseMenuById", s.handleGetBaseMenuById)
|
||||
r.POST("/menu/addMenuAuthority", s.handleAddMenuAuthority)
|
||||
r.POST("/menu/getMenuAuthority", s.handleGetMenuAuthority)
|
||||
}
|
||||
|
||||
// HTTP Handlers
|
||||
func (s *MenuService) handleGetMenu(ctx http.Context) error {
|
||||
authorityId := middleware.GetAuthorityID(ctx)
|
||||
menus, err := s.GetMenu(ctx, authorityId)
|
||||
if err != nil {
|
||||
return errors.InternalServer("GET_MENU_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "获取成功", "data": map[string]any{"menus": menus}})
|
||||
}
|
||||
|
||||
func (s *MenuService) handleGetBaseMenuTree(ctx http.Context) error {
|
||||
authorityId := middleware.GetAuthorityID(ctx)
|
||||
menus, err := s.GetBaseMenuTree(ctx, authorityId)
|
||||
if err != nil {
|
||||
return errors.InternalServer("GET_MENU_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "获取成功", "data": map[string]any{"menus": menus}})
|
||||
}
|
||||
|
||||
func (s *MenuService) handleGetMenuList(ctx http.Context) error {
|
||||
authorityId := middleware.GetAuthorityID(ctx)
|
||||
menus, err := s.GetMenuList(ctx, authorityId)
|
||||
if err != nil {
|
||||
return errors.InternalServer("GET_MENU_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "获取成功", "data": menus})
|
||||
}
|
||||
|
||||
func (s *MenuService) handleAddBaseMenu(ctx http.Context) error {
|
||||
var req BaseMenuRequest
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddBaseMenu(ctx, &req); err != nil {
|
||||
return errors.BadRequest("ADD_MENU_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "添加成功"})
|
||||
}
|
||||
|
||||
func (s *MenuService) handleUpdateBaseMenu(ctx http.Context) error {
|
||||
var req BaseMenuRequest
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.UpdateBaseMenu(ctx, &req); err != nil {
|
||||
return errors.BadRequest("UPDATE_MENU_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "更新成功"})
|
||||
}
|
||||
|
||||
func (s *MenuService) handleDeleteBaseMenu(ctx http.Context) error {
|
||||
var req struct {
|
||||
ID uint `json:"ID"`
|
||||
}
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.DeleteBaseMenu(ctx, req.ID); err != nil {
|
||||
return errors.BadRequest("DELETE_MENU_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "删除成功"})
|
||||
}
|
||||
|
||||
func (s *MenuService) handleGetBaseMenuById(ctx http.Context) error {
|
||||
var req struct {
|
||||
ID uint `json:"ID"`
|
||||
}
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
menu, err := s.GetBaseMenuById(ctx, req.ID)
|
||||
if err != nil {
|
||||
return errors.NotFound("MENU_NOT_FOUND", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "获取成功", "data": map[string]any{"menu": menu}})
|
||||
}
|
||||
|
||||
func (s *MenuService) handleAddMenuAuthority(ctx http.Context) error {
|
||||
var req AddMenuAuthorityRequest
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
adminAuthorityID := middleware.GetAuthorityID(ctx)
|
||||
if err := s.AddMenuAuthority(ctx, adminAuthorityID, &req); err != nil {
|
||||
return errors.BadRequest("ADD_AUTHORITY_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "添加成功"})
|
||||
}
|
||||
|
||||
func (s *MenuService) handleGetMenuAuthority(ctx http.Context) error {
|
||||
var req struct {
|
||||
AuthorityId uint `json:"authorityId"`
|
||||
}
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
menus, err := s.GetMenuAuthority(ctx, req.AuthorityId)
|
||||
if err != nil {
|
||||
return errors.InternalServer("GET_AUTHORITY_FAILED", err.Error())
|
||||
}
|
||||
return ctx.Result(200, map[string]any{"code": 0, "msg": "获取成功", "data": map[string]any{"menus": menus}})
|
||||
}
|
||||
Loading…
Reference in New Issue