
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
139 lines
3.3 KiB
Go
139 lines
3.3 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type Sprint struct {
|
|
ID uint `json:"id" gorm:"primaryKey"`
|
|
ProjectID uint `json:"project_id"`
|
|
Name string `json:"name" gorm:"not null"`
|
|
Description string `json:"description"`
|
|
Status string `json:"status" gorm:"default:'planning'"`
|
|
StartDate *time.Time `json:"start_date"`
|
|
EndDate *time.Time `json:"end_date"`
|
|
Goal string `json:"goal"`
|
|
Project Project `json:"project" gorm:"foreignKey:ProjectID"`
|
|
Stories []Story `json:"stories" gorm:"foreignKey:SprintID"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
type SprintStatus string
|
|
|
|
const (
|
|
SprintStatusPlanning SprintStatus = "planning"
|
|
SprintStatusActive SprintStatus = "active"
|
|
SprintStatusCompleted SprintStatus = "completed"
|
|
)
|
|
|
|
func (s *Sprint) BeforeCreate(tx *gorm.DB) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *Sprint) GetVelocity() (int, error) {
|
|
var totalStoryPoints int
|
|
err := DB.Model(&Story{}).
|
|
Where("sprint_id = ? AND status = 'done'", s.ID).
|
|
Select("COALESCE(SUM(story_points), 0)").
|
|
Scan(&totalStoryPoints).Error
|
|
return totalStoryPoints, err
|
|
}
|
|
|
|
func (s *Sprint) GetBurndownData() ([]map[string]interface{}, error) {
|
|
var stories []Story
|
|
err := DB.Where("sprint_id = ?", s.ID).Find(&stories).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var burndownData []map[string]interface{}
|
|
|
|
// Calculate total story points
|
|
totalPoints := 0
|
|
for _, story := range stories {
|
|
if story.StoryPoints != nil {
|
|
totalPoints += *story.StoryPoints
|
|
}
|
|
}
|
|
|
|
// This is a simplified version - in a real implementation,
|
|
// you'd track daily progress
|
|
burndownData = append(burndownData, map[string]interface{}{
|
|
"date": s.StartDate,
|
|
"remaining": totalPoints,
|
|
"ideal": totalPoints,
|
|
})
|
|
|
|
if s.EndDate != nil {
|
|
burndownData = append(burndownData, map[string]interface{}{
|
|
"date": s.EndDate,
|
|
"remaining": 0,
|
|
"ideal": 0,
|
|
})
|
|
}
|
|
|
|
return burndownData, nil
|
|
}
|
|
|
|
func CreateSprint(sprint *Sprint) error {
|
|
return DB.Create(sprint).Error
|
|
}
|
|
|
|
func GetSprintByID(id uint) (*Sprint, error) {
|
|
var sprint Sprint
|
|
err := DB.Preload("Project").Preload("Stories.Assignee").
|
|
First(&sprint, id).Error
|
|
return &sprint, err
|
|
}
|
|
|
|
func GetSprintsByProject(projectID uint) ([]Sprint, error) {
|
|
var sprints []Sprint
|
|
err := DB.Where("project_id = ?", projectID).
|
|
Preload("Stories").
|
|
Find(&sprints).Error
|
|
return sprints, err
|
|
}
|
|
|
|
func GetActiveSprintByProject(projectID uint) (*Sprint, error) {
|
|
var sprint Sprint
|
|
err := DB.Where("project_id = ? AND status = 'active'", projectID).
|
|
Preload("Stories.Assignee").
|
|
First(&sprint).Error
|
|
return &sprint, err
|
|
}
|
|
|
|
func UpdateSprint(sprint *Sprint) error {
|
|
return DB.Save(sprint).Error
|
|
}
|
|
|
|
func DeleteSprint(id uint) error {
|
|
return DB.Delete(&Sprint{}, id).Error
|
|
}
|
|
|
|
func ListSprints(offset, limit int, filters map[string]interface{}) ([]Sprint, int64, error) {
|
|
var sprints []Sprint
|
|
var total int64
|
|
|
|
query := DB.Model(&Sprint{})
|
|
|
|
for key, value := range filters {
|
|
switch key {
|
|
case "project_id":
|
|
query = query.Where("project_id = ?", value)
|
|
case "status":
|
|
query = query.Where("status = ?", value)
|
|
}
|
|
}
|
|
|
|
err := query.Count(&total).Error
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
err = query.Preload("Project").
|
|
Offset(offset).Limit(limit).Find(&sprints).Error
|
|
return sprints, total, err
|
|
} |