Barcode generation with Boombuler
This commit is contained in:
parent
4cc563f6f1
commit
07b186c986
1
go.mod
1
go.mod
@ -63,6 +63,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go v1.55.7
|
github.com/aws/aws-sdk-go v1.55.7
|
||||||
|
github.com/boombuler/barcode v1.1.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.3
|
github.com/golang-jwt/jwt/v5 v5.2.3
|
||||||
github.com/redis/go-redis/v9 v9.19.0
|
github.com/redis/go-redis/v9 v9.19.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
|||||||
2
go.sum
2
go.sum
@ -42,6 +42,8 @@ github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE
|
|||||||
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
|
||||||
|
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
|
|||||||
@ -5,8 +5,11 @@ import (
|
|||||||
"apskel-pos-be/internal/constants"
|
"apskel-pos-be/internal/constants"
|
||||||
"apskel-pos-be/internal/contract"
|
"apskel-pos-be/internal/contract"
|
||||||
"apskel-pos-be/internal/logger"
|
"apskel-pos-be/internal/logger"
|
||||||
|
"apskel-pos-be/internal/pkg/qrcode"
|
||||||
"apskel-pos-be/internal/util"
|
"apskel-pos-be/internal/util"
|
||||||
"apskel-pos-be/internal/validator"
|
"apskel-pos-be/internal/validator"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -16,12 +19,14 @@ import (
|
|||||||
type TableHandler struct {
|
type TableHandler struct {
|
||||||
tableService TableService
|
tableService TableService
|
||||||
tableValidator *validator.TableValidator
|
tableValidator *validator.TableValidator
|
||||||
|
baseURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTableHandler(tableService TableService, tableValidator *validator.TableValidator) *TableHandler {
|
func NewTableHandler(tableService TableService, tableValidator *validator.TableValidator, baseURL string) *TableHandler {
|
||||||
return &TableHandler{
|
return &TableHandler{
|
||||||
tableService: tableService,
|
tableService: tableService,
|
||||||
tableValidator: tableValidator,
|
tableValidator: tableValidator,
|
||||||
|
baseURL: baseURL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,3 +291,45 @@ func (h *TableHandler) GetOccupiedTables(c *gin.Context) {
|
|||||||
|
|
||||||
util.HandleResponse(c.Writer, c.Request, response, "TableHandler::GetOccupiedTables")
|
util.HandleResponse(c.Writer, c.Request, response, "TableHandler::GetOccupiedTables")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *TableHandler) GenerateQRCode(c *gin.Context) {
|
||||||
|
ctx := c.Request.Context()
|
||||||
|
|
||||||
|
id := c.Param("id")
|
||||||
|
tableID, err := uuid.Parse(id)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(ctx).WithError(err).Error("TableHandler::GenerateQRCode -> Invalid table ID")
|
||||||
|
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid table ID")
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "TableHandler::GenerateQRCode")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := h.tableService.GetTableToken(ctx, tableID)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(ctx).WithError(err).Error("TableHandler::GenerateQRCode -> table not found")
|
||||||
|
validationResponseError := contract.NewResponseError(constants.NotFoundErrorCode, constants.TableEntity, "Table not found")
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "TableHandler::GenerateQRCode")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
selfOrderURL := fmt.Sprintf("%s/api/v1/self-order/table/%s", h.baseURL, token)
|
||||||
|
|
||||||
|
size := 256
|
||||||
|
if sizeStr := c.Query("size"); sizeStr != "" {
|
||||||
|
if s, err := strconv.Atoi(sizeStr); err == nil && s > 0 && s <= 1024 {
|
||||||
|
size = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pngBytes, err := qrcode.GeneratePNG(selfOrderURL, size)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(ctx).WithError(err).Error("TableHandler::GenerateQRCode -> QR generation failed")
|
||||||
|
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.TableEntity, "Failed to generate QR code")
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "TableHandler::GenerateQRCode")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Header("Content-Type", "image/png")
|
||||||
|
c.Header("Content-Disposition", fmt.Sprintf("inline; filename=\"table-%s-qr.png\"", tableID))
|
||||||
|
c.Data(http.StatusOK, "image/png", pngBytes)
|
||||||
|
}
|
||||||
|
|||||||
@ -17,4 +17,5 @@ type TableService interface {
|
|||||||
ReleaseTable(ctx context.Context, tableID uuid.UUID, req *contract.ReleaseTableRequest) *contract.Response
|
ReleaseTable(ctx context.Context, tableID uuid.UUID, req *contract.ReleaseTableRequest) *contract.Response
|
||||||
GetAvailableTables(ctx context.Context, outletID uuid.UUID) *contract.Response
|
GetAvailableTables(ctx context.Context, outletID uuid.UUID) *contract.Response
|
||||||
GetOccupiedTables(ctx context.Context, outletID uuid.UUID) *contract.Response
|
GetOccupiedTables(ctx context.Context, outletID uuid.UUID) *contract.Response
|
||||||
|
GetTableToken(ctx context.Context, tableID uuid.UUID) (string, error)
|
||||||
}
|
}
|
||||||
|
|||||||
32
internal/pkg/qrcode/generator.go
Normal file
32
internal/pkg/qrcode/generator.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package qrcode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image/png"
|
||||||
|
|
||||||
|
"github.com/boombuler/barcode"
|
||||||
|
"github.com/boombuler/barcode/qr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GeneratePNG(content string, size int) ([]byte, error) {
|
||||||
|
if size <= 0 {
|
||||||
|
size = 256
|
||||||
|
}
|
||||||
|
|
||||||
|
qrCode, err := qr.Encode(content, qr.M, qr.Auto)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
qrCode, err = barcode.Scale(qrCode, size, size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := png.Encode(&buf, qrCode); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
@ -207,6 +207,14 @@ func (p *TableProcessor) GetOccupiedTables(ctx context.Context, outletID uuid.UU
|
|||||||
return responses, nil
|
return responses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *TableProcessor) GetTokenByID(ctx context.Context, id uuid.UUID) (string, error) {
|
||||||
|
table, err := p.tableRepo.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return table.Token, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *TableProcessor) mapTableToResponse(table *entities.Table) *models.TableResponse {
|
func (p *TableProcessor) mapTableToResponse(table *entities.Table) *models.TableResponse {
|
||||||
response := &models.TableResponse{
|
response := &models.TableResponse{
|
||||||
ID: table.ID,
|
ID: table.ID,
|
||||||
|
|||||||
@ -70,7 +70,7 @@ func NewRouter(cfg *config.Config, healthHandler *handler.HealthHandler, authSer
|
|||||||
paymentMethodHandler: handler.NewPaymentMethodHandler(paymentMethodService, paymentMethodValidator),
|
paymentMethodHandler: handler.NewPaymentMethodHandler(paymentMethodService, paymentMethodValidator),
|
||||||
analyticsHandler: handler.NewAnalyticsHandler(analyticsService, transformer.NewTransformer()),
|
analyticsHandler: handler.NewAnalyticsHandler(analyticsService, transformer.NewTransformer()),
|
||||||
reportHandler: handler.NewReportHandler(reportService, userService),
|
reportHandler: handler.NewReportHandler(reportService, userService),
|
||||||
tableHandler: handler.NewTableHandler(tableService, tableValidator),
|
tableHandler: handler.NewTableHandler(tableService, tableValidator, cfg.Server.BaseUrl),
|
||||||
unitHandler: handler.NewUnitHandler(unitService),
|
unitHandler: handler.NewUnitHandler(unitService),
|
||||||
ingredientHandler: handler.NewIngredientHandler(ingredientService),
|
ingredientHandler: handler.NewIngredientHandler(ingredientService),
|
||||||
productRecipeHandler: handler.NewProductRecipeHandler(productRecipeService),
|
productRecipeHandler: handler.NewProductRecipeHandler(productRecipeService),
|
||||||
@ -323,6 +323,7 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
|||||||
tables.DELETE("/:id", r.tableHandler.Delete)
|
tables.DELETE("/:id", r.tableHandler.Delete)
|
||||||
tables.POST("/:id/occupy", r.tableHandler.OccupyTable)
|
tables.POST("/:id/occupy", r.tableHandler.OccupyTable)
|
||||||
tables.POST("/:id/release", r.tableHandler.ReleaseTable)
|
tables.POST("/:id/release", r.tableHandler.ReleaseTable)
|
||||||
|
tables.GET("/:id/qr", r.tableHandler.GenerateQRCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
ingredients := protected.Group("/ingredients")
|
ingredients := protected.Group("/ingredients")
|
||||||
|
|||||||
@ -152,3 +152,7 @@ func (s *TableServiceImpl) GetOccupiedTables(ctx context.Context, outletID uuid.
|
|||||||
|
|
||||||
return contract.BuildSuccessResponse(responses)
|
return contract.BuildSuccessResponse(responses)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TableServiceImpl) GetTableToken(ctx context.Context, tableID uuid.UUID) (string, error) {
|
||||||
|
return s.tableProcessor.GetTokenByID(ctx, tableID)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user