package handler import ( "net/http" "strings" "go-backend-template/internal/constants" "go-backend-template/internal/contract" "go-backend-template/internal/logger" "go-backend-template/internal/util" "github.com/gin-gonic/gin" ) type DukcapilHandler struct { dukcapilService DukcapilService } func NewDukcapilHandler(dukcapilService DukcapilService) *DukcapilHandler { return &DukcapilHandler{dukcapilService: dukcapilService} } // FaceMatch handles POST /api/v1/dukcapil/face-match (1:N face recognition). func (h *DukcapilHandler) FaceMatch(c *gin.Context) { var req contract.FaceMatchRequest if err := c.ShouldBindJSON(&req); err != nil { logger.FromContext(c.Request.Context()).WithError(err).Error("DukcapilHandler::FaceMatch -> request binding failed") h.sendValidationError(c, "Invalid request body", constants.MalformedFieldErrorCode) return } if strings.TrimSpace(req.TransactionID) == "" { h.sendValidationError(c, "transaction_id is required", constants.MissingFieldErrorCode) return } if strings.TrimSpace(req.TransactionSource) == "" { h.sendValidationError(c, "transaction_source is required", constants.MissingFieldErrorCode) return } if strings.TrimSpace(req.Threshold) == "" { h.sendValidationError(c, "threshold is required", constants.MissingFieldErrorCode) return } if strings.TrimSpace(req.Image) == "" { h.sendValidationError(c, "image is required (base64-encoded)", constants.MissingFieldErrorCode) return } res, err := h.dukcapilService.FaceMatch(c.Request.Context(), &req) if err != nil { logger.FromContext(c.Request.Context()).WithError(err).Error("DukcapilHandler::FaceMatch -> upstream call failed") c.JSON(http.StatusBadGateway, &contract.ErrorResponse{ Error: "upstream_error", Message: err.Error(), Code: http.StatusBadGateway, Details: map[string]interface{}{"entity": constants.DukcapilHandlerEntity}, }) return } logger.FromContext(c.Request.Context()).Infof("DukcapilHandler::FaceMatch -> tid=%s errorCode=%s matches=%d", res.TID, res.ErrorCode, len(res.Matches)) util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(res), "DukcapilHandler::FaceMatch") } func (h *DukcapilHandler) sendValidationError(c *gin.Context, message, code string) { c.JSON(http.StatusBadRequest, &contract.ErrorResponse{ Error: "validation_error", Message: message, Code: http.StatusBadRequest, Details: map[string]interface{}{ "error_code": code, "entity": constants.DukcapilHandlerEntity, }, }) }