feat(seeder): implemented 4 pharmacies with staff in lean mode
This commit is contained in:
parent
d44a8873b9
commit
ebfc72969c
2 changed files with 134 additions and 68 deletions
|
|
@ -13,11 +13,30 @@ Microserviço utilitário para popular o banco de dados com dados de teste para
|
||||||
## 🎯 Modos de Operação
|
## 🎯 Modos de Operação
|
||||||
|
|
||||||
### 1. `mode=lean` (Recomendado para Dev)
|
### 1. `mode=lean` (Recomendado para Dev)
|
||||||
Gera um ambiente funcional mínimo:
|
Gera um ambiente funcional com **4 Farmácias** em Anápolis e equipe completa em cada uma:
|
||||||
- **1 Farmácia** (Farmácia Modelo)
|
|
||||||
- **4 Usuários** (Admin, Dono, Colaborador, Entregador)
|
#### 1. Farmácia Central (Sufixo 1)
|
||||||
- **15 Produtos**
|
- **CNPJ**: 11.111.111/0001-11
|
||||||
- Ideal para testar fluxos de login, carrinho, checkout.
|
- **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)
|
### 2. `mode=full` (Padrão/Load Test)
|
||||||
Gera volume de dados:
|
Gera volume de dados:
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,6 @@ func SeedLean(dsn string) (string, error) {
|
||||||
updated_at TIMESTAMPTZ NOT NULL
|
updated_at TIMESTAMPTZ NOT NULL
|
||||||
)`)
|
)`)
|
||||||
|
|
||||||
// Add missing users table creation here to be complete for independent seeder run
|
|
||||||
mustExec(db, `CREATE TABLE users (
|
mustExec(db, `CREATE TABLE users (
|
||||||
id UUID PRIMARY KEY,
|
id UUID PRIMARY KEY,
|
||||||
company_id UUID NOT NULL REFERENCES companies(id),
|
company_id UUID NOT NULL REFERENCES companies(id),
|
||||||
|
|
@ -142,76 +141,124 @@ func SeedLean(dsn string) (string, error) {
|
||||||
updated_at TIMESTAMPTZ NOT NULL
|
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
|
// Helper for hashing
|
||||||
hashPwd := func(pwd string) string {
|
hashPwd := func(pwd string) string {
|
||||||
h, _ := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
|
h, _ := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
|
||||||
return string(h)
|
return string(h)
|
||||||
}
|
}
|
||||||
|
defaultPwdHash := hashPwd("123456")
|
||||||
|
|
||||||
// 1. Admin
|
// Fixed Pharmacies Data
|
||||||
adminID := uuid.Must(uuid.NewV7())
|
pharmacies := []struct {
|
||||||
mustExec(db, fmt.Sprintf(`INSERT INTO users (id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at)
|
Name string
|
||||||
VALUES ('%s', '%s', 'Admin', 'Administrador', 'admin', 'admin@saveinmed.com', true, '%s', NOW(), NOW())`,
|
CNPJ string
|
||||||
adminID, pharmacyID, hashPwd("admin123"),
|
Lat float64
|
||||||
))
|
Lng float64
|
||||||
|
Suffix string // for usernames/emails e.g. "1" -> dono1, email1
|
||||||
// 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)
|
Name: "Farmácia Central",
|
||||||
VALUES ('%s', '%s', 'Dono', 'João Dono', 'dono', 'dono@farmacia.com', true, '%s', NOW(), NOW())`,
|
CNPJ: "11111111000111",
|
||||||
ownerID, pharmacyID, hashPwd("123456"),
|
Lat: AnapolisLat,
|
||||||
))
|
Lng: AnapolisLng,
|
||||||
|
Suffix: "1",
|
||||||
// 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)
|
Name: "Farmácia Jundiaí",
|
||||||
VALUES ('%s', '%s', 'Colaborador', 'Maria Colaboradora', 'colaborador', 'colaborador@farmacia.com', true, '%s', NOW(), NOW())`,
|
CNPJ: "22222222000122",
|
||||||
empID, pharmacyID, hashPwd("123456"),
|
Lat: AnapolisLat + 0.015, // Slightly North
|
||||||
))
|
Lng: AnapolisLng + 0.010, // East
|
||||||
|
Suffix: "2",
|
||||||
// 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?
|
Name: "Farmácia Jaiara",
|
||||||
// The prompt implies "entregador" as a user role.
|
CNPJ: "33333333000133",
|
||||||
// Linking to Pharmacy for simplicity (internal delivery fleet).
|
Lat: AnapolisLat + 0.030, // More North
|
||||||
delID := uuid.Must(uuid.NewV7())
|
Lng: AnapolisLng - 0.010, // West
|
||||||
mustExec(db, fmt.Sprintf(`INSERT INTO users (id, company_id, role, name, username, email, email_verified, password_hash, created_at, updated_at)
|
Suffix: "3",
|
||||||
VALUES ('%s', '%s', 'Entregador', 'José Entregador', 'entregador', 'entregador@farmacia.com', true, '%s', NOW(), NOW())`,
|
},
|
||||||
delID, pharmacyID, hashPwd("123456"),
|
{
|
||||||
))
|
Name: "Farmácia Universitária",
|
||||||
|
CNPJ: "44444444000144",
|
||||||
log.Println("✅ [Lean] Users created: admin, dono, colaborador, entregador")
|
Lat: AnapolisLat - 0.020, // South
|
||||||
|
Lng: AnapolisLng + 0.020, // East
|
||||||
// Create Products for the Pharmacy
|
Suffix: "4",
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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) {
|
func SeedFull(dsn string) (string, error) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue