first commit
This commit is contained in:
484
internal/database/seeder.go
Normal file
484
internal/database/seeder.go
Normal file
@@ -0,0 +1,484 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"cardverse/internal/modules/cardmaster"
|
||||
"cardverse/internal/modules/seriessetmaster"
|
||||
"cardverse/internal/modules/user"
|
||||
"cardverse/internal/pkg/utils"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Seed(db *gorm.DB) error {
|
||||
if err := seedAdminUser(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := seedSeriesData(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := seedSetData(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := seedCardData(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func seedAdminUser(db *gorm.DB) error {
|
||||
repo := user.NewRepository(db)
|
||||
|
||||
const adminEmail = "admin@example.com"
|
||||
const adminPassword = "admin12345"
|
||||
|
||||
existing, _ := repo.FindByEmail(adminEmail)
|
||||
if existing != nil {
|
||||
log.Println("seed: admin user sudah ada, skip")
|
||||
return nil
|
||||
}
|
||||
|
||||
hashed, err := utils.HashPassword(adminPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
admin := &user.User{
|
||||
Name: "Administrator",
|
||||
Email: adminEmail,
|
||||
Password: hashed,
|
||||
Role: user.RoleSuperAdmin,
|
||||
Status: user.StatusActive,
|
||||
IsEmailVerified: true,
|
||||
}
|
||||
|
||||
if err := repo.Create(admin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("seed: admin user berhasil dibuat (email: %s / password: %s)\n", adminEmail, adminPassword)
|
||||
return nil
|
||||
}
|
||||
|
||||
// seriesJSON merepresentasikan struktur JSON dari file series.json
|
||||
type seriesJSON struct {
|
||||
Name string `json:"name"`
|
||||
ImageLocalPath string `json:"image_local_path"`
|
||||
ExpansionCount string `json:"expansion_count"`
|
||||
CardCount string `json:"card_count"`
|
||||
}
|
||||
|
||||
func seedSeriesData(db *gorm.DB) error {
|
||||
var count int64
|
||||
db.Model(&seriessetmaster.SeriesSetMaster{}).Where("parent_id IS NULL").Count(&count)
|
||||
if count > 0 {
|
||||
log.Println("seed: data series sudah ada, skip")
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := os.ReadFile("public/json/series.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rawSeries []seriesJSON
|
||||
if err := json.Unmarshal(file, &rawSeries); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
records := make([]seriessetmaster.SeriesSetMaster, 0, len(rawSeries))
|
||||
for _, s := range rawSeries {
|
||||
expansionCount, _ := strconv.Atoi(s.ExpansionCount)
|
||||
cardCount, _ := strconv.Atoi(s.CardCount)
|
||||
|
||||
records = append(records, seriessetmaster.SeriesSetMaster{
|
||||
SeriesName: s.Name,
|
||||
Image: s.ImageLocalPath,
|
||||
ExpansionCount: expansionCount,
|
||||
CardCount: cardCount,
|
||||
})
|
||||
}
|
||||
|
||||
if err := db.Create(&records).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("seed: %d data series berhasil dimasukkan\n", len(records))
|
||||
return nil
|
||||
}
|
||||
|
||||
// setJSON merepresentasikan struktur JSON dari file set (misal MA1.json)
|
||||
type setJSON struct {
|
||||
SetCode string `json:"set_code"`
|
||||
SetName string `json:"set_name"`
|
||||
SeriesName string `json:"series_name"`
|
||||
CoverImageLocalPath string `json:"cover_image_local_path"`
|
||||
LogoImageLocalPath string `json:"logo_image_local_path"`
|
||||
ReleaseDate string `json:"release_date"`
|
||||
TotalCards string `json:"total_cards"`
|
||||
Cards []json.RawMessage `json:"cards"`
|
||||
}
|
||||
|
||||
const imagesBasePath = "public/images"
|
||||
|
||||
// folder yang harus di-skip saat scan series
|
||||
var skipFolders = map[string]bool{
|
||||
"elements": true,
|
||||
"series": true,
|
||||
}
|
||||
|
||||
func seedSetData(db *gorm.DB) error {
|
||||
var count int64
|
||||
db.Model(&seriessetmaster.SeriesSetMaster{}).Where("parent_id IS NOT NULL").Count(&count)
|
||||
if count > 0 {
|
||||
log.Println("seed: data set sudah ada, skip")
|
||||
return nil
|
||||
}
|
||||
|
||||
var allSeries []seriessetmaster.SeriesSetMaster
|
||||
db.Where("parent_id IS NULL").Find(&allSeries)
|
||||
|
||||
seriesMap := make(map[string]uint, len(allSeries))
|
||||
for _, s := range allSeries {
|
||||
seriesMap[s.SeriesName] = s.ID
|
||||
}
|
||||
|
||||
seriesFolders, err := os.ReadDir(imagesBasePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var totalSetsInserted int
|
||||
for _, seriesFolder := range seriesFolders {
|
||||
if !seriesFolder.IsDir() || skipFolders[seriesFolder.Name()] {
|
||||
continue
|
||||
}
|
||||
|
||||
seriesPath := filepath.Join(imagesBasePath, seriesFolder.Name())
|
||||
setFolders, err := os.ReadDir(seriesPath)
|
||||
if err != nil {
|
||||
log.Printf("seed: gagal baca folder %s: %v\n", seriesPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
var setRecords []seriessetmaster.SeriesSetMaster
|
||||
var totalCardsInSeries int
|
||||
|
||||
for _, setFolder := range setFolders {
|
||||
if !setFolder.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
setPath := filepath.Join(seriesPath, setFolder.Name())
|
||||
jsonFiles, err := filepath.Glob(filepath.Join(setPath, "*.json"))
|
||||
if err != nil || len(jsonFiles) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
rawFile, err := os.ReadFile(jsonFiles[0])
|
||||
if err != nil {
|
||||
log.Printf("seed: gagal baca file %s: %v\n", jsonFiles[0], err)
|
||||
continue
|
||||
}
|
||||
|
||||
var setData setJSON
|
||||
if err := json.Unmarshal(rawFile, &setData); err != nil {
|
||||
log.Printf("seed: gagal parse JSON %s: %v\n", jsonFiles[0], err)
|
||||
continue
|
||||
}
|
||||
|
||||
parentID, exists := seriesMap[setData.SeriesName]
|
||||
if !exists {
|
||||
log.Printf("seed: series '%s' tidak ditemukan di DB, skip set '%s'\n", setData.SeriesName, setData.SetCode)
|
||||
continue
|
||||
}
|
||||
|
||||
cardCount := len(setData.Cards)
|
||||
totalCardsInSeries += cardCount
|
||||
|
||||
var releaseDate *time.Time
|
||||
if setData.ReleaseDate != "" {
|
||||
parsed, err := time.Parse("2006-01-02", setData.ReleaseDate)
|
||||
if err == nil {
|
||||
releaseDate = &parsed
|
||||
}
|
||||
}
|
||||
|
||||
setRecords = append(setRecords, seriessetmaster.SeriesSetMaster{
|
||||
ParentID: &parentID,
|
||||
SetCode: setData.SetCode,
|
||||
SetName: setData.SetName,
|
||||
SeriesName: setData.SeriesName,
|
||||
Image: setData.CoverImageLocalPath,
|
||||
Logo: setData.LogoImageLocalPath,
|
||||
ReleaseDate: releaseDate,
|
||||
TotalCards: cardCount,
|
||||
})
|
||||
}
|
||||
|
||||
if len(setRecords) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := db.Create(&setRecords).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
seriesName := setRecords[0].SeriesName
|
||||
if parentID, exists := seriesMap[seriesName]; exists {
|
||||
db.Model(&seriessetmaster.SeriesSetMaster{}).
|
||||
Where("id = ?", parentID).
|
||||
Updates(map[string]interface{}{
|
||||
"expansion_count": len(setRecords),
|
||||
"card_count": totalCardsInSeries,
|
||||
})
|
||||
}
|
||||
|
||||
totalSetsInserted += len(setRecords)
|
||||
log.Printf("seed: %d set untuk series '%s' berhasil dimasukkan (total kartu: %d)\n",
|
||||
len(setRecords), seriesName, totalCardsInSeries)
|
||||
}
|
||||
|
||||
log.Printf("seed: total %d data set berhasil dimasukkan\n", totalSetsInserted)
|
||||
return nil
|
||||
}
|
||||
|
||||
// cardJSON merepresentasikan struktur card dari JSON
|
||||
type cardJSON struct {
|
||||
Name string `json:"name"`
|
||||
Number string `json:"number"`
|
||||
ImageLocalPath string `json:"image_local_path"`
|
||||
Stage string `json:"stage"`
|
||||
Element string `json:"element"`
|
||||
EvolvesFrom string `json:"evolves_from"`
|
||||
Illustrator string `json:"illustrator"`
|
||||
Regulation string `json:"regulation"`
|
||||
Rarity string `json:"rarity"`
|
||||
IDPriceChartingEng string `json:"id_price_charting_eng"`
|
||||
LinkPriceChartingEng string `json:"link_price_charting_eng"`
|
||||
IDPriceChartingJpn string `json:"id_price_charting_jpn"`
|
||||
LinkPriceChartingJpn string `json:"link_price_charting_jpn"`
|
||||
HP json.RawMessage `json:"hp"`
|
||||
Attacks json.RawMessage `json:"attacks"`
|
||||
Abilities json.RawMessage `json:"abilities"`
|
||||
Battle json.RawMessage `json:"battle"`
|
||||
EvolutionLine json.RawMessage `json:"evolution_line"`
|
||||
PokedexInfo json.RawMessage `json:"pokedex_info"`
|
||||
Effects json.RawMessage `json:"effects"`
|
||||
}
|
||||
|
||||
const cardBatchSize = 100
|
||||
|
||||
func seedCardData(db *gorm.DB) error {
|
||||
var count int64
|
||||
db.Model(&cardmaster.CardMaster{}).Count(&count)
|
||||
if count > 0 {
|
||||
log.Println("seed: data card sudah ada, skip")
|
||||
return nil
|
||||
}
|
||||
|
||||
var allSets []seriessetmaster.SeriesSetMaster
|
||||
db.Where("parent_id IS NOT NULL").Find(&allSets)
|
||||
|
||||
setMap := make(map[string]uint, len(allSets))
|
||||
for _, s := range allSets {
|
||||
setMap[s.SetCode] = s.ID
|
||||
}
|
||||
|
||||
seriesFolders, err := os.ReadDir(imagesBasePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var totalCardsInserted int
|
||||
for _, seriesFolder := range seriesFolders {
|
||||
if !seriesFolder.IsDir() || skipFolders[seriesFolder.Name()] {
|
||||
continue
|
||||
}
|
||||
|
||||
seriesPath := filepath.Join(imagesBasePath, seriesFolder.Name())
|
||||
setFolders, err := os.ReadDir(seriesPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, setFolder := range setFolders {
|
||||
if !setFolder.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
setPath := filepath.Join(seriesPath, setFolder.Name())
|
||||
jsonFiles, err := filepath.Glob(filepath.Join(setPath, "*.json"))
|
||||
if err != nil || len(jsonFiles) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
rawFile, err := os.ReadFile(jsonFiles[0])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var setData setJSON
|
||||
if err := json.Unmarshal(rawFile, &setData); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
setID, exists := setMap[setData.SetCode]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
cardRecords := make([]cardmaster.CardMaster, 0, len(setData.Cards))
|
||||
for _, rawCard := range setData.Cards {
|
||||
var c cardJSON
|
||||
if err := json.Unmarshal(rawCard, &c); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
enrichedHP := enrichHP(c.HP)
|
||||
enrichedAttacks := enrichAttacks(c.Attacks)
|
||||
enrichedBattle := enrichBattle(c.Battle)
|
||||
|
||||
cardRecords = append(cardRecords, cardmaster.CardMaster{
|
||||
IDSet: setID,
|
||||
Name: c.Name,
|
||||
Number: c.Number,
|
||||
Image: c.ImageLocalPath,
|
||||
Stage: c.Stage,
|
||||
Element: c.Element,
|
||||
ElementImage: cardmaster.GetElementImage(c.Element),
|
||||
EvolvesFrom: c.EvolvesFrom,
|
||||
Illustrator: c.Illustrator,
|
||||
Regulation: c.Regulation,
|
||||
Rarity: c.Rarity,
|
||||
IDPriceChartingEng: c.IDPriceChartingEng,
|
||||
LinkPriceChartingEng: c.LinkPriceChartingEng,
|
||||
IDPriceChartingJpn: c.IDPriceChartingJpn,
|
||||
LinkPriceChartingJpn: c.LinkPriceChartingJpn,
|
||||
HP: cardmaster.JSONB(enrichedHP),
|
||||
Attacks: cardmaster.JSONB(enrichedAttacks),
|
||||
Abilities: cardmaster.JSONB(c.Abilities),
|
||||
Battle: cardmaster.JSONB(enrichedBattle),
|
||||
EvolutionLine: cardmaster.JSONB(c.EvolutionLine),
|
||||
PokedexInfo: cardmaster.JSONB(c.PokedexInfo),
|
||||
Effects: cardmaster.JSONB(c.Effects),
|
||||
})
|
||||
}
|
||||
|
||||
if len(cardRecords) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := db.CreateInBatches(&cardRecords, cardBatchSize).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
totalCardsInserted += len(cardRecords)
|
||||
log.Printf("seed: %d kartu untuk set '%s' berhasil dimasukkan\n", len(cardRecords), setData.SetCode)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("seed: total %d data kartu berhasil dimasukkan\n", totalCardsInserted)
|
||||
return nil
|
||||
}
|
||||
|
||||
func enrichHP(raw json.RawMessage) json.RawMessage {
|
||||
if len(raw) == 0 || string(raw) == "null" {
|
||||
return raw
|
||||
}
|
||||
|
||||
var hp cardmaster.HP
|
||||
if err := json.Unmarshal(raw, &hp); err != nil {
|
||||
return raw
|
||||
}
|
||||
|
||||
hp.ElementImage = cardmaster.GetElementImage(hp.Element)
|
||||
enriched, err := json.Marshal(hp)
|
||||
if err != nil {
|
||||
return raw
|
||||
}
|
||||
return enriched
|
||||
}
|
||||
|
||||
func enrichAttacks(raw json.RawMessage) json.RawMessage {
|
||||
if len(raw) == 0 || string(raw) == "null" {
|
||||
return raw
|
||||
}
|
||||
|
||||
var rawAttacks []struct {
|
||||
Cost []string `json:"cost"`
|
||||
Name string `json:"name"`
|
||||
Damage string `json:"damage,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &rawAttacks); err != nil {
|
||||
return raw
|
||||
}
|
||||
|
||||
enriched := make([]cardmaster.Attack, 0, len(rawAttacks))
|
||||
for _, a := range rawAttacks {
|
||||
costs := make([]cardmaster.AttackCost, 0, len(a.Cost))
|
||||
for _, c := range a.Cost {
|
||||
costs = append(costs, cardmaster.AttackCost{
|
||||
Element: c,
|
||||
Image: cardmaster.GetElementImage(c),
|
||||
})
|
||||
}
|
||||
enriched = append(enriched, cardmaster.Attack{
|
||||
Cost: costs,
|
||||
Name: a.Name,
|
||||
Damage: a.Damage,
|
||||
Description: a.Description,
|
||||
})
|
||||
}
|
||||
|
||||
result, err := json.Marshal(enriched)
|
||||
if err != nil {
|
||||
return raw
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func enrichBattle(raw json.RawMessage) json.RawMessage {
|
||||
if len(raw) == 0 || string(raw) == "null" {
|
||||
return raw
|
||||
}
|
||||
|
||||
var battle cardmaster.Battle
|
||||
if err := json.Unmarshal(raw, &battle); err != nil {
|
||||
return raw
|
||||
}
|
||||
|
||||
for i := range battle.Weakness {
|
||||
battle.Weakness[i].ElementImage = cardmaster.GetElementImage(battle.Weakness[i].Element)
|
||||
}
|
||||
for i := range battle.Resistance {
|
||||
battle.Resistance[i].ElementImage = cardmaster.GetElementImage(battle.Resistance[i].Element)
|
||||
}
|
||||
for i := range battle.Retreat {
|
||||
battle.Retreat[i].ElementImage = cardmaster.GetElementImage(battle.Retreat[i].Element)
|
||||
}
|
||||
|
||||
enriched, err := json.Marshal(battle)
|
||||
if err != nil {
|
||||
return raw
|
||||
}
|
||||
return enriched
|
||||
}
|
||||
|
||||
func seriesFolderName(name string) string {
|
||||
lower := strings.ToLower(name)
|
||||
lower = strings.ReplaceAll(lower, " & ", "-")
|
||||
lower = strings.ReplaceAll(lower, " ", "-")
|
||||
return lower
|
||||
}
|
||||
Reference in New Issue
Block a user