stat.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /**
  2. * @class UniStatDataStat uni统计-数据统计调度处理模块
  3. * @function cron 数据统计定时任务处理函数
  4. * @function stat 数据统计调度处理函数
  5. * @function cleanLog 日志清理调度处理函数
  6. */
  7. const {
  8. DateTime
  9. } = require('./lib')
  10. const {
  11. sleep
  12. } = require('../shared')
  13. const {
  14. BaseMod,
  15. SessionLog,
  16. PageLog,
  17. EventLog,
  18. ShareLog,
  19. ErrorLog,
  20. StatResult,
  21. ActiveDevices,
  22. ActiveUsers,
  23. PageResult,
  24. PageDetailResult,
  25. EventResult,
  26. ErrorResult,
  27. Loyalty,
  28. RunErrors,
  29. UserSessionLog,
  30. uniPay,
  31. Setting
  32. } = require('./mod')
  33. class UniStatDataStat {
  34. /**
  35. * 数据统计定时任务处理函数
  36. * @param {Object} context 服务器请求上下文参数
  37. */
  38. async cron(context) {
  39. const baseMod = new BaseMod()
  40. const dateTime = new DateTime()
  41. console.log('Cron start time: ', dateTime.getDate('Y-m-d H:i:s'))
  42. // const setting = new Setting();
  43. // let settingValue = await setting.getSetting()
  44. // if (settingValue.mode === "close") {
  45. // // 如果关闭了统计任务,则任务直接结束
  46. // return {
  47. // code: 0,
  48. // msg: 'Task is close',
  49. // }
  50. // } else if (settingValue.mode === "auto") {
  51. // // 如果开启了节能模式,则判断N天内是否有设备访问记录
  52. // let runKey = await setting.checkAutoRun(settingValue);
  53. // if (!runKey) {
  54. // return {
  55. // code: 0,
  56. // msg: 'Task is auto close',
  57. // }
  58. // }
  59. // }
  60. //获取运行参数
  61. const timeInfo = dateTime.getTimeInfo(null, false)
  62. const cronConfig = baseMod.getConfig('cron')
  63. const cronMin = baseMod.getConfig('cronMin')
  64. const realtimeStat = baseMod.getConfig('realtimeStat')
  65. // 数据跑批
  66. let res = null
  67. if (cronConfig && cronConfig.length > 0) {
  68. for (let mi in cronConfig) {
  69. const currCronConfig = cronConfig[mi]
  70. const cronType = currCronConfig.type
  71. const cronTime = currCronConfig.time.split(' ')
  72. const cronDimension = currCronConfig.dimension
  73. //未开启分钟级定时任务,则设置为小时级定时任务
  74. if (cronTime.length === 4 && !cronMin) {
  75. cronTime.splice(3, 1)
  76. }
  77. if (baseMod.debug) {
  78. console.log('cronTime', cronTime)
  79. }
  80. //精度为分钟级的定时任务
  81. if (cronTime.length === 4) {
  82. if (cronTime[0] !== '*') {
  83. //周统计任务
  84. if (timeInfo.nWeek == cronTime[0] && timeInfo.nHour == cronTime[2] && timeInfo.nMinutes ==
  85. cronTime[3]) {
  86. let dimension = cronDimension || 'week';
  87. console.log(cronType + `--${dimension} run`)
  88. res = await this.stat({
  89. type: cronType,
  90. dimension: cronDimension,
  91. config: currCronConfig
  92. })
  93. }
  94. } else if (cronTime[1] !== '*') {
  95. //月统计任务(包含季度统计任务和年统计任务)
  96. if (timeInfo.nDay == cronTime[1] && timeInfo.nHour == cronTime[2] && timeInfo.nMinutes ==
  97. cronTime[3]) {
  98. let dimension = cronDimension || 'month';
  99. console.log(cronType + `--${dimension} run`)
  100. res = await this.stat({
  101. type: cronType,
  102. dimension: dimension,
  103. config: currCronConfig
  104. })
  105. }
  106. } else if (cronTime[2] !== '*') {
  107. //日统计任务
  108. if (timeInfo.nHour == cronTime[2] && timeInfo.nMinutes == cronTime[3]) {
  109. let dimension = cronDimension || 'day';
  110. console.log(cronType + `--${dimension} run`)
  111. res = await this.stat({
  112. type: cronType,
  113. dimension: dimension,
  114. config: currCronConfig
  115. })
  116. }
  117. } else if (cronTime[3] !== '*') {
  118. //实时统计任务
  119. if (timeInfo.nMinutes == cronTime[3] && realtimeStat) {
  120. let dimension = cronDimension || 'hour';
  121. console.log(cronType + `--${dimension} run`)
  122. res = await this.stat({
  123. type: cronType,
  124. dimension: dimension,
  125. config: currCronConfig
  126. })
  127. }
  128. }
  129. }
  130. //精度为小时级的定时任务
  131. else if (cronTime.length === 3) {
  132. if (cronTime[0] !== '*') {
  133. //周统计任务
  134. if (timeInfo.nWeek == cronTime[0] && timeInfo.nHour == cronTime[2]) {
  135. let dimension = cronDimension || 'week';
  136. console.log(cronType + `--${dimension} run`)
  137. res = await this.stat({
  138. type: cronType,
  139. dimension: dimension,
  140. config: currCronConfig
  141. })
  142. }
  143. } else if (cronTime[1] !== '*') {
  144. //月统计任务(包含季度统计任务和年统计任务)
  145. if (timeInfo.nDay == cronTime[1] && timeInfo.nHour == cronTime[2]) {
  146. let dimension = cronDimension || 'month';
  147. console.log(cronType + `--${dimension} run`)
  148. res = await this.stat({
  149. type: cronType,
  150. dimension: dimension,
  151. config: currCronConfig
  152. })
  153. }
  154. } else if (cronTime[2] !== '*') {
  155. //日统计任务
  156. if (timeInfo.nHour == cronTime[2]) {
  157. let dimension = cronDimension || 'day';
  158. console.log(cronType + `--${dimension} run`)
  159. res = await this.stat({
  160. type: cronType,
  161. dimension: dimension,
  162. config: currCronConfig
  163. })
  164. }
  165. } else {
  166. //实时统计任务
  167. if (realtimeStat) {
  168. let dimension = cronDimension || 'hour';
  169. console.log(cronType + `--${dimension} run`)
  170. res = await this.stat({
  171. type: cronType,
  172. dimension: dimension,
  173. config: currCronConfig
  174. })
  175. }
  176. }
  177. } else {
  178. console.error('Cron configuration error')
  179. }
  180. }
  181. }
  182. console.log('Cron end time: ', dateTime.getDate('Y-m-d H:i:s'))
  183. return {
  184. code: 0,
  185. msg: 'Task have done',
  186. lastCronResult: res
  187. }
  188. }
  189. /**
  190. * 数据统计调度处理函数
  191. * @param {Object} params 统计参数
  192. */
  193. async stat(params) {
  194. const {
  195. type,
  196. dimension,
  197. date,
  198. reset,
  199. config
  200. } = params
  201. let res = {
  202. code: 0,
  203. msg: 'success'
  204. }
  205. try {
  206. switch (type) {
  207. // 基础统计
  208. case 'stat': {
  209. const resultStat = new StatResult()
  210. res = await resultStat.stat(dimension, date, reset)
  211. break
  212. }
  213. // 活跃设备统计归集
  214. case 'active-device': {
  215. const activeDevices = new ActiveDevices()
  216. res = await activeDevices.stat(date, reset)
  217. break
  218. }
  219. // 活跃用户统计归集
  220. case 'active-user': {
  221. const activeUsers = new ActiveUsers()
  222. res = await activeUsers.stat(date, reset)
  223. break
  224. }
  225. // 设备留存统计
  226. case 'retention-device': {
  227. const retentionStat = new StatResult()
  228. res = await retentionStat.retentionStat(dimension, date)
  229. break
  230. }
  231. // 用户留存统计
  232. case 'retention-user': {
  233. const retentionStat = new StatResult()
  234. res = await retentionStat.retentionStat(dimension, date, 'user')
  235. break
  236. }
  237. // 页面统计
  238. case 'page': {
  239. const pageStat = new PageResult()
  240. res = await pageStat.stat(dimension, date, reset)
  241. break
  242. }
  243. // 页面内容统计
  244. case 'page-detail': {
  245. const pageDetailStat = new PageDetailResult()
  246. res = await pageDetailStat.stat(dimension, date, reset)
  247. break
  248. }
  249. // 事件统计
  250. case 'event': {
  251. const eventStat = new EventResult()
  252. res = await eventStat.stat(dimension, date, reset)
  253. break
  254. }
  255. // 错误统计
  256. case 'error': {
  257. const errorStat = new ErrorResult()
  258. res = await errorStat.stat(dimension, date, reset)
  259. break
  260. }
  261. // 设备忠诚度统计
  262. case 'loyalty': {
  263. const loyaltyStat = new Loyalty()
  264. res = await loyaltyStat.stat(dimension, date, reset)
  265. break
  266. }
  267. // 日志清理
  268. case 'clean': {
  269. res = await this.cleanLog()
  270. }
  271. // 支付统计
  272. case 'pay-result': {
  273. const paymentResult = new uniPay.PayResult()
  274. res = await paymentResult.stat(dimension, date, reset, config)
  275. break
  276. }
  277. }
  278. } catch (e) {
  279. const maxTryTimes = 2
  280. if (!this.tryTimes) {
  281. this.tryTimes = 1
  282. } else {
  283. this.tryTimes++
  284. }
  285. //报错则重新尝试2次, 解决部分云服务器偶现连接超时问题
  286. if (this.tryTimes <= maxTryTimes) {
  287. //休眠3秒后重新调用
  288. await sleep(3000)
  289. params.reset = true
  290. res = await this.stat(params)
  291. } else {
  292. // 2次尝试失败后记录错误
  293. console.error('server error: ' + e)
  294. const runError = new RunErrors()
  295. runError.create({
  296. mod: 'stat',
  297. params: params,
  298. error: e,
  299. create_time: new DateTime().getTime()
  300. })
  301. res = {
  302. code: 500,
  303. msg: 'server error' + e
  304. }
  305. }
  306. }
  307. return res
  308. }
  309. /**
  310. * 日志清理调度处理函数
  311. */
  312. async cleanLog() {
  313. const baseMod = new BaseMod()
  314. const cleanLog = baseMod.getConfig('cleanLog')
  315. if (!cleanLog || !cleanLog.open) {
  316. return {
  317. code: 100,
  318. msg: 'The log cleanup service has not been turned on'
  319. }
  320. }
  321. const res = {
  322. code: 0,
  323. msg: 'success',
  324. data: {}
  325. }
  326. // 会话日志
  327. if (cleanLog.reserveDays.sessionLog > 0) {
  328. const sessionLog = new SessionLog()
  329. res.data.sessionLog = await sessionLog.clean(cleanLog.reserveDays.sessionLog)
  330. }
  331. // 用户会话日志
  332. if (cleanLog.reserveDays.userSessionLog > 0) {
  333. const userSessionLog = new UserSessionLog()
  334. res.data.userSessionLog = await userSessionLog.clean(cleanLog.reserveDays.userSessionLog)
  335. }
  336. // 页面日志
  337. if (cleanLog.reserveDays.pageLog > 0) {
  338. const pageLog = new PageLog()
  339. res.data.pageLog = await pageLog.clean(cleanLog.reserveDays.pageLog)
  340. }
  341. // 事件日志
  342. if (cleanLog.reserveDays.eventLog > 0) {
  343. const eventLog = new EventLog()
  344. res.data.eventLog = await eventLog.clean(cleanLog.reserveDays.eventLog)
  345. }
  346. // 分享日志
  347. if (cleanLog.reserveDays.shareLog > 0) {
  348. const shareLog = new ShareLog()
  349. res.data.shareLog = await shareLog.clean(cleanLog.reserveDays.shareLog)
  350. }
  351. // 错误日志
  352. if (cleanLog.reserveDays.errorLog > 0) {
  353. const errorLog = new ErrorLog()
  354. res.data.errorLog = await errorLog.clean(cleanLog.reserveDays.errorLog)
  355. }
  356. // 活跃设备日志
  357. const activeDevicesLog = new ActiveDevices()
  358. res.data.activeDevicesLog = await activeDevicesLog.clean()
  359. // 活跃用户日志
  360. const activeUsersLog = new ActiveUsers()
  361. res.data.activeUsersLog = await activeUsersLog.clean()
  362. // 实时统计日志
  363. const resultHourLog = new StatResult()
  364. res.data.resultHourLog = await resultHourLog.cleanHourLog()
  365. //原生应用崩溃日志
  366. const appCrashLogs = new AppCrashLogs()
  367. res.data.appCrashLogs = await appCrashLogs.clean()
  368. return res
  369. }
  370. }
  371. module.exports = UniStatDataStat