UserOnlineService.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. package service
  2. import (
  3. "bufio"
  4. "designs/config"
  5. "designs/utils"
  6. "fmt"
  7. "math"
  8. "os"
  9. "path/filepath"
  10. "sort"
  11. "strconv"
  12. "strings"
  13. "time"
  14. )
  15. type logData struct {
  16. LogTime time.Time `json:"LogTime"`
  17. Type int `json:"Type"`
  18. }
  19. // 查询单日使用时长曲线
  20. func UserOnlineSummaryByDay(gid string, pf string, startTime string, endTime string) (map[string]int, int, int, error) {
  21. //var onlineData []model.UserOnline
  22. ////从数据库中查询出所有的数据
  23. //query := global.App.DB.Table("user_online").Where("gid", gid).Where("pf", pf)
  24. //
  25. //if startTime != "" && endTime != "" {
  26. // query = query.Where("logTime", ">=", startTime).Where("logTime", "<=", endTime)
  27. //}
  28. //err := query.Scan(&onlineData).Error
  29. //if err != nil {
  30. // return nil, 0, 0, err
  31. //}
  32. ////对数据进行分组
  33. //userOnlineData := make(map[string]map[int][]logData)
  34. //dateSlice := utils.GetTimeDayDateFormat(startTime, endTime)
  35. //for _, v := range dateSlice {
  36. // userOnlineData[v] = make(map[int][]logData)
  37. //}
  38. //
  39. //for _, v := range onlineData {
  40. // date := v.LogTime.Format("2006-01-02")
  41. // userOnlineData[date][v.UserId] = append(userOnlineData[date][v.UserId], logData{v.LogTime, v.Type})
  42. //}
  43. userOnlineData := make(map[string]map[int][]logData)
  44. dateSlice := utils.GetTimeDayDateFormat(startTime, endTime)
  45. for _, v := range dateSlice {
  46. userOnlineData[v] = make(map[int][]logData)
  47. userOnlineData[v] = GetLocalActiveLogDetail(gid, pf, v)
  48. }
  49. //计算每天的数据,然后得出平均值
  50. var totalAvg int
  51. var total int
  52. var totalTime int64
  53. daysAvg := make(map[string]int)
  54. for date, daysData := range userOnlineData {
  55. var userTotalTime int64
  56. //分组后对于每个用户的数据进行处理,得到他们的具体在线时长
  57. for _, v := range daysData {
  58. userTotalTime = userTotalTime + calculateUserOnlineTime(v)
  59. }
  60. //计算出平均值,并且格式化
  61. if len(daysData) > 0 {
  62. avg := float64(userTotalTime) / float64(len(daysData))
  63. daysAvg[date] = int(math.Round(avg))
  64. } else {
  65. daysAvg[date] = 0
  66. }
  67. //统计累加
  68. total = total + len(daysData)
  69. totalTime = totalTime + userTotalTime
  70. }
  71. //统计平均值
  72. if total <= 0 {
  73. totalAvg = 0
  74. } else {
  75. avg := float64(totalTime) / float64(total)
  76. totalAvg = int(math.Round(avg))
  77. }
  78. return daysAvg, total, totalAvg, nil
  79. }
  80. func UserOnlineSummary(gid string, pf string, date string, startTime string, endTime string) (map[int]int64, error) {
  81. //var onlineData []model.UserOnline
  82. ////从数据库中查询出所有的数据
  83. //query := global.App.DB.Table("user_online").Where("gid", gid).Where("pf", pf)
  84. //if date != "" {
  85. // query = query.Where("date", "=", date)
  86. //}
  87. //
  88. //if startTime != "" && endTime != "" {
  89. // query = query.Where("logTime", ">=", startTime).Where("logTime", "<=", endTime)
  90. //}
  91. //err := query.Scan(&onlineData).Error
  92. //if err != nil {
  93. // return nil, err
  94. //}
  95. ////对数据进行分组
  96. //userOnlineData := make(map[int][]logData)
  97. //for _, v := range onlineData {
  98. // value, exists := userOnlineData[v.UserId]
  99. // if exists {
  100. // userOnlineData[v.UserId] = append(value, logData{v.LogTime, v.Type})
  101. //
  102. // } else {
  103. // userOnlineData[v.UserId] = []logData{{v.LogTime, v.Type}}
  104. // }
  105. //}
  106. //onlineData = nil
  107. userOnlineData := make(map[int][]logData)
  108. userOnlineData = GetLocalActiveLogDetailRange(gid, pf, startTime, endTime)
  109. userLogMsg := make(map[int]int64)
  110. //分组后对于每个用户的数据进行处理,得到他们的具体在线时长
  111. for k, v := range userOnlineData {
  112. //aaa, _ := json.Marshal(v)
  113. //global.App.Log.Error(string(aaa))
  114. userLogMsg[k] = calculateUserOnlineTime(v)
  115. }
  116. userOnlineData = nil
  117. return userLogMsg, nil
  118. }
  119. func calculateUserOnlineTime(logData []logData) int64 {
  120. sort.Slice(logData, func(i, j int) bool {
  121. return logData[i].LogTime.Before(logData[j].LogTime)
  122. })
  123. var lastLog int64
  124. var isStart bool
  125. var onlineTimeTotal int64
  126. for k, v := range logData {
  127. logTime := v.LogTime.Unix()
  128. //如果跟上一次的记录隔的时间超过6分钟,就认为是之前断线了,属于无效数据
  129. if k > 0 && logData[k-1].LogTime.Unix()+60*6 < logTime {
  130. isStart = false
  131. continue
  132. }
  133. if v.Type == 1 && isStart == false {
  134. isStart = true
  135. lastLog = v.LogTime.Unix()
  136. continue
  137. }
  138. if v.Type == 1 && isStart == true {
  139. //logTime := v.LogTime.Unix()
  140. onlineTimeTotal = onlineTimeTotal + (logTime - lastLog)
  141. lastLog = logTime
  142. //如果下一次的心跳,间隔时间大于6分钟了,就可以认为,这次就算是结束了
  143. if k+1 == len(logData) || logData[k+1].LogTime.Unix() > logTime+60*6 {
  144. isStart = false
  145. }
  146. continue
  147. }
  148. if v.Type == 2 && isStart == true {
  149. //logTime := v.LogTime.Unix()
  150. onlineTimeTotal = onlineTimeTotal + (logTime - lastLog)
  151. isStart = false
  152. continue
  153. }
  154. }
  155. return onlineTimeTotal
  156. }
  157. func GetLocalActiveLogDetail(gid string, pf string, date string) map[int][]logData {
  158. var dir string
  159. if config.Get("app.local") == "local" {
  160. //url = "mongodb://localhost:27017"
  161. dir = "storage"
  162. } else {
  163. dir = "/www/wwwroot/chunhao_receive/storage"
  164. }
  165. dirPath := filepath.Join(dir, date)
  166. fileName := fmt.Sprintf("%s_%s.txt", gid, pf)
  167. filePath := filepath.Join(dirPath, fileName)
  168. file, _ := os.Open(filePath)
  169. defer file.Close() // 确保函数结束时关闭文件
  170. // 创建 Scanner 对象
  171. scanner := bufio.NewScanner(file)
  172. res := make(map[int][]logData)
  173. // 逐行读取文件内容
  174. lineNumber := 1
  175. for scanner.Scan() {
  176. line := scanner.Text() // 获取当前行内容
  177. lineNumber++
  178. //11887,2,2025-03-24 09:05:19
  179. lineSlice := strings.Split(line, ",")
  180. userId, _ := strconv.Atoi(lineSlice[0])
  181. types, _ := strconv.Atoi(lineSlice[1])
  182. // 解析时间
  183. t, _ := time.Parse("2006-01-02 15:04:05", lineSlice[2])
  184. res[userId] = append(res[userId], logData{t, types})
  185. }
  186. return res
  187. }
  188. func GetLocalActiveLogDetailRange(gid string, pf string, start string, end string) map[int][]logData {
  189. var dir string
  190. if config.Get("app.local") == "local" {
  191. //url = "mongodb://localhost:27017"
  192. dir = "storage"
  193. } else {
  194. dir = "/www/wwwroot/chunhao_receive/storage"
  195. }
  196. dateSlice := utils.GetTimeDayDateFormat(start, end)
  197. res := make(map[int][]logData)
  198. for _, v := range dateSlice {
  199. dirPath := filepath.Join(dir, v)
  200. fileName := fmt.Sprintf("%s_%s.txt", gid, pf)
  201. filePath := filepath.Join(dirPath, fileName)
  202. file, _ := os.Open(filePath)
  203. // 创建 Scanner 对象
  204. scanner := bufio.NewScanner(file)
  205. // 逐行读取文件内容
  206. lineNumber := 1
  207. for scanner.Scan() {
  208. line := scanner.Text() // 获取当前行内容
  209. lineNumber++
  210. //11887,2,2025-03-24 09:05:19
  211. lineSlice := strings.Split(line, ",")
  212. userId, _ := strconv.Atoi(lineSlice[0])
  213. types, _ := strconv.Atoi(lineSlice[1])
  214. // 解析时间
  215. t, _ := time.Parse("2006-01-02 15:04:05", lineSlice[2])
  216. res[userId] = append(res[userId], logData{t, types})
  217. }
  218. file.Close() // 确保函数结束时关闭文件
  219. }
  220. return res
  221. }