kra-new/internal/data/menu.go

322 lines
12 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 data
import (
"context"
"errors"
"kra/internal/biz"
"gorm.io/gorm"
)
type menuRepo struct{ data *Data }
func NewMenuRepo(data *Data) biz.MenuRepo { return &menuRepo{data: data} }
func menuFromPO(po menuPO) *biz.Menu {
return &biz.Menu{ID: po.ID, ParentID: po.ParentID, Path: po.Path, Name: po.Name, Hidden: po.Hidden, Component: po.Component, Sort: po.Sort, ActiveName: po.ActiveName, KeepAlive: po.KeepAlive, DefaultMenu: po.DefaultMenu, Title: po.Title, Icon: po.Icon, CloseTab: po.CloseTab, TransitionType: po.TransitionType}
}
func menuToPO(v *biz.Menu) menuPO {
return menuPO{ID: v.ID, ParentID: v.ParentID, Path: v.Path, Name: v.Name, Hidden: v.Hidden, Component: v.Component, Sort: v.Sort, ActiveName: v.ActiveName, KeepAlive: v.KeepAlive, DefaultMenu: v.DefaultMenu, Title: v.Title, Icon: v.Icon, CloseTab: v.CloseTab, TransitionType: v.TransitionType}
}
func replaceMenuRelations(tx *gorm.DB, menu *biz.Menu) error {
var oldButtonIDs []uint
if err := tx.Model(&menuButtonPO{}).Where("sys_base_menu_id = ?", menu.ID).Pluck("id", &oldButtonIDs).Error; err != nil {
return err
}
if len(oldButtonIDs) > 0 {
if err := tx.Where("sys_base_menu_btn_id IN ?", oldButtonIDs).Delete(&authorityButtonPO{}).Error; err != nil {
return err
}
}
if err := tx.Where("sys_base_menu_id = ?", menu.ID).Delete(&menuButtonPO{}).Error; err != nil {
return err
}
if err := tx.Where("sys_base_menu_id = ?", menu.ID).Delete(&menuParameterPO{}).Error; err != nil {
return err
}
buttons := make([]menuButtonPO, 0, len(menu.Buttons))
for _, button := range menu.Buttons {
buttons = append(buttons, menuButtonPO{Name: button.Name, Description: button.Description, MenuID: menu.ID})
}
if len(buttons) > 0 {
if err := tx.Create(&buttons).Error; err != nil {
return err
}
}
parameters := make([]menuParameterPO, 0, len(menu.Parameters))
for _, parameter := range menu.Parameters {
parameters = append(parameters, menuParameterPO{MenuID: menu.ID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
}
if len(parameters) > 0 {
return tx.Create(&parameters).Error
}
return nil
}
func (r *menuRepo) Create(ctx context.Context, v *biz.Menu) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var count int64
if err := tx.Model(&menuPO{}).Where("name = ?", v.Name).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("存在重复name请修改name")
}
if v.ParentID != 0 {
var parent menuPO
if err := tx.First(&parent, v.ParentID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("父菜单不存在")
}
return err
}
if err := tx.Model(&menuPO{}).Where("parent_id = ?", v.ParentID).Count(&count).Error; err != nil {
return err
}
if count == 0 {
if err := tx.Model(&authorityPO{}).Where("default_router = ?", parent.Name).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("父菜单已被其他角色的首页占用,请先释放父菜单的首页权限")
}
if err := tx.Where("sys_base_menu_id = ?", v.ParentID).Delete(&authorityMenuPO{}).Error; err != nil {
return err
}
}
}
po := menuToPO(v)
if err := tx.Create(&po).Error; err != nil {
return err
}
v.ID = po.ID
return replaceMenuRelations(tx, v)
})
}
func (r *menuRepo) Update(ctx context.Context, v *biz.Menu) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var old menuPO
if err := tx.First(&old, v.ID).Error; err != nil {
return err
}
var count int64
if old.Name != v.Name {
if err := tx.Model(&menuPO{}).Where("id <> ? AND name = ?", v.ID, v.Name).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("存在相同name修改失败")
}
}
if err := replaceMenuRelations(tx, v); err != nil {
return err
}
values := map[string]any{"parent_id": v.ParentID, "path": v.Path, "name": v.Name, "hidden": v.Hidden, "component": v.Component, "sort": v.Sort, "active_name": v.ActiveName, "keep_alive": v.KeepAlive, "default_menu": v.DefaultMenu, "title": v.Title, "icon": v.Icon, "close_tab": v.CloseTab, "transition_type": v.TransitionType}
return tx.Model(&old).Updates(values).Error
})
}
func (r *menuRepo) Delete(ctx context.Context, id uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var count int64
if err := tx.Model(&menuPO{}).Where("parent_id = ?", id).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("此菜单存在子菜单不可删除")
}
var menu menuPO
if err := tx.First(&menu, id).Error; err != nil {
return errors.New("记录不存在")
}
if err := tx.Model(&authorityPO{}).Where("default_router = ?", menu.Name).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("此菜单有角色正在作为首页,不可删除")
}
var buttonIDs []uint
if err := tx.Model(&menuButtonPO{}).Where("sys_base_menu_id = ?", id).Pluck("id", &buttonIDs).Error; err != nil {
return err
}
if err := tx.Where("sys_menu_id = ?", id).Delete(&authorityButtonPO{}).Error; err != nil {
return err
}
if err := tx.Where("sys_base_menu_id = ?", id).Delete(&menuButtonPO{}).Error; err != nil {
return err
}
if err := tx.Where("sys_base_menu_id = ?", id).Delete(&menuParameterPO{}).Error; err != nil {
return err
}
if err := tx.Where("sys_base_menu_id = ?", id).Delete(&authorityMenuPO{}).Error; err != nil {
return err
}
return tx.Delete(&menuPO{}, id).Error
})
}
func (r *menuRepo) loadRelations(ctx context.Context, menu *biz.Menu) error {
var parameters []menuParameterPO
if err := r.data.gormDB.WithContext(ctx).Where("sys_base_menu_id = ?", menu.ID).Order("id").Find(&parameters).Error; err != nil {
return err
}
menu.Parameters = make([]*biz.MenuParameter, 0, len(parameters))
for _, parameter := range parameters {
menu.Parameters = append(menu.Parameters, &biz.MenuParameter{ID: parameter.ID, MenuID: parameter.MenuID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
}
var buttons []menuButtonPO
if err := r.data.gormDB.WithContext(ctx).Where("sys_base_menu_id = ?", menu.ID).Order("id").Find(&buttons).Error; err != nil {
return err
}
menu.Buttons = make([]*biz.MenuButton, 0, len(buttons))
for _, button := range buttons {
menu.Buttons = append(menu.Buttons, &biz.MenuButton{ID: button.ID, Name: button.Name, Description: button.Description, MenuID: button.MenuID})
}
return nil
}
func (r *menuRepo) Find(ctx context.Context, id uint) (*biz.Menu, error) {
var po menuPO
if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil {
return nil, err
}
menu := menuFromPO(po)
if err := r.loadRelations(ctx, menu); err != nil {
return nil, err
}
return menu, nil
}
func (r *menuRepo) List(ctx context.Context) ([]*biz.Menu, error) {
var pos []menuPO
db := r.data.gormDB.WithContext(ctx).Model(&menuPO{}).Order("sort,id")
config := r.data.runtime.Admin()
if actor, ok := biz.ActorFromContext(ctx); ok && config != nil && config.System != nil && config.System.UseStrictAuth {
var authority authorityPO
if err := r.data.gormDB.WithContext(ctx).Where("authority_id = ?", actor.AuthorityID).First(&authority).Error; err != nil {
return nil, err
}
if authority.ParentID != nil && *authority.ParentID != 0 {
db = db.Where("id IN (?)", r.data.gormDB.WithContext(ctx).Model(&authorityMenuPO{}).Select("sys_base_menu_id").Where("sys_authority_authority_id = ?", actor.AuthorityID))
}
}
if err := db.Find(&pos).Error; err != nil {
return nil, err
}
out := make([]*biz.Menu, 0, len(pos))
for _, po := range pos {
out = append(out, menuFromPO(po))
}
if len(out) == 0 {
return out, nil
}
byID := make(map[uint]*biz.Menu, len(out))
ids := make([]uint, 0, len(out))
for _, menu := range out {
byID[menu.ID] = menu
ids = append(ids, menu.ID)
menu.Parameters = []*biz.MenuParameter{}
menu.Buttons = []*biz.MenuButton{}
}
var parameters []menuParameterPO
if err := r.data.gormDB.WithContext(ctx).Where("sys_base_menu_id IN ?", ids).Order("id").Find(&parameters).Error; err != nil {
return nil, err
}
for _, parameter := range parameters {
menu := byID[parameter.MenuID]
menu.Parameters = append(menu.Parameters, &biz.MenuParameter{ID: parameter.ID, MenuID: parameter.MenuID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
}
var buttons []menuButtonPO
if err := r.data.gormDB.WithContext(ctx).Where("sys_base_menu_id IN ?", ids).Order("id").Find(&buttons).Error; err != nil {
return nil, err
}
for _, button := range buttons {
menu := byID[button.MenuID]
menu.Buttons = append(menu.Buttons, &biz.MenuButton{ID: button.ID, Name: button.Name, Description: button.Description, MenuID: button.MenuID})
}
return out, nil
}
func (r *menuRepo) SetAuthorityMenus(ctx context.Context, id uint, ids []uint) error {
if err := (&accessRepo{data: r.data}).checkAuthorityIDAuth(ctx, id); err != nil {
return err
}
config := r.data.runtime.Admin()
if actor, ok := biz.ActorFromContext(ctx); ok && config != nil && config.System != nil && config.System.UseStrictAuth {
var authority authorityPO
if err := r.data.gormDB.WithContext(ctx).Where("authority_id = ?", actor.AuthorityID).First(&authority).Error; err != nil {
return err
}
if authority.ParentID != nil && *authority.ParentID != 0 {
var allowedIDs []uint
if err := r.data.gormDB.WithContext(ctx).Model(&authorityMenuPO{}).Where("sys_authority_authority_id = ?", actor.AuthorityID).Pluck("sys_base_menu_id", &allowedIDs).Error; err != nil {
return err
}
allowed := make(map[uint]bool, len(allowedIDs))
for _, menuID := range allowedIDs {
allowed[menuID] = true
}
for _, menuID := range ids {
if !allowed[menuID] {
return errors.New("添加失败,请勿跨级操作")
}
}
}
}
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("sys_authority_authority_id = ?", id).Delete(&authorityMenuPO{}).Error; err != nil {
return err
}
links := make([]authorityMenuPO, 0, len(ids))
for _, menuID := range ids {
links = append(links, authorityMenuPO{SysAuthorityAuthorityID: id, SysBaseMenuID: menuID})
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}
func (r *menuRepo) AuthorityMenuIDs(ctx context.Context, id uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&authorityMenuPO{}).Where("sys_authority_authority_id = ?", id).Pluck("sys_base_menu_id", &ids).Error
return ids, err
}
func (r *menuRepo) MenuRoleIDs(ctx context.Context, id uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&authorityMenuPO{}).Where("sys_base_menu_id = ?", id).Pluck("sys_authority_authority_id", &ids).Error
return ids, err
}
func (r *menuRepo) DefaultRouterRoleIDs(ctx context.Context, id uint) ([]uint, error) {
var menu menuPO
if err := r.data.gormDB.WithContext(ctx).First(&menu, id).Error; err != nil {
return nil, err
}
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&authorityPO{}).Where("default_router = ?", menu.Name).Pluck("authority_id", &ids).Error
return ids, err
}
func (r *menuRepo) SetMenuRoles(ctx context.Context, id uint, ids []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("sys_base_menu_id = ?", id).Delete(&authorityMenuPO{}).Error; err != nil {
return err
}
links := make([]authorityMenuPO, 0, len(ids))
for _, authorityID := range ids {
links = append(links, authorityMenuPO{SysAuthorityAuthorityID: authorityID, SysBaseMenuID: id})
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}