package main import ( "fmt" "os" "path/filepath" "strings" "time" ) // BpjsHandlerData contains template data for BPJS handler generation type BpjsHandlerData struct { Name string NameLower string NamePlural string ModuleName string Category string HasGet bool HasGetByID bool HasPost bool HasPut bool HasDelete bool HasStats bool HasFilter bool Timestamp string Endpoints []BpjsEndpoint } // BpjsEndpoint represents a BPJS API endpoint configuration type BpjsEndpoint struct { Name string Method string Path string Description string Parameters []EndpointParam } // EndpointParam represents an endpoint parameter type EndpointParam struct { Name string Type string Required bool Description string Location string // "path", "query", "body" } func main() { if len(os.Args) < 2 { fmt.Println("Usage: go run generate-bpjs-handler.go [category/]entity [methods]") fmt.Println("Example: go run generate-bpjs-handler.go reference/peserta get getbyid") fmt.Println("Example: go run generate-bpjs-handler.go peserta get getbyid") os.Exit(1) } // Parse entity input (could be "entity" or "category/entity") entityInput := os.Args[1] var category, entityName string if strings.Contains(entityInput, "/") { parts := strings.Split(entityInput, "/") if len(parts) != 2 { fmt.Println("Invalid format. Use: category/entity or entity") os.Exit(1) } category = parts[0] entityName = strings.Title(parts[1]) // PascalCase entity name } else { category = "" entityName = strings.Title(entityInput) // PascalCase entity name } methods := []string{} if len(os.Args) > 2 { methods = os.Args[2:] } else { // Default methods if none specified methods = []string{"get", "getbyid"} } entityLower := strings.ToLower(entityName) entityPlural := entityLower + "s" data := BpjsHandlerData{ Name: entityName, NameLower: entityLower, NamePlural: entityPlural, ModuleName: "api-service", Category: category, Timestamp: time.Now().Format("2006-01-02 15:04:05"), } // Set methods based on arguments for _, m := range methods { switch strings.ToLower(m) { case "get": data.HasGet = true case "getbyid": data.HasGetByID = true case "post": data.HasPost = true case "put": data.HasPut = true case "delete": data.HasDelete = true case "stats": data.HasStats = true } } // Define default endpoints based on entity data.Endpoints = generateDefaultEndpoints(data) // Create directories based on category var handlerDir, modelDir string if category != "" { handlerDir = filepath.Join("internal", "handlers", "bpjs", category) modelDir = filepath.Join("internal", "models", "bpjs", category) } else { handlerDir = filepath.Join("internal", "handlers", "bpjs", entityLower) modelDir = filepath.Join("internal", "models", "bpjs", entityLower) } for _, d := range []string{handlerDir, modelDir} { if err := os.MkdirAll(d, 0755); err != nil { panic(err) } } // Generate files generateBpjsHandlerFile(data, handlerDir) generateBpjsModelFile(data, modelDir) updateBpjsRoutesFile(data) fmt.Printf("✅ Successfully generated 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")) } // generateDefaultEndpoints creates default endpoints based on entity type func generateDefaultEndpoints(data BpjsHandlerData) []BpjsEndpoint { endpoints := []BpjsEndpoint{} switch data.NameLower { case "peserta": endpoints = append(endpoints, BpjsEndpoint{ Name: "GetByNIK", Method: "GET", Path: "/Peserta/nik/%s/tglSEP/%s", Description: "Get participant data by NIK and service date", Parameters: []EndpointParam{ {Name: "nik", Type: "string", Required: true, Description: "NIK KTP", Location: "path"}, {Name: "tglSEP", Type: "string", Required: true, Description: "Service date (yyyy-MM-dd)", Location: "path"}, }, }) case "poli": endpoints = append(endpoints, BpjsEndpoint{ Name: "GetAll", Method: "GET", Path: "/referensi/poli", Description: "Get all poli reference data", Parameters: []EndpointParam{}, }) case "diagnosa": endpoints = append(endpoints, BpjsEndpoint{ Name: "GetAll", Method: "GET", Path: "/referensi/diagnosa", Description: "Get all diagnosa reference data", Parameters: []EndpointParam{}, }) case "provider": endpoints = append(endpoints, BpjsEndpoint{ Name: "GetByJenis", Method: "GET", Path: "/referensi/faskes/%s/%s", Description: "Get provider data by type and level", Parameters: []EndpointParam{ {Name: "jnsFaskes", Type: "string", Required: true, Description: "Jenis Faskes (1=Faskes 1, 2=Faskes 2)", Location: "path"}, {Name: "kdFaskes", Type: "string", Required: true, Description: "Kode Faskes", Location: "path"}, }, }) default: // Generic endpoint endpoints = append(endpoints, BpjsEndpoint{ Name: "GetAll", Method: "GET", Path: fmt.Sprintf("/referensi/%s", data.NameLower), Description: fmt.Sprintf("Get all %s reference data", data.NameLower), Parameters: []EndpointParam{}, }) } return endpoints } // ================= BPJS HANDLER GENERATION ===================== func generateBpjsHandlerFile(data BpjsHandlerData, handlerDir string) { // Determine import path based on category // var modelsImportPath string // if data.Category != "" { // modelsImportPath = `models "` + data.ModuleName + `/internal/models/bpjs/` + data.Category + `"` // } else { // modelsImportPath = `models "` + data.ModuleName + `/internal/models/bpjs/` + data.NameLower + `"` // } handlerContent := `package handlers import ( "context" "fmt" "net/http" "time" "` + data.ModuleName + `/internal/config" services "` + data.ModuleName + `/internal/services/bpjs" "github.com/gin-gonic/gin" ) // ` + data.Name + `Handler handles BPJS ` + data.NameLower + ` operations type ` + data.Name + `Handler struct { bpjsService services.VClaimService } // New` + data.Name + `Handler creates a new ` + data.Name + `Handler instance func New` + data.Name + `Handler(cfg config.BpjsConfig) *` + data.Name + `Handler { return &` + data.Name + `Handler{ bpjsService: services.NewService(cfg), } }` // Add methods based on endpoints for _, endpoint := range data.Endpoints { handlerContent += generateBpjsEndpointMethod(data, endpoint) } // Add helper methods handlerContent += generateBpjsHelperMethods(data) writeFile(filepath.Join(handlerDir, data.NameLower+".go"), handlerContent) } func generateBpjsEndpointMethod(data BpjsHandlerData, endpoint BpjsEndpoint) string { methodName := endpoint.Name // Generate parameter extraction paramExtraction := "" paramValidation := "" pathParams := []string{} for _, param := range endpoint.Parameters { if param.Location == "path" { paramExtraction += "\t" + param.Name + ` := c.Param("` + param.Name + `")` + "\n" pathParams = append(pathParams, param.Name) if param.Required { paramValidation += ` if ` + param.Name + ` == "" { c.JSON(http.StatusBadRequest, gin.H{ "error": "` + strings.Title(param.Name) + ` parameter is required", "message": "` + param.Description + ` tidak boleh kosong", }) return } ` } // Add date validation if parameter contains date if strings.Contains(param.Name, "tgl") || strings.Contains(param.Name, "date") { paramValidation += ` // Validate date format if _, err := time.Parse("2006-01-02", ` + param.Name + `); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid date format", "message": "Format tanggal harus yyyy-MM-dd", }) return } ` } } } // Generate endpoint URL building endpointURL := endpoint.Path if len(pathParams) > 0 { endpointURL = `fmt.Sprintf("` + endpoint.Path + `", ` + strings.Join(pathParams, ", ") + `)` } else { endpointURL = `"` + endpoint.Path + `"` } // Generate route path for documentation routePath := endpoint.Path for _, param := range endpoint.Parameters { if param.Location == "path" { routePath = strings.Replace(routePath, "%s", "{"+param.Name+"}", 1) } } // Generate category prefix for router documentation categoryPrefix := "" if data.Category != "" { categoryPrefix = "/" + data.Category } swaggerParams := generateSwaggerParams(endpoint.Parameters) return ` // ` + methodName + ` godoc // @Summary ` + endpoint.Description + ` // @Description ` + endpoint.Description + ` // @Tags bpjs` + categoryPrefix + ` // @Accept json // @Produce json` + swaggerParams + ` // @Success 200 {object} models.` + data.Name + `Response "Success response" // @Failure 400 {object} map[string]interface{} "Bad request" // @Failure 404 {object} map[string]interface{} "Data not found" // @Failure 500 {object} map[string]interface{} "Internal server error" // @Router /api/v1/bpjs` + categoryPrefix + routePath + ` [` + strings.ToLower(endpoint.Method) + `] func (h *` + data.Name + `Handler) ` + methodName + `(c *gin.Context) { ` + paramExtraction + paramValidation + ` // Create context with timeout ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() // Build endpoint URL endpoint := ` + endpointURL + ` // Call BPJS service var result map[string]interface{} if err := h.bpjsService.Get(ctx, endpoint, &result); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to fetch ` + data.NameLower + ` data", "message": err.Error(), }) return } // Return successful response c.JSON(http.StatusOK, gin.H{ "message": "Data ` + data.NameLower + ` berhasil diambil", "data": result, }) } ` } func generateSwaggerParams(params []EndpointParam) string { result := "" for _, param := range params { required := "false" if param.Required { required = "true" } result += ` // @Param ` + param.Name + ` ` + param.Location + ` ` + param.Type + ` ` + required + ` "` + param.Description + `"` } return result } func generateBpjsHelperMethods(data BpjsHandlerData) string { handlerName := data.Name return ` // Helper methods for error handling and response formatting // handleBPJSError handles BPJS service errors and returns appropriate HTTP responses func (h *` + handlerName + `Handler) handleBPJSError(c *gin.Context, err error, operation string) { c.JSON(http.StatusInternalServerError, gin.H{ "error": fmt.Sprintf("Failed to %s", operation), "message": err.Error(), }) } // validateDateFormat validates if the date string is in yyyy-MM-dd format func (h *` + handlerName + `Handler) validateDateFormat(dateStr string) error { _, err := time.Parse("2006-01-02", dateStr) return err } // buildSuccessResponse builds a standardized success response func (h *` + handlerName + `Handler) buildSuccessResponse(message string, data interface{}) gin.H { return gin.H{ "message": message, "data": data, } } ` } // ================= BPJS MODEL GENERATION ===================== func generateBpjsModelFile(data BpjsHandlerData, modelDir string) { modelContent := `package models // ` + data.Name + `Response represents the response structure for BPJS ` + data.NameLower + ` data type ` + data.Name + `Response struct { Message string ` + "`json:\"message\"`" + ` Data map[string]interface{} ` + "`json:\"data\"`" + ` } // ` + data.Name + `RawResponse represents the raw response structure from BPJS API type ` + data.Name + `RawResponse struct { MetaData struct { Code string ` + "`json:\"code\"`" + ` Message string ` + "`json:\"message\"`" + ` } ` + "`json:\"metaData\"`" + ` Response interface{} ` + "`json:\"response\"`" + ` } ` // Add entity-specific models switch data.NameLower { case "peserta": modelContent += generatePesertaModels() case "poli": modelContent += generatePoliModels() case "diagnosa": modelContent += generateDiagnosaModels() case "provider": modelContent += generateProviderModels() } // Add common request/response models modelContent += generateCommonBpjsModels(data) writeFile(filepath.Join(modelDir, data.NameLower+".go"), modelContent) } func generatePesertaModels() string { return ` // PesertaRequest represents the request structure for BPJS participant search type PesertaRequest struct { NIK string ` + "`json:\"nik\" binding:\"required\"`" + ` TglSEP string ` + "`json:\"tglSEP\" binding:\"required\"`" + ` } // PesertaData represents the participant data structure type PesertaData struct { NoKartu string ` + "`json:\"noKartu\"`" + ` NIK string ` + "`json:\"nik\"`" + ` Nama string ` + "`json:\"nama\"`" + ` Pisa string ` + "`json:\"pisa\"`" + ` Sex string ` + "`json:\"sex\"`" + ` TglLahir string ` + "`json:\"tglLahir\"`" + ` Pob string ` + "`json:\"pob\"`" + ` KdProvider string ` + "`json:\"kdProvider\"`" + ` NmProvider string ` + "`json:\"nmProvider\"`" + ` KelasRawat string ` + "`json:\"kelasRawat\"`" + ` Keterangan string ` + "`json:\"keterangan\"`" + ` NoTelepon string ` + "`json:\"noTelepon\"`" + ` Alamat string ` + "`json:\"alamat\"`" + ` KdPos string ` + "`json:\"kdPos\"`" + ` Pekerjaan string ` + "`json:\"pekerjaan\"`" + ` StatusKawin string ` + "`json:\"statusKawin\"`" + ` TglCetakKartu string ` + "`json:\"tglCetakKartu\"`" + ` TglTAT string ` + "`json:\"tglTAT\"`" + ` TglTMT string ` + "`json:\"tglTMT\"`" + ` ProvUmum struct { KdProvider string ` + "`json:\"kdProvider\"`" + ` NmProvider string ` + "`json:\"nmProvider\"`" + ` } ` + "`json:\"provUmum\"`" + ` JenisPeserta struct { KdJenisPeserta string ` + "`json:\"kdJenisPeserta\"`" + ` NmJenisPeserta string ` + "`json:\"nmJenisPeserta\"`" + ` } ` + "`json:\"jenisPeserta\"`" + ` KelasTanggungan struct { KdKelas string ` + "`json:\"kdKelas\"`" + ` NmKelas string ` + "`json:\"nmKelas\"`" + ` } ` + "`json:\"kelasTanggungan\"`" + ` Informasi struct { Dinsos string ` + "`json:\"dinsos\"`" + ` NoSKTM string ` + "`json:\"noSKTM\"`" + ` ProlanisPRB string ` + "`json:\"prolanisPRB\"`" + ` } ` + "`json:\"informasi\"`" + ` Cob struct { NoAsuransi string ` + "`json:\"noAsuransi\"`" + ` NmAsuransi string ` + "`json:\"nmAsuransi\"`" + ` TglTAT string ` + "`json:\"tglTAT\"`" + ` TglTMT string ` + "`json:\"tglTMT\"`" + ` } ` + "`json:\"cob\"`" + ` HakKelas struct { Kode string ` + "`json:\"kode\"`" + ` Nama string ` + "`json:\"nama\"`" + ` } ` + "`json:\"hakKelas\"`" + ` Mr struct { NoMR string ` + "`json:\"noMR\"`" + ` NoTelepon string ` + "`json:\"noTelepon\"`" + ` } ` + "`json:\"mr\"`" + ` ProvRujuk struct { KdProvider string ` + "`json:\"kdProvider\"`" + ` NmProvider string ` + "`json:\"nmProvider\"`" + ` } ` + "`json:\"provRujuk\"`" + ` StatusPeserta struct { Kode string ` + "`json:\"kode\"`" + ` Nama string ` + "`json:\"nama\"`" + ` } ` + "`json:\"statusPeserta\"`" + ` }` } func generatePoliModels() string { return ` // PoliData represents the poli reference data structure type PoliData struct { KdPoli string ` + "`json:\"kdPoli\"`" + ` NmPoli string ` + "`json:\"nmPoli\"`" + ` } // PoliListResponse represents the response structure for poli list type PoliListResponse struct { List []PoliData ` + "`json:\"list\"`" + ` }` } func generateDiagnosaModels() string { return ` // DiagnosaData represents the diagnosa reference data structure type DiagnosaData struct { KdDiag string ` + "`json:\"kdDiag\"`" + ` NmDiag string ` + "`json:\"nmDiag\"`" + ` } // DiagnosaListResponse represents the response structure for diagnosa list type DiagnosaListResponse struct { Diagnosa []DiagnosaData ` + "`json:\"diagnosa\"`" + ` }` } func generateProviderModels() string { return ` // ProviderData represents the provider reference data structure type ProviderData struct { KdProvider string ` + "`json:\"kdProvider\"`" + ` NmProvider string ` + "`json:\"nmProvider\"`" + ` JnsFaskes string ` + "`json:\"jnsFaskes\"`" + ` Alamat string ` + "`json:\"alamat\"`" + ` NoTelp string ` + "`json:\"noTelp\"`" + ` } // ProviderListResponse represents the response structure for provider list type ProviderListResponse struct { Faskes []ProviderData ` + "`json:\"faskes\"`" + ` }` } func generateCommonBpjsModels(data BpjsHandlerData) string { return ` // ErrorResponse represents error response structure type ErrorResponse struct { Error string ` + "`json:\"error\"`" + ` Message string ` + "`json:\"message\"`" + ` Code int ` + "`json:\"code,omitempty\"`" + ` } // BPJSMetaData represents BPJS API metadata structure type BPJSMetaData struct { Code string ` + "`json:\"code\"`" + ` Message string ` + "`json:\"message\"`" + ` } // ` + data.Name + `Filter represents filter parameters for ` + data.NameLower + ` queries type ` + data.Name + `Filter struct { NIK *string ` + "`form:\"nik\" json:\"nik,omitempty\"`" + ` TglSEP *string ` + "`form:\"tglSEP\" json:\"tglSEP,omitempty\"`" + ` } ` } // ================= ROUTES GENERATION ===================== func updateBpjsRoutesFile(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 routes to your routes.go file:\n") printBpjsRoutesSample(data) return } routesContent := string(content) // Determine import path based on category var importPath string if data.Category != "" { importPath = data.ModuleName + "/internal/handlers/bpjs/" + data.Category } else { importPath = data.ModuleName + "/internal/handlers/bpjs/" + data.NameLower } // Add import importPattern := `bpjs` + strings.Title(data.NameLower) + `Handlers "` + importPath + `"` if !strings.Contains(routesContent, importPattern) { importToAdd := "\t" + `bpjs` + strings.Title(data.NameLower) + `Handlers "` + importPath + `"` if strings.Contains(routesContent, "import (") { routesContent = strings.Replace(routesContent, "import (", "import (\n"+importToAdd, 1) } } // Generate routes newRoutes := "\t\t// BPJS " + data.Name + " endpoints\n" newRoutes += "\t\tbpjs" + strings.Title(data.NameLower) + "Handler := bpjs" + strings.Title(data.NameLower) + "Handlers.New" + data.Name + "Handler(cfg.Bpjs)\n" // Add routes based on endpoints for _, endpoint := range data.Endpoints { routePath := endpoint.Path for _, param := range endpoint.Parameters { if param.Location == "path" { routePath = strings.Replace(routePath, "%s", ":"+param.Name, 1) } } methodName := endpoint.Name routePrefix := "/bpjs" if data.Category != "" { routePrefix = "/bpjs/" + data.Category } newRoutes += "\t\tv1." + strings.ToUpper(endpoint.Method) + `("` + routePrefix + routePath + `", bpjs` + strings.Title(data.NameLower) + "Handler." + methodName + ")\n" } newRoutes += "\n" insertMarker := "\t\tprotected := v1.Group(\"/\")" if strings.Contains(routesContent, insertMarker) { if !strings.Contains(routesContent, "New"+data.Name+"Handler") { routesContent = strings.Replace(routesContent, insertMarker, newRoutes+insertMarker, 1) } else { fmt.Printf("✅ Routes for BPJS %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 } fmt.Printf("✅ Updated routes.go with BPJS %s endpoints\n", data.Name) } func printBpjsRoutesSample(data BpjsHandlerData) { fmt.Printf(` // BPJS %s endpoints bpjs%sHandler := bpjs%sHandlers.New%sHandler(cfg.Bpjs) `, data.Name, strings.Title(data.NameLower), strings.Title(data.NameLower), data.Name) for _, endpoint := range data.Endpoints { routePath := endpoint.Path for _, param := range endpoint.Parameters { if param.Location == "path" { routePath = strings.Replace(routePath, "%s", ":"+param.Name, 1) } } routePrefix := "/bpjs" if data.Category != "" { routePrefix = "/bpjs/" + data.Category } fmt.Printf("\tv1.%s(\"%s%s\", bpjs%sHandler.%s)\n", strings.ToUpper(endpoint.Method), routePrefix, routePath, strings.Title(data.NameLower), endpoint.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: %s\n", filename) }