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:
159
internal/models/story.go
Normal file
159
internal/models/story.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Story struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
EpicID *uint `json:"epic_id"`
|
||||
ProjectID uint `json:"project_id"`
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
AcceptanceCriteria string `json:"acceptance_criteria"`
|
||||
Status string `json:"status" gorm:"default:'backlog'"`
|
||||
Priority string `json:"priority" gorm:"default:'medium'"`
|
||||
StoryPoints *int `json:"story_points"`
|
||||
AssigneeID *uint `json:"assignee_id"`
|
||||
ReporterID uint `json:"reporter_id"`
|
||||
SprintID *uint `json:"sprint_id"`
|
||||
EstimatedHours *float64 `json:"estimated_hours"`
|
||||
ActualHours float64 `json:"actual_hours" gorm:"default:0"`
|
||||
Epic *Epic `json:"epic" gorm:"foreignKey:EpicID"`
|
||||
Project Project `json:"project" gorm:"foreignKey:ProjectID"`
|
||||
Assignee *User `json:"assignee" gorm:"foreignKey:AssigneeID"`
|
||||
Reporter User `json:"reporter" gorm:"foreignKey:ReporterID"`
|
||||
Sprint *Sprint `json:"sprint" gorm:"foreignKey:SprintID"`
|
||||
Tasks []Task `json:"tasks" gorm:"foreignKey:StoryID"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type StoryStatus string
|
||||
|
||||
const (
|
||||
StoryStatusBacklog StoryStatus = "backlog"
|
||||
StoryStatusTodo StoryStatus = "todo"
|
||||
StoryStatusInProgress StoryStatus = "in_progress"
|
||||
StoryStatusReview StoryStatus = "review"
|
||||
StoryStatusTesting StoryStatus = "testing"
|
||||
StoryStatusDone StoryStatus = "done"
|
||||
StoryStatusCancelled StoryStatus = "cancelled"
|
||||
)
|
||||
|
||||
func (s *Story) BeforeCreate(tx *gorm.DB) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Story) GetTasks() ([]Task, error) {
|
||||
var tasks []Task
|
||||
err := DB.Where("story_id = ?", s.ID).Preload("Assignee").Find(&tasks).Error
|
||||
return tasks, err
|
||||
}
|
||||
|
||||
func (s *Story) UpdateProgress() error {
|
||||
var totalTasks int64
|
||||
var completedTasks int64
|
||||
|
||||
err := DB.Model(&Task{}).Where("story_id = ?", s.ID).Count(&totalTasks).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if totalTasks == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = DB.Model(&Task{}).Where("story_id = ? AND status = 'done'", s.ID).Count(&completedTasks).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if completedTasks == totalTasks {
|
||||
s.Status = string(StoryStatusDone)
|
||||
} else if completedTasks > 0 {
|
||||
s.Status = string(StoryStatusInProgress)
|
||||
}
|
||||
|
||||
return DB.Save(s).Error
|
||||
}
|
||||
|
||||
func CreateStory(story *Story) error {
|
||||
return DB.Create(story).Error
|
||||
}
|
||||
|
||||
func GetStoryByID(id uint) (*Story, error) {
|
||||
var story Story
|
||||
err := DB.Preload("Epic").Preload("Project").Preload("Assignee").
|
||||
Preload("Reporter").Preload("Sprint").Preload("Tasks").
|
||||
First(&story, id).Error
|
||||
return &story, err
|
||||
}
|
||||
|
||||
func GetStoriesByProject(projectID uint) ([]Story, error) {
|
||||
var stories []Story
|
||||
err := DB.Where("project_id = ?", projectID).
|
||||
Preload("Epic").Preload("Assignee").Preload("Reporter").
|
||||
Find(&stories).Error
|
||||
return stories, err
|
||||
}
|
||||
|
||||
func GetStoriesByEpic(epicID uint) ([]Story, error) {
|
||||
var stories []Story
|
||||
err := DB.Where("epic_id = ?", epicID).
|
||||
Preload("Assignee").Preload("Reporter").
|
||||
Find(&stories).Error
|
||||
return stories, err
|
||||
}
|
||||
|
||||
func GetStoriesBySprint(sprintID uint) ([]Story, error) {
|
||||
var stories []Story
|
||||
err := DB.Where("sprint_id = ?", sprintID).
|
||||
Preload("Assignee").Preload("Reporter").
|
||||
Find(&stories).Error
|
||||
return stories, err
|
||||
}
|
||||
|
||||
func UpdateStory(story *Story) error {
|
||||
return DB.Save(story).Error
|
||||
}
|
||||
|
||||
func DeleteStory(id uint) error {
|
||||
return DB.Delete(&Story{}, id).Error
|
||||
}
|
||||
|
||||
func ListStories(offset, limit int, filters map[string]interface{}) ([]Story, int64, error) {
|
||||
var stories []Story
|
||||
var total int64
|
||||
|
||||
query := DB.Model(&Story{})
|
||||
|
||||
for key, value := range filters {
|
||||
switch key {
|
||||
case "project_id":
|
||||
query = query.Where("project_id = ?", value)
|
||||
case "epic_id":
|
||||
query = query.Where("epic_id = ?", value)
|
||||
case "sprint_id":
|
||||
query = query.Where("sprint_id = ?", value)
|
||||
case "status":
|
||||
query = query.Where("status = ?", value)
|
||||
case "priority":
|
||||
query = query.Where("priority = ?", 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("Epic").Preload("Project").Preload("Assignee").
|
||||
Preload("Reporter").Preload("Sprint").
|
||||
Offset(offset).Limit(limit).Find(&stories).Error
|
||||
return stories, total, err
|
||||
}
|
Reference in New Issue
Block a user