perbaikan

This commit is contained in:
2025-08-24 16:18:15 +07:00
parent 9838c48eab
commit 7681c796e8
24 changed files with 2443 additions and 2057 deletions

View File

@@ -1,85 +0,0 @@
package handlers
import (
"context"
"fmt"
"net/http"
"time"
"api-service/internal/config"
services "api-service/internal/services/bpjs"
"github.com/gin-gonic/gin"
)
// DiagnosaHandler handles BPJS diagnosa operations
type DiagnosaHandler struct {
bpjsService services.VClaimService
}
// NewDiagnosaHandler creates a new DiagnosaHandler instance
func NewDiagnosaHandler(cfg config.BpjsConfig) *DiagnosaHandler {
return &DiagnosaHandler{
bpjsService: services.NewService(cfg),
}
}
// GetAll godoc
// @Summary Get all diagnosa reference data
// @Description Get all diagnosa reference data
// @Tags bpjs/reference
// @Accept json
// @Produce json
// @Success 200 {object} models.DiagnosaResponse "Success response"
// @Failure 400 {object} map[string]interface{} "Bad request"
// @Failure 404 {object} map[string]interface{} "Data not found"
// @Failure 500 {object} map[string]interface{} "Internal server error"
// @Router /api/v1/bpjs/reference/referensi/diagnosa [get]
func (h *DiagnosaHandler) GetAll(c *gin.Context) {
// Create context with timeout
ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
defer cancel()
// Build endpoint URL
endpoint := "/referensi/diagnosa"
// Call BPJS service
var result map[string]interface{}
if err := h.bpjsService.Get(ctx, endpoint, &result); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to fetch diagnosa data",
"message": err.Error(),
})
return
}
// Return successful response
c.JSON(http.StatusOK, gin.H{
"message": "Data diagnosa berhasil diambil",
"data": result,
})
}
// Helper methods for error handling and response formatting
// handleBPJSError handles BPJS service errors and returns appropriate HTTP responses
func (h *DiagnosaHandler) handleBPJSError(c *gin.Context, err error, operation string) {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to %s", operation),
"message": err.Error(),
})
}
// validateDateFormat validates if the date string is in yyyy-MM-dd format
func (h *DiagnosaHandler) validateDateFormat(dateStr string) error {
_, err := time.Parse("2006-01-02", dateStr)
return err
}
// buildSuccessResponse builds a standardized success response
func (h *DiagnosaHandler) buildSuccessResponse(message string, data interface{}) gin.H {
return gin.H{
"message": message,
"data": data,
}
}

View File

@@ -0,0 +1,24 @@
package healthcheck
import (
"api-service/internal/database"
"net/http"
"github.com/gin-gonic/gin"
)
// HealthCheckHandler handles health check requests
type HealthCheckHandler struct {
dbService database.Service
}
// NewHealthCheckHandler creates a new HealthCheckHandler
func NewHealthCheckHandler(dbService database.Service) *HealthCheckHandler {
return &HealthCheckHandler{dbService: dbService}
}
// CheckHealth checks the health of the application
func (h *HealthCheckHandler) CheckHealth(c *gin.Context) {
healthStatus := h.dbService.Health() // Call the health check function from the database service
c.JSON(http.StatusOK, healthStatus)
}

View File

