You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

339 lines
9.9 KiB

package statistics
import (
"net/http"
"strconv"
"time"
"task-track-backend/model"
"task-track-backend/pkg/database"
"github.com/gin-gonic/gin"
)
// TaskStatisticsResponse 任务统计响应
type TaskStatisticsResponse struct {
// 基本统计
TotalTasks int64 `json:"total_tasks"`
PendingTasks int64 `json:"pending_tasks"`
InProgressTasks int64 `json:"in_progress_tasks"`
CompletedTasks int64 `json:"completed_tasks"`
CancelledTasks int64 `json:"cancelled_tasks"`
// 按状态统计
TasksByStatus []TaskStatusStat `json:"tasks_by_status"`
// 按优先级统计
TasksByPriority []TaskPriorityStat `json:"tasks_by_priority"`
// 按类型统计
TasksByType []TaskTypeStat `json:"tasks_by_type"`
// 按创建者统计
TasksByCreator []TaskCreatorStat `json:"tasks_by_creator"`
// 按分配者统计
TasksByAssignee []TaskAssigneeStat `json:"tasks_by_assignee"`
// 时间趋势统计(最近30天)
DailyTrend []DailyTaskStat `json:"daily_trend"`
// 完成率统计
CompletionRate float64 `json:"completion_rate"`
// 平均完成时间(小时)
AverageCompletionTime float64 `json:"average_completion_time"`
// 逾期任务统计
OverdueTasks int64 `json:"overdue_tasks"`
// 即将到期任务统计(7天内)
UpcomingTasks int64 `json:"upcoming_tasks"`
}
// TaskStatusStat 任务状态统计
type TaskStatusStat struct {
Status string `json:"status"`
Count int64 `json:"count"`
Percentage float64 `json:"percentage"`
}
// TaskPriorityStat 任务优先级统计
type TaskPriorityStat struct {
Priority string `json:"priority"`
Count int64 `json:"count"`
Percentage float64 `json:"percentage"`
}
// TaskTypeStat 任务类型统计
type TaskTypeStat struct {
Type string `json:"type"`
Count int64 `json:"count"`
Percentage float64 `json:"percentage"`
}
// TaskCreatorStat 任务创建者统计
type TaskCreatorStat struct {
CreatorID uint `json:"creator_id"`
CreatorName string `json:"creator_name"`
Count int64 `json:"count"`
}
// TaskAssigneeStat 任务分配者统计
type TaskAssigneeStat struct {
AssigneeID uint `json:"assignee_id"`
AssigneeName string `json:"assignee_name"`
Count int64 `json:"count"`
}
// DailyTaskStat 每日任务统计
type DailyTaskStat struct {
Date string `json:"date"`
Created int64 `json:"created"`
Completed int64 `json:"completed"`
}
func (h *StatisticsHandler) GetTaskStatistics(c *gin.Context) {
// 获取组织ID参数(可选)
organizationIDStr := c.Query("organization_id")
var organizationID uint
if organizationIDStr != "" {
if id, err := strconv.ParseUint(organizationIDStr, 10, 32); err == nil {
organizationID = uint(id)
}
}
// 获取时间范围参数
daysStr := c.DefaultQuery("days", "30")
days, err := strconv.Atoi(daysStr)
if err != nil {
days = 30
}
db := database.GetDB()
var response TaskStatisticsResponse
// 基础查询构建器
baseTaskQuery := db.Model(&model.Task{})
if organizationID > 0 {
baseTaskQuery = baseTaskQuery.Where("organization_id = ?", organizationID)
}
// 1. 基本统计
if err := baseTaskQuery.Count(&response.TotalTasks).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取任务总数失败"})
return
}
// 各状态任务数
if err := baseTaskQuery.Where("status = ?", "pending").Count(&response.PendingTasks).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取待处理任务数失败"})
return
}
if err := baseTaskQuery.Where("status = ?", "in_progress").Count(&response.InProgressTasks).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取进行中任务数失败"})
return
}
if err := baseTaskQuery.Where("status = ?", "completed").Count(&response.CompletedTasks).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取已完成任务数失败"})
return
}
if err := baseTaskQuery.Where("status = ?", "cancelled").Count(&response.CancelledTasks).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取已取消任务数失败"})
return
}
// 2. 按状态统计
var statusStats []struct {
Status string `json:"status"`
Count int64 `json:"count"`
}
statusQuery := baseTaskQuery.Select("status, count(*) as count").Group("status")
if err := statusQuery.Find(&statusStats).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取任务状态统计失败"})
return
}
response.TasksByStatus = make([]TaskStatusStat, len(statusStats))
for i, stat := range statusStats {
percentage := float64(0)
if response.TotalTasks > 0 {
percentage = float64(stat.Count) / float64(response.TotalTasks) * 100
}
response.TasksByStatus[i] = TaskStatusStat{
Status: stat.Status,
Count: stat.Count,
Percentage: percentage,
}
}
// 3. 按优先级统计
var priorityStats []struct {
Priority string `json:"priority"`
Count int64 `json:"count"`
}
priorityQuery := baseTaskQuery.Select("priority, count(*) as count").Group("priority")
if err := priorityQuery.Find(&priorityStats).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取任务优先级统计失败"})
return
}
response.TasksByPriority = make([]TaskPriorityStat, len(priorityStats))
for i, stat := range priorityStats {
percentage := float64(0)
if response.TotalTasks > 0 {
percentage = float64(stat.Count) / float64(response.TotalTasks) * 100
}
response.TasksByPriority[i] = TaskPriorityStat{
Priority: stat.Priority,
Count: stat.Count,
Percentage: percentage,
}
}
// 4. 按类型统计
var typeStats []struct {
Type string `json:"type"`
Count int64 `json:"count"`
}
typeQuery := baseTaskQuery.Select("type, count(*) as count").Group("type")
if err := typeQuery.Find(&typeStats).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取任务类型统计失败"})
return
}
response.TasksByType = make([]TaskTypeStat, len(typeStats))
for i, stat := range typeStats {
percentage := float64(0)
if response.TotalTasks > 0 {
percentage = float64(stat.Count) / float64(response.TotalTasks) * 100
}
response.TasksByType[i] = TaskTypeStat{
Type: stat.Type,
Count: stat.Count,
Percentage: percentage,
}
}
// 5. 按创建者统计
var creatorStats []struct {
CreatorID uint `json:"creator_id"`
CreatorName string `json:"creator_name"`
Count int64 `json:"count"`
}
creatorQuery := baseTaskQuery.Select("creator_id, users.real_name as creator_name, count(*) as count").
Joins("LEFT JOIN users ON tasks.creator_id = users.id").
Group("creator_id, users.real_name").
Order("count desc").
Limit(10)
if err := creatorQuery.Find(&creatorStats).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取创建者统计失败"})
return
}
response.TasksByCreator = make([]TaskCreatorStat, len(creatorStats))
for i, stat := range creatorStats {
response.TasksByCreator[i] = TaskCreatorStat{
CreatorID: stat.CreatorID,
CreatorName: stat.CreatorName,
Count: stat.Count,
}
}
// 6. 按分配者统计
var assigneeStats []struct {
AssigneeID uint `json:"assignee_id"`
AssigneeName string `json:"assignee_name"`
Count int64 `json:"count"`
}
assigneeQuery := baseTaskQuery.Select("assignee_id, users.real_name as assignee_name, count(*) as count").
Joins("LEFT JOIN users ON tasks.assignee_id = users.id").
Where("assignee_id IS NOT NULL").
Group("assignee_id, users.real_name").
Order("count desc").
Limit(10)
if err := assigneeQuery.Find(&assigneeStats).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取分配者统计失败"})
return
}
response.TasksByAssignee = make([]TaskAssigneeStat, len(assigneeStats))
for i, stat := range assigneeStats {
response.TasksByAssignee[i] = TaskAssigneeStat{
AssigneeID: stat.AssigneeID,
AssigneeName: stat.AssigneeName,
Count: stat.Count,
}
}
// 7. 时间趋势统计
now := time.Now()
startDate := now.AddDate(0, 0, -days)
response.DailyTrend = make([]DailyTaskStat, 0)
for i := 0; i < days; i++ {
date := startDate.AddDate(0, 0, i)
dateStr := date.Format("2006-01-02")
// 当天创建的任务数
var createdCount int64
createdQuery := baseTaskQuery.Where("DATE(created_at) = ?", dateStr)
if err := createdQuery.Count(&createdCount).Error; err != nil {
createdCount = 0
}
// 当天完成的任务数
var completedCount int64
completedQuery := baseTaskQuery.Where("status = ? AND DATE(updated_at) = ?", "completed", dateStr)
if err := completedQuery.Count(&completedCount).Error; err != nil {
completedCount = 0
}
response.DailyTrend = append(response.DailyTrend, DailyTaskStat{
Date: dateStr,
Created: createdCount,
Completed: completedCount,
})
}
// 8. 完成率
if response.TotalTasks > 0 {
response.CompletionRate = float64(response.CompletedTasks) / float64(response.TotalTasks) * 100
}
// 9. 平均完成时间
var avgCompletionTime struct {
AverageHours float64 `json:"average_hours"`
}
avgQuery := baseTaskQuery.Select("AVG(TIMESTAMPDIFF(HOUR, created_at, updated_at)) as average_hours").
Where("status = ?", "completed")
if err := avgQuery.Scan(&avgCompletionTime).Error; err == nil {
response.AverageCompletionTime = avgCompletionTime.AverageHours
}
// 10. 逾期任务统计
now = time.Now()
overdueQuery := baseTaskQuery.Where("end_time < ? AND status != ?", now, "completed")
if err := overdueQuery.Count(&response.OverdueTasks).Error; err != nil {
response.OverdueTasks = 0
}
// 11. 即将到期任务统计
sevenDaysLater := now.AddDate(0, 0, 7)
upcomingQuery := baseTaskQuery.Where("end_time BETWEEN ? AND ? AND status != ?", now, sevenDaysLater, "completed")
if err := upcomingQuery.Count(&response.UpcomingTasks).Error; err != nil {
response.UpcomingTasks = 0
}
c.JSON(http.StatusOK, response)
}