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:
139
internal/models/sprint.go
Normal file
139
internal/models/sprint.go
Normal file
@@ -0,0 +1,139 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user