saveinmed/backend/cmd/seeder/main.go

276 lines
8.2 KiB
Go

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)
}
}