implement graphQL and init postgres
This commit is contained in:
213
local/model/task.go
Normal file
213
local/model/task.go
Normal file
@@ -0,0 +1,213 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user