Files
bamort/backend/gsmaster/skill_enhanced_handlers.go
T

333 lines
9.1 KiB
Go
Raw Normal View History

2026-01-02 10:21:29 +01:00
package gsmaster
import (
"bamort/database"
"bamort/models"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// SkillWithCategories represents a skill with all its categories and difficulties
type SkillWithCategories struct {
models.Skill
Categories []SkillCategoryInfo `json:"categories"`
Difficulties []string `json:"difficulties"`
}
// SkillCategoryInfo contains category details for a skill
type SkillCategoryInfo struct {
CategoryID uint `json:"category_id"`
CategoryName string `json:"category_name"`
DifficultyID uint `json:"difficulty_id"`
DifficultyName string `json:"difficulty_name"`
LearnCost int `json:"learn_cost"`
}
// GetSkillWithCategories retrieves a skill with all its category-difficulty relationships
func GetSkillWithCategories(skillID uint) (*SkillWithCategories, error) {
var skill models.Skill
if err := database.DB.First(&skill, skillID).Error; err != nil {
return nil, err
}
// Get all category-difficulty relationships
var scds []models.SkillCategoryDifficulty
err := database.DB.Preload("SkillCategory").Preload("SkillDifficulty").
Where("skill_id = ?", skillID).Find(&scds).Error
if err != nil {
return nil, err
}
result := &SkillWithCategories{
Skill: skill,
Categories: make([]SkillCategoryInfo, len(scds)),
Difficulties: make([]string, len(scds)),
}
for i, scd := range scds {
result.Categories[i] = SkillCategoryInfo{
CategoryID: scd.SkillCategoryID,
CategoryName: scd.SkillCategory.Name,
DifficultyID: scd.SkillDifficultyID,
DifficultyName: scd.SkillDifficulty.Name,
LearnCost: scd.LearnCost,
}
result.Difficulties[i] = scd.SkillDifficulty.Name
}
return result, nil
}
// GetAllSkillsWithCategories retrieves all skills with their categories
func GetAllSkillsWithCategories() ([]SkillWithCategories, error) {
var skills []models.Skill
if err := database.DB.Find(&skills).Error; err != nil {
return nil, err
}
result := make([]SkillWithCategories, len(skills))
for i, skill := range skills {
skillWithCats, err := GetSkillWithCategories(skill.ID)
if err != nil {
return nil, err
}
result[i] = *skillWithCats
}
return result, nil
}
// SkillUpdateRequest represents the request to update a skill with categories
type SkillUpdateRequest struct {
models.Skill
CategoryDifficulties []CategoryDifficultyPair `json:"category_difficulties"`
}
// CategoryDifficultyPair represents a category-difficulty mapping
type CategoryDifficultyPair struct {
CategoryID uint `json:"category_id"`
DifficultyID uint `json:"difficulty_id"`
LearnCost int `json:"learn_cost,omitempty"`
}
2026-01-04 17:49:04 +01:00
// CreateSkillWithCategories creates a new skill with category-difficulty relationships
func CreateSkillWithCategories(req SkillUpdateRequest) (uint, error) {
// Validate required fields
if req.Skill.Name == "" {
return 0, fmt.Errorf("skill name is required")
}
var skillID uint
// Start transaction
err := database.DB.Transaction(func(tx *gorm.DB) error {
// Create skill
if err := tx.Create(&req.Skill).Error; err != nil {
return err
}
skillID = req.Skill.ID
// Create category-difficulty relationships
for _, cd := range req.CategoryDifficulties {
// Get category and difficulty names for denormalized fields
var category models.SkillCategory
if err := tx.First(&category, cd.CategoryID).Error; err != nil {
return fmt.Errorf("category not found: %w", err)
}
var difficulty models.SkillDifficulty
if err := tx.First(&difficulty, cd.DifficultyID).Error; err != nil {
return fmt.Errorf("difficulty not found: %w", err)
}
learnCost := cd.LearnCost
if learnCost == 0 {
// Use default based on difficulty
learnCost = getDefaultLearnCost(difficulty.Name)
}
scd := models.SkillCategoryDifficulty{
SkillID: skillID,
SkillCategoryID: cd.CategoryID,
SkillDifficultyID: cd.DifficultyID,
LearnCost: learnCost,
SCategory: category.Name,
SDifficulty: difficulty.Name,
}
if err := tx.Create(&scd).Error; err != nil {
return err
}
}
return nil
})
if err != nil {
return 0, err
}
return skillID, nil
}
2026-01-02 10:21:29 +01:00
// UpdateSkillWithCategories updates a skill and its category-difficulty relationships
func UpdateSkillWithCategories(skillID uint, req SkillUpdateRequest) error {
// Start transaction
return database.DB.Transaction(func(tx *gorm.DB) error {
2026-01-04 17:49:04 +01:00
// Update skill basic info - use Select to explicitly include boolean fields
// This ensures false values are also updated (GORM skips zero values by default in Updates)
if err := tx.Model(&models.Skill{}).Where("id = ?", skillID).
2026-01-04 17:49:25 +01:00
Select("name", "beschreibung", "game_system", "initialwert", "basis_wert",
2026-01-04 17:49:04 +01:00
"bonuseigenschaft", "improvable", "innate_skill", "source_id", "page_number").
Updates(req.Skill).Error; err != nil {
2026-01-02 10:21:29 +01:00
return err
}
// Delete existing category-difficulty relationships
if err := tx.Where("skill_id = ?", skillID).Delete(&models.SkillCategoryDifficulty{}).Error; err != nil {
return err
}
// Create new relationships
for _, cd := range req.CategoryDifficulties {
// Get category and difficulty names for denormalized fields
var category models.SkillCategory
if err := tx.First(&category, cd.CategoryID).Error; err != nil {
return fmt.Errorf("category not found: %w", err)
}
var difficulty models.SkillDifficulty
if err := tx.First(&difficulty, cd.DifficultyID).Error; err != nil {
return fmt.Errorf("difficulty not found: %w", err)
}
learnCost := cd.LearnCost
if learnCost == 0 {
// Use default based on difficulty
learnCost = getDefaultLearnCost(difficulty.Name)
}
scd := models.SkillCategoryDifficulty{
SkillID: skillID,
SkillCategoryID: cd.CategoryID,
SkillDifficultyID: cd.DifficultyID,
LearnCost: learnCost,
SCategory: category.Name,
SDifficulty: difficulty.Name,
}
if err := tx.Create(&scd).Error; err != nil {
return err
}
}
return nil
})
}
// getDefaultLearnCost returns default LE cost based on difficulty
func getDefaultLearnCost(difficulty string) int {
switch strings.ToLower(difficulty) {
case "leicht", "easy":
return 5
case "normal", "standard":
return 10
case "schwer", "hard":
return 20
case "sehr schwer", "very hard":
return 30
default:
return 10
}
}
// ===== Handler Functions =====
// GetEnhancedMDSkills returns skills with their full category/difficulty information
func GetEnhancedMDSkills(c *gin.Context) {
skills, err := GetAllSkillsWithCategories()
if err != nil {
respondWithError(c, http.StatusInternalServerError, "Failed to retrieve skills: "+err.Error())
return
}
// Also get learning sources and difficulties for the dropdowns
var sources []models.Source
database.DB.Where("is_active = ?", true).Find(&sources)
var categories []models.SkillCategory
database.DB.Find(&categories)
var difficulties []models.SkillDifficulty
database.DB.Find(&difficulties)
c.JSON(http.StatusOK, gin.H{
"skills": skills,
"sources": sources,
"categories": categories,
"difficulties": difficulties,
})
}
// GetEnhancedMDSkill returns a single skill with category/difficulty information
func GetEnhancedMDSkill(c *gin.Context) {
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
respondWithError(c, http.StatusBadRequest, "Invalid ID")
return
}
skill, err := GetSkillWithCategories(uint(id))
if err != nil {
respondWithError(c, http.StatusNotFound, "Skill not found")
return
}
c.JSON(http.StatusOK, skill)
}
// UpdateEnhancedMDSkill updates a skill with its categories
func UpdateEnhancedMDSkill(c *gin.Context) {
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
respondWithError(c, http.StatusBadRequest, "Invalid ID")
return
}
var req SkillUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
respondWithError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
return
}
// Ensure the ID matches
req.Skill.ID = uint(id)
if err := UpdateSkillWithCategories(uint(id), req); err != nil {
respondWithError(c, http.StatusInternalServerError, "Failed to update skill: "+err.Error())
return
}
// Return updated skill
skill, err := GetSkillWithCategories(uint(id))
if err != nil {
respondWithError(c, http.StatusInternalServerError, "Failed to retrieve updated skill")
return
}
c.JSON(http.StatusOK, skill)
}
2026-01-04 17:49:04 +01:00
// CreateEnhancedMDSkill creates a new skill with categories
func CreateEnhancedMDSkill(c *gin.Context) {
var req SkillUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
respondWithError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
return
}
// Create the skill
skillID, err := CreateSkillWithCategories(req)
if err != nil {
respondWithError(c, http.StatusInternalServerError, "Failed to create skill: "+err.Error())
return
}
// Return created skill
skill, err := GetSkillWithCategories(skillID)
if err != nil {
respondWithError(c, http.StatusInternalServerError, "Failed to retrieve created skill")
return
}
c.JSON(http.StatusCreated, skill)
}