From dae2c0daa9814b356b39f934796908d99755cb4e Mon Sep 17 00:00:00 2001 From: Habib Fatkhul Rohman Date: Tue, 16 Sep 2025 11:01:46 +0700 Subject: [PATCH] Add Product entity, migration, seeder, and JSON data for multi-tenant support --- database/entities/product_entity.go | 16 ++++ database/entities/tenant_entity.go | 2 + database/migration.go | 15 ++++ database/seeder.go | 1 + database/seeders/json/products.json | 42 ++++++++++ database/seeders/seeds/product_seed.go | 109 +++++++++++++++++++++++++ 6 files changed, 185 insertions(+) create mode 100644 database/entities/product_entity.go create mode 100644 database/seeders/json/products.json create mode 100644 database/seeders/seeds/product_seed.go diff --git a/database/entities/product_entity.go b/database/entities/product_entity.go new file mode 100644 index 0000000..08c97ad --- /dev/null +++ b/database/entities/product_entity.go @@ -0,0 +1,16 @@ +package entities + +import ( + "github.com/google/uuid" +) + +type Product struct { + ID uuid.UUID `gorm:"type:uuid;primary_key;default:uuid_generate_v4()" json:"id"` + Name string `gorm:"type:varchar(100);not null" json:"name"` + Price float64 `gorm:"type:numeric;not null" json:"price"` + + TenantID uuid.UUID `gorm:"type:uuid;null;index" json:"tenant_id"` + Tenant Tenant `gorm:"foreignKey:TenantID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"tenant"` + + Timestamp +} diff --git a/database/entities/tenant_entity.go b/database/entities/tenant_entity.go index 60bd0af..b29a5e1 100644 --- a/database/entities/tenant_entity.go +++ b/database/entities/tenant_entity.go @@ -8,5 +8,7 @@ type Tenant struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:uuid_generate_v4()" json:"id"` Name string `gorm:"type:varchar(100);not null" json:"name"` + Products []Product `gorm:"foreignKey:TenantID" json:"products"` + Users []User `gorm:"foreignKey:TenantID" json:"users"` Timestamp } diff --git a/database/migration.go b/database/migration.go index 3daa90b..a484e9e 100644 --- a/database/migration.go +++ b/database/migration.go @@ -10,9 +10,24 @@ func Migrate(db *gorm.DB) error { &entities.Tenant{}, &entities.User{}, &entities.RefreshToken{}, + &entities.Product{}, ); err != nil { return err } return nil } + +func MigrateFresh(db *gorm.DB) error { + // Drop tables + if err := db.Migrator().DropTable( + &entities.Tenant{}, + &entities.User{}, + &entities.RefreshToken{}, + &entities.Product{}, + ); err != nil { + return err + } + // Migrate ulang + return Migrate(db) +} diff --git a/database/seeder.go b/database/seeder.go index df07d39..ad713b2 100644 --- a/database/seeder.go +++ b/database/seeder.go @@ -9,6 +9,7 @@ func Seeder(db *gorm.DB) error { seeders := []func(*gorm.DB) error{ seeds.ListTenantSeeder, seeds.ListUserSeeder, + seeds.ListProductSeeder, } for _, seeder := range seeders { diff --git a/database/seeders/json/products.json b/database/seeders/json/products.json new file mode 100644 index 0000000..906949c --- /dev/null +++ b/database/seeders/json/products.json @@ -0,0 +1,42 @@ +[ + { + "name": "MacBook Pro", + "price": 10000, + "tenant_name": "Tenant A" + }, + { + "name": "iPhone 13", + "price": 5000, + "tenant_name": "Tenant B" + }, + { + "name": "Laptop Pro", + "price": 13000.99, + "tenant_name": "Tenant A" + }, + { + "name": "iPhone 11", + "price": 7000.99, + "tenant_name": "Tenant B" + }, + { + "name": "MacBook Pro M1", + "price": 10000.15, + "tenant_name": "Tenant A" + }, + { + "name": "iPhone 10", + "price": 5000.99, + "tenant_name": "Tenant B" + }, + { + "name": "MacBook Pro M2", + "price": 90000.99, + "tenant_name": "Tenant A" + }, + { + "name": "iPhone 14", + "price": 20000.15, + "tenant_name": "Tenant B" + } +] diff --git a/database/seeders/seeds/product_seed.go b/database/seeders/seeds/product_seed.go new file mode 100644 index 0000000..d038238 --- /dev/null +++ b/database/seeders/seeds/product_seed.go @@ -0,0 +1,109 @@ +package seeds + +import ( + "encoding/json" + "errors" + "io" + "os" + + "github.com/Caknoooo/go-gin-clean-starter/database/entities" + "gorm.io/gorm" +) + +type ProductSeed struct { + Name string `json:"name"` + Price float64 `json:"price"` + TenantName string `json:"tenant_name"` +} + +func ListProductSeeder(db *gorm.DB) error { + jsonFile, err := os.Open("./database/seeders/json/products.json") + if err != nil { + return err + } + + jsonData, err := io.ReadAll(jsonFile) + if err != nil { + return err + } + + var listProduct []ProductSeed + if err := json.Unmarshal(jsonData, &listProduct); err != nil { + return err + } + + hasTable := db.Migrator().HasTable(&entities.Product{}) + if !hasTable { + if err := db.Migrator().CreateTable(&entities.Product{}); err != nil { + return err + } + } + + for _, data := range listProduct { + var tenant entities.Tenant + if err := db.Where("name = ?", data.TenantName).First(&tenant).Error; err != nil { + return err // tenant tidak ditemukan + } + + var product entities.Product + err := db.Where(&entities.Product{Name: data.Name}).First(&product).Error + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + + isData := db.Find(&product, "name = ?", data.Name).RowsAffected + if isData == 0 { + newProduct := entities.Product{ + Name: data.Name, + Price: data.Price, + TenantID: tenant.ID, + } + if err := db.Create(&newProduct).Error; err != nil { + return err + } + } + } + + return nil +} + +// func ListUserSeeder(db *gorm.DB) error { +// jsonFile, err := os.Open("./database/seeders/json/users.json") +// if err != nil { +// return err +// } + +// jsonData, err := io.ReadAll(jsonFile) +// if err != nil { +// return err +// } + +// var listUser []entities.User +// if err := json.Unmarshal(jsonData, &listUser); err != nil { +// return err +// } + +// hasTable := db.Migrator().HasTable(&entities.User{}) +// if !hasTable { +// if err := db.Migrator().CreateTable(&entities.User{}); err != nil { +// return err +// } +// } + +// for _, data := range listUser { +// var user entities.User +// err := db.Where(&entities.User{Email: data.Email}).First(&user).Error +// if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { +// return err +// } + +// isData := db.Find(&user, "email = ?", data.Email).RowsAffected +// if isData == 0 { +// if err := db.Create(&data).Error; err != nil { +// return err +// } +// } +// } + +// return nil +// }