forked from rachmadiyanti.annisa.3004/service_antrean
Perbaikan Generate code
This commit is contained in:
@@ -340,11 +340,6 @@ BPJS_SECRETKEY=1bV36ASDQQ3512D
|
||||
**Generate Handler untuk Retribusi:**
|
||||
|
||||
```bash
|
||||
# Generate handler dasar
|
||||
go run tools/general/generate-handler.go retribusi get post put delete
|
||||
|
||||
# Generate dengan fitur advanced
|
||||
go run tools/general/generate-handler.go retribusi get post put delete dynamic search stats
|
||||
|
||||
# Config
|
||||
go run tools/general/generate-handler.go --config tools/general/services-config.yaml --verbose
|
||||
@@ -352,7 +347,7 @@ go run tools/general/generate-handler.go --config tools/general/services-config.
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
OBANESTHESI0003
|
||||
## 🚀 Deployment
|
||||
|
||||
### 🐳 Docker Deployment
|
||||
|
||||
@@ -394,22 +394,22 @@ func loadKeycloakConfig() KeycloakConfig {
|
||||
|
||||
func (c *Config) loadDatabaseConfigs() {
|
||||
// Load PostgreSQL configurations
|
||||
c.addPostgreSQLConfigs()
|
||||
// c.addPostgreSQLConfigs()
|
||||
|
||||
// Load MySQL configurations
|
||||
c.addMySQLConfigs()
|
||||
// // Load MySQL configurations
|
||||
// c.addMySQLConfigs()
|
||||
|
||||
// Load MongoDB configurations
|
||||
c.addMongoDBConfigs()
|
||||
// // Load MongoDB configurations
|
||||
// c.addMongoDBConfigs()
|
||||
|
||||
// Load SQLite configurations
|
||||
c.addSQLiteConfigs()
|
||||
// // Load SQLite configurations
|
||||
// c.addSQLiteConfigs()
|
||||
|
||||
// Load custom database configurations from environment variables
|
||||
c.loadCustomDatabaseConfigs()
|
||||
|
||||
// Remove duplicate database configurations
|
||||
c.removeDuplicateDatabases()
|
||||
// c.removeDuplicateDatabases()
|
||||
}
|
||||
|
||||
func (c *Config) removeDuplicateDatabases() {
|
||||
|
||||
@@ -127,16 +127,16 @@ func (s *service) addDatabase(name string, config config.DatabaseConfig) error {
|
||||
defer s.mu.Unlock()
|
||||
|
||||
// Check for duplicate database connections
|
||||
for existingName, existingConfig := range s.configs {
|
||||
if existingName != name &&
|
||||
existingConfig.Host == config.Host &&
|
||||
existingConfig.Port == config.Port &&
|
||||
existingConfig.Database == config.Database &&
|
||||
existingConfig.Type == config.Type {
|
||||
log.Printf("⚠️ Database %s appears to be a duplicate of %s (same host:port:database), skipping connection", name, existingName)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// for existingName, existingConfig := range s.configs {
|
||||
// if existingName != name &&
|
||||
// existingConfig.Host == config.Host &&
|
||||
// existingConfig.Port == config.Port &&
|
||||
// existingConfig.Database == config.Database &&
|
||||
// existingConfig.Type == config.Type {
|
||||
// log.Printf("⚠️ Database %s appears to be a duplicate of %s (same host:port:database), skipping connection", name, existingName)
|
||||
// return nil
|
||||
// }
|
||||
// }
|
||||
|
||||
var db *sql.DB
|
||||
var err error
|
||||
|
||||
2149
internal/handlers/pasien/pasien.go
Normal file
2149
internal/handlers/pasien/pasien.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -172,6 +172,7 @@ func NewRetribusiHandler() *RetribusiHandler {
|
||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||
// @Router /api/v1/retribusis [get]
|
||||
func (h *RetribusiHandler) GetRetribusi(c *gin.Context) {
|
||||
logger.Info("Request received", map[string]interface{}{"method": c.Request.Method, "path": c.Request.URL.Path})
|
||||
// CHANGE: Increase timeout for complex queries
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 120*time.Second)
|
||||
defer cancel()
|
||||
@@ -179,9 +180,31 @@ func (h *RetribusiHandler) GetRetribusi(c *gin.Context) {
|
||||
// CHANGE: Use the core fetchRetribusisDynamic function for all data retrieval logic.
|
||||
// We only need to build DynamicQuery from simple parameters.
|
||||
query := queryUtils.DynamicQuery{
|
||||
From: "data_retribusi",
|
||||
Fields: []queryUtils.SelectField{{Expression: "*"}},
|
||||
Sort: []queryUtils.SortField{{Column: "date_created", Order: "DESC"}},
|
||||
From: "data_retribusi",
|
||||
Fields: []queryUtils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "status"},
|
||||
{Expression: "sort"},
|
||||
{Expression: "user_created"},
|
||||
{Expression: "date_created"},
|
||||
{Expression: "user_updated"},
|
||||
{Expression: "date_updated"},
|
||||
{Expression: "Jenis"},
|
||||
{Expression: "Pelayanan"},
|
||||
{Expression: "Dinas"},
|
||||
{Expression: "Kelompok_obyek"},
|
||||
{Expression: "Kode_tarif"},
|
||||
{Expression: "Tarif"},
|
||||
{Expression: "Satuan"},
|
||||
{Expression: "Tarif_overtime"},
|
||||
{Expression: "Satuan_overtime"},
|
||||
{Expression: "Rekening_pokok"},
|
||||
{Expression: "Rekening_denda"},
|
||||
{Expression: "Uraian_1"},
|
||||
{Expression: "Uraian_2"},
|
||||
{Expression: "Uraian_3"},
|
||||
},
|
||||
Sort: []queryUtils.SortField{{Column: "date_created", Order: "DESC"}},
|
||||
}
|
||||
|
||||
// Parse pagination
|
||||
@@ -343,6 +366,7 @@ func (h *RetribusiHandler) GetRetribusi(c *gin.Context) {
|
||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||
// @Router /api/v1/retribusi/{id} [get]
|
||||
func (h *RetribusiHandler) GetRetribusiByID(c *gin.Context) {
|
||||
logger.Info("Request received", map[string]interface{}{"method": c.Request.Method, "path": c.Request.URL.Path})
|
||||
id := c.Param("id")
|
||||
if _, err := uuid.Parse(id); err != nil {
|
||||
h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
|
||||
@@ -375,8 +399,30 @@ func (h *RetribusiHandler) GetRetribusiByID(c *gin.Context) {
|
||||
defer cancel()
|
||||
|
||||
dynamicQuery := queryUtils.DynamicQuery{
|
||||
From: "data_retribusi",
|
||||
Fields: []queryUtils.SelectField{{Expression: "*"}},
|
||||
From: "data_retribusi",
|
||||
Fields: []queryUtils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "status"},
|
||||
{Expression: "sort"},
|
||||
{Expression: "user_created"},
|
||||
{Expression: "date_created"},
|
||||
{Expression: "user_updated"},
|
||||
{Expression: "date_updated"},
|
||||
{Expression: "Jenis"},
|
||||
{Expression: "Pelayanan"},
|
||||
{Expression: "Dinas"},
|
||||
{Expression: "Kelompok_obyek"},
|
||||
{Expression: "Kode_tarif"},
|
||||
{Expression: "Tarif"},
|
||||
{Expression: "Satuan"},
|
||||
{Expression: "Tarif_overtime"},
|
||||
{Expression: "Satuan_overtime"},
|
||||
{Expression: "Rekening_pokok"},
|
||||
{Expression: "Rekening_denda"},
|
||||
{Expression: "Uraian_1"},
|
||||
{Expression: "Uraian_2"},
|
||||
{Expression: "Uraian_3"},
|
||||
},
|
||||
Filters: []queryUtils.FilterGroup{{
|
||||
Filters: []queryUtils.DynamicFilter{
|
||||
{Column: "id", Operator: queryUtils.OpEqual, Value: id},
|
||||
@@ -424,6 +470,7 @@ func (h *RetribusiHandler) GetRetribusiByID(c *gin.Context) {
|
||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||
// @Router /api/v1/retribusis/dynamic [get]
|
||||
func (h *RetribusiHandler) GetRetribusiDynamic(c *gin.Context) {
|
||||
logger.Info("Request received", map[string]interface{}{"method": c.Request.Method, "path": c.Request.URL.Path})
|
||||
parser := queryUtils.NewQueryParser().SetLimits(10, 100)
|
||||
dynamicQuery, err := parser.ParseQuery(c.Request.URL.Query(), "data_retribusi")
|
||||
if err != nil {
|
||||
@@ -495,6 +542,7 @@ func (h *RetribusiHandler) GetRetribusiDynamic(c *gin.Context) {
|
||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||
// @Router /api/v1/retribusis [post]
|
||||
func (h *RetribusiHandler) CreateRetribusi(c *gin.Context) {
|
||||
logger.Info("Request received", map[string]interface{}{"method": c.Request.Method, "path": c.Request.URL.Path})
|
||||
var req retribusi.RetribusiCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
|
||||
@@ -599,6 +647,7 @@ func (h *RetribusiHandler) CreateRetribusi(c *gin.Context) {
|
||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||
// @Router /api/v1/retribusi/{id} [put]
|
||||
func (h *RetribusiHandler) UpdateRetribusi(c *gin.Context) {
|
||||
logger.Info("Request received", map[string]interface{}{"method": c.Request.Method, "path": c.Request.URL.Path})
|
||||
id := c.Param("id")
|
||||
if _, err := uuid.Parse(id); err != nil {
|
||||
h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
|
||||
@@ -748,6 +797,7 @@ func (h *RetribusiHandler) UpdateRetribusi(c *gin.Context) {
|
||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||
// @Router /api/v1/retribusi/{id} [delete]
|
||||
func (h *RetribusiHandler) DeleteRetribusi(c *gin.Context) {
|
||||
logger.Info("Request received", map[string]interface{}{"method": c.Request.Method, "path": c.Request.URL.Path})
|
||||
id := c.Param("id")
|
||||
if _, err := uuid.Parse(id); err != nil {
|
||||
h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
|
||||
@@ -843,6 +893,7 @@ func (h *RetribusiHandler) DeleteRetribusi(c *gin.Context) {
|
||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||
// @Router /api/v1/retribusis/stats [get]
|
||||
func (h *RetribusiHandler) GetRetribusiStats(c *gin.Context) {
|
||||
logger.Info("Request received", map[string]interface{}{"method": c.Request.Method, "path": c.Request.URL.Path})
|
||||
// CHANGE: Try ambil dari cache terlebih dahulu
|
||||
cacheKey := fmt.Sprintf("retribusi:stats:%s", c.Query("status"))
|
||||
if cachedData, found := h.cache.Get(cacheKey); found {
|
||||
@@ -898,6 +949,19 @@ func (h *RetribusiHandler) GetRetribusiStats(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// GetWelcome godoc
|
||||
// @Summary Get welcome message
|
||||
// @Description Returns a welcome message and logs the request
|
||||
// @Tags Retribusi
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]string "Welcome message"
|
||||
// @Router /api/v1/retribusis/welcome [get]
|
||||
func (h *RetribusiHandler) GetWelcome(c *gin.Context) {
|
||||
logger.Info("Request received", map[string]interface{}{"method": c.Request.Method, "path": c.Request.URL.Path})
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Welcome to the Retribusi API Service!"})
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
397
internal/models/pasien/pasien.go
Normal file
397
internal/models/pasien/pasien.go
Normal file
@@ -0,0 +1,397 @@
|
||||
package pasien
|
||||
|
||||
import (
|
||||
"api-service/internal/models"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Pasien represents the data structure for the m_pasien table
|
||||
// with proper null handling and optimized JSON marshaling
|
||||
type Pasien struct {
|
||||
ID int32 `json:"id" db:"id"`
|
||||
Nomr sql.NullString `json:"nomr,omitempty" db:"nomr"`
|
||||
Status sql.NullString `json:"status,omitempty" db:"status"`
|
||||
Title sql.NullString `json:"title,omitempty" db:"title"`
|
||||
Nama sql.NullString `json:"nama,omitempty" db:"nama"`
|
||||
Tempat sql.NullString `json:"tempat,omitempty" db:"tempat"`
|
||||
Tgllahir sql.NullTime `json:"tgllahir,omitempty" db:"tgllahir"`
|
||||
Jeniskelamin sql.NullString `json:"jeniskelamin,omitempty" db:"jeniskelamin"`
|
||||
Alamat sql.NullString `json:"alamat,omitempty" db:"alamat"`
|
||||
Kelurahan sql.NullInt64 `json:"kelurahan,omitempty" db:"kelurahan"`
|
||||
Kdkecamatan sql.NullInt32 `json:"kdkecamatan,omitempty" db:"kdkecamatan"`
|
||||
Kota sql.NullInt32 `json:"kota,omitempty" db:"kota"`
|
||||
Kdprovinsi sql.NullInt32 `json:"kdprovinsi,omitempty" db:"kdprovinsi"`
|
||||
Agama sql.NullInt32 `json:"agama,omitempty" db:"agama"`
|
||||
NoKartu sql.NullString `json:"noKartu,omitempty" db:"no_kartu"`
|
||||
NoktpBaru sql.NullString `json:"noktpBaru,omitempty" db:"noktp_baru"`
|
||||
CreatedAt sql.NullTime `json:"createdAt,omitempty" db:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updatedAt,omitempty" db:"updated_at"`
|
||||
Idprovinsi int32 `json:"idprovinsi" db:"idprovinsi"`
|
||||
Namaprovinsi sql.NullString `json:"namaprovinsi,omitempty" db:"namaprovinsi"`
|
||||
Idkota int32 `json:"idkota" db:"idkota"`
|
||||
Namakota sql.NullString `json:"namakota,omitempty" db:"namakota"`
|
||||
Idkecamatan int64 `json:"idkecamatan" db:"idkecamatan"`
|
||||
Namakecamatan sql.NullString `json:"namakecamatan,omitempty" db:"namakecamatan"`
|
||||
Idkelurahan int64 `json:"idkelurahan" db:"idkelurahan"`
|
||||
Namakelurahan sql.NullString `json:"namakelurahan,omitempty" db:"namakelurahan"`
|
||||
}
|
||||
|
||||
// Custom JSON marshaling for Pasien so NULL values don't appear in response
|
||||
func (r Pasien) MarshalJSON() ([]byte, error) {
|
||||
type Alias Pasien
|
||||
aux := &struct {
|
||||
*Alias
|
||||
Nomr *string `json:"nomr,omitempty"`
|
||||
Status *string `json:"status,omitempty"`
|
||||
Title *string `json:"title,omitempty"`
|
||||
Nama *string `json:"nama,omitempty"`
|
||||
Tempat *string `json:"tempat,omitempty"`
|
||||
Tgllahir *time.Time `json:"tgllahir,omitempty"`
|
||||
Jeniskelamin *string `json:"jeniskelamin,omitempty"`
|
||||
Alamat *string `json:"alamat,omitempty"`
|
||||
Kelurahan *int64 `json:"kelurahan,omitempty"`
|
||||
Kdkecamatan *int32 `json:"kdkecamatan,omitempty"`
|
||||
Kota *int32 `json:"kota,omitempty"`
|
||||
Kdprovinsi *int32 `json:"kdprovinsi,omitempty"`
|
||||
Agama *int32 `json:"agama,omitempty"`
|
||||
NoKartu *string `json:"noKartu,omitempty"`
|
||||
NoktpBaru *string `json:"noktpBaru,omitempty"`
|
||||
CreatedAt *time.Time `json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
Namaprovinsi *string `json:"namaprovinsi,omitempty"`
|
||||
Namakota *string `json:"namakota,omitempty"`
|
||||
Namakecamatan *string `json:"namakecamatan,omitempty"`
|
||||
Namakelurahan *string `json:"namakelurahan,omitempty"`
|
||||
}{
|
||||
Alias: (*Alias)(&r),
|
||||
}
|
||||
|
||||
if r.Nomr.Valid {
|
||||
aux.Nomr = &r.Nomr.String
|
||||
}
|
||||
if r.Status.Valid {
|
||||
aux.Status = &r.Status.String
|
||||
}
|
||||
if r.Title.Valid {
|
||||
aux.Title = &r.Title.String
|
||||
}
|
||||
if r.Nama.Valid {
|
||||
aux.Nama = &r.Nama.String
|
||||
}
|
||||
if r.Tempat.Valid {
|
||||
aux.Tempat = &r.Tempat.String
|
||||
}
|
||||
if r.Tgllahir.Valid {
|
||||
aux.Tgllahir = &r.Tgllahir.Time
|
||||
}
|
||||
if r.Jeniskelamin.Valid {
|
||||
aux.Jeniskelamin = &r.Jeniskelamin.String
|
||||
}
|
||||
if r.Alamat.Valid {
|
||||
aux.Alamat = &r.Alamat.String
|
||||
}
|
||||
if r.Kelurahan.Valid {
|
||||
aux.Kelurahan = &r.Kelurahan.Int64
|
||||
}
|
||||
if r.Kdkecamatan.Valid {
|
||||
aux.Kdkecamatan = &r.Kdkecamatan.Int32
|
||||
}
|
||||
if r.Kota.Valid {
|
||||
aux.Kota = &r.Kota.Int32
|
||||
}
|
||||
if r.Kdprovinsi.Valid {
|
||||
aux.Kdprovinsi = &r.Kdprovinsi.Int32
|
||||
}
|
||||
if r.Agama.Valid {
|
||||
aux.Agama = &r.Agama.Int32
|
||||
}
|
||||
if r.NoKartu.Valid {
|
||||
aux.NoKartu = &r.NoKartu.String
|
||||
}
|
||||
if r.NoktpBaru.Valid {
|
||||
aux.NoktpBaru = &r.NoktpBaru.String
|
||||
}
|
||||
if r.CreatedAt.Valid {
|
||||
aux.CreatedAt = &r.CreatedAt.Time
|
||||
}
|
||||
if r.UpdatedAt.Valid {
|
||||
aux.UpdatedAt = &r.UpdatedAt.Time
|
||||
}
|
||||
if r.Namaprovinsi.Valid {
|
||||
aux.Namaprovinsi = &r.Namaprovinsi.String
|
||||
}
|
||||
if r.Namakota.Valid {
|
||||
aux.Namakota = &r.Namakota.String
|
||||
}
|
||||
if r.Namakecamatan.Valid {
|
||||
aux.Namakecamatan = &r.Namakecamatan.String
|
||||
}
|
||||
if r.Namakelurahan.Valid {
|
||||
aux.Namakelurahan = &r.Namakelurahan.String
|
||||
}
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
|
||||
// Helper method to safely get Nomr
|
||||
func (r *Pasien) GetNomr() string {
|
||||
if r.Nomr.Valid {
|
||||
return r.Nomr.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get Status
|
||||
func (r *Pasien) GetStatus() string {
|
||||
if r.Status.Valid {
|
||||
return r.Status.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get Title
|
||||
func (r *Pasien) GetTitle() string {
|
||||
if r.Title.Valid {
|
||||
return r.Title.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get Nama
|
||||
func (r *Pasien) GetNama() string {
|
||||
if r.Nama.Valid {
|
||||
return r.Nama.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get Tempat
|
||||
func (r *Pasien) GetTempat() string {
|
||||
if r.Tempat.Valid {
|
||||
return r.Tempat.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get Tgllahir
|
||||
func (r *Pasien) GetTgllahir() time.Time {
|
||||
if r.Tgllahir.Valid {
|
||||
return r.Tgllahir.Time
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Helper method to safely get Jeniskelamin
|
||||
func (r *Pasien) GetJeniskelamin() string {
|
||||
if r.Jeniskelamin.Valid {
|
||||
return r.Jeniskelamin.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get Alamat
|
||||
func (r *Pasien) GetAlamat() string {
|
||||
if r.Alamat.Valid {
|
||||
return r.Alamat.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get Kelurahan
|
||||
func (r *Pasien) GetKelurahan() int64 {
|
||||
if r.Kelurahan.Valid {
|
||||
return r.Kelurahan.Int64
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Helper method to safely get Kdkecamatan
|
||||
func (r *Pasien) GetKdkecamatan() int32 {
|
||||
if r.Kdkecamatan.Valid {
|
||||
return r.Kdkecamatan.Int32
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Helper method to safely get Kota
|
||||
func (r *Pasien) GetKota() int32 {
|
||||
if r.Kota.Valid {
|
||||
return r.Kota.Int32
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Helper method to safely get Kdprovinsi
|
||||
func (r *Pasien) GetKdprovinsi() int32 {
|
||||
if r.Kdprovinsi.Valid {
|
||||
return r.Kdprovinsi.Int32
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Helper method to safely get Agama
|
||||
func (r *Pasien) GetAgama() int32 {
|
||||
if r.Agama.Valid {
|
||||
return r.Agama.Int32
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Helper method to safely get NoKartu
|
||||
func (r *Pasien) GetNoKartu() string {
|
||||
if r.NoKartu.Valid {
|
||||
return r.NoKartu.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get NoktpBaru
|
||||
func (r *Pasien) GetNoktpBaru() string {
|
||||
if r.NoktpBaru.Valid {
|
||||
return r.NoktpBaru.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get CreatedAt
|
||||
func (r *Pasien) GetCreatedAt() time.Time {
|
||||
if r.CreatedAt.Valid {
|
||||
return r.CreatedAt.Time
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Helper method to safely get UpdatedAt
|
||||
func (r *Pasien) GetUpdatedAt() time.Time {
|
||||
if r.UpdatedAt.Valid {
|
||||
return r.UpdatedAt.Time
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Helper method to safely get Namaprovinsi
|
||||
func (r *Pasien) GetNamaprovinsi() string {
|
||||
if r.Namaprovinsi.Valid {
|
||||
return r.Namaprovinsi.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get Namakota
|
||||
func (r *Pasien) GetNamakota() string {
|
||||
if r.Namakota.Valid {
|
||||
return r.Namakota.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get Namakecamatan
|
||||
func (r *Pasien) GetNamakecamatan() string {
|
||||
if r.Namakecamatan.Valid {
|
||||
return r.Namakecamatan.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Helper method to safely get Namakelurahan
|
||||
func (r *Pasien) GetNamakelurahan() string {
|
||||
if r.Namakelurahan.Valid {
|
||||
return r.Namakelurahan.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Response struct for delete
|
||||
type PasienDeleteResponse struct {
|
||||
Message string `json:"message"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// Response struct for by age
|
||||
type PasienAgeStatsResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// Response struct for create
|
||||
type PasienCreateResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data *Pasien `json:"data"`
|
||||
}
|
||||
|
||||
// Request struct for create
|
||||
type PasienCreateRequest struct {
|
||||
Status *string `json:"status" validate:"required,oneof=draft active inactive"`
|
||||
ID *int32 `json:"id"`
|
||||
Nomr *string `json:"nomr"`
|
||||
Title *string `json:"title" validate:"required,min=1,max=100"`
|
||||
Nama *string `json:"nama" validate:"required,min=1,max=100"`
|
||||
Tempat *string `json:"tempat"`
|
||||
Tgllahir *time.Time `json:"tgllahir"`
|
||||
Jeniskelamin *string `json:"jeniskelamin" validate:"oneof=L P"`
|
||||
Alamat *string `json:"alamat"`
|
||||
Kelurahan *int64 `json:"kelurahan"`
|
||||
Kdkecamatan *int32 `json:"kdkecamatan"`
|
||||
Kota *int32 `json:"kota"`
|
||||
Kdprovinsi *int32 `json:"kdprovinsi"`
|
||||
Agama *int32 `json:"agama"`
|
||||
NoKartu *string `json:"noKartu"`
|
||||
NoktpBaru *string `json:"noktpBaru"`
|
||||
}
|
||||
|
||||
// Response struct for GET list
|
||||
type PasienGetResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data []Pasien `json:"data"`
|
||||
Meta models.MetaResponse `json:"meta"`
|
||||
Summary *models.AggregateData `json:"summary,omitempty"`
|
||||
}
|
||||
|
||||
// Response struct for update
|
||||
type PasienUpdateResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data *Pasien `json:"data"`
|
||||
}
|
||||
|
||||
// Update request
|
||||
type PasienUpdateRequest struct {
|
||||
ID *int32 `json:"-" validate:"required"`
|
||||
Status *string `json:"status" validate:"required,oneof=draft active inactive"`
|
||||
Nomr *string `json:"nomr"`
|
||||
Title *string `json:"title" validate:"omitempty,min=1,max=255"`
|
||||
Nama *string `json:"nama" validate:"required,min=1,max=100"`
|
||||
Tempat *string `json:"tempat"`
|
||||
Tgllahir *time.Time `json:"tgllahir"`
|
||||
Jeniskelamin *string `json:"jeniskelamin" validate:"oneof=L P"`
|
||||
Alamat *string `json:"alamat"`
|
||||
Kelurahan *int64 `json:"kelurahan"`
|
||||
Kdkecamatan *int32 `json:"kdkecamatan"`
|
||||
Kota *int32 `json:"kota"`
|
||||
Kdprovinsi *int32 `json:"kdprovinsi"`
|
||||
Agama *int32 `json:"agama"`
|
||||
NoKartu *string `json:"noKartu"`
|
||||
NoktpBaru *string `json:"noktpBaru"`
|
||||
}
|
||||
|
||||
// Response struct for get by ID
|
||||
type PasienGetByIDResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data *Pasien `json:"data"`
|
||||
}
|
||||
|
||||
// Response struct for get by nomr
|
||||
type PasienGetByNomrResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data []Pasien `json:"data"`
|
||||
Meta models.MetaResponse `json:"meta"`
|
||||
Summary *models.AggregateData `json:"summary,omitempty"`
|
||||
}
|
||||
|
||||
// Filter struct for query parameters
|
||||
type PasienFilter struct {
|
||||
Search *string `json:"search,omitempty" form:"search"`
|
||||
DateFrom *time.Time `json:"date_from,omitempty" form:"date_from"`
|
||||
DateTo *time.Time `json:"date_to,omitempty" form:"date_to"`
|
||||
Status *string `json:"status,omitempty" form:"status"`
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"api-service/internal/database"
|
||||
authHandlers "api-service/internal/handlers/auth"
|
||||
healthcheckHandlers "api-service/internal/handlers/healthcheck"
|
||||
|
||||
pasienPasienHandlers "api-service/internal/handlers/pasien"
|
||||
retribusiHandlers "api-service/internal/handlers/retribusi"
|
||||
"api-service/internal/middleware"
|
||||
services "api-service/internal/services/auth"
|
||||
@@ -121,6 +123,19 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine {
|
||||
retribusiHandler.DeleteRetribusi(c)
|
||||
})
|
||||
}
|
||||
// Pasien endpoints
|
||||
pasienPasienHandler := pasienPasienHandlers.NewPasienHandler()
|
||||
pasienPasienGroup := v1.Group("/pasien")
|
||||
{
|
||||
pasienPasienGroup.PUT("/:nomr", pasienPasienHandler.UpdatePasien)
|
||||
pasienPasienGroup.POST("/", pasienPasienHandler.CreatePasien)
|
||||
pasienPasienGroup.DELETE("/:nomr", pasienPasienHandler.DeletePasien)
|
||||
pasienPasienGroup.GET("/dynamic", pasienPasienHandler.GetPasienDynamic)
|
||||
pasienPasienGroup.GET("/", pasienPasienHandler.GetPasien)
|
||||
pasienPasienGroup.GET("/by-age", pasienPasienHandler.GetPasienByAge)
|
||||
pasienPasienGroup.GET("/:nomr", pasienPasienHandler.GetPasienByNomr)
|
||||
pasienPasienGroup.GET("/by-location", pasienPasienHandler.GetPasienByLocation)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PROTECTED ROUTES (Authentication Required)
|
||||
|
||||
@@ -1812,6 +1812,24 @@ func (qp *QueryParser) parseSorting(values url.Values) ([]SortField, error) {
|
||||
return sorts, nil
|
||||
}
|
||||
|
||||
// ParseQueryWithDefaultFields parses URL query parameters into a DynamicQuery struct with default fields.
|
||||
func (qp *QueryParser) ParseQueryWithDefaultFields(values url.Values, defaultTable string, defaultFields []string) (DynamicQuery, error) {
|
||||
query, err := qp.ParseQuery(values, defaultTable)
|
||||
if err != nil {
|
||||
return query, err
|
||||
}
|
||||
|
||||
// If no fields specified, use default fields
|
||||
if len(query.Fields) == 0 || (len(query.Fields) == 1 && query.Fields[0].Expression == "*") {
|
||||
query.Fields = make([]SelectField, len(defaultFields))
|
||||
for i, field := range defaultFields {
|
||||
query.Fields[i] = SelectField{Expression: field}
|
||||
}
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MONGODB QUERY BUILDER
|
||||
// =============================================================================
|
||||
|
||||
918
internal/utils/query/exemple.txt
Normal file
918
internal/utils/query/exemple.txt
Normal file
@@ -0,0 +1,918 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq" // PostgreSQL driver
|
||||
"yourpackage/utils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Inisialisasi koneksi database
|
||||
db, err := sqlx.Connect("postgres", "user=postgres dbname=testdb sslmode=disable")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Inisialisasi QueryBuilder
|
||||
qb := utils.NewQueryBuilder(utils.DBTypePostgreSQL)
|
||||
|
||||
// Contoh penggunaan
|
||||
simpleQueryExample(db, qb)
|
||||
complexQueryExample(db, qb)
|
||||
nestedJoinExample(db, qb)
|
||||
multiJoinExample(db, qb)
|
||||
commonQueriesExample(db, qb)
|
||||
jsonQueryExample(db, qb)
|
||||
windowFunctionExample(db, qb)
|
||||
cteExample(db, qb)
|
||||
unionExample(db, qb)
|
||||
aggregateExample(db, qb)
|
||||
}
|
||||
|
||||
func simpleQueryExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== Simple Query Example ===")
|
||||
|
||||
// Query sederhana dengan filter
|
||||
query := utils.DynamicQuery{
|
||||
From: "users",
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "id", Alias: "user_id"},
|
||||
{Expression: "name", Alias: "user_name"},
|
||||
{Expression: "email"},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "status", Operator: utils.OpEqual, Value: "active"},
|
||||
{Column: "created_at", Operator: utils.OpGreaterThanEqual, Value: time.Now().AddDate(0, -1, 0)},
|
||||
},
|
||||
LogicOp: "AND",
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "name", Order: "ASC"},
|
||||
},
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := qb.ExecuteQuery(context.Background(), db, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing simple query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d users\n", len(results))
|
||||
for _, user := range results {
|
||||
fmt.Printf("User: %+v\n", user)
|
||||
}
|
||||
}
|
||||
|
||||
func complexQueryExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== Complex Query Example ===")
|
||||
|
||||
// Query dengan nested filter dan berbagai operator
|
||||
query := utils.DynamicQuery{
|
||||
From: "orders",
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "id", Alias: "order_id"},
|
||||
{Expression: "customer_id"},
|
||||
{Expression: "total_amount"},
|
||||
{Expression: "order_date"},
|
||||
{Expression: "status"},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "status", Operator: utils.OpIn, Value: []string{"completed", "processing"}},
|
||||
{Column: "total_amount", Operator: utils.OpGreaterThan, Value: 1000},
|
||||
},
|
||||
LogicOp: "AND",
|
||||
},
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "order_date", Operator: utils.OpBetween, Value: []interface{}{time.Now().AddDate(0, -3, 0), time.Now()}},
|
||||
{Column: "customer_id", Operator: utils.OpNotIn, Value: []int{1, 2, 3}},
|
||||
},
|
||||
LogicOp: "OR",
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "order_date", Order: "DESC"},
|
||||
{Column: "total_amount", Order: "DESC"},
|
||||
},
|
||||
Limit: 20,
|
||||
Offset: 10,
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := qb.ExecuteQuery(context.Background(), db, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing complex query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d orders\n", len(results))
|
||||
for _, order := range results {
|
||||
fmt.Printf("Order: %+v\n", order)
|
||||
}
|
||||
}
|
||||
|
||||
func nestedJoinExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== Nested Join Example ===")
|
||||
|
||||
// Query dengan nested join
|
||||
query := utils.DynamicQuery{
|
||||
From: "customers",
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "customers.id", Alias: "customer_id"},
|
||||
{Expression: "customers.name", Alias: "customer_name"},
|
||||
{Expression: "orders.id", Alias: "order_id"},
|
||||
{Expression: "orders.total_amount"},
|
||||
{Expression: "order_items.product_id"},
|
||||
{Expression: "order_items.quantity"},
|
||||
{Expression: "products.name", Alias: "product_name"},
|
||||
},
|
||||
Joins: []utils.Join{
|
||||
{
|
||||
Type: "LEFT",
|
||||
Table: "orders",
|
||||
Alias: "orders",
|
||||
OnConditions: utils.FilterGroup{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "customers.id", Operator: utils.OpEqual, Value: "orders.customer_id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "LEFT",
|
||||
Table: "order_items",
|
||||
Alias: "order_items",
|
||||
OnConditions: utils.FilterGroup{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "orders.id", Operator: utils.OpEqual, Value: "order_items.order_id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "LEFT",
|
||||
Table: "products",
|
||||
Alias: "products",
|
||||
OnConditions: utils.FilterGroup{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "order_items.product_id", Operator: utils.OpEqual, Value: "products.id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "customers.status", Operator: utils.OpEqual, Value: "active"},
|
||||
{Column: "orders.status", Operator: utils.OpEqual, Value: "completed"},
|
||||
},
|
||||
LogicOp: "AND",
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "customers.name", Order: "ASC"},
|
||||
{Column: "orders.id", Order: "DESC"},
|
||||
},
|
||||
Limit: 50,
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := qb.ExecuteQuery(context.Background(), db, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing nested join query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d customer-order-product records\n", len(results))
|
||||
for _, record := range results {
|
||||
fmt.Printf("Record: %+v\n", record)
|
||||
}
|
||||
}
|
||||
|
||||
func multiJoinExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== Multi Join Example ===")
|
||||
|
||||
// Query dengan multiple join types
|
||||
query := utils.DynamicQuery{
|
||||
From: "employees",
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "employees.id", Alias: "employee_id"},
|
||||
{Expression: "employees.name", Alias: "employee_name"},
|
||||
{Expression: "departments.name", Alias: "department_name"},
|
||||
{Expression: "projects.name", Alias: "project_name"},
|
||||
{Expression: "tasks.title", Alias: "task_title"},
|
||||
{Expression: "task_assignments.assigned_date"},
|
||||
},
|
||||
Joins: []utils.Join{
|
||||
{
|
||||
Type: "INNER",
|
||||
Table: "departments",
|
||||
Alias: "departments",
|
||||
OnConditions: utils.FilterGroup{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "employees.department_id", Operator: utils.OpEqual, Value: "departments.id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "LEFT",
|
||||
Table: "task_assignments",
|
||||
Alias: "task_assignments",
|
||||
OnConditions: utils.FilterGroup{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "employees.id", Operator: utils.OpEqual, Value: "task_assignments.employee_id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "LEFT",
|
||||
Table: "tasks",
|
||||
Alias: "tasks",
|
||||
OnConditions: utils.FilterGroup{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "task_assignments.task_id", Operator: utils.OpEqual, Value: "tasks.id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "LEFT",
|
||||
Table: "projects",
|
||||
Alias: "projects",
|
||||
OnConditions: utils.FilterGroup{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "tasks.project_id", Operator: utils.OpEqual, Value: "projects.id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "employees.status", Operator: utils.OpEqual, Value: "active"},
|
||||
{Column: "departments.status", Operator: utils.OpEqual, Value: "active"},
|
||||
},
|
||||
LogicOp: "AND",
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "departments.name", Order: "ASC"},
|
||||
{Column: "employees.name", Order: "ASC"},
|
||||
{Column: "task_assignments.assigned_date", Order: "DESC"},
|
||||
},
|
||||
Limit: 100,
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := qb.ExecuteQuery(context.Background(), db, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing multi join query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d employee-task-project records\n", len(results))
|
||||
for _, record := range results {
|
||||
fmt.Printf("Record: %+v\n", record)
|
||||
}
|
||||
}
|
||||
|
||||
func commonQueriesExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== Common Queries Example ===")
|
||||
|
||||
// 1. Query dengan LIKE/ILIKE
|
||||
likeQuery := utils.DynamicQuery{
|
||||
From: "products",
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "name"},
|
||||
{Expression: "price"},
|
||||
{Expression: "category"},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "name", Operator: utils.OpILike, Value: "%laptop%"},
|
||||
{Column: "category", Operator: utils.OpEqual, Value: "electronics"},
|
||||
},
|
||||
LogicOp: "AND",
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "price", Order: "ASC"},
|
||||
},
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
var products []map[string]interface{}
|
||||
err := qb.ExecuteQuery(context.Background(), db, likeQuery, &products)
|
||||
if err != nil {
|
||||
log.Printf("Error executing LIKE query: %v", err)
|
||||
} else {
|
||||
fmt.Printf("Found %d products matching 'laptop'\n", len(products))
|
||||
}
|
||||
|
||||
// 2. Query dengan pagination
|
||||
page := 2
|
||||
pageSize := 20
|
||||
paginationQuery := utils.DynamicQuery{
|
||||
From: "orders",
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "customer_id"},
|
||||
{Expression: "total_amount"},
|
||||
{Expression: "order_date"},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "status", Operator: utils.OpEqual, Value: "completed"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "order_date", Order: "DESC"},
|
||||
},
|
||||
Limit: pageSize,
|
||||
Offset: (page - 1) * pageSize,
|
||||
}
|
||||
|
||||
var orders []map[string]interface{}
|
||||
err = qb.ExecuteQuery(context.Background(), db, paginationQuery, &orders)
|
||||
if err != nil {
|
||||
log.Printf("Error executing pagination query: %v", err)
|
||||
} else {
|
||||
fmt.Printf("Found %d orders on page %d\n", len(orders), page)
|
||||
}
|
||||
|
||||
// 3. Query dengan NULL/NOT NULL
|
||||
nullQuery := utils.DynamicQuery{
|
||||
From: "customers",
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "name"},
|
||||
{Expression: "email"},
|
||||
{Expression: "phone"},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "email", Operator: utils.OpNotNull},
|
||||
{Column: "phone", Operator: utils.OpNull},
|
||||
},
|
||||
LogicOp: "AND",
|
||||
},
|
||||
},
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
var customers []map[string]interface{}
|
||||
err = qb.ExecuteQuery(context.Background(), db, nullQuery, &customers)
|
||||
if err != nil {
|
||||
log.Printf("Error executing NULL query: %v", err)
|
||||
} else {
|
||||
fmt.Printf("Found %d customers with email but no phone\n", len(customers))
|
||||
}
|
||||
|
||||
// 4. Query dengan BETWEEN
|
||||
betweenQuery := utils.DynamicQuery{
|
||||
From: "transactions",
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "account_id"},
|
||||
{Expression: "amount"},
|
||||
{Expression: "transaction_date"},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "amount", Operator: utils.OpBetween, Value: []interface{}{100, 1000}},
|
||||
{Column: "transaction_date", Operator: utils.OpBetween, Value: []interface{}{time.Now().AddDate(0, -1, 0), time.Now()}},
|
||||
},
|
||||
LogicOp: "AND",
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "transaction_date", Order: "DESC"},
|
||||
},
|
||||
Limit: 20,
|
||||
}
|
||||
|
||||
var transactions []map[string]interface{}
|
||||
err = qb.ExecuteQuery(context.Background(), db, betweenQuery, &transactions)
|
||||
if err != nil {
|
||||
log.Printf("Error executing BETWEEN query: %v", err)
|
||||
} else {
|
||||
fmt.Printf("Found %d transactions between $100 and $1000 in the last month\n", len(transactions))
|
||||
}
|
||||
}
|
||||
|
||||
func jsonQueryExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== JSON Query Example ===")
|
||||
|
||||
// Query dengan operasi JSON
|
||||
query := utils.DynamicQuery{
|
||||
From: "products",
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "name"},
|
||||
{Expression: "price"},
|
||||
{Expression: "attributes"},
|
||||
},
|
||||
JsonOperations: []utils.JsonOperation{
|
||||
{
|
||||
Type: "extract",
|
||||
Column: "attributes",
|
||||
Path: "$.color",
|
||||
Alias: "color",
|
||||
},
|
||||
{
|
||||
Type: "extract",
|
||||
Column: "attributes",
|
||||
Path: "$.size",
|
||||
Alias: "size",
|
||||
},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{
|
||||
Column: "attributes",
|
||||
Operator: utils.OpJsonContains,
|
||||
Value: map[string]interface{}{"category": "electronics"},
|
||||
Options: map[string]interface{}{"path": "$"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "name", Order: "ASC"},
|
||||
},
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := qb.ExecuteQuery(context.Background(), db, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing JSON query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d products with JSON attributes\n", len(results))
|
||||
for _, product := range results {
|
||||
fmt.Printf("Product: %+v\n", product)
|
||||
}
|
||||
}
|
||||
|
||||
func windowFunctionExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== Window Function Example ===")
|
||||
|
||||
// Query dengan window functions
|
||||
query := utils.DynamicQuery{
|
||||
From: "sales",
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "salesperson_id"},
|
||||
{Expression: "amount"},
|
||||
{Expression: "sale_date"},
|
||||
},
|
||||
WindowFunctions: []utils.WindowFunction{
|
||||
{
|
||||
Function: "ROW_NUMBER",
|
||||
Over: "salesperson_id",
|
||||
OrderBy: "amount DESC",
|
||||
Alias: "sales_rank",
|
||||
},
|
||||
{
|
||||
Function: "SUM",
|
||||
Over: "salesperson_id",
|
||||
OrderBy: "sale_date",
|
||||
Frame: "ROWS UNBOUNDED PRECEDING",
|
||||
Alias: "running_total",
|
||||
},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "sale_date", Operator: utils.OpGreaterThanEqual, Value: time.Now().AddDate(0, -6, 0)},
|
||||
},
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "salesperson_id", Order: "ASC"},
|
||||
{Column: "amount", Order: "DESC"},
|
||||
},
|
||||
Limit: 50,
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := qb.ExecuteQuery(context.Background(), db, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing window function query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d sales records with window functions\n", len(results))
|
||||
for _, sale := range results {
|
||||
fmt.Printf("Sale: %+v\n", sale)
|
||||
}
|
||||
}
|
||||
|
||||
func cteExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== CTE Example ===")
|
||||
|
||||
// Query dengan CTE
|
||||
query := utils.DynamicQuery{
|
||||
CTEs: []utils.CTE{
|
||||
{
|
||||
Name: "monthly_sales",
|
||||
Query: utils.DynamicQuery{
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "salesperson_id"},
|
||||
{Expression: "EXTRACT(MONTH FROM sale_date) AS month"},
|
||||
{Expression: "SUM(amount) AS total"},
|
||||
},
|
||||
From: "sales",
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "sale_date", Operator: utils.OpGreaterThanEqual, Value: time.Now().AddDate(-1, 0, 0)},
|
||||
},
|
||||
},
|
||||
},
|
||||
GroupBy: []string{"salesperson_id", "EXTRACT(MONTH FROM sale_date)"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "top_salespeople",
|
||||
Query: utils.DynamicQuery{
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "salesperson_id"},
|
||||
{Expression: "SUM(total) AS yearly_total"},
|
||||
},
|
||||
From: "monthly_sales",
|
||||
GroupBy: []string{"salesperson_id"},
|
||||
Having: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "SUM(total)", Operator: utils.OpGreaterThan, Value: 10000},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "salespeople.id"},
|
||||
{Expression: "salespeople.name"},
|
||||
{Expression: "top_salespeople.yearly_total"},
|
||||
},
|
||||
From: "salespeople",
|
||||
Joins: []utils.Join{
|
||||
{
|
||||
Type: "INNER",
|
||||
Table: "top_salespeople",
|
||||
Alias: "top_salespeople",
|
||||
OnConditions: utils.FilterGroup{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "salespeople.id", Operator: utils.OpEqual, Value: "top_salespeople.salesperson_id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "top_salespeople.yearly_total", Order: "DESC"},
|
||||
},
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := qb.ExecuteQuery(context.Background(), db, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing CTE query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d top salespeople\n", len(results))
|
||||
for _, salesperson := range results {
|
||||
fmt.Printf("Salesperson: %+v\n", salesperson)
|
||||
}
|
||||
}
|
||||
|
||||
func unionExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== UNION Example ===")
|
||||
|
||||
// Query dengan UNION
|
||||
query := utils.DynamicQuery{
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "name"},
|
||||
{Expression: "email"},
|
||||
{Expression: "'customer' AS user_type"},
|
||||
},
|
||||
From: "customers",
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "status", Operator: utils.OpEqual, Value: "active"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Unions: []utils.Union{
|
||||
{
|
||||
Type: "UNION ALL",
|
||||
Query: utils.DynamicQuery{
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "id"},
|
||||
{Expression: "name"},
|
||||
{Expression: "email"},
|
||||
{Expression: "'employee' AS user_type"},
|
||||
},
|
||||
From: "employees",
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "status", Operator: utils.OpEqual, Value: "active"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "name", Order: "ASC"},
|
||||
},
|
||||
Limit: 20,
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := qb.ExecuteQuery(context.Background(), db, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing UNION query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d users (customers + employees)\n", len(results))
|
||||
for _, user := range results {
|
||||
fmt.Printf("User: %+v\n", user)
|
||||
}
|
||||
}
|
||||
|
||||
func aggregateExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== Aggregate Example ===")
|
||||
|
||||
// Query dengan fungsi agregasi
|
||||
query := utils.DynamicQuery{
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "category"},
|
||||
{Expression: "COUNT(*) AS product_count"},
|
||||
{Expression: "AVG(price) AS avg_price"},
|
||||
{Expression: "MIN(price) AS min_price"},
|
||||
{Expression: "MAX(price) AS max_price"},
|
||||
{Expression: "SUM(stock_quantity) AS total_stock"},
|
||||
},
|
||||
From: "products",
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "status", Operator: utils.OpEqual, Value: "active"},
|
||||
},
|
||||
},
|
||||
},
|
||||
GroupBy: []string{"category"},
|
||||
Having: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "COUNT(*)", Operator: utils.OpGreaterThan, Value: 5},
|
||||
},
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "product_count", Order: "DESC"},
|
||||
},
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := qb.ExecuteQuery(context.Background(), db, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing aggregate query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d product categories\n", len(results))
|
||||
for _, category := range results {
|
||||
fmt.Printf("Category: %+v\n", category)
|
||||
}
|
||||
}
|
||||
|
||||
func crudOperationsExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== CRUD Operations Example ===")
|
||||
|
||||
// INSERT
|
||||
insertData := utils.InsertData{
|
||||
Columns: []string{"name", "email", "status", "created_at"},
|
||||
Values: []interface{}{"John Doe", "john@example.com", "active", time.Now()},
|
||||
JsonValues: map[string]interface{}{
|
||||
"preferences": map[string]interface{}{
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result, err := qb.ExecuteInsert(context.Background(), db, "customers", insertData, "id")
|
||||
if err != nil {
|
||||
log.Printf("Error executing INSERT: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
log.Printf("Error getting inserted ID: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Inserted customer with ID: %d\n", id)
|
||||
|
||||
// UPDATE
|
||||
updateData := utils.UpdateData{
|
||||
Columns: []string{"name", "status"},
|
||||
Values: []interface{}{"John Smith", "inactive"},
|
||||
JsonUpdates: map[string]utils.JsonUpdate{
|
||||
"preferences": {
|
||||
Path: "$.theme",
|
||||
Value: "light",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
filters := []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "id", Operator: utils.OpEqual, Value: id},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result, err = qb.ExecuteUpdate(context.Background(), db, "customers", updateData, filters, "updated_at")
|
||||
if err != nil {
|
||||
log.Printf("Error executing UPDATE: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
log.Printf("Error getting rows affected: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Updated %d customer(s)\n", rowsAffected)
|
||||
|
||||
// DELETE
|
||||
result, err = qb.ExecuteDelete(context.Background(), db, "customers", filters)
|
||||
if err != nil {
|
||||
log.Printf("Error executing DELETE: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
rowsAffected, err = result.RowsAffected()
|
||||
if err != nil {
|
||||
log.Printf("Error getting rows affected: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Deleted %d customer(s)\n", rowsAffected)
|
||||
}
|
||||
|
||||
|
||||
func mongoExample() {
|
||||
fmt.Println("\n=== MongoDB Example ===")
|
||||
|
||||
// Inisialisasi koneksi MongoDB
|
||||
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://localhost:27017"))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to MongoDB: %v", err)
|
||||
}
|
||||
defer client.Disconnect(context.Background())
|
||||
|
||||
db := client.Database("testdb")
|
||||
collection := db.Collection("users")
|
||||
|
||||
// Inisialisasi MongoQueryBuilder
|
||||
mqb := utils.NewMongoQueryBuilder()
|
||||
|
||||
// Query sederhana
|
||||
query := utils.DynamicQuery{
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "name"},
|
||||
{Expression: "email"},
|
||||
{Expression: "status"},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "status", Operator: utils.OpEqual, Value: "active"},
|
||||
{Column: "age", Operator: utils.OpGreaterThan, Value: 18},
|
||||
},
|
||||
LogicOp: "AND",
|
||||
},
|
||||
},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "name", Order: "ASC"},
|
||||
},
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err = mqb.ExecuteFind(context.Background(), collection, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing MongoDB query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d users\n", len(results))
|
||||
for _, user := range results {
|
||||
fmt.Printf("User: %+v\n", user)
|
||||
}
|
||||
|
||||
// Aggregation pipeline
|
||||
aggQuery := utils.DynamicQuery{
|
||||
Fields: []utils.SelectField{
|
||||
{Expression: "department", Alias: "_id"},
|
||||
{Expression: "COUNT(*)", Alias: "employee_count"},
|
||||
{Expression: "AVG(salary)", Alias: "avg_salary"},
|
||||
},
|
||||
Filters: []utils.FilterGroup{
|
||||
{
|
||||
Filters: []utils.DynamicFilter{
|
||||
{Column: "status", Operator: utils.OpEqual, Value: "active"},
|
||||
},
|
||||
},
|
||||
},
|
||||
GroupBy: []string{"department"},
|
||||
Sort: []utils.SortField{
|
||||
{Column: "employee_count", Order: "DESC"},
|
||||
},
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
var aggResults []map[string]interface{}
|
||||
err = mqb.ExecuteAggregate(context.Background(), collection, aggQuery, &aggResults)
|
||||
if err != nil {
|
||||
log.Printf("Error executing MongoDB aggregation: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d departments\n", len(aggResults))
|
||||
for _, dept := range aggResults {
|
||||
fmt.Printf("Department: %+v\n", dept)
|
||||
}
|
||||
}
|
||||
|
||||
func queryParserExample(db *sqlx.DB, qb *utils.QueryBuilder) {
|
||||
fmt.Println("\n=== Query Parser Example ===")
|
||||
|
||||
// Inisialisasi QueryParser
|
||||
qp := utils.NewQueryParser()
|
||||
|
||||
// Parse URL query parameters
|
||||
values := url.Values{}
|
||||
values.Add("fields", "id,name,email,status")
|
||||
values.Add("filter[status][_eq]", "active")
|
||||
values.Add("filter[created_at][_gte]", "2023-01-01")
|
||||
values.Add("filter[age][_between]", "18,65")
|
||||
values.Add("sort", "+name,-created_at")
|
||||
values.Add("limit", "20")
|
||||
values.Add("offset", "10")
|
||||
|
||||
// Parse query parameters into DynamicQuery
|
||||
query, err := qp.ParseQuery(values, "users")
|
||||
if err != nil {
|
||||
log.Printf("Error parsing query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Execute the parsed query
|
||||
var results []map[string]interface{}
|
||||
err = qb.ExecuteQuery(context.Background(), db, query, &results)
|
||||
if err != nil {
|
||||
log.Printf("Error executing parsed query: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d users using parsed query\n", len(results))
|
||||
for _, user := range results {
|
||||
fmt.Printf("User: %+v\n", user)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,79 +19,109 @@ services:
|
||||
table_name: "m_pasien"
|
||||
|
||||
# Define all columns once for reuse
|
||||
columns:
|
||||
- name: "nomr"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Nomor Rekam Medis"
|
||||
- name: "title"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Gelar pasien (Tn, Ny, Sdr, dll)"
|
||||
- name: "nama"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
validation: "required,min=1,max=100"
|
||||
description: "Nama lengkap pasien"
|
||||
- name: "tempat"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Tempat lahir pasien"
|
||||
- name: "tgllahir"
|
||||
type: "date"
|
||||
nullable: true
|
||||
go_type: "time.Time"
|
||||
description: "Tanggal lahir pasien"
|
||||
- name: "jeniskelamin"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
validation: "oneof=L P"
|
||||
description: "Jenis kelamin (L/P)"
|
||||
- name: "alamat"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Alamat lengkap pasien"
|
||||
- name: "kelurahan"
|
||||
type: "int8"
|
||||
nullable: true
|
||||
go_type: "int64"
|
||||
description: "ID Kelurahan"
|
||||
- name: "kdkecamatan"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
go_type: "int32"
|
||||
description: "ID Kecamatan"
|
||||
- name: "kota"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
go_type: "int32"
|
||||
description: "ID Kota"
|
||||
- name: "kdprovinsi"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
go_type: "int32"
|
||||
description: "ID Provinsi"
|
||||
- name: "agama"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
go_type: "int32"
|
||||
description: "ID Agama"
|
||||
- name: "no_kartu"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Nomor kartu identitas"
|
||||
- name: "noktp_baru"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Nomor KTP baru"
|
||||
schema:
|
||||
columns:
|
||||
- name: "id"
|
||||
type: "serial4"
|
||||
nullable: false
|
||||
go_type: "int32"
|
||||
primary_key: true
|
||||
unique: true
|
||||
description: "Primary key for schedule"
|
||||
- name: "nomr"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
searchable: true
|
||||
unique: true
|
||||
description: "Nomor Rekam Medis"
|
||||
- name: "status"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Status pasien (A = Aktif, I = Inaktif)"
|
||||
- name: "title"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Gelar pasien (Tn, Ny, Sdr, dll)"
|
||||
- name: "nama"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
validation: "required,min=1,max=100"
|
||||
searchable: true
|
||||
description: "Nama lengkap pasien"
|
||||
- name: "tempat"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Tempat lahir pasien"
|
||||
- name: "tgllahir"
|
||||
type: "date"
|
||||
nullable: true
|
||||
go_type: "time.Time"
|
||||
description: "Tanggal lahir pasien"
|
||||
- name: "jeniskelamin"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
validation: "oneof=L P"
|
||||
description: "Jenis kelamin (L/P)"
|
||||
- name: "alamat"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Alamat lengkap pasien"
|
||||
- name: "kelurahan"
|
||||
type: "int8"
|
||||
nullable: true
|
||||
go_type: "int64"
|
||||
description: "ID Kelurahan"
|
||||
- name: "kdkecamatan"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
go_type: "int32"
|
||||
description: "ID Kecamatan"
|
||||
- name: "kota"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
go_type: "int32"
|
||||
description: "ID Kota"
|
||||
- name: "kdprovinsi"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
go_type: "int32"
|
||||
description: "ID Provinsi"
|
||||
- name: "agama"
|
||||
type: "int4"
|
||||
nullable: true
|
||||
go_type: "int32"
|
||||
description: "ID Agama"
|
||||
- name: "no_kartu"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
searchable: true
|
||||
unique: true
|
||||
description: "Nomor kartu identitas"
|
||||
- name: "noktp_baru"
|
||||
type: "varchar"
|
||||
nullable: true
|
||||
go_type: "string"
|
||||
description: "Nomor KTP baru"
|
||||
- name: "created_at"
|
||||
type: "timestamp"
|
||||
nullable: true
|
||||
go_type: "time.Time"
|
||||
system_field: true
|
||||
description: "Tanggal pembuatan record"
|
||||
- name: "updated_at"
|
||||
type: "timestamp"
|
||||
nullable: true
|
||||
go_type: "time.Time"
|
||||
system_field: true
|
||||
description: "Tanggal update record"
|
||||
|
||||
# Define relationships with other tables
|
||||
relationships:
|
||||
@@ -167,6 +197,8 @@ services:
|
||||
# Define endpoints with reusable configurations
|
||||
endpoints:
|
||||
list:
|
||||
handler_folder: "pasien"
|
||||
handler_file: "pasien.go"
|
||||
methods: ["GET"]
|
||||
path: "/"
|
||||
description: "Get list of pasien with pagination and filters"
|
||||
@@ -182,19 +214,37 @@ services:
|
||||
fields: "with_location_names"
|
||||
response_model: "PasienGetResponse"
|
||||
|
||||
get_by_nomr:
|
||||
get_by_id:
|
||||
handler_folder: "pasien"
|
||||
handler_file: "pasien.go"
|
||||
methods: ["GET"]
|
||||
path: "/:nomr"
|
||||
description: "Get pasien by NOMR"
|
||||
summary: "Get Pasien by NOMR"
|
||||
path: "/:id"
|
||||
description: "Get pasien by ID"
|
||||
summary: "Get Pasien by ID"
|
||||
tags: ["Pasien"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
cache_ttl: 300
|
||||
fields: "with_location_names"
|
||||
response_model: "PasienGetByNOMRResponse"
|
||||
response_model: "PasienGetByIDResponse"
|
||||
|
||||
get_by_nomr:
|
||||
handler_folder: "pasien"
|
||||
handler_file: "pasien.go"
|
||||
methods: ["GET"]
|
||||
path: "/nomr/:nomr"
|
||||
description: "Get pasien by Nomr"
|
||||
summary: "Get Pasien by Nomr"
|
||||
tags: ["Pasien"]
|
||||
require_auth: true
|
||||
cache_enabled: true
|
||||
cache_ttl: 300
|
||||
fields: "with_location_names"
|
||||
response_model: "PasienGetByNomrResponse"
|
||||
|
||||
create:
|
||||
handler_folder: "pasien"
|
||||
handler_file: "pasien.go"
|
||||
methods: ["POST"]
|
||||
path: "/"
|
||||
description: "Create a new pasien"
|
||||
@@ -206,6 +256,8 @@ services:
|
||||
response_model: "PasienCreateResponse"
|
||||
|
||||
update:
|
||||
handler_folder: "pasien"
|
||||
handler_file: "pasien.go"
|
||||
methods: ["PUT"]
|
||||
path: "/:nomr"
|
||||
description: "Update an existing pasien"
|
||||
@@ -217,16 +269,20 @@ services:
|
||||
response_model: "PasienUpdateResponse"
|
||||
|
||||
delete:
|
||||
handler_folder: "pasien"
|
||||
handler_file: "pasien.go"
|
||||
methods: ["DELETE"]
|
||||
path: "/:nomr"
|
||||
description: "Delete a pasien"
|
||||
summary: "Delete Pasien"
|
||||
tags: ["Pasien"]
|
||||
require_auth: true
|
||||
soft_delete: false
|
||||
soft_delete: true
|
||||
response_model: "PasienDeleteResponse"
|
||||
|
||||
dynamic:
|
||||
handler_folder: "pasien"
|
||||
handler_file: "pasien.go"
|
||||
methods: ["GET"]
|
||||
path: "/dynamic"
|
||||
description: "Get pasien with dynamic filtering"
|
||||
@@ -237,28 +293,34 @@ services:
|
||||
fields: "with_location_names"
|
||||
response_model: "PasienGetResponse"
|
||||
|
||||
search:
|
||||
methods: ["GET"]
|
||||
path: "/search"
|
||||
description: "Search pasien by name or NOMR"
|
||||
summary: "Search Pasien"
|
||||
tags: ["Pasien"]
|
||||
require_auth: true
|
||||
has_search: true
|
||||
fields: "with_location_names"
|
||||
response_model: "PasienGetResponse"
|
||||
# search:
|
||||
# handler_folder: "pasien"
|
||||
# handler_file: "pasien.go"
|
||||
# methods: ["GET"]
|
||||
# path: "/search"
|
||||
# description: "Search pasien by name or NOMR"
|
||||
# summary: "Search Pasien"
|
||||
# tags: ["Pasien"]
|
||||
# require_auth: true
|
||||
# has_search: true
|
||||
# fields: "with_location_names"
|
||||
# response_model: "PasienGetResponse"
|
||||
|
||||
stats:
|
||||
methods: ["GET"]
|
||||
path: "/stats"
|
||||
description: "Get pasien statistics"
|
||||
summary: "Get Pasien Stats"
|
||||
tags: ["Pasien"]
|
||||
require_auth: true
|
||||
has_stats: true
|
||||
response_model: "AggregateData"
|
||||
# stats:
|
||||
# handler_folder: "pasien"
|
||||
# handler_file: "pasien.go"
|
||||
# methods: ["GET"]
|
||||
# path: "/stats"
|
||||
# description: "Get pasien statistics"
|
||||
# summary: "Get Pasien Stats"
|
||||
# tags: ["Pasien"]
|
||||
# require_auth: true
|
||||
# has_stats: true
|
||||
# response_model: "AggregateData"
|
||||
|
||||
by_location:
|
||||
handler_folder: "pasien"
|
||||
handler_file: "pasien.go"
|
||||
methods: ["GET"]
|
||||
path: "/by-location"
|
||||
description: "Get pasien by location (provinsi, kota, kecamatan, kelurahan)"
|
||||
@@ -270,6 +332,8 @@ services:
|
||||
response_model: "PasienGetResponse"
|
||||
|
||||
by_age:
|
||||
handler_folder: "pasien"
|
||||
handler_file: "pasien.go"
|
||||
methods: ["GET"]
|
||||
path: "/by-age"
|
||||
description: "Get pasien statistics by age group"
|
||||
@@ -277,4 +341,201 @@ services:
|
||||
tags: ["Pasien"]
|
||||
require_auth: true
|
||||
has_stats: true
|
||||
response_model: "PasienAgeStatsResponse"
|
||||
response_model: "PasienAgeStatsResponse"
|
||||
|
||||
# schedule:
|
||||
# name: "Jadwal Dokter"
|
||||
# category: "schedule"
|
||||
# package: "schedule"
|
||||
# description: "Jadwal Dokter management"
|
||||
# base_url: ""
|
||||
# timeout: 30
|
||||
# retry_count: 3
|
||||
# table_name: "daftar_jadwal_dokter"
|
||||
|
||||
# # Define all columns once for reuse
|
||||
# schema:
|
||||
# columns:
|
||||
# - name: "id"
|
||||
# type: "serial4"
|
||||
# nullable: false
|
||||
# go_type: "int32"
|
||||
# primary_key: true
|
||||
# description: "Primary key for schedule"
|
||||
# - name: "hari"
|
||||
# type: "int4"
|
||||
# nullable: true
|
||||
# go_type: "int32"
|
||||
# description: "Day of week (1-7)"
|
||||
# - name: "nama_hari"
|
||||
# type: "varchar"
|
||||
# nullable: true
|
||||
# go_type: "string"
|
||||
# searchable: true
|
||||
# description: "Name of day"
|
||||
# - name: "waktu"
|
||||
# type: "varchar"
|
||||
# nullable: true
|
||||
# go_type: "string"
|
||||
# searchable: true
|
||||
# description: "Time schedule"
|
||||
# - name: "dokter"
|
||||
# type: "uuid"
|
||||
# nullable: true
|
||||
# go_type: "string"
|
||||
# searchable: true
|
||||
# description: "Doctor ID"
|
||||
# - name: "spesialis"
|
||||
# type: "int4"
|
||||
# nullable: true
|
||||
# go_type: "int32"
|
||||
# description: "Specialization ID"
|
||||
# - name: "sub_spesialis"
|
||||
# type: "int4"
|
||||
# nullable: true
|
||||
# go_type: "int32"
|
||||
# description: "Sub-specialization ID"
|
||||
# - name: "status"
|
||||
# type: "int4"
|
||||
# nullable: true
|
||||
# go_type: "int32"
|
||||
# description: "Status (1=active, 0=inactive)"
|
||||
# - name: "date_created"
|
||||
# type: "timestamp"
|
||||
# nullable: true
|
||||
# go_type: "time.Time"
|
||||
# system_field: true
|
||||
# description: "Tanggal pembuatan record"
|
||||
# - name: "date_updated"
|
||||
# type: "timestamp"
|
||||
# nullable: true
|
||||
# go_type: "time.Time"
|
||||
# system_field: true
|
||||
# description: "Tanggal update record"
|
||||
# - name: "user_created"
|
||||
# type: "varchar"
|
||||
# nullable: true
|
||||
# go_type: "string"
|
||||
# system_field: true
|
||||
# description: "User yang membuat record"
|
||||
# - name: "user_updated"
|
||||
# type: "varchar"
|
||||
# nullable: true
|
||||
# go_type: "string"
|
||||
# system_field: true
|
||||
# description: "User yang mengupdate record"
|
||||
|
||||
# # Define reusable field groups
|
||||
# field_groups:
|
||||
# base_fields: ["id", "hari", "nama_hari", "waktu", "dokter"]
|
||||
# all_fields: ["id", "hari", "nama_hari", "waktu", "dokter", "spesialis", "sub_spesialis", "status"]
|
||||
|
||||
# # Define endpoints with reusable configurations
|
||||
# endpoints:
|
||||
# list:
|
||||
# handler_folder: "schedule"
|
||||
# handler_file: "schedule.go"
|
||||
# methods: ["GET"]
|
||||
# path: "/"
|
||||
# description: "Get list of schedule with pagination and filters"
|
||||
# summary: "Get Schedule List"
|
||||
# tags: ["Schedule"]
|
||||
# require_auth: true
|
||||
# cache_enabled: true
|
||||
# cache_ttl: 300
|
||||
# has_pagination: true
|
||||
# has_filter: true
|
||||
# has_search: true
|
||||
# has_stats: true
|
||||
# fields: "all_fields"
|
||||
# response_model: "ScheduleGetResponse"
|
||||
|
||||
# get_by_id:
|
||||
# handler_folder: "schedule"
|
||||
# handler_file: "schedule.go"
|
||||
# methods: ["GET"]
|
||||
# path: "/:id"
|
||||
# description: "Get schedule by ID"
|
||||
# summary: "Get Schedule by ID"
|
||||
# tags: ["Schedule"]
|
||||
# require_auth: true
|
||||
# cache_enabled: true
|
||||
# cache_ttl: 300
|
||||
# fields: "all_fields"
|
||||
# response_model: "ScheduleGetByIDResponse"
|
||||
|
||||
# create:
|
||||
# handler_folder: "schedule"
|
||||
# handler_file: "schedule.go"
|
||||
# methods: ["POST"]
|
||||
# path: "/"
|
||||
# description: "Create a new schedule"
|
||||
# summary: "Create Schedule"
|
||||
# tags: ["Schedule"]
|
||||
# require_auth: true
|
||||
# fields: "all_fields"
|
||||
# request_model: "ScheduleCreateRequest"
|
||||
# response_model: "ScheduleCreateResponse"
|
||||
|
||||
# update:
|
||||
# handler_folder: "schedule"
|
||||
# handler_file: "schedule.go"
|
||||
# methods: ["PUT"]
|
||||
# path: "/:id"
|
||||
# description: "Update an existing schedule"
|
||||
# summary: "Update Schedule"
|
||||
# tags: ["Schedule"]
|
||||
# require_auth: true
|
||||
# fields: "all_fields"
|
||||
# request_model: "ScheduleUpdateRequest"
|
||||
# response_model: "ScheduleUpdateResponse"
|
||||
|
||||
# delete:
|
||||
# handler_folder: "schedule"
|
||||
# handler_file: "schedule.go"
|
||||
# methods: ["DELETE"]
|
||||
# path: "/:id"
|
||||
# description: "Delete a schedule"
|
||||
# summary: "Delete Schedule"
|
||||
# tags: ["Schedule"]
|
||||
# require_auth: true
|
||||
# soft_delete: true
|
||||
# response_model: "ScheduleDeleteResponse"
|
||||
|
||||
# dynamic:
|
||||
# handler_folder: "schedule"
|
||||
# handler_file: "schedule.go"
|
||||
# methods: ["GET"]
|
||||
# path: "/dynamic"
|
||||
# description: "Get schedule with dynamic filtering"
|
||||
# summary: "Get Schedule Dynamic"
|
||||
# tags: ["Schedule"]
|
||||
# require_auth: true
|
||||
# has_dynamic: true
|
||||
# fields: "all_fields"
|
||||
# response_model: "ScheduleGetResponse"
|
||||
|
||||
# search:
|
||||
# handler_folder: "schedule"
|
||||
# handler_file: "schedule.go"
|
||||
# methods: ["GET"]
|
||||
# path: "/search"
|
||||
# description: "Search schedule by name or doctor"
|
||||
# summary: "Search Schedule"
|
||||
# tags: ["Schedule"]
|
||||
# require_auth: true
|
||||
# has_search: true
|
||||
# fields: "all_fields"
|
||||
# response_model: "ScheduleGetResponse"
|
||||
|
||||
# stats:
|
||||
# handler_folder: "schedule"
|
||||
# handler_file: "schedule.go"
|
||||
# methods: ["GET"]
|
||||
# path: "/stats"
|
||||
# description: "Get schedule statistics"
|
||||
# summary: "Get Schedule Stats"
|
||||
# tags: ["Schedule"]
|
||||
# require_auth: true
|
||||
# has_stats: true
|
||||
# response_model: "AggregateData"
|
||||
Reference in New Issue
Block a user