Perbaikan Generate code
This commit is contained in:
No files matched your search
@@ -340,11 +340,6 @@ BPJS_SECRETKEY=1bV36ASDQQ3512D
|
|||||||
**Generate Handler untuk Retribusi:**
|
**Generate Handler untuk Retribusi:**
|
||||||
|
|
||||||
```bash
|
```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
|
# Config
|
||||||
go run tools/general/generate-handler.go --config tools/general/services-config.yaml --verbose
|
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
|
## 🚀 Deployment
|
||||||
|
|
||||||
### 🐳 Docker Deployment
|
### 🐳 Docker Deployment
|
||||||
|
|||||||
@@ -394,22 +394,22 @@ func loadKeycloakConfig() KeycloakConfig {
|
|||||||
|
|
||||||
func (c *Config) loadDatabaseConfigs() {
|
func (c *Config) loadDatabaseConfigs() {
|
||||||
// Load PostgreSQL configurations
|
// Load PostgreSQL configurations
|
||||||
c.addPostgreSQLConfigs()
|
// c.addPostgreSQLConfigs()
|
||||||
|
|
||||||
// Load MySQL configurations
|
// // Load MySQL configurations
|
||||||
c.addMySQLConfigs()
|
// c.addMySQLConfigs()
|
||||||
|
|
||||||
// Load MongoDB configurations
|
// // Load MongoDB configurations
|
||||||
c.addMongoDBConfigs()
|
// c.addMongoDBConfigs()
|
||||||
|
|
||||||
// Load SQLite configurations
|
// // Load SQLite configurations
|
||||||
c.addSQLiteConfigs()
|
// c.addSQLiteConfigs()
|
||||||
|
|
||||||
// Load custom database configurations from environment variables
|
// Load custom database configurations from environment variables
|
||||||
c.loadCustomDatabaseConfigs()
|
c.loadCustomDatabaseConfigs()
|
||||||
|
|
||||||
// Remove duplicate database configurations
|
// Remove duplicate database configurations
|
||||||
c.removeDuplicateDatabases()
|
// c.removeDuplicateDatabases()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) removeDuplicateDatabases() {
|
func (c *Config) removeDuplicateDatabases() {
|
||||||
|
|||||||
@@ -127,16 +127,16 @@ func (s *service) addDatabase(name string, config config.DatabaseConfig) error {
|
|||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
// Check for duplicate database connections
|
// Check for duplicate database connections
|
||||||
for existingName, existingConfig := range s.configs {
|
// for existingName, existingConfig := range s.configs {
|
||||||
if existingName != name &&
|
// if existingName != name &&
|
||||||
existingConfig.Host == config.Host &&
|
// existingConfig.Host == config.Host &&
|
||||||
existingConfig.Port == config.Port &&
|
// existingConfig.Port == config.Port &&
|
||||||
existingConfig.Database == config.Database &&
|
// existingConfig.Database == config.Database &&
|
||||||
existingConfig.Type == config.Type {
|
// existingConfig.Type == config.Type {
|
||||||
log.Printf("⚠️ Database %s appears to be a duplicate of %s (same host:port:database), skipping connection", name, existingName)
|
// log.Printf("⚠️ Database %s appears to be a duplicate of %s (same host:port:database), skipping connection", name, existingName)
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
var db *sql.DB
|
var db *sql.DB
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
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"
|
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||||
// @Router /api/v1/retribusis [get]
|
// @Router /api/v1/retribusis [get]
|
||||||
func (h *RetribusiHandler) GetRetribusi(c *gin.Context) {
|
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
|
// CHANGE: Increase timeout for complex queries
|
||||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 120*time.Second)
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 120*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -179,9 +180,31 @@ func (h *RetribusiHandler) GetRetribusi(c *gin.Context) {
|
|||||||
// CHANGE: Use the core fetchRetribusisDynamic function for all data retrieval logic.
|
// CHANGE: Use the core fetchRetribusisDynamic function for all data retrieval logic.
|
||||||
// We only need to build DynamicQuery from simple parameters.
|
// We only need to build DynamicQuery from simple parameters.
|
||||||
query := queryUtils.DynamicQuery{
|
query := queryUtils.DynamicQuery{
|
||||||
From: "data_retribusi",
|
From: "data_retribusi",
|
||||||
Fields: []queryUtils.SelectField{{Expression: "*"}},
|
Fields: []queryUtils.SelectField{
|
||||||
Sort: []queryUtils.SortField{{Column: "date_created", Order: "DESC"}},
|
{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
|
// Parse pagination
|
||||||
@@ -343,6 +366,7 @@ func (h *RetribusiHandler) GetRetribusi(c *gin.Context) {
|
|||||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||||
// @Router /api/v1/retribusi/{id} [get]
|
// @Router /api/v1/retribusi/{id} [get]
|
||||||
func (h *RetribusiHandler) GetRetribusiByID(c *gin.Context) {
|
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")
|
id := c.Param("id")
|
||||||
if _, err := uuid.Parse(id); err != nil {
|
if _, err := uuid.Parse(id); err != nil {
|
||||||
h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
|
h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
|
||||||
@@ -375,8 +399,30 @@ func (h *RetribusiHandler) GetRetribusiByID(c *gin.Context) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
dynamicQuery := queryUtils.DynamicQuery{
|
dynamicQuery := queryUtils.DynamicQuery{
|
||||||
From: "data_retribusi",
|
From: "data_retribusi",
|
||||||
Fields: []queryUtils.SelectField{{Expression: "*"}},
|
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.FilterGroup{{
|
||||||
Filters: []queryUtils.DynamicFilter{
|
Filters: []queryUtils.DynamicFilter{
|
||||||
{Column: "id", Operator: queryUtils.OpEqual, Value: id},
|
{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"
|
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||||
// @Router /api/v1/retribusis/dynamic [get]
|
// @Router /api/v1/retribusis/dynamic [get]
|
||||||
func (h *RetribusiHandler) GetRetribusiDynamic(c *gin.Context) {
|
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)
|
parser := queryUtils.NewQueryParser().SetLimits(10, 100)
|
||||||
dynamicQuery, err := parser.ParseQuery(c.Request.URL.Query(), "data_retribusi")
|
dynamicQuery, err := parser.ParseQuery(c.Request.URL.Query(), "data_retribusi")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -495,6 +542,7 @@ func (h *RetribusiHandler) GetRetribusiDynamic(c *gin.Context) {
|
|||||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||||
// @Router /api/v1/retribusis [post]
|
// @Router /api/v1/retribusis [post]
|
||||||
func (h *RetribusiHandler) CreateRetribusi(c *gin.Context) {
|
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
|
var req retribusi.RetribusiCreateRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
|
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"
|
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||||
// @Router /api/v1/retribusi/{id} [put]
|
// @Router /api/v1/retribusi/{id} [put]
|
||||||
func (h *RetribusiHandler) UpdateRetribusi(c *gin.Context) {
|
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")
|
id := c.Param("id")
|
||||||
if _, err := uuid.Parse(id); err != nil {
|
if _, err := uuid.Parse(id); err != nil {
|
||||||
h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
|
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"
|
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||||
// @Router /api/v1/retribusi/{id} [delete]
|
// @Router /api/v1/retribusi/{id} [delete]
|
||||||
func (h *RetribusiHandler) DeleteRetribusi(c *gin.Context) {
|
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")
|
id := c.Param("id")
|
||||||
if _, err := uuid.Parse(id); err != nil {
|
if _, err := uuid.Parse(id); err != nil {
|
||||||
h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
|
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"
|
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||||
// @Router /api/v1/retribusis/stats [get]
|
// @Router /api/v1/retribusis/stats [get]
|
||||||
func (h *RetribusiHandler) GetRetribusiStats(c *gin.Context) {
|
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
|
// CHANGE: Try ambil dari cache terlebih dahulu
|
||||||
cacheKey := fmt.Sprintf("retribusi:stats:%s", c.Query("status"))
|
cacheKey := fmt.Sprintf("retribusi:stats:%s", c.Query("status"))
|
||||||
if cachedData, found := h.cache.Get(cacheKey); found {
|
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
|
// HELPER FUNCTIONS
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|||||||
@@ -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"
|
"api-service/internal/database"
|
||||||
authHandlers "api-service/internal/handlers/auth"
|
authHandlers "api-service/internal/handlers/auth"
|
||||||
healthcheckHandlers "api-service/internal/handlers/healthcheck"
|
healthcheckHandlers "api-service/internal/handlers/healthcheck"
|
||||||
|
|
||||||
|
pasienPasienHandlers "api-service/internal/handlers/pasien"
|
||||||
retribusiHandlers "api-service/internal/handlers/retribusi"
|
retribusiHandlers "api-service/internal/handlers/retribusi"
|
||||||
"api-service/internal/middleware"
|
"api-service/internal/middleware"
|
||||||
services "api-service/internal/services/auth"
|
services "api-service/internal/services/auth"
|
||||||
@@ -121,6 +123,19 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine {
|
|||||||
retribusiHandler.DeleteRetribusi(c)
|
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)
|
// PROTECTED ROUTES (Authentication Required)
|
||||||
|
|||||||
@@ -1812,6 +1812,24 @@ func (qp *QueryParser) parseSorting(values url.Values) ([]SortField, error) {
|
|||||||
return sorts, nil
|
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
|
// MONGODB QUERY BUILDER
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|||||||
@@ -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", "[email protected]", "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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+3073
-1698
File diff suppressed because it is too large
Load diff
@@ -19,79 +19,109 @@ services:
|
|||||||
table_name: "m_pasien"
|
table_name: "m_pasien"
|
||||||
|
|
||||||
# Define all columns once for reuse
|
# Define all columns once for reuse
|
||||||
columns:
|
schema:
|
||||||
- name: "nomr"
|
columns:
|
||||||
type: "varchar"
|
- name: "id"
|
||||||
nullable: true
|
type: "serial4"
|
||||||
go_type: "string"
|
nullable: false
|
||||||
description: "Nomor Rekam Medis"
|
go_type: "int32"
|
||||||
- name: "title"
|
primary_key: true
|
||||||
type: "varchar"
|
unique: true
|
||||||
nullable: true
|
description: "Primary key for schedule"
|
||||||
go_type: "string"
|
- name: "nomr"
|
||||||
description: "Gelar pasien (Tn, Ny, Sdr, dll)"
|
type: "varchar"
|
||||||
- name: "nama"
|
nullable: true
|
||||||
type: "varchar"
|
go_type: "string"
|
||||||
nullable: true
|
searchable: true
|
||||||
go_type: "string"
|
unique: true
|
||||||
validation: "required,min=1,max=100"
|
description: "Nomor Rekam Medis"
|
||||||
description: "Nama lengkap pasien"
|
- name: "status"
|
||||||
- name: "tempat"
|
type: "varchar"
|
||||||
type: "varchar"
|
nullable: true
|
||||||
nullable: true
|
go_type: "string"
|
||||||
go_type: "string"
|
description: "Status pasien (A = Aktif, I = Inaktif)"
|
||||||
description: "Tempat lahir pasien"
|
- name: "title"
|
||||||
- name: "tgllahir"
|
type: "varchar"
|
||||||
type: "date"
|
nullable: true
|
||||||
nullable: true
|
go_type: "string"
|
||||||
go_type: "time.Time"
|
description: "Gelar pasien (Tn, Ny, Sdr, dll)"
|
||||||
description: "Tanggal lahir pasien"
|
- name: "nama"
|
||||||
- name: "jeniskelamin"
|
type: "varchar"
|
||||||
type: "varchar"
|
nullable: true
|
||||||
nullable: true
|
go_type: "string"
|
||||||
go_type: "string"
|
validation: "required,min=1,max=100"
|
||||||
validation: "oneof=L P"
|
searchable: true
|
||||||
description: "Jenis kelamin (L/P)"
|
description: "Nama lengkap pasien"
|
||||||
- name: "alamat"
|
- name: "tempat"
|
||||||
type: "varchar"
|
type: "varchar"
|
||||||
nullable: true
|
nullable: true
|
||||||
go_type: "string"
|
go_type: "string"
|
||||||
description: "Alamat lengkap pasien"
|
description: "Tempat lahir pasien"
|
||||||
- name: "kelurahan"
|
- name: "tgllahir"
|
||||||
type: "int8"
|
type: "date"
|
||||||
nullable: true
|
nullable: true
|
||||||
go_type: "int64"
|
go_type: "time.Time"
|
||||||
description: "ID Kelurahan"
|
description: "Tanggal lahir pasien"
|
||||||
- name: "kdkecamatan"
|
- name: "jeniskelamin"
|
||||||
type: "int4"
|
type: "varchar"
|
||||||
nullable: true
|
nullable: true
|
||||||
go_type: "int32"
|
go_type: "string"
|
||||||
description: "ID Kecamatan"
|
validation: "oneof=L P"
|
||||||
- name: "kota"
|
description: "Jenis kelamin (L/P)"
|
||||||
type: "int4"
|
- name: "alamat"
|
||||||
nullable: true
|
type: "varchar"
|
||||||
go_type: "int32"
|
nullable: true
|
||||||
description: "ID Kota"
|
go_type: "string"
|
||||||
- name: "kdprovinsi"
|
description: "Alamat lengkap pasien"
|
||||||
type: "int4"
|
- name: "kelurahan"
|
||||||
nullable: true
|
type: "int8"
|
||||||
go_type: "int32"
|
nullable: true
|
||||||
description: "ID Provinsi"
|
go_type: "int64"
|
||||||
- name: "agama"
|
description: "ID Kelurahan"
|
||||||
type: "int4"
|
- name: "kdkecamatan"
|
||||||
nullable: true
|
type: "int4"
|
||||||
go_type: "int32"
|
nullable: true
|
||||||
description: "ID Agama"
|
go_type: "int32"
|
||||||
- name: "no_kartu"
|
description: "ID Kecamatan"
|
||||||
type: "varchar"
|
- name: "kota"
|
||||||
nullable: true
|
type: "int4"
|
||||||
go_type: "string"
|
nullable: true
|
||||||
description: "Nomor kartu identitas"
|
go_type: "int32"
|
||||||
- name: "noktp_baru"
|
description: "ID Kota"
|
||||||
type: "varchar"
|
- name: "kdprovinsi"
|
||||||
nullable: true
|
type: "int4"
|
||||||
go_type: "string"
|
nullable: true
|
||||||
description: "Nomor KTP baru"
|
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
|
# Define relationships with other tables
|
||||||
relationships:
|
relationships:
|
||||||
@@ -167,6 +197,8 @@ services:
|
|||||||
# Define endpoints with reusable configurations
|
# Define endpoints with reusable configurations
|
||||||
endpoints:
|
endpoints:
|
||||||
list:
|
list:
|
||||||
|
handler_folder: "pasien"
|
||||||
|
handler_file: "pasien.go"
|
||||||
methods: ["GET"]
|
methods: ["GET"]
|
||||||
path: "/"
|
path: "/"
|
||||||
description: "Get list of pasien with pagination and filters"
|
description: "Get list of pasien with pagination and filters"
|
||||||
@@ -182,19 +214,37 @@ services:
|
|||||||
fields: "with_location_names"
|
fields: "with_location_names"
|
||||||
response_model: "PasienGetResponse"
|
response_model: "PasienGetResponse"
|
||||||
|
|
||||||
get_by_nomr:
|
get_by_id:
|
||||||
|
handler_folder: "pasien"
|
||||||
|
handler_file: "pasien.go"
|
||||||
methods: ["GET"]
|
methods: ["GET"]
|
||||||
path: "/:nomr"
|
path: "/:id"
|
||||||
description: "Get pasien by NOMR"
|
description: "Get pasien by ID"
|
||||||
summary: "Get Pasien by NOMR"
|
summary: "Get Pasien by ID"
|
||||||
tags: ["Pasien"]
|
tags: ["Pasien"]
|
||||||
require_auth: true
|
require_auth: true
|
||||||
cache_enabled: true
|
cache_enabled: true
|
||||||
cache_ttl: 300
|
cache_ttl: 300
|
||||||
fields: "with_location_names"
|
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:
|
create:
|
||||||
|
handler_folder: "pasien"
|
||||||
|
handler_file: "pasien.go"
|
||||||
methods: ["POST"]
|
methods: ["POST"]
|
||||||
path: "/"
|
path: "/"
|
||||||
description: "Create a new pasien"
|
description: "Create a new pasien"
|
||||||
@@ -206,6 +256,8 @@ services:
|
|||||||
response_model: "PasienCreateResponse"
|
response_model: "PasienCreateResponse"
|
||||||
|
|
||||||
update:
|
update:
|
||||||
|
handler_folder: "pasien"
|
||||||
|
handler_file: "pasien.go"
|
||||||
methods: ["PUT"]
|
methods: ["PUT"]
|
||||||
path: "/:nomr"
|
path: "/:nomr"
|
||||||
description: "Update an existing pasien"
|
description: "Update an existing pasien"
|
||||||
@@ -217,16 +269,20 @@ services:
|
|||||||
response_model: "PasienUpdateResponse"
|
response_model: "PasienUpdateResponse"
|
||||||
|
|
||||||
delete:
|
delete:
|
||||||
|
handler_folder: "pasien"
|
||||||
|
handler_file: "pasien.go"
|
||||||
methods: ["DELETE"]
|
methods: ["DELETE"]
|
||||||
path: "/:nomr"
|
path: "/:nomr"
|
||||||
description: "Delete a pasien"
|
description: "Delete a pasien"
|
||||||
summary: "Delete Pasien"
|
summary: "Delete Pasien"
|
||||||
tags: ["Pasien"]
|
tags: ["Pasien"]
|
||||||
require_auth: true
|
require_auth: true
|
||||||
soft_delete: false
|
soft_delete: true
|
||||||
response_model: "PasienDeleteResponse"
|
response_model: "PasienDeleteResponse"
|
||||||
|
|
||||||
dynamic:
|
dynamic:
|
||||||
|
handler_folder: "pasien"
|
||||||
|
handler_file: "pasien.go"
|
||||||
methods: ["GET"]
|
methods: ["GET"]
|
||||||
path: "/dynamic"
|
path: "/dynamic"
|
||||||
description: "Get pasien with dynamic filtering"
|
description: "Get pasien with dynamic filtering"
|
||||||
@@ -237,28 +293,34 @@ services:
|
|||||||
fields: "with_location_names"
|
fields: "with_location_names"
|
||||||
response_model: "PasienGetResponse"
|
response_model: "PasienGetResponse"
|
||||||
|
|
||||||
search:
|
# search:
|
||||||
methods: ["GET"]
|
# handler_folder: "pasien"
|
||||||
path: "/search"
|
# handler_file: "pasien.go"
|
||||||
description: "Search pasien by name or NOMR"
|
# methods: ["GET"]
|
||||||
summary: "Search Pasien"
|
# path: "/search"
|
||||||
tags: ["Pasien"]
|
# description: "Search pasien by name or NOMR"
|
||||||
require_auth: true
|
# summary: "Search Pasien"
|
||||||
has_search: true
|
# tags: ["Pasien"]
|
||||||
fields: "with_location_names"
|
# require_auth: true
|
||||||
response_model: "PasienGetResponse"
|
# has_search: true
|
||||||
|
# fields: "with_location_names"
|
||||||
|
# response_model: "PasienGetResponse"
|
||||||
|
|
||||||
stats:
|
# stats:
|
||||||
methods: ["GET"]
|
# handler_folder: "pasien"
|
||||||
path: "/stats"
|
# handler_file: "pasien.go"
|
||||||
description: "Get pasien statistics"
|
# methods: ["GET"]
|
||||||
summary: "Get Pasien Stats"
|
# path: "/stats"
|
||||||
tags: ["Pasien"]
|
# description: "Get pasien statistics"
|
||||||
require_auth: true
|
# summary: "Get Pasien Stats"
|
||||||
has_stats: true
|
# tags: ["Pasien"]
|
||||||
response_model: "AggregateData"
|
# require_auth: true
|
||||||
|
# has_stats: true
|
||||||
|
# response_model: "AggregateData"
|
||||||
|
|
||||||
by_location:
|
by_location:
|
||||||
|
handler_folder: "pasien"
|
||||||
|
handler_file: "pasien.go"
|
||||||
methods: ["GET"]
|
methods: ["GET"]
|
||||||
path: "/by-location"
|
path: "/by-location"
|
||||||
description: "Get pasien by location (provinsi, kota, kecamatan, kelurahan)"
|
description: "Get pasien by location (provinsi, kota, kecamatan, kelurahan)"
|
||||||
@@ -270,6 +332,8 @@ services:
|
|||||||
response_model: "PasienGetResponse"
|
response_model: "PasienGetResponse"
|
||||||
|
|
||||||
by_age:
|
by_age:
|
||||||
|
handler_folder: "pasien"
|
||||||
|
handler_file: "pasien.go"
|
||||||
methods: ["GET"]
|
methods: ["GET"]
|
||||||
path: "/by-age"
|
path: "/by-age"
|
||||||
description: "Get pasien statistics by age group"
|
description: "Get pasien statistics by age group"
|
||||||
@@ -278,3 +342,200 @@ services:
|
|||||||
require_auth: true
|
require_auth: true
|
||||||
has_stats: 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