compute_statistic.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. package crontask
  2. import (
  3. "strconv"
  4. "time"
  5. "wechat-api/ent"
  6. "wechat-api/ent/contact"
  7. "wechat-api/ent/custom_types"
  8. "wechat-api/ent/labelrelationship"
  9. "wechat-api/ent/messagerecords"
  10. "wechat-api/ent/usagedetail"
  11. "wechat-api/ent/usagestatisticday"
  12. "wechat-api/ent/usagestatistichour"
  13. "wechat-api/ent/usagestatisticmonth"
  14. "wechat-api/ent/wx"
  15. )
  16. func (l *CronTask) computeStatistic() {
  17. startTime := time.Now()
  18. // 获取所有机器人信息
  19. wxbots, err := l.svcCtx.DB.Wx.Query().Select(wx.FieldWxid, wx.FieldID, wx.FieldOrganizationID).All(l.ctx)
  20. if err != nil {
  21. l.Errorf("fetch wxids error:%v\n", err)
  22. return
  23. }
  24. wxbotsSet := make(map[uint64][]*ent.Wx)
  25. for _, bot := range wxbots {
  26. wxbotsSet[bot.OrganizationID] = append(wxbotsSet[bot.OrganizationID], bot)
  27. }
  28. LabelsCountSet := make(map[uint64][]custom_types.LabelDist)
  29. /*
  30. 计算本小时的数据
  31. 1. 查询出上小时里所有 usagedetail 内容
  32. 2. 挨个遍历他的 bot_id ,再查询他的 bot_id 相关的参数
  33. 3. 遍历的时候可能有重复,所以要先检查是否生成了数据,如果有就忽略,没有再生成
  34. ----------------------------------------------------------------------------------------------------------
  35. */
  36. // 获取当前时间
  37. now := time.Now()
  38. // 获取本小时的第一分钟
  39. currentHour := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
  40. currentHourInt, _ := strconv.Atoi(currentHour.Format("2006010215"))
  41. // 上一个小时的起始时间
  42. lastHour := currentHour.Add(-time.Hour * 1)
  43. lastHourInt, _ := strconv.Atoi(lastHour.Format("2006010215"))
  44. for orgID, wxinfos := range wxbotsSet {
  45. var orgAiResponseInt, orgSopRunInt, orgFriendCountInt, orgGroupCountInt, orgAccountBalanceInt, orgConsumeTokenInt, orgActiveUserInt, orgNewUserInt int
  46. for _, wxinfo := range wxinfos {
  47. l.Logger.Infof("开始计算小时数据:%d\n", currentHourInt)
  48. // 先判断该账号是否已经统计了小时数据,如果已经统计了,就不需要再统计了
  49. var aiResponseInt, sopRunInt, friendCountInt, groupCountInt, accountBalanceInt, consumeTokenInt, activeUserInt, newUserInt int
  50. hourDataCount, _ := l.svcCtx.DB.UsageStatisticHour.Query().Where(
  51. usagestatistichour.Type(1),
  52. usagestatistichour.BotID(wxinfo.Wxid),
  53. usagestatistichour.Addtime(uint64(currentHourInt)),
  54. ).Count(l.ctx)
  55. if hourDataCount > 0 {
  56. continue
  57. }
  58. // AI回复包括:SOP次数+AI次数
  59. // SOP次数:content 非空,source_type = 3 或 4,sub_source_id = 0
  60. // AI次数:app = 1 或 3
  61. sopresp, _ := l.svcCtx.DB.MessageRecords.Query().Where(
  62. messagerecords.SubSourceID(0),
  63. messagerecords.SourceTypeIn(3, 4),
  64. messagerecords.BotWxid(wxinfo.Wxid),
  65. messagerecords.CreatedAtGTE(lastHour),
  66. messagerecords.CreatedAtLT(currentHour),
  67. ).Count(l.ctx)
  68. airesp, _ := l.svcCtx.DB.UsageDetail.Query().Where(
  69. usagedetail.AppIn(1, 3),
  70. usagedetail.BotID(wxinfo.Wxid),
  71. usagedetail.CreatedAtGTE(lastHour),
  72. usagedetail.CreatedAtLT(currentHour),
  73. ).Count(l.ctx)
  74. aiResponseInt = sopresp + airesp
  75. orgAiResponseInt += aiResponseInt
  76. // SOP执行次数:SOP阶段和节点的执行次数。
  77. sopRunInt, _ = l.svcCtx.DB.MessageRecords.Query().Where(
  78. messagerecords.BotWxid(wxinfo.Wxid),
  79. messagerecords.SubSourceIDEQ(0),
  80. messagerecords.SourceTypeIn(3, 4),
  81. messagerecords.BotWxid(wxinfo.Wxid),
  82. messagerecords.CreatedAtGTE(lastHour),
  83. messagerecords.CreatedAtLT(currentHour),
  84. ).Count(l.ctx)
  85. orgSopRunInt += sopRunInt
  86. // 好友总数:contact 表中 type=1
  87. friendCountInt, _ = l.svcCtx.DB.Contact.Query().Where(
  88. contact.Type(1),
  89. contact.WxWxid(wxinfo.Wxid),
  90. ).Count(l.ctx)
  91. orgFriendCountInt += friendCountInt
  92. // 群总数:contact 表中 type=2
  93. groupCountInt, _ = l.svcCtx.DB.Contact.Query().Where(
  94. contact.Type(2),
  95. contact.WxWxid(wxinfo.Wxid),
  96. ).Count(l.ctx)
  97. orgGroupCountInt += groupCountInt
  98. // 消耗Token数:usage_detail 表
  99. consumeTokenInt, _ = l.svcCtx.DB.UsageDetail.Query().Where(
  100. usagedetail.TypeEQ(1),
  101. usagedetail.BotID(wxinfo.Wxid),
  102. usagedetail.CreatedAtGTE(lastHour),
  103. usagedetail.CreatedAtLT(currentHour),
  104. ).Aggregate(ent.Sum("total_tokens")).Int(l.ctx)
  105. orgConsumeTokenInt += consumeTokenInt
  106. // 账户余额
  107. accountBalanceInt = 0
  108. orgAccountBalanceInt = 0
  109. // 活跃好友:usage_detail 表 type = 1
  110. activeUserInt, _ = l.svcCtx.DB.UsageDetail.Query().Where(
  111. usagedetail.Type(1),
  112. usagedetail.BotID(wxinfo.Wxid),
  113. usagedetail.CreatedAtGTE(lastHour),
  114. usagedetail.CreatedAtLT(currentHour),
  115. ).GroupBy(usagedetail.FieldBotID).Int(l.ctx)
  116. orgActiveUserInt += activeUserInt
  117. lastHourData, _ := l.svcCtx.DB.UsageStatisticHour.Query().Where(
  118. usagestatistichour.AddtimeEQ(uint64(lastHourInt)),
  119. usagestatistichour.Type(1),
  120. usagestatistichour.BotID(wxinfo.Wxid),
  121. ).First(l.ctx)
  122. if lastHourData == nil {
  123. newUserInt = friendCountInt
  124. } else {
  125. newUserInt = int(lastHourData.TotalFriend) - friendCountInt
  126. }
  127. orgNewUserInt += newUserInt
  128. _, err := l.svcCtx.DB.UsageStatisticHour.Create().
  129. SetType(1).
  130. SetBotID(wxinfo.Wxid).
  131. SetOrganizationID(wxinfo.OrganizationID).
  132. SetAiResponse(uint64(aiResponseInt)).
  133. SetSopRun(uint64(sopRunInt)).
  134. SetTotalFriend(uint64(friendCountInt)).
  135. SetTotalGroup(uint64(groupCountInt)).
  136. SetAccountBalance(uint64(accountBalanceInt)).
  137. SetConsumeToken(uint64(consumeTokenInt)).
  138. SetActiveUser(uint64(activeUserInt)).
  139. SetNewUser(int64(newUserInt)).
  140. SetAddtime(uint64(currentHourInt)).
  141. Save(l.ctx)
  142. l.Errorf("save hour data error:%v \n", err)
  143. }
  144. // 先判断该租户是否已经统计了小时数据,如果已经统计了,就不需要再统计了
  145. hourDataCount, _ := l.svcCtx.DB.UsageStatisticHour.Query().Where(
  146. usagestatistichour.Type(1),
  147. usagestatistichour.OrganizationID(orgID),
  148. usagestatistichour.BotIDIsNil(),
  149. usagestatistichour.Addtime(uint64(currentHourInt)),
  150. ).Count(l.ctx)
  151. if hourDataCount > 0 {
  152. continue
  153. }
  154. var LabelsCount []custom_types.LabelDist
  155. err := l.svcCtx.DB.LabelRelationship.Query().Where(labelrelationship.OrganizationIDEQ(orgID), labelrelationship.DeletedAtIsNil()).GroupBy(labelrelationship.FieldLabelID).Aggregate(ent.Count()).Scan(l.ctx, &LabelsCount)
  156. l.Errorf("save hour data error:%v \n", err)
  157. LabelsCountSet[orgID] = LabelsCount
  158. _, err = l.svcCtx.DB.UsageStatisticHour.Create().
  159. SetType(1).
  160. SetOrganizationID(orgID).
  161. SetAiResponse(uint64(orgAiResponseInt)).
  162. SetSopRun(uint64(orgSopRunInt)).
  163. SetTotalFriend(uint64(orgFriendCountInt)).
  164. SetTotalGroup(uint64(orgGroupCountInt)).
  165. SetAccountBalance(uint64(orgAccountBalanceInt)).
  166. SetConsumeToken(uint64(orgConsumeTokenInt)).
  167. SetActiveUser(uint64(orgActiveUserInt)).
  168. SetNewUser(int64(orgNewUserInt)).
  169. SetAddtime(uint64(currentHourInt)).
  170. SetNotNilLabelDist(LabelsCount).
  171. Save(l.ctx)
  172. l.Errorf("save hour data error:%v \n", err)
  173. }
  174. /*
  175. 计算日数据
  176. ----------------------------------------------------------------------------------------------------------
  177. */
  178. dayStr := time.Now().Format("20060102")
  179. day, _ := strconv.Atoi(dayStr)
  180. // 获取昨天的第一小时
  181. yesterday := now.AddDate(0, 0, -1)
  182. yesterdayFirstHour := time.Date(yesterday.Year(), yesterday.Month(), now.Day(), 0, 0, 0, 0, now.Location())
  183. yesterdayFirstHourInt, _ := strconv.Atoi(yesterdayFirstHour.Format("20060102"))
  184. // 获取昨天的最后一小时
  185. yesterdayLastHour := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
  186. yesterdayLastHourInt, _ := strconv.Atoi(yesterdayLastHour.Format("20060102"))
  187. for orgID, wxinfos := range wxbotsSet {
  188. var orgAiResponseInt, orgSopRunInt, orgFriendCountInt, orgGroupCountInt, orgAccountBalanceInt, orgConsumeTokenInt, orgActiveUserInt uint64
  189. var orgNewUserInt int64
  190. for _, wxinfo := range wxinfos {
  191. l.Logger.Infof("开始计算日数据:%d\n", day)
  192. // 先判断该账号是否已经统计了日数据,如果已经统计了,就不需要再统计了
  193. dayDataCount, _ := l.svcCtx.DB.UsageStatisticDay.Query().Where(
  194. usagestatisticday.Type(1),
  195. usagestatisticday.BotID(wxinfo.Wxid),
  196. usagestatisticday.Addtime(uint64(day)),
  197. ).Count(l.ctx)
  198. // 如果添加过了就略过
  199. if dayDataCount > 0 {
  200. continue
  201. }
  202. hourDataBatch, _ := l.svcCtx.DB.UsageStatisticHour.Query().Where(
  203. usagestatistichour.Type(1),
  204. usagestatistichour.BotID(wxinfo.Wxid),
  205. usagestatistichour.AddtimeGTE(uint64(yesterdayFirstHourInt)),
  206. usagestatistichour.AddtimeLT(uint64(yesterdayLastHourInt)),
  207. ).All(l.ctx)
  208. var aiResponse, sopRun, totalFriend, totalGroup, accountBalance, consumeToken, activeUser uint64
  209. var newUser int64
  210. for _, hourData := range hourDataBatch {
  211. aiResponse += hourData.AiResponse
  212. sopRun += hourData.SopRun
  213. totalFriend += hourData.TotalFriend
  214. totalGroup += hourData.TotalGroup
  215. accountBalance += hourData.AccountBalance
  216. consumeToken += hourData.ConsumeToken
  217. activeUser += hourData.ActiveUser
  218. newUser += hourData.NewUser
  219. }
  220. orgAiResponseInt += aiResponse
  221. orgSopRunInt += sopRun
  222. orgFriendCountInt += totalFriend
  223. orgGroupCountInt += totalGroup
  224. orgAccountBalanceInt += accountBalance
  225. orgConsumeTokenInt += consumeToken
  226. orgActiveUserInt += activeUser
  227. orgNewUserInt += newUser
  228. _, err := l.svcCtx.DB.UsageStatisticDay.Create().
  229. SetAddtime(uint64(day)).
  230. SetType(1).
  231. SetBotID(wxinfo.Wxid).
  232. SetOrganizationID(wxinfo.OrganizationID).
  233. SetAiResponse(aiResponse).
  234. SetSopRun(sopRun).
  235. SetTotalFriend(totalFriend).
  236. SetTotalGroup(totalGroup).
  237. SetAccountBalance(accountBalance).
  238. SetConsumeToken(consumeToken).
  239. SetActiveUser(activeUser).
  240. SetNewUser(newUser).
  241. Save(l.ctx)
  242. if err != nil {
  243. l.Errorf("create day data error:%v \n", err)
  244. continue
  245. }
  246. }
  247. // 先判断该租户是否已经统计了日数据,如果已经统计了,就不需要再统计了
  248. dayDataCount, _ := l.svcCtx.DB.UsageStatisticDay.Query().Where(
  249. usagestatisticday.Type(1),
  250. usagestatisticday.OrganizationID(orgID),
  251. usagestatisticday.BotIDIsNil(),
  252. usagestatisticday.Addtime(uint64(day)),
  253. ).Count(l.ctx)
  254. // 如果添加过了就略过
  255. if dayDataCount > 0 {
  256. continue
  257. }
  258. _, err := l.svcCtx.DB.UsageStatisticDay.Create().
  259. SetAddtime(uint64(day)).
  260. SetType(1).
  261. SetOrganizationID(orgID).
  262. SetAiResponse(orgAiResponseInt).
  263. SetSopRun(orgSopRunInt).
  264. SetTotalFriend(orgFriendCountInt).
  265. SetTotalGroup(orgGroupCountInt).
  266. SetAccountBalance(orgAccountBalanceInt).
  267. SetConsumeToken(orgConsumeTokenInt).
  268. SetActiveUser(orgActiveUserInt).
  269. SetNewUser(orgNewUserInt).
  270. SetNotNilLabelDist(LabelsCountSet[orgID]).
  271. Save(l.ctx)
  272. if err != nil {
  273. l.Errorf("create day data error:%v \n", err)
  274. continue
  275. }
  276. }
  277. /*
  278. 查看月表数据是否已经完成
  279. 1. 查询出上月里所有 usagedetail 内容
  280. 2. 挨个遍历他的 bot_id ,再查询他的 bot_id 相关的参数
  281. ----------------------------------------------------------------------------------------------------------
  282. */
  283. monthStr := time.Now().Format("200601")
  284. month, _ := strconv.Atoi(monthStr)
  285. for orgID, wxinfos := range wxbotsSet {
  286. var orgAiResponseInt, orgSopRunInt, orgFriendCountInt, orgGroupCountInt, orgAccountBalanceInt, orgConsumeTokenInt, orgActiveUserInt uint64
  287. var orgNewUserInt int64
  288. for _, wxinfo := range wxinfos {
  289. l.Logger.Infof("开始计算月数据:%d\n", month)
  290. // 获取上月的第一天
  291. monthFirstDay := time.Date(now.Year(), now.Month()-1, 1, 0, 0, 0, 0, now.Location())
  292. monthFirstDayInt, _ := strconv.Atoi(monthFirstDay.Format("20060102"))
  293. // 获取上月的最后一天
  294. monthLastDay := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
  295. monthLastDayInt, _ := strconv.Atoi(monthLastDay.Format("20060102"))
  296. // 先判断该账号是否已经统计了月数据,如果已经统计了,就不需要再统计了
  297. monthDataCount, _ := l.svcCtx.DB.UsageStatisticMonth.Query().Where(
  298. usagestatisticmonth.Type(1),
  299. usagestatisticmonth.BotID(wxinfo.Wxid),
  300. usagestatisticmonth.Addtime(uint64(month)),
  301. ).Count(l.ctx)
  302. // 如果添加过了就略过
  303. if monthDataCount > 0 {
  304. continue
  305. }
  306. dayDataBatch, _ := l.svcCtx.DB.UsageStatisticDay.Query().Where(
  307. usagestatisticday.Type(1),
  308. usagestatisticday.BotID(wxinfo.Wxid),
  309. usagestatisticday.AddtimeGTE(uint64(monthFirstDayInt)),
  310. usagestatisticday.AddtimeLT(uint64(monthLastDayInt)),
  311. ).All(l.ctx)
  312. var aiResponse, sopRun, totalFriend, totalGroup, accountBalance, consumeToken, activeUser uint64
  313. var newUser int64
  314. for _, dayData := range dayDataBatch {
  315. aiResponse += dayData.AiResponse
  316. sopRun += dayData.SopRun
  317. totalFriend += dayData.TotalFriend
  318. totalGroup += dayData.TotalGroup
  319. accountBalance += dayData.AccountBalance
  320. consumeToken += dayData.ConsumeToken
  321. activeUser += dayData.ActiveUser
  322. newUser += dayData.NewUser
  323. }
  324. orgAiResponseInt += aiResponse
  325. orgSopRunInt += sopRun
  326. orgFriendCountInt += totalFriend
  327. orgGroupCountInt += totalGroup
  328. orgAccountBalanceInt += accountBalance
  329. orgConsumeTokenInt += consumeToken
  330. orgActiveUserInt += activeUser
  331. orgNewUserInt += newUser
  332. _, err := l.svcCtx.DB.UsageStatisticMonth.Create().
  333. SetAddtime(uint64(month)).
  334. SetType(1).
  335. SetBotID(wxinfo.Wxid).
  336. SetOrganizationID(wxinfo.OrganizationID).
  337. SetAiResponse(aiResponse).
  338. SetSopRun(sopRun).
  339. SetTotalFriend(totalFriend).
  340. SetTotalGroup(totalGroup).
  341. SetAccountBalance(accountBalance).
  342. SetConsumeToken(consumeToken).
  343. SetActiveUser(activeUser).
  344. SetNewUser(newUser).
  345. Save(l.ctx)
  346. if err != nil {
  347. l.Errorf("create month data error:%v \n", err)
  348. continue
  349. }
  350. }
  351. // 先判断该租户是否已经统计了月数据,如果已经统计了,就不需要再统计了
  352. monthDataCount, _ := l.svcCtx.DB.UsageStatisticMonth.Query().Where(
  353. usagestatisticmonth.Type(1),
  354. usagestatisticmonth.OrganizationID(orgID),
  355. usagestatisticmonth.BotIDIsNil(),
  356. usagestatisticmonth.Addtime(uint64(month)),
  357. ).Count(l.ctx)
  358. // 如果添加过了就略过
  359. if monthDataCount > 0 {
  360. continue
  361. }
  362. _, err := l.svcCtx.DB.UsageStatisticMonth.Create().
  363. SetAddtime(uint64(month)).
  364. SetType(1).
  365. SetOrganizationID(orgID).
  366. SetAiResponse(orgAiResponseInt).
  367. SetSopRun(orgSopRunInt).
  368. SetTotalFriend(orgFriendCountInt).
  369. SetTotalGroup(orgGroupCountInt).
  370. SetAccountBalance(orgAccountBalanceInt).
  371. SetConsumeToken(orgConsumeTokenInt).
  372. SetActiveUser(orgActiveUserInt).
  373. SetNewUser(orgNewUserInt).
  374. SetNotNilLabelDist(LabelsCountSet[orgID]).
  375. Save(l.ctx)
  376. if err != nil {
  377. l.Errorf("create month data error:%v \n", err)
  378. continue
  379. }
  380. }
  381. finishTime := time.Now()
  382. l.Logger.Infof("This process cost %v", finishTime.Sub(startTime).String())
  383. return
  384. }