179 lines
6.0 KiB
Go
179 lines
6.0 KiB
Go
package schema
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.guxuan/haibei/internal/config"
|
|
"github.guxuan/haibei/pkg/errors"
|
|
"github.guxuan/haibei/pkg/util"
|
|
)
|
|
|
|
const (
|
|
MenuStatusDisabled = "disabled"
|
|
MenuStatusEnabled = "enabled"
|
|
)
|
|
|
|
var (
|
|
MenusOrderParams = []util.OrderByParam{
|
|
{Field: "sequence", Direction: util.DESC},
|
|
{Field: "created_at", Direction: util.DESC},
|
|
}
|
|
)
|
|
|
|
// Menu management for RBAC
|
|
type Menu struct {
|
|
ID string `json:"id" gorm:"size:20;primarykey;"` // Unique ID
|
|
Code string `json:"code" gorm:"size:32;index;"` // Code of menu (unique for each level)
|
|
Name string `json:"name" gorm:"size:128;index"` // Display name of menu
|
|
Description string `json:"description" gorm:"size:1024"` // Details about menu
|
|
Sequence int `json:"sequence" gorm:"index;"` // Sequence for sorting (Order by desc)
|
|
Type string `json:"type" gorm:"size:20;index"` // Type of menu (page, button)
|
|
Path string `json:"path" gorm:"size:255;"` // Access path of menu
|
|
Properties string `json:"properties" gorm:"type:text;"` // Properties of menu (JSON)
|
|
Status string `json:"status" gorm:"size:20;index"` // Status of menu (enabled, disabled)
|
|
ParentID string `json:"parent_id" gorm:"size:20;index;"` // Parent ID (From Menu.ID)
|
|
ParentPath string `json:"parent_path" gorm:"size:255;index;"` // Parent path (split by .)
|
|
Children *Menus `json:"children" gorm:"-"` // Child menus
|
|
CreatedAt time.Time `json:"created_at" gorm:"index;"` // Create time
|
|
UpdatedAt time.Time `json:"updated_at" gorm:"index;"` // Update time
|
|
Resources MenuResources `json:"resources" gorm:"-"` // Resources of menu
|
|
}
|
|
|
|
func (a *Menu) TableName() string {
|
|
return config.C.FormatTableName("menu")
|
|
}
|
|
|
|
// Defining the query parameters for the `Menu` struct.
|
|
type MenuQueryParam struct {
|
|
util.PaginationParam
|
|
CodePath string `form:"code"` // Code path (like xxx.xxx.xxx)
|
|
LikeName string `form:"name"` // Display name of menu
|
|
IncludeResources bool `form:"includeResources"` // Include resources
|
|
InIDs []string `form:"-"` // Include menu IDs
|
|
Status string `form:"-"` // Status of menu (disabled, enabled)
|
|
ParentID string `form:"-"` // Parent ID (From Menu.ID)
|
|
ParentPathPrefix string `form:"-"` // Parent path (split by .)
|
|
UserID string `form:"-"` // User ID
|
|
RoleID string `form:"-"` // Role ID
|
|
}
|
|
|
|
// Defining the query options for the `Menu` struct.
|
|
type MenuQueryOptions struct {
|
|
util.QueryOptions
|
|
}
|
|
|
|
// Defining the query result for the `Menu` struct.
|
|
type MenuQueryResult struct {
|
|
Data Menus
|
|
PageResult *util.PaginationResult
|
|
}
|
|
|
|
// Defining the slice of `Menu` struct.
|
|
type Menus []*Menu
|
|
|
|
func (a Menus) Len() int {
|
|
return len(a)
|
|
}
|
|
|
|
func (a Menus) Less(i, j int) bool {
|
|
if a[i].Sequence == a[j].Sequence {
|
|
return a[i].CreatedAt.Unix() > a[j].CreatedAt.Unix()
|
|
}
|
|
return a[i].Sequence > a[j].Sequence
|
|
}
|
|
|
|
func (a Menus) Swap(i, j int) {
|
|
a[i], a[j] = a[j], a[i]
|
|
}
|
|
|
|
func (a Menus) ToMap() map[string]*Menu {
|
|
m := make(map[string]*Menu)
|
|
for _, item := range a {
|
|
m[item.ID] = item
|
|
}
|
|
return m
|
|
}
|
|
|
|
func (a Menus) SplitParentIDs() []string {
|
|
parentIDs := make([]string, 0, len(a))
|
|
idMapper := make(map[string]struct{})
|
|
for _, item := range a {
|
|
if _, ok := idMapper[item.ID]; ok {
|
|
continue
|
|
}
|
|
idMapper[item.ID] = struct{}{}
|
|
if pp := item.ParentPath; pp != "" {
|
|
for _, pid := range strings.Split(pp, util.TreePathDelimiter) {
|
|
if pid == "" {
|
|
continue
|
|
}
|
|
if _, ok := idMapper[pid]; ok {
|
|
continue
|
|
}
|
|
parentIDs = append(parentIDs, pid)
|
|
idMapper[pid] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
return parentIDs
|
|
}
|
|
|
|
func (a Menus) ToTree() Menus {
|
|
var list Menus
|
|
m := a.ToMap()
|
|
for _, item := range a {
|
|
if item.ParentID == "" {
|
|
list = append(list, item)
|
|
continue
|
|
}
|
|
if parent, ok := m[item.ParentID]; ok {
|
|
if parent.Children == nil {
|
|
children := Menus{item}
|
|
parent.Children = &children
|
|
continue
|
|
}
|
|
*parent.Children = append(*parent.Children, item)
|
|
}
|
|
}
|
|
return list
|
|
}
|
|
|
|
// Defining the data structure for creating a `Menu` struct.
|
|
type MenuForm struct {
|
|
Code string `json:"code" binding:"required,max=32"` // Code of menu (unique for each level)
|
|
Name string `json:"name" binding:"required,max=128"` // Display name of menu
|
|
Description string `json:"description"` // Details about menu
|
|
Sequence int `json:"sequence"` // Sequence for sorting (Order by desc)
|
|
Type string `json:"type" binding:"required,oneof=page button"` // Type of menu (page, button)
|
|
Path string `json:"path"` // Access path of menu
|
|
Properties string `json:"properties"` // Properties of menu (JSON)
|
|
Status string `json:"status" binding:"required,oneof=disabled enabled"` // Status of menu (enabled, disabled)
|
|
ParentID string `json:"parent_id"` // Parent ID (From Menu.ID)
|
|
Resources MenuResources `json:"resources"` // Resources of menu
|
|
}
|
|
|
|
// A validation function for the `MenuForm` struct.
|
|
func (a *MenuForm) Validate() error {
|
|
if v := a.Properties; v != "" {
|
|
if !json.Valid([]byte(v)) {
|
|
return errors.BadRequest("", "invalid properties")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *MenuForm) FillTo(menu *Menu) error {
|
|
menu.Code = a.Code
|
|
menu.Name = a.Name
|
|
menu.Description = a.Description
|
|
menu.Sequence = a.Sequence
|
|
menu.Type = a.Type
|
|
menu.Path = a.Path
|
|
menu.Properties = a.Properties
|
|
menu.Status = a.Status
|
|
menu.ParentID = a.ParentID
|
|
return nil
|
|
}
|