package main import ( "fmt" "os" "path/filepath" "strings" "time" ) // BpjsHandlerData contains template data for BPJS handler generation type BpjsHandlerData struct { Name string NameLower string NameUpper string Category string CategoryPath string ModuleName string HasGet bool HasPost bool HasPut bool HasDelete bool GetEndpoint string PostEndpoint string PutEndpoint string DeleteEndpoint string Timestamp string } func main() { if len(os.Args) < 2 { fmt.Println("Usage: go run generate-bpjs-handler.go [category/]entity [methods]") fmt.Println("Examples:") fmt.Println(" go run generate-bpjs-handler.go vclaim/sep get post put delete") fmt.Println(" go run generate-bpjs-handler.go eclaim/klaim get post") fmt.Println(" go run generate-bpjs-handler.go peserta get") os.Exit(1) } // Parse entity path entityPath := os.Args[1] methods := []string{} if len(os.Args) > 2 { methods = os.Args[2:] } else { methods = []string{"get", "post", "put", "delete"} } // Parse category and entity var category, entityName string if strings.Contains(entityPath, "/") { parts := strings.Split(entityPath, "/") if len(parts) != 2 { fmt.Println("❌ Error: Invalid path format. Use 'category/entity' or just 'entity'") os.Exit(1) } category = parts[0] entityName = parts[1] } else { category = "" entityName = entityPath } // Format names entityName = strings.Title(entityName) entityLower := strings.ToLower(entityName) entityUpper := strings.ToUpper(entityName) data := BpjsHandlerData{ Name: entityName, NameLower: entityLower, NameUpper: entityUpper, Category: category, CategoryPath: category, ModuleName: "api-service", Timestamp: time.Now().Format("2006-01-02 15:04:05"), } // Set methods and endpoints for _, m := range methods { switch strings.ToLower(m) { case "get": data.HasGet = true data.GetEndpoint = fmt.Sprintf("%s/{id}", entityUpper) case "post": data.HasPost = true data.PostEndpoint = fmt.Sprintf("%s/2.0/insert", entityUpper) case "put": data.HasPut = true data.PutEndpoint = fmt.Sprintf("%s/2.0/update", entityUpper) case "delete": data.HasDelete = true data.DeleteEndpoint = fmt.Sprintf("%s/2.0/delete", entityUpper) } } // Create directories var handlerDir, modelDir string if category != "" { handlerDir = filepath.Join("internal", "handlers", category) modelDir = filepath.Join("internal", "models", category) } else { handlerDir = filepath.Join("internal", "handlers") modelDir = filepath.Join("internal", "models") } for _, d := range []string{handlerDir, modelDir} { if err := os.MkdirAll(d, 0755); err != nil { panic(err) } } // Generate files generateOptimizedBpjsHandlerFile(data, handlerDir) generateOptimizedBpjsModelFile(data, modelDir) updateOptimizedBpjsRoutesFile(data) fmt.Printf("✅ Successfully generated optimized BPJS handler: %s\n", entityName) if category != "" { fmt.Printf("📁 Category: %s\n", category) } fmt.Printf("📁 Handler: %s\n", filepath.Join(handlerDir, entityLower+".go")) fmt.Printf("📁 Model: %s\n", filepath.Join(modelDir, entityLower+".go")) } // ================= OPTIMIZED HANDLER GENERATION ===================== func generateOptimizedBpjsHandlerFile(data BpjsHandlerData, handlerDir string) { var modelsImportPath string if data.Category != "" { modelsImportPath = data.ModuleName + "/internal/models/" + data.Category } else { modelsImportPath = data.ModuleName + "/internal/models" } handlerContent := `package handlers import ( "context" "fmt" "net/http" "time" "` + data.ModuleName + `/internal/config" "` + modelsImportPath + `" services "` + data.ModuleName + `/internal/services/bpjs" "` + data.ModuleName + `/pkg/logger" "` + data.ModuleName + `/pkg/validator" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/go-playground/validator/v10" ) // ` + data.Name + `Handler handles ` + data.NameLower + ` BPJS services with optimized error handling and logging type ` + data.Name + `Handler struct { service services.VClaimService validator *validator.Validate logger logger.Logger config *config.BpjsConfig } // HandlerConfig contains configuration for ` + data.Name + `Handler type ` + data.Name + `HandlerConfig struct { BpjsConfig *config.BpjsConfig Logger logger.Logger Validator *validator.Validate } // New` + data.Name + `Handler creates a new optimized ` + data.Name + `Handler func New` + data.Name + `Handler(cfg *` + data.Name + `HandlerConfig) *` + data.Name + `Handler { return &` + data.Name + `Handler{ service: services.NewService(cfg.BpjsConfig), validator: cfg.Validator, logger: cfg.Logger, config: cfg.BpjsConfig, } }` // Add optimized methods based on flags if data.HasPost { handlerContent += generateOptimizedBpjsCreateMethod(data) } if data.HasPut { handlerContent += generateOptimizedBpjsUpdateMethod(data) } if data.HasDelete { handlerContent += generateOptimizedBpjsDeleteMethod(data) } if data.HasGet { handlerContent += generateOptimizedBpjsGetMethod(data) } // Add helper methods handlerContent += generateHelperMethods(data) writeFile(filepath.Join(handlerDir, data.NameLower+".go"), handlerContent) } func generateOptimizedBpjsCreateMethod(data BpjsHandlerData) string { var routePath, tagName string if data.Category != "" { routePath = data.Category + "/" + data.NameLower tagName = strings.Title(data.Category) + "-" + strings.Title(data.NameLower) } else { routePath = data.NameLower tagName = strings.Title(data.NameLower) } return ` // Create` + data.Name + ` creates a new ` + data.Name + ` with comprehensive error handling and validation // @Summary Create a new ` + data.NameUpper + ` // @Description Create a new ` + data.Name + ` in BPJS system with enhanced validation and logging // @Tags ` + tagName + ` // @Accept json // @Produce json // @Param request body models.` + data.Name + `PostRequest true "` + data.Name + ` creation request" // @Success 200 {object} models.` + data.Name + `Response "` + data.Name + ` created successfully" // @Failure 400 {object} models.` + data.Name + `Response "Bad request - validation error" // @Failure 422 {object} models.` + data.Name + `Response "Unprocessable entity - business logic error" // @Failure 500 {object} models.` + data.Name + `Response "Internal server error" // @Router /api/v1/` + routePath + ` [post] func (h *` + data.Name + `Handler) Create` + data.Name + `(c *gin.Context) { requestID := uuid.New().String() startTime := time.Now() h.logger.Info("Creating ` + data.Name + `", map[string]interface{}{ "request_id": requestID, "timestamp": startTime, }) var req models.` + data.Name + `PostRequest req.RequestID = requestID req.Timestamp = startTime // Bind and validate JSON if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind JSON", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) h.sendErrorResponse(c, http.StatusBadRequest, "INVALID_REQUEST_FORMAT", "Format request tidak valid", err.Error(), requestID) return } // Custom validation if err := req.Validate(); err != nil { h.logger.Error("Custom validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) h.sendErrorResponse(c, http.StatusBadRequest, "VALIDATION_ERROR", "Validasi gagal", err.Error(), requestID) return } // Struct validation if err := h.validator.Struct(&req); err != nil { h.logger.Error("Struct validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) h.sendErrorResponse(c, http.StatusBadRequest, "VALIDATION_ERROR", "Validasi struktur gagal", h.formatValidationError(err), requestID) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() var rawResponse models.BpjsRawResponse if err := h.service.Post(ctx, "` + data.PostEndpoint + `", req, &rawResponse); err != nil { h.logger.Error("Failed to call BPJS service", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "endpoint": "` + data.PostEndpoint + `", }) statusCode, errorCode := h.categorizeError(err) h.sendErrorResponse(c, statusCode, errorCode, "Gagal membuat ` + data.Name + `", err.Error(), requestID) return } // Check BPJS response if rawResponse.MetaData.Code != "200" { h.logger.Warn("BPJS returned error", map[string]interface{}{ "bpjs_code": rawResponse.MetaData.Code, "bpjs_message": rawResponse.MetaData.Message, "request_id": requestID, }) statusCode := h.mapBpjsCodeToHttpStatus(rawResponse.MetaData.Code) h.sendErrorResponse(c, statusCode, rawResponse.MetaData.Code, rawResponse.MetaData.Message, "", requestID) return } duration := time.Since(startTime) h.logger.Info("` + data.Name + ` created successfully", map[string]interface{}{ "request_id": requestID, "duration": duration.String(), }) h.sendSuccessResponse(c, "` + data.Name + ` berhasil dibuat", rawResponse.Response, requestID) }` } func generateOptimizedBpjsUpdateMethod(data BpjsHandlerData) string { var routePath, tagName string if data.Category != "" { routePath = data.Category + "/" + data.NameLower tagName = strings.Title(data.Category) + "-" + strings.Title(data.NameLower) } else { routePath = data.NameLower tagName = strings.Title(data.NameLower) } return ` // Update` + data.Name + ` updates an existing ` + data.Name + ` with comprehensive validation // @Summary Update an existing ` + data.NameUpper + ` // @Description Update an existing ` + data.Name + ` in BPJS system with enhanced validation and logging // @Tags ` + tagName + ` // @Accept json // @Produce json // @Param request body models.` + data.Name + `PutRequest true "` + data.Name + ` update request" // @Success 200 {object} models.` + data.Name + `Response "` + data.Name + ` updated successfully" // @Failure 400 {object} models.` + data.Name + `Response "Bad request - validation error" // @Failure 422 {object} models.` + data.Name + `Response "Unprocessable entity - business logic error" // @Failure 500 {object} models.` + data.Name + `Response "Internal server error" // @Router /api/v1/` + routePath + ` [put] func (h *` + data.Name + `Handler) Update` + data.Name + `(c *gin.Context) { requestID := uuid.New().String() startTime := time.Now() h.logger.Info("Updating ` + data.Name + `", map[string]interface{}{ "request_id": requestID, "timestamp": startTime, }) var req models.` + data.Name + `PutRequest req.RequestID = requestID req.Timestamp = startTime if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind JSON for update", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) h.sendErrorResponse(c, http.StatusBadRequest, "INVALID_REQUEST_FORMAT", "Format request tidak valid", err.Error(), requestID) return } if err := req.Validate(); err != nil { h.logger.Error("Update validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) h.sendErrorResponse(c, http.StatusBadRequest, "VALIDATION_ERROR", "Validasi gagal", err.Error(), requestID) return } if err := h.validator.Struct(&req); err != nil { h.logger.Error("Struct validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) h.sendErrorResponse(c, http.StatusBadRequest, "VALIDATION_ERROR", "Validasi struktur gagal", h.formatValidationError(err), requestID) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() var rawResponse models.BpjsRawResponse if err := h.service.Put(ctx, "` + data.PutEndpoint + `", req, &rawResponse); err != nil { h.logger.Error("Failed to update ` + data.Name + `", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) statusCode, errorCode := h.categorizeError(err) h.sendErrorResponse(c, statusCode, errorCode, "Gagal memperbarui ` + data.Name + `", err.Error(), requestID) return } if rawResponse.MetaData.Code != "200" { h.logger.Warn("BPJS update returned error", map[string]interface{}{ "bpjs_code": rawResponse.MetaData.Code, "bpjs_message": rawResponse.MetaData.Message, "request_id": requestID, }) statusCode := h.mapBpjsCodeToHttpStatus(rawResponse.MetaData.Code) h.sendErrorResponse(c, statusCode, rawResponse.MetaData.Code, rawResponse.MetaData.Message, "", requestID) return } duration := time.Since(startTime) h.logger.Info("` + data.Name + ` updated successfully", map[string]interface{}{ "request_id": requestID, "duration": duration.String(), }) h.sendSuccessResponse(c, "` + data.Name + ` berhasil diperbarui", rawResponse.Response, requestID) }` } func generateOptimizedBpjsDeleteMethod(data BpjsHandlerData) string { var routePath, tagName string if data.Category != "" { routePath = data.Category + "/" + data.NameLower tagName = strings.Title(data.Category) + "-" + strings.Title(data.NameLower) } else { routePath = data.NameLower tagName = strings.Title(data.NameLower) } return ` // Delete` + data.Name + ` deletes an existing ` + data.Name + ` with comprehensive validation // @Summary Delete an existing ` + data.NameUpper + ` // @Description Delete a ` + data.Name + ` by ID with enhanced validation and logging // @Tags ` + tagName + ` // @Accept json // @Produce json // @Param id path string true "` + data.Name + ` ID" // @Param user query string true "User identifier" // @Success 200 {object} models.` + data.Name + `Response "` + data.Name + ` deleted successfully" // @Failure 400 {object} models.` + data.Name + `Response "Bad request - missing parameters" // @Failure 422 {object} models.` + data.Name + `Response "Unprocessable entity - business logic error" // @Failure 500 {object} models.` + data.Name + `Response "Internal server error" // @Router /api/v1/` + routePath + `/{id} [delete] func (h *` + data.Name + `Handler) Delete` + data.Name + `(c *gin.Context) { requestID := uuid.New().String() startTime := time.Now() id := c.Param("id") user := c.Query("user") h.logger.Info("Deleting ` + data.Name + `", map[string]interface{}{ "request_id": requestID, "timestamp": startTime, "id": id, "user": user, }) // Validate parameters if id == "" { h.sendErrorResponse(c, http.StatusBadRequest, "MISSING_PARAMETER", "Parameter ID wajib diisi", "", requestID) return } if user == "" { h.sendErrorResponse(c, http.StatusBadRequest, "MISSING_PARAMETER", "Parameter user wajib diisi", "", requestID) return } req := models.` + data.Name + `DeleteRequest{ BaseRequest: models.BaseRequest{ RequestID: requestID, Timestamp: startTime, }, T` + data.Name + `: models.` + data.Name + `DeleteData{ ID: id, User: user, }, } if err := req.Validate(); err != nil { h.logger.Error("Delete validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) h.sendErrorResponse(c, http.StatusBadRequest, "VALIDATION_ERROR", "Validasi gagal", err.Error(), requestID) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() var rawResponse models.BpjsRawResponse if err := h.service.Delete(ctx, "` + data.DeleteEndpoint + `", req, &rawResponse); err != nil { h.logger.Error("Failed to delete ` + data.Name + `", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "id": id, }) statusCode, errorCode := h.categorizeError(err) h.sendErrorResponse(c, statusCode, errorCode, "Gagal menghapus ` + data.Name + `", err.Error(), requestID) return } if rawResponse.MetaData.Code != "200" { h.logger.Warn("BPJS delete returned error", map[string]interface{}{ "bpjs_code": rawResponse.MetaData.Code, "bpjs_message": rawResponse.MetaData.Message, "request_id": requestID, "id": id, }) statusCode := h.mapBpjsCodeToHttpStatus(rawResponse.MetaData.Code) h.sendErrorResponse(c, statusCode, rawResponse.MetaData.Code, rawResponse.MetaData.Message, "", requestID) return } duration := time.Since(startTime) h.logger.Info("` + data.Name + ` deleted successfully", map[string]interface{}{ "request_id": requestID, "duration": duration.String(), "id": id, }) h.sendSuccessResponse(c, "` + data.Name + ` berhasil dihapus", rawResponse.Response, requestID) }` } func generateOptimizedBpjsGetMethod(data BpjsHandlerData) string { var routePath, tagName string if data.Category != "" { routePath = data.Category + "/" + data.NameLower tagName = strings.Title(data.Category) + "-" + strings.Title(data.NameLower) } else { routePath = data.NameLower tagName = strings.Title(data.NameLower) } return ` // Get` + data.Name + ` retrieves ` + data.Name + ` details with comprehensive error handling // @Summary Get an existing ` + data.NameUpper + ` // @Description Retrieve a ` + data.Name + ` by ID with enhanced validation and logging // @Tags ` + tagName + ` // @Accept json // @Produce json // @Param id path string true "` + data.Name + ` ID" // @Success 200 {object} models.` + data.Name + `Response "Data ` + data.Name + ` retrieved successfully" // @Failure 400 {object} models.` + data.Name + `Response "Bad request - invalid ID" // @Failure 404 {object} models.` + data.Name + `Response "` + data.Name + ` not found" // @Failure 500 {object} models.` + data.Name + `Response "Internal server error" // @Router /api/v1/` + routePath + `/{id} [get] func (h *` + data.Name + `Handler) Get` + data.Name + `(c *gin.Context) { requestID := uuid.New().String() startTime := time.Now() id := c.Param("id") h.logger.Info("Getting ` + data.Name + `", map[string]interface{}{ "request_id": requestID, "timestamp": startTime, "id": id, }) if id == "" { h.sendErrorResponse(c, http.StatusBadRequest, "MISSING_PARAMETER", "Parameter ID wajib diisi", "", requestID) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() endpoint := fmt.Sprintf("` + strings.Replace(data.GetEndpoint, "{id}", "%s", 1) + `", id) var rawResponse models.BpjsRawResponse if err := h.service.Get(ctx, endpoint, &rawResponse); err != nil { h.logger.Error("Failed to get ` + data.Name + `", map[string]interface{}{ "error": err.Error(), "request_id": requestID, "id": id, }) statusCode, errorCode := h.categorizeError(err) h.sendErrorResponse(c, statusCode, errorCode, "Gagal mengambil data ` + data.Name + `", err.Error(), requestID) return } if rawResponse.MetaData.Code != "200" { // Handle specific BPJS error codes if rawResponse.MetaData.Code == "201" { h.logger.Info("` + data.Name + ` not found", map[string]interface{}{ "request_id": requestID, "id": id, }) h.sendErrorResponse(c, http.StatusNotFound, "DATA_NOT_FOUND", "Data ` + data.Name + ` tidak ditemukan", rawResponse.MetaData.Message, requestID) return } h.logger.Warn("BPJS get returned error", map[string]interface{}{ "bpjs_code": rawResponse.MetaData.Code, "bpjs_message": rawResponse.MetaData.Message, "request_id": requestID, "id": id, }) statusCode := h.mapBpjsCodeToHttpStatus(rawResponse.MetaData.Code) h.sendErrorResponse(c, statusCode, rawResponse.MetaData.Code, rawResponse.MetaData.Message, "", requestID) return } duration := time.Since(startTime) h.logger.Info("` + data.Name + ` retrieved successfully", map[string]interface{}{ "request_id": requestID, "duration": duration.String(), "id": id, }) h.sendSuccessResponse(c, "Data ` + data.Name + ` berhasil diambil", rawResponse.Response, requestID) }` } func generateHelperMethods(data BpjsHandlerData) string { return ` // Helper methods for ` + data.Name + `Handler func (h *` + data.Name + `Handler) sendSuccessResponse(c *gin.Context, message string, data interface{}, requestID string) { response := models.` + data.Name + `Response{ BaseResponse: models.BaseResponse{ Status: "success", Message: message, Data: data, Metadata: &models.ResponseMetadata{ Timestamp: time.Now(), Version: "2.0", RequestID: requestID, }, }, } c.JSON(http.StatusOK, response) } func (h *` + data.Name + `Handler) sendErrorResponse(c *gin.Context, statusCode int, errorCode, message, details, requestID string) { response := models.` + data.Name + `Response{ BaseResponse: models.BaseResponse{ Status: "error", Message: message, Error: &models.ErrorResponse{ Code: errorCode, Message: message, Details: details, }, Metadata: &models.ResponseMetadata{ Timestamp: time.Now(), Version: "2.0", RequestID: requestID, }, }, } c.JSON(statusCode, response) } func (h *` + data.Name + `Handler) formatValidationError(err error) string { if validationErrors, ok := err.(validator.ValidationErrors); ok { var messages []string for _, e := range validationErrors { switch e.Tag() { case "required": messages = append(messages, fmt.Sprintf("%s wajib diisi", e.Field())) case "min": messages = append(messages, fmt.Sprintf("%s minimal %s karakter", e.Field(), e.Param())) case "max": messages = append(messages, fmt.Sprintf("%s maksimal %s karakter", e.Field(), e.Param())) case "oneof": messages = append(messages, fmt.Sprintf("%s harus salah satu dari: %s", e.Field(), e.Param())) default: messages = append(messages, fmt.Sprintf("%s tidak valid", e.Field())) } } return fmt.Sprintf("Validasi gagal: %v", messages) } return err.Error() } func (h *` + data.Name + `Handler) categorizeError(err error) (int, string) { if err == nil { return http.StatusOK, "SUCCESS" } errStr := err.Error() if h.isTimeoutError(err) { return http.StatusRequestTimeout, "REQUEST_TIMEOUT" } if h.isNetworkError(err) { return http.StatusBadGateway, "NETWORK_ERROR" } if h.isAuthError(errStr) { return http.StatusUnauthorized, "AUTH_ERROR" } return http.StatusInternalServerError, "INTERNAL_ERROR" } func (h *` + data.Name + `Handler) mapBpjsCodeToHttpStatus(bpjsCode string) int { switch bpjsCode { case "200": return http.StatusOK case "201": return http.StatusNotFound case "202": return http.StatusBadRequest case "400": return http.StatusBadRequest case "401": return http.StatusUnauthorized case "403": return http.StatusForbidden case "404": return http.StatusNotFound case "500": return http.StatusInternalServerError default: return http.StatusUnprocessableEntity } } func (h *` + data.Name + `Handler) isTimeoutError(err error) bool { return err != nil && (err.Error() == "context deadline exceeded" || err.Error() == "timeout") } func (h *` + data.Name + `Handler) isNetworkError(err error) bool { return err != nil && (err.Error() == "connection refused" || err.Error() == "no such host") } func (h *` + data.Name + `Handler) isAuthError(errStr string) bool { return errStr == "unauthorized" || errStr == "invalid credentials" }` } // ================= OPTIMIZED MODEL GENERATION ===================== func generateOptimizedBpjsModelFile(data BpjsHandlerData, modelDir string) { modelContent := `package models import ( "encoding/json" "fmt" "time" ) // ` + data.Name + ` BPJS Models with Enhanced Validation // Generated at: ` + data.Timestamp + ` // Category: ` + data.Category + ` // Base request/response structures type BaseRequest struct { RequestID string ` + "`json:\"request_id,omitempty\"`" + ` Timestamp time.Time ` + "`json:\"timestamp,omitempty\"`" + ` } type BaseResponse struct { Status string ` + "`json:\"status\"`" + ` Message string ` + "`json:\"message\"`" + ` Data interface{} ` + "`json:\"data,omitempty\"`" + ` Error *ErrorResponse ` + "`json:\"error,omitempty\"`" + ` Metadata *ResponseMetadata ` + "`json:\"metadata,omitempty\"`" + ` } type ErrorResponse struct { Code string ` + "`json:\"code\"`" + ` Message string ` + "`json:\"message\"`" + ` Details string ` + "`json:\"details,omitempty\"`" + ` } type ResponseMetadata struct { Timestamp time.Time ` + "`json:\"timestamp\"`" + ` Version string ` + "`json:\"version\"`" + ` RequestID string ` + "`json:\"request_id,omitempty\"`" + ` } // ` + data.Name + ` Response Structure type ` + data.Name + `Response struct { BaseResponse } // BPJS Raw Response Structure type BpjsRawResponse struct { MetaData struct { Code string ` + "`json:\"code\"`" + ` Message string ` + "`json:\"message\"`" + ` } ` + "`json:\"metaData\"`" + ` Response interface{} ` + "`json:\"response\"`" + ` }` if data.HasPost { modelContent += ` // ` + data.Name + ` POST Request Structure with Enhanced Validation type ` + data.Name + `PostRequest struct { BaseRequest T` + data.Name + ` ` + data.Name + `Post ` + "`json:\"t` + data.NameLower + `\" binding:\"required\" validate:\"required\"`" + ` } type ` + data.Name + `Post struct { // Core BPJS fields - customize based on your specific requirements NoKartu string ` + "`json:\"noKartu\" binding:\"required\" validate:\"required,min=13,max=13\"`" + ` TglLayanan string ` + "`json:\"tglLayanan\" binding:\"required\" validate:\"required\"`" + ` JnsPelayanan string ` + "`json:\"jnsPelayanan\" binding:\"required\" validate:\"required,oneof=1 2\"`" + ` PpkPelayanan string ` + "`json:\"ppkPelayanan\" binding:\"required\" validate:\"required\"`" + ` Catatan string ` + "`json:\"catatan\" validate:\"omitempty,max=200\"`" + ` User string ` + "`json:\"user\" binding:\"required\" validate:\"required\"`" + ` } // Validate validates the ` + data.Name + `PostRequest func (r *` + data.Name + `PostRequest) Validate() error { if r.T` + data.Name + `.NoKartu == "" { return fmt.Errorf("nomor kartu tidak boleh kosong") } if len(r.T` + data.Name + `.NoKartu) != 13 { return fmt.Errorf("nomor kartu harus 13 digit") } if _, err := time.Parse("2006-01-02", r.T` + data.Name + `.TglLayanan); err != nil { return fmt.Errorf("format tanggal layanan tidak valid, gunakan yyyy-MM-dd") } return nil } // ToJSON converts struct to JSON string func (r *` + data.Name + `PostRequest) ToJSON() (string, error) { data, err := json.Marshal(r) return string(data), err }` } if data.HasPut { modelContent += ` // ` + data.Name + ` PUT Request Structure with Enhanced Validation type ` + data.Name + `PutRequest struct { BaseRequest T` + data.Name + ` ` + data.Name + `Put ` + "`json:\"t` + data.NameLower + `\" binding:\"required\" validate:\"required\"`" + ` } type ` + data.Name + `Put struct { ID string ` + "`json:\"id\" binding:\"required\" validate:\"required\"`" + ` NoKartu string ` + "`json:\"noKartu\" validate:\"omitempty,min=13,max=13\"`" + ` TglLayanan string ` + "`json:\"tglLayanan\" validate:\"omitempty\"`" + ` JnsPelayanan string ` + "`json:\"jnsPelayanan\" validate:\"omitempty,oneof=1 2\"`" + ` PpkPelayanan string ` + "`json:\"ppkPelayanan\" validate:\"omitempty\"`" + ` Catatan string ` + "`json:\"catatan\" validate:\"omitempty,max=200\"`" + ` User string ` + "`json:\"user\" binding:\"required\" validate:\"required\"`" + ` } // Validate validates the ` + data.Name + `PutRequest func (r *` + data.Name + `PutRequest) Validate() error { if r.T` + data.Name + `.ID == "" { return fmt.Errorf("ID tidak boleh kosong") } if r.T` + data.Name + `.NoKartu != "" && len(r.T` + data.Name + `.NoKartu) != 13 { return fmt.Errorf("nomor kartu harus 13 digit") } return nil } // ToJSON converts struct to JSON string func (r *` + data.Name + `PutRequest) ToJSON() (string, error) { data, err := json.Marshal(r) return string(data), err }` } if data.HasDelete { modelContent += ` // ` + data.Name + ` DELETE Request Structure with Enhanced Validation type ` + data.Name + `DeleteRequest struct { BaseRequest T` + data.Name + ` ` + data.Name + `DeleteData ` + "`json:\"t` + data.NameLower + `\" binding:\"required\" validate:\"required\"`" + ` } type ` + data.Name + `DeleteData struct { ID string ` + "`json:\"id\" binding:\"required\" validate:\"required\"`" + ` User string ` + "`json:\"user\" binding:\"required\" validate:\"required\"`" + ` } // Validate validates the ` + data.Name + `DeleteRequest func (r *` + data.Name + `DeleteRequest) Validate() error { if r.T` + data.Name + `.ID == "" { return fmt.Errorf("ID tidak boleh kosong") } if r.T` + data.Name + `.User == "" { return fmt.Errorf("User tidak boleh kosong") } return nil } // ToJSON converts struct to JSON string func (r *` + data.Name + `DeleteRequest) ToJSON() (string, error) { data, err := json.Marshal(r) return string(data), err }` } // Add common helper structures modelContent += ` // Common Helper Structures for BPJS type Flag struct { Flag string ` + "`json:\"flag\" binding:\"required\" validate:\"required,oneof=0 1\"`" + ` } type Poli struct { Tujuan string ` + "`json:\"tujuan\" binding:\"required\" validate:\"required\"`" + ` Eksekutif string ` + "`json:\"eksekutif\" binding:\"required\" validate:\"required,oneof=0 1\"`" + ` } type KlsRawat struct { KlsRawatHak string ` + "`json:\"klsRawatHak\" binding:\"required\" validate:\"required,oneof=1 2 3\"`" + ` KlsRawatNaik string ` + "`json:\"klsRawatNaik\" validate:\"omitempty,oneof=1 2 3 4 5 6 7\"`" + ` Pembiayaan string ` + "`json:\"pembiayaan\" validate:\"omitempty,oneof=1 2 3\"`" + ` PenanggungJawab string ` + "`json:\"penanggungJawab\" validate:\"omitempty,max=100\"`" + ` } // Validation helper functions func IsValidStatus(status string) bool { validStatuses := []string{"active", "inactive", "pending", "processed"} for _, v := range validStatuses { if v == status { return true } } return false } func IsValidJnsPelayanan(jns string) bool { return jns == "1" || jns == "2" // 1: rawat jalan, 2: rawat inap } func IsValidKlsRawat(kls string) bool { validKelas := []string{"1", "2", "3"} for _, v := range validKelas { if v == kls { return true } } return false }` writeFile(filepath.Join(modelDir, data.NameLower+".go"), modelContent) } // ================= OPTIMIZED ROUTES GENERATION ===================== func updateOptimizedBpjsRoutesFile(data BpjsHandlerData) { routesFile := "internal/routes/v1/routes.go" content, err := os.ReadFile(routesFile) if err != nil { fmt.Printf("⚠️ Could not read routes.go: %v\n", err) fmt.Printf("📝 Please manually add these optimized routes to your routes.go file:\n") printOptimizedBpjsRoutesSample(data) return } routesContent := string(content) var importPath, importAlias string if data.Category != "" { importPath = fmt.Sprintf("%s/internal/handlers/%s", data.ModuleName, data.Category) importAlias = data.NameLower + "Handlers" } else { importPath = fmt.Sprintf("%s/internal/handlers", data.ModuleName) importAlias = data.NameLower + "Handlers" } // Check and add import importPattern := fmt.Sprintf("%s \"%s\"", importAlias, importPath) if !strings.Contains(routesContent, importPattern) { importToAdd := fmt.Sprintf("\t%s \"%s\"", importAlias, importPath) if strings.Contains(routesContent, "import (") { routesContent = strings.Replace(routesContent, "import (", "import (\n"+importToAdd, 1) } } // Build optimized routes newRoutes := fmt.Sprintf("\t\t// Optimized %s endpoints with enhanced error handling\n", data.Name) if data.Category != "" { newRoutes = fmt.Sprintf("\t\t// Optimized %s %s endpoints with enhanced error handling\n", data.Category, data.Name) } newRoutes += fmt.Sprintf("\t\t%sHandlerConfig := &%s.%sHandlerConfig{\n", data.NameLower, importAlias, data.Name) newRoutes += fmt.Sprintf("\t\t\tBpjsConfig: &config.LoadConfig().Bpjs,\n") newRoutes += fmt.Sprintf("\t\t\tLogger: logger.GetLogger(),\n") newRoutes += fmt.Sprintf("\t\t\tValidator: validator.New(),\n") newRoutes += fmt.Sprintf("\t\t}\n") newRoutes += fmt.Sprintf("\t\t%sHandler := %s.New%sHandler(%sHandlerConfig)\n", data.NameLower, importAlias, data.Name, data.NameLower) var routePath string if data.Category != "" { routePath = data.Category + "/" + data.NameLower } else { routePath = data.NameLower } if data.HasGet { newRoutes += fmt.Sprintf("\t\tv1.GET(\"/%s/:id\", %sHandler.Get%s)\n", routePath, data.NameLower, data.Name) } if data.HasPost { newRoutes += fmt.Sprintf("\t\tv1.POST(\"/%s\", %sHandler.Create%s)\n", routePath, data.NameLower, data.Name) } if data.HasPut { newRoutes += fmt.Sprintf("\t\tv1.PUT(\"/%s\", %sHandler.Update%s)\n", routePath, data.NameLower, data.Name) } if data.HasDelete { newRoutes += fmt.Sprintf("\t\tv1.DELETE(\"/%s/:id\", %sHandler.Delete%s)\n", routePath, data.NameLower, data.Name) } newRoutes += "\n" // Insert routes insertMarker := "\t\tprotected := v1.Group(\"/\")" if strings.Contains(routesContent, insertMarker) { if !strings.Contains(routesContent, fmt.Sprintf("New%sHandler", data.Name)) { routesContent = strings.Replace(routesContent, insertMarker, newRoutes+insertMarker, 1) } else { fmt.Printf("✅ Optimized routes for %s already exist, skipping...\n", data.Name) return } } if err := os.WriteFile(routesFile, []byte(routesContent), 0644); err != nil { fmt.Printf("Error writing routes.go: %v\n", err) return } if data.Category != "" { fmt.Printf("✅ Updated routes.go with optimized %s %s endpoints\n", data.Category, data.Name) } else { fmt.Printf("✅ Updated routes.go with optimized %s endpoints\n", data.Name) } } func printOptimizedBpjsRoutesSample(data BpjsHandlerData) { var importAlias string if data.Category != "" { importAlias = data.NameLower + "Handlers" } else { importAlias = data.NameLower + "Handlers" } var routePath string if data.Category != "" { routePath = data.Category + "/" + data.NameLower } else { routePath = data.NameLower } if data.Category != "" { fmt.Printf(` // Optimized %s %s endpoints with enhanced error handling %sHandlerConfig := &%s.%sHandlerConfig{ BpjsConfig: &config.LoadConfig().Bpjs, Logger: logger.GetLogger(), Validator: validator.New(), } %sHandler := %s.New%sHandler(%sHandlerConfig) `, data.Category, data.Name, data.NameLower, importAlias, data.Name, data.NameLower, importAlias, data.Name, data.NameLower) } else { fmt.Printf(` // Optimized %s endpoints with enhanced error handling %sHandlerConfig := &%s.%sHandlerConfig{ BpjsConfig: &config.LoadConfig().Bpjs, Logger: logger.GetLogger(), Validator: validator.New(), } %sHandler := %s.New%sHandler(%sHandlerConfig) `, data.Name, data.NameLower, importAlias, data.Name, data.NameLower, importAlias, data.Name, data.NameLower) } if data.HasGet { fmt.Printf("\tv1.GET(\"/%s/:id\", %sHandler.Get%s)\n", routePath, data.NameLower, data.Name) } if data.HasPost { fmt.Printf("\tv1.POST(\"/%s\", %sHandler.Create%s)\n", routePath, data.NameLower, data.Name) } if data.HasPut { fmt.Printf("\tv1.PUT(\"/%s\", %sHandler.Update%s)\n", routePath, data.NameLower, data.Name) } if data.HasDelete { fmt.Printf("\tv1.DELETE(\"/%s/:id\", %sHandler.Delete%s)\n", routePath, data.NameLower, data.Name) } fmt.Println() } // ================= UTILITY FUNCTIONS ===================== func writeFile(filename, content string) { if err := os.WriteFile(filename, []byte(content), 0644); err != nil { fmt.Printf("❌ Error creating file %s: %v\n", filename, err) return } fmt.Printf("✅ Generated optimized file: %s\n", filename) }