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 }