shutdownStatisticsServices.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. package services
  2. import (
  3. "context"
  4. "fmt"
  5. "fwh-toolbox-server/cache"
  6. "fwh-toolbox-server/domain"
  7. "fwh-toolbox-server/repositories"
  8. "time"
  9. "go.uber.org/zap"
  10. )
  11. // 从数据库获取shutdown统计最新日期
  12. func GetShutdownStatisticsDate() (*string, error) {
  13. return getDashboardDate()
  14. }
  15. // 从数据库获取shutdown统计
  16. func GetShutdownStatistics(date *string) (*domain.ShutdownStatisticsDashboard, error) {
  17. currDate, err := getDashboardDate()
  18. if err != nil {
  19. return nil, err
  20. }
  21. var weekly *domain.ShutdownStatisticsArea[[7]uint]
  22. var yearly *domain.ShutdownStatisticsArea[[12]uint]
  23. if date == nil {
  24. date = currDate
  25. }
  26. weekly, err = getDashboardWeekly(*date)
  27. if err != nil {
  28. return nil, err
  29. }
  30. yearly, err = getDashboardYearly(*date)
  31. if err != nil {
  32. return nil, err
  33. }
  34. return &domain.ShutdownStatisticsDashboard{
  35. Date: *date,
  36. Weekly: *weekly,
  37. Yearly: *yearly,
  38. }, nil
  39. }
  40. // 从数据库获取shutdown统计信息详细数据
  41. func GetShutdownInfoMap(date *string) (*domain.ShutdownStatisticsItem, error) {
  42. currDate, err := getDashboardDate()
  43. if err != nil {
  44. return nil, err
  45. }
  46. if date == nil {
  47. date = currDate
  48. }
  49. return repositories.GetStatisticsShutdown(*date)
  50. }
  51. // 对Dashboard常规数据做缓存
  52. const (
  53. RedisKeyDashboardDate = "fwhtoolbox:dashboard:date"
  54. RedisKeyDashboardWeekly = "fwhtoolbox:dashboard:weekly"
  55. RedisKeyDashboardYearly = "fwhtoolbox:dashboard:yearly"
  56. )
  57. func getDashboardDate() (*string, error) {
  58. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  59. defer cancel()
  60. var date string
  61. err := cache.GetJSON(ctx, RedisKeyDashboardDate, &date)
  62. if err == nil {
  63. return &date, nil // 命中缓存
  64. }
  65. // 未命中缓存 → 查询数据库
  66. shutdownData, err := repositories.GetLatestStatisticsShutdown()
  67. if err != nil {
  68. return nil, err
  69. }
  70. date = shutdownData.Date
  71. // 缓存结果 24h
  72. cache.SetJSON(ctx, RedisKeyDashboardDate, date, time.Hour*24)
  73. return &date, nil
  74. }
  75. func getDashboardWeekly(date string) (*domain.ShutdownStatisticsArea[[7]uint], error) {
  76. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  77. defer cancel()
  78. // 未命中缓存 → 查询数据库
  79. currDate, err := getDashboardDate()
  80. if err != nil {
  81. return nil, err
  82. }
  83. // 判断给定日期是否最新
  84. if date == *currDate {
  85. var data domain.ShutdownStatisticsArea[[7]uint]
  86. err := cache.GetJSON(ctx, RedisKeyDashboardWeekly, &data)
  87. if err == nil {
  88. return &data, nil // 命中缓存
  89. }
  90. }
  91. shutdownDataList, err := handleWeeklyData(*currDate)
  92. if err != nil {
  93. return nil, err
  94. }
  95. // 缓存结果,只缓存最新日期
  96. if date == *currDate {
  97. cache.SetJSON(ctx, RedisKeyDashboardWeekly, shutdownDataList, time.Hour*24*7)
  98. }
  99. return shutdownDataList, nil
  100. }
  101. func getDashboardYearly(date string) (*domain.ShutdownStatisticsArea[[12]uint], error) {
  102. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  103. defer cancel()
  104. // 未命中缓存 → 查询数据库
  105. currDate, err := getDashboardDate()
  106. if err != nil {
  107. return nil, err
  108. }
  109. t_currDate, err := time.Parse(time.DateOnly, *currDate)
  110. if err != nil {
  111. return nil, err
  112. }
  113. t_date, err := time.Parse(time.DateOnly, date)
  114. if err != nil {
  115. return nil, err
  116. }
  117. // 判断是否同年
  118. if t_currDate.Year() == t_date.Year() {
  119. var data domain.ShutdownStatisticsArea[[12]uint]
  120. err := cache.GetJSON(ctx, RedisKeyDashboardYearly, &data)
  121. if err == nil {
  122. return &data, nil // 命中缓存
  123. }
  124. }
  125. currDateTime, err := time.Parse(time.DateOnly, *currDate)
  126. if err != nil {
  127. zap.L().Error("getDashboardYearly currDate日期格式错误", zap.String("currDate", *currDate))
  128. return nil, err
  129. }
  130. shutdownDataList, err := handleYearlyData(currDateTime.Year())
  131. if err != nil {
  132. return nil, err
  133. }
  134. // 缓存结果
  135. if t_currDate.Year() == t_date.Year() {
  136. cache.SetJSON(ctx, RedisKeyDashboardYearly, shutdownDataList, time.Hour*24*365)
  137. }
  138. return shutdownDataList, nil
  139. }
  140. func handleWeeklyData(date string) (*domain.ShutdownStatisticsArea[[7]uint], error) {
  141. timeDate, err := time.Parse(time.DateOnly, date)
  142. if err != nil {
  143. zap.L().Error("handleWeeklyData日期格式错误", zap.String("date", date))
  144. return nil, err
  145. }
  146. // 计算6天前的日期
  147. startDate := timeDate.AddDate(0, 0, -6).Format(time.DateOnly)
  148. dataList, err := repositories.GetStatisticsShutdownByDateRange(startDate, date)
  149. if err != nil {
  150. zap.L().Error("GetStatisticsShutdownByDateRange error.", zap.String("err", err.Error()))
  151. return nil, err
  152. }
  153. var res domain.ShutdownStatisticsArea[[7]uint]
  154. for _, d := range dataList {
  155. t, err := time.Parse(time.DateOnly, d.Date)
  156. if err != nil {
  157. zap.L().Error("handleWeeklyData日期格式错误", zap.String("date", d.Date))
  158. return nil, err
  159. }
  160. diff := timeDate.Sub(t)
  161. // 转为天数(取整)
  162. days := int(diff.Hours() / 24)
  163. idx := 6 - days
  164. if idx < 0 || idx >= 7 {
  165. continue
  166. }
  167. res.ALL[idx] = d.Count.ALL
  168. res.US[idx] = d.Count.US
  169. res.AU[idx] = d.Count.AU
  170. }
  171. return &res, nil
  172. }
  173. func handleYearlyData(year int) (*domain.ShutdownStatisticsArea[[12]uint], error) {
  174. // 判断年份是否有效
  175. if year < 2000 || year > (time.Now().Year()+1) {
  176. return nil, fmt.Errorf("handleYearlyData年份不正确 year = %d", year)
  177. }
  178. var yearList domain.ShutdownStatisticsArea[[12]uint]
  179. for month := 1; month <= 12; month++ {
  180. // 每个月的第一天
  181. start := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.Local)
  182. // 下个月的第一天再减1天 = 本月最后一天
  183. end := start.AddDate(0, 1, -1)
  184. // 获取月数据
  185. dataList, err := repositories.GetStatisticsShutdownByDateRange(start.Format(time.DateOnly), end.Format(time.DateOnly))
  186. if err != nil {
  187. zap.L().Error("GetStatisticsShutdownByDateRange error.", zap.String("err", err.Error()))
  188. return nil, err
  189. }
  190. // 计算月总数
  191. var monthCount domain.ShutdownStatisticsArea[uint]
  192. for _, d := range dataList {
  193. monthCount.ALL = monthCount.ALL + d.Count.ALL
  194. monthCount.US = monthCount.US + d.Count.US
  195. monthCount.AU = monthCount.AU + d.Count.AU
  196. }
  197. yearList.ALL[month-1] = monthCount.ALL
  198. yearList.US[month-1] = monthCount.US
  199. yearList.AU[month-1] = monthCount.AU
  200. }
  201. return &yearList, nil
  202. }