Files
be.ems/src/modules/system/controller/sys_menu.go
2025-06-07 16:32:04 +08:00

398 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 controller
import (
"fmt"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/regular"
"be.ems/src/modules/system/model"
"be.ems/src/modules/system/model/vo"
"be.ems/src/modules/system/service"
"github.com/gin-gonic/gin"
)
// 实例化控制层 SysMenuController 结构体
var NewSysMenu = &SysMenuController{
sysMenuService: service.NewSysMenu,
}
// 菜单信息
//
// PATH /system/menu
type SysMenuController struct {
sysMenuService *service.SysMenu // 菜单服务
}
// 菜单列表
//
// GET /list
//
// @Tags system/menu
// @Accept json
// @Produce json
// @Param menuName query string false "menuName"
// @Param status query string false "status" Enums(0, 1)
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary Menu Information List
// @Description Menu Information List
// @Router /system/menu/list [get]
func (s *SysMenuController) List(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
query := model.SysMenu{}
if v, ok := c.GetQuery("menuName"); ok {
query.MenuName = i18n.TFindKeyPrefix(language, "menu", v)
}
if v, ok := c.GetQuery("statusFlag"); ok {
query.StatusFlag = v
}
userId := reqctx.LoginUserToUserID(c)
if config.IsSystemUser(userId) {
userId = 0
}
data := s.sysMenuService.Find(query, userId)
// 闭包函数处理多语言
var converI18n func(language string, arr *[]model.SysMenu)
converI18n = func(language string, arr *[]model.SysMenu) {
for i := range *arr {
(*arr)[i].MenuName = i18n.TKey(language, (*arr)[i].MenuName)
(*arr)[i].Remark = i18n.TKey(language, (*arr)[i].Remark)
if len((*arr)[i].Children) > 0 {
converI18n(language, &(*arr)[i].Children)
}
}
}
converI18n(language, &data)
c.JSON(200, resp.OkData(data))
}
// 菜单信息
//
// GET /:menuId
func (s *SysMenuController) Info(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
menuId := parse.Number(c.Param("menuId"))
if menuId <= 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: menuId is empty"))
return
}
data := s.sysMenuService.FindById(menuId)
if data.MenuId == menuId {
// 处理多语言
data.MenuName = i18n.TKey(language, data.MenuName)
data.Remark = i18n.TKey(language, data.Remark)
c.JSON(200, resp.OkData(data))
return
}
c.JSON(200, resp.Err(nil))
}
// 菜单新增
//
// POST /
func (s *SysMenuController) Add(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body model.SysMenu
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
if body.MenuId > 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: menuId not is empty"))
return
}
// 目录和菜单检查地址唯一
if constants.MENU_TYPE_DIR == body.MenuType || constants.MENU_TYPE_MENU == body.MenuType {
uniqueMenuPath := s.sysMenuService.CheckUniqueParentIdByMenuPath(body.ParentId, body.MenuPath, 0)
if !uniqueMenuPath {
// msg := fmt.Sprintf("菜单新增【%s】失败菜单路由地址已存在", body.MenuName)
msg := i18n.TTemplate(language, "menu.errPathExists", map[string]any{"name": body.MenuName})
c.JSON(200, resp.ErrMsg(msg))
return
}
}
// 检查名称唯一
uniqueMenuName := s.sysMenuService.CheckUniqueParentIdByMenuName(body.ParentId, body.MenuName, 0)
if !uniqueMenuName {
// msg := fmt.Sprintf("菜单新增【%s】失败菜单名称已存在", body.MenuName)
msg := i18n.TTemplate(language, "menu.errNameExists", map[string]any{"name": body.MenuName})
c.JSON(200, resp.ErrMsg(msg))
return
}
// 外链菜单需要符合网站http(s)开头
if body.FrameFlag == constants.STATUS_NO && !regular.ValidHttp(body.MenuPath) {
// msg := fmt.Sprintf("菜单新增【%s】失败非内部地址必须以http(s)://开头", body.MenuName)
msg := i18n.TTemplate(language, "menu.errFramePath", map[string]any{"name": body.MenuName})
c.JSON(200, resp.ErrMsg(msg))
return
}
body.CreateBy = reqctx.LoginUserToUserName(c)
insertId := s.sysMenuService.Insert(body)
if insertId > 0 {
c.JSON(200, resp.OkData(insertId))
return
}
c.JSON(200, resp.Err(nil))
}
// 菜单修改
//
// PUT /
func (s *SysMenuController) Edit(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body model.SysMenu
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
return
}
if body.MenuId <= 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: menuId is empty"))
return
}
// 上级菜单不能选自己
if body.MenuId == body.ParentId {
// msg := fmt.Sprintf("菜单修改【%s】失败上级菜单不能选择自己", body.MenuName)
msg := i18n.TTemplate(language, "menu.errPathExists", map[string]any{"name": body.MenuName})
c.JSON(200, resp.ErrMsg(msg))
return
}
// 检查数据是否存在
menuInfo := s.sysMenuService.FindById(body.MenuId)
if menuInfo.MenuId != body.MenuId {
// c.JSON(200, resp.ErrMsg("没有权限访问菜单数据"))
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "menu.noData")))
return
}
// 父级ID不为0是要检查
if body.ParentId > 0 {
menuParent := s.sysMenuService.FindById(body.ParentId)
if menuParent.MenuId != body.ParentId {
// c.JSON(200, resp.ErrMsg("没有权限访问菜单数据"))
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "menu.noData")))
return
}
// 禁用菜单时检查父菜单是否使用
if body.StatusFlag == constants.STATUS_YES && menuParent.StatusFlag == constants.STATUS_NO {
// c.JSON(200, resp.ErrMsg("上级菜单未启用!"))
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "menu.errParentStatus")))
return
}
}
// 目录和菜单检查地址唯一
if constants.MENU_TYPE_DIR == body.MenuType || constants.MENU_TYPE_MENU == body.MenuType {
uniqueMenuPath := s.sysMenuService.CheckUniqueParentIdByMenuPath(body.ParentId, body.MenuPath, body.MenuId)
if !uniqueMenuPath {
// msg := fmt.Sprintf("菜单修改【%s】失败菜单路由地址已存在", body.MenuName)
msg := i18n.TTemplate(language, "menu.errPathExists", map[string]any{"name": body.MenuName})
c.JSON(200, resp.ErrMsg(msg))
return
}
}
// 检查名称唯一
uniqueMenuName := s.sysMenuService.CheckUniqueParentIdByMenuName(body.ParentId, body.MenuName, body.MenuId)
if !uniqueMenuName {
// msg := fmt.Sprintf("菜单修改【%s】失败菜单名称已存在", body.MenuName)
msg := i18n.TTemplate(language, "menu.errNameExists", map[string]any{"name": body.MenuName})
c.JSON(200, resp.ErrMsg(msg))
return
}
// 外链菜单需要符合网站http(s)开头
if body.FrameFlag == constants.STATUS_NO && !regular.ValidHttp(body.MenuPath) {
// msg := fmt.Sprintf("菜单修改【%s】失败非内部地址必须以http(s)://开头", body.MenuName)
msg := i18n.TTemplate(language, "menu.errFramePath", map[string]any{"name": body.MenuName})
c.JSON(200, resp.ErrMsg(msg))
return
}
// 禁用菜单时检查子菜单是否使用
if body.StatusFlag == constants.STATUS_NO {
hasStatus := s.sysMenuService.ExistChildrenByMenuIdAndStatus(body.MenuId, constants.STATUS_YES)
if hasStatus > 0 {
// msg := fmt.Sprintf("不允许禁用,存在使用子菜单数:%d", hasStatus)
msg := i18n.TTemplate(language, "menu.errHasChildUse", map[string]any{"name": body.MenuName, "num": hasStatus})
c.JSON(200, resp.ErrMsg(msg))
return
}
}
// 多语言非原始值
i18nValue := i18n.TKey(language, menuInfo.MenuName)
if i18nValue != menuInfo.MenuName {
service.NewSysI18n.UpdateKeyValue(language, menuInfo.MenuName, body.MenuName)
body.MenuName = menuInfo.MenuName
}
// 多语言非原始值
i18nValue2 := i18n.TKey(language, menuInfo.Remark)
if i18nValue2 != menuInfo.Remark {
service.NewSysI18n.UpdateKeyValue(language, menuInfo.Remark, body.Remark)
body.Remark = menuInfo.Remark
}
menuInfo.ParentId = body.ParentId
menuInfo.MenuName = body.MenuName
menuInfo.MenuType = body.MenuType
menuInfo.MenuSort = body.MenuSort
menuInfo.MenuPath = body.MenuPath
menuInfo.Component = body.Component
menuInfo.FrameFlag = body.FrameFlag
menuInfo.CacheFlag = body.CacheFlag
menuInfo.VisibleFlag = body.VisibleFlag
menuInfo.StatusFlag = body.StatusFlag
menuInfo.Perms = body.Perms
menuInfo.Icon = body.Icon
menuInfo.Remark = body.Remark
menuInfo.UpdateBy = reqctx.LoginUserToUserName(c)
rows := s.sysMenuService.Update(menuInfo)
if rows > 0 {
c.JSON(200, resp.Ok(nil))
return
}
c.JSON(200, resp.Err(nil))
}
// Remove 菜单删除
//
// DELETE /:menuId
func (s SysMenuController) Remove(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
menuId := parse.Number(c.Param("menuId"))
if menuId <= 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: menuId is empty"))
return
}
// 检查数据是否存在
menu := s.sysMenuService.FindById(menuId)
if menu.MenuId != menuId {
// c.JSON(200, resp.ErrMsg("没有权限访问菜单数据!"))
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "menu.noData")))
return
}
// 检查是否存在子菜单
hasChild := s.sysMenuService.ExistChildrenByMenuIdAndStatus(menuId, "")
if hasChild > 0 {
// msg := fmt.Sprintf("不允许删除,存在子菜单数:%d", hasChild)
msg := i18n.TTemplate(language, "menu.errHasChildUse", map[string]any{"name": menu.MenuName, "num": hasChild})
c.JSON(200, resp.ErrMsg(msg))
return
}
// 检查是否分配给角色
existRole := s.sysMenuService.ExistRoleByMenuId(menuId)
if existRole > 0 {
// msg := fmt.Sprintf("不允许删除,菜单已分配给角色数:%d", existRole)
msg := i18n.TTemplate(language, "menu.errHasRoleUse", map[string]any{"name": menu.MenuName, "num": existRole})
c.JSON(200, resp.ErrMsg(msg))
return
}
rows := s.sysMenuService.DeleteById(menuId)
if rows > 0 {
// msg := fmt.Sprintf("删除成功:%d", rows)
msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows})
c.JSON(200, resp.OkMsg(msg))
return
}
c.JSON(200, resp.Err(nil))
}
// 菜单树结构列表
//
// GET /tree
func (s *SysMenuController) Tree(c *gin.Context) {
query := model.SysMenu{}
if v, ok := c.GetQuery("menuName"); ok {
query.MenuName = v
}
if v, ok := c.GetQuery("statusFlag"); ok {
query.StatusFlag = v
}
userId := reqctx.LoginUserToUserID(c)
if config.IsSystemUser(userId) {
userId = 0
}
trees := s.sysMenuService.BuildTreeSelectByUserId(query, userId)
// 闭包函数处理多语言
language := reqctx.AcceptLanguage(c)
var converI18n func(language string, arr *[]vo.TreeSelect)
converI18n = func(language string, arr *[]vo.TreeSelect) {
for i := range *arr {
(*arr)[i].Label = i18n.TKey(language, (*arr)[i].Label)
if len((*arr)[i].Children) > 0 {
converI18n(language, &(*arr)[i].Children)
}
}
}
converI18n(language, &trees)
c.JSON(200, resp.OkData(trees))
}
// 菜单树结构列表(指定角色)
//
// GET /tree/role/:roleId
func (s *SysMenuController) TreeRole(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
roleId := parse.Number(c.Param("roleId"))
if roleId <= 0 {
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: roleId is empty"))
return
}
sysMenu := model.SysMenu{}
if v, ok := c.GetQuery("menuName"); ok {
sysMenu.MenuName = v
}
if v, ok := c.GetQuery("statusFlag"); ok {
sysMenu.StatusFlag = v
}
userId := reqctx.LoginUserToUserID(c)
if config.IsSystemUser(userId) {
userId = 0
}
menuTreeSelect := s.sysMenuService.BuildTreeSelectByUserId(sysMenu, userId)
checkedKeys := s.sysMenuService.FindByRoleId(roleId)
// 闭包函数处理多语言
var converI18n func(language string, arr *[]vo.TreeSelect)
converI18n = func(language string, arr *[]vo.TreeSelect) {
for i := range *arr {
(*arr)[i].Label = i18n.TKey(language, (*arr)[i].Label)
if len((*arr)[i].Children) > 0 {
converI18n(language, &(*arr)[i].Children)
}
}
}
converI18n(language, &menuTreeSelect)
c.JSON(200, resp.OkData(map[string]any{
"menus": menuTreeSelect,
"checkedKeys": checkedKeys,
}))
}