Add Search
Some checks failed
Go-test / build (push) Has been cancelled

This commit is contained in:
2025-09-08 14:47:27 +07:00
parent 6d57abf442
commit 4b12c49455
8 changed files with 1339 additions and 617 deletions

View File

@@ -5,11 +5,12 @@ import (
"api-service/internal/database"
models "api-service/internal/models"
"api-service/internal/models/qris"
"api-service/internal/utils/validation"
utils "api-service/internal/utils/filters"
"context"
"database/sql"
"fmt"
"log"
"net"
"net/http"
"strconv"
"strings"
@@ -69,7 +70,7 @@ func NewQrisHandler() *QrisHandler {
// @Success 200 {object} qris.QrisGetResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/qriss [get]
// @Router /qris [get]
func (h *QrisHandler) GetQris(c *gin.Context) {
// Parse pagination parameters
limit, offset, err := h.parsePaginationParams(c)
@@ -118,7 +119,7 @@ func (h *QrisHandler) GetQris(c *gin.Context) {
wg.Add(1)
go func() {
defer wg.Done()
result, err := h.fetchQriss(ctx, dbConn, filter, limit, offset)
result, err := h.fetchQris(ctx, dbConn, filter, limit, offset)
mu.Lock()
if err != nil {
errChan <- fmt.Errorf("failed to fetch data: %w", err)
@@ -177,16 +178,16 @@ func (h *QrisHandler) GetQris(c *gin.Context) {
// @Tags Qris
// @Accept json
// @Produce json
// @Param id path string true "Qris ID (UUID)"
// @Param id path string true "Qris ID (Int serial4)"
// @Success 200 {object} qris.QrisGetByIDResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Invalid ID format"
// @Failure 404 {object} models.ErrorResponse "Qris not found"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/qris/{id} [get]
// @Router /qris/{id} [get]
func (h *QrisHandler) GetQrisByID(c *gin.Context) {
id := c.Param("id")
// Validate UUID format
// Validate Int ID format
intID, err := strconv.Atoi(id)
if err != nil {
h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
@@ -220,6 +221,54 @@ func (h *QrisHandler) GetQrisByID(c *gin.Context) {
c.JSON(http.StatusOK, response)
}
// GetQrisByIP godoc
// @Summary Get Qris by IP
// @Description Returns a single qris by IP
// @Tags Qris
// @Accept json
// @Produce json
// @Param ip path string true "IP address"
// @Success 200 {object} qris.QrisGetByIPResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Invalid IP address format"
// @Failure 404 {object} models.ErrorResponse "Qris not found"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /qris/ip/{ip} [get]
func (h *QrisHandler) GetQrisByIP(c *gin.Context) {
ip := c.Param("ip")
// Validate IP address format
if net.ParseIP(ip) == nil {
h.respondError(c, "Invalid IP address format", fmt.Errorf("invalid IP: %s", ip), http.StatusBadRequest)
return
}
dbConn, err := h.db.GetDB("simrs_backup")
if err != nil {
h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
return
}
ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
defer cancel()
item, err := h.getQrisByIP(ctx, dbConn, ip)
if err != nil {
if err == sql.ErrNoRows {
h.respondError(c, "Qris not found", err, http.StatusNotFound)
} else {
h.logAndRespondError(c, "Failed to get qris", err, http.StatusInternalServerError)
}
return
}
response := qris.QrisGetByIPResponse{
Message: "qris details retrieved successfully",
Data: item,
}
c.JSON(http.StatusOK, response)
}
// GetQrisStats godoc
// @Summary Get qris statistics
// @Description Returns comprehensive statistics about qris data
@@ -229,7 +278,7 @@ func (h *QrisHandler) GetQrisByID(c *gin.Context) {
// @Param status query string false "Filter statistics by status"
// @Success 200 {object} models.AggregateData "Statistics data"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/qriss/stats [get]
// @Router /qris/stats [get]
func (h *QrisHandler) GetQrisStats(c *gin.Context) {
dbConn, err := h.db.GetDB("simrs_backup")
if err != nil {
@@ -255,11 +304,24 @@ func (h *QrisHandler) GetQrisStats(c *gin.Context) {
// Database operations
func (h *QrisHandler) getQrisByID(ctx context.Context, dbConn *sql.DB, id int) (*qris.Qris, error) {
query := "SELECT id, status, created_at, updated_at, display_name FROM t_qrdata WHERE id = $1 AND status IS NOT NULL"
query := "SELECT id, status, created_at, updated_at, display_name, display_amount, qrvalue, ip, display_nobill, posdevice FROM t_qrdata WHERE id = $1 AND status != '0'"
row := dbConn.QueryRowContext(ctx, query, id)
var item qris.Qris
err := row.Scan(&item.ID, &item.Status, &item.CreatedAt, &item.UpdatedAt, &item.DisplayName)
err := row.Scan(&item.ID, &item.Status, &item.CreatedAt, &item.UpdatedAt, &item.DisplayName, &item.DisplayAmount, &item.QrValue, &item.IPAddress, &item.DisplayNoBill, &item.PosDevice)
if err != nil {
return nil, err
}
return &item, nil
}
func (h *QrisHandler) getQrisByIP(ctx context.Context, dbConn *sql.DB, ip string) (*qris.Qris, error) {
query := "SELECT id, status, created_at, updated_at, display_name, display_amount, qrvalue, ip, display_nobill, posdevice FROM t_qrdata WHERE ip = $1 AND status != '0'"
row := dbConn.QueryRowContext(ctx, query, ip)
var item qris.Qris
err := row.Scan(&item.ID, &item.Status, &item.CreatedAt, &item.UpdatedAt, &item.DisplayName, &item.DisplayAmount, &item.QrValue, &item.IPAddress, &item.DisplayNoBill, &item.PosDevice)
if err != nil {
return nil, err
}
@@ -298,7 +360,7 @@ func (h *QrisHandler) getQrisByID(ctx context.Context, dbConn *sql.DB, id int) (
return &item, nil
}*/
func (h *QrisHandler) deleteQris(ctx context.Context, dbConn *sql.DB, id string) error {
/*func (h *QrisHandler) deleteQris(ctx context.Context, dbConn *sql.DB, id string) error {
now := time.Now()
query := "UPDATE data_qris_qris SET status = 'deleted', updated_at = $2 WHERE id = $1 AND status != 'deleted'"
@@ -317,16 +379,16 @@ func (h *QrisHandler) deleteQris(ctx context.Context, dbConn *sql.DB, id string)
}
return nil
}
}*/
func (h *QrisHandler) fetchQriss(ctx context.Context, dbConn *sql.DB, filter qris.QrisFilter, limit, offset int) ([]qris.Qris, error) {
func (h *QrisHandler) fetchQris(ctx context.Context, dbConn *sql.DB, filter qris.QrisFilter, limit, offset int) ([]qris.Qris, error) {
whereClause, args := h.buildWhereClause(filter)
query := fmt.Sprintf("SELECT id, status, created_at, updated_at, display_name FROM t_qrdata WHERE %s ORDER BY created_at DESC NULLS LAST LIMIT $%d OFFSET $%d", whereClause, len(args)+1, len(args)+2)
query := fmt.Sprintf("SELECT id, status, created_at, updated_at, display_name, display_amount, qrvalue, ip, display_nobill, posdevice FROM t_qrdata WHERE %s ORDER BY created_at DESC NULLS LAST LIMIT $%d OFFSET $%d", whereClause, len(args)+1, len(args)+2)
args = append(args, limit, offset)
rows, err := dbConn.QueryContext(ctx, query, args...)
if err != nil {
return nil, fmt.Errorf("fetch qriss query failed: %w", err)
return nil, fmt.Errorf("fetch qris query failed: %w", err)
}
defer rows.Close()
@@ -343,7 +405,7 @@ func (h *QrisHandler) fetchQriss(ctx context.Context, dbConn *sql.DB, filter qri
return nil, fmt.Errorf("rows iteration error: %w", err)
}
log.Printf("Successfully fetched %d qriss with filters applied", len(items))
log.Printf("Successfully fetched %d qris with filters applied", len(items))
return items, nil
}
@@ -355,9 +417,14 @@ func (h *QrisHandler) scanQris(rows *sql.Rows) (qris.Qris, error) {
err := rows.Scan(
&item.ID,
&item.Status,
&item.CreatedAt, //.Time, &item.CreatedAt.Valid, // sql.NullTime
&item.UpdatedAt, //.Time, &item.UpdatedAt.Valid, // sql.NullTime
&item.DisplayName, //.String, &item.DisplayName.Valid, // sql.NullString
&item.CreatedAt,
&item.UpdatedAt,
&item.DisplayName,
&item.DisplayAmount,
&item.QrValue,
&item.IPAddress,
&item.DisplayNoBill,
&item.PosDevice,
)
return item, err
@@ -368,6 +435,7 @@ func (h *QrisHandler) getTotalCount(ctx context.Context, dbConn *sql.DB, filter
countQuery := fmt.Sprintf("SELECT COUNT(*) FROM t_qrdata WHERE %s", whereClause)
if err := dbConn.QueryRowContext(ctx, countQuery, args...).Scan(total); err != nil {
return fmt.Errorf("total count query failed: %w", err)
}
return nil
}
@@ -562,7 +630,7 @@ func (h *QrisHandler) parseFilterParams(c *gin.Context) qris.QrisFilter {
// Build WHERE clause dengan filter parameters
func (h *QrisHandler) buildWhereClause(filter qris.QrisFilter) (string, []interface{}) {
conditions := []string{"status IS NOT NULL"}
conditions := []string{"status != '0'"}
args := []interface{}{}
paramCount := 1
@@ -573,11 +641,16 @@ func (h *QrisHandler) buildWhereClause(filter qris.QrisFilter) (string, []interf
}
if filter.Search != nil {
searchCondition := fmt.Sprintf("display_name ILIKE $%d", paramCount)
conditions = append(conditions, searchCondition)
searchTerm := "%" + *filter.Search + "%"
args = append(args, searchTerm)
paramCount++
searchFields := []string{"ip", "display_name", "posdevice"}
searchConditions := make([]string, 0, len(searchFields))
for _, field := range searchFields {
searchConditions = append(searchConditions, fmt.Sprintf("%s ILIKE $%d", field, paramCount))
args = append(args, searchTerm)
paramCount++
}
// Combine with OR
conditions = append(conditions, fmt.Sprintf("(%s)", strings.Join(searchConditions, " OR ")))
}
if filter.DateFrom != nil {
@@ -674,7 +747,257 @@ func (h *QrisHandler) calculateMeta(limit, offset, total int) models.MetaRespons
}*/
// GetLastSubmissionTime example
func (h *QrisHandler) getLastSubmissionTimeExample(ctx context.Context, dbConn *sql.DB, identifier string) (*time.Time, error) {
/*func (h *QrisHandler) getLastSubmissionTimeExample(ctx context.Context, dbConn *sql.DB, identifier string) (*time.Time, error) {
validator := validation.NewDuplicateValidator(dbConn)
return validator.GetLastSubmissionTime(ctx, "t_qrdata", "id", "created_at", identifier)
}*/
// fetchDynamic executes dynamic query
func (h *QrisHandler) fetchDynamic(ctx context.Context, dbConn *sql.DB, query utils.DynamicQuery) ([]qris.Qris, int, error) {
// Setup query builder
builder := utils.NewQueryBuilder("t_qrdata").
SetColumnMapping(map[string]string{
"id": "id",
"status": "status",
"created_at": "created_at",
"updated_at": "updated_at",
"display_name": "display_name",
"display_amount": "display_amount",
"qrvalue": "qrvalue",
"ip": "ip",
"display_nobill": "display_nobill",
"posdevice": "posdevice",
}).
SetAllowedColumns([]string{
"id", "status", "created_at", "updated_at", "display_name",
"display_amount", "qrvalue", "ip", "display_nobill", "posdevice",
})
// Add default filter to exclude deleted records
query.Filters = append([]utils.FilterGroup{{
Filters: []utils.DynamicFilter{{
Column: "status",
Operator: utils.OpNotEqual,
Value: "0",
}},
LogicOp: "AND",
}}, query.Filters...)
// Execute concurrent queries
var (
items []qris.Qris
total int
wg sync.WaitGroup
errChan = make(chan error, 2)
mu sync.Mutex
)
// Fetch total count
wg.Add(1)
go func() {
defer wg.Done()
countQuery := query
countQuery.Limit = 0
countQuery.Offset = 0
countSQL, countArgs, err := builder.BuildCountQuery(countQuery)
if err != nil {
errChan <- fmt.Errorf("failed to build count query: %w", err)
return
}
if err := dbConn.QueryRowContext(ctx, countSQL, countArgs...).Scan(&total); err != nil {
errChan <- fmt.Errorf("failed to get total count: %w", err)
return
}
}()
// Fetch main data
wg.Add(1)
go func() {
defer wg.Done()
mainSQL, mainArgs, err := builder.BuildQuery(query)
if err != nil {
errChan <- fmt.Errorf("failed to build main query: %w", err)
return
}
rows, err := dbConn.QueryContext(ctx, mainSQL, mainArgs...)
if err != nil {
errChan <- fmt.Errorf("failed to execute main query: %w", err)
return
}
defer rows.Close()
var results []qris.Qris
for rows.Next() {
item, err := h.scanQris(rows)
if err != nil {
errChan <- fmt.Errorf("failed to scan search: %w", err)
return
}
results = append(results, item)
}
if err := rows.Err(); err != nil {
errChan <- fmt.Errorf("rows iteration error: %w", err)
return
}
mu.Lock()
items = results
mu.Unlock()
}()
// Wait for all goroutines
wg.Wait()
close(errChan)
// Check for errors
for err := range errChan {
if err != nil {
return nil, 0, err
}
}
return items, total, nil
}
// GetSearchDynamic godoc
// @Summary Get search with dynamic filtering
// @Description Returns search with advanced dynamic filtering like Directus
// @Tags Search
// @Accept json
// @Produce json
// @Param fields query string false "Fields to select (e.g., fields=*.*)"
// @Param filter[column][operator] query string false "Dynamic filters (e.g., filter[name][_eq]=value)"
// @Param sort query string false "Sort fields (e.g., sort=date_created,-name)"
// @Param limit query int false "Limit" default(10)
// @Param offset query int false "Offset" default(0)
// @Success 200 {object} qris.QrisGetResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /qris/search/dynamic [get]
func (h *QrisHandler) GetSearchDynamic(c *gin.Context) {
// Parse query parameters
parser := utils.NewQueryParser().SetLimits(10, 100)
dynamicQuery, err := parser.ParseQuery(c.Request.URL.Query())
if err != nil {
h.respondError(c, "Invalid query parameters", err, http.StatusBadRequest)
return
}
// Get database connection
dbConn, err := h.db.GetDB("simrs_backup")
if err != nil {
h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
return
}
// Create context with timeout
ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
defer cancel()
// Execute query with dynamic filtering
items, total, err := h.fetchDynamic(ctx, dbConn, dynamicQuery)
if err != nil {
h.logAndRespondError(c, "Failed to fetch data", err, http.StatusInternalServerError)
return
}
// Build response
meta := h.calculateMeta(dynamicQuery.Limit, dynamicQuery.Offset, total)
response := qris.QrisGetResponse{
Message: "Data qris berhasil diambil",
Data: items,
Meta: meta,
}
c.JSON(http.StatusOK, response)
}
// SearchAdvanced provides advanced search capabilities
func (h *QrisHandler) SearchAdvanced(c *gin.Context) {
// Parse complex search parameters
searchQuery := c.Query("q")
if searchQuery == "" {
h.respondError(c, "Search query is required", fmt.Errorf("empty search query"), http.StatusBadRequest)
return
}
// Build dynamic query for search
query := utils.DynamicQuery{
Fields: []string{"*"},
Filters: []utils.FilterGroup{{
Filters: []utils.DynamicFilter{
{
Column: "status",
Operator: utils.OpNotEqual,
Value: "0",
},
{
Column: "ip",
Operator: utils.OpContains,
Value: searchQuery,
LogicOp: "OR",
},
{
Column: "display_name",
Operator: utils.OpContains,
Value: searchQuery,
LogicOp: "OR",
},
{
Column: "posdevice",
Operator: utils.OpContains,
Value: searchQuery,
LogicOp: "OR",
},
},
LogicOp: "AND",
}},
Sort: []utils.SortField{{
Column: "created_at",
Order: "DESC",
}},
Limit: 20,
Offset: 0,
}
// Parse pagination if provided
if limit := c.Query("limit"); limit != "" {
if l, err := strconv.Atoi(limit); err == nil && l > 0 && l <= 100 {
query.Limit = l
}
}
if offset := c.Query("offset"); offset != "" {
if o, err := strconv.Atoi(offset); err == nil && o >= 0 {
query.Offset = o
}
}
// Get database connection
dbConn, err := h.db.GetDB("simrs_backup")
if err != nil {
h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
return
}
ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
defer cancel()
// Execute search
items, total, err := h.fetchDynamic(ctx, dbConn, query)
if err != nil {
h.logAndRespondError(c, "Search failed", err, http.StatusInternalServerError)
return
}
// Build response
meta := h.calculateMeta(query.Limit, query.Offset, total)
response := qris.QrisGetResponse{
Message: fmt.Sprintf("Search results for '%s'", searchQuery),
Data: items,
Meta: meta,
}
c.JSON(http.StatusOK, response)
}

View File

@@ -76,7 +76,7 @@ func NewRetribusiHandler() *RetribusiHandler {
// @Success 200 {object} modelsretribusi.RetribusiGetResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusis [get]
// @Router /retribusis [get]
func (h *RetribusiHandler) GetRetribusi(c *gin.Context) {
// Parse pagination parameters
limit, offset, err := h.parsePaginationParams(c)
@@ -189,7 +189,7 @@ func (h *RetribusiHandler) GetRetribusi(c *gin.Context) {
// @Failure 400 {object} models.ErrorResponse "Invalid ID format"
// @Failure 404 {object} models.ErrorResponse "Retribusi not found"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusi/{id} [get]
// @Router /retribusi/{id} [get]
func (h *RetribusiHandler) GetRetribusiByID(c *gin.Context) {
id := c.Param("id")
@@ -240,7 +240,7 @@ func (h *RetribusiHandler) GetRetribusiByID(c *gin.Context) {
// @Success 200 {object} modelsretribusi.RetribusiGetResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusis/dynamic [get]
// @Router /retribusi/dynamic [get]
func (h *RetribusiHandler) GetRetribusiDynamic(c *gin.Context) {
// Parse query parameters
parser := utils.NewQueryParser().SetLimits(10, 100)
@@ -503,7 +503,7 @@ func (h *RetribusiHandler) SearchRetribusiAdvanced(c *gin.Context) {
// @Success 201 {object} modelsretribusi.RetribusiCreateResponse "Retribusi created successfully"
// @Failure 400 {object} models.ErrorResponse "Bad request or validation error"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusis [post]
// @Router /retribusis [post]
func (h *RetribusiHandler) CreateRetribusi(c *gin.Context) {
var req modelsretribusi.RetribusiCreateRequest
@@ -559,7 +559,7 @@ func (h *RetribusiHandler) CreateRetribusi(c *gin.Context) {
// @Failure 400 {object} models.ErrorResponse "Bad request or validation error"
// @Failure 404 {object} models.ErrorResponse "Retribusi not found"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusi/{id} [put]
// @Router /retribusi/{id} [put]
func (h *RetribusiHandler) UpdateRetribusi(c *gin.Context) {
id := c.Param("id")
@@ -622,7 +622,7 @@ func (h *RetribusiHandler) UpdateRetribusi(c *gin.Context) {
// @Failure 400 {object} models.ErrorResponse "Invalid ID format"
// @Failure 404 {object} models.ErrorResponse "Retribusi not found"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusi/{id} [delete]
// @Router /retribusi/{id} [delete]
func (h *RetribusiHandler) DeleteRetribusi(c *gin.Context) {
id := c.Param("id")
@@ -668,7 +668,7 @@ func (h *RetribusiHandler) DeleteRetribusi(c *gin.Context) {
// @Param status query string false "Filter statistics by status"
// @Success 200 {object} models.AggregateData "Statistics data"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusis/stats [get]
// @Router /retribusis/stats [get]
func (h *RetribusiHandler) GetRetribusiStats(c *gin.Context) {
dbConn, err := h.db.GetDB("postgres_satudata")
if err != nil {

View File

@@ -13,25 +13,26 @@ type Qris struct {
ID string `json:"id" db:"id"`
Status string `json:"status" db:"status"`
Sort models.NullableInt32 `json:"sort,omitempty" db:"sort"`
UserCreated sql.NullString `json:"user_created,omitempty" db:"user_created"`
PosDevice sql.NullString `json:"posdevice,omitempty" db:"posdevice"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UserUpdated sql.NullString `json:"user_updated,omitempty" db:"user_updated"`
InvoiceNumber sql.NullString `json:"invoice_number,omitempty" db:"invoice_number"`
UpdatedAt sql.NullTime `json:"updated_at,omitempty" db:"updated_at"`
DisplayName sql.NullString `json:"display_name,omitempty" db:"display_name"`
DisplayAmount float64 `json:"display_amount" db:"display_amount"`
QrValue string `json:"qrvalue" db:"qrvalue"`
IP string `json:"ip" db:"ip"`
IPAddress string `json:"ip" db:"ip"`
DisplayNoBill string `json:"display_nobill" db:"display_nobill"`
}
// Custom JSON marshaling untuk Qris agar NULL values tidak muncul di response
func (r Qris) MarshalJSON() ([]byte, error) {
type Alias Qris
aux := &struct {
Sort *int `json:"sort,omitempty"`
UserCreated *string `json:"user_created,omitempty"`
UserUpdated *string `json:"user_updated,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
DisplayName *string `json:"display_name,omitempty"`
Sort *int `json:"sort,omitempty"`
PosDevice *string `json:"posdevice,omitempty"`
InvoiceNumber *string `json:"invoice_number,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
DisplayName *string `json:"display_name,omitempty"`
*Alias
}{
Alias: (*Alias)(&r),
@@ -41,11 +42,11 @@ func (r Qris) MarshalJSON() ([]byte, error) {
sort := int(r.Sort.Int32)
aux.Sort = &sort
}
if r.UserCreated.Valid {
aux.UserCreated = &r.UserCreated.String
if r.PosDevice.Valid {
aux.PosDevice = &r.PosDevice.String
}
if r.UserUpdated.Valid {
aux.UserUpdated = &r.UserUpdated.String
if r.InvoiceNumber.Valid {
aux.InvoiceNumber = &r.InvoiceNumber.String
}
if r.UpdatedAt.Valid {
aux.UpdatedAt = &r.UpdatedAt.Time
@@ -70,6 +71,12 @@ type QrisGetByIDResponse struct {
Data *Qris `json:"data"`
}
// Response struct untuk GET by IP
type QrisGetByIPResponse struct {
Message string `json:"message"`
Data *Qris `json:"data"`
}
// Enhanced GET response dengan pagination dan aggregation
type QrisGetResponse struct {
Message string `json:"message"`

View File

@@ -73,21 +73,35 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine {
{
qrisQrisGroup.GET("", qrisQrisHandler.GetQris)
qrisQrisGroup.GET("/:id", qrisQrisHandler.GetQrisByID)
qrisQrisGroup.GET("/ip/:ip", qrisQrisHandler.GetQrisByIP)
qrisQrisGroup.GET("/stats", qrisQrisHandler.GetQrisStats)
qrisQrisGroup.GET("/dynamic", qrisQrisHandler.GetSearchDynamic)
qrisQrisGroup.GET("/search", qrisQrisHandler.SearchAdvanced)
}
// // Retribusi endpoints
// retribusiHandler := retribusiHandlers.NewRetribusiHandler()
// retribusiGroup := v1.Group("/retribusi")
// {
// retribusiGroup.GET("", retribusiHandler.GetRetribusi)
// retribusiGroup.GET("/dynamic", retribusiHandler.GetRetribusiDynamic) // Route baru
// retribusiGroup.GET("/search", retribusiHandler.SearchRetribusiAdvanced) // Route pencarian
// retribusiGroup.GET("/:id", retribusiHandler.GetRetribusiByID)
// retribusiGroup.POST("", retribusiHandler.CreateRetribusi)
// retribusiGroup.PUT("/:id", retribusiHandler.UpdateRetribusi)
// retribusiGroup.DELETE("/:id", retribusiHandler.DeleteRetribusi)
// }
// Search endpoints
/*qrisSearchHandler := qrisSearchHandlers.NewSearchHandler()
qrisSearchGroup := v1.Group("/qris/search")
{
//qrisSearchGroup.GET("", qrisSearchHandler.GetSearch)
qrisSearchGroup.GET("/dynamic", qrisSearchHandler.GetSearchDynamic) // Route baru
qrisSearchGroup.GET("/advanced", qrisSearchHandler.SearchAdvanced) // Route pencarian
//qrisSearchGroup.GET("/:id", qrisSearchHandler.GetSearchByID)
qrisSearchGroup.GET("/stats", qrisSearchHandler.GetSearchStats)
}*/
// Retribusi endpoints
retribusiHandler := retribusiHandlers.NewRetribusiHandler()
retribusiGroup := v1.Group("/retribusi")
{
retribusiGroup.GET("", retribusiHandler.GetRetribusi)
retribusiGroup.GET("/dynamic", retribusiHandler.GetRetribusiDynamic) // Route baru
retribusiGroup.GET("/search", retribusiHandler.SearchRetribusiAdvanced) // Route pencarian
retribusiGroup.GET("/:id", retribusiHandler.GetRetribusiByID)
retribusiGroup.POST("", retribusiHandler.CreateRetribusi)
retribusiGroup.PUT("/:id", retribusiHandler.UpdateRetribusi)
retribusiGroup.DELETE("/:id", retribusiHandler.DeleteRetribusi)
}
// =============================================================================
// PROTECTED ROUTES (Authentication Required)
// =============================================================================
@@ -100,15 +114,15 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine {
protected.GET("/auth/me", authHandler.Me)
// Retribusi endpoints (CRUD operations - should be protected)
retribusiHandler := retribusiHandlers.NewRetribusiHandler()
protectedRetribusi := protected.Group("/retribusi")
{
protectedRetribusi.GET("", retribusiHandler.GetRetribusi) // GET /api/v1/retribusi
protectedRetribusi.GET("/:id", retribusiHandler.GetRetribusiByID) // GET /api/v1/retribusi/:id
protectedRetribusi.POST("/", retribusiHandler.CreateRetribusi) // POST /api/v1/retribusi/
protectedRetribusi.PUT("/:id", retribusiHandler.UpdateRetribusi) // PUT /api/v1/retribusi/:id
protectedRetribusi.DELETE("/:id", retribusiHandler.DeleteRetribusi) // DELETE /api/v1/retribusi/:id
}
// retribusiHandler := retribusiHandlers.NewRetribusiHandler()
// protectedRetribusi := protected.Group("/retribusi")
// {
// protectedRetribusi.GET("", retribusiHandler.GetRetribusi) // GET /api/v1/retribusi
// protectedRetribusi.GET("/:id", retribusiHandler.GetRetribusiByID) // GET /api/v1/retribusi/:id
// protectedRetribusi.POST("/", retribusiHandler.CreateRetribusi) // POST /api/v1/retribusi/
// protectedRetribusi.PUT("/:id", retribusiHandler.UpdateRetribusi) // PUT /api/v1/retribusi/:id
// protectedRetribusi.DELETE("/:id", retribusiHandler.DeleteRetribusi) // DELETE /api/v1/retribusi/:id
// }
// // BPJS VClaim endpoints (require authentication)
// // Peserta routes