package models import ( "time" "gorm.io/gorm" ) type Story struct { ID uint `json:"id" gorm:"primaryKey"` EpicID *uint `json:"epic_id"` ProjectID uint `json:"project_id"` Title string `json:"title" gorm:"not null"` Description string `json:"description"` AcceptanceCriteria string `json:"acceptance_criteria"` Status string `json:"status" gorm:"default:'backlog'"` Priority string `json:"priority" gorm:"default:'medium'"` StoryPoints *int `json:"story_points"` AssigneeID *uint `json:"assignee_id"` ReporterID uint `json:"reporter_id"` SprintID *uint `json:"sprint_id"` EstimatedHours *float64 `json:"estimated_hours"` ActualHours float64 `json:"actual_hours" gorm:"default:0"` Epic *Epic `json:"epic" gorm:"foreignKey:EpicID"` Project Project `json:"project" gorm:"foreignKey:ProjectID"` Assignee *User `json:"assignee" gorm:"foreignKey:AssigneeID"` Reporter User `json:"reporter" gorm:"foreignKey:ReporterID"` Sprint *Sprint `json:"sprint" gorm:"foreignKey:SprintID"` Tasks []Task `json:"tasks" gorm:"foreignKey:StoryID"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type StoryStatus string const ( StoryStatusBacklog StoryStatus = "backlog" StoryStatusTodo StoryStatus = "todo" StoryStatusInProgress StoryStatus = "in_progress" StoryStatusReview StoryStatus = "review" StoryStatusTesting StoryStatus = "testing" StoryStatusDone StoryStatus = "done" StoryStatusCancelled StoryStatus = "cancelled" ) func (s *Story) BeforeCreate(tx *gorm.DB) error { return nil } func (s *Story) GetTasks() ([]Task, error) { var tasks []Task err := DB.Where("story_id = ?", s.ID).Preload("Assignee").Find(&tasks).Error return tasks, err } func (s *Story) UpdateProgress() error { var totalTasks int64 var completedTasks int64 err := DB.Model(&Task{}).Where("story_id = ?", s.ID).Count(&totalTasks).Error if err != nil { return err } if totalTasks == 0 { return nil } err = DB.Model(&Task{}).Where("story_id = ? AND status = 'done'", s.ID).Count(&completedTasks).Error if err != nil { return err } if completedTasks == totalTasks { s.Status = string(StoryStatusDone) } else if completedTasks > 0 { s.Status = string(StoryStatusInProgress) } return DB.Save(s).Error } func CreateStory(story *Story) error { return DB.Create(story).Error } func GetStoryByID(id uint) (*Story, error) { var story Story err := DB.Preload("Epic").Preload("Project").Preload("Assignee"). Preload("Reporter").Preload("Sprint").Preload("Tasks"). First(&story, id).Error return &story, err } func GetStoriesByProject(projectID uint) ([]Story, error) { var stories []Story err := DB.Where("project_id = ?", projectID). Preload("Epic").Preload("Assignee").Preload("Reporter"). Find(&stories).Error return stories, err } func GetStoriesByEpic(epicID uint) ([]Story, error) { var stories []Story err := DB.Where("epic_id = ?", epicID). Preload("Assignee").Preload("Reporter"). Find(&stories).Error return stories, err } func GetStoriesBySprint(sprintID uint) ([]Story, error) { var stories []Story err := DB.Where("sprint_id = ?", sprintID). Preload("Assignee").Preload("Reporter"). Find(&stories).Error return stories, err } func UpdateStory(story *Story) error { return DB.Save(story).Error } func DeleteStory(id uint) error { return DB.Delete(&Story{}, id).Error } func ListStories(offset, limit int, filters map[string]interface{}) ([]Story, int64, error) { var stories []Story var total int64 query := DB.Model(&Story{}) for key, value := range filters { switch key { case "project_id": query = query.Where("project_id = ?", value) case "epic_id": query = query.Where("epic_id = ?", value) case "sprint_id": query = query.Where("sprint_id = ?", value) case "status": query = query.Where("status = ?", value) case "priority": query = query.Where("priority = ?", value) case "assignee_id": query = query.Where("assignee_id = ?", value) } } err := query.Count(&total).Error if err != nil { return nil, 0, err } err = query.Preload("Epic").Preload("Project").Preload("Assignee"). Preload("Reporter").Preload("Sprint"). Offset(offset).Limit(limit).Find(&stories).Error return stories, total, err }