@@ -1,192 +0,0 @@
package satusehat
import (
"net/http"
"api-service/internal/services/satusehat"
"github.com/gin-gonic/gin"
)
type PatientHandler struct {
service *satusehat.SatuSehatService
}
func NewPatientHandler(service *satusehat.SatuSehatService) *PatientHandler {
return &PatientHandler{
service: service,
}
}
// SearchPatientByNIK godoc
// @Summary Search patient by NIK
// @Description Search patient data from SatuSehat by National Identity Number (NIK)
// @Tags SatuSehat
// @Accept json
// @Produce json
// @Param nik query string true "National Identity Number (NIK)"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{}
// @Failure 500 {object} map[string]interface{}
// @Router /satusehat/patient/search/nik [get]
func (h *PatientHandler) SearchPatientByNIK(c *gin.Context) {
nik := c.Query("nik")
if nik == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Bad Request",
"message": "NIK parameter is required",
})
return
}
patientResp, err := h.service.SearchPatientByNIK(c.Request.Context(), nik)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Internal Server Error",
"message": err.Error(),
})
return
}
patientInfo, err := satusehat.ExtractPatientInfo(patientResp)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{
"error": "Not Found",
"message": "Patient not found",
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": patientInfo,
})
}
// SearchPatientByName godoc
// @Summary Search patient by name
// @Description Search patient data from SatuSehat by name
// @Tags SatuSehat
// @Accept json
// @Produce json
// @Param name query string true "Patient name"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{}
// @Failure 500 {object} map[string]interface{}
// @Router /satusehat/patient/search/name [get]
func (h *PatientHandler) SearchPatientByName(c *gin.Context) {
name := c.Query("name")
if name == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Bad Request",
"message": "Name parameter is required",
})
return
}
patientResp, err := h.service.SearchPatientByName(c.Request.Context(), name)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Internal Server Error",
"message": err.Error(),
})
return
}
if patientResp == nil || len(patientResp.Entry) == 0 {
c.JSON(http.StatusNotFound, gin.H{
"error": "Not Found",
"message": "Patient not found",
})
return
}
// Return all found patients
var patients []map[string]interface{}
for _, entry := range patientResp.Entry {
patientInfo := map[string]interface{}{
"id": entry.Resource.ID,
"name": satusehat.ExtractPatientName(entry.Resource.Name),
"nik": satusehat.ExtractNIK(entry.Resource.Identifier),
"gender": entry.Resource.Gender,
"birthDate": entry.Resource.BirthDate,
"address": satusehat.ExtractAddress(entry.Resource.Address),
"phone": satusehat.ExtractPhone(entry.Resource.Telecom),
"lastUpdated": entry.Resource.Meta.LastUpdated,
}
patients = append(patients, patientInfo)
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": patients,
"total": len(patients),
})
}
// CreatePatient godoc
// @Summary Create new patient
// @Description Create new patient data in SatuSehat
// @Tags SatuSehat
// @Accept json
// @Produce json
// @Param patient body map[string]interface{} true "Patient data"
// @Success 201 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{}
// @Failure 500 {object} map[string]interface{}
// @Router /satusehat/patient [post]
func (h *PatientHandler) CreatePatient(c *gin.Context) {
var patientData map[string]interface{}
if err := c.ShouldBindJSON(&patientData); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Bad Request",
"message": "Invalid JSON format",
})
return
}
response, err := h.service.CreatePatient(c.Request.Context(), patientData)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Internal Server Error",
"message": err.Error(),
})
return
}
c.JSON(http.StatusCreated, gin.H{
"success": true,
"data": response,
})
}
// GetAccessToken godoc
// @Summary Get access token
// @Description Get SatuSehat access token
// @Tags SatuSehat
// @Accept json
// @Produce json
// @Success 200 {object} map[string]interface{}
// @Failure 500 {object} map[string]interface{}
// @Router /satusehat/token [get]
func (h *PatientHandler) GetAccessToken(c *gin.Context) {
token, err := h.service.GetAccessToken(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Internal Server Error",
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": map[string]interface{}{
"access_token": token.AccessToken,
"token_type": token.TokenType,
"expires_in": token.ExpiresIn,
"scope": token.Scope,
"issued_at": token.IssuedAt,
},
})
}

View File

