299 lines
8.2 KiB
Go
299 lines
8.2 KiB
Go
package admin
|
|
|
|
import (
|
|
"github.com/goravel/framework/contracts/http"
|
|
"github.com/goravel/framework/facades"
|
|
"github.com/goravel/framework/support/str"
|
|
"github.com/spf13/cast"
|
|
|
|
apperrors "goravel/app/errors"
|
|
"goravel/app/http/helpers"
|
|
adminrequests "goravel/app/http/requests/admin"
|
|
"goravel/app/http/response"
|
|
"goravel/app/models"
|
|
"goravel/app/services"
|
|
)
|
|
|
|
type MenuController struct {
|
|
treeService services.TreeService
|
|
menuService services.MenuService
|
|
}
|
|
|
|
func NewMenuController() *MenuController {
|
|
return &MenuController{
|
|
treeService: services.NewTreeServiceImpl(),
|
|
menuService: services.NewMenuService(),
|
|
}
|
|
}
|
|
|
|
// findMenuByID 根据ID查找菜单,如果不存在则返回错误响应
|
|
func (r *MenuController) findMenuByID(ctx http.Context, id uint) (*models.Menu, http.Response) {
|
|
return response.FindByID[models.Menu](ctx, id, &response.FindByIDOptions{
|
|
NotFoundMessageKey: apperrors.ErrMenuNotFound.Code,
|
|
})
|
|
}
|
|
|
|
// Index 菜单列表(树形结构)
|
|
func (r *MenuController) Index(ctx http.Context) http.Response {
|
|
menus, err := r.treeService.BuildMenuTree(0)
|
|
if err != nil {
|
|
return response.Error(ctx, http.StatusInternalServerError, apperrors.ErrQueryFailed.Code)
|
|
}
|
|
|
|
// 检查是否需要隐藏服务监控菜单
|
|
// 只有当配置值不为空且不等于 "0" 时才隐藏("0" 表示不隐藏)
|
|
monitorHidden := facades.Config().GetString("admin.monitor_hidden", "")
|
|
if monitorHidden != "" && monitorHidden != "0" {
|
|
// 获取当前管理员ID
|
|
adminValue := ctx.Value("admin")
|
|
var adminID uint
|
|
if adminValue != nil {
|
|
if admin, ok := adminValue.(models.Admin); ok {
|
|
adminID = admin.ID
|
|
} else if adminPtr, ok := adminValue.(*models.Admin); ok && adminPtr != nil {
|
|
adminID = adminPtr.ID
|
|
}
|
|
}
|
|
|
|
// 检查是否是开发者管理员
|
|
developerIDsStr := facades.Config().GetString("admin.developer_ids", "2")
|
|
isDeveloperAdmin := r.isDeveloperAdmin(adminID, developerIDsStr)
|
|
|
|
// 如果不是开发者管理员,则过滤掉服务监控菜单
|
|
if !isDeveloperAdmin {
|
|
menus = r.filterMonitorMenu(menus)
|
|
}
|
|
}
|
|
|
|
// 检查是否需要隐藏开发工具菜单
|
|
enableDevTool := facades.Config().GetBool("app.enable_dev_tool")
|
|
if !enableDevTool {
|
|
menus = r.filterDevMenu(menus)
|
|
}
|
|
|
|
return response.Success(ctx, http.Json{
|
|
"menus": menus,
|
|
})
|
|
}
|
|
|
|
// Show 菜单详情
|
|
func (r *MenuController) Show(ctx http.Context) http.Response {
|
|
id := helpers.GetUintRoute(ctx, "id")
|
|
menu, resp := r.findMenuByID(ctx, id)
|
|
if resp != nil {
|
|
return resp
|
|
}
|
|
|
|
return response.Success(ctx, http.Json{
|
|
"menu": *menu,
|
|
})
|
|
}
|
|
|
|
// Store 创建菜单
|
|
func (r *MenuController) Store(ctx http.Context) http.Response {
|
|
// 使用请求验证
|
|
var menuCreate adminrequests.MenuCreate
|
|
errors, err := ctx.Request().ValidateRequest(&menuCreate)
|
|
if err != nil {
|
|
return response.Error(ctx, http.StatusBadRequest, err.Error())
|
|
}
|
|
if errors != nil {
|
|
return response.ValidationError(ctx, http.StatusBadRequest, "validation_failed", errors.All())
|
|
}
|
|
|
|
// 检查 slug 是否已存在
|
|
exists, err := facades.Orm().Query().Model(&models.Menu{}).Where("slug", menuCreate.Slug).Exists()
|
|
if err != nil {
|
|
return response.Error(ctx, http.StatusInternalServerError, apperrors.ErrCreateFailed.Code)
|
|
}
|
|
if exists {
|
|
return response.Error(ctx, http.StatusBadRequest, apperrors.ErrMenuSlugExists.Code)
|
|
}
|
|
|
|
menu, err := r.menuService.Create(
|
|
menuCreate.ParentID,
|
|
menuCreate.Title,
|
|
menuCreate.Slug,
|
|
menuCreate.Icon,
|
|
menuCreate.Path,
|
|
menuCreate.Component,
|
|
menuCreate.Permission,
|
|
menuCreate.Type,
|
|
menuCreate.Status,
|
|
menuCreate.Sort,
|
|
menuCreate.IsHidden,
|
|
menuCreate.LinkType,
|
|
menuCreate.OpenType,
|
|
)
|
|
if err != nil {
|
|
return response.ErrorWithLog(ctx, "menu", err, map[string]any{
|
|
"title": menuCreate.Title,
|
|
"slug": menuCreate.Slug,
|
|
})
|
|
}
|
|
|
|
return response.Success(ctx, http.Json{
|
|
"menu": *menu,
|
|
})
|
|
}
|
|
|
|
func (r *MenuController) Update(ctx http.Context) http.Response {
|
|
id := helpers.GetUintRoute(ctx, "id")
|
|
menu, resp := r.findMenuByID(ctx, id)
|
|
if resp != nil {
|
|
return resp
|
|
}
|
|
|
|
// 使用请求验证
|
|
var menuUpdate adminrequests.MenuUpdate
|
|
errors, err := ctx.Request().ValidateRequest(&menuUpdate)
|
|
if err != nil {
|
|
return response.Error(ctx, http.StatusBadRequest, err.Error())
|
|
}
|
|
if errors != nil {
|
|
return response.ValidationError(ctx, http.StatusBadRequest, "validation_failed", errors.All())
|
|
}
|
|
|
|
// 使用 All() 方法检查字段是否存在
|
|
allInputs := ctx.Request().All()
|
|
|
|
if _, exists := allInputs["title"]; exists {
|
|
menu.Title = menuUpdate.Title
|
|
}
|
|
if _, exists := allInputs["slug"]; exists {
|
|
// 检查 slug 是否已被其他菜单使用
|
|
exists, err := facades.Orm().Query().Model(&models.Menu{}).Where("slug", menuUpdate.Slug).Where("id != ?", id).Exists()
|
|
if err != nil {
|
|
return response.Error(ctx, http.StatusInternalServerError, apperrors.ErrUpdateFailed.Code)
|
|
}
|
|
if exists {
|
|
return response.Error(ctx, http.StatusBadRequest, apperrors.ErrMenuSlugExists.Code)
|
|
}
|
|
menu.Slug = menuUpdate.Slug
|
|
}
|
|
// 处理 parent_id,需要根据是否存在来更新,如果存在但为空或0,则设为0
|
|
if val, exists := allInputs["parent_id"]; exists {
|
|
if val == nil {
|
|
menu.ParentID = 0
|
|
} else {
|
|
menu.ParentID = menuUpdate.ParentID
|
|
}
|
|
}
|
|
if _, exists := allInputs["icon"]; exists {
|
|
menu.Icon = menuUpdate.Icon
|
|
}
|
|
if _, exists := allInputs["path"]; exists {
|
|
menu.Path = menuUpdate.Path
|
|
}
|
|
if _, exists := allInputs["component"]; exists {
|
|
menu.Component = menuUpdate.Component
|
|
}
|
|
if _, exists := allInputs["permission"]; exists {
|
|
menu.Permission = menuUpdate.Permission
|
|
}
|
|
if _, exists := allInputs["type"]; exists {
|
|
menu.Type = menuUpdate.Type
|
|
}
|
|
if _, exists := allInputs["status"]; exists {
|
|
menu.Status = menuUpdate.Status
|
|
}
|
|
if _, exists := allInputs["sort"]; exists {
|
|
menu.Sort = menuUpdate.Sort
|
|
}
|
|
if _, exists := allInputs["is_hidden"]; exists {
|
|
menu.IsHidden = menuUpdate.IsHidden
|
|
}
|
|
if _, exists := allInputs["link_type"]; exists {
|
|
menu.LinkType = menuUpdate.LinkType
|
|
}
|
|
if _, exists := allInputs["open_type"]; exists {
|
|
menu.OpenType = menuUpdate.OpenType
|
|
}
|
|
|
|
if err := r.menuService.Update(menu); err != nil {
|
|
return response.ErrorWithLog(ctx, "menu", err, map[string]any{
|
|
"menu_id": menu.ID,
|
|
})
|
|
}
|
|
|
|
return response.Success(ctx, http.Json{
|
|
"menu": *menu,
|
|
})
|
|
}
|
|
|
|
func (r *MenuController) Destroy(ctx http.Context) http.Response {
|
|
id := helpers.GetUintRoute(ctx, "id")
|
|
menu, resp := r.findMenuByID(ctx, id)
|
|
if resp != nil {
|
|
return resp
|
|
}
|
|
|
|
hasChildren, err := r.treeService.HasMenuChildren(id)
|
|
if err != nil {
|
|
return response.Error(ctx, http.StatusInternalServerError, apperrors.ErrQueryFailed.Code)
|
|
}
|
|
if hasChildren {
|
|
return response.Error(ctx, http.StatusBadRequest, apperrors.ErrMenuHasChildren.Code)
|
|
}
|
|
|
|
if err := r.menuService.Delete(menu); err != nil {
|
|
return response.ErrorWithLog(ctx, "menu", err, map[string]any{
|
|
"menu_id": menu.ID,
|
|
})
|
|
}
|
|
|
|
return response.Success(ctx)
|
|
}
|
|
|
|
// isDeveloperAdmin 检查是否是开发者管理员
|
|
func (r *MenuController) isDeveloperAdmin(adminID uint, developerIDsStr string) bool {
|
|
if developerIDsStr == "" {
|
|
return false
|
|
}
|
|
|
|
// 解析开发者ID列表
|
|
parts := str.Of(developerIDsStr).Split(",")
|
|
for _, part := range parts {
|
|
part = str.Of(part).Trim().String()
|
|
if !str.Of(part).IsEmpty() {
|
|
if id := cast.ToUint(part); id > 0 && id == adminID {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// filterMonitorMenu 递归过滤掉服务监控菜单
|
|
func (r *MenuController) filterMonitorMenu(menus []models.Menu) []models.Menu {
|
|
var filteredMenus []models.Menu
|
|
for _, menu := range menus {
|
|
// 如果当前菜单不是服务监控菜单,则保留
|
|
if menu.Slug != "monitor" {
|
|
// 递归过滤子菜单
|
|
if len(menu.Children) > 0 {
|
|
menu.Children = r.filterMonitorMenu(menu.Children)
|
|
}
|
|
filteredMenus = append(filteredMenus, menu)
|
|
}
|
|
}
|
|
return filteredMenus
|
|
}
|
|
|
|
// filterDevMenu 递归过滤掉开发工具菜单
|
|
func (r *MenuController) filterDevMenu(menus []models.Menu) []models.Menu {
|
|
var filteredMenus []models.Menu
|
|
for _, menu := range menus {
|
|
// 如果当前菜单不是开发工具菜单,则保留
|
|
if menu.Slug != "dev" {
|
|
// 递归过滤子菜单
|
|
if len(menu.Children) > 0 {
|
|
menu.Children = r.filterDevMenu(menu.Children)
|
|
}
|
|
filteredMenus = append(filteredMenus, menu)
|
|
}
|
|
}
|
|
return filteredMenus
|
|
}
|