package models import ( "time" "gorm.io/gorm" ) type Task struct { ID uint `json:"id" gorm:"primaryKey"` StoryID *uint `json:"story_id"` ProjectID uint `json:"project_id"` Title string `json:"title" gorm:"not null"` Description string `json:"description"` Status string `json:"status" gorm:"default:'todo'"` Priority string `json:"priority" gorm:"default:'medium'"` TaskType string `json:"task_type" gorm:"default:'feature'"` AssigneeID *uint `json:"assignee_id"` ReporterID uint `json:"reporter_id"` EstimatedHours *float64 `json:"estimated_hours"` ActualHours float64 `json:"actual_hours" gorm:"default:0"` DueDate *time.Time `json:"due_date"` Story *Story `json:"story" gorm:"foreignKey:StoryID"` Project Project `json:"project" gorm:"foreignKey:ProjectID"` Assignee *User `json:"assignee" gorm:"foreignKey:AssigneeID"` Reporter User `json:"reporter" gorm:"foreignKey:ReporterID"` GiteaRelations []GiteaRelation `json:"gitea_relations" gorm:"foreignKey:TaskID"` TimeLogs []TimeLog `json:"time_logs" gorm:"foreignKey:TaskID"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type TaskStatus string const ( TaskStatusTodo TaskStatus = "todo" TaskStatusInProgress TaskStatus = "in_progress" TaskStatusReview TaskStatus = "review" TaskStatusTesting TaskStatus = "testing" TaskStatusDone TaskStatus = "done" TaskStatusBlocked TaskStatus = "blocked" ) type TaskType string const ( TaskTypeFeature TaskType = "feature" TaskTypeBug TaskType = "bug" TaskTypeImprovement TaskType = "improvement" TaskTypeResearch TaskType = "research" TaskTypeDocumentation TaskType = "documentation" ) func (t *Task) BeforeCreate(tx *gorm.DB) error { return nil } func (t *Task) LogTime(userID uint, hours float64, description string, logDate time.Time) error { timeLog := TimeLog{ TaskID: t.ID, UserID: userID, Hours: hours, Description: description, LogDate: logDate, } if err := DB.Create(&timeLog).Error; err != nil { return err } t.ActualHours += hours return DB.Save(t).Error } func (t *Task) LinkGiteaObject(repoID uint, repoName, relationType, objectID string, objectNumber *uint, title, url string) error { relation := GiteaRelation{ TaskID: t.ID, GiteaRepoID: repoID, GiteaRepoName: repoName, RelationType: relationType, GiteaObjectID: objectID, GiteaObjectNumber: objectNumber, GiteaObjectTitle: title, GiteaObjectURL: url, } return DB.Create(&relation).Error } func CreateTask(task *Task) error { return DB.Create(task).Error } func GetTaskByID(id uint) (*Task, error) { var task Task err := DB.Preload("Story").Preload("Project").Preload("Assignee"). Preload("Reporter").Preload("GiteaRelations").Preload("TimeLogs"). First(&task, id).Error return &task, err } func GetTasksByProject(projectID uint) ([]Task, error) { var tasks []Task err := DB.Where("project_id = ?", projectID). Preload("Story").Preload("Assignee").Preload("Reporter"). Find(&tasks).Error return tasks, err } func GetTasksByStory(storyID uint) ([]Task, error) { var tasks []Task err := DB.Where("story_id = ?", storyID). Preload("Assignee").Preload("Reporter"). Find(&tasks).Error return tasks, err } func GetTasksByAssignee(assigneeID uint) ([]Task, error) { var tasks []Task err := DB.Where("assignee_id = ?", assigneeID). Preload("Story").Preload("Project"). Find(&tasks).Error return tasks, err } func UpdateTask(task *Task) error { return DB.Save(task).Error } func DeleteTask(id uint) error { return DB.Delete(&Task{}, id).Error } func ListTasks(offset, limit int, filters map[string]interface{}) ([]Task, int64, error) { var tasks []Task var total int64 query := DB.Model(&Task{}) for key, value := range filters { switch key { case "project_id": query = query.Where("project_id = ?", value) case "story_id": query = query.Where("story_id = ?", value) case "status": query = query.Where("status = ?", value) case "priority": query = query.Where("priority = ?", value) case "task_type": query = query.Where("task_type = ?", 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("Story").Preload("Project").Preload("Assignee"). Preload("Reporter").Preload("GiteaRelations"). Offset(offset).Limit(limit).Find(&tasks).Error return tasks, total, err }