486 lines
14 KiB
Go
486 lines
14 KiB
Go
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
|
||
}
|