statResult.js 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152
  1. /**
  2. * @class StatResult 基础数据结果统计模型
  3. */
  4. const BaseMod = require('./base')
  5. const Platform = require('./platform')
  6. const Channel = require('./channel')
  7. const Version = require('./version')
  8. const SessionLog = require('./sessionLog')
  9. const UserSessionLog = require('./userSessionLog')
  10. const ErrorLog = require('./errorLog')
  11. const ActiveDevices = require('./activeDevices')
  12. const ActiveUsers = require('./activeUsers')
  13. const UniIDUsers = require('./uniIDUsers')
  14. const {
  15. DateTime
  16. } = require('../lib')
  17. module.exports = class StatResult extends BaseMod {
  18. constructor() {
  19. super()
  20. this.tableName = 'result'
  21. this.platforms = []
  22. this.channels = []
  23. this.versions = []
  24. }
  25. /**
  26. * 基础数据统计
  27. * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
  28. * @param {Date|Time} date 指定日期或时间戳
  29. * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
  30. */
  31. async stat(type, date, reset) {
  32. const allowedType = ['hour', 'day', 'week', 'month']
  33. if (!allowedType.includes(type)) {
  34. return {
  35. code: 1002,
  36. msg: 'This type is not allowed'
  37. }
  38. }
  39. if (this.debug) {
  40. console.log('result --type:' + type + ', date:' + date + ', reset:' + reset)
  41. }
  42. this.fillType = type
  43. const dateTime = new DateTime()
  44. const dateDimension = dateTime.getTimeDimensionByType(type, -1, date)
  45. this.startTime = dateDimension.startTime
  46. this.endTime = dateDimension.endTime
  47. if (this.debug) {
  48. console.log('dimension time', this.startTime + '--' + this.endTime)
  49. }
  50. // 查看当前时间段日志是否已存在,防止重复生成
  51. if (!reset) {
  52. const checkRes = await this.getCollection(this.tableName).where({
  53. dimension: this.fillType,
  54. start_time: this.startTime,
  55. end_time: this.endTime
  56. }).get()
  57. if (checkRes.data.length > 0) {
  58. console.log('log have existed')
  59. return {
  60. code: 1003,
  61. msg: 'This log have existed'
  62. }
  63. }
  64. } else {
  65. const delRes = await this.delete(this.tableName, {
  66. start_time: this.startTime,
  67. end_time: this.endTime
  68. })
  69. console.log('delete old data result:', JSON.stringify(delRes))
  70. }
  71. // 周月数据单独统计
  72. if (['week', 'month'].includes(this.fillType)) {
  73. return await this.statWeekOrMonth()
  74. }
  75. // 数据获取
  76. this.sessionLog = new SessionLog()
  77. const statRes = await this.aggregate(this.sessionLog.tableName, {
  78. project: {
  79. appid: 1,
  80. version: 1,
  81. platform: 1,
  82. channel: 1,
  83. is_first_visit: 1,
  84. page_count: 1,
  85. duration: 1,
  86. create_time: 1
  87. },
  88. match: {
  89. create_time: {
  90. $gte: this.startTime,
  91. $lte: this.endTime
  92. }
  93. },
  94. group: {
  95. _id: {
  96. appid: '$appid',
  97. version: '$version',
  98. platform: '$platform',
  99. channel: '$channel'
  100. },
  101. new_device_count: {
  102. $sum: '$is_first_visit'
  103. },
  104. page_view_count: {
  105. $sum: '$page_count'
  106. },
  107. total_duration: {
  108. $sum: '$duration'
  109. },
  110. session_times: {
  111. $sum: 1
  112. }
  113. },
  114. sort: {
  115. new_device_count: 1,
  116. page_view_count: 1,
  117. session_times: 1
  118. },
  119. getAll: true
  120. })
  121. let res = {
  122. code: 0,
  123. msg: 'success'
  124. }
  125. if (this.debug) {
  126. console.log('statRes', JSON.stringify(statRes))
  127. }
  128. this.fillData = []
  129. this.composes = []
  130. if (statRes.data.length > 0) {
  131. for (const i in statRes.data) {
  132. await this.fill(statRes.data[i])
  133. }
  134. }
  135. //补充数据
  136. await this.replenishStat()
  137. if (this.fillData.length > 0) {
  138. res = await this.batchInsert(this.tableName, this.fillData)
  139. }
  140. return res
  141. }
  142. /**
  143. * 按周/月统计
  144. */
  145. async statWeekOrMonth() {
  146. const statRes = await this.aggregate(this.tableName, {
  147. project: {
  148. appid: 1,
  149. version_id: 1,
  150. platform_id: 1,
  151. channel_id: 1,
  152. new_device_count: 1,
  153. new_user_count: 1,
  154. page_visit_count: 1,
  155. user_visit_times: 1,
  156. app_launch_count: 1,
  157. error_count: 1,
  158. bounce_times: 1,
  159. duration: 1,
  160. user_duration: 1,
  161. dimension: 1,
  162. start_time: 1
  163. },
  164. match: {
  165. dimension: 'day',
  166. start_time: {
  167. $gte: this.startTime,
  168. $lte: this.endTime
  169. }
  170. },
  171. group: {
  172. _id: {
  173. appid: '$appid',
  174. version_id: '$version_id',
  175. platform_id: '$platform_id',
  176. channel_id: '$channel_id'
  177. },
  178. new_device_count: {
  179. $sum: '$new_device_count'
  180. },
  181. new_user_count: {
  182. $sum: '$new_user_count'
  183. },
  184. error_count: {
  185. $sum: '$error_count'
  186. },
  187. page_count: {
  188. $sum: '$page_visit_count'
  189. },
  190. total_duration: {
  191. $sum: '$duration'
  192. },
  193. total_user_duration: {
  194. $sum: '$user_duration'
  195. },
  196. total_user_session_times: {
  197. $sum: '$user_session_times'
  198. },
  199. session_times: {
  200. $sum: '$app_launch_count'
  201. },
  202. total_bounce_times: {
  203. $sum: '$bounce_times'
  204. }
  205. },
  206. sort: {
  207. new_device_count: 1,
  208. error_count: 1,
  209. page_count: 1
  210. },
  211. getAll: true
  212. })
  213. let res = {
  214. code: 0,
  215. msg: 'success'
  216. }
  217. if (this.debug) {
  218. console.log('statRes', JSON.stringify(statRes))
  219. }
  220. this.activeDevices = new ActiveDevices()
  221. this.activeUsers = new ActiveUsers()
  222. this.fillData = []
  223. this.composes = []
  224. if (statRes.data.length > 0) {
  225. for (const i in statRes.data) {
  226. await this.getWeekOrMonthData(statRes.data[i])
  227. }
  228. }
  229. //补充数据
  230. await this.replenishStat()
  231. if (this.fillData.length > 0) {
  232. res = await this.batchInsert(this.tableName, this.fillData)
  233. }
  234. return res
  235. }
  236. /**
  237. * 获取周/月维度的填充数据
  238. * @param {Object} data 统计数据
  239. */
  240. async getWeekOrMonthData(data) {
  241. const matchCondition = {
  242. ...data._id,
  243. create_time: {
  244. $gte: this.startTime,
  245. $lte: this.endTime
  246. }
  247. }
  248. // 查询活跃设备数
  249. const statVisitDeviceRes = await this.getCollection(this.activeDevices.tableName).where({
  250. ...matchCondition,
  251. dimension: this.fillType
  252. }).count()
  253. let activeDeviceCount = 0
  254. if (statVisitDeviceRes && statVisitDeviceRes.total > 0) {
  255. activeDeviceCount = statVisitDeviceRes.total
  256. }
  257. // 设备次均停留时长
  258. let avgSessionTime = 0
  259. if (data.total_duration > 0 && data.session_times > 0) {
  260. avgSessionTime = Math.round(data.total_duration / data.session_times)
  261. }
  262. // 设均停留时长
  263. let avgDeviceTime = 0
  264. if (data.total_duration > 0 && activeDeviceCount > 0) {
  265. avgDeviceTime = Math.round(data.total_duration / activeDeviceCount)
  266. }
  267. // 跳出率
  268. let bounceRate = 0
  269. if (data.total_bounce_times > 0 && data.session_times > 0) {
  270. bounceRate = data.total_bounce_times * 100 / data.session_times
  271. bounceRate = parseFloat(bounceRate.toFixed(2))
  272. }
  273. // 累计设备数
  274. let totalDevices = data.new_device_count
  275. const totalDeviceRes = await this.getCollection(this.tableName).where({
  276. ...matchCondition,
  277. dimension: this.fillType,
  278. start_time: {
  279. $lt: this.startTime
  280. }
  281. }).orderBy('start_time', 'desc').limit(1).get()
  282. if (totalDeviceRes && totalDeviceRes.data.length > 0) {
  283. totalDevices += totalDeviceRes.data[0].total_devices
  284. }
  285. //活跃用户数
  286. const statVisitUserRes = await this.getCollection(this.activeUsers.tableName).where({
  287. ...matchCondition,
  288. dimension: this.fillType
  289. }).count()
  290. let activeUserCount = 0
  291. if (statVisitUserRes && statVisitUserRes.total > 0) {
  292. activeUserCount = statVisitUserRes.total
  293. }
  294. // 平台信息
  295. let platformInfo = null
  296. if (this.platforms && this.platforms[data._id.platform_id]) {
  297. platformInfo = this.platforms[data._id.platform_id]
  298. } else {
  299. const platform = new Platform()
  300. platformInfo = await this.getById(platform.tableName, data._id.platform_id)
  301. if (!platformInfo || platformInfo.length === 0) {
  302. platformInfo.code = ''
  303. }
  304. this.platforms[data._id.platform_id] = platformInfo
  305. }
  306. // 渠道信息
  307. let channelInfo = null
  308. if (this.channels && this.channels[data._id.channel_id]) {
  309. channelInfo = this.channels[data._id.channel_id]
  310. } else {
  311. const channel = new Channel()
  312. channelInfo = await this.getById(channel.tableName, data._id.channel_id)
  313. if (!channelInfo || channelInfo.length === 0) {
  314. channelInfo.channel_code = ''
  315. }
  316. this.channels[data._id.channel_id] = channelInfo
  317. }
  318. // 版本信息
  319. let versionInfo = null
  320. if (this.versions && this.versions[data._id.version_id]) {
  321. versionInfo = this.versions[data._id.version_id]
  322. } else {
  323. const version = new Version()
  324. versionInfo = await this.getById(version.tableName, data._id.version_id, false)
  325. if (!versionInfo || versionInfo.length === 0) {
  326. versionInfo.version = ''
  327. }
  328. this.versions[data._id.version_id] = versionInfo
  329. }
  330. //总用户数
  331. const uniIDUsers = new UniIDUsers()
  332. let totalUserCount = await uniIDUsers.getUserCount(data._id.appid, platformInfo.code, channelInfo.channel_code, versionInfo.version, {
  333. $lte: this.endTime
  334. })
  335. //人均停留时长
  336. let avgUserTime = 0
  337. if (data.total_user_duration > 0 && activeUserCount > 0) {
  338. avgUserTime = Math.round(data.total_user_duration / activeUserCount)
  339. }
  340. //用户次均访问时长
  341. let avgUserSessionTime = 0
  342. if (data.total_user_duration > 0 && data.total_user_session_times > 0) {
  343. avgUserSessionTime = Math.round(data.total_user_duration / data.total_user_session_times)
  344. }
  345. //安卓平台活跃设备数需要减去sdk更新后device_id发生变更的设备数
  346. if(platformInfo.code === 'android') {
  347. try{
  348. const statVisitOldDeviceRes = await this.getCollection(this.activeDevices.tableName).where({
  349. ...data._id,
  350. create_time: {
  351. $gte: this.startTime,
  352. $lte: this.endTime
  353. },
  354. dimension: this.fillType + '-old'
  355. }).count()
  356. if (statVisitOldDeviceRes && statVisitOldDeviceRes.total > 0) {
  357. // 活跃设备留存数
  358. activeDeviceCount -= statVisitOldDeviceRes.total
  359. //平均设备停留时长
  360. avgDeviceTime = Math.round(data.total_duration / activeDeviceCount)
  361. }
  362. } catch (e) {
  363. console.log('server error: ' + e)
  364. }
  365. }
  366. const insertParam = {
  367. appid: data._id.appid,
  368. platform_id: data._id.platform_id,
  369. channel_id: data._id.channel_id,
  370. version_id: data._id.version_id,
  371. //总设备数
  372. total_devices: totalDevices,
  373. //本时间段新增设备数
  374. new_device_count: data.new_device_count,
  375. //登录用户会话次数
  376. user_session_times: data.total_user_session_times,
  377. //活跃设备数
  378. active_device_count: activeDeviceCount,
  379. //总用户数
  380. total_users: totalUserCount,
  381. //新增用户数
  382. new_user_count: data.new_user_count,
  383. //活跃用户数
  384. active_user_count: activeUserCount,
  385. //应用启动次数 = 设备会话次数
  386. app_launch_count: data.session_times,
  387. //页面访问次数
  388. page_visit_count: data.page_view_count,
  389. //错误次数
  390. error_count: data.error_count,
  391. //会话总访问时长
  392. duration: data.total_duration,
  393. //用户会话总访问时长
  394. user_duration: data.total_user_duration,
  395. avg_device_session_time: avgSessionTime,
  396. avg_user_session_time: avgUserSessionTime,
  397. avg_device_time: avgDeviceTime,
  398. avg_user_time: avgUserTime,
  399. bounce_times: data.total_bounce_times,
  400. bounce_rate: bounceRate,
  401. retention: {},
  402. dimension: this.fillType,
  403. stat_date: new DateTime().getDate('Ymd', this.startTime),
  404. start_time: this.startTime,
  405. end_time: this.endTime
  406. }
  407. this.fillData.push(insertParam)
  408. this.composes.push(data._id.appid + '_' + data._id.platform_id + '_' + data._id.channel_id + '_' + data
  409. ._id.version_id)
  410. return insertParam
  411. }
  412. /**
  413. * 基础填充数据-目前只有小时和天维度的数据
  414. * @param {Object} data 统计数据
  415. */
  416. async fill(data) {
  417. // 平台信息
  418. let platformInfo = null
  419. if (this.platforms && this.platforms[data._id.platform]) {
  420. //暂存下数据,减少读库
  421. platformInfo = this.platforms[data._id.platform]
  422. } else {
  423. const platform = new Platform()
  424. platformInfo = await platform.getPlatformAndCreate(data._id.platform, null)
  425. if (!platformInfo || platformInfo.length === 0) {
  426. platformInfo._id = ''
  427. }
  428. this.platforms[data._id.platform] = platformInfo
  429. if (this.debug) {
  430. console.log('platformInfo', JSON.stringify(platformInfo))
  431. }
  432. }
  433. // 渠道信息
  434. let channelInfo = null
  435. const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel
  436. if (this.channels && this.channels[channelKey]) {
  437. channelInfo = this.channels[channelKey]
  438. } else {
  439. const channel = new Channel()
  440. channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel)
  441. if (!channelInfo || channelInfo.length === 0) {
  442. channelInfo._id = ''
  443. }
  444. this.channels[channelKey] = channelInfo
  445. if (this.debug) {
  446. console.log('channelInfo', JSON.stringify(channelInfo))
  447. }
  448. }
  449. // 版本信息
  450. let versionInfo = null
  451. const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version
  452. if (this.versions && this.versions[versionKey]) {
  453. versionInfo = this.versions[versionKey]
  454. } else {
  455. const version = new Version()
  456. versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version)
  457. if (!versionInfo || versionInfo.length === 0) {
  458. versionInfo._id = ''
  459. }
  460. this.versions[versionKey] = versionInfo
  461. if (this.debug) {
  462. console.log('versionInfo', JSON.stringify(versionInfo))
  463. }
  464. }
  465. // 访问设备数统计
  466. const matchCondition = data._id
  467. Object.assign(matchCondition, {
  468. create_time: {
  469. $gte: this.startTime,
  470. $lte: this.endTime
  471. }
  472. })
  473. const statVisitDeviceRes = await this.aggregate(this.sessionLog.tableName, {
  474. project: {
  475. appid: 1,
  476. version: 1,
  477. platform: 1,
  478. channel: 1,
  479. device_id: 1,
  480. create_time: 1
  481. },
  482. match: matchCondition,
  483. group: [{
  484. _id: {
  485. device_id: '$device_id'
  486. }
  487. }, {
  488. _id: {},
  489. total_devices: {
  490. $sum: 1
  491. }
  492. }]
  493. })
  494. let activeDeviceCount = 0
  495. if (statVisitDeviceRes.data.length > 0) {
  496. activeDeviceCount = statVisitDeviceRes.data[0].total_devices
  497. }
  498. //安卓平台活跃设备数需要减去sdk更新后device_id发生变更的设备数
  499. if(platformInfo.code === 'android') {
  500. const oldDeviceRes = await this.aggregate(this.sessionLog.tableName, {
  501. project: {
  502. appid: 1,
  503. version: 1,
  504. platform: 1,
  505. channel: 1,
  506. old_device_id: 1,
  507. create_time: 1
  508. },
  509. match: matchCondition,
  510. group: {
  511. _id: {
  512. device_id: '$old_device_id'
  513. },
  514. create_time: {
  515. $min: '$create_time'
  516. },
  517. sessionCount: {
  518. $sum: 1
  519. }
  520. },
  521. sort: {
  522. create_time: 1,
  523. sessionCount: 1
  524. },
  525. getAll: true
  526. })
  527. if(oldDeviceRes.data.length) {
  528. const thisOldDeviceIds = []
  529. for (const tau in oldDeviceRes.data) {
  530. if(oldDeviceRes.data[tau]._id.device_id) {
  531. thisOldDeviceIds.push(oldDeviceRes.data[tau]._id.device_id)
  532. }
  533. }
  534. const statVisitOldDeviceRes = await this.aggregate(this.sessionLog.tableName, {
  535. project: {
  536. appid: 1,
  537. version: 1,
  538. platform: 1,
  539. channel: 1,
  540. device_id: 1,
  541. create_time: 1
  542. },
  543. match: {
  544. ...matchCondition,
  545. device_id: {
  546. $in: thisOldDeviceIds
  547. }
  548. },
  549. group: [{
  550. _id: {
  551. device_id: '$device_id'
  552. }
  553. }, {
  554. _id: {},
  555. total_devices: {
  556. $sum: 1
  557. }
  558. }]
  559. })
  560. if (this.debug) {
  561. console.log('statVisitOldDeviceRes', JSON.stringify(statVisitOldDeviceRes))
  562. }
  563. if (statVisitOldDeviceRes && statVisitOldDeviceRes.data.length > 0) {
  564. // 活跃设备留存数
  565. activeDeviceCount -= statVisitOldDeviceRes.data[0].total_devices
  566. }
  567. }
  568. }
  569. // 错误数量统计
  570. const errorLog = new ErrorLog()
  571. const statErrorRes = await this.getCollection(errorLog.tableName).where(matchCondition).count()
  572. let errorCount = 0
  573. if (statErrorRes && statErrorRes.total > 0) {
  574. errorCount = statErrorRes.total
  575. }
  576. // 设备的次均停留时长,设备访问总时长/设备会话次数
  577. let avgSessionTime = 0
  578. if (data.total_duration > 0 && data.session_times > 0) {
  579. avgSessionTime = Math.round(data.total_duration / data.session_times)
  580. }
  581. // 设均停留时长
  582. let avgDeviceTime = 0
  583. if (data.total_duration > 0 && activeDeviceCount > 0) {
  584. avgDeviceTime = Math.round(data.total_duration / activeDeviceCount)
  585. }
  586. // 跳出率
  587. let bounceTimes = 0
  588. const bounceRes = await this.getCollection(this.sessionLog.tableName).where({
  589. ...matchCondition,
  590. page_count: 1
  591. }).count()
  592. if (bounceRes && bounceRes.total > 0) {
  593. bounceTimes = bounceRes.total
  594. }
  595. let bounceRate = 0
  596. if (bounceTimes > 0 && data.session_times > 0) {
  597. bounceRate = bounceTimes * 100 / data.session_times
  598. bounceRate = parseFloat(bounceRate.toFixed(2))
  599. }
  600. // 应用启动次数 = 会话次数
  601. const launchCount = data.session_times
  602. // 累计设备数
  603. let totalDevices = data.new_device_count
  604. const totalDeviceRes = await this.getCollection(this.tableName).where({
  605. appid: data._id.appid,
  606. platform_id: platformInfo._id,
  607. channel_id: channelInfo._id,
  608. version_id: versionInfo._id,
  609. dimension: this.fillType,
  610. start_time: {
  611. $lt: this.startTime
  612. }
  613. }).orderBy('start_time', 'desc').limit(1).get()
  614. if (totalDeviceRes && totalDeviceRes.data.length > 0) {
  615. totalDevices += totalDeviceRes.data[0].total_devices
  616. }
  617. //活跃用户数
  618. const userSessionLog = new UserSessionLog()
  619. let activeUserCount = 0
  620. const activeUserCountRes = await this.aggregate(userSessionLog.tableName, {
  621. project: {
  622. appid: 1,
  623. version: 1,
  624. platform: 1,
  625. channel: 1,
  626. uid: 1,
  627. create_time: 1
  628. },
  629. match: matchCondition,
  630. group: [{
  631. _id: {
  632. uid: '$uid'
  633. }
  634. }, {
  635. _id: {},
  636. total_users: {
  637. $sum: 1
  638. }
  639. }]
  640. })
  641. if (activeUserCountRes && activeUserCountRes.data.length > 0) {
  642. activeUserCount = activeUserCountRes.data[0].total_users
  643. }
  644. //新增用户数
  645. const uniIDUsers = new UniIDUsers()
  646. let newUserCount = await uniIDUsers.getUserCount(matchCondition.appid, matchCondition.platform,
  647. matchCondition.channel, matchCondition.version, {
  648. $gte: this.startTime,
  649. $lte: this.endTime
  650. })
  651. //总用户数
  652. let totalUserCount = await uniIDUsers.getUserCount(matchCondition.appid, matchCondition.platform,
  653. matchCondition.channel, matchCondition.version, {
  654. $lte: this.endTime
  655. })
  656. //用户停留总时长及总会话次数
  657. let totalUserDuration = 0
  658. let totalUserSessionTimes = 0
  659. const totalUserDurationRes = await this.aggregate(userSessionLog.tableName, {
  660. project: {
  661. appid: 1,
  662. version: 1,
  663. platform: 1,
  664. channel: 1,
  665. duration: 1,
  666. create_time: 1
  667. },
  668. match: matchCondition,
  669. group: [{
  670. _id: {},
  671. total_duration: {
  672. $sum: "$duration"
  673. },
  674. total_session_times: {
  675. $sum: 1
  676. }
  677. }]
  678. })
  679. if (totalUserDurationRes && totalUserDurationRes.data.length > 0) {
  680. totalUserDuration = totalUserDurationRes.data[0].total_duration
  681. totalUserSessionTimes = totalUserDurationRes.data[0].total_session_times
  682. }
  683. //人均停留时长
  684. let avgUserTime = 0
  685. //用户次均访问时长
  686. let avgUserSessionTime = 0
  687. if (totalUserDuration > 0 && activeUserCount > 0) {
  688. avgUserTime = Math.round(totalUserDuration / activeUserCount)
  689. avgUserSessionTime = Math.round(totalUserDuration / totalUserSessionTimes)
  690. }
  691. //设置填充数据
  692. const datetime = new DateTime()
  693. const insertParam = {
  694. appid: data._id.appid,
  695. platform_id: platformInfo._id,
  696. channel_id: channelInfo._id,
  697. version_id: versionInfo._id,
  698. total_devices: totalDevices,
  699. new_device_count: data.new_device_count,
  700. active_device_count: activeDeviceCount,
  701. total_users: totalUserCount,
  702. new_user_count: newUserCount,
  703. active_user_count: activeUserCount,
  704. user_session_times: totalUserSessionTimes,
  705. app_launch_count: launchCount,
  706. page_visit_count: data.page_view_count,
  707. error_count: errorCount,
  708. duration: data.total_duration,
  709. user_duration: totalUserDuration,
  710. avg_device_session_time: avgSessionTime,
  711. avg_user_session_time: avgUserSessionTime,
  712. avg_device_time: avgDeviceTime,
  713. avg_user_time: avgUserTime,
  714. bounce_times: bounceTimes,
  715. bounce_rate: bounceRate,
  716. retention: {},
  717. dimension: this.fillType,
  718. stat_date: datetime.getDate('Ymd', this.startTime),
  719. start_time: this.startTime,
  720. end_time: this.endTime
  721. }
  722. //数据填充
  723. this.fillData.push(insertParam)
  724. this.composes.push(data._id.appid + '_' + platformInfo._id + '_' + channelInfo._id + '_' + versionInfo
  725. ._id)
  726. return insertParam
  727. }
  728. /**
  729. * 基础统计数据补充,防止累计数据丢失
  730. */
  731. async replenishStat() {
  732. if (this.debug) {
  733. console.log('composes data', this.composes)
  734. }
  735. const datetime = new DateTime()
  736. // const {
  737. // startTime
  738. // } = datetime.getTimeDimensionByType(this.fillType, -1, this.startTime)
  739. //上一次统计时间
  740. let preStatRes = await this.getCollection(this.tableName).where({
  741. start_time: {$lt: this.startTime},
  742. dimension: this.fillType
  743. }).orderBy('start_time', 'desc').limit(1).get()
  744. let preStartTime = 0
  745. if(preStatRes && preStatRes.data.length > 0) {
  746. preStartTime = preStatRes.data[0].start_time
  747. }
  748. if (this.debug) {
  749. console.log('replenishStat-preStartTime', preStartTime)
  750. }
  751. if(!preStartTime) {
  752. return false
  753. }
  754. // 上一阶段数据
  755. const preStatData = await this.selectAll(this.tableName, {
  756. start_time: preStartTime,
  757. dimension: this.fillType
  758. }, {
  759. appid: 1,
  760. platform_id: 1,
  761. channel_id: 1,
  762. version_id: 1,
  763. total_devices: 1,
  764. total_users: 1
  765. })
  766. if (!preStatData || preStatData.data.length === 0) {
  767. return false
  768. }
  769. if (this.debug) {
  770. console.log('preStatData', JSON.stringify(preStatData))
  771. }
  772. let preKey
  773. const preKeyArr = []
  774. for (const pi in preStatData.data) {
  775. preKey = preStatData.data[pi].appid + '_' + preStatData.data[pi].platform_id + '_' + preStatData
  776. .data[pi].channel_id + '_' + preStatData.data[pi].version_id
  777. if (!this.composes.includes(preKey) && !preKeyArr.includes(preKey)) {
  778. preKeyArr.push(preKey)
  779. if (this.debug) {
  780. console.log('preKey -add', preKey)
  781. }
  782. this.fillData.push({
  783. appid: preStatData.data[pi].appid,
  784. platform_id: preStatData.data[pi].platform_id,
  785. channel_id: preStatData.data[pi].channel_id,
  786. version_id: preStatData.data[pi].version_id,
  787. total_devices: preStatData.data[pi].total_devices,
  788. new_device_count: 0,
  789. active_device_count: 0,
  790. total_users: preStatData.data[pi].total_users,
  791. new_user_count: 0,
  792. active_user_count: 0,
  793. user_session_times: 0,
  794. app_launch_count: 0,
  795. page_visit_count: 0,
  796. error_count: 0,
  797. duration: 0,
  798. user_duration: 0,
  799. avg_device_session_time: 0,
  800. avg_user_session_time: 0,
  801. avg_device_time: 0,
  802. avg_user_time: 0,
  803. bounce_times: 0,
  804. bounce_rate: 0,
  805. retention: {},
  806. dimension: this.fillType,
  807. stat_date: datetime.getDate('Ymd', this.startTime),
  808. start_time: this.startTime,
  809. end_time: this.endTime
  810. })
  811. } else if (this.debug) {
  812. console.log('preKey -have', preKey)
  813. }
  814. }
  815. return true
  816. }
  817. /**
  818. * 留存数据统计
  819. * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
  820. * @param {Date|Time} date 指定日期或时间戳
  821. * @param {String} mod 统计模块 device:设备,user:用户
  822. */
  823. async retentionStat(type, date, mod = 'device') {
  824. date = date ? date : new DateTime().getTimeBySetDays(-1, date)
  825. const allowedType = ['day', 'week', 'month']
  826. if (!allowedType.includes(type)) {
  827. return {
  828. code: 1002,
  829. msg: 'This type is not allowed'
  830. }
  831. }
  832. let days = []
  833. switch (type) {
  834. case 'week':
  835. case 'month':
  836. days = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  837. break
  838. default:
  839. days = [1, 2, 3, 4, 5, 6, 7, 14, 30]
  840. break
  841. }
  842. let res = {
  843. code: 0,
  844. msg: 'success'
  845. }
  846. for (const day in days) {
  847. //留存统计数据库查询较为复杂,因此这里拆分为多个维度
  848. if (mod && mod === 'user') {
  849. //用户留存统计
  850. if (type === 'day') {
  851. res = await this.userRetentionFillDayly(type, days[day], date)
  852. } else {
  853. res = await this.userRetentionFillWeekOrMonth(type, days[day], date)
  854. }
  855. } else {
  856. //设备留存统计
  857. if (type === 'day') {
  858. res = await this.deviceRetentionFillDayly(type, days[day], date)
  859. } else {
  860. res = await this.deviceRetentionFillWeekOrMonth(type, days[day], date)
  861. }
  862. }
  863. }
  864. return res
  865. }
  866. /**
  867. * 设备日留存统计数据填充
  868. * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
  869. * @param {Date|Time} date 指定日期或时间戳
  870. * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
  871. */
  872. async deviceRetentionFillDayly(type, day, date) {
  873. if (type !== 'day') {
  874. return {
  875. code: 301,
  876. msg: 'Type error:' + type
  877. }
  878. }
  879. const dateTime = new DateTime()
  880. const {
  881. startTime,
  882. endTime
  883. } = dateTime.getTimeDimensionByType(type, 0 - day, date)
  884. if (!startTime || !endTime) {
  885. return {
  886. code: 1001,
  887. msg: 'The statistic time get failed'
  888. }
  889. }
  890. // 截止时间范围
  891. const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date)
  892. // 获取当时批次的统计日志
  893. const resultLogRes = await this.selectAll(this.tableName, {
  894. dimension: type,
  895. start_time: startTime,
  896. end_time: endTime
  897. })
  898. // const resultLogRes = await this.getCollection(this.tableName).where({
  899. // dimension: type,
  900. // start_time: startTime,
  901. // end_time: endTime
  902. // }).get()
  903. if (this.debug) {
  904. console.log('resultLogRes', JSON.stringify(resultLogRes))
  905. }
  906. if (!resultLogRes || resultLogRes.data.length === 0) {
  907. if (this.debug) {
  908. console.log('Not found this log --' + type + ':' + day + ', start:' + startTime + ',endTime:' +
  909. endTime)
  910. }
  911. return {
  912. code: 1000,
  913. msg: 'Not found this log'
  914. }
  915. }
  916. const sessionLog = new SessionLog()
  917. const platform = new Platform()
  918. const channel = new Channel()
  919. const version = new Version()
  920. let res = null
  921. for (const resultIndex in resultLogRes.data) {
  922. const resultLog = resultLogRes.data[resultIndex]
  923. // 平台信息
  924. let platformInfo = null
  925. if (this.platforms && this.platforms[resultLog.platform_id]) {
  926. platformInfo = this.platforms[resultLog.platform_id]
  927. } else {
  928. platformInfo = await this.getById(platform.tableName, resultLog.platform_id)
  929. if (!platformInfo || platformInfo.length === 0) {
  930. platformInfo.code = ''
  931. }
  932. this.platforms[resultLog.platform_id] = platformInfo
  933. if (this.debug) {
  934. console.log('platformInfo', JSON.stringify(platformInfo))
  935. }
  936. }
  937. // 渠道信息
  938. let channelInfo = null
  939. if (this.channels && this.channels[resultLog.channel_id]) {
  940. channelInfo = this.channels[resultLog.channel_id]
  941. } else {
  942. channelInfo = await this.getById(channel.tableName, resultLog.channel_id)
  943. if (!channelInfo || channelInfo.length === 0) {
  944. channelInfo.channel_code = ''
  945. }
  946. this.channels[resultLog.channel_id] = channelInfo
  947. if (this.debug) {
  948. console.log('channelInfo', JSON.stringify(channelInfo))
  949. }
  950. }
  951. // 版本信息
  952. let versionInfo = null
  953. if (this.versions && this.versions[resultLog.version_id]) {
  954. versionInfo = this.versions[resultLog.version_id]
  955. } else {
  956. versionInfo = await this.getById(version.tableName, resultLog.version_id, false)
  957. if (!versionInfo || versionInfo.length === 0) {
  958. versionInfo.version = ''
  959. }
  960. this.versions[resultLog.version_id] = versionInfo
  961. if (this.debug) {
  962. console.log('versionInfo', JSON.stringify(versionInfo))
  963. }
  964. }
  965. // 获取该批次的活跃设备数
  966. const activeDeviceRes = await this.aggregate(sessionLog.tableName, {
  967. project: {
  968. appid: 1,
  969. version: 1,
  970. platform: 1,
  971. channel: 1,
  972. device_id: 1,
  973. create_time: 1
  974. },
  975. match: {
  976. appid: resultLog.appid,
  977. version: versionInfo.version,
  978. platform: platformInfo.code,
  979. channel: channelInfo.channel_code,
  980. create_time: {
  981. $gte: startTime,
  982. $lte: endTime
  983. }
  984. },
  985. group: {
  986. _id: {
  987. device_id: '$device_id'
  988. },
  989. create_time: {
  990. $min: '$create_time'
  991. },
  992. sessionCount: {
  993. $sum: 1
  994. }
  995. },
  996. sort: {
  997. create_time: 1,
  998. sessionCount: 1
  999. },
  1000. getAll: true
  1001. })
  1002. if (this.debug) {
  1003. console.log('activeDeviceRes', JSON.stringify(activeDeviceRes))
  1004. }
  1005. let activeDeviceRate = 0
  1006. let activeDevices = 0
  1007. if (activeDeviceRes && activeDeviceRes.data.length > 0) {
  1008. const thisDayActiveDevices = activeDeviceRes.data.length
  1009. const thisDayActiveDeviceIds = []
  1010. for (const tau in activeDeviceRes.data) {
  1011. thisDayActiveDeviceIds.push(activeDeviceRes.data[tau]._id.device_id)
  1012. }
  1013. if (this.debug) {
  1014. console.log('thisDayActiveDeviceIds', JSON.stringify(thisDayActiveDeviceIds))
  1015. }
  1016. // 留存活跃设备查询
  1017. const retentionActiveDeviceRes = await this.aggregate(sessionLog.tableName, {
  1018. project: {
  1019. appid: 1,
  1020. version: 1,
  1021. platform: 1,
  1022. channel: 1,
  1023. device_id: 1,
  1024. create_time: 1
  1025. },
  1026. match: {
  1027. appid: resultLog.appid,
  1028. version: versionInfo.version,
  1029. platform: platformInfo.code,
  1030. channel: channelInfo.channel_code,
  1031. device_id: {
  1032. $in: thisDayActiveDeviceIds
  1033. },
  1034. create_time: {
  1035. $gte: lastTimeInfo.startTime,
  1036. $lte: lastTimeInfo.endTime
  1037. }
  1038. },
  1039. group: [{
  1040. _id: {
  1041. device_id: '$device_id'
  1042. }
  1043. }, {
  1044. _id: {},
  1045. total_devices: {
  1046. $sum: 1
  1047. }
  1048. }]
  1049. })
  1050. if (this.debug) {
  1051. console.log('retentionActiveDeviceRes', JSON.stringify(retentionActiveDeviceRes))
  1052. }
  1053. if (retentionActiveDeviceRes && retentionActiveDeviceRes.data.length > 0) {
  1054. // 活跃设备留存数
  1055. activeDevices = retentionActiveDeviceRes.data[0].total_devices
  1056. // 活跃设备留存率
  1057. activeDeviceRate = parseFloat((activeDevices * 100 / thisDayActiveDevices).toFixed(2))
  1058. }
  1059. //安卓平台留存需要增加sdk更新后device_id发生变更的设备数
  1060. if(platformInfo.code === 'android') {
  1061. const retentionActiveOldDeviceRes = await this.aggregate(sessionLog.tableName, {
  1062. project: {
  1063. appid: 1,
  1064. version: 1,
  1065. platform: 1,
  1066. channel: 1,
  1067. old_device_id: 1,
  1068. create_time: 1
  1069. },
  1070. match: {
  1071. appid: resultLog.appid,
  1072. version: versionInfo.version,
  1073. platform: platformInfo.code,
  1074. channel: channelInfo.channel_code,
  1075. old_device_id: {
  1076. $in: thisDayActiveDeviceIds
  1077. },
  1078. create_time: {
  1079. $gte: lastTimeInfo.startTime,
  1080. $lte: lastTimeInfo.endTime
  1081. }
  1082. },
  1083. group: [{
  1084. _id: {
  1085. device_id: '$old_device_id'
  1086. }
  1087. }, {
  1088. _id: {},
  1089. total_devices: {
  1090. $sum: 1
  1091. }
  1092. }]
  1093. })
  1094. if (this.debug) {
  1095. console.log('retentionActiveOldDeviceRes', JSON.stringify(retentionActiveOldDeviceRes))
  1096. }
  1097. if (retentionActiveOldDeviceRes && retentionActiveOldDeviceRes.data.length > 0) {
  1098. // 活跃设备留存数
  1099. activeDevices += retentionActiveOldDeviceRes.data[0].total_devices
  1100. // 活跃设备留存率
  1101. activeDeviceRate = parseFloat((activeDevices * 100 / thisDayActiveDevices).toFixed(2))
  1102. }
  1103. }
  1104. }
  1105. // 获取该批次新增设备数
  1106. const newDeviceRes = await this.aggregate(sessionLog.tableName, {
  1107. project: {
  1108. appid: 1,
  1109. version: 1,
  1110. platform: 1,
  1111. channel: 1,
  1112. is_first_visit: 1,
  1113. device_id: 1,
  1114. create_time: 1
  1115. },
  1116. match: {
  1117. appid: resultLog.appid,
  1118. version: versionInfo.version,
  1119. platform: platformInfo.code,
  1120. channel: channelInfo.channel_code,
  1121. is_first_visit: 1,
  1122. create_time: {
  1123. $gte: startTime,
  1124. $lte: endTime
  1125. }
  1126. },
  1127. group: {
  1128. _id: {
  1129. device_id: '$device_id'
  1130. },
  1131. create_time: {
  1132. $min: '$create_time'
  1133. },
  1134. sessionCount: {
  1135. $sum: 1
  1136. }
  1137. },
  1138. sort: {
  1139. create_time: 1,
  1140. sessionCount: 1
  1141. },
  1142. getAll: true
  1143. })
  1144. let newDeviceRate = 0
  1145. let newDevices = 0
  1146. if (newDeviceRes && newDeviceRes.data.length > 0) {
  1147. const thisDayNewDevices = newDeviceRes.data.length
  1148. const thisDayNewDeviceIds = []
  1149. for (const tau in newDeviceRes.data) {
  1150. thisDayNewDeviceIds.push(newDeviceRes.data[tau]._id.device_id)
  1151. }
  1152. if (this.debug) {
  1153. console.log('thisDayNewDeviceIds', JSON.stringify(thisDayNewDeviceIds))
  1154. }
  1155. // 留存的设备查询
  1156. const retentionNewDeviceRes = await this.aggregate(sessionLog.tableName, {
  1157. project: {
  1158. appid: 1,
  1159. version: 1,
  1160. platform: 1,
  1161. channel: 1,
  1162. device_id: 1,
  1163. create_time: 1
  1164. },
  1165. match: {
  1166. appid: resultLog.appid,
  1167. version: versionInfo.version,
  1168. platform: platformInfo.code,
  1169. channel: channelInfo.channel_code,
  1170. device_id: {
  1171. $in: thisDayNewDeviceIds
  1172. },
  1173. create_time: {
  1174. $gte: lastTimeInfo.startTime,
  1175. $lte: lastTimeInfo.endTime
  1176. }
  1177. },
  1178. group: [{
  1179. _id: {
  1180. device_id: '$device_id'
  1181. }
  1182. }, {
  1183. _id: {},
  1184. total_devices: {
  1185. $sum: 1
  1186. }
  1187. }]
  1188. })
  1189. if (retentionNewDeviceRes && retentionNewDeviceRes.data.length > 0) {
  1190. // 新增设备留存数
  1191. newDevices = retentionNewDeviceRes.data[0].total_devices
  1192. // 新增设备留存率
  1193. newDeviceRate = parseFloat((newDevices * 100 / thisDayNewDevices).toFixed(2))
  1194. }
  1195. //安卓平台留存需要增加sdk更新后device_id发生变更的设备数
  1196. if(platformInfo.code === 'android') {
  1197. const retentionNewOldDeviceRes = await this.aggregate(sessionLog.tableName, {
  1198. project: {
  1199. appid: 1,
  1200. version: 1,
  1201. platform: 1,
  1202. channel: 1,
  1203. old_device_id: 1,
  1204. create_time: 1
  1205. },
  1206. match: {
  1207. appid: resultLog.appid,
  1208. version: versionInfo.version,
  1209. platform: platformInfo.code,
  1210. channel: channelInfo.channel_code,
  1211. old_device_id: {
  1212. $in: thisDayNewDeviceIds
  1213. },
  1214. create_time: {
  1215. $gte: lastTimeInfo.startTime,
  1216. $lte: lastTimeInfo.endTime
  1217. }
  1218. },
  1219. group: [{
  1220. _id: {
  1221. device_id: '$old_device_id'
  1222. }
  1223. }, {
  1224. _id: {},
  1225. total_devices: {
  1226. $sum: 1
  1227. }
  1228. }]
  1229. })
  1230. if (this.debug) {
  1231. console.log('retentionNewOldDeviceRes', JSON.stringify(retentionNewOldDeviceRes))
  1232. }
  1233. if (retentionNewOldDeviceRes && retentionNewOldDeviceRes.data.length > 0) {
  1234. // 新增设备留存数
  1235. newDevices += retentionNewOldDeviceRes.data[0].total_devices
  1236. // 新增设备留存率
  1237. newDeviceRate = parseFloat((newDevices * 100 / thisDayNewDevices).toFixed(2))
  1238. }
  1239. }
  1240. }
  1241. // 数据更新
  1242. const retentionData = resultLog.retention
  1243. const dataKey = type.substr(0, 1) + '_' + day
  1244. if (!retentionData.active_device) {
  1245. retentionData.active_device = {}
  1246. }
  1247. retentionData.active_device[dataKey] = {
  1248. device_count: activeDevices,
  1249. device_rate: activeDeviceRate
  1250. }
  1251. if (!retentionData.new_device) {
  1252. retentionData.new_device = {}
  1253. }
  1254. retentionData.new_device[dataKey] = {
  1255. device_count: newDevices,
  1256. device_rate: newDeviceRate
  1257. }
  1258. if (this.debug) {
  1259. console.log('retentionData', JSON.stringify(retentionData))
  1260. }
  1261. res = await this.update(this.tableName, {
  1262. retention: retentionData
  1263. }, {
  1264. _id: resultLog._id
  1265. })
  1266. }
  1267. if (res && res.updated) {
  1268. return {
  1269. code: 0,
  1270. msg: 'success'
  1271. }
  1272. } else {
  1273. return {
  1274. code: 500,
  1275. msg: 'retention data update failed'
  1276. }
  1277. }
  1278. }
  1279. /**
  1280. * 设备周/月留存数据填充
  1281. * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
  1282. * @param {Date|Time} date 指定日期或时间戳
  1283. * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
  1284. */
  1285. async deviceRetentionFillWeekOrMonth(type, day, date) {
  1286. if (!['week', 'month'].includes(type)) {
  1287. return {
  1288. code: 301,
  1289. msg: 'Type error:' + type
  1290. }
  1291. }
  1292. const dateTime = new DateTime()
  1293. const {
  1294. startTime,
  1295. endTime
  1296. } = dateTime.getTimeDimensionByType(type, 0 - day, date)
  1297. if (!startTime || !endTime) {
  1298. return {
  1299. code: 1001,
  1300. msg: 'The statistic time get failed'
  1301. }
  1302. }
  1303. // 截止时间范围
  1304. const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date)
  1305. // 获取当时批次的统计日志
  1306. const resultLogRes = await this.selectAll(this.tableName, {
  1307. dimension: type,
  1308. start_time: startTime,
  1309. end_time: endTime
  1310. })
  1311. if (this.debug) {
  1312. console.log('resultLogRes', JSON.stringify(resultLogRes))
  1313. }
  1314. if (!resultLogRes || resultLogRes.data.length === 0) {
  1315. if (this.debug) {
  1316. console.log('Not found this session log --' + type + ':' + day + ', start:' + startTime +
  1317. ',endTime:' + endTime)
  1318. }
  1319. return {
  1320. code: 1000,
  1321. msg: 'Not found this session log'
  1322. }
  1323. }
  1324. const activeDevicesObj = new ActiveDevices()
  1325. let res = null;
  1326. let activeDeviceRes;
  1327. let activeDeviceRate;
  1328. let activeDevices;
  1329. let newDeviceRate;
  1330. let newDevices
  1331. for (const resultIndex in resultLogRes.data) {
  1332. const resultLog = resultLogRes.data[resultIndex]
  1333. // 获取该批次的活跃设备数
  1334. activeDeviceRes = await this.selectAll(activeDevicesObj.tableName, {
  1335. appid: resultLog.appid,
  1336. platform_id: resultLog.platform_id,
  1337. channel_id: resultLog.channel_id,
  1338. version_id: resultLog.version_id,
  1339. dimension: type,
  1340. create_time: {
  1341. $gte: startTime,
  1342. $lte: endTime
  1343. }
  1344. }, {
  1345. device_id: 1
  1346. })
  1347. if (this.debug) {
  1348. console.log('activeDeviceRes', JSON.stringify(activeDeviceRes))
  1349. }
  1350. activeDeviceRate = 0
  1351. activeDevices = 0
  1352. if (activeDeviceRes && activeDeviceRes.data.length > 0) {
  1353. const thisDayActiveDevices = activeDeviceRes.data.length
  1354. const thisDayActiveDeviceIds = []
  1355. for (const tau in activeDeviceRes.data) {
  1356. thisDayActiveDeviceIds.push(activeDeviceRes.data[tau].device_id)
  1357. }
  1358. if (this.debug) {
  1359. console.log('thisDayActiveDeviceIds', JSON.stringify(thisDayActiveDeviceIds))
  1360. }
  1361. // 留存活跃设备数
  1362. const retentionActiveDeviceRes = await this.getCollection(activeDevicesObj.tableName).where({
  1363. appid: resultLog.appid,
  1364. platform_id: resultLog.platform_id,
  1365. channel_id: resultLog.channel_id,
  1366. version_id: resultLog.version_id,
  1367. device_id: {
  1368. $in: thisDayActiveDeviceIds
  1369. },
  1370. dimension: type,
  1371. create_time: {
  1372. $gte: lastTimeInfo.startTime,
  1373. $lte: lastTimeInfo.endTime
  1374. }
  1375. }).count()
  1376. if (this.debug) {
  1377. console.log('retentionActiveDeviceRes', JSON.stringify(retentionActiveDeviceRes))
  1378. }
  1379. if (retentionActiveDeviceRes && retentionActiveDeviceRes.total > 0) {
  1380. // 活跃设备留存数
  1381. activeDevices = retentionActiveDeviceRes.total
  1382. // 活跃设备留存率
  1383. activeDeviceRate = parseFloat((activeDevices * 100 / thisDayActiveDevices).toFixed(2))
  1384. }
  1385. }
  1386. // 获取该批次的新增设备数
  1387. const newDeviceRes = await this.selectAll(activeDevicesObj.tableName, {
  1388. appid: resultLog.appid,
  1389. platform_id: resultLog.platform_id,
  1390. channel_id: resultLog.channel_id,
  1391. version_id: resultLog.version_id,
  1392. is_new: 1,
  1393. dimension: type,
  1394. create_time: {
  1395. $gte: startTime,
  1396. $lte: endTime
  1397. }
  1398. }, {
  1399. device_id: 1
  1400. })
  1401. newDeviceRate = 0
  1402. newDevices = 0
  1403. if (newDeviceRes && newDeviceRes.data.length > 0) {
  1404. const thisDayNewDevices = newDeviceRes.data.length
  1405. const thisDayNewDeviceIds = []
  1406. for (const tau in newDeviceRes.data) {
  1407. thisDayNewDeviceIds.push(newDeviceRes.data[tau].device_id)
  1408. }
  1409. // 新增设备留存数
  1410. const retentionNewDeviceRes = await this.getCollection(activeDevicesObj.tableName).where({
  1411. appid: resultLog.appid,
  1412. platform_id: resultLog.platform_id,
  1413. channel_id: resultLog.channel_id,
  1414. version_id: resultLog.version_id,
  1415. device_id: {
  1416. $in: thisDayNewDeviceIds
  1417. },
  1418. dimension: type,
  1419. create_time: {
  1420. $gte: lastTimeInfo.startTime,
  1421. $lte: lastTimeInfo.endTime
  1422. }
  1423. }).count()
  1424. if (retentionNewDeviceRes && retentionNewDeviceRes.total > 0) {
  1425. // 新增设备留存数
  1426. newDevices = retentionNewDeviceRes.total
  1427. // 新增设备留存率
  1428. newDeviceRate = parseFloat((newDevices * 100 / thisDayNewDevices).toFixed(2))
  1429. }
  1430. }
  1431. // 数据更新
  1432. const retentionData = resultLog.retention
  1433. const dataKey = type.substr(0, 1) + '_' + day
  1434. if (!retentionData.active_device) {
  1435. retentionData.active_device = {}
  1436. }
  1437. retentionData.active_device[dataKey] = {
  1438. device_count: activeDevices,
  1439. device_rate: activeDeviceRate
  1440. }
  1441. if (!retentionData.new_device) {
  1442. retentionData.new_device = {}
  1443. }
  1444. retentionData.new_device[dataKey] = {
  1445. device_count: newDevices,
  1446. device_rate: newDeviceRate
  1447. }
  1448. if (this.debug) {
  1449. console.log('retentionData', JSON.stringify(retentionData))
  1450. }
  1451. res = await this.update(this.tableName, {
  1452. retention: retentionData
  1453. }, {
  1454. _id: resultLog._id
  1455. })
  1456. }
  1457. if (res && res.updated) {
  1458. return {
  1459. code: 0,
  1460. msg: 'success'
  1461. }
  1462. } else {
  1463. return {
  1464. code: 500,
  1465. msg: 'retention data update failed'
  1466. }
  1467. }
  1468. }
  1469. /**
  1470. * 用户日留存数据填充
  1471. * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
  1472. * @param {Date|Time} date 指定日期或时间戳
  1473. * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
  1474. */
  1475. async userRetentionFillDayly(type, day, date) {
  1476. if (type !== 'day') {
  1477. return {
  1478. code: 301,
  1479. msg: 'Type error:' + type
  1480. }
  1481. }
  1482. const dateTime = new DateTime()
  1483. const {
  1484. startTime,
  1485. endTime
  1486. } = dateTime.getTimeDimensionByType(type, 0 - day, date)
  1487. if (!startTime || !endTime) {
  1488. return {
  1489. code: 1001,
  1490. msg: 'The statistic time get failed'
  1491. }
  1492. }
  1493. // 截止时间范围
  1494. const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date)
  1495. // 获取当时批次的统计日志
  1496. const resultLogRes = await this.selectAll(this.tableName, {
  1497. dimension: type,
  1498. start_time: startTime,
  1499. end_time: endTime
  1500. })
  1501. if (this.debug) {
  1502. console.log('resultLogRes', JSON.stringify(resultLogRes))
  1503. }
  1504. if (!resultLogRes || resultLogRes.data.length === 0) {
  1505. if (this.debug) {
  1506. console.log('Not found this log --' + type + ':' + day + ', start:' + startTime + ',endTime:' +
  1507. endTime)
  1508. }
  1509. return {
  1510. code: 1000,
  1511. msg: 'Not found this log'
  1512. }
  1513. }
  1514. const userSessionLog = new UserSessionLog()
  1515. const uniIDUsers = new UniIDUsers()
  1516. const platform = new Platform()
  1517. const channel = new Channel()
  1518. const version = new Version()
  1519. let res = null
  1520. for (const resultIndex in resultLogRes.data) {
  1521. const resultLog = resultLogRes.data[resultIndex]
  1522. // 平台信息
  1523. let platformInfo = null
  1524. if (this.platforms && this.platforms[resultLog.platform_id]) {
  1525. platformInfo = this.platforms[resultLog.platform_id]
  1526. } else {
  1527. platformInfo = await this.getById(platform.tableName, resultLog.platform_id)
  1528. if (!platformInfo || platformInfo.length === 0) {
  1529. platformInfo.code = ''
  1530. }
  1531. this.platforms[resultLog.platform_id] = platformInfo
  1532. if (this.debug) {
  1533. console.log('platformInfo', JSON.stringify(platformInfo))
  1534. }
  1535. }
  1536. // 渠道信息
  1537. let channelInfo = null
  1538. if (this.channels && this.channels[resultLog.channel_id]) {
  1539. channelInfo = this.channels[resultLog.channel_id]
  1540. } else {
  1541. channelInfo = await this.getById(channel.tableName, resultLog.channel_id)
  1542. if (!channelInfo || channelInfo.length === 0) {
  1543. channelInfo.channel_code = ''
  1544. }
  1545. this.channels[resultLog.channel_id] = channelInfo
  1546. if (this.debug) {
  1547. console.log('channelInfo', JSON.stringify(channelInfo))
  1548. }
  1549. }
  1550. // 版本信息
  1551. let versionInfo = null
  1552. if (this.versions && this.versions[resultLog.version_id]) {
  1553. versionInfo = this.versions[resultLog.version_id]
  1554. } else {
  1555. versionInfo = await this.getById(version.tableName, resultLog.version_id, false)
  1556. if (!versionInfo || versionInfo.length === 0) {
  1557. versionInfo.version = ''
  1558. }
  1559. this.versions[resultLog.version_id] = versionInfo
  1560. if (this.debug) {
  1561. console.log('versionInfo', JSON.stringify(versionInfo))
  1562. }
  1563. }
  1564. // 获取该时间段内的活跃用户
  1565. const activeUserRes = await this.aggregate(userSessionLog.tableName, {
  1566. project: {
  1567. appid: 1,
  1568. version: 1,
  1569. platform: 1,
  1570. channel: 1,
  1571. uid: 1,
  1572. create_time: 1
  1573. },
  1574. match: {
  1575. appid: resultLog.appid,
  1576. version: versionInfo.version,
  1577. platform: platformInfo.code,
  1578. channel: channelInfo.channel_code,
  1579. create_time: {
  1580. $gte: startTime,
  1581. $lte: endTime
  1582. }
  1583. },
  1584. group: {
  1585. _id: {
  1586. uid: '$uid'
  1587. },
  1588. create_time: {
  1589. $min: '$create_time'
  1590. },
  1591. sessionCount: {
  1592. $sum: 1
  1593. }
  1594. },
  1595. sort: {
  1596. create_time: 1,
  1597. sessionCount: 1
  1598. },
  1599. getAll: true
  1600. })
  1601. if (this.debug) {
  1602. console.log('activeUserRes', JSON.stringify(activeUserRes))
  1603. }
  1604. //活跃用户留存率
  1605. let activeUserRate = 0
  1606. //活跃用户留存数
  1607. let activeUsers = 0
  1608. if (activeUserRes && activeUserRes.data.length > 0) {
  1609. const thisDayActiveUsers = activeUserRes.data.length
  1610. //获取该时间段内的活跃用户编号,这里没用lookup联查是因为数据量较大时lookup效率很低
  1611. const thisDayActiveUids = []
  1612. for (let tau in activeUserRes.data) {
  1613. thisDayActiveUids.push(activeUserRes.data[tau]._id.uid)
  1614. }
  1615. if (this.debug) {
  1616. console.log('thisDayActiveUids', JSON.stringify(thisDayActiveUids))
  1617. }
  1618. // 留存活跃用户数
  1619. const retentionActiveUserRes = await this.aggregate(userSessionLog.tableName, {
  1620. project: {
  1621. appid: 1,
  1622. version: 1,
  1623. platform: 1,
  1624. channel: 1,
  1625. uid: 1,
  1626. create_time: 1
  1627. },
  1628. match: {
  1629. appid: resultLog.appid,
  1630. version: versionInfo.version,
  1631. platform: platformInfo.code,
  1632. channel: channelInfo.channel_code,
  1633. uid: {
  1634. $in: thisDayActiveUids
  1635. },
  1636. create_time: {
  1637. $gte: lastTimeInfo.startTime,
  1638. $lte: lastTimeInfo.endTime
  1639. }
  1640. },
  1641. group: [{
  1642. _id: {
  1643. uid: '$uid'
  1644. }
  1645. }, {
  1646. _id: {},
  1647. total_users: {
  1648. $sum: 1
  1649. }
  1650. }]
  1651. })
  1652. if (this.debug) {
  1653. console.log('retentionActiveUserRes', JSON.stringify(retentionActiveUserRes))
  1654. }
  1655. if (retentionActiveUserRes && retentionActiveUserRes.data.length > 0) {
  1656. // 活跃用户留存数
  1657. activeUsers = retentionActiveUserRes.data[0].total_users
  1658. // 活跃用户留存率
  1659. activeUserRate = parseFloat((activeUsers * 100 / thisDayActiveUsers).toFixed(2))
  1660. }
  1661. }
  1662. //新增用户编号
  1663. const thisDayNewUids = await uniIDUsers.getUserIds(resultLog.appid, platformInfo.code, channelInfo.channel_code, versionInfo.version, {
  1664. $gte: startTime,
  1665. $lte: endTime
  1666. })
  1667. //新增用户留存率
  1668. let newUserRate = 0
  1669. //新增用户留存数
  1670. let newUsers = 0
  1671. if (thisDayNewUids.length > 0) {
  1672. // 现在依然活跃的用户数
  1673. const retentionNewUserRes = await this.aggregate(userSessionLog.tableName, {
  1674. project: {
  1675. appid: 1,
  1676. version: 1,
  1677. platform: 1,
  1678. channel: 1,
  1679. uid: 1,
  1680. create_time: 1
  1681. },
  1682. match: {
  1683. appid: resultLog.appid,
  1684. version: versionInfo.version,
  1685. platform: platformInfo.code,
  1686. channel: channelInfo.channel_code,
  1687. uid: {
  1688. $in: thisDayNewUids
  1689. },
  1690. create_time: {
  1691. $gte: lastTimeInfo.startTime,
  1692. $lte: lastTimeInfo.endTime
  1693. }
  1694. },
  1695. group: [{
  1696. _id: {
  1697. uid: '$uid'
  1698. }
  1699. }, {
  1700. _id: {},
  1701. total_users: {
  1702. $sum: 1
  1703. }
  1704. }]
  1705. })
  1706. if (retentionNewUserRes && retentionNewUserRes.data.length > 0) {
  1707. // 新增用户留存数
  1708. newUsers = retentionNewUserRes.data[0].total_users
  1709. // 新增用户留存率
  1710. newUserRate = parseFloat((newUsers * 100 / thisDayNewUids.length).toFixed(2))
  1711. }
  1712. }
  1713. // 数据更新
  1714. const retentionData = resultLog.retention
  1715. const dataKey = type.substr(0, 1) + '_' + day
  1716. if (!retentionData.active_user) {
  1717. retentionData.active_user = {}
  1718. }
  1719. retentionData.active_user[dataKey] = {
  1720. user_count: activeUsers,
  1721. user_rate: activeUserRate
  1722. }
  1723. if (!retentionData.new_user) {
  1724. retentionData.new_user = {}
  1725. }
  1726. retentionData.new_user[dataKey] = {
  1727. user_count: newUsers,
  1728. user_rate: newUserRate
  1729. }
  1730. if (this.debug) {
  1731. console.log('retentionData', JSON.stringify(retentionData))
  1732. }
  1733. res = await this.update(this.tableName, {
  1734. retention: retentionData
  1735. }, {
  1736. _id: resultLog._id
  1737. })
  1738. }
  1739. if (res && res.updated) {
  1740. return {
  1741. code: 0,
  1742. msg: 'success'
  1743. }
  1744. } else {
  1745. return {
  1746. code: 500,
  1747. msg: 'retention data update failed'
  1748. }
  1749. }
  1750. }
  1751. /**
  1752. * 用户周/月留存数据填充
  1753. * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
  1754. * @param {Date|Time} date 指定日期或时间戳
  1755. * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
  1756. */
  1757. async userRetentionFillWeekOrMonth(type, day, date) {
  1758. if (!['week', 'month'].includes(type)) {
  1759. return {
  1760. code: 301,
  1761. msg: 'Type error:' + type
  1762. }
  1763. }
  1764. const dateTime = new DateTime()
  1765. const {
  1766. startTime,
  1767. endTime
  1768. } = dateTime.getTimeDimensionByType(type, 0 - day, date)
  1769. if (!startTime || !endTime) {
  1770. return {
  1771. code: 1001,
  1772. msg: 'The statistic time get failed'
  1773. }
  1774. }
  1775. // 截止时间范围
  1776. const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date)
  1777. // 获取当时批次的统计日志
  1778. const resultLogRes = await this.selectAll(this.tableName, {
  1779. dimension: type,
  1780. start_time: startTime,
  1781. end_time: endTime
  1782. })
  1783. if (this.debug) {
  1784. console.log('resultLogRes', JSON.stringify(resultLogRes))
  1785. }
  1786. if (!resultLogRes || resultLogRes.data.length === 0) {
  1787. if (this.debug) {
  1788. console.log('Not found this session log --' + type + ':' + day + ', start:' + startTime +
  1789. ',endTime:' + endTime)
  1790. }
  1791. return {
  1792. code: 1000,
  1793. msg: 'Not found this session log'
  1794. }
  1795. }
  1796. const activeUserObj = new ActiveUsers()
  1797. const uniIDUsers = new UniIDUsers()
  1798. const platform = new Platform()
  1799. const channel = new Channel()
  1800. const version = new Version()
  1801. let res = null
  1802. //活跃用户留存率
  1803. let activeUserRate
  1804. //活跃用户留存数
  1805. let activeUsers
  1806. //新增用户留存率
  1807. let newUserRate
  1808. //新增用户留存数
  1809. let newUsers
  1810. for (const resultIndex in resultLogRes.data) {
  1811. const resultLog = resultLogRes.data[resultIndex]
  1812. // 平台信息
  1813. let platformInfo = null
  1814. if (this.platforms && this.platforms[resultLog.platform_id]) {
  1815. platformInfo = this.platforms[resultLog.platform_id]
  1816. } else {
  1817. platformInfo = await this.getById(platform.tableName, resultLog.platform_id)
  1818. if (!platformInfo || platformInfo.length === 0) {
  1819. platformInfo.code = ''
  1820. }
  1821. this.platforms[resultLog.platform_id] = platformInfo
  1822. if (this.debug) {
  1823. console.log('platformInfo', JSON.stringify(platformInfo))
  1824. }
  1825. }
  1826. // 渠道信息
  1827. let channelInfo = null
  1828. if (this.channels && this.channels[resultLog.channel_id]) {
  1829. channelInfo = this.channels[resultLog.channel_id]
  1830. } else {
  1831. channelInfo = await this.getById(channel.tableName, resultLog.channel_id)
  1832. if (!channelInfo || channelInfo.length === 0) {
  1833. channelInfo.channel_code = ''
  1834. }
  1835. this.channels[resultLog.channel_id] = channelInfo
  1836. if (this.debug) {
  1837. console.log('channelInfo', JSON.stringify(channelInfo))
  1838. }
  1839. }
  1840. // 版本信息
  1841. let versionInfo = null
  1842. if (this.versions && this.versions[resultLog.version_id]) {
  1843. versionInfo = this.versions[resultLog.version_id]
  1844. } else {
  1845. versionInfo = await this.getById(version.tableName, resultLog.version_id, false)
  1846. if (!versionInfo || versionInfo.length === 0) {
  1847. versionInfo.version = ''
  1848. }
  1849. this.versions[resultLog.version_id] = versionInfo
  1850. if (this.debug) {
  1851. console.log('versionInfo', JSON.stringify(versionInfo))
  1852. }
  1853. }
  1854. // 获取该批次的活跃用户数
  1855. const activeUserRes = await this.selectAll(activeUserObj.tableName, {
  1856. appid: resultLog.appid,
  1857. platform_id: resultLog.platform_id,
  1858. channel_id: resultLog.channel_id,
  1859. version_id: resultLog.version_id,
  1860. dimension: type,
  1861. create_time: {
  1862. $gte: startTime,
  1863. $lte: endTime
  1864. }
  1865. }, {
  1866. uid: 1
  1867. })
  1868. if (this.debug) {
  1869. console.log('activeUserRes', JSON.stringify(activeUserRes))
  1870. }
  1871. activeUserRate = 0
  1872. activeUsers = 0
  1873. if (activeUserRes && activeUserRes.data.length > 0) {
  1874. const thisDayactiveUsers = activeUserRes.data.length
  1875. const thisDayActiveDeviceIds = []
  1876. for (const tau in activeUserRes.data) {
  1877. thisDayActiveDeviceIds.push(activeUserRes.data[tau].uid)
  1878. }
  1879. if (this.debug) {
  1880. console.log('thisDayActiveDeviceIds', JSON.stringify(thisDayActiveDeviceIds))
  1881. }
  1882. // 留存活跃用户数
  1883. const retentionactiveUserRes = await this.getCollection(activeUserObj.tableName).where({
  1884. appid: resultLog.appid,
  1885. platform_id: resultLog.platform_id,
  1886. channel_id: resultLog.channel_id,
  1887. version_id: resultLog.version_id,
  1888. uid: {
  1889. $in: thisDayActiveDeviceIds
  1890. },
  1891. dimension: type,
  1892. create_time: {
  1893. $gte: lastTimeInfo.startTime,
  1894. $lte: lastTimeInfo.endTime
  1895. }
  1896. }).count()
  1897. if (this.debug) {
  1898. console.log('retentionactiveUserRes', JSON.stringify(retentionactiveUserRes))
  1899. }
  1900. if (retentionactiveUserRes && retentionactiveUserRes.total > 0) {
  1901. // 活跃用户留存数
  1902. activeUsers = retentionactiveUserRes.total
  1903. // 活跃用户留存率
  1904. activeUserRate = parseFloat((activeUsers * 100 / thisDayactiveUsers).toFixed(2))
  1905. }
  1906. }
  1907. //新增用户编号
  1908. const thisDayNewUids = await uniIDUsers.getUserIds(resultLog.appid, platformInfo.code, channelInfo.channel_code, versionInfo.version, {
  1909. $gte: startTime,
  1910. $lte: endTime
  1911. })
  1912. // 新增用户留存率
  1913. newUserRate = 0
  1914. // 新增用户留存数
  1915. newUsers = 0
  1916. if(thisDayNewUids && thisDayNewUids.length > 0) {
  1917. // 新增用户留存数
  1918. const retentionnewUserRes = await this.getCollection(activeUserObj.tableName).where({
  1919. appid: resultLog.appid,
  1920. platform_id: resultLog.platform_id,
  1921. channel_id: resultLog.channel_id,
  1922. version_id: resultLog.version_id,
  1923. uid: {
  1924. $in: thisDayNewUids
  1925. },
  1926. dimension: type,
  1927. create_time: {
  1928. $gte: lastTimeInfo.startTime,
  1929. $lte: lastTimeInfo.endTime
  1930. }
  1931. }).count()
  1932. if (retentionnewUserRes && retentionnewUserRes.total > 0) {
  1933. // 新增用户留存数
  1934. newUsers = retentionnewUserRes.total
  1935. // 新增用户留存率
  1936. newUserRate = parseFloat((newUsers * 100 / thisDayNewUids.length).toFixed(2))
  1937. }
  1938. }
  1939. // 数据更新
  1940. const retentionData = resultLog.retention
  1941. const dataKey = type.substr(0, 1) + '_' + day
  1942. if (!retentionData.active_user) {
  1943. retentionData.active_user = {}
  1944. }
  1945. retentionData.active_user[dataKey] = {
  1946. user_count: activeUsers,
  1947. user_rate: activeUserRate
  1948. }
  1949. if (!retentionData.new_user) {
  1950. retentionData.new_user = {}
  1951. }
  1952. retentionData.new_user[dataKey] = {
  1953. user_count: newUsers,
  1954. user_rate: newUserRate
  1955. }
  1956. if (this.debug) {
  1957. console.log('retentionData', JSON.stringify(retentionData))
  1958. }
  1959. res = await this.update(this.tableName, {
  1960. retention: retentionData
  1961. }, {
  1962. _id: resultLog._id
  1963. })
  1964. }
  1965. if (res && res.updated) {
  1966. return {
  1967. code: 0,
  1968. msg: 'success'
  1969. }
  1970. } else {
  1971. return {
  1972. code: 500,
  1973. msg: 'retention data update failed'
  1974. }
  1975. }
  1976. }
  1977. /**
  1978. * 清理实时统计的日志
  1979. * @param {Number} days 实时统计日志保留天数
  1980. */
  1981. async cleanHourLog(days = 7) {
  1982. console.log('clean hour logs - day:', days)
  1983. const dateTime = new DateTime()
  1984. const res = await this.delete(this.tableName, {
  1985. dimension: 'hour',
  1986. start_time: {
  1987. $lt: dateTime.getTimeBySetDays(0 - days)
  1988. }
  1989. })
  1990. if (!res.code) {
  1991. console.log('clean hour logs - res:', res)
  1992. }
  1993. }
  1994. }