package main import ( "context" "encoding/json" "fmt" "log" "math/rand" "os" "time" "github.com/gofrs/uuid/v5" _ "github.com/jackc/pgx/v5/stdlib" "github.com/jmoiron/sqlx" "github.com/joho/godotenv" ) // Anápolis, GO coordinates const ( AnapolisLat = -16.3281 AnapolisLng = -48.9530 VarianceKm = 5.0 // ~5km spread ) // Product categories with sample medicines var categories = []string{ "Analgésicos", "Antibióticos", "Anti-inflamatórios", "Cardiovasculares", "Dermatológicos", "Vitaminas", "Oftálmicos", "Respiratórios", } var medicamentos = []struct { Name string Category string BasePrice int64 }{ {"Dipirona 500mg", "Analgésicos", 1299}, {"Paracetamol 750mg", "Analgésicos", 899}, {"Ibuprofeno 400mg", "Anti-inflamatórios", 1599}, {"Nimesulida 100mg", "Anti-inflamatórios", 2499}, {"Amoxicilina 500mg", "Antibióticos", 3499}, {"Azitromicina 500mg", "Antibióticos", 4999}, {"Losartana 50mg", "Cardiovasculares", 2199}, {"Atenolol 50mg", "Cardiovasculares", 1899}, {"Vitamina C 1g", "Vitaminas", 1599}, {"Vitamina D 2000UI", "Vitaminas", 2299}, {"Cetoconazol Creme", "Dermatológicos", 2899}, {"Dexametasona Creme", "Dermatológicos", 1999}, {"Colírio Lubrificante", "Oftálmicos", 3299}, {"Lacrifilm 15ml", "Oftálmicos", 2899}, {"Loratadina 10mg", "Respiratórios", 1799}, {"Dexclorfeniramina 2mg", "Respiratórios", 1599}, {"Omeprazol 20mg", "Gastrointestinais", 1999}, {"Pantoprazol 40mg", "Gastrointestinais", 2999}, {"Metformina 850mg", "Antidiabéticos", 1299}, {"Glibenclamida 5mg", "Antidiabéticos", 999}, } var pharmacyNames = []string{ "Farmácia Popular", "Drogaria Central", "Farma Vida", "Drogasil", "Farmácia São João", "Droga Raia", "Ultrafarma", "Farma Bem", "Drogaria Moderna", "Farmácia União", "Saúde Total", "Bem Estar", "Vida Saudável", "Mais Saúde", "Farmácia do Povo", "Super Farma", } func main() { godotenv.Load() dsn := os.Getenv("DATABASE_URL") if dsn == "" { log.Fatal("DATABASE_URL not set") } db, err := sqlx.Connect("pgx", dsn) if err != nil { log.Fatalf("db connect: %v", err) } defer db.Close() ctx := context.Background() rng := rand.New(rand.NewSource(time.Now().UnixNano())) // Generate 400 tenants tenants := generateTenants(rng, 400) log.Printf("Generated %d tenants", len(tenants)) // Insert tenants for _, t := range tenants { _, err := db.NamedExecContext(ctx, ` INSERT INTO companies (id, cnpj, corporate_name, category, license_number, is_verified, latitude, longitude, city, state, created_at, updated_at) VALUES (:id, :cnpj, :corporate_name, :category, :license_number, :is_verified, :latitude, :longitude, :city, :state, :created_at, :updated_at) ON CONFLICT (cnpj) DO NOTHING`, t) if err != nil { log.Printf("insert tenant %s: %v", t["corporate_name"], err) } } log.Println("Tenants inserted") // Generate and insert products for each tenant totalProducts := 0 for _, t := range tenants { tenantID := t["id"].(uuid.UUID) numProducts := 20 + rng.Intn(481) // 20-500 products := generateProducts(rng, tenantID, numProducts) totalProducts += len(products) for _, p := range products { _, err := db.NamedExecContext(ctx, ` INSERT INTO products (id, seller_id, name, description, batch, expires_at, price_cents, stock, created_at, updated_at) VALUES (:id, :seller_id, :name, :description, :batch, :expires_at, :price_cents, :stock, :created_at, :updated_at) ON CONFLICT DO NOTHING`, p) if err != nil { log.Printf("insert product: %v", err) } } } log.Printf("Inserted %d products total", totalProducts) // Output summary summary := map[string]interface{}{ "tenants": len(tenants), "products": totalProducts, "location": "Anápolis, GO", } out, _ := json.MarshalIndent(summary, "", " ") fmt.Println(string(out)) } func generateTenants(rng *rand.Rand, count int) []map[string]interface{} { now := time.Now().UTC() tenants := make([]map[string]interface{}, 0, count) for i := 0; i < count; i++ { id, _ := uuid.NewV4() name := fmt.Sprintf("%s %d", pharmacyNames[rng.Intn(len(pharmacyNames))], i+1) cnpj := generateCNPJ(rng) // Vary lat/lng within ~5km of Anápolis center latOffset := (rng.Float64() - 0.5) * 0.09 // ~5km lngOffset := (rng.Float64() - 0.5) * 0.09 tenants = append(tenants, map[string]interface{}{ "id": id, "cnpj": cnpj, "corporate_name": name, "category": "farmacia", "license_number": fmt.Sprintf("CRF-GO-%05d", rng.Intn(99999)), "is_verified": rng.Float32() > 0.3, // 70% verified "latitude": AnapolisLat + latOffset, "longitude": AnapolisLng + lngOffset, "city": "Anápolis", "state": "GO", "created_at": now, "updated_at": now, }) } return tenants } func generateProducts(rng *rand.Rand, sellerID uuid.UUID, count int) []map[string]interface{} { now := time.Now().UTC() products := make([]map[string]interface{}, 0, count) for i := 0; i < count; i++ { id, _ := uuid.NewV4() med := medicamentos[rng.Intn(len(medicamentos))] // Random expiration: 30 days to 2 years from now daysToExpire := 30 + rng.Intn(700) expiresAt := now.AddDate(0, 0, daysToExpire) // Price variation: -20% to +30% priceMultiplier := 0.8 + rng.Float64()*0.5 price := int64(float64(med.BasePrice) * priceMultiplier) products = append(products, map[string]interface{}{ "id": id, "seller_id": sellerID, "name": med.Name, "description": fmt.Sprintf("%s - %s", med.Name, med.Category), "batch": fmt.Sprintf("L%d%03d", now.Year(), rng.Intn(999)), "expires_at": expiresAt, "price_cents": price, "stock": int64(10 + rng.Intn(500)), "created_at": now, "updated_at": now, }) } return products } func generateCNPJ(rng *rand.Rand) string { return fmt.Sprintf("%02d.%03d.%03d/%04d-%02d", rng.Intn(99), rng.Intn(999), rng.Intn(999), rng.Intn(9999)+1, rng.Intn(99)) }