2 Commits
Author SHA1 Message Date
achmad.nauval 8a860a791e referencen 2026-05-20 08:39:24 +07:00
achmad.nauval.0510 55fb837379 references 2026-02-11 09:16:53 +07:00
3 changed files with 153 additions and 18 deletions

No files matched your search

+45 -17
View File
@@ -6,6 +6,7 @@ import (
models "api-service/internal/models"
referenceModels "api-service/internal/models/reference"
queryUtils "api-service/internal/utils/query"
"api-service/internal/utils/validation"
"api-service/pkg/logger"
"context"
"database/sql"
@@ -118,10 +119,37 @@ func (c *InMemoryCache) DeleteByPrefix(prefix string) {
type ReferenceHandler struct {
db database.Service
queryBuilder *queryUtils.QueryBuilder
validator *validation.DynamicValidator
cache *InMemoryCache
}
func NewReferenceHandler() *ReferenceHandler {
queryBuilder := queryUtils.NewQueryBuilder(queryUtils.DBTypePostgreSQL).
SetAllowedColumns([]string{
// tabel ref_service_type -> Penjamin
"id","name","active",
// tabel ref_payment_type
"id","name","active",
// tabel visit_type
"id","name","description","active","fk_ref_healthcare_status_id",
// tabel D
"id", "status", "sort", "user_created", "date_created",
"user_updated", "date_updated", "Jenis", "Pelayanan",
"Dinas", "Kelompok_obyek", "Kode_tarif", "Tarif", "Satuan",
"Tarif_overtime", "Satuan_overtime", "Rekening_pokok",
"Rekening_denda", "Uraian_1", "Uraian_2", "Uraian_3",
})
return &ReferenceHandler{
db: referencedb,
queryBuilder: queryBuilder,
validator: validation.NewDynamicValidator(queryBuilder),
cache: NewInMemoryCache(), // Initialize in-memory cache
}
}
// =============================================================================
// HANDLER ENDPOINTS (READ-ONLY)
// =============================================================================
@@ -152,7 +180,7 @@ func (h *ReferenceHandler) GetRefServiceType(c *gin.Context) {
Fields: []queryUtils.SelectField{
{Expression: "rst.id", Alias: "id"},
{Expression: "rst.name", Alias: "name"},
{Expression: "rst.active", Alias: "medical_record_number"},
{Expression: "rst.active", Alias: "status"},
},
Sort: []queryUtils.SortField{
{Column: "rst.name", Order: "ASC"},
@@ -200,7 +228,7 @@ func (h *ReferenceHandler) GetRefServiceType(c *gin.Context) {
if cachedData, found := h.cache.Get(cacheKey); found {
logger.Info("Cache hit for search", map[string]interface{}{"search": search})
if patients, ok := cachedData.([]referenceModels.RefServiceType); ok {
if serviceTypeData, ok := cachedData.([]referenceModels.RefServiceType); ok {
var aggregateData *models.AggregateData
if c.Query("include_summary") == "true" {
fullFilterGroups := []queryUtils.FilterGroup{
@@ -216,11 +244,11 @@ func (h *ReferenceHandler) GetRefServiceType(c *gin.Context) {
}
}
meta := h.calculateMeta(query.Limit, query.Offset, len(patients))
// meta := h.calculateMeta(query.Limit, query.Offset, len(serviceTypeData))
response := referenceModels.RefServiceTypeGetResponse{
Message: "Data patient berhasil diambil (dari cache)",
Data: patients,
Meta: meta,
Message: "Data Jenis Layanan berhasil diambil",
Data: serviceTypeData,
// Meta: meta,
}
if aggregateData != nil {
@@ -249,19 +277,19 @@ func (h *ReferenceHandler) GetRefServiceType(c *gin.Context) {
}
// Process results into structured format
patients := h.processQueryResultsRST(results)
serviceTypeData := h.processQueryResultsRST(results)
// Get total count for pagination metadata
total, err := h.getTotalCount(ctx, dbConn, query)
// total, err := h.getTotalCount(ctx, dbConn, query)
if err != nil {
h.logAndRespondError(c, "Failed to get total count", err, http.StatusInternalServerError)
return
}
// Cache results if this was a search query
if useCache && len(patients) > 0 {
h.cache.Set(cacheKey, patients, 15*time.Minute)
logger.Info("Cached search results", map[string]interface{}{"search": search, "count": len(patients)})
if useCache && len(serviceTypeData) > 0 {
h.cache.Set(cacheKey, serviceTypeData, 15*time.Minute)
logger.Info("Cached search results", map[string]interface{}{"search": search, "count": len(serviceTypeData)})
}
// Get aggregate data if requested
@@ -275,11 +303,11 @@ func (h *ReferenceHandler) GetRefServiceType(c *gin.Context) {
}
// Build and send response
meta := h.calculateMeta(query.Limit, query.Offset, total)
// meta := h.calculateMeta(query.Limit, query.Offset, total)
response := referenceModels.RefServiceTypeGetResponse{
Message: "Data patient berhasil diambil",
Data: patients,
Meta: meta,
Message: "Data Jenis Layanan berhasil diambil",
Data: serviceTypeData,
// Meta: meta,
}
if aggregateData != nil {
@@ -412,7 +440,7 @@ func (h *ReferenceHandler) GetRefPaymentType(c *gin.Context) {
}
// Process results into structured format
patients := h.processQueryResultsRST(results)
patients := h.processQueryResultsRPT(results)
// Get total count for pagination metadata
total, err := h.getTotalCount(ctx, dbConn, query)
@@ -439,7 +467,7 @@ func (h *ReferenceHandler) GetRefPaymentType(c *gin.Context) {
// Build and send response
meta := h.calculateMeta(query.Limit, query.Offset, total)
response := referenceModels.RefServiceTypeGetResponse{
response := referenceModels.RefPaymentTypeGetResponse{
Message: "Data patient berhasil diambil",
Data: patients,
Meta: meta,
+99 -1
View File
@@ -6,6 +6,17 @@ import (
"encoding/json"
)
// tabel reference.ref_visit_status
type RefVisitStatus struct {
ID int64 `json:"id" db:"id"`
Name sql.NullString `json:"name,omitempty" db:"name"`
Description sql.NullString `json:"description,omitempty" db:"description"`
Active sql.NullBool `json:"active,omitempty" db:"active"`
FkRefHealthcareStatusID sql.NullInt64 `json:"fk_ref_healthcare_status_id,omitempty" db:"fk_ref_healthcare_status_id"`
}
// table ref_service_type table
type RefServiceType struct {
ID int64 `json:"id" db:"id"`
@@ -178,6 +189,48 @@ func (r *RefHealthcareType) GetActive() bool {
return false
}
// table ref_healtcare_status table
type RefHealthcareStatus struct {
ID int64 `json:"id" db:"id"`
Name sql.NullString `json:"name,omitempty" db:"name"`
Active sql.NullBool `json:"active,omitempty" db:"active"`
}
// Custom JSON marshaling untuk RefHealthcareStatus agar NULL values tidak muncul di response
func (r RefHealthcareStatus) MarshalJSON() ([]byte, error) {
type Alias RefHealthcareStatus
aux := &struct {
*Alias
Name *string `json:"name,omitempty"`
Active *bool `json:"active,omitempty"`
}{
Alias: (*Alias)(&r),
}
if r.Name.Valid {
aux.Name = &r.Name.String
}
if r.Active.Valid {
aux.Active = &r.Active.Bool
}
return json.Marshal(aux)
}
func (r *RefHealthcareStatus) GetName() string {
if r.Name.Valid {
return r.Name.String
}
return ""
}
func (r *RefHealthcareStatus) GetActive() bool {
if r.Active.Valid {
return r.Active.Bool
}
return false
}
// Response structs for RefServiceType
type RefServiceTypeGetByIDResponse struct {
Message string `json:"message"`
@@ -187,7 +240,7 @@ type RefServiceTypeGetByIDResponse struct {
type RefServiceTypeGetResponse struct {
Message string `json:"message"`
Data []RefServiceType `json:"data"`
Meta models.MetaResponse `json:"meta"`
// Meta models.MetaResponse `json:"meta"`
Summary *models.AggregateData `json:"summary,omitempty"`
}
@@ -352,4 +405,49 @@ type RefHealthcareTypeDeleteResponse struct {
type RefHealthcareTypeFilter struct {
Status *string `json:"status,omitempty" form:"status"`
Search *string `json:"search,omitempty" form:"search"`
}
// Response structs for RefHealthcareStatus
type RefHealthcareStatusGetByIDResponse struct {
Message string `json:"message"`
Data *RefHealthcareStatus `json:"data"`
}
type RefHealthcareStatusGetResponse struct {
Message string `json:"message"`
Data []RefHealthcareStatus `json:"data"`
Meta models.MetaResponse `json:"meta"`
Summary *models.AggregateData `json:"summary,omitempty"`
}
type RefHealthcareStatusCreateRequest struct {
Name *string `json:"name" validate:"required,min=1,max=20"`
Active *bool `json:"active"`
}
type RefHealthcareStatusCreateResponse struct {
Message string `json:"message"`
Data *RefHealthcareStatus `json:"data"`
}
type RefHealthcareStatusUpdateRequest struct {
ID *int `json:"-" validate:"required,min=1"` // Menggunakan *int64 agar selaras dengan tipe ID di model
Name *string `json:"name" validate:"required,min=1,max=20"`
Active *bool `json:"active"`
}
type RefHealthcareStatusUpdateResponse struct {
Message string `json:"message"`
Data *RefHealthcareStatus `json:"data"`
}
type RefHealthcareStatusDeleteResponse struct {
Message string `json:"message"`
ID string `json:"id"` // Format string, biasanya dari path param
}
type RefHealthcareStatusFilter struct {
Status *string `json:"status,omitempty" form:"status"`
Search *string `json:"search,omitempty" form:"search"`
}
+9
View File
@@ -8,6 +8,7 @@ import (
pagesRolpagesHandlers "api-service/internal/handlers/pages"
pasienPasienHandlers "api-service/internal/handlers/pasien"
patientMspatientHandlers "api-service/internal/handlers/patient"
referenceReferenceHandler "api-service/internal/handlers/reference"
websocketHandlers "api-service/internal/handlers/websocket"
"api-service/internal/middleware"
services "api-service/internal/services/auth"
@@ -141,6 +142,14 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine {
pasienPasienGroup.GET("/by-location", pasienPasienHandler.GetPasienByLocation)
}
referenceReferenceHandler := referenceReferenceHandler.NewReferenceHandler()
referenceGroup := v1.Group("/ref")
{
referenceGroup.GET("/jenisPenjamin", referenceReferenceHandler.GetRefPaymentType)
referenceGroup.GET("/jenisPelayanan", referenceReferenceHandler.GetRefServiceType)
referenceGroup.GET("/statusReservasi", referenceReferenceHandler.GetRefVisitType)
}
// Rol_pages endpoints
pagesRolpagesHandler := pagesRolpagesHandlers.NewRol_pagesHandler()
pagesRolpagesGroup := v1.Group("/pages")