package services import ( "context" "fmt" "fwh-toolbox-server/cache" "fwh-toolbox-server/domain" "fwh-toolbox-server/repositories" "time" "go.uber.org/zap" ) // 从数据库获取shutdown统计最新日期 func GetShutdownStatisticsDate() (*string, error) { return getDashboardDate() } // 从数据库获取shutdown统计 func GetShutdownStatistics(date *string) (*domain.ShutdownStatisticsDashboard, error) { currDate, err := getDashboardDate() if err != nil { return nil, err } var weekly *domain.ShutdownStatisticsArea[[7]uint] var yearly *domain.ShutdownStatisticsArea[[12]uint] if date == nil { date = currDate } weekly, err = getDashboardWeekly(*date) if err != nil { return nil, err } yearly, err = getDashboardYearly(*date) if err != nil { return nil, err } return &domain.ShutdownStatisticsDashboard{ Date: *date, Weekly: *weekly, Yearly: *yearly, }, nil } // 从数据库获取shutdown统计信息详细数据 func GetShutdownInfoMap(date *string) (*domain.ShutdownStatisticsItem, error) { currDate, err := getDashboardDate() if err != nil { return nil, err } if date == nil { date = currDate } return repositories.GetStatisticsShutdown(*date) } // 对Dashboard常规数据做缓存 const ( RedisKeyDashboardDate = "fwhtoolbox:dashboard:date" RedisKeyDashboardWeekly = "fwhtoolbox:dashboard:weekly" RedisKeyDashboardYearly = "fwhtoolbox:dashboard:yearly" ) func getDashboardDate() (*string, error) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() var date string err := cache.GetJSON(ctx, RedisKeyDashboardDate, &date) if err == nil { return &date, nil // 命中缓存 } // 未命中缓存 → 查询数据库 shutdownData, err := repositories.GetLatestStatisticsShutdown() if err != nil { return nil, err } date = shutdownData.Date // 缓存结果 24h cache.SetJSON(ctx, RedisKeyDashboardDate, date, time.Hour*24) return &date, nil } func getDashboardWeekly(date string) (*domain.ShutdownStatisticsArea[[7]uint], error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // 未命中缓存 → 查询数据库 currDate, err := getDashboardDate() if err != nil { return nil, err } // 判断给定日期是否最新 if date == *currDate { var data domain.ShutdownStatisticsArea[[7]uint] err := cache.GetJSON(ctx, RedisKeyDashboardWeekly, &data) if err == nil { return &data, nil // 命中缓存 } } shutdownDataList, err := handleWeeklyData(*currDate) if err != nil { return nil, err } // 缓存结果,只缓存最新日期 if date == *currDate { cache.SetJSON(ctx, RedisKeyDashboardWeekly, shutdownDataList, time.Hour*24*7) } return shutdownDataList, nil } func getDashboardYearly(date string) (*domain.ShutdownStatisticsArea[[12]uint], error) { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // 未命中缓存 → 查询数据库 currDate, err := getDashboardDate() if err != nil { return nil, err } t_currDate, err := time.Parse(time.DateOnly, *currDate) if err != nil { return nil, err } t_date, err := time.Parse(time.DateOnly, date) if err != nil { return nil, err } // 判断是否同年 if t_currDate.Year() == t_date.Year() { var data domain.ShutdownStatisticsArea[[12]uint] err := cache.GetJSON(ctx, RedisKeyDashboardYearly, &data) if err == nil { return &data, nil // 命中缓存 } } currDateTime, err := time.Parse(time.DateOnly, *currDate) if err != nil { zap.L().Error("getDashboardYearly currDate日期格式错误", zap.String("currDate", *currDate)) return nil, err } shutdownDataList, err := handleYearlyData(currDateTime.Year()) if err != nil { return nil, err } // 缓存结果 if t_currDate.Year() == t_date.Year() { cache.SetJSON(ctx, RedisKeyDashboardYearly, shutdownDataList, time.Hour*24*365) } return shutdownDataList, nil } func handleWeeklyData(date string) (*domain.ShutdownStatisticsArea[[7]uint], error) { timeDate, err := time.Parse(time.DateOnly, date) if err != nil { zap.L().Error("handleWeeklyData日期格式错误", zap.String("date", date)) return nil, err } // 计算6天前的日期 startDate := timeDate.AddDate(0, 0, -6).Format(time.DateOnly) dataList, err := repositories.GetStatisticsShutdownByDateRange(startDate, date) if err != nil { zap.L().Error("GetStatisticsShutdownByDateRange error.", zap.String("err", err.Error())) return nil, err } var res domain.ShutdownStatisticsArea[[7]uint] for _, d := range dataList { t, err := time.Parse(time.DateOnly, d.Date) if err != nil { zap.L().Error("handleWeeklyData日期格式错误", zap.String("date", d.Date)) return nil, err } diff := timeDate.Sub(t) // 转为天数(取整) days := int(diff.Hours() / 24) idx := 6 - days if idx < 0 || idx >= 7 { continue } res.ALL[idx] = d.Count.ALL res.US[idx] = d.Count.US res.AU[idx] = d.Count.AU } return &res, nil } func handleYearlyData(year int) (*domain.ShutdownStatisticsArea[[12]uint], error) { // 判断年份是否有效 if year < 2000 || year > (time.Now().Year()+1) { return nil, fmt.Errorf("handleYearlyData年份不正确 year = %d", year) } var yearList domain.ShutdownStatisticsArea[[12]uint] for month := 1; month <= 12; month++ { // 每个月的第一天 start := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.Local) // 下个月的第一天再减1天 = 本月最后一天 end := start.AddDate(0, 1, -1) // 获取月数据 dataList, err := repositories.GetStatisticsShutdownByDateRange(start.Format(time.DateOnly), end.Format(time.DateOnly)) if err != nil { zap.L().Error("GetStatisticsShutdownByDateRange error.", zap.String("err", err.Error())) return nil, err } // 计算月总数 var monthCount domain.ShutdownStatisticsArea[uint] for _, d := range dataList { monthCount.ALL = monthCount.ALL + d.Count.ALL monthCount.US = monthCount.US + d.Count.US monthCount.AU = monthCount.AU + d.Count.AU } yearList.ALL[month-1] = monthCount.ALL yearList.US[month-1] = monthCount.US yearList.AU[month-1] = monthCount.AU } return &yearList, nil }