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.
 
 
 
 
 
 

450 lines
14 KiB

package statistics
import (
"fmt"
"net/http"
"strconv"
"time"
"task-track-backend/model"
"task-track-backend/pkg/database"
"github.com/gin-gonic/gin"
)
// UserStatisticsResponse 用户统计响应
type UserStatisticsResponse struct {
// 基本统计
TotalUsers int64 `json:"total_users"`
ActiveUsers int64 `json:"active_users"` // 最近30天有活动的用户
// 用户任务统计
UserTaskStats []UserTaskStat `json:"user_task_stats"`
// 用户效率统计
UserEfficiencyStats []UserEfficiencyStat `json:"user_efficiency_stats"`
// 用户活跃度统计
UserActivityStats []UserActivityStat `json:"user_activity_stats"`
// 用户完成率排行
UserCompletionRank []UserCompletionRank `json:"user_completion_rank"`
// 用户任务分配统计
UserAssignmentStats []UserAssignmentStat `json:"user_assignment_stats"`
// 总体用户效率指标
OverallEfficiency struct {
AverageCompletionRate float64 `json:"average_completion_rate"`
AverageTasksPerUser float64 `json:"average_tasks_per_user"`
AverageCompletionTime float64 `json:"average_completion_time"`
} `json:"overall_efficiency"`
}
// UserTaskStat 用户任务统计
type UserTaskStat struct {
UserID uint `json:"user_id"`
Username string `json:"username"`
RealName string `json:"real_name"`
CreatedTasks int64 `json:"created_tasks"`
AssignedTasks int64 `json:"assigned_tasks"`
CompletedTasks int64 `json:"completed_tasks"`
PendingTasks int64 `json:"pending_tasks"`
InProgressTasks int64 `json:"in_progress_tasks"`
}
// UserEfficiencyStat 用户效率统计
type UserEfficiencyStat struct {
UserID uint `json:"user_id"`
Username string `json:"username"`
RealName string `json:"real_name"`
CompletionRate float64 `json:"completion_rate"`
AverageCompletionTime float64 `json:"average_completion_time"`
TasksCompletedThisWeek int64 `json:"tasks_completed_this_week"`
TasksCompletedThisMonth int64 `json:"tasks_completed_this_month"`
}
// UserActivityStat 用户活跃度统计
type UserActivityStat struct {
UserID uint `json:"user_id"`
Username string `json:"username"`
RealName string `json:"real_name"`
LastActivity time.Time `json:"last_activity"`
CommentsCount int64 `json:"comments_count"`
TasksCreatedThisWeek int64 `json:"tasks_created_this_week"`
TasksCreatedThisMonth int64 `json:"tasks_created_this_month"`
}
// UserCompletionRank 用户完成率排行
type UserCompletionRank struct {
UserID uint `json:"user_id"`
Username string `json:"username"`
RealName string `json:"real_name"`
CompletionRate float64 `json:"completion_rate"`
CompletedTasks int64 `json:"completed_tasks"`
TotalTasks int64 `json:"total_tasks"`
Rank int `json:"rank"`
}
// UserAssignmentStat 用户任务分配统计
type UserAssignmentStat struct {
UserID uint `json:"user_id"`
Username string `json:"username"`
RealName string `json:"real_name"`
UrgentTasks int64 `json:"urgent_tasks"`
HighPriorityTasks int64 `json:"high_priority_tasks"`
MediumPriorityTasks int64 `json:"medium_priority_tasks"`
LowPriorityTasks int64 `json:"low_priority_tasks"`
OverdueTasks int64 `json:"overdue_tasks"`
}
func (h *StatisticsHandler) GetUserStatistics(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)
}
}
// 获取用户ID参数(可选,如果提供则只查询该用户)
userIDStr := c.Query("user_id")
var userID uint
if userIDStr != "" {
if id, err := strconv.ParseUint(userIDStr, 10, 32); err == nil {
userID = uint(id)
}
}
db := database.GetDB()
var response UserStatisticsResponse
// 基础用户查询构建器
baseUserQuery := db.Model(&model.User{})
if organizationID > 0 {
baseUserQuery = baseUserQuery.Joins("JOIN user_organizations ON users.id = user_organizations.user_id").
Where("user_organizations.organization_id = ?", organizationID)
}
if userID > 0 {
baseUserQuery = baseUserQuery.Where("users.id = ?", userID)
}
// 1. 基本统计
if err := baseUserQuery.Count(&response.TotalUsers).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取用户总数失败"})
return
}
// 活跃用户数(最近30天有任务活动)
thirtyDaysAgo := time.Now().AddDate(0, 0, -30)
activeUserQuery := baseUserQuery.Joins("JOIN tasks ON users.id = tasks.creator_id OR users.id = tasks.assignee_id").
Where("tasks.created_at >= ? OR tasks.updated_at >= ?", thirtyDaysAgo, thirtyDaysAgo).
Distinct("users.id")
if err := activeUserQuery.Count(&response.ActiveUsers).Error; err != nil {
response.ActiveUsers = 0
}
// 2. 用户任务统计
var userTaskStats []UserTaskStat
// 构建用户任务统计查询
userTaskQuery := `
SELECT
u.id as user_id,
u.username,
u.real_name,
COALESCE(created_tasks.count, 0) as created_tasks,
COALESCE(assigned_tasks.count, 0) as assigned_tasks,
COALESCE(completed_tasks.count, 0) as completed_tasks,
COALESCE(pending_tasks.count, 0) as pending_tasks,
COALESCE(in_progress_tasks.count, 0) as in_progress_tasks
FROM users u
LEFT JOIN (
SELECT creator_id, COUNT(*) as count
FROM tasks
WHERE 1=1 %s
GROUP BY creator_id
) created_tasks ON u.id = created_tasks.creator_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL %s
GROUP BY assignee_id
) assigned_tasks ON u.id = assigned_tasks.assignee_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND status = 'completed' %s
GROUP BY assignee_id
) completed_tasks ON u.id = completed_tasks.assignee_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND status = 'pending' %s
GROUP BY assignee_id
) pending_tasks ON u.id = pending_tasks.assignee_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND status = 'in_progress' %s
GROUP BY assignee_id
) in_progress_tasks ON u.id = in_progress_tasks.assignee_id
WHERE 1=1 %s
ORDER BY (COALESCE(created_tasks.count, 0) + COALESCE(assigned_tasks.count, 0)) DESC
LIMIT 20
`
// 构建WHERE条件
orgCondition := ""
userCondition := ""
if organizationID > 0 {
orgCondition = "AND organization_id = " + strconv.FormatUint(uint64(organizationID), 10)
}
if userID > 0 {
userCondition = "AND u.id = " + strconv.FormatUint(uint64(userID), 10)
}
finalQuery := fmt.Sprintf(userTaskQuery, orgCondition, orgCondition, orgCondition, orgCondition, orgCondition, userCondition)
if err := db.Raw(finalQuery).Scan(&userTaskStats).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取用户任务统计失败"})
return
}
response.UserTaskStats = userTaskStats
// 3. 用户效率统计
var userEfficiencyStats []UserEfficiencyStat
now := time.Now()
weekStart := now.AddDate(0, 0, -int(now.Weekday())+1)
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
efficiencyQuery := `
SELECT
u.id as user_id,
u.username,
u.real_name,
CASE
WHEN total_assigned.count > 0 THEN
(completed_assigned.count * 100.0 / total_assigned.count)
ELSE 0
END as completion_rate,
COALESCE(avg_completion.avg_hours, 0) as average_completion_time,
COALESCE(week_completed.count, 0) as tasks_completed_this_week,
COALESCE(month_completed.count, 0) as tasks_completed_this_month
FROM users u
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL %s
GROUP BY assignee_id
) total_assigned ON u.id = total_assigned.assignee_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND status = 'completed' %s
GROUP BY assignee_id
) completed_assigned ON u.id = completed_assigned.assignee_id
LEFT JOIN (
SELECT assignee_id, AVG(TIMESTAMPDIFF(HOUR, created_at, updated_at)) as avg_hours
FROM tasks
WHERE assignee_id IS NOT NULL AND status = 'completed' %s
GROUP BY assignee_id
) avg_completion ON u.id = avg_completion.assignee_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND status = 'completed' AND updated_at >= '%s' %s
GROUP BY assignee_id
) week_completed ON u.id = week_completed.assignee_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND status = 'completed' AND updated_at >= '%s' %s
GROUP BY assignee_id
) month_completed ON u.id = month_completed.assignee_id
WHERE 1=1 %s
HAVING total_assigned.count > 0
ORDER BY completion_rate DESC
LIMIT 20
`
finalEfficiencyQuery := fmt.Sprintf(efficiencyQuery,
orgCondition, orgCondition, orgCondition,
weekStart.Format("2006-01-02"), orgCondition,
monthStart.Format("2006-01-02"), orgCondition,
userCondition)
if err := db.Raw(finalEfficiencyQuery).Scan(&userEfficiencyStats).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取用户效率统计失败"})
return
}
response.UserEfficiencyStats = userEfficiencyStats
// 4. 用户活跃度统计
var userActivityStats []UserActivityStat
activityQuery := `
SELECT
u.id as user_id,
u.username,
u.real_name,
COALESCE(MAX(GREATEST(t.created_at, t.updated_at)), u.created_at) as last_activity,
COALESCE(comments.count, 0) as comments_count,
COALESCE(week_created.count, 0) as tasks_created_this_week,
COALESCE(month_created.count, 0) as tasks_created_this_month
FROM users u
LEFT JOIN tasks t ON u.id = t.creator_id OR u.id = t.assignee_id
LEFT JOIN (
SELECT user_id, COUNT(*) as count
FROM task_comments
GROUP BY user_id
) comments ON u.id = comments.user_id
LEFT JOIN (
SELECT creator_id, COUNT(*) as count
FROM tasks
WHERE created_at >= '%s' %s
GROUP BY creator_id
) week_created ON u.id = week_created.creator_id
LEFT JOIN (
SELECT creator_id, COUNT(*) as count
FROM tasks
WHERE created_at >= '%s' %s
GROUP BY creator_id
) month_created ON u.id = month_created.creator_id
WHERE 1=1 %s
GROUP BY u.id, u.username, u.real_name, u.created_at, comments.count, week_created.count, month_created.count
ORDER BY last_activity DESC
LIMIT 20
`
finalActivityQuery := fmt.Sprintf(activityQuery,
weekStart.Format("2006-01-02"), orgCondition,
monthStart.Format("2006-01-02"), orgCondition,
userCondition)
if err := db.Raw(finalActivityQuery).Scan(&userActivityStats).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取用户活跃度统计失败"})
return
}
response.UserActivityStats = userActivityStats
// 5. 用户完成率排行
var userCompletionRank []UserCompletionRank
for i, stat := range userEfficiencyStats {
rank := UserCompletionRank{
UserID: stat.UserID,
Username: stat.Username,
RealName: stat.RealName,
CompletionRate: stat.CompletionRate,
Rank: i + 1,
}
// 获取用户的总任务数和完成任务数
for _, taskStat := range userTaskStats {
if taskStat.UserID == stat.UserID {
rank.TotalTasks = taskStat.AssignedTasks
rank.CompletedTasks = taskStat.CompletedTasks
break
}
}
userCompletionRank = append(userCompletionRank, rank)
}
response.UserCompletionRank = userCompletionRank
// 6. 用户任务分配统计
var userAssignmentStats []UserAssignmentStat
assignmentQuery := `
SELECT
u.id as user_id,
u.username,
u.real_name,
COALESCE(urgent.count, 0) as urgent_tasks,
COALESCE(high_priority.count, 0) as high_priority_tasks,
COALESCE(medium_priority.count, 0) as medium_priority_tasks,
COALESCE(low_priority.count, 0) as low_priority_tasks,
COALESCE(overdue.count, 0) as overdue_tasks
FROM users u
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND priority = 'urgent' %s
GROUP BY assignee_id
) urgent ON u.id = urgent.assignee_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND priority = 'high' %s
GROUP BY assignee_id
) high_priority ON u.id = high_priority.assignee_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND priority = 'medium' %s
GROUP BY assignee_id
) medium_priority ON u.id = medium_priority.assignee_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND priority = 'low' %s
GROUP BY assignee_id
) low_priority ON u.id = low_priority.assignee_id
LEFT JOIN (
SELECT assignee_id, COUNT(*) as count
FROM tasks
WHERE assignee_id IS NOT NULL AND end_time < NOW() AND status != 'completed' %s
GROUP BY assignee_id
) overdue ON u.id = overdue.assignee_id
WHERE 1=1 %s
ORDER BY (COALESCE(urgent.count, 0) + COALESCE(high_priority.count, 0)) DESC
LIMIT 20
`
finalAssignmentQuery := fmt.Sprintf(assignmentQuery,
orgCondition, orgCondition, orgCondition, orgCondition, orgCondition, userCondition)
if err := db.Raw(finalAssignmentQuery).Scan(&userAssignmentStats).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取用户任务分配统计失败"})
return
}
response.UserAssignmentStats = userAssignmentStats
// 7. 总体效率指标
if len(userEfficiencyStats) > 0 {
var totalCompletionRate float64
var totalTasks int64
var totalCompletionTime float64
var validTimeCount int64
for _, stat := range userEfficiencyStats {
totalCompletionRate += stat.CompletionRate
if stat.AverageCompletionTime > 0 {
totalCompletionTime += stat.AverageCompletionTime
validTimeCount++
}
}
for _, stat := range userTaskStats {
totalTasks += stat.AssignedTasks
}
response.OverallEfficiency.AverageCompletionRate = totalCompletionRate / float64(len(userEfficiencyStats))
if response.TotalUsers > 0 {
response.OverallEfficiency.AverageTasksPerUser = float64(totalTasks) / float64(response.TotalUsers)
}
if validTimeCount > 0 {
response.OverallEfficiency.AverageCompletionTime = totalCompletionTime / float64(validTimeCount)
}
}
c.JSON(http.StatusOK, response)
}