Files
omega-server/local/model/task.go
2025-07-06 19:19:36 +02:00

214 lines
6.2 KiB
Go

package model
import (
"errors"
"strings"
"gorm.io/gorm"
)
// TaskStatus represents the status of a task
type TaskStatus string
const (
TaskStatusTodo TaskStatus = "todo"
TaskStatusInProgress TaskStatus = "in_progress"
TaskStatusDone TaskStatus = "done"
TaskStatusCanceled TaskStatus = "canceled"
)
// TaskPriority represents the priority of a task
type TaskPriority string
const (
TaskPriorityLow TaskPriority = "low"
TaskPriorityMedium TaskPriority = "medium"
TaskPriorityHigh TaskPriority = "high"
)
// Task represents a task in the system
type Task struct {
BaseModel
Title string `json:"title" gorm:"not null;type:varchar(255)"`
Description string `json:"description" gorm:"type:text"`
Status TaskStatus `json:"status" gorm:"not null;default:'todo';type:varchar(20)"`
Priority TaskPriority `json:"priority" gorm:"not null;default:'medium';type:varchar(20)"`
ProjectID string `json:"project_id" gorm:"not null;type:uuid;index;references:projects(id);onDelete:CASCADE"`
DueDate *string `json:"due_date" gorm:"type:date"`
Project Project `json:"project,omitempty" gorm:"foreignKey:ProjectID"`
Assignees []User `json:"assignees,omitempty" gorm:"many2many:task_assignees;"`
}
// TaskCreateRequest represents the request to create a new task
type TaskCreateRequest struct {
Title string `json:"title" validate:"required,min=1,max=255"`
Description string `json:"description" validate:"max=1000"`
Status TaskStatus `json:"status" validate:"omitempty,oneof=todo in_progress done canceled"`
Priority TaskPriority `json:"priority" validate:"omitempty,oneof=low medium high"`
ProjectID string `json:"project_id" validate:"required,uuid"`
DueDate *string `json:"due_date"`
AssigneeIDs []string `json:"assignee_ids"`
}
// TaskUpdateRequest represents the request to update a task
type TaskUpdateRequest struct {
Title *string `json:"title,omitempty" validate:"omitempty,min=1,max=255"`
Description *string `json:"description,omitempty" validate:"omitempty,max=1000"`
Status *TaskStatus `json:"status,omitempty" validate:"omitempty,oneof=todo in_progress done canceled"`
Priority *TaskPriority `json:"priority,omitempty" validate:"omitempty,oneof=low medium high"`
DueDate *string `json:"due_date,omitempty"`
AssigneeIDs []string `json:"assignee_ids,omitempty"`
}
// TaskInfo represents public task information
type TaskInfo struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Status TaskStatus `json:"status"`
Priority TaskPriority `json:"priority"`
ProjectID string `json:"project_id"`
DueDate *string `json:"due_date"`
Project ProjectInfo `json:"project,omitempty"`
Assignees []UserInfo `json:"assignees,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// TaskAssignee represents the many-to-many relationship between tasks and users
type TaskAssignee struct {
TaskID string `json:"task_id" gorm:"type:uuid;primaryKey"`
UserID string `json:"user_id" gorm:"type:uuid;primaryKey"`
Task Task `json:"task,omitempty" gorm:"foreignKey:TaskID"`
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
}
// BeforeCreate is called before creating a task
func (t *Task) BeforeCreate(tx *gorm.DB) error {
t.BaseModel.BeforeCreate()
// Normalize title and description
t.Title = strings.TrimSpace(t.Title)
t.Description = strings.TrimSpace(t.Description)
// Set default values
if t.Status == "" {
t.Status = TaskStatusTodo
}
if t.Priority == "" {
t.Priority = TaskPriorityMedium
}
return t.Validate()
}
// BeforeUpdate is called before updating a task
func (t *Task) BeforeUpdate(tx *gorm.DB) error {
t.BaseModel.BeforeUpdate()
// Normalize fields if they're being updated
if t.Title != "" {
t.Title = strings.TrimSpace(t.Title)
}
if t.Description != "" {
t.Description = strings.TrimSpace(t.Description)
}
return t.Validate()
}
// Validate validates task data
func (t *Task) Validate() error {
if t.Title == "" {
return errors.New("title is required")
}
if len(t.Title) > 255 {
return errors.New("title must not exceed 255 characters")
}
if t.ProjectID == "" {
return errors.New("project_id is required")
}
// Validate status
if t.Status != "" {
switch t.Status {
case TaskStatusTodo, TaskStatusInProgress, TaskStatusDone, TaskStatusCanceled:
// Valid status
default:
return errors.New("invalid status")
}
}
// Validate priority
if t.Priority != "" {
switch t.Priority {
case TaskPriorityLow, TaskPriorityMedium, TaskPriorityHigh:
// Valid priority
default:
return errors.New("invalid priority")
}
}
return nil
}
// ToTaskInfo converts Task to TaskInfo (public information)
func (t *Task) ToTaskInfo() TaskInfo {
taskInfo := TaskInfo{
ID: t.ID,
Title: t.Title,
Description: t.Description,
Status: t.Status,
Priority: t.Priority,
ProjectID: t.ProjectID,
DueDate: t.DueDate,
CreatedAt: t.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
UpdatedAt: t.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
Assignees: make([]UserInfo, len(t.Assignees)),
}
// Add project info if loaded
if t.Project.ID != "" {
taskInfo.Project = t.Project.ToProjectInfo()
}
// Add assignee info if loaded
for i, assignee := range t.Assignees {
taskInfo.Assignees[i] = assignee.ToUserInfo()
}
return taskInfo
}
// IsAssignedTo checks if a user is assigned to this task
func (t *Task) IsAssignedTo(userID string) bool {
for _, assignee := range t.Assignees {
if assignee.ID == userID {
return true
}
}
return false
}
// IsCompleted checks if the task is completed
func (t *Task) IsCompleted() bool {
return t.Status == TaskStatusDone
}
// IsCanceled checks if the task is canceled
func (t *Task) IsCanceled() bool {
return t.Status == TaskStatusCanceled
}
// IsInProgress checks if the task is in progress
func (t *Task) IsInProgress() bool {
return t.Status == TaskStatusInProgress
}
// IsTodo checks if the task is todo
func (t *Task) IsTodo() bool {
return t.Status == TaskStatusTodo
}