@@ -0,0 +1,100 @@
package swagger
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"api-service/internal/config"
)
// Handler handles Swagger documentation
type Handler struct {
config *config.Config
}
// NewHandler creates a new Swagger handler
func NewHandler(cfg *config.Config) *Handler {
return &Handler{
config: cfg,
}
}
// RegisterRoutes registers Swagger routes
func (h *Handler) RegisterRoutes(router *gin.Engine) {
// Serve Swagger UI
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// Serve OpenAPI spec
router.GET("/openapi.json", h.serveOpenAPISpec)
router.GET("/openapi.yaml", h.serveOpenAPISpecYAML)
}
// serveOpenAPISpec serves the OpenAPI JSON specification
func (h *Handler) serveOpenAPISpec(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"openapi": "3.0.0",
"info": map[string]interface{}{
"title": h.config.Swagger.Title,
"description": h.config.Swagger.Description,
"version": h.config.Swagger.Version,
"termsOfService": h.config.Swagger.TermsOfService,
"contact": map[string]interface{}{
"name": h.config.Swagger.ContactName,
"url": h.config.Swagger.ContactURL,
"email": h.config.Swagger.ContactEmail,
},
"license": map[string]interface{}{
"name": h.config.Swagger.LicenseName,
"url": h.config.Swagger.LicenseURL,
},
},
"servers": []map[string]interface{}{
{
"url": strings.Join([]string{strings.ToLower(h.config.Swagger.Schemes[0]), "://", h.config.Swagger.Host, h.config.Swagger.BasePath}, ""),
"description": "API Server",
},
},
"paths": map[string]interface{}{},
"components": map[string]interface{}{
"schemas": map[string]interface{}{},
"securitySchemes": map[string]interface{}{},
},
})
}
// serveOpenAPISpecYAML serves the OpenAPI YAML specification
func (h *Handler) serveOpenAPISpecYAML(c *gin.Context) {
c.YAML(http.StatusOK, map[string]interface{}{
"openapi": "3.0.0",
"info": map[string]interface{}{
"title": h.config.Swagger.Title,
"description": h.config.Swagger.Description,
"version": h.config.Swagger.Version,
"termsOfService": h.config.Swagger.TermsOfService,
"contact": map[string]interface{}{
"name": h.config.Swagger.ContactName,
"url": h.config.Swagger.ContactURL,
"email": h.config.Swagger.ContactEmail,
},
"license": map[string]interface{}{
"name": h.config.Swagger.LicenseName,
"url": h.config.Swagger.LicenseURL,
},
},
"servers": []map[string]interface{}{
{
"url": strings.Join([]string{strings.ToLower(h.config.Swagger.Schemes[0]), "://", h.config.Swagger.Host, h.config.Swagger.BasePath}, ""),
"description": "API Server",
},
},
"paths": map[string]interface{}{},
"components": map[string]interface{}{
"schemas": map[string]interface{}{},
"securitySchemes": map[string]interface{}{},
},
})
}

View File

@@ -7,8 +7,7 @@ import (
"time"
"api-service/internal/config"
models "api-service/internal/models/bpjs/vclaim"
vclaimModels "api-service/internal/models/vclaim"
services "api-service/internal/services/bpjs"
"github.com/gin-gonic/gin"
@@ -27,18 +26,22 @@ func NewSepHandler(cfg config.BpjsConfig) *SepHandler {
// CreateSEP godoc
// @Summary Create a new SEP
// @Description Create a new Surat Eligibilitas Peserta
// @Tags bpjs
// @Tags SEP
// @Accept json
// @Produce json
// @Param request body models.SepPostRequest true "SEP creation request"
// @Success 200 {object} models.SepResponse "SEP created successfully"
// @Param request body vclaimModels.SepPostRequest true "SEP creation request"
// @Success 200 {object} vclaimModels.SepResponse "SEP created successfully"
// @Failure 400 {object} gin.H "Invalid request"
// @Failure 500 {object} gin.H "Internal server error"
// @Router /sep [post]
func (h *SepHandler) CreateSEP(c *gin.Context) {
var req models.SepPostRequest
var req vclaimModels.SepPostRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body", "message": err.Error()})
c.JSON(http.StatusBadRequest, gin.H{
"error": "invalid body",
"message": err.Error(),
})
return
}
@@ -46,32 +49,39 @@ func (h *SepHandler) CreateSEP(c *gin.Context) {
defer cancel()
var result map[string]interface{}
if err := h.service.Post(ctx, "/SEP/2.0/insert", req, &result); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "create failed", "message": err.Error()})
if err := h.service.Post(ctx, "SEP/2.0/insert", req, &result); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "create failed",
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, models.SepResponse{
c.JSON(http.StatusOK, vclaimModels.SepResponse{
Message: "SEP berhasil dibuat",
Data: result,
})
}
// UpdateSEP godoc
// @Summary Update an existing SEP
// @Description Update an existing Surat Eligibilitas Peserta
// @Tags bpjs
// @Summary Update SEP
// @Description Update Surat Eligibilitas Peserta
// @Tags SEP
// @Accept json
// @Produce json
// @Param request body models.SepPutRequest true "SEP update request"
// @Success 200 {object} models.SepResponse "SEP updated successfully"
// @Param request body vclaimModels.SepPutRequest true "SEP update request"
// @Success 200 {object} vclaimModels.SepResponse "SEP updated successfully"
// @Failure 400 {object} gin.H "Invalid request"
// @Failure 500 {object} gin.H "Internal server error"
// @Router /sep [put]
func (h *SepHandler) UpdateSEP(c *gin.Context) {
var req models.SepPutRequest
var req vclaimModels.SepPutRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body", "message": err.Error()})
c.JSON(http.StatusBadRequest, gin.H{
"error": "invalid body",
"message": err.Error(),
})
return
}
@@ -79,85 +89,101 @@ func (h *SepHandler) UpdateSEP(c *gin.Context) {
defer cancel()
var result map[string]interface{}
if err := h.service.Put(ctx, "/SEP/2.0/update", req, &result); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "update failed", "message": err.Error()})
if err := h.service.Put(ctx, "SEP/2.0/update", req, &result); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "update failed",
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, models.SepResponse{
c.JSON(http.StatusOK, vclaimModels.SepResponse{
Message: "SEP berhasil diperbarui",
Data: result,
})
}
// DeleteSEP godoc
// @Summary Delete an existing SEP
// @Summary Delete SEP
// @Description Delete a Surat Eligibilitas Peserta by noSep
// @Tags bpjs
// @Tags SEP
// @Accept json
// @Produce json
// @Param noSep path string true "No SEP"
// @Param user query string true "User"
// @Success 200 {object} models.SepResponse "SEP deleted successfully"
// @Success 200 {object} vclaimModels.SepResponse "SEP deleted successfully"
// @Failure 400 {object} gin.H "Invalid request"
// @Failure 500 {object} gin.H "Internal server error"
// @Router /sep/{noSep} [delete]
func (h *SepHandler) DeleteSEP(c *gin.Context) {
noSep := c.Param("noSep")
user := c.Query("user")
if noSep == "" || user == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "noSep & user required"})
c.JSON(http.StatusBadRequest, gin.H{
"error": "noSep and user required",
})
return
}
body := models.SepDeleteRequest{}
body := vclaimModels.SepDeleteRequest{}
body.TSep.NoSep = noSep
body.TSep.User = user
ctx, cancel := context.WithTimeout(c, 30*time.Second)
defer cancel()
if err := h.service.Delete(ctx, "/SEP/2.0/delete", body); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "delete failed", "message": err.Error()})
if err := h.service.Delete(ctx, "SEP/2.0/delete", body); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "delete failed",
"message": err.Error(),
})
return
}
var result map[string]interface{}
c.JSON(http.StatusOK, models.SepResponse{
c.JSON(http.StatusOK, vclaimModels.SepResponse{
Message: "SEP berhasil dihapus",
Data: result,
})
}
// GetSEP godoc
// @Summary Get an existing SEP
// @Summary Get SEP
// @Description Retrieve a Surat Eligibilitas Peserta by noSep
// @Tags bpjs
// @Tags SEP
// @Accept json
// @Produce json
// @Param noSep path string true "No SEP"
// @Success 200 {object} models.SepResponse "Data SEP retrieved successfully"
// @Success 200 {object} vclaimModels.SepResponse "Data SEP retrieved successfully"
// @Failure 400 {object} gin.H "Invalid request"
// @Failure 500 {object} gin.H "Internal server error"
// @Router /sep/{noSep} [get]
func (h *SepHandler) GetSEP(c *gin.Context) {
noSep := c.Param("noSep")
if noSep == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "noSep required"})
c.JSON(http.StatusBadRequest, gin.H{
"error": "noSep required",
})
return
}
ctx, cancel := context.WithTimeout(c, 30*time.Second)
defer cancel()
endpoint := fmt.Sprintf("/SEP/%s", noSep)
endpoint := fmt.Sprintf("SEP/%s", noSep)
var result map[string]interface{}
if err := h.service.Get(ctx, endpoint, &result); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "fetch failed", "message": err.Error()})
c.JSON(http.StatusInternalServerError, gin.H{
"error": "fetch failed",
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, models.SepResponse{
c.JSON(http.StatusOK, vclaimModels.SepResponse{
Message: "Data SEP berhasil diambil",
Data: result,
})