package main import ( "context" "fmt" "log" "time" "github.com/gofrs/uuid/v5" _ "github.com/jackc/pgx/v5/stdlib" "github.com/jmoiron/sqlx" "golang.org/x/crypto/bcrypt" "github.com/saveinmed/backend-go/internal/config" "github.com/saveinmed/backend-go/internal/domain" ) func main() { cfg := config.Load() db, err := sqlx.Open("pgx", cfg.DatabaseURL) if err != nil { log.Fatalf("Failed to connect to DB: %v", err) } defer db.Close() if err := db.Ping(); err != nil { log.Fatalf("Failed to ping DB: %v", err) } ctx := context.Background() log.Println("🧹 Cleaning database...") cleanDB(ctx, db) log.Println("🌱 Seeding data...") seedData(ctx, db, cfg) log.Println("✅ Seeding complete!") } func cleanDB(ctx context.Context, db *sqlx.DB) { tables := []string{ "reviews", "shipments", "payment_preferences", "orders", "order_items", "cart_items", "products", "companies", "users", "shipping_settings", } for _, table := range tables { _, err := db.ExecContext(ctx, fmt.Sprintf("TRUNCATE TABLE %s CASCADE", table)) if err != nil { // Ignore error if table doesn't exist or is empty log.Printf("Warning cleaning %s: %v", table, err) } } } func seedData(ctx context.Context, db *sqlx.DB, cfg config.Config) { // 1. Seed Admin adminCompanyID := uuid.Must(uuid.NewV7()) createCompany(ctx, db, &domain.Company{ ID: adminCompanyID, CNPJ: "00000000000000", CorporateName: "SaveInMed Admin", Category: "admin", LicenseNumber: "ADMIN", IsVerified: true, }) createUser(ctx, db, &domain.User{ CompanyID: adminCompanyID, Role: "Admin", Name: cfg.AdminName, Username: cfg.AdminUsername, Email: cfg.AdminEmail, }, cfg.AdminPassword, cfg.PasswordPepper) // 2. Distributors (Sellers) distributor1ID := uuid.Must(uuid.NewV7()) createCompany(ctx, db, &domain.Company{ ID: distributor1ID, CNPJ: "11111111000111", CorporateName: "Distribuidora Nacional", Category: "distribuidora", LicenseNumber: "DIST-001", IsVerified: true, Latitude: -23.55052, Longitude: -46.633308, }) createUser(ctx, db, &domain.User{ CompanyID: distributor1ID, Role: "Owner", Name: "Dono da Distribuidora", Username: "distribuidora", Email: "distribuidora@saveinmed.com", }, "123456", cfg.PasswordPepper) createShippingSettings(ctx, db, distributor1ID) // 3. Pharmacies (Buyers) pharmacy1ID := uuid.Must(uuid.NewV7()) createCompany(ctx, db, &domain.Company{ ID: pharmacy1ID, CNPJ: "22222222000122", CorporateName: "Farmácia Central", Category: "farmacia", LicenseNumber: "FARM-001", IsVerified: true, Latitude: -23.56052, Longitude: -46.643308, }) createUser(ctx, db, &domain.User{ CompanyID: pharmacy1ID, Role: "Owner", Name: "Dono da Farmácia", Username: "farmacia", Email: "farmacia@saveinmed.com", }, "123456", cfg.PasswordPepper) // 4. Products products := []struct { Name string Price int64 Stock int64 }{ {"Dipirona 500mg", 500, 1000}, {"Paracetamol 750mg", 750, 1000}, {"Ibuprofeno 600mg", 1200, 500}, {"Amoxicilina 500mg", 2500, 300}, {"Omeprazol 20mg", 1500, 800}, } var productIDs []uuid.UUID for _, p := range products { id := uuid.Must(uuid.NewV7()) expiry := time.Now().AddDate(1, 0, 0) createProduct(ctx, db, &domain.Product{ ID: id, SellerID: distributor1ID, Name: p.Name, Description: "Medicamento genérico de alta qualidade", Batch: "BATCH-" + id.String()[:8], ExpiresAt: expiry, PriceCents: p.Price, Stock: p.Stock, }) productIDs = append(productIDs, id) } // 5. Orders // Create an order from Pharmacy to Distributor orderID := uuid.Must(uuid.NewV7()) totalCents := int64(0) // Items qty := int64(10) price := products[0].Price itemTotal := price * qty totalCents += itemTotal createOrder(ctx, db, &domain.Order{ ID: orderID, BuyerID: pharmacy1ID, SellerID: distributor1ID, Status: "Faturado", // Ready for "Confirmar Entrega" test TotalCents: totalCents, CreatedAt: time.Now().AddDate(0, 0, -2), UpdatedAt: time.Now(), }) createOrderItem(ctx, db, &domain.OrderItem{ ID: uuid.Must(uuid.NewV7()), OrderID: orderID, ProductID: productIDs[0], Quantity: qty, UnitCents: price, Batch: "BATCH-" + productIDs[0].String()[:8], ExpiresAt: time.Now().AddDate(1, 0, 0), }) } func createCompany(ctx context.Context, db *sqlx.DB, c *domain.Company) { now := time.Now().UTC() c.CreatedAt = now c.UpdatedAt = now _, err := db.NamedExecContext(ctx, ` INSERT INTO companies (id, cnpj, corporate_name, category, license_number, is_verified, latitude, longitude, created_at, updated_at) VALUES (:id, :cnpj, :corporate_name, :category, :license_number, :is_verified, :latitude, :longitude, :created_at, :updated_at) `, c) if err != nil { log.Printf("Error creating company %s: %v", c.CorporateName, err) } } func createUser(ctx context.Context, db *sqlx.DB, u *domain.User, password, pepper string) { hashed, _ := bcrypt.GenerateFromPassword([]byte(password+pepper), bcrypt.DefaultCost) u.ID = uuid.Must(uuid.NewV7()) u.PasswordHash = string(hashed) u.CreatedAt = time.Now().UTC() u.UpdatedAt = time.Now().UTC() // Ensure email/username uniqueness is handled by DB constraint, usually we just insert _, err := db.NamedExecContext(ctx, ` INSERT INTO users (id, company_id, role, name, username, email, password_hash, email_verified, created_at, updated_at) VALUES (:id, :company_id, :role, :name, :username, :email, :password_hash, :email_verified, :created_at, :updated_at) `, u) if err != nil { log.Printf("Error creating user %s: %v", u.Username, err) } } func createProduct(ctx context.Context, db *sqlx.DB, p *domain.Product) { now := time.Now().UTC() p.CreatedAt = now p.UpdatedAt = now _, 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) `, p) if err != nil { log.Printf("Error creating product %s: %v", p.Name, err) } } func createShippingSettings(ctx context.Context, db *sqlx.DB, vendorID uuid.UUID) { now := time.Now().UTC() settings := &domain.ShippingSettings{ VendorID: vendorID, Active: true, MaxRadiusKm: 50, PricePerKmCents: 150, // R$ 1.50 MinFeeCents: 1000, // R$ 10.00 FreeShippingThresholdCents: nil, PickupActive: true, PickupAddress: "Rua da Distribuidora, 1000", PickupHours: "Seg-Sex 08-18h", CreatedAt: now, UpdatedAt: now, Latitude: -23.55052, Longitude: -46.633308, } _, err := db.NamedExecContext(ctx, ` INSERT INTO shipping_settings ( vendor_id, active, max_radius_km, price_per_km_cents, min_fee_cents, free_shipping_threshold_cents, pickup_active, pickup_address, pickup_hours, latitude, longitude, created_at, updated_at ) VALUES ( :vendor_id, :active, :max_radius_km, :price_per_km_cents, :min_fee_cents, :free_shipping_threshold_cents, :pickup_active, :pickup_address, :pickup_hours, :latitude, :longitude, :created_at, :updated_at ) `, settings) if err != nil { log.Printf("Error creating shipping settings: %v", err) } } func createOrder(ctx context.Context, db *sqlx.DB, o *domain.Order) { _, err := db.NamedExecContext(ctx, ` INSERT INTO orders (id, buyer_id, seller_id, status, total_cents, created_at, updated_at) VALUES (:id, :buyer_id, :seller_id, :status, :total_cents, :created_at, :updated_at) `, o) if err != nil { log.Printf("Error creating order: %v", err) } } func createOrderItem(ctx context.Context, db *sqlx.DB, item *domain.OrderItem) { _, err := db.NamedExecContext(ctx, ` INSERT INTO order_items (id, order_id, product_id, quantity, unit_cents, batch, expires_at) VALUES (:id, :order_id, :product_id, :quantity, :unit_cents, :batch, :expires_at) `, item) if err != nil { log.Printf("Error creating order item: %v", err) } }