package handler import ( "fmt" "strconv" "time" "xboard-go/internal/database" "xboard-go/internal/model" "github.com/gin-gonic/gin" ) // AdminGetTrafficRank returns traffic ranking for nodes or users. func AdminGetTrafficRank(c *gin.Context) { rankType := c.Query("type") // node or user startTimeStr := c.Query("start_time") endTimeStr := c.Query("end_time") startTime, _ := strconv.ParseInt(startTimeStr, 10, 64) endTime, _ := strconv.ParseInt(endTimeStr, 10, 64) if startTime == 0 { startTime = time.Now().AddDate(0, 0, -7).Unix() } if endTime == 0 { endTime = time.Now().Unix() } periodDuration := endTime - startTime if periodDuration <= 0 { periodDuration = 86400 } previousStart := startTime - periodDuration previousEnd := startTime var result []gin.H if rankType == "user" { type userRank struct { ID int `json:"id"` Value int64 `json:"value"` } var ranks []userRank database.DB.Model(&model.StatUser{}). Select("user_id as id, SUM(u + d) as value"). Where("record_at >= ? AND record_at <= ?", startTime, endTime). Group("user_id"). Order("value DESC"). Limit(10). Scan(&ranks) var userIDs []int for _, r := range ranks { userIDs = append(userIDs, r.ID) } userEmails := loadUserEmailMap(userIDs) previousValues := loadUserPreviousTrafficMap(userIDs, previousStart, previousEnd) for _, r := range ranks { previousValue := previousValues[r.ID] result = append(result, gin.H{ "id": fmt.Sprintf("%d", r.ID), "name": userEmails[r.ID], "value": r.Value, "previousValue": previousValue, "change": calculateGrowth(r.Value, previousValue), }) } } else { // Default to node type nodeRank struct { ID int `json:"id"` Value int64 `json:"value"` Name string `json:"name"` } var ranks []nodeRank database.DB.Model(&model.StatServer{}). Select("server_id as id, SUM(u + d) as value"). Where("record_at >= ? AND record_at <= ?", startTime, endTime). Group("server_id"). Order("value DESC"). Limit(10). Scan(&ranks) var nodeIDs []int for _, r := range ranks { nodeIDs = append(nodeIDs, r.ID) } nodeNames := loadNodeNameMap(nodeIDs) previousValues := loadNodePreviousTrafficMap(nodeIDs, previousStart, previousEnd) for _, r := range ranks { previousValue := previousValues[r.ID] result = append(result, gin.H{ "id": fmt.Sprintf("%d", r.ID), "name": nodeNames[r.ID], "value": r.Value, "previousValue": previousValue, "change": calculateGrowth(r.Value, previousValue), }) } } Success(c, result) } // AdminGetOrderStats returns order-related statistics for charts. func AdminGetOrderStats(c *gin.Context) { startDateStr := c.Query("start_date") endDateStr := c.Query("end_date") statType := c.Query("type") // paid_total, paid_count, etc. var startDate int64 var endDate int64 if startDateStr != "" { t, _ := time.Parse("2006-01-02", startDateStr) startDate = t.Unix() } else { startDate = time.Now().AddDate(0, 0, -30).Truncate(24 * time.Hour).Unix() } if endDateStr != "" { t, _ := time.Parse("2006-01-02", endDateStr) endDate = t.Unix() + 86399 } else { endDate = time.Now().Unix() } var stats []model.Stat database.DB.Where("record_at >= ? AND record_at <= ? AND record_type = ?", startDate, endDate, "d"). Order("record_at ASC"). Find(&stats) var list []gin.H var paidTotal int64 var paidCount int64 var commissionTotal int64 var commissionCount int64 for _, s := range stats { dateStr := time.Unix(s.RecordAt, 0).Format("2006-01-02") item := gin.H{ "date": dateStr, } if statType != "" { item["value"] = getStatFieldValue(s, statType) } else { item["paid_total"] = s.PaidTotal item["paid_count"] = s.PaidCount item["commission_total"] = s.CommissionTotal item["commission_count"] = s.CommissionCount } paidTotal += s.PaidTotal paidCount += int64(s.PaidCount) commissionTotal += s.CommissionTotal commissionCount += int64(s.CommissionCount) list = append(list, item) } avgPaidAmount := int64(0) if paidCount > 0 { avgPaidAmount = paidTotal / paidCount } commissionRate := 0.0 if paidTotal > 0 { commissionRate = float64(commissionTotal) / float64(paidTotal) * 100.0 } Success(c, gin.H{ "list": list, "summary": gin.H{ "start_date": time.Unix(startDate, 0).Format("2006-01-02"), "end_date": time.Unix(endDate, 0).Format("2006-01-02"), "paid_total": paidTotal, "paid_count": paidCount, "avg_paid_amount": avgPaidAmount, "commission_total": commissionTotal, "commission_count": commissionCount, "commission_rate": commissionRate, }, }) } func loadUserPreviousTrafficMap(ids []int, startTime int64, endTime int64) map[int]int64 { result := make(map[int]int64) if len(ids) == 0 { return result } type userRank struct { ID int `json:"id"` Value int64 `json:"value"` } var ranks []userRank database.DB.Model(&model.StatUser{}). Select("user_id as id, SUM(u + d) as value"). Where("user_id IN ? AND record_at >= ? AND record_at <= ?", ids, startTime, endTime). Group("user_id"). Scan(&ranks) for _, rank := range ranks { result[rank.ID] = rank.Value } return result } func loadNodePreviousTrafficMap(ids []int, startTime int64, endTime int64) map[int]int64 { result := make(map[int]int64) if len(ids) == 0 { return result } type nodeRank struct { ID int `json:"id"` Value int64 `json:"value"` } var ranks []nodeRank database.DB.Model(&model.StatServer{}). Select("server_id as id, SUM(u + d) as value"). Where("server_id IN ? AND record_at >= ? AND record_at <= ?", ids, startTime, endTime). Group("server_id"). Scan(&ranks) for _, rank := range ranks { result[rank.ID] = rank.Value } return result } func loadNodeNameMap(ids []int) map[int]string { result := make(map[int]string) if len(ids) == 0 { return result } var servers []model.Server database.DB.Select("id", "name").Where("id IN ?", ids).Find(&servers) for _, s := range servers { result[s.ID] = s.Name } return result } func getStatFieldValue(s model.Stat, field string) any { switch field { case "paid_total": return s.PaidTotal case "paid_count": return s.PaidCount case "commission_total": return s.CommissionTotal case "commission_count": return s.CommissionCount case "register_count": return s.RegisterCount default: return 0 } }