package models import ( "context" "errors" "fmt" "github.com/oklog/ulid/v2" "gorm.io/gorm" ) // Project repräsentiert ein Projekt im System type Project struct { EntityBase Name string `gorm:"column:name;not null"` CustomerID ulid.ULID `gorm:"column:customer_id;type:uuid;not null"` // Beziehungen (für Eager Loading) Customer *Customer `gorm:"foreignKey:CustomerID"` } // TableName gibt den Tabellennamen für GORM an func (Project) TableName() string { return "projects" } // ProjectCreate enthält die Felder zum Erstellen eines neuen Projekts type ProjectCreate struct { Name string CustomerID ulid.ULID } // ProjectUpdate enthält die aktualisierbaren Felder eines Projekts type ProjectUpdate struct { ID ulid.ULID `gorm:"-"` // Ausschließen von Updates Name *string `gorm:"column:name"` CustomerID *ulid.ULID `gorm:"column:customer_id"` } // Validate prüft, ob die Create-Struktur gültige Daten enthält func (pc *ProjectCreate) Validate() error { if pc.Name == "" { return errors.New("project name darf nicht leer sein") } // Prüfung auf gültige CustomerID if pc.CustomerID.Compare(ulid.ULID{}) == 0 { return errors.New("customerID darf nicht leer sein") } return nil } // Validate prüft, ob die Update-Struktur gültige Daten enthält func (pu *ProjectUpdate) Validate() error { if pu.Name != nil && *pu.Name == "" { return errors.New("project name darf nicht leer sein") } return nil } // GetProjectByID sucht ein Projekt anhand seiner ID func GetProjectByID(ctx context.Context, id ulid.ULID) (*Project, error) { var project Project result := GetEngine(ctx).Where("id = ?", id).First(&project) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, nil } return nil, result.Error } return &project, nil } // GetProjectWithCustomer lädt ein Projekt mit den zugehörigen Kundeninformationen func GetProjectWithCustomer(ctx context.Context, id ulid.ULID) (*Project, error) { var project Project result := GetEngine(ctx).Preload("Customer").Where("id = ?", id).First(&project) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, nil } return nil, result.Error } return &project, nil } // GetAllProjects gibt alle Projekte zurück func GetAllProjects(ctx context.Context) ([]Project, error) { var projects []Project result := GetEngine(ctx).Find(&projects) if result.Error != nil { return nil, result.Error } return projects, nil } // GetAllProjectsWithCustomers gibt alle Projekte mit Kundeninformationen zurück func GetAllProjectsWithCustomers(ctx context.Context) ([]Project, error) { var projects []Project result := GetEngine(ctx).Preload("Customer").Find(&projects) if result.Error != nil { return nil, result.Error } return projects, nil } // GetProjectsByCustomerID gibt alle Projekte eines bestimmten Kunden zurück func GetProjectsByCustomerID(ctx context.Context, customerID ulid.ULID) ([]Project, error) { var projects []Project result := GetEngine(ctx).Where("customer_id = ?", customerID).Find(&projects) if result.Error != nil { return nil, result.Error } return projects, nil } // CreateProject erstellt ein neues Projekt mit Validierung func CreateProject(ctx context.Context, create ProjectCreate) (*Project, error) { // Validierung if err := create.Validate(); err != nil { return nil, fmt.Errorf("validierungsfehler: %w", err) } // Prüfen, ob der Kunde existiert customer, err := GetCustomerByID(ctx, create.CustomerID) if err != nil { return nil, fmt.Errorf("fehler beim Prüfen des Kunden: %w", err) } if customer == nil { return nil, errors.New("der angegebene Kunde existiert nicht") } project := Project{ Name: create.Name, CustomerID: create.CustomerID, } result := GetEngine(ctx).Create(&project) if result.Error != nil { return nil, fmt.Errorf("fehler beim Erstellen des Projekts: %w", result.Error) } return &project, nil } // UpdateProject aktualisiert ein bestehendes Projekt mit Validierung func UpdateProject(ctx context.Context, update ProjectUpdate) (*Project, error) { // Validierung if err := update.Validate(); err != nil { return nil, fmt.Errorf("validierungsfehler: %w", err) } project, err := GetProjectByID(ctx, update.ID) if err != nil { return nil, err } if project == nil { return nil, errors.New("project nicht gefunden") } // Wenn CustomerID aktualisiert wird, prüfen ob der Kunde existiert if update.CustomerID != nil { customer, err := GetCustomerByID(ctx, *update.CustomerID) if err != nil { return nil, fmt.Errorf("fehler beim Prüfen des Kunden: %w", err) } if customer == nil { return nil, errors.New("der angegebene Kunde existiert nicht") } } // Generische Update-Funktion verwenden if err := UpdateModel(ctx, project, update); err != nil { return nil, fmt.Errorf("fehler beim Aktualisieren des Projekts: %w", err) } // Aktualisierte Daten aus der Datenbank laden return GetProjectByID(ctx, update.ID) } // DeleteProject löscht ein Projekt anhand seiner ID func DeleteProject(ctx context.Context, id ulid.ULID) error { // Hier könnte man prüfen, ob abhängige Entitäten existieren result := GetEngine(ctx).Delete(&Project{}, id) if result.Error != nil { return fmt.Errorf("fehler beim Löschen des Projekts: %w", result.Error) } return nil } // CreateProjectWithTransaction erstellt ein Projekt innerhalb einer Transaktion func CreateProjectWithTransaction(ctx context.Context, create ProjectCreate) (*Project, error) { // Validierung if err := create.Validate(); err != nil { return nil, fmt.Errorf("validierungsfehler: %w", err) } var project *Project // Transaktion starten err := GetEngine(ctx).Transaction(func(tx *gorm.DB) error { // Kundenprüfung innerhalb der Transaktion var customer Customer if err := tx.Where("id = ?", create.CustomerID).First(&customer).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.New("der angegebene Kunde existiert nicht") } return err } // Projekt erstellen newProject := Project{ Name: create.Name, CustomerID: create.CustomerID, } if err := tx.Create(&newProject).Error; err != nil { return err } // Projekt für die Rückgabe speichern project = &newProject return nil }) if err != nil { return nil, fmt.Errorf("transaktionsfehler: %w", err) } return project, nil }