Files
gitpm/internal/models/task.go
huxunan 885fad6c64 Initial commit: Gitea Project Management System
Features:
- Complete project management system with Epic/Story/Task hierarchy
- Vue.js 3 + Element Plus frontend with kanban board
- Go backend with Gin framework and GORM
- OAuth2 integration with Gitea
- Docker containerization with MySQL
- RESTful API for project, task, and user management
- JWT authentication and authorization
- Responsive web interface with dashboard
2025-09-22 14:53:53 +08:00

166 lines
4.7 KiB
Go

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
}