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(¶ms) // 获取按钮 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 }