package service import ( "context" "errors" "github.com/Caknoooo/go-gin-clean-starter/database/entities" "github.com/Caknoooo/go-gin-clean-starter/modules/auth/service" "github.com/Caknoooo/go-gin-clean-starter/modules/menu/dto" "github.com/Caknoooo/go-gin-clean-starter/modules/menu/repository" staticDto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto" "github.com/google/uuid" "gorm.io/gorm" ) type MenuService interface { Create(ctx context.Context, menu dto.MenuResponse) (dto.MenuResponse, error) GetById(ctx context.Context, menuId string) (dto.MenuResponse, error) GetByName(ctx context.Context, name string) (dto.MenuResponse, error) Update(ctx context.Context, menuId string, menu dto.MenuUpdateRequest) (dto.MenuResponse, error) GetAll(ctx context.Context) ([]dto.MenuResponse, error) Delete(ctx context.Context, menuId string) error } type menuService struct { menuRepository repository.MenuRepository jwtService service.JWTService db *gorm.DB } // GetAll implements MenuService. func (m *menuService) GetAll(ctx context.Context) ([]dto.MenuResponse, error) { entities, err := m.menuRepository.GetAll(ctx, m.db) if err != nil { return nil, err } responses := make([]dto.MenuResponse, 0, len(entities)) for _, entity := range entities { res := dto.MenuResponse{ ID: entity.ID.String(), Name: entity.Name, IconUrl: entity.IconUrl, Url: entity.Url, Sequence: entity.Sequence, Mode: entity.Mode, Status: entity.Status, } if entity.Parent != nil { res.Parent = &dto.MenuParentResponse{ ID: entity.Parent.ID.String(), Name: entity.Parent.Name, } } else if entity.ParentID != nil { res.Parent = &dto.MenuParentResponse{ID: entity.ParentID.String()} } if len(entity.Permissions) > 0 { res.Permissions = make([]staticDto.PermissionResponse, 0, len(entity.Permissions)) for _, perm := range entity.Permissions { res.Permissions = append(res.Permissions, staticDto.PermissionResponse{ ID: perm.ID.String(), Name: perm.Name, }) } } responses = append(responses, res) } return responses, nil } // Create implements MenuService. func (m *menuService) Create(ctx context.Context, menu dto.MenuResponse) (dto.MenuResponse, error) { var entity entities.M_Menu // ID: use provided or generate new if menu.ID != "" { id, err := uuid.Parse(menu.ID) if err != nil { return dto.MenuResponse{}, err } entity.ID = id } else { entity.ID = uuid.New() } // map simple fields entity.Name = menu.Name entity.IconUrl = menu.IconUrl entity.Url = menu.Url entity.Sequence = menu.Sequence entity.Mode = menu.Mode entity.Status = menu.Status // ParentID if provided if menu.Parent != nil && menu.Parent.ID != "" { pid, err := uuid.Parse(menu.Parent.ID) if err != nil { return dto.MenuResponse{}, err } entity.ParentID = &pid } else { entity.ParentID = nil } created, err := m.menuRepository.Create(ctx, m.db, entity) if err != nil { return dto.MenuResponse{}, err } // map back to DTO res := dto.MenuResponse{ ID: created.ID.String(), Name: created.Name, IconUrl: created.IconUrl, Url: created.Url, Sequence: created.Sequence, Mode: created.Mode, Status: created.Status, } if created.Parent != nil { res.Parent = &dto.MenuParentResponse{ ID: created.Parent.ID.String(), Name: created.Parent.Name, } } else if created.ParentID != nil { res.Parent = &dto.MenuParentResponse{ID: created.ParentID.String()} } return res, nil } // Delete implements MenuService. func (m *menuService) Delete(ctx context.Context, menuId string) error { return m.menuRepository.Delete(ctx, m.db, menuId) } // GetById implements MenuService. func (m *menuService) GetById(ctx context.Context, menuId string) (dto.MenuResponse, error) { entity, err := m.menuRepository.GetById(ctx, m.db, menuId) if err != nil { return dto.MenuResponse{}, err } res := dto.MenuResponse{ ID: entity.ID.String(), Name: entity.Name, IconUrl: entity.IconUrl, Url: entity.Url, Sequence: entity.Sequence, Mode: entity.Mode, Status: entity.Status, } if entity.Parent != nil { res.Parent = &dto.MenuParentResponse{ ID: entity.Parent.ID.String(), Name: entity.Parent.Name, } } else if entity.ParentID != nil { res.Parent = &dto.MenuParentResponse{ID: entity.ParentID.String()} } if len(entity.Permissions) > 0 { res.Permissions = make([]staticDto.PermissionResponse, 0, len(entity.Permissions)) for _, perm := range entity.Permissions { res.Permissions = append(res.Permissions, staticDto.PermissionResponse{ ID: perm.ID.String(), Name: perm.Name, // Description: perm.Description, // Code: perm.Code, }) } } return res, nil } // GetByName implements MenuService. func (m *menuService) GetByName(ctx context.Context, name string) (dto.MenuResponse, error) { entity, err := m.menuRepository.GetByName(ctx, m.db, name) if err != nil { return dto.MenuResponse{}, err } res := dto.MenuResponse{ ID: entity.ID.String(), Name: entity.Name, IconUrl: entity.IconUrl, Url: entity.Url, Sequence: entity.Sequence, Mode: entity.Mode, Status: entity.Status, } if entity.Parent != nil { res.Parent = &dto.MenuParentResponse{ ID: entity.Parent.ID.String(), Name: entity.Parent.Name, } } else if entity.ParentID != nil { res.Parent = &dto.MenuParentResponse{ID: entity.ParentID.String()} } return res, nil } // Update implements MenuService. func (m *menuService) Update(ctx context.Context, menuId string, menu dto.MenuUpdateRequest) (dto.MenuResponse, error) { existing, err := m.menuRepository.GetById(ctx, m.db, menuId) if err != nil { return dto.MenuResponse{}, dto.ErrMenuNotFound } if menu.Name != "" { menuByName, err := m.menuRepository.GetByName(ctx, m.db, menu.Name) if err == nil && menuByName.ID != existing.ID { return dto.MenuResponse{}, errors.New("menu name already exists") } existing.Name = menu.Name } if menu.IconUrl != "" { existing.IconUrl = menu.IconUrl } if menu.Url != "" { existing.Url = menu.Url } if menu.Mode != "" { existing.Mode = menu.Mode } if menu.Status != "" { existing.Status = menu.Status } if menu.Sequence != 0 { existing.Sequence = menu.Sequence } if menu.ParentID != nil { pid := *menu.ParentID existing.ParentID = &pid } else { existing.ParentID = nil } updated, err := m.menuRepository.Update(ctx, m.db, existing) if err != nil { return dto.MenuResponse{}, err } res := dto.MenuResponse{ ID: updated.ID.String(), Name: updated.Name, IconUrl: updated.IconUrl, Url: updated.Url, Sequence: updated.Sequence, Mode: updated.Mode, Status: updated.Status, } if updated.Parent != nil { res.Parent = &dto.MenuParentResponse{ ID: updated.Parent.ID.String(), Name: updated.Parent.Name, } } else if updated.ParentID != nil { res.Parent = &dto.MenuParentResponse{ID: updated.ParentID.String()} } return res, nil } func NewMenuService( menuRepo repository.MenuRepository, jwtService service.JWTService, db *gorm.DB, ) MenuService { return &menuService{ menuRepository: menuRepo, jwtService: jwtService, db: db, } }