273 lines
8.5 KiB
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
|
|
}
|