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 }