aggregateDailyTraffic 将原始节点日记录按日期聚合,填充完整的 days 天窗口,并计算图表高度比例。
(raw []nodes.NodeDailyUsage, days int)
| 304 | |
| 305 | // aggregateDailyTraffic 将原始节点日记录按日期聚合,填充完整的 days 天窗口,并计算图表高度比例。 |
| 306 | func aggregateDailyTraffic(raw []nodes.NodeDailyUsage, days int) []DailyTrafficPoint { |
| 307 | byDate := make(map[string]*DailyTrafficPoint, days) |
| 308 | for _, r := range raw { |
| 309 | p, ok := byDate[r.Date] |
| 310 | if !ok { |
| 311 | t, _ := time.Parse("2006-01-02", r.Date) |
| 312 | byDate[r.Date] = &DailyTrafficPoint{ |
| 313 | Date: r.Date, |
| 314 | Label: fmt.Sprintf("%d/%d", int(t.Month()), t.Day()), |
| 315 | } |
| 316 | p = byDate[r.Date] |
| 317 | } |
| 318 | p.UploadBytes += r.UploadBytes |
| 319 | p.DownloadBytes += r.DownloadBytes |
| 320 | p.TotalBytes += r.UploadBytes + r.DownloadBytes |
| 321 | } |
| 322 | |
| 323 | result := make([]DailyTrafficPoint, days) |
| 324 | now := time.Now().UTC() |
| 325 | for i := range result { |
| 326 | d := now.AddDate(0, 0, -(days - 1 - i)) |
| 327 | dateStr := d.Format("2006-01-02") |
| 328 | if p, ok := byDate[dateStr]; ok { |
| 329 | result[i] = *p |
| 330 | } else { |
| 331 | result[i] = DailyTrafficPoint{ |
| 332 | Date: dateStr, |
| 333 | Label: fmt.Sprintf("%d/%d", int(d.Month()), d.Day()), |
| 334 | } |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | var maxBytes int64 |
| 339 | for _, p := range result { |
| 340 | if p.TotalBytes > maxBytes { |
| 341 | maxBytes = p.TotalBytes |
| 342 | } |
| 343 | } |
| 344 | if maxBytes > 0 { |
| 345 | for i := range result { |
| 346 | result[i].HeightPct = float64(result[i].TotalBytes) / float64(maxBytes) * 100 |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | return result |
| 351 | } |
| 352 | |
| 353 | // aggregateNodePeriodStats 按节点汇总时段内的流量增量(来自 daily_usage 原始记录)。 |
| 354 | func aggregateNodePeriodStats(raw []nodes.NodeDailyUsage, nodeList []nodes.Node) []NodePeriodStat { |