go-platform/controllers/backend_menu.go
2026-04-02 00:05:03 +08:00

442 lines
12 KiB
Go
Raw Permalink 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 controllers
import (
"encoding/json"
"io"
"server/models"
"strconv"
"strings"
beego "github.com/beego/beego/v2/server/web"
)
// AdminMenuController 后台菜单控制器
type AdminMenuController struct {
beego.Controller
}
type menuPayload struct {
Pid *int64 `json:"pid"`
Title *string `json:"title"`
Path *string `json:"path"`
ComponentPath *string `json:"component_path"`
Icon *string `json:"icon"`
Sort *int64 `json:"sort"`
Status *int8 `json:"status"`
IsVisible *int8 `json:"is_visible"`
Views []int `json:"views"`
Type *int8 `json:"type"`
Permission *string `json:"permission"`
}
func parseViews(raw *string) []int {
if raw == nil {
return nil
}
s := strings.TrimSpace(*raw)
if s == "" {
return nil
}
var arr []int
if err := json.Unmarshal([]byte(s), &arr); err != nil {
return nil
}
return arr
}
func hasView(arr []int, v int) bool {
for _, n := range arr {
if n == v {
return true
}
}
return false
}
func viewsJSON(views []int) string {
// 默认:平台端显示
if len(views) == 0 {
views = []int{1}
}
b, _ := json.Marshal(views)
return string(b)
}
func filterMenusByView(menus []models.SystemMenu, v int) []models.SystemMenu {
out := make([]models.SystemMenu, 0, len(menus))
for _, m := range menus {
views := parseViews(m.Views)
// 兼容旧数据views 为空时,平台端菜单默认可见(保持旧 is_platform=1 的常见默认体验)
// 租户端不做默认放行,避免把未迁移数据误暴露到租户端。
if v == 1 && len(views) == 0 {
out = append(out, m)
continue
}
if hasView(views, v) {
out = append(out, m)
}
}
return out
}
// GetMenu 获取指定用户可见的菜单列表(简化版:当前先忽略用户权限,返回全部启用且平台端菜单)
// 路由示例GET /platform/menu/1
func (c *AdminMenuController) GetMenu() {
// 从路由参数中解析用户 ID占位保留方便后续按用户权限过滤
_ = c.Ctx.Input.Param(":id")
// 查询所有启用菜单,再按 views 过滤平台端可见
var menus []models.SystemMenu
qs := models.Orm.
QueryTable(new(models.SystemMenu)).
Filter("status", 1)
_, err := qs.All(&menus)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 500,
"msg": "获取菜单失败: " + err.Error(),
"data": nil,
}
_ = c.ServeJSON()
return
}
menus = filterMenusByView(menus, 1)
// 将平铺的菜单列表构建为树形结构
menuTree := buildMenuTree(menus, 0)
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "success",
"data": menuTree,
}
_ = c.ServeJSON()
}
// GetBackendMenu 获取租户端用户可见的菜单列表(简化版:当前先忽略用户权限,返回全部启用且租户端菜单)
// 路由示例GET /backend/menu/1
func (c *AdminMenuController) GetBackendMenu() {
// 从路由参数中解析用户 ID占位保留方便后续按用户权限过滤
_ = c.Ctx.Input.Param(":id")
var menus []models.SystemMenu
qs := models.Orm.
QueryTable(new(models.SystemMenu)).
Filter("status", 1)
_, err := qs.All(&menus)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 500,
"msg": "获取菜单失败: " + err.Error(),
"data": nil,
}
_ = c.ServeJSON()
return
}
menus = filterMenusByView(menus, 2)
menuTree := buildMenuTree(menus, 0)
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "success",
"data": menuTree,
}
_ = c.ServeJSON()
}
// GetAllMenus 获取平台端全部菜单(用于菜单管理界面)
// 路由GET /platform/allmenu
func (c *AdminMenuController) GetAllMenus() {
var menus []models.SystemMenu
cid, _ := c.GetInt("cid")
qs := models.Orm.QueryTable(new(models.SystemMenu))
// 菜单管理默认返回全量菜单;仅在明确传 cid 时按分类筛选
// cid: 1平台角色 -> 平台菜单2租户角色 -> 租户菜单
_, err := qs.All(&menus)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 500,
"msg": "获取菜单失败: " + err.Error(),
"data": nil,
}
_ = c.ServeJSON()
return
}
if cid == 1 {
menus = filterMenusByView(menus, 1)
} else if cid == 2 {
menus = filterMenusByView(menus, 2)
}
tree := buildMenuTree(menus, 0)
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "success",
"data": tree,
}
_ = c.ServeJSON()
}
// GetAllBackendMenus 获取租户端全部菜单(用于菜单管理界面)
// 路由GET /backend/allmenu
func (c *AdminMenuController) GetAllBackendMenus() {
var menus []models.SystemMenu
_, err := models.Orm.QueryTable(new(models.SystemMenu)).
All(&menus)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 500,
"msg": "获取菜单失败: " + err.Error(),
"data": nil,
}
_ = c.ServeJSON()
return
}
menus = filterMenusByView(menus, 2)
tree := buildMenuTree(menus, 0)
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "success",
"data": tree,
}
_ = c.ServeJSON()
}
// menuNode 用于 JSON 返回的菜单结构
type menuNode struct {
ID uint64 `json:"id"`
Pid int64 `json:"pid"`
Title string `json:"title"`
Path string `json:"path,omitempty"`
ComponentPath string `json:"component_path,omitempty"`
Icon string `json:"icon,omitempty"`
Sort int64 `json:"sort"`
Status int8 `json:"status"`
IsVisible *int8 `json:"is_visible,omitempty"`
Views []int `json:"views,omitempty"`
Type int8 `json:"type"`
Permission string `json:"permission,omitempty"`
Children []*menuNode `json:"children,omitempty"`
}
// buildMenuTree 将菜单列表构建成树结构
func buildMenuTree(menus []models.SystemMenu, pid int64) []*menuNode {
var tree []*menuNode
for _, m := range menus {
if m.Pid == pid {
node := &menuNode{
ID: m.ID,
Pid: m.Pid,
Title: m.Title,
Sort: m.Sort,
Status: m.Status,
IsVisible: m.IsVisible,
Views: parseViews(m.Views),
Type: m.Type,
}
if m.Path != nil {
node.Path = *m.Path
}
if m.ComponentPath != nil {
node.ComponentPath = *m.ComponentPath
}
if m.Icon != nil {
node.Icon = *m.Icon
}
if m.Permission != nil {
node.Permission = *m.Permission
}
// 递归查找子菜单
children := buildMenuTree(menus, int64(m.ID))
if len(children) > 0 {
node.Children = children
}
tree = append(tree, node)
}
}
return tree
}
// UpdateMenuStatus 更新菜单状态
// 路由PATCH /platform/menu/status/:id
func (c *AdminMenuController) UpdateMenuStatus() {
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
if err != nil || id == 0 {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "无效菜单ID"}
_ = c.ServeJSON()
return
}
var body struct {
Status *int8 `json:"status"`
}
rawBody, _ := io.ReadAll(c.Ctx.Request.Body)
if err := json.Unmarshal(rawBody, &body); err != nil || body.Status == nil {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "参数错误"}
_ = c.ServeJSON()
return
}
_, err = models.Orm.QueryTable(new(models.SystemMenu)).
Filter("id", id).
Update(map[string]interface{}{"status": *body.Status})
if err != nil {
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "更新失败: " + err.Error()}
_ = c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "success": true}
_ = c.ServeJSON()
}
// CreateMenu 创建菜单
// 路由POST /platform/createmenu
func (c *AdminMenuController) CreateMenu() {
payload, ok := c.parseMenuPayload(true)
if !ok {
return
}
menu := models.SystemMenu{
Pid: valueInt64(payload.Pid, 0),
Title: strings.TrimSpace(valueString(payload.Title, "")),
Sort: valueInt64(payload.Sort, 0),
Status: valueInt8(payload.Status, 1),
IsVisible: ptrInt8(valueInt8(payload.IsVisible, 1)),
Views: ptrString(viewsJSON(payload.Views)),
Type: valueInt8(payload.Type, 1),
}
menu.Path = ptrString(valueString(payload.Path, ""))
menu.ComponentPath = ptrString(valueString(payload.ComponentPath, ""))
menu.Icon = ptrString(valueString(payload.Icon, ""))
menu.Permission = ptrString(valueString(payload.Permission, ""))
id, err := models.Orm.Insert(&menu)
if err != nil {
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "创建失败: " + err.Error()}
_ = c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "创建成功",
"data": map[string]interface{}{"id": id},
}
_ = c.ServeJSON()
}
// UpdateMenu 更新菜单
// 路由PUT /platform/updatemenu/:id
func (c *AdminMenuController) UpdateMenu() {
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
if err != nil || id == 0 {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "无效菜单ID"}
_ = c.ServeJSON()
return
}
payload, ok := c.parseMenuPayload(false)
if !ok {
return
}
update := map[string]interface{}{
"pid": valueInt64(payload.Pid, 0),
"title": strings.TrimSpace(valueString(payload.Title, "")),
"path": valueString(payload.Path, ""),
"component_path": valueString(payload.ComponentPath, ""),
"icon": valueString(payload.Icon, ""),
"sort": valueInt64(payload.Sort, 0),
"status": valueInt8(payload.Status, 1),
"is_visible": valueInt8(payload.IsVisible, 1),
"views": viewsJSON(payload.Views),
"type": valueInt8(payload.Type, 1),
"permission": valueString(payload.Permission, ""),
}
_, err = models.Orm.QueryTable(new(models.SystemMenu)).
Filter("id", id).
Update(update)
if err != nil {
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "更新失败: " + err.Error()}
_ = c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "更新成功"}
_ = c.ServeJSON()
}
// DeleteMenu 删除菜单
// 路由DELETE /platform/deletemenu/:id
func (c *AdminMenuController) DeleteMenu() {
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
if err != nil || id == 0 {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "无效菜单ID"}
_ = c.ServeJSON()
return
}
_, err = models.Orm.QueryTable(new(models.SystemMenu)).Filter("id", id).Delete()
if err != nil {
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "删除失败: " + err.Error()}
_ = c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "删除成功", "success": true}
_ = c.ServeJSON()
}
func (c *AdminMenuController) parseMenuPayload(needTitle bool) (*menuPayload, bool) {
rawBody, _ := io.ReadAll(c.Ctx.Request.Body)
var payload menuPayload
if err := json.Unmarshal(rawBody, &payload); err != nil {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "参数错误"}
_ = c.ServeJSON()
return nil, false
}
if needTitle && strings.TrimSpace(valueString(payload.Title, "")) == "" {
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "菜单名称不能为空"}
_ = c.ServeJSON()
return nil, false
}
return &payload, true
}
func valueString(v *string, def string) string {
if v == nil {
return def
}
return *v
}
func valueInt8(v *int8, def int8) int8 {
if v == nil {
return def
}
return *v
}
func valueInt64(v *int64, def int64) int64 {
if v == nil {
return def
}
return *v
}
func ptrString(v string) *string {
return &v
}
func ptrInt8(v int8) *int8 {
return &v
}