252 lines
6.9 KiB
Go
252 lines
6.9 KiB
Go
package menuService
|
|
|
|
import (
|
|
"slices"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
|
categoryrepo "git.ma-al.com/goc_daniel/b2b/app/repos/categoryRepo"
|
|
routesRepo "git.ma-al.com/goc_daniel/b2b/app/repos/routesRepo"
|
|
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
|
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
|
)
|
|
|
|
type MenuService struct {
|
|
categoryRepo categoryrepo.UICategoryRepo
|
|
routesRepo routesRepo.UIRoutesRepo
|
|
}
|
|
|
|
func New() *MenuService {
|
|
return &MenuService{
|
|
categoryRepo: categoryrepo.New(),
|
|
routesRepo: routesRepo.New(),
|
|
}
|
|
}
|
|
|
|
func (s *MenuService) GetCategoryTree(root_category_id uint, id_lang uint) (*model.Category, error) {
|
|
all_categories, err := s.categoryRepo.RetrieveMenuCategories(id_lang)
|
|
if err != nil {
|
|
return &model.Category{}, err
|
|
}
|
|
|
|
// remove blacklisted categories
|
|
// to do so, we detach them from the main tree
|
|
for i := 0; i < len(all_categories); i++ {
|
|
if slices.Contains(constdata.CATEGORY_BLACKLIST, all_categories[i].CategoryID) {
|
|
all_categories[i].ParentID = all_categories[i].CategoryID
|
|
}
|
|
}
|
|
|
|
iso_code := all_categories[0].IsoCode
|
|
s.appendAdditional(&all_categories, id_lang, iso_code)
|
|
|
|
// find the root
|
|
root_index := 0
|
|
root_found := false
|
|
for i := 0; i < len(all_categories); i++ {
|
|
if all_categories[i].CategoryID == root_category_id {
|
|
root_index = i
|
|
root_found = true
|
|
break
|
|
}
|
|
}
|
|
if !root_found {
|
|
return &model.Category{}, responseErrors.ErrNoRootFound
|
|
}
|
|
|
|
// now create the children and reorder them according to position
|
|
id_to_index := make(map[uint]int)
|
|
for i := 0; i < len(all_categories); i++ {
|
|
all_categories[i].Visited = false
|
|
id_to_index[all_categories[i].CategoryID] = i
|
|
}
|
|
|
|
children_indices := make(map[int][]ChildWithPosition)
|
|
for i := 0; i < len(all_categories); i++ {
|
|
parent_index := id_to_index[all_categories[i].ParentID]
|
|
children_indices[parent_index] = append(children_indices[parent_index], ChildWithPosition{Index: i, Position: all_categories[i].Position})
|
|
}
|
|
|
|
for key := range children_indices {
|
|
sort.Sort(ByPosition(children_indices[key]))
|
|
}
|
|
|
|
// finally, create the tree
|
|
tree, success := s.createTree(root_index, &all_categories, &children_indices)
|
|
if !success {
|
|
return &tree, responseErrors.ErrCircularDependency
|
|
}
|
|
|
|
return &tree, nil
|
|
}
|
|
|
|
func (s *MenuService) createTree(index int, all_categories *([]model.ScannedCategory), children_indices *(map[int][]ChildWithPosition)) (model.Category, bool) {
|
|
node := s.scannedToNormalCategory((*all_categories)[index])
|
|
|
|
if (*all_categories)[index].Visited {
|
|
return node, false
|
|
}
|
|
(*all_categories)[index].Visited = true
|
|
|
|
for i := 0; i < len((*children_indices)[index]); i++ {
|
|
next_child, success := s.createTree((*children_indices)[index][i].Index, all_categories, children_indices)
|
|
if !success {
|
|
return node, false
|
|
}
|
|
node.Children = append(node.Children, next_child)
|
|
}
|
|
|
|
(*all_categories)[index].Visited = false // just in case we have a "diamond" diagram
|
|
return node, true
|
|
}
|
|
|
|
func (s *MenuService) GetRoutes(id_lang uint) ([]model.Route, error) {
|
|
return s.routesRepo.GetRoutes(id_lang)
|
|
}
|
|
|
|
func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) model.Category {
|
|
var normal model.Category
|
|
// normal.Active = scanned.Active
|
|
normal.CategoryID = scanned.CategoryID
|
|
normal.Label = scanned.Name
|
|
// normal.Active = scanned.Active == 1
|
|
normal.Params = model.CategoryParams{CategoryID: normal.CategoryID, LinkRewrite: scanned.LinkRewrite, Locale: scanned.IsoCode, Filter: scanned.Filter}
|
|
normal.Children = []model.Category{}
|
|
return normal
|
|
}
|
|
|
|
type ChildWithPosition struct {
|
|
Index int
|
|
Position uint
|
|
}
|
|
type ByPosition []ChildWithPosition
|
|
|
|
func (a ByPosition) Len() int { return len(a) }
|
|
func (a ByPosition) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a ByPosition) Less(i, j int) bool { return a[i].Position < a[j].Position }
|
|
|
|
func (s *MenuService) GetBreadcrumb(root_category_id uint, start_category_id uint, id_lang uint) ([]model.CategoryInBreadcrumb, error) {
|
|
all_categories, err := s.categoryRepo.RetrieveMenuCategories(id_lang)
|
|
if err != nil {
|
|
return []model.CategoryInBreadcrumb{}, err
|
|
}
|
|
|
|
iso_code := all_categories[0].IsoCode
|
|
s.appendAdditional(&all_categories, id_lang, iso_code)
|
|
|
|
breadcrumb := []model.CategoryInBreadcrumb{}
|
|
|
|
start_index := 0
|
|
start_found := false
|
|
for i := 0; i < len(all_categories); i++ {
|
|
if all_categories[i].CategoryID == start_category_id {
|
|
start_index = i
|
|
start_found = true
|
|
break
|
|
}
|
|
}
|
|
if !start_found {
|
|
return []model.CategoryInBreadcrumb{}, responseErrors.ErrStartCategoryNotFound
|
|
}
|
|
|
|
// map category ids to indices
|
|
id_to_index := make(map[uint]int)
|
|
for i := 0; i < len(all_categories); i++ {
|
|
all_categories[i].Visited = false
|
|
id_to_index[all_categories[i].CategoryID] = i
|
|
}
|
|
|
|
// do a simple graph traversal, always jumping from node to its parent
|
|
index := start_index
|
|
success := true
|
|
for {
|
|
if all_categories[index].Visited {
|
|
success = false
|
|
break
|
|
}
|
|
all_categories[index].Visited = true
|
|
|
|
var next_category model.CategoryInBreadcrumb
|
|
next_category.CategoryID = all_categories[index].CategoryID
|
|
next_category.Name = all_categories[index].Name
|
|
breadcrumb = append(breadcrumb, next_category)
|
|
|
|
if all_categories[index].CategoryID == root_category_id {
|
|
break
|
|
}
|
|
next_index, ok := id_to_index[all_categories[index].ParentID]
|
|
if !ok {
|
|
success = false
|
|
break
|
|
}
|
|
index = next_index
|
|
}
|
|
|
|
slices.Reverse(breadcrumb)
|
|
|
|
if !success {
|
|
return breadcrumb, responseErrors.ErrRootNeverReached
|
|
}
|
|
|
|
return breadcrumb, nil
|
|
}
|
|
|
|
func (s *MenuService) GetTopMenu(languageId uint, roleId uint) ([]*model.B2BTopMenu, error) {
|
|
items, err := s.routesRepo.GetTopMenu(languageId, roleId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
menuMap := make(map[int]*model.B2BTopMenu, len(items))
|
|
roots := make([]*model.B2BTopMenu, 0)
|
|
|
|
for i := range items {
|
|
menu := &items[i]
|
|
menu.Children = make([]*model.B2BTopMenu, 0)
|
|
menuMap[menu.MenuID] = menu
|
|
}
|
|
|
|
for i := range items {
|
|
menu := &items[i]
|
|
|
|
if menu.ParentID == nil {
|
|
roots = append(roots, menu)
|
|
continue
|
|
}
|
|
|
|
parent, ok := menuMap[*menu.ParentID]
|
|
if !ok {
|
|
// fallback for orphaned nodes
|
|
roots = append(roots, menu)
|
|
continue
|
|
}
|
|
|
|
parent.Children = append(parent.Children, menu)
|
|
}
|
|
|
|
return roots, nil
|
|
}
|
|
|
|
func (s *MenuService) appendAdditional(all_categories *[]model.ScannedCategory, id_lang uint, iso_code string) {
|
|
for i := 0; i < len(*all_categories); i++ {
|
|
(*all_categories)[i].Filter = "category_id_in=" + strconv.Itoa(int((*all_categories)[i].CategoryID))
|
|
}
|
|
|
|
var additional model.ScannedCategory
|
|
additional.CategoryID = 10001
|
|
additional.Name = "New Products"
|
|
additional.Active = 1
|
|
additional.Position = 10
|
|
additional.ParentID = 2
|
|
additional.IsRoot = 0
|
|
additional.LinkRewrite = i18n.T___(id_lang, "category.new_products")
|
|
additional.IsoCode = iso_code
|
|
|
|
additional.Visited = false
|
|
additional.Filter = "is_new_in=true"
|
|
|
|
*all_categories = append(*all_categories, additional)
|
|
}
|