// Package rujukan handles Rujukan BPJS services // Generated on: 2025-09-07 11:01:18 package handlers import ( "context" "encoding/json" "net/http" "strings" "time" "api-service/internal/config" "api-service/internal/models" "api-service/internal/models/vclaim/rujukan" "api-service/internal/services/bpjs" "api-service/pkg/logger" "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" "github.com/google/uuid" ) // RujukanHandler handles Rujukan BPJS services type RujukanHandler struct { service services.VClaimService validator *validator.Validate logger logger.Logger config config.BpjsConfig } // RujukanHandlerConfig contains configuration for RujukanHandler type RujukanHandlerConfig struct { BpjsConfig config.BpjsConfig Logger logger.Logger Validator *validator.Validate } // NewRujukanHandler creates a new RujukanHandler func NewRujukanHandler(cfg RujukanHandlerConfig) *RujukanHandler { return &RujukanHandler{ service: services.NewService(cfg.BpjsConfig), validator: cfg.Validator, logger: cfg.Logger, config: cfg.BpjsConfig, } } // CreateRujukan godoc // @Summary Create new Rujukan // @Description Create new Rujukan in BPJS system // @Tags Rujukan // @Accept json // @Produce json // @Security ApiKeyAuth // @Param X-Request-ID header string false "Request ID for tracking" // @Param request body rujukan.RujukanRequest true "Rujukan data" // @Success 201 {object} rujukan.RujukanResponse "Successfully created Rujukan" // @Failure 400 {object} models.ErrorResponseBpjs "Bad request" // @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized" // @Failure 409 {object} models.ErrorResponseBpjs "Conflict" // @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" // @Router /Rujukan/:norujukan [post] func (h *RujukanHandler) CreateRujukan(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() // Generate request ID if not present requestID := c.GetHeader("X-Request-ID") if requestID == "" { requestID = uuid.New().String() c.Header("X-Request-ID", requestID) } h.logger.Info("Processing CreateRujukan request", map[string]interface{}{ "request_id": requestID, "endpoint": "/Rujukan", }) // Bind and validate request body var req rujukan.RujukanRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind request body", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Invalid request body: " + err.Error(), RequestID: requestID, }) return } // Validate request structure if err := h.validator.Struct(&req); err != nil { h.logger.Error("Request validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Validation failed: " + err.Error(), RequestID: requestID, }) return } // Call service method var response rujukan.RujukanResponse resp, err := h.service.PostRawResponse(ctx, "/Rujukan", req) if err != nil { h.logger.Error("Failed to create Rujukan", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) // Handle specific BPJS errors if strings.Contains(err.Error(), "409") || strings.Contains(err.Error(), "conflict") { c.JSON(http.StatusConflict, models.ErrorResponseBpjs{ Status: "error", Message: "Rujukan already exists or conflict occurred", RequestID: requestID, }) return } c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, }) return } // Map the raw response response.MetaData = resp.MetaData if resp.Response != nil { response.Data = &rujukan.RujukanData{} if respStr, ok := resp.Response.(string); ok { // Decrypt the response string consID, secretKey, _, tstamp, _ := h.config.SetHeader() decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp) if err != nil { h.logger.Error("Failed to decrypt response", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) } else { json.Unmarshal([]byte(decryptedResp), response.Data) } } else if respMap, ok := resp.Response.(map[string]interface{}); ok { // Response is already unmarshaled JSON if dataMap, exists := respMap["rujukan"]; exists { dataBytes, _ := json.Marshal(dataMap) json.Unmarshal(dataBytes, response.Data) } else { // Try to unmarshal the whole response respBytes, _ := json.Marshal(resp.Response) json.Unmarshal(respBytes, response.Data) } } } // Ensure response has proper fields response.Status = "success" response.RequestID = requestID h.logger.Info("Successfully created Rujukan", map[string]interface{}{ "request_id": requestID, }) c.JSON(http.StatusCreated, response) } // UpdateRujukan godoc // @Summary Update existing Rujukan // @Description Update existing Rujukan in BPJS system // @Tags Rujukan // @Accept json // @Produce json // @Security ApiKeyAuth // @Param X-Request-ID header string false "Request ID for tracking" // @Param request body rujukan.RujukanRequest true "Rujukan update data" // @Success 200 {object} rujukan.RujukanResponse "Successfully updated Rujukan" // @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters" // @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" // @Failure 404 {object} models.ErrorResponseBpjs "Not found - Rujukan not found" // @Failure 409 {object} models.ErrorResponseBpjs "Conflict - update conflict occurred" // @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" // @Router /Rujukan/:norujukan [put] func (h *RujukanHandler) UpdateRujukan(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() // Generate request ID if not present requestID := c.GetHeader("X-Request-ID") if requestID == "" { requestID = uuid.New().String() c.Header("X-Request-ID", requestID) } h.logger.Info("Processing UpdateRujukan request", map[string]interface{}{ "request_id": requestID, "endpoint": "/Rujukan", }) // Extract path parameters // Bind and validate request body var req rujukan.RujukanRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind request body", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Invalid request body: " + err.Error(), RequestID: requestID, }) return } // Validate request structure if err := h.validator.Struct(&req); err != nil { h.logger.Error("Request validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Validation failed: " + err.Error(), RequestID: requestID, }) return } // Call service method var response rujukan.RujukanResponse resp, err := h.service.PutRawResponse(ctx, "/Rujukan", req) if err != nil { h.logger.Error("Failed to update Rujukan", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) // Handle specific BPJS errors if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") { c.JSON(http.StatusNotFound, models.ErrorResponseBpjs{ Status: "error", Message: "Rujukan not found", RequestID: requestID, }) return } if strings.Contains(err.Error(), "409") || strings.Contains(err.Error(), "conflict") { c.JSON(http.StatusConflict, models.ErrorResponseBpjs{ Status: "error", Message: "Update conflict occurred", RequestID: requestID, }) return } c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, }) return } // Map the raw response response.MetaData = resp.MetaData if resp.Response != nil { response.Data = &rujukan.RujukanData{} if respStr, ok := resp.Response.(string); ok { // Decrypt the response string consID, secretKey, _, tstamp, _ := h.config.SetHeader() decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp) if err != nil { h.logger.Error("Failed to decrypt response", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) } else { json.Unmarshal([]byte(decryptedResp), response.Data) } } else if respMap, ok := resp.Response.(map[string]interface{}); ok { // Response is already unmarshaled JSON if dataMap, exists := respMap["rujukan"]; exists { dataBytes, _ := json.Marshal(dataMap) json.Unmarshal(dataBytes, response.Data) } else { // Try to unmarshal the whole response respBytes, _ := json.Marshal(resp.Response) json.Unmarshal(respBytes, response.Data) } } } // Ensure response has proper fields response.Status = "success" response.RequestID = requestID h.logger.Info("Successfully updated Rujukan", map[string]interface{}{ "request_id": requestID, }) c.JSON(http.StatusOK, response) } // DeleteRujukan godoc // @Summary Delete existing Rujukan // @Description Delete existing Rujukan from BPJS system // @Tags Rujukan // @Accept json // @Produce json // @Security ApiKeyAuth // @Param X-Request-ID header string false "Request ID for tracking" // @Success 200 {object} rujukan.RujukanResponse "Successfully deleted Rujukan" // @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters" // @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" // @Failure 404 {object} models.ErrorResponseBpjs "Not found - Rujukan not found" // @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" // @Router /Rujukan/:norujukan [delete] func (h *RujukanHandler) DeleteRujukan(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() // Generate request ID if not present requestID := c.GetHeader("X-Request-ID") if requestID == "" { requestID = uuid.New().String() c.Header("X-Request-ID", requestID) } h.logger.Info("Processing DeleteRujukan request", map[string]interface{}{ "request_id": requestID, "endpoint": "/Rujukan", }) // Extract path parameters // Call service method var response rujukan.RujukanResponse resp, err := h.service.DeleteRawResponse(ctx, "/Rujukan") if err != nil { h.logger.Error("Failed to delete Rujukan", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) // Handle specific BPJS errors if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") { c.JSON(http.StatusNotFound, models.ErrorResponseBpjs{ Status: "error", Message: "Rujukan not found", RequestID: requestID, }) return } c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, }) return } // Map the raw response response.MetaData = resp.MetaData if resp.Response != nil { response.Data = &rujukan.RujukanData{} if respStr, ok := resp.Response.(string); ok { // Decrypt the response string consID, secretKey, _, tstamp, _ := h.config.SetHeader() decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp) if err != nil { h.logger.Error("Failed to decrypt response", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) } else { json.Unmarshal([]byte(decryptedResp), response.Data) } } else if respMap, ok := resp.Response.(map[string]interface{}); ok { // Response is already unmarshaled JSON if dataMap, exists := respMap["rujukan"]; exists { dataBytes, _ := json.Marshal(dataMap) json.Unmarshal(dataBytes, response.Data) } else { // Try to unmarshal the whole response respBytes, _ := json.Marshal(resp.Response) json.Unmarshal(respBytes, response.Data) } } } else { // For delete operations, sometimes there's no data in response response.Data = nil } // Ensure response has proper fields response.Status = "success" response.RequestID = requestID h.logger.Info("Successfully deleted Rujukan", map[string]interface{}{ "request_id": requestID, }) c.JSON(http.StatusOK, response) } // CreateRujukanbalik godoc // @Summary Create new Rujukanbalik // @Description Create new Rujukanbalik in BPJS system // @Tags Rujukan // @Accept json // @Produce json // @Security ApiKeyAuth // @Param X-Request-ID header string false "Request ID for tracking" // @Param request body rujukan.RujukanRequest true "Rujukanbalik data" // @Success 201 {object} rujukan.RujukanResponse "Successfully created Rujukanbalik" // @Failure 400 {object} models.ErrorResponseBpjs "Bad request" // @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized" // @Failure 409 {object} models.ErrorResponseBpjs "Conflict" // @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" // @Router /Rujukanbalik/:norujukan [post] func (h *RujukanHandler) CreateRujukanbalik(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() // Generate request ID if not present requestID := c.GetHeader("X-Request-ID") if requestID == "" { requestID = uuid.New().String() c.Header("X-Request-ID", requestID) } h.logger.Info("Processing CreateRujukanbalik request", map[string]interface{}{ "request_id": requestID, "endpoint": "/Rujukanbalik", }) // Bind and validate request body var req rujukan.RujukanRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind request body", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Invalid request body: " + err.Error(), RequestID: requestID, }) return } // Validate request structure if err := h.validator.Struct(&req); err != nil { h.logger.Error("Request validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Validation failed: " + err.Error(), RequestID: requestID, }) return } // Call service method var response rujukan.RujukanResponse resp, err := h.service.PostRawResponse(ctx, "/Rujukanbalik", req) if err != nil { h.logger.Error("Failed to create Rujukanbalik", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) // Handle specific BPJS errors if strings.Contains(err.Error(), "409") || strings.Contains(err.Error(), "conflict") { c.JSON(http.StatusConflict, models.ErrorResponseBpjs{ Status: "error", Message: "Rujukanbalik already exists or conflict occurred", RequestID: requestID, }) return } c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, }) return } // Map the raw response response.MetaData = resp.MetaData if resp.Response != nil { response.Data = &rujukan.RujukanData{} if respStr, ok := resp.Response.(string); ok { // Decrypt the response string consID, secretKey, _, tstamp, _ := h.config.SetHeader() decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp) if err != nil { h.logger.Error("Failed to decrypt response", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) } else { json.Unmarshal([]byte(decryptedResp), response.Data) } } else if respMap, ok := resp.Response.(map[string]interface{}); ok { // Response is already unmarshaled JSON if dataMap, exists := respMap["rujukan"]; exists { dataBytes, _ := json.Marshal(dataMap) json.Unmarshal(dataBytes, response.Data) } else { // Try to unmarshal the whole response respBytes, _ := json.Marshal(resp.Response) json.Unmarshal(respBytes, response.Data) } } } // Ensure response has proper fields response.Status = "success" response.RequestID = requestID h.logger.Info("Successfully created Rujukanbalik", map[string]interface{}{ "request_id": requestID, }) c.JSON(http.StatusCreated, response) } // UpdateRujukanbalik godoc // @Summary Update existing Rujukanbalik // @Description Update existing Rujukanbalik in BPJS system // @Tags Rujukan // @Accept json // @Produce json // @Security ApiKeyAuth // @Param X-Request-ID header string false "Request ID for tracking" // @Param request body rujukan.RujukanRequest true "Rujukanbalik update data" // @Success 200 {object} rujukan.RujukanResponse "Successfully updated Rujukanbalik" // @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters" // @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" // @Failure 404 {object} models.ErrorResponseBpjs "Not found - Rujukanbalik not found" // @Failure 409 {object} models.ErrorResponseBpjs "Conflict - update conflict occurred" // @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" // @Router /Rujukanbalik/:norujukan [put] func (h *RujukanHandler) UpdateRujukanbalik(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() // Generate request ID if not present requestID := c.GetHeader("X-Request-ID") if requestID == "" { requestID = uuid.New().String() c.Header("X-Request-ID", requestID) } h.logger.Info("Processing UpdateRujukanbalik request", map[string]interface{}{ "request_id": requestID, "endpoint": "/Rujukanbalik", }) // Extract path parameters // Bind and validate request body var req rujukan.RujukanRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind request body", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Invalid request body: " + err.Error(), RequestID: requestID, }) return } // Validate request structure if err := h.validator.Struct(&req); err != nil { h.logger.Error("Request validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Validation failed: " + err.Error(), RequestID: requestID, }) return } // Call service method var response rujukan.RujukanResponse resp, err := h.service.PutRawResponse(ctx, "/Rujukanbalik", req) if err != nil { h.logger.Error("Failed to update Rujukanbalik", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) // Handle specific BPJS errors if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") { c.JSON(http.StatusNotFound, models.ErrorResponseBpjs{ Status: "error", Message: "Rujukanbalik not found", RequestID: requestID, }) return } if strings.Contains(err.Error(), "409") || strings.Contains(err.Error(), "conflict") { c.JSON(http.StatusConflict, models.ErrorResponseBpjs{ Status: "error", Message: "Update conflict occurred", RequestID: requestID, }) return } c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, }) return } // Map the raw response response.MetaData = resp.MetaData if resp.Response != nil { response.Data = &rujukan.RujukanData{} if respStr, ok := resp.Response.(string); ok { // Decrypt the response string consID, secretKey, _, tstamp, _ := h.config.SetHeader() decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp) if err != nil { h.logger.Error("Failed to decrypt response", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) } else { json.Unmarshal([]byte(decryptedResp), response.Data) } } else if respMap, ok := resp.Response.(map[string]interface{}); ok { // Response is already unmarshaled JSON if dataMap, exists := respMap["rujukan"]; exists { dataBytes, _ := json.Marshal(dataMap) json.Unmarshal(dataBytes, response.Data) } else { // Try to unmarshal the whole response respBytes, _ := json.Marshal(resp.Response) json.Unmarshal(respBytes, response.Data) } } } // Ensure response has proper fields response.Status = "success" response.RequestID = requestID h.logger.Info("Successfully updated Rujukanbalik", map[string]interface{}{ "request_id": requestID, }) c.JSON(http.StatusOK, response) } // DeleteRujukanbalik godoc // @Summary Delete existing Rujukanbalik // @Description Delete existing Rujukanbalik from BPJS system // @Tags Rujukan // @Accept json // @Produce json // @Security ApiKeyAuth // @Param X-Request-ID header string false "Request ID for tracking" // @Success 200 {object} rujukan.RujukanResponse "Successfully deleted Rujukanbalik" // @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters" // @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" // @Failure 404 {object} models.ErrorResponseBpjs "Not found - Rujukanbalik not found" // @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" // @Router /Rujukanbalik/:norujukan [delete] func (h *RujukanHandler) DeleteRujukanbalik(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() // Generate request ID if not present requestID := c.GetHeader("X-Request-ID") if requestID == "" { requestID = uuid.New().String() c.Header("X-Request-ID", requestID) } h.logger.Info("Processing DeleteRujukanbalik request", map[string]interface{}{ "request_id": requestID, "endpoint": "/Rujukanbalik", }) // Extract path parameters // Call service method var response rujukan.RujukanResponse resp, err := h.service.DeleteRawResponse(ctx, "/Rujukanbalik") if err != nil { h.logger.Error("Failed to delete Rujukanbalik", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) // Handle specific BPJS errors if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") { c.JSON(http.StatusNotFound, models.ErrorResponseBpjs{ Status: "error", Message: "Rujukanbalik not found", RequestID: requestID, }) return } c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, }) return } // Map the raw response response.MetaData = resp.MetaData if resp.Response != nil { response.Data = &rujukan.RujukanData{} if respStr, ok := resp.Response.(string); ok { // Decrypt the response string consID, secretKey, _, tstamp, _ := h.config.SetHeader() decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp) if err != nil { h.logger.Error("Failed to decrypt response", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) } else { json.Unmarshal([]byte(decryptedResp), response.Data) } } else if respMap, ok := resp.Response.(map[string]interface{}); ok { // Response is already unmarshaled JSON if dataMap, exists := respMap["rujukan"]; exists { dataBytes, _ := json.Marshal(dataMap) json.Unmarshal(dataBytes, response.Data) } else { // Try to unmarshal the whole response respBytes, _ := json.Marshal(resp.Response) json.Unmarshal(respBytes, response.Data) } } } else { // For delete operations, sometimes there's no data in response response.Data = nil } // Ensure response has proper fields response.Status = "success" response.RequestID = requestID h.logger.Info("Successfully deleted Rujukanbalik", map[string]interface{}{ "request_id": requestID, }) c.JSON(http.StatusOK, response) }