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
This commit is contained in:
166
internal/models/task.go
Normal file
166
internal/models/task.go
Normal file
@@ -0,0 +1,166 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user