package transfer import ( "bamort/database" "bamort/models" "fmt" "time" ) // CharacterExport contains all data needed to export and import a character type CharacterExport struct { Character models.Char `json:"character"` GSMSkills []models.Skill `json:"gsm_skills"` GSMWeaponSkills []models.WeaponSkill `json:"gsm_weapon_skills"` GSMSpells []models.Spell `json:"gsm_spells"` GSMWeapons []models.Weapon `json:"gsm_weapons"` GSMEquipment []models.Equipment `json:"gsm_equipment"` GSMContainers []models.Container `json:"gsm_containers"` LearningData LearningDataExport `json:"learning_data"` AuditLogEntries []models.AuditLogEntry `json:"audit_log_entries"` } // LearningDataExport contains all learning-related master data type LearningDataExport struct { Sources []models.Source `json:"sources"` CharacterClasses []models.CharacterClass `json:"character_classes"` SkillCategories []models.SkillCategory `json:"skill_categories"` SkillDifficulties []models.SkillDifficulty `json:"skill_difficulties"` SpellSchools []models.SpellSchool `json:"spell_schools"` ClassCategoryEPCosts []models.ClassCategoryEPCost `json:"class_category_ep_costs"` ClassSpellSchoolEPCosts []models.ClassSpellSchoolEPCost `json:"class_spell_school_ep_costs"` SpellLevelLECosts []models.SpellLevelLECost `json:"spell_level_le_costs"` SkillCategoryDifficulties []models.SkillCategoryDifficulty `json:"skill_category_difficulties"` SkillImprovementCosts []models.SkillImprovementCost `json:"skill_improvement_costs"` } // ExportCharacter exports a complete character with all related data func ExportCharacter(characterID uint) (*CharacterExport, error) { var char models.Char // Load character with all relations err := database.DB. Preload("User"). Preload("Lp"). Preload("Ap"). Preload("B"). Preload("Merkmale"). Preload("Eigenschaften"). Preload("Fertigkeiten"). Preload("Waffenfertigkeiten"). Preload("Zauber"). Preload("Bennies"). Preload("Vermoegen"). Preload("Erfahrungsschatz"). Preload("Waffen"). Preload("Behaeltnisse"). Preload("Transportmittel"). Preload("Ausruestung"). First(&char, characterID).Error if err != nil { return nil, fmt.Errorf("failed to load character: %w", err) } // Remove sensitive user information from export char.User.PasswordHash = "" char.User.UpdatedAt = time.Time{} char.User.ResetPwHash = nil char.User.ResetPwHashExpires = nil export := &CharacterExport{ Character: char, } // Collect GSM skill data export.GSMSkills = make([]models.Skill, 0) export.GSMWeaponSkills = make([]models.WeaponSkill, 0) skillNames := make(map[string]bool) for _, skill := range char.Fertigkeiten { if !skillNames[skill.Name] { var gsmSkill models.Skill err := gsmSkill.First(skill.Name) if err == nil && gsmSkill.ID != 0 { export.GSMSkills = append(export.GSMSkills, gsmSkill) skillNames[skill.Name] = true } } } weaponSkillNames := make(map[string]bool) for _, skill := range char.Waffenfertigkeiten { if !weaponSkillNames[skill.Name] { var weaponSkill models.WeaponSkill err := weaponSkill.First(skill.Name) if err == nil && weaponSkill.ID != 0 { export.GSMWeaponSkills = append(export.GSMWeaponSkills, weaponSkill) weaponSkillNames[skill.Name] = true } } } // Collect GSM spell data export.GSMSpells = make([]models.Spell, 0) spellNames := make(map[string]bool) for _, spell := range char.Zauber { if !spellNames[spell.Name] { var gsmSpell models.Spell err := gsmSpell.First(spell.Name) if err == nil && gsmSpell.ID != 0 { export.GSMSpells = append(export.GSMSpells, gsmSpell) spellNames[spell.Name] = true } } } // Collect GSM weapon data export.GSMWeapons = make([]models.Weapon, 0) weaponNames := make(map[string]bool) for _, weapon := range char.Waffen { if !weaponNames[weapon.Name] { var gsmWeapon models.Weapon err := gsmWeapon.First(weapon.Name) if err == nil && gsmWeapon.ID != 0 { export.GSMWeapons = append(export.GSMWeapons, gsmWeapon) weaponNames[weapon.Name] = true } } } // Collect GSM equipment data export.GSMEquipment = make([]models.Equipment, 0) equipmentNames := make(map[string]bool) for _, equip := range char.Ausruestung { if !equipmentNames[equip.Name] { var gsmEquip models.Equipment err := gsmEquip.First(equip.Name) if err == nil && gsmEquip.ID != 0 { export.GSMEquipment = append(export.GSMEquipment, gsmEquip) equipmentNames[equip.Name] = true } } } // Collect GSM container data export.GSMContainers = make([]models.Container, 0) containerNames := make(map[string]bool) for _, container := range char.Behaeltnisse { if !containerNames[container.Name] { var gsmContainer models.Container err := gsmContainer.First(container.Name) if err == nil && gsmContainer.ID != 0 { export.GSMContainers = append(export.GSMContainers, gsmContainer) containerNames[container.Name] = true } } } for _, container := range char.Transportmittel { if !containerNames[container.Name] { var gsmContainer models.Container err := gsmContainer.First(container.Name) if err == nil && gsmContainer.ID != 0 { export.GSMContainers = append(export.GSMContainers, gsmContainer) containerNames[container.Name] = true } } } // Load learning data export.LearningData = LearningDataExport{ Sources: make([]models.Source, 0), CharacterClasses: make([]models.CharacterClass, 0), SkillCategories: make([]models.SkillCategory, 0), SkillDifficulties: make([]models.SkillDifficulty, 0), SpellSchools: make([]models.SpellSchool, 0), ClassCategoryEPCosts: make([]models.ClassCategoryEPCost, 0), ClassSpellSchoolEPCosts: make([]models.ClassSpellSchoolEPCost, 0), SpellLevelLECosts: make([]models.SpellLevelLECost, 0), SkillCategoryDifficulties: make([]models.SkillCategoryDifficulty, 0), SkillImprovementCosts: make([]models.SkillImprovementCost, 0), } database.DB.Find(&export.LearningData.Sources) database.DB.Preload("Source").Find(&export.LearningData.CharacterClasses) database.DB.Preload("Source").Find(&export.LearningData.SkillCategories) database.DB.Find(&export.LearningData.SkillDifficulties) database.DB.Preload("Source").Find(&export.LearningData.SpellSchools) database.DB.Preload("CharacterClass").Preload("SkillCategory").Find(&export.LearningData.ClassCategoryEPCosts) database.DB.Preload("CharacterClass").Preload("SpellSchool").Find(&export.LearningData.ClassSpellSchoolEPCosts) database.DB.Find(&export.LearningData.SpellLevelLECosts) database.DB.Preload("Skill").Preload("SkillCategory").Preload("SkillDifficulty").Find(&export.LearningData.SkillCategoryDifficulties) database.DB.Find(&export.LearningData.SkillImprovementCosts) // Load audit log entries export.AuditLogEntries = make([]models.AuditLogEntry, 0) database.DB.Where("character_id = ?", characterID).Order("timestamp ASC").Find(&export.AuditLogEntries) return export, nil }