Files
apskel-pos-backend/internal/handler/enakgame_admin_handler.go
T

459 lines
15 KiB
Go
Raw Normal View History

package handler
import (
"io"
"mime/multipart"
"strings"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"apskel-pos-be/internal/appcontext"
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/models"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/util"
)
// EnakGameAdminHandler serves /marketing/enakgame (docs/rfc-enakgame.md §11). Every
// request works on the admin's own organization.
type EnakGameAdminHandler struct {
service service.EnakGameAdminService
}
func NewEnakGameAdminHandler(s service.EnakGameAdminService) *EnakGameAdminHandler {
return &EnakGameAdminHandler{service: s}
}
// voucherCodeUploadLimit caps a CSV of codes: 50 000 lines of up to 255 characters.
const voucherCodeUploadLimit = 16 << 20
// pathID reads a UUID path parameter, answering 400 when it is not one.
func pathID(c *gin.Context, name, method string) (uuid.UUID, bool) {
id, err := uuid.Parse(c.Param(name))
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "invalid "+name),
}), method)
return uuid.Nil, false
}
return id, true
}
// rawBody reads the request body, answering 400 when it cannot.
func rawBody(c *gin.Context, method string) ([]byte, bool) {
body, err := c.GetRawData()
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "cannot read the request body"),
}), method)
return nil, false
}
return body, true
}
func bindQuery(c *gin.Context, q any, method string) bool {
if err := c.ShouldBindQuery(q); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, err.Error()),
}), method)
return false
}
return true
}
// CreateGame is POST /marketing/enakgame/games.
func (h *EnakGameAdminHandler) CreateGame(c *gin.Context) {
const method = "EnakGameAdminHandler::CreateGame"
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.CreateGame(ctx, appcontext.FromGinContext(ctx), body), method)
}
// ListGames is GET /marketing/enakgame/games?status=&search=&page=&limit=.
func (h *EnakGameAdminHandler) ListGames(c *gin.Context) {
const method = "EnakGameAdminHandler::ListGames"
var q models.EnakGameListQuery
if !bindQuery(c, &q, method) {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.ListGames(ctx, appcontext.FromGinContext(ctx), q), method)
}
// GetGame is GET /marketing/enakgame/games/:id.
func (h *EnakGameAdminHandler) GetGame(c *gin.Context) {
const method = "EnakGameAdminHandler::GetGame"
id, ok := pathID(c, "id", method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.GetGame(ctx, appcontext.FromGinContext(ctx), id), method)
}
// UpdateGame is PUT /marketing/enakgame/games/:id.
func (h *EnakGameAdminHandler) UpdateGame(c *gin.Context) {
const method = "EnakGameAdminHandler::UpdateGame"
id, ok := pathID(c, "id", method)
if !ok {
return
}
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.UpdateGame(ctx, appcontext.FromGinContext(ctx), id, body), method)
}
// SetGameStatus is PUT /marketing/enakgame/games/:id/status.
func (h *EnakGameAdminHandler) SetGameStatus(c *gin.Context) {
const method = "EnakGameAdminHandler::SetGameStatus"
id, ok := pathID(c, "id", method)
if !ok {
return
}
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.SetGameStatus(ctx, appcontext.FromGinContext(ctx), id, body), method)
}
// CreateRewardConfig is POST /marketing/enakgame/games/:id/reward-configs.
func (h *EnakGameAdminHandler) CreateRewardConfig(c *gin.Context) {
const method = "EnakGameAdminHandler::CreateRewardConfig"
id, ok := pathID(c, "id", method)
if !ok {
return
}
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.CreateRewardConfig(ctx, appcontext.FromGinContext(ctx), id, body), method)
}
// ListRewardConfigs is GET /marketing/enakgame/games/:id/reward-configs.
func (h *EnakGameAdminHandler) ListRewardConfigs(c *gin.Context) {
const method = "EnakGameAdminHandler::ListRewardConfigs"
id, ok := pathID(c, "id", method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.ListRewardConfigs(ctx, appcontext.FromGinContext(ctx), id), method)
}
// ActivateRewardConfig is POST /marketing/enakgame/reward-configs/:id/activate.
func (h *EnakGameAdminHandler) ActivateRewardConfig(c *gin.Context) {
const method = "EnakGameAdminHandler::ActivateRewardConfig"
id, ok := pathID(c, "id", method)
if !ok {
return
}
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.ActivateRewardConfig(ctx, appcontext.FromGinContext(ctx), id, body), method)
}
// CreateBudget is POST /marketing/enakgame/budgets.
func (h *EnakGameAdminHandler) CreateBudget(c *gin.Context) {
const method = "EnakGameAdminHandler::CreateBudget"
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.CreateBudget(ctx, appcontext.FromGinContext(ctx), body), method)
}
// ListBudgets is GET /marketing/enakgame/budgets?scope=&page=&limit=.
func (h *EnakGameAdminHandler) ListBudgets(c *gin.Context) {
const method = "EnakGameAdminHandler::ListBudgets"
var q models.GameBudgetListQuery
if !bindQuery(c, &q, method) {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.ListBudgets(ctx, appcontext.FromGinContext(ctx), q), method)
}
// GetBudget is GET /marketing/enakgame/budgets/:id.
func (h *EnakGameAdminHandler) GetBudget(c *gin.Context) {
const method = "EnakGameAdminHandler::GetBudget"
id, ok := pathID(c, "id", method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.GetBudget(ctx, appcontext.FromGinContext(ctx), id), method)
}
// UpdateBudget is PUT /marketing/enakgame/budgets/:id.
func (h *EnakGameAdminHandler) UpdateBudget(c *gin.Context) {
const method = "EnakGameAdminHandler::UpdateBudget"
id, ok := pathID(c, "id", method)
if !ok {
return
}
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.UpdateBudget(ctx, appcontext.FromGinContext(ctx), id, body), method)
}
// DeleteBudget is DELETE /marketing/enakgame/budgets/:id.
func (h *EnakGameAdminHandler) DeleteBudget(c *gin.Context) {
const method = "EnakGameAdminHandler::DeleteBudget"
id, ok := pathID(c, "id", method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.DeleteBudget(ctx, appcontext.FromGinContext(ctx), id), method)
}
// BudgetMetrics is GET /marketing/enakgame/budgets/:id/metrics.
func (h *EnakGameAdminHandler) BudgetMetrics(c *gin.Context) {
const method = "EnakGameAdminHandler::BudgetMetrics"
id, ok := pathID(c, "id", method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.BudgetMetrics(ctx, appcontext.FromGinContext(ctx), id), method)
}
// BudgetRecommendation is GET /marketing/enakgame/budgets/:id/recommendation.
func (h *EnakGameAdminHandler) BudgetRecommendation(c *gin.Context) {
const method = "EnakGameAdminHandler::BudgetRecommendation"
id, ok := pathID(c, "id", method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.BudgetRecommendation(ctx, appcontext.FromGinContext(ctx), id), method)
}
// AcceptBudgetRecommendation is POST /marketing/enakgame/budgets/:id/recommendation/accept.
func (h *EnakGameAdminHandler) AcceptBudgetRecommendation(c *gin.Context) {
const method = "EnakGameAdminHandler::AcceptBudgetRecommendation"
id, ok := pathID(c, "id", method)
if !ok {
return
}
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.AcceptBudgetRecommendation(ctx, appcontext.FromGinContext(ctx), id, body), method)
}
// GameAnalytics is GET /marketing/enakgame/analytics/games?from=&to=&game_id=.
func (h *EnakGameAdminHandler) GameAnalytics(c *gin.Context) {
const method = "EnakGameAdminHandler::GameAnalytics"
var q models.EnakGameAnalyticsQuery
if !bindQuery(c, &q, method) {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.GameAnalytics(ctx, appcontext.FromGinContext(ctx), q), method)
}
// EconomyAnalytics is GET /marketing/enakgame/analytics/economy?from=&to=.
func (h *EnakGameAdminHandler) EconomyAnalytics(c *gin.Context) {
const method = "EnakGameAdminHandler::EconomyAnalytics"
var q models.EnakGameAnalyticsQuery
if !bindQuery(c, &q, method) {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.EconomyAnalytics(ctx, appcontext.FromGinContext(ctx), q), method)
}
// CreateVoucher is POST /marketing/vouchers.
func (h *EnakGameAdminHandler) CreateVoucher(c *gin.Context) {
const method = "EnakGameAdminHandler::CreateVoucher"
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.CreateVoucher(ctx, appcontext.FromGinContext(ctx), body), method)
}
// ListVouchers is GET /marketing/vouchers?status=&search=&page=&limit=.
func (h *EnakGameAdminHandler) ListVouchers(c *gin.Context) {
const method = "EnakGameAdminHandler::ListVouchers"
var q models.VoucherListQuery
if !bindQuery(c, &q, method) {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.ListVouchers(ctx, appcontext.FromGinContext(ctx), q), method)
}
// GetVoucher is GET /marketing/vouchers/:id.
func (h *EnakGameAdminHandler) GetVoucher(c *gin.Context) {
const method = "EnakGameAdminHandler::GetVoucher"
id, ok := pathID(c, "id", method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.GetVoucher(ctx, appcontext.FromGinContext(ctx), id), method)
}
// UpdateVoucher is PUT /marketing/vouchers/:id.
func (h *EnakGameAdminHandler) UpdateVoucher(c *gin.Context) {
const method = "EnakGameAdminHandler::UpdateVoucher"
id, ok := pathID(c, "id", method)
if !ok {
return
}
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.UpdateVoucher(ctx, appcontext.FromGinContext(ctx), id, body), method)
}
// SetVoucherStatus is PUT /marketing/vouchers/:id/status.
func (h *EnakGameAdminHandler) SetVoucherStatus(c *gin.Context) {
const method = "EnakGameAdminHandler::SetVoucherStatus"
id, ok := pathID(c, "id", method)
if !ok {
return
}
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.SetVoucherStatus(ctx, appcontext.FromGinContext(ctx), id, body), method)
}
// ImportVoucherCodes is POST /marketing/vouchers/:id/codes, with the CSV as
// a multipart file named "file" or as the request body.
func (h *EnakGameAdminHandler) ImportVoucherCodes(c *gin.Context) {
const method = "EnakGameAdminHandler::ImportVoucherCodes"
id, ok := pathID(c, "id", method)
if !ok {
return
}
var data []byte
if strings.HasPrefix(c.ContentType(), "multipart/") {
file, err := c.FormFile("file")
if err == nil {
var f multipart.File
if f, err = file.Open(); err == nil {
data, err = io.ReadAll(io.LimitReader(f, voucherCodeUploadLimit))
f.Close()
}
}
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "send the CSV as a file named \"file\""),
}), method)
return
}
} else if data, ok = rawBody(c, method); !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.ImportVoucherCodes(ctx, appcontext.FromGinContext(ctx), id, data), method)
}
// ListVoucherCodes is GET /marketing/vouchers/:id/codes?status=&page=&limit=.
func (h *EnakGameAdminHandler) ListVoucherCodes(c *gin.Context) {
const method = "EnakGameAdminHandler::ListVoucherCodes"
id, ok := pathID(c, "id", method)
if !ok {
return
}
var q models.VoucherCodeListQuery
if !bindQuery(c, &q, method) {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.ListVoucherCodes(ctx, appcontext.FromGinContext(ctx), id, q), method)
}
// CreateEvent is POST /marketing/enakgame/events.
func (h *EnakGameAdminHandler) CreateEvent(c *gin.Context) {
const method = "EnakGameAdminHandler::CreateEvent"
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.CreateEvent(ctx, appcontext.FromGinContext(ctx), body), method)
}
// ListEvents is GET /marketing/enakgame/events?status=&page=&limit=.
func (h *EnakGameAdminHandler) ListEvents(c *gin.Context) {
const method = "EnakGameAdminHandler::ListEvents"
var q models.GameEventListQuery
if !bindQuery(c, &q, method) {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.ListEvents(ctx, appcontext.FromGinContext(ctx), q), method)
}
// GetEvent is GET /marketing/enakgame/events/:id.
func (h *EnakGameAdminHandler) GetEvent(c *gin.Context) {
const method = "EnakGameAdminHandler::GetEvent"
id, ok := pathID(c, "id", method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.GetEvent(ctx, appcontext.FromGinContext(ctx), id), method)
}
// UpdateEvent is PUT /marketing/enakgame/events/:id.
func (h *EnakGameAdminHandler) UpdateEvent(c *gin.Context) {
const method = "EnakGameAdminHandler::UpdateEvent"
id, ok := pathID(c, "id", method)
if !ok {
return
}
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.UpdateEvent(ctx, appcontext.FromGinContext(ctx), id, body), method)
}
// SetEventStatus is PUT /marketing/enakgame/events/:id/status.
func (h *EnakGameAdminHandler) SetEventStatus(c *gin.Context) {
const method = "EnakGameAdminHandler::SetEventStatus"
id, ok := pathID(c, "id", method)
if !ok {
return
}
body, ok := rawBody(c, method)
if !ok {
return
}
ctx := c.Request.Context()
util.HandleResponse(c.Writer, c.Request, h.service.SetEventStatus(ctx, appcontext.FromGinContext(ctx), id, body), method)
}