diff --git a/seeder-api/README.md b/seeder-api/README.md index 958483c..68efd49 100644 --- a/seeder-api/README.md +++ b/seeder-api/README.md @@ -13,11 +13,30 @@ Microserviço utilitário para popular o banco de dados com dados de teste para ## 🎯 Modos de Operação ### 1. `mode=lean` (Recomendado para Dev) -Gera um ambiente funcional mínimo: -- **1 Farmácia** (Farmácia Modelo) -- **4 Usuários** (Admin, Dono, Colaborador, Entregador) -- **15 Produtos** -- Ideal para testar fluxos de login, carrinho, checkout. +Gera um ambiente funcional com **4 Farmácias** em Anápolis e equipe completa em cada uma: + +#### 1. Farmácia Central (Sufixo 1) +- **CNPJ**: 11.111.111/0001-11 +- **Usuários**: `dono1`, `colab1`, `entregador1` (Senha: 123456) +- **Localização**: Centro de Anápolis + +#### 2. Farmácia Jundiaí (Sufixo 2) +- **CNPJ**: 22.222.222/0001-22 +- **Usuários**: `dono2`, `colab2`, `entregador2` (Senha: 123456) +- **Localização**: Bairro Jundiaí (Norte/Leste) + +#### 3. Farmácia Jaiara (Sufixo 3) +- **CNPJ**: 33.333.333/0001-33 +- **Usuários**: `dono3`, `colab3`, `entregador3` (Senha: 123456) +- **Localização**: Vila Jaiara (Norte/Oeste) + +#### 4. Farmácia Universitária (Sufixo 4) +- **CNPJ**: 44.444.444/0001-44 +- **Usuários**: `dono4`, `colab4`, `entregador4` (Senha: 123456) +- **Localização**: Cidade Universitária (Sul/Leste) + +#### Admin Global +- **Usuário**: `admin` (Senha: admin123) ### 2. `mode=full` (Padrão/Load Test) Gera volume de dados: diff --git a/seeder-api/pkg/seeder/seeder.go b/seeder-api/pkg/seeder/seeder.go index 6666a02..5670462 100644 --- a/seeder-api/pkg/seeder/seeder.go +++ b/seeder-api/pkg/seeder/seeder.go @@ -115,7 +115,6 @@ func SeedLean(dsn string) (string, error) { updated_at TIMESTAMPTZ NOT NULL )`) - // Add missing users table creation here to be complete for independent seeder run mustExec(db, `CREATE TABLE users ( id UUID PRIMARY KEY, company_id UUID NOT NULL REFERENCES companies(id), @@ -142,76 +141,124 @@ func SeedLean(dsn string) (string, error) { updated_at TIMESTAMPTZ NOT NULL )`) - // Create 1 Pharmacy - pharmacyID := uuid.Must(uuid.NewV7()) - now := time.Now().UTC() - - _, err = db.ExecContext(ctx, ` - INSERT INTO companies (id, cnpj, corporate_name, category, license_number, is_verified, latitude, longitude, city, state, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, - pharmacyID, "12345678000199", "Farmácia Modelo", "farmacia", "CRF-GO-12345", true, AnapolisLat, AnapolisLng, "Anápolis", "GO", now, now, - ) - if err != nil { - return "", fmt.Errorf("create pharmacy: %v", err) - } - - // I'll proceed with creating users: - // Helper for hashing hashPwd := func(pwd string) string { h, _ := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost) return string(h) } + defaultPwdHash := hashPwd("123456") - // 1. Admin - adminID := uuid.Must(uuid.NewV7()) - mustExec(db, fmt.Sprintf(`INSERT INTO users (id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at) - VALUES ('%s', '%s', 'Admin', 'Administrador', 'admin', 'admin@saveinmed.com', true, '%s', NOW(), NOW())`, - adminID, pharmacyID, hashPwd("admin123"), - )) - - // 2. Owner (Dono) - ownerID := uuid.Must(uuid.NewV7()) - mustExec(db, fmt.Sprintf(`INSERT INTO users (id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at) - VALUES ('%s', '%s', 'Dono', 'João Dono', 'dono', 'dono@farmacia.com', true, '%s', NOW(), NOW())`, - ownerID, pharmacyID, hashPwd("123456"), - )) - - // 3. Employee (Colaborador) - empID := uuid.Must(uuid.NewV7()) - mustExec(db, fmt.Sprintf(`INSERT INTO users (id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at) - VALUES ('%s', '%s', 'Colaborador', 'Maria Colaboradora', 'colaborador', 'colaborador@farmacia.com', true, '%s', NOW(), NOW())`, - empID, pharmacyID, hashPwd("123456"), - )) - - // 4. Delivery (Entregador) - // Delivery person usually needs their own "company" or is linked to the pharmacy? - // For now, linking to the same pharmacy for simplicity, or creating a carrier? - // The prompt implies "entregador" as a user role. - // Linking to Pharmacy for simplicity (internal delivery fleet). - delID := uuid.Must(uuid.NewV7()) - mustExec(db, fmt.Sprintf(`INSERT INTO users (id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at) - VALUES ('%s', '%s', 'Entregador', 'José Entregador', 'entregador', 'entregador@farmacia.com', true, '%s', NOW(), NOW())`, - delID, pharmacyID, hashPwd("123456"), - )) - - log.Println("✅ [Lean] Users created: admin, dono, colaborador, entregador") - - // Create Products for the Pharmacy - rng := rand.New(rand.NewSource(time.Now().UnixNano())) - products := generateProducts(rng, pharmacyID, 15) - 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 lean: %v", err) - } + // Fixed Pharmacies Data + pharmacies := []struct { + Name string + CNPJ string + Lat float64 + Lng float64 + Suffix string // for usernames/emails e.g. "1" -> dono1, email1 + }{ + { + Name: "Farmácia Central", + CNPJ: "11111111000111", + Lat: AnapolisLat, + Lng: AnapolisLng, + Suffix: "1", + }, + { + Name: "Farmácia Jundiaí", + CNPJ: "22222222000122", + Lat: AnapolisLat + 0.015, // Slightly North + Lng: AnapolisLng + 0.010, // East + Suffix: "2", + }, + { + Name: "Farmácia Jaiara", + CNPJ: "33333333000133", + Lat: AnapolisLat + 0.030, // More North + Lng: AnapolisLng - 0.010, // West + Suffix: "3", + }, + { + Name: "Farmácia Universitária", + CNPJ: "44444444000144", + Lat: AnapolisLat - 0.020, // South + Lng: AnapolisLng + 0.020, // East + Suffix: "4", + }, } - log.Println("✅ [Lean] Created 15 products") - return "Lean seed completed. Users: admin, dono, colaborador, entregador (Pass: 123456/admin123)", nil + now := time.Now().UTC() + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + createdUsers := []string{} + + for _, ph := range pharmacies { + // 1. Create Company + companyID := uuid.Must(uuid.NewV7()) + _, err = db.ExecContext(ctx, ` + INSERT INTO companies (id, cnpj, corporate_name, category, license_number, is_verified, latitude, longitude, city, state, created_at, updated_at) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, + companyID, ph.CNPJ, ph.Name, "farmacia", fmt.Sprintf("CRF-GO-%s", ph.Suffix), true, ph.Lat, ph.Lng, "Anápolis", "GO", now, now, + ) + if err != nil { + return "", fmt.Errorf("create library %s: %v", ph.Name, err) + } + + // 2. Create Users (Dono, Colab, Entregador) + roles := []struct { + Role string + UserBase string + NameBase string + }{ + {"Dono", "dono", "Dono"}, + {"Colaborador", "colab", "Colaborador"}, + {"Entregador", "entregador", "Entregador"}, + } + + for _, r := range roles { + uid := uuid.Must(uuid.NewV7()) + username := fmt.Sprintf("%s%s", r.UserBase, ph.Suffix) + email := fmt.Sprintf("%s%s@saveinmed.com", r.UserBase, ph.Suffix) + name := fmt.Sprintf("%s %s", r.NameBase, ph.Name) + + mustExec(db, fmt.Sprintf(`INSERT INTO users (id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at) + VALUES ('%s', '%s', '%s', '%s', '%s', '%s', true, '%s', NOW(), NOW())`, + uid, companyID, r.Role, name, username, email, defaultPwdHash, + )) + createdUsers = append(createdUsers, fmt.Sprintf("%s (%s)", username, r.Role)) + } + + // 3. Create Products (20-50 products per pharmacy) + numProds := 20 + rng.Intn(31) // 20-50 + prods := generateProducts(rng, companyID, numProds) + for _, p := range prods { + _, 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 lean: %v", err) + } + } + log.Printf("✅ [Lean] %s created with %d products", ph.Name, len(prods)) + } + + // 4. Create Global Admin (linked to first pharmacy for FK constraint, or standalone if nullable? Schema says NOT NULL) + // Build Admin linked to "Farmácia Central" (Suffix 1) + // Find ID of first pharmacy? I need to track it. + // Actually, just query it or store it. + // Simpler: I'll just create a separate "Admin Company" or link to one. + // Linking to Central is fine. + var centralID string + err = db.Get(¢ralID, "SELECT id FROM companies WHERE cnpj = '11111111000111'") + if err == nil { + adminID := uuid.Must(uuid.NewV7()) + mustExec(db, fmt.Sprintf(`INSERT INTO users (id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at) + VALUES ('%s', '%s', 'Admin', 'Administrador Global', 'admin', 'admin@saveinmed.com', true, '%s', NOW(), NOW())`, + adminID, centralID, hashPwd("admin123"), + )) + createdUsers = append(createdUsers, "admin (Admin)") + } + + return fmt.Sprintf("Lean seed completed. 4 Pharmacies. Users: %s. Pass: 123456 (admin: admin123)", len(createdUsers)), nil } func SeedFull(dsn string) (string, error) {