Files
gitpm/internal/models/sprint.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

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
}