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) Delete(ctx context.Context, menuId string) error } type menuService struct { menuRepository repository.MenuRepository jwtService service.JWTService db *gorm.DB } // 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, } }