kra/internal/data/system/menu.go

486 lines
14 KiB
Go
Raw Permalink 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"
"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: 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: 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 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: 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: 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(&params)
// 获取按钮
var btns []model.SysBaseMenuBtn
r.db.WithContext(ctx).Where("sys_base_menu_id = ?", id).Find(&btns)
return toBizBaseMenu(&menu, params, btns), nil
}
// FindBaseMenuByName 根据名称获取基础菜单
func (r *menuRepo) FindBaseMenuByName(ctx context.Context, name string) (*system.BaseMenu, error) {
var menu model.SysBaseMenu
if err := r.db.WithContext(ctx).Where("name = ?", name).First(&menu).Error; err != nil {
return nil, err
}
return toBizBaseMenu(&menu, nil, nil), nil
}
// CheckMenuInAuthority 检查菜单是否在角色权限中
func (r *menuRepo) CheckMenuInAuthority(ctx context.Context, authorityId uint, menuName string) (bool, error) {
// 获取角色的菜单ID列表
var menuIds []string
if err := r.db.WithContext(ctx).Model(&model.SysAuthorityMenu{}).
Where("sys_authority_authority_id = ?", authorityId).
Pluck("sys_base_menu_id", &menuIds).Error; err != nil {
return false, err
}
if len(menuIds) == 0 {
return false, nil
}
// 检查菜单名称是否在这些菜单中
var count int64
if err := r.db.WithContext(ctx).Model(&model.SysBaseMenu{}).
Where("name = ? AND id IN (?)", menuName, menuIds).
Count(&count).Error; err != nil {
return false, err
}
return count > 0, 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 {
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 {
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 {
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 {
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 {
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] = mb.Name
}
}
result := make([]*system.AuthorityBtn, len(btns))
for i, b := range btns {
result[i] = &system.AuthorityBtn{
AuthorityId: uint(b.AuthorityID),
SysMenuID: uint(b.SysMenuID),
SysBaseMenuBtnID: uint(b.SysBaseMenuBtnID),
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 {
return &model.SysBaseMenu{
ID: int64(m.ID),
ParentID: int64(m.ParentId),
Path: m.Path,
Name: m.Name,
Hidden: m.Hidden,
Component: m.Component,
Sort: int64(m.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),
ParentId: uint(m.ParentID),
Path: m.Path,
Name: m.Name,
Hidden: m.Hidden,
Component: m.Component,
Sort: int(m.Sort),
ActiveName: m.ActiveName,
KeepAlive: m.KeepAlive,
DefaultMenu: m.DefaultMenu,
Title: m.Title,
Icon: m.Icon,
CloseTab: m.CloseTab,
TransitionType: m.TransitionType,
}
// 转换参数
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),
SysBaseMenuID: uint(p.SysBaseMenuID),
Type: p.Type,
Key: p.Key,
Value: p.Value,
}
}
}
// 转换按钮
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),
SysBaseMenuID: uint(b.SysBaseMenuID),
Name: b.Name,
Desc: b.Desc,
}
}
}
return menu
}
func ptrInt64(v int64) *int64 {
return &v
}