wms-be/modules/menu/controller/menu_controller.go

273 lines
8.5 KiB
Go

package controller
import (
"errors"
"net/http"
"github.com/Caknoooo/go-gin-clean-starter/modules/menu/dto"
"github.com/Caknoooo/go-gin-clean-starter/modules/menu/query"
"github.com/Caknoooo/go-gin-clean-starter/modules/menu/service"
"github.com/Caknoooo/go-gin-clean-starter/pkg/constants"
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
"github.com/Caknoooo/go-pagination"
"github.com/gin-gonic/gin"
"github.com/samber/do"
"gorm.io/gorm"
)
type (
MenuController interface {
CreateMenu(ctx *gin.Context)
GetMenuByID(ctx *gin.Context)
GetMenuByName(ctx *gin.Context)
GetMenus(ctx *gin.Context)
UpdateMenu(ctx *gin.Context)
DeleteMenu(ctx *gin.Context)
}
menuController struct {
menuService service.MenuService
db *gorm.DB
}
)
// CreateMenu godoc
// @Summary Create a new menu
// @Description Create a new menu with the provided information
// @Tags Menus
// @Accept json
// @Produce json
// @Param request body dto.MenuCreateRequest true "Menu creation data"
// @Success 200 {object} utils.Response
// @Failure 400 {object} utils.Response
// @Router /menus [post]
func (m *menuController) CreateMenu(ctx *gin.Context) {
var req dto.MenuCreateRequest
if err := ctx.ShouldBind(&req); err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil)
ctx.AbortWithStatusJSON(http.StatusBadRequest, res)
return
}
in := dto.MenuResponse{
Name: req.Name,
IconUrl: req.IconUrl,
Url: req.Url,
Sequence: req.Sequence,
Mode: req.Mode,
Status: req.Status,
}
if req.ParentID != nil {
in.Parent = &dto.MenuParentResponse{ID: req.ParentID.String()}
}
res, err := m.menuService.Create(ctx.Request.Context(), in)
if err != nil {
resp := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_MENU, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, resp)
return
}
resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_CREATE_MENU, res)
ctx.JSON(http.StatusOK, resp)
}
// GetMenus godoc
// @Summary Get list of menus
// @Description Get paginated list of menus with filtering and sorting capabilities
// @Tags Menus
// @Accept json
// @Produce json
// @Param name query string false "Filter by name (partial match)"
// @Param parent_id query string false "Filter by parent menu ID"
// @Param includes query []string false "Include related entities"
// @Param page query int false "Page number (default: 1)"
// @Param page_size query int false "Page size (default: 10)"
// @Success 200 {object} utils.ResponseWithPagination
// @Failure 400 {object} utils.Response
// @Router /menus [get]
func (m *menuController) GetMenus(ctx *gin.Context) {
_ = ctx.MustGet("client_id").(string)
var filter = &query.MenuFilter{
// ClientID: clientId,
Name: ctx.Query("name"),
ParentID: ctx.Query("parent_id"),
Includes: ctx.QueryArray("includes"),
}
filter.BindPagination(ctx)
ctx.ShouldBindQuery(filter)
menus, total, err := pagination.PaginatedQueryWithIncludable[query.M_Menu](m.db, filter)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_LIST_MENU, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
menuResponses := ToMenuResponses(menus)
paginationResponse := pagination.CalculatePagination(filter.Pagination, total)
response := pagination.NewPaginatedResponse(http.StatusOK, dto.MESSAGE_SUCCESS_GET_LIST_MENU, menuResponses, paginationResponse)
ctx.JSON(http.StatusOK, response)
}
// GetMenuByID godoc
// @Summary Get menu by ID
// @Description Get detailed information of a specific menu by its ID
// @Tags Menus
// @Accept json
// @Produce json
// @Param id path string true "Menu ID"
// @Success 200 {object} utils.Response
// @Failure 400 {object} utils.Response
// @Failure 404 {object} utils.Response
// @Router /menus/{id} [get]
func (m *menuController) GetMenuByID(ctx *gin.Context) {
id := ctx.Param("id")
if id == "" {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_MENU, "id is required", nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
res, err := m.menuService.GetById(ctx.Request.Context(), id)
if err != nil {
resErr := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_MENU, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, resErr)
return
}
resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_MENU, res)
ctx.JSON(http.StatusOK, resp)
}
// GetMenuByName godoc
// @Summary Get menu by name
// @Description Get menu information by its name
// @Tags Menus
// @Accept json
// @Produce json
// @Param name query string true "Menu name"
// @Success 200 {object} utils.Response
// @Failure 400 {object} utils.Response
// @Router /menus/by-name [get]
func (m *menuController) GetMenuByName(ctx *gin.Context) {
name := ctx.Query("name")
if name == "" {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_MENU, "name query is required", nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
res, err := m.menuService.GetByName(ctx.Request.Context(), name)
if err != nil {
resErr := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_MENU, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, resErr)
return
}
resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_MENU, res)
ctx.JSON(http.StatusOK, resp)
}
// UpdateMenu godoc
// @Summary Update menu
// @Description Update menu information by ID
// @Tags Menus
// @Accept json
// @Produce json
// @Param id path string true "Menu ID"
// @Param request body dto.MenuUpdateRequest true "Menu update data"
// @Success 200 {object} utils.Response
// @Failure 400 {object} utils.Response
// @Router /menus/{id} [put]
func (m *menuController) UpdateMenu(ctx *gin.Context) {
var req dto.MenuUpdateRequest
if err := ctx.ShouldBind(&req); err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil)
ctx.AbortWithStatusJSON(http.StatusBadRequest, res)
return
}
menuId := ctx.Param("id")
result, err := m.menuService.Update(ctx.Request.Context(), menuId, req)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UPDATE_MENU, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UPDATE_MENU, result)
ctx.JSON(http.StatusOK, resp)
}
// DeleteMenu godoc
// @Summary Delete menu
// @Description Delete a menu by ID
// @Tags Menus
// @Accept json
// @Produce json
// @Param id path string true "Menu ID"
// @Success 200 {object} utils.Response
// @Failure 400 {object} utils.Response
// @Failure 404 {object} utils.Response
// @Router /menus/{id} [delete]
func (m *menuController) DeleteMenu(ctx *gin.Context) {
id := ctx.Param("id")
if id == "" {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_MENU, "id is required", nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
if err := m.menuService.Delete(ctx.Request.Context(), id); err != nil {
// if record not found, return 404
if errors.Is(err, gorm.ErrRecordNotFound) {
ctx.JSON(http.StatusNotFound, gin.H{"error": "menu not found"})
return
}
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_MENU, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_DELETE_MENU, nil)
ctx.JSON(http.StatusOK, resp)
}
func NewMenuController(i *do.Injector, ms service.MenuService) MenuController {
db := do.MustInvokeNamed[*gorm.DB](i, constants.DB)
return &menuController{
menuService: ms,
db: db,
}
}
// Fungsi transform dari entities ke DTO
func ToMenuResponse(menu query.M_Menu) dto.MenuResponse {
response := dto.MenuResponse{
ID: menu.ID.String(),
Name: menu.Name,
IconUrl: menu.IconUrl,
Url: menu.Url,
Sequence: menu.Sequence,
Mode: menu.Mode,
Status: menu.Status,
}
// Handle parent jika ada
if menu.Parent != nil {
response.Parent = &dto.MenuParentResponse{
ID: menu.Parent.ID.String(),
Name: menu.Parent.Name,
}
}
return response
}
func ToMenuResponses(menus []query.M_Menu) []dto.MenuResponse {
responses := make([]dto.MenuResponse, len(menus))
for i, menu := range menus {
responses[i] = ToMenuResponse(menu)
}
return responses
}