1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152 |
- /**
- * @class StatResult 基础数据结果统计模型
- */
- const BaseMod = require('./base')
- const Platform = require('./platform')
- const Channel = require('./channel')
- const Version = require('./version')
- const SessionLog = require('./sessionLog')
- const UserSessionLog = require('./userSessionLog')
- const ErrorLog = require('./errorLog')
- const ActiveDevices = require('./activeDevices')
- const ActiveUsers = require('./activeUsers')
- const UniIDUsers = require('./uniIDUsers')
- const {
- DateTime
- } = require('../lib')
- module.exports = class StatResult extends BaseMod {
- constructor() {
- super()
- this.tableName = 'result'
- this.platforms = []
- this.channels = []
- this.versions = []
- }
- /**
- * 基础数据统计
- * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
- * @param {Date|Time} date 指定日期或时间戳
- * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
- */
- async stat(type, date, reset) {
- const allowedType = ['hour', 'day', 'week', 'month']
- if (!allowedType.includes(type)) {
- return {
- code: 1002,
- msg: 'This type is not allowed'
- }
- }
- if (this.debug) {
- console.log('result --type:' + type + ', date:' + date + ', reset:' + reset)
- }
- this.fillType = type
- const dateTime = new DateTime()
- const dateDimension = dateTime.getTimeDimensionByType(type, -1, date)
- this.startTime = dateDimension.startTime
- this.endTime = dateDimension.endTime
- if (this.debug) {
- console.log('dimension time', this.startTime + '--' + this.endTime)
- }
- // 查看当前时间段日志是否已存在,防止重复生成
- if (!reset) {
- const checkRes = await this.getCollection(this.tableName).where({
- dimension: this.fillType,
- start_time: this.startTime,
- end_time: this.endTime
- }).get()
- if (checkRes.data.length > 0) {
- console.log('log have existed')
- return {
- code: 1003,
- msg: 'This log have existed'
- }
- }
- } else {
- const delRes = await this.delete(this.tableName, {
- start_time: this.startTime,
- end_time: this.endTime
- })
- console.log('delete old data result:', JSON.stringify(delRes))
- }
- // 周月数据单独统计
- if (['week', 'month'].includes(this.fillType)) {
- return await this.statWeekOrMonth()
- }
- // 数据获取
- this.sessionLog = new SessionLog()
- const statRes = await this.aggregate(this.sessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- is_first_visit: 1,
- page_count: 1,
- duration: 1,
- create_time: 1
- },
- match: {
- create_time: {
- $gte: this.startTime,
- $lte: this.endTime
- }
- },
- group: {
- _id: {
- appid: '$appid',
- version: '$version',
- platform: '$platform',
- channel: '$channel'
- },
- new_device_count: {
- $sum: '$is_first_visit'
- },
- page_view_count: {
- $sum: '$page_count'
- },
- total_duration: {
- $sum: '$duration'
- },
- session_times: {
- $sum: 1
- }
- },
- sort: {
- new_device_count: 1,
- page_view_count: 1,
- session_times: 1
- },
- getAll: true
- })
- let res = {
- code: 0,
- msg: 'success'
- }
- if (this.debug) {
- console.log('statRes', JSON.stringify(statRes))
- }
- this.fillData = []
- this.composes = []
- if (statRes.data.length > 0) {
- for (const i in statRes.data) {
- await this.fill(statRes.data[i])
- }
- }
- //补充数据
- await this.replenishStat()
- if (this.fillData.length > 0) {
- res = await this.batchInsert(this.tableName, this.fillData)
- }
- return res
- }
- /**
- * 按周/月统计
- */
- async statWeekOrMonth() {
- const statRes = await this.aggregate(this.tableName, {
- project: {
- appid: 1,
- version_id: 1,
- platform_id: 1,
- channel_id: 1,
- new_device_count: 1,
- new_user_count: 1,
- page_visit_count: 1,
- user_visit_times: 1,
- app_launch_count: 1,
- error_count: 1,
- bounce_times: 1,
- duration: 1,
- user_duration: 1,
- dimension: 1,
- start_time: 1
- },
- match: {
- dimension: 'day',
- start_time: {
- $gte: this.startTime,
- $lte: this.endTime
- }
- },
- group: {
- _id: {
- appid: '$appid',
- version_id: '$version_id',
- platform_id: '$platform_id',
- channel_id: '$channel_id'
- },
- new_device_count: {
- $sum: '$new_device_count'
- },
- new_user_count: {
- $sum: '$new_user_count'
- },
- error_count: {
- $sum: '$error_count'
- },
- page_count: {
- $sum: '$page_visit_count'
- },
- total_duration: {
- $sum: '$duration'
- },
- total_user_duration: {
- $sum: '$user_duration'
- },
- total_user_session_times: {
- $sum: '$user_session_times'
- },
- session_times: {
- $sum: '$app_launch_count'
- },
- total_bounce_times: {
- $sum: '$bounce_times'
- }
- },
- sort: {
- new_device_count: 1,
- error_count: 1,
- page_count: 1
- },
- getAll: true
- })
- let res = {
- code: 0,
- msg: 'success'
- }
- if (this.debug) {
- console.log('statRes', JSON.stringify(statRes))
- }
- this.activeDevices = new ActiveDevices()
- this.activeUsers = new ActiveUsers()
- this.fillData = []
- this.composes = []
- if (statRes.data.length > 0) {
- for (const i in statRes.data) {
- await this.getWeekOrMonthData(statRes.data[i])
- }
- }
- //补充数据
- await this.replenishStat()
- if (this.fillData.length > 0) {
- res = await this.batchInsert(this.tableName, this.fillData)
- }
- return res
- }
- /**
- * 获取周/月维度的填充数据
- * @param {Object} data 统计数据
- */
- async getWeekOrMonthData(data) {
- const matchCondition = {
- ...data._id,
- create_time: {
- $gte: this.startTime,
- $lte: this.endTime
- }
- }
- // 查询活跃设备数
- const statVisitDeviceRes = await this.getCollection(this.activeDevices.tableName).where({
- ...matchCondition,
- dimension: this.fillType
- }).count()
- let activeDeviceCount = 0
- if (statVisitDeviceRes && statVisitDeviceRes.total > 0) {
- activeDeviceCount = statVisitDeviceRes.total
- }
- // 设备次均停留时长
- let avgSessionTime = 0
- if (data.total_duration > 0 && data.session_times > 0) {
- avgSessionTime = Math.round(data.total_duration / data.session_times)
- }
- // 设均停留时长
- let avgDeviceTime = 0
- if (data.total_duration > 0 && activeDeviceCount > 0) {
- avgDeviceTime = Math.round(data.total_duration / activeDeviceCount)
- }
- // 跳出率
- let bounceRate = 0
- if (data.total_bounce_times > 0 && data.session_times > 0) {
- bounceRate = data.total_bounce_times * 100 / data.session_times
- bounceRate = parseFloat(bounceRate.toFixed(2))
- }
- // 累计设备数
- let totalDevices = data.new_device_count
- const totalDeviceRes = await this.getCollection(this.tableName).where({
- ...matchCondition,
- dimension: this.fillType,
- start_time: {
- $lt: this.startTime
- }
- }).orderBy('start_time', 'desc').limit(1).get()
- if (totalDeviceRes && totalDeviceRes.data.length > 0) {
- totalDevices += totalDeviceRes.data[0].total_devices
- }
- //活跃用户数
- const statVisitUserRes = await this.getCollection(this.activeUsers.tableName).where({
- ...matchCondition,
- dimension: this.fillType
- }).count()
- let activeUserCount = 0
- if (statVisitUserRes && statVisitUserRes.total > 0) {
- activeUserCount = statVisitUserRes.total
- }
- // 平台信息
- let platformInfo = null
- if (this.platforms && this.platforms[data._id.platform_id]) {
- platformInfo = this.platforms[data._id.platform_id]
- } else {
- const platform = new Platform()
- platformInfo = await this.getById(platform.tableName, data._id.platform_id)
- if (!platformInfo || platformInfo.length === 0) {
- platformInfo.code = ''
- }
- this.platforms[data._id.platform_id] = platformInfo
- }
- // 渠道信息
- let channelInfo = null
- if (this.channels && this.channels[data._id.channel_id]) {
- channelInfo = this.channels[data._id.channel_id]
- } else {
- const channel = new Channel()
- channelInfo = await this.getById(channel.tableName, data._id.channel_id)
- if (!channelInfo || channelInfo.length === 0) {
- channelInfo.channel_code = ''
- }
- this.channels[data._id.channel_id] = channelInfo
- }
- // 版本信息
- let versionInfo = null
- if (this.versions && this.versions[data._id.version_id]) {
- versionInfo = this.versions[data._id.version_id]
- } else {
- const version = new Version()
- versionInfo = await this.getById(version.tableName, data._id.version_id, false)
- if (!versionInfo || versionInfo.length === 0) {
- versionInfo.version = ''
- }
- this.versions[data._id.version_id] = versionInfo
- }
- //总用户数
- const uniIDUsers = new UniIDUsers()
- let totalUserCount = await uniIDUsers.getUserCount(data._id.appid, platformInfo.code, channelInfo.channel_code, versionInfo.version, {
- $lte: this.endTime
- })
- //人均停留时长
- let avgUserTime = 0
- if (data.total_user_duration > 0 && activeUserCount > 0) {
- avgUserTime = Math.round(data.total_user_duration / activeUserCount)
- }
- //用户次均访问时长
- let avgUserSessionTime = 0
- if (data.total_user_duration > 0 && data.total_user_session_times > 0) {
- avgUserSessionTime = Math.round(data.total_user_duration / data.total_user_session_times)
- }
- //安卓平台活跃设备数需要减去sdk更新后device_id发生变更的设备数
- if(platformInfo.code === 'android') {
- try{
- const statVisitOldDeviceRes = await this.getCollection(this.activeDevices.tableName).where({
- ...data._id,
- create_time: {
- $gte: this.startTime,
- $lte: this.endTime
- },
- dimension: this.fillType + '-old'
- }).count()
- if (statVisitOldDeviceRes && statVisitOldDeviceRes.total > 0) {
- // 活跃设备留存数
- activeDeviceCount -= statVisitOldDeviceRes.total
- //平均设备停留时长
- avgDeviceTime = Math.round(data.total_duration / activeDeviceCount)
- }
- } catch (e) {
- console.log('server error: ' + e)
- }
- }
- const insertParam = {
- appid: data._id.appid,
- platform_id: data._id.platform_id,
- channel_id: data._id.channel_id,
- version_id: data._id.version_id,
- //总设备数
- total_devices: totalDevices,
- //本时间段新增设备数
- new_device_count: data.new_device_count,
- //登录用户会话次数
- user_session_times: data.total_user_session_times,
- //活跃设备数
- active_device_count: activeDeviceCount,
- //总用户数
- total_users: totalUserCount,
- //新增用户数
- new_user_count: data.new_user_count,
- //活跃用户数
- active_user_count: activeUserCount,
- //应用启动次数 = 设备会话次数
- app_launch_count: data.session_times,
- //页面访问次数
- page_visit_count: data.page_view_count,
- //错误次数
- error_count: data.error_count,
- //会话总访问时长
- duration: data.total_duration,
- //用户会话总访问时长
- user_duration: data.total_user_duration,
- avg_device_session_time: avgSessionTime,
- avg_user_session_time: avgUserSessionTime,
- avg_device_time: avgDeviceTime,
- avg_user_time: avgUserTime,
- bounce_times: data.total_bounce_times,
- bounce_rate: bounceRate,
- retention: {},
- dimension: this.fillType,
- stat_date: new DateTime().getDate('Ymd', this.startTime),
- start_time: this.startTime,
- end_time: this.endTime
- }
- this.fillData.push(insertParam)
- this.composes.push(data._id.appid + '_' + data._id.platform_id + '_' + data._id.channel_id + '_' + data
- ._id.version_id)
- return insertParam
- }
- /**
- * 基础填充数据-目前只有小时和天维度的数据
- * @param {Object} data 统计数据
- */
- async fill(data) {
- // 平台信息
- let platformInfo = null
- if (this.platforms && this.platforms[data._id.platform]) {
- //暂存下数据,减少读库
- platformInfo = this.platforms[data._id.platform]
- } else {
- const platform = new Platform()
- platformInfo = await platform.getPlatformAndCreate(data._id.platform, null)
- if (!platformInfo || platformInfo.length === 0) {
- platformInfo._id = ''
- }
- this.platforms[data._id.platform] = platformInfo
- if (this.debug) {
- console.log('platformInfo', JSON.stringify(platformInfo))
- }
- }
- // 渠道信息
- let channelInfo = null
- const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel
- if (this.channels && this.channels[channelKey]) {
- channelInfo = this.channels[channelKey]
- } else {
- const channel = new Channel()
- channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel)
- if (!channelInfo || channelInfo.length === 0) {
- channelInfo._id = ''
- }
- this.channels[channelKey] = channelInfo
- if (this.debug) {
- console.log('channelInfo', JSON.stringify(channelInfo))
- }
- }
- // 版本信息
- let versionInfo = null
- const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version
- if (this.versions && this.versions[versionKey]) {
- versionInfo = this.versions[versionKey]
- } else {
- const version = new Version()
- versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version)
- if (!versionInfo || versionInfo.length === 0) {
- versionInfo._id = ''
- }
- this.versions[versionKey] = versionInfo
- if (this.debug) {
- console.log('versionInfo', JSON.stringify(versionInfo))
- }
- }
- // 访问设备数统计
- const matchCondition = data._id
- Object.assign(matchCondition, {
- create_time: {
- $gte: this.startTime,
- $lte: this.endTime
- }
- })
- const statVisitDeviceRes = await this.aggregate(this.sessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- device_id: 1,
- create_time: 1
- },
- match: matchCondition,
- group: [{
- _id: {
- device_id: '$device_id'
- }
- }, {
- _id: {},
- total_devices: {
- $sum: 1
- }
- }]
- })
- let activeDeviceCount = 0
- if (statVisitDeviceRes.data.length > 0) {
- activeDeviceCount = statVisitDeviceRes.data[0].total_devices
- }
- //安卓平台活跃设备数需要减去sdk更新后device_id发生变更的设备数
- if(platformInfo.code === 'android') {
- const oldDeviceRes = await this.aggregate(this.sessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- old_device_id: 1,
- create_time: 1
- },
- match: matchCondition,
- group: {
- _id: {
- device_id: '$old_device_id'
- },
- create_time: {
- $min: '$create_time'
- },
- sessionCount: {
- $sum: 1
- }
- },
- sort: {
- create_time: 1,
- sessionCount: 1
- },
- getAll: true
- })
- if(oldDeviceRes.data.length) {
- const thisOldDeviceIds = []
- for (const tau in oldDeviceRes.data) {
- if(oldDeviceRes.data[tau]._id.device_id) {
- thisOldDeviceIds.push(oldDeviceRes.data[tau]._id.device_id)
- }
- }
- const statVisitOldDeviceRes = await this.aggregate(this.sessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- device_id: 1,
- create_time: 1
- },
- match: {
- ...matchCondition,
- device_id: {
- $in: thisOldDeviceIds
- }
- },
- group: [{
- _id: {
- device_id: '$device_id'
- }
- }, {
- _id: {},
- total_devices: {
- $sum: 1
- }
- }]
- })
- if (this.debug) {
- console.log('statVisitOldDeviceRes', JSON.stringify(statVisitOldDeviceRes))
- }
- if (statVisitOldDeviceRes && statVisitOldDeviceRes.data.length > 0) {
- // 活跃设备留存数
- activeDeviceCount -= statVisitOldDeviceRes.data[0].total_devices
- }
- }
- }
- // 错误数量统计
- const errorLog = new ErrorLog()
- const statErrorRes = await this.getCollection(errorLog.tableName).where(matchCondition).count()
- let errorCount = 0
- if (statErrorRes && statErrorRes.total > 0) {
- errorCount = statErrorRes.total
- }
- // 设备的次均停留时长,设备访问总时长/设备会话次数
- let avgSessionTime = 0
- if (data.total_duration > 0 && data.session_times > 0) {
- avgSessionTime = Math.round(data.total_duration / data.session_times)
- }
- // 设均停留时长
- let avgDeviceTime = 0
- if (data.total_duration > 0 && activeDeviceCount > 0) {
- avgDeviceTime = Math.round(data.total_duration / activeDeviceCount)
- }
- // 跳出率
- let bounceTimes = 0
- const bounceRes = await this.getCollection(this.sessionLog.tableName).where({
- ...matchCondition,
- page_count: 1
- }).count()
- if (bounceRes && bounceRes.total > 0) {
- bounceTimes = bounceRes.total
- }
- let bounceRate = 0
- if (bounceTimes > 0 && data.session_times > 0) {
- bounceRate = bounceTimes * 100 / data.session_times
- bounceRate = parseFloat(bounceRate.toFixed(2))
- }
- // 应用启动次数 = 会话次数
- const launchCount = data.session_times
- // 累计设备数
- let totalDevices = data.new_device_count
- const totalDeviceRes = await this.getCollection(this.tableName).where({
- appid: data._id.appid,
- platform_id: platformInfo._id,
- channel_id: channelInfo._id,
- version_id: versionInfo._id,
- dimension: this.fillType,
- start_time: {
- $lt: this.startTime
- }
- }).orderBy('start_time', 'desc').limit(1).get()
- if (totalDeviceRes && totalDeviceRes.data.length > 0) {
- totalDevices += totalDeviceRes.data[0].total_devices
- }
- //活跃用户数
- const userSessionLog = new UserSessionLog()
- let activeUserCount = 0
- const activeUserCountRes = await this.aggregate(userSessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- uid: 1,
- create_time: 1
- },
- match: matchCondition,
- group: [{
- _id: {
- uid: '$uid'
- }
- }, {
- _id: {},
- total_users: {
- $sum: 1
- }
- }]
- })
- if (activeUserCountRes && activeUserCountRes.data.length > 0) {
- activeUserCount = activeUserCountRes.data[0].total_users
- }
- //新增用户数
- const uniIDUsers = new UniIDUsers()
- let newUserCount = await uniIDUsers.getUserCount(matchCondition.appid, matchCondition.platform,
- matchCondition.channel, matchCondition.version, {
- $gte: this.startTime,
- $lte: this.endTime
- })
- //总用户数
- let totalUserCount = await uniIDUsers.getUserCount(matchCondition.appid, matchCondition.platform,
- matchCondition.channel, matchCondition.version, {
- $lte: this.endTime
- })
- //用户停留总时长及总会话次数
- let totalUserDuration = 0
- let totalUserSessionTimes = 0
- const totalUserDurationRes = await this.aggregate(userSessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- duration: 1,
- create_time: 1
- },
- match: matchCondition,
- group: [{
- _id: {},
- total_duration: {
- $sum: "$duration"
- },
- total_session_times: {
- $sum: 1
- }
- }]
- })
- if (totalUserDurationRes && totalUserDurationRes.data.length > 0) {
- totalUserDuration = totalUserDurationRes.data[0].total_duration
- totalUserSessionTimes = totalUserDurationRes.data[0].total_session_times
- }
- //人均停留时长
- let avgUserTime = 0
- //用户次均访问时长
- let avgUserSessionTime = 0
- if (totalUserDuration > 0 && activeUserCount > 0) {
- avgUserTime = Math.round(totalUserDuration / activeUserCount)
- avgUserSessionTime = Math.round(totalUserDuration / totalUserSessionTimes)
- }
- //设置填充数据
- const datetime = new DateTime()
- const insertParam = {
- appid: data._id.appid,
- platform_id: platformInfo._id,
- channel_id: channelInfo._id,
- version_id: versionInfo._id,
- total_devices: totalDevices,
- new_device_count: data.new_device_count,
- active_device_count: activeDeviceCount,
- total_users: totalUserCount,
- new_user_count: newUserCount,
- active_user_count: activeUserCount,
- user_session_times: totalUserSessionTimes,
- app_launch_count: launchCount,
- page_visit_count: data.page_view_count,
- error_count: errorCount,
- duration: data.total_duration,
- user_duration: totalUserDuration,
- avg_device_session_time: avgSessionTime,
- avg_user_session_time: avgUserSessionTime,
- avg_device_time: avgDeviceTime,
- avg_user_time: avgUserTime,
- bounce_times: bounceTimes,
- bounce_rate: bounceRate,
- retention: {},
- dimension: this.fillType,
- stat_date: datetime.getDate('Ymd', this.startTime),
- start_time: this.startTime,
- end_time: this.endTime
- }
- //数据填充
- this.fillData.push(insertParam)
- this.composes.push(data._id.appid + '_' + platformInfo._id + '_' + channelInfo._id + '_' + versionInfo
- ._id)
- return insertParam
- }
- /**
- * 基础统计数据补充,防止累计数据丢失
- */
- async replenishStat() {
- if (this.debug) {
- console.log('composes data', this.composes)
- }
- const datetime = new DateTime()
- // const {
- // startTime
- // } = datetime.getTimeDimensionByType(this.fillType, -1, this.startTime)
- //上一次统计时间
- let preStatRes = await this.getCollection(this.tableName).where({
- start_time: {$lt: this.startTime},
- dimension: this.fillType
- }).orderBy('start_time', 'desc').limit(1).get()
- let preStartTime = 0
- if(preStatRes && preStatRes.data.length > 0) {
- preStartTime = preStatRes.data[0].start_time
- }
- if (this.debug) {
- console.log('replenishStat-preStartTime', preStartTime)
- }
- if(!preStartTime) {
- return false
- }
- // 上一阶段数据
- const preStatData = await this.selectAll(this.tableName, {
- start_time: preStartTime,
- dimension: this.fillType
- }, {
- appid: 1,
- platform_id: 1,
- channel_id: 1,
- version_id: 1,
- total_devices: 1,
- total_users: 1
- })
- if (!preStatData || preStatData.data.length === 0) {
- return false
- }
- if (this.debug) {
- console.log('preStatData', JSON.stringify(preStatData))
- }
- let preKey
- const preKeyArr = []
- for (const pi in preStatData.data) {
- preKey = preStatData.data[pi].appid + '_' + preStatData.data[pi].platform_id + '_' + preStatData
- .data[pi].channel_id + '_' + preStatData.data[pi].version_id
- if (!this.composes.includes(preKey) && !preKeyArr.includes(preKey)) {
- preKeyArr.push(preKey)
- if (this.debug) {
- console.log('preKey -add', preKey)
- }
- this.fillData.push({
- appid: preStatData.data[pi].appid,
- platform_id: preStatData.data[pi].platform_id,
- channel_id: preStatData.data[pi].channel_id,
- version_id: preStatData.data[pi].version_id,
- total_devices: preStatData.data[pi].total_devices,
- new_device_count: 0,
- active_device_count: 0,
- total_users: preStatData.data[pi].total_users,
- new_user_count: 0,
- active_user_count: 0,
- user_session_times: 0,
- app_launch_count: 0,
- page_visit_count: 0,
- error_count: 0,
- duration: 0,
- user_duration: 0,
- avg_device_session_time: 0,
- avg_user_session_time: 0,
- avg_device_time: 0,
- avg_user_time: 0,
- bounce_times: 0,
- bounce_rate: 0,
- retention: {},
- dimension: this.fillType,
- stat_date: datetime.getDate('Ymd', this.startTime),
- start_time: this.startTime,
- end_time: this.endTime
- })
- } else if (this.debug) {
- console.log('preKey -have', preKey)
- }
- }
- return true
- }
- /**
- * 留存数据统计
- * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
- * @param {Date|Time} date 指定日期或时间戳
- * @param {String} mod 统计模块 device:设备,user:用户
- */
- async retentionStat(type, date, mod = 'device') {
- date = date ? date : new DateTime().getTimeBySetDays(-1, date)
- const allowedType = ['day', 'week', 'month']
- if (!allowedType.includes(type)) {
- return {
- code: 1002,
- msg: 'This type is not allowed'
- }
- }
- let days = []
- switch (type) {
- case 'week':
- case 'month':
- days = [1, 2, 3, 4, 5, 6, 7, 8, 9]
- break
- default:
- days = [1, 2, 3, 4, 5, 6, 7, 14, 30]
- break
- }
- let res = {
- code: 0,
- msg: 'success'
- }
- for (const day in days) {
- //留存统计数据库查询较为复杂,因此这里拆分为多个维度
- if (mod && mod === 'user') {
- //用户留存统计
- if (type === 'day') {
- res = await this.userRetentionFillDayly(type, days[day], date)
- } else {
- res = await this.userRetentionFillWeekOrMonth(type, days[day], date)
- }
- } else {
- //设备留存统计
- if (type === 'day') {
- res = await this.deviceRetentionFillDayly(type, days[day], date)
- } else {
- res = await this.deviceRetentionFillWeekOrMonth(type, days[day], date)
- }
- }
- }
- return res
- }
- /**
- * 设备日留存统计数据填充
- * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
- * @param {Date|Time} date 指定日期或时间戳
- * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
- */
- async deviceRetentionFillDayly(type, day, date) {
- if (type !== 'day') {
- return {
- code: 301,
- msg: 'Type error:' + type
- }
- }
- const dateTime = new DateTime()
- const {
- startTime,
- endTime
- } = dateTime.getTimeDimensionByType(type, 0 - day, date)
- if (!startTime || !endTime) {
- return {
- code: 1001,
- msg: 'The statistic time get failed'
- }
- }
- // 截止时间范围
- const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date)
- // 获取当时批次的统计日志
- const resultLogRes = await this.selectAll(this.tableName, {
- dimension: type,
- start_time: startTime,
- end_time: endTime
- })
- // const resultLogRes = await this.getCollection(this.tableName).where({
- // dimension: type,
- // start_time: startTime,
- // end_time: endTime
- // }).get()
- if (this.debug) {
- console.log('resultLogRes', JSON.stringify(resultLogRes))
- }
- if (!resultLogRes || resultLogRes.data.length === 0) {
- if (this.debug) {
- console.log('Not found this log --' + type + ':' + day + ', start:' + startTime + ',endTime:' +
- endTime)
- }
- return {
- code: 1000,
- msg: 'Not found this log'
- }
- }
- const sessionLog = new SessionLog()
- const platform = new Platform()
- const channel = new Channel()
- const version = new Version()
- let res = null
- for (const resultIndex in resultLogRes.data) {
- const resultLog = resultLogRes.data[resultIndex]
- // 平台信息
- let platformInfo = null
- if (this.platforms && this.platforms[resultLog.platform_id]) {
- platformInfo = this.platforms[resultLog.platform_id]
- } else {
- platformInfo = await this.getById(platform.tableName, resultLog.platform_id)
- if (!platformInfo || platformInfo.length === 0) {
- platformInfo.code = ''
- }
- this.platforms[resultLog.platform_id] = platformInfo
- if (this.debug) {
- console.log('platformInfo', JSON.stringify(platformInfo))
- }
- }
- // 渠道信息
- let channelInfo = null
- if (this.channels && this.channels[resultLog.channel_id]) {
- channelInfo = this.channels[resultLog.channel_id]
- } else {
- channelInfo = await this.getById(channel.tableName, resultLog.channel_id)
- if (!channelInfo || channelInfo.length === 0) {
- channelInfo.channel_code = ''
- }
- this.channels[resultLog.channel_id] = channelInfo
- if (this.debug) {
- console.log('channelInfo', JSON.stringify(channelInfo))
- }
- }
- // 版本信息
- let versionInfo = null
- if (this.versions && this.versions[resultLog.version_id]) {
- versionInfo = this.versions[resultLog.version_id]
- } else {
- versionInfo = await this.getById(version.tableName, resultLog.version_id, false)
- if (!versionInfo || versionInfo.length === 0) {
- versionInfo.version = ''
- }
- this.versions[resultLog.version_id] = versionInfo
- if (this.debug) {
- console.log('versionInfo', JSON.stringify(versionInfo))
- }
- }
- // 获取该批次的活跃设备数
- const activeDeviceRes = await this.aggregate(sessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- device_id: 1,
- create_time: 1
- },
- match: {
- appid: resultLog.appid,
- version: versionInfo.version,
- platform: platformInfo.code,
- channel: channelInfo.channel_code,
- create_time: {
- $gte: startTime,
- $lte: endTime
- }
- },
- group: {
- _id: {
- device_id: '$device_id'
- },
- create_time: {
- $min: '$create_time'
- },
- sessionCount: {
- $sum: 1
- }
- },
- sort: {
- create_time: 1,
- sessionCount: 1
- },
- getAll: true
- })
- if (this.debug) {
- console.log('activeDeviceRes', JSON.stringify(activeDeviceRes))
- }
- let activeDeviceRate = 0
- let activeDevices = 0
- if (activeDeviceRes && activeDeviceRes.data.length > 0) {
- const thisDayActiveDevices = activeDeviceRes.data.length
- const thisDayActiveDeviceIds = []
- for (const tau in activeDeviceRes.data) {
- thisDayActiveDeviceIds.push(activeDeviceRes.data[tau]._id.device_id)
- }
- if (this.debug) {
- console.log('thisDayActiveDeviceIds', JSON.stringify(thisDayActiveDeviceIds))
- }
- // 留存活跃设备查询
- const retentionActiveDeviceRes = await this.aggregate(sessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- device_id: 1,
- create_time: 1
- },
- match: {
- appid: resultLog.appid,
- version: versionInfo.version,
- platform: platformInfo.code,
- channel: channelInfo.channel_code,
- device_id: {
- $in: thisDayActiveDeviceIds
- },
- create_time: {
- $gte: lastTimeInfo.startTime,
- $lte: lastTimeInfo.endTime
- }
- },
- group: [{
- _id: {
- device_id: '$device_id'
- }
- }, {
- _id: {},
- total_devices: {
- $sum: 1
- }
- }]
- })
- if (this.debug) {
- console.log('retentionActiveDeviceRes', JSON.stringify(retentionActiveDeviceRes))
- }
- if (retentionActiveDeviceRes && retentionActiveDeviceRes.data.length > 0) {
- // 活跃设备留存数
- activeDevices = retentionActiveDeviceRes.data[0].total_devices
- // 活跃设备留存率
- activeDeviceRate = parseFloat((activeDevices * 100 / thisDayActiveDevices).toFixed(2))
- }
- //安卓平台留存需要增加sdk更新后device_id发生变更的设备数
- if(platformInfo.code === 'android') {
- const retentionActiveOldDeviceRes = await this.aggregate(sessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- old_device_id: 1,
- create_time: 1
- },
- match: {
- appid: resultLog.appid,
- version: versionInfo.version,
- platform: platformInfo.code,
- channel: channelInfo.channel_code,
- old_device_id: {
- $in: thisDayActiveDeviceIds
- },
- create_time: {
- $gte: lastTimeInfo.startTime,
- $lte: lastTimeInfo.endTime
- }
- },
- group: [{
- _id: {
- device_id: '$old_device_id'
- }
- }, {
- _id: {},
- total_devices: {
- $sum: 1
- }
- }]
- })
- if (this.debug) {
- console.log('retentionActiveOldDeviceRes', JSON.stringify(retentionActiveOldDeviceRes))
- }
- if (retentionActiveOldDeviceRes && retentionActiveOldDeviceRes.data.length > 0) {
- // 活跃设备留存数
- activeDevices += retentionActiveOldDeviceRes.data[0].total_devices
- // 活跃设备留存率
- activeDeviceRate = parseFloat((activeDevices * 100 / thisDayActiveDevices).toFixed(2))
- }
- }
- }
- // 获取该批次新增设备数
- const newDeviceRes = await this.aggregate(sessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- is_first_visit: 1,
- device_id: 1,
- create_time: 1
- },
- match: {
- appid: resultLog.appid,
- version: versionInfo.version,
- platform: platformInfo.code,
- channel: channelInfo.channel_code,
- is_first_visit: 1,
- create_time: {
- $gte: startTime,
- $lte: endTime
- }
- },
- group: {
- _id: {
- device_id: '$device_id'
- },
- create_time: {
- $min: '$create_time'
- },
- sessionCount: {
- $sum: 1
- }
- },
- sort: {
- create_time: 1,
- sessionCount: 1
- },
- getAll: true
- })
- let newDeviceRate = 0
- let newDevices = 0
- if (newDeviceRes && newDeviceRes.data.length > 0) {
- const thisDayNewDevices = newDeviceRes.data.length
- const thisDayNewDeviceIds = []
- for (const tau in newDeviceRes.data) {
- thisDayNewDeviceIds.push(newDeviceRes.data[tau]._id.device_id)
- }
- if (this.debug) {
- console.log('thisDayNewDeviceIds', JSON.stringify(thisDayNewDeviceIds))
- }
- // 留存的设备查询
- const retentionNewDeviceRes = await this.aggregate(sessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- device_id: 1,
- create_time: 1
- },
- match: {
- appid: resultLog.appid,
- version: versionInfo.version,
- platform: platformInfo.code,
- channel: channelInfo.channel_code,
- device_id: {
- $in: thisDayNewDeviceIds
- },
- create_time: {
- $gte: lastTimeInfo.startTime,
- $lte: lastTimeInfo.endTime
- }
- },
- group: [{
- _id: {
- device_id: '$device_id'
- }
- }, {
- _id: {},
- total_devices: {
- $sum: 1
- }
- }]
- })
- if (retentionNewDeviceRes && retentionNewDeviceRes.data.length > 0) {
- // 新增设备留存数
- newDevices = retentionNewDeviceRes.data[0].total_devices
- // 新增设备留存率
- newDeviceRate = parseFloat((newDevices * 100 / thisDayNewDevices).toFixed(2))
- }
- //安卓平台留存需要增加sdk更新后device_id发生变更的设备数
- if(platformInfo.code === 'android') {
- const retentionNewOldDeviceRes = await this.aggregate(sessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- old_device_id: 1,
- create_time: 1
- },
- match: {
- appid: resultLog.appid,
- version: versionInfo.version,
- platform: platformInfo.code,
- channel: channelInfo.channel_code,
- old_device_id: {
- $in: thisDayNewDeviceIds
- },
- create_time: {
- $gte: lastTimeInfo.startTime,
- $lte: lastTimeInfo.endTime
- }
- },
- group: [{
- _id: {
- device_id: '$old_device_id'
- }
- }, {
- _id: {},
- total_devices: {
- $sum: 1
- }
- }]
- })
- if (this.debug) {
- console.log('retentionNewOldDeviceRes', JSON.stringify(retentionNewOldDeviceRes))
- }
- if (retentionNewOldDeviceRes && retentionNewOldDeviceRes.data.length > 0) {
- // 新增设备留存数
- newDevices += retentionNewOldDeviceRes.data[0].total_devices
- // 新增设备留存率
- newDeviceRate = parseFloat((newDevices * 100 / thisDayNewDevices).toFixed(2))
- }
- }
- }
- // 数据更新
- const retentionData = resultLog.retention
- const dataKey = type.substr(0, 1) + '_' + day
- if (!retentionData.active_device) {
- retentionData.active_device = {}
- }
- retentionData.active_device[dataKey] = {
- device_count: activeDevices,
- device_rate: activeDeviceRate
- }
- if (!retentionData.new_device) {
- retentionData.new_device = {}
- }
- retentionData.new_device[dataKey] = {
- device_count: newDevices,
- device_rate: newDeviceRate
- }
- if (this.debug) {
- console.log('retentionData', JSON.stringify(retentionData))
- }
- res = await this.update(this.tableName, {
- retention: retentionData
- }, {
- _id: resultLog._id
- })
- }
- if (res && res.updated) {
- return {
- code: 0,
- msg: 'success'
- }
- } else {
- return {
- code: 500,
- msg: 'retention data update failed'
- }
- }
- }
- /**
- * 设备周/月留存数据填充
- * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
- * @param {Date|Time} date 指定日期或时间戳
- * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
- */
- async deviceRetentionFillWeekOrMonth(type, day, date) {
- if (!['week', 'month'].includes(type)) {
- return {
- code: 301,
- msg: 'Type error:' + type
- }
- }
- const dateTime = new DateTime()
- const {
- startTime,
- endTime
- } = dateTime.getTimeDimensionByType(type, 0 - day, date)
- if (!startTime || !endTime) {
- return {
- code: 1001,
- msg: 'The statistic time get failed'
- }
- }
- // 截止时间范围
- const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date)
- // 获取当时批次的统计日志
- const resultLogRes = await this.selectAll(this.tableName, {
- dimension: type,
- start_time: startTime,
- end_time: endTime
- })
- if (this.debug) {
- console.log('resultLogRes', JSON.stringify(resultLogRes))
- }
- if (!resultLogRes || resultLogRes.data.length === 0) {
- if (this.debug) {
- console.log('Not found this session log --' + type + ':' + day + ', start:' + startTime +
- ',endTime:' + endTime)
- }
- return {
- code: 1000,
- msg: 'Not found this session log'
- }
- }
- const activeDevicesObj = new ActiveDevices()
- let res = null;
- let activeDeviceRes;
- let activeDeviceRate;
- let activeDevices;
- let newDeviceRate;
- let newDevices
- for (const resultIndex in resultLogRes.data) {
- const resultLog = resultLogRes.data[resultIndex]
- // 获取该批次的活跃设备数
- activeDeviceRes = await this.selectAll(activeDevicesObj.tableName, {
- appid: resultLog.appid,
- platform_id: resultLog.platform_id,
- channel_id: resultLog.channel_id,
- version_id: resultLog.version_id,
- dimension: type,
- create_time: {
- $gte: startTime,
- $lte: endTime
- }
- }, {
- device_id: 1
- })
- if (this.debug) {
- console.log('activeDeviceRes', JSON.stringify(activeDeviceRes))
- }
- activeDeviceRate = 0
- activeDevices = 0
- if (activeDeviceRes && activeDeviceRes.data.length > 0) {
- const thisDayActiveDevices = activeDeviceRes.data.length
- const thisDayActiveDeviceIds = []
- for (const tau in activeDeviceRes.data) {
- thisDayActiveDeviceIds.push(activeDeviceRes.data[tau].device_id)
- }
- if (this.debug) {
- console.log('thisDayActiveDeviceIds', JSON.stringify(thisDayActiveDeviceIds))
- }
- // 留存活跃设备数
- const retentionActiveDeviceRes = await this.getCollection(activeDevicesObj.tableName).where({
- appid: resultLog.appid,
- platform_id: resultLog.platform_id,
- channel_id: resultLog.channel_id,
- version_id: resultLog.version_id,
- device_id: {
- $in: thisDayActiveDeviceIds
- },
- dimension: type,
- create_time: {
- $gte: lastTimeInfo.startTime,
- $lte: lastTimeInfo.endTime
- }
- }).count()
- if (this.debug) {
- console.log('retentionActiveDeviceRes', JSON.stringify(retentionActiveDeviceRes))
- }
- if (retentionActiveDeviceRes && retentionActiveDeviceRes.total > 0) {
- // 活跃设备留存数
- activeDevices = retentionActiveDeviceRes.total
- // 活跃设备留存率
- activeDeviceRate = parseFloat((activeDevices * 100 / thisDayActiveDevices).toFixed(2))
- }
- }
- // 获取该批次的新增设备数
- const newDeviceRes = await this.selectAll(activeDevicesObj.tableName, {
- appid: resultLog.appid,
- platform_id: resultLog.platform_id,
- channel_id: resultLog.channel_id,
- version_id: resultLog.version_id,
- is_new: 1,
- dimension: type,
- create_time: {
- $gte: startTime,
- $lte: endTime
- }
- }, {
- device_id: 1
- })
- newDeviceRate = 0
- newDevices = 0
- if (newDeviceRes && newDeviceRes.data.length > 0) {
- const thisDayNewDevices = newDeviceRes.data.length
- const thisDayNewDeviceIds = []
- for (const tau in newDeviceRes.data) {
- thisDayNewDeviceIds.push(newDeviceRes.data[tau].device_id)
- }
- // 新增设备留存数
- const retentionNewDeviceRes = await this.getCollection(activeDevicesObj.tableName).where({
- appid: resultLog.appid,
- platform_id: resultLog.platform_id,
- channel_id: resultLog.channel_id,
- version_id: resultLog.version_id,
- device_id: {
- $in: thisDayNewDeviceIds
- },
- dimension: type,
- create_time: {
- $gte: lastTimeInfo.startTime,
- $lte: lastTimeInfo.endTime
- }
- }).count()
- if (retentionNewDeviceRes && retentionNewDeviceRes.total > 0) {
- // 新增设备留存数
- newDevices = retentionNewDeviceRes.total
- // 新增设备留存率
- newDeviceRate = parseFloat((newDevices * 100 / thisDayNewDevices).toFixed(2))
- }
- }
- // 数据更新
- const retentionData = resultLog.retention
- const dataKey = type.substr(0, 1) + '_' + day
- if (!retentionData.active_device) {
- retentionData.active_device = {}
- }
- retentionData.active_device[dataKey] = {
- device_count: activeDevices,
- device_rate: activeDeviceRate
- }
- if (!retentionData.new_device) {
- retentionData.new_device = {}
- }
- retentionData.new_device[dataKey] = {
- device_count: newDevices,
- device_rate: newDeviceRate
- }
- if (this.debug) {
- console.log('retentionData', JSON.stringify(retentionData))
- }
- res = await this.update(this.tableName, {
- retention: retentionData
- }, {
- _id: resultLog._id
- })
- }
- if (res && res.updated) {
- return {
- code: 0,
- msg: 'success'
- }
- } else {
- return {
- code: 500,
- msg: 'retention data update failed'
- }
- }
- }
- /**
- * 用户日留存数据填充
- * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
- * @param {Date|Time} date 指定日期或时间戳
- * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
- */
- async userRetentionFillDayly(type, day, date) {
- if (type !== 'day') {
- return {
- code: 301,
- msg: 'Type error:' + type
- }
- }
- const dateTime = new DateTime()
- const {
- startTime,
- endTime
- } = dateTime.getTimeDimensionByType(type, 0 - day, date)
- if (!startTime || !endTime) {
- return {
- code: 1001,
- msg: 'The statistic time get failed'
- }
- }
- // 截止时间范围
- const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date)
- // 获取当时批次的统计日志
- const resultLogRes = await this.selectAll(this.tableName, {
- dimension: type,
- start_time: startTime,
- end_time: endTime
- })
- if (this.debug) {
- console.log('resultLogRes', JSON.stringify(resultLogRes))
- }
- if (!resultLogRes || resultLogRes.data.length === 0) {
- if (this.debug) {
- console.log('Not found this log --' + type + ':' + day + ', start:' + startTime + ',endTime:' +
- endTime)
- }
- return {
- code: 1000,
- msg: 'Not found this log'
- }
- }
- const userSessionLog = new UserSessionLog()
- const uniIDUsers = new UniIDUsers()
- const platform = new Platform()
- const channel = new Channel()
- const version = new Version()
- let res = null
- for (const resultIndex in resultLogRes.data) {
- const resultLog = resultLogRes.data[resultIndex]
- // 平台信息
- let platformInfo = null
- if (this.platforms && this.platforms[resultLog.platform_id]) {
- platformInfo = this.platforms[resultLog.platform_id]
- } else {
- platformInfo = await this.getById(platform.tableName, resultLog.platform_id)
- if (!platformInfo || platformInfo.length === 0) {
- platformInfo.code = ''
- }
- this.platforms[resultLog.platform_id] = platformInfo
- if (this.debug) {
- console.log('platformInfo', JSON.stringify(platformInfo))
- }
- }
- // 渠道信息
- let channelInfo = null
- if (this.channels && this.channels[resultLog.channel_id]) {
- channelInfo = this.channels[resultLog.channel_id]
- } else {
- channelInfo = await this.getById(channel.tableName, resultLog.channel_id)
- if (!channelInfo || channelInfo.length === 0) {
- channelInfo.channel_code = ''
- }
- this.channels[resultLog.channel_id] = channelInfo
- if (this.debug) {
- console.log('channelInfo', JSON.stringify(channelInfo))
- }
- }
- // 版本信息
- let versionInfo = null
- if (this.versions && this.versions[resultLog.version_id]) {
- versionInfo = this.versions[resultLog.version_id]
- } else {
- versionInfo = await this.getById(version.tableName, resultLog.version_id, false)
- if (!versionInfo || versionInfo.length === 0) {
- versionInfo.version = ''
- }
- this.versions[resultLog.version_id] = versionInfo
- if (this.debug) {
- console.log('versionInfo', JSON.stringify(versionInfo))
- }
- }
- // 获取该时间段内的活跃用户
- const activeUserRes = await this.aggregate(userSessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- uid: 1,
- create_time: 1
- },
- match: {
- appid: resultLog.appid,
- version: versionInfo.version,
- platform: platformInfo.code,
- channel: channelInfo.channel_code,
- create_time: {
- $gte: startTime,
- $lte: endTime
- }
- },
- group: {
- _id: {
- uid: '$uid'
- },
- create_time: {
- $min: '$create_time'
- },
- sessionCount: {
- $sum: 1
- }
- },
- sort: {
- create_time: 1,
- sessionCount: 1
- },
- getAll: true
- })
- if (this.debug) {
- console.log('activeUserRes', JSON.stringify(activeUserRes))
- }
- //活跃用户留存率
- let activeUserRate = 0
- //活跃用户留存数
- let activeUsers = 0
- if (activeUserRes && activeUserRes.data.length > 0) {
- const thisDayActiveUsers = activeUserRes.data.length
- //获取该时间段内的活跃用户编号,这里没用lookup联查是因为数据量较大时lookup效率很低
- const thisDayActiveUids = []
- for (let tau in activeUserRes.data) {
- thisDayActiveUids.push(activeUserRes.data[tau]._id.uid)
- }
- if (this.debug) {
- console.log('thisDayActiveUids', JSON.stringify(thisDayActiveUids))
- }
- // 留存活跃用户数
- const retentionActiveUserRes = await this.aggregate(userSessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- uid: 1,
- create_time: 1
- },
- match: {
- appid: resultLog.appid,
- version: versionInfo.version,
- platform: platformInfo.code,
- channel: channelInfo.channel_code,
- uid: {
- $in: thisDayActiveUids
- },
- create_time: {
- $gte: lastTimeInfo.startTime,
- $lte: lastTimeInfo.endTime
- }
- },
- group: [{
- _id: {
- uid: '$uid'
- }
- }, {
- _id: {},
- total_users: {
- $sum: 1
- }
- }]
- })
- if (this.debug) {
- console.log('retentionActiveUserRes', JSON.stringify(retentionActiveUserRes))
- }
- if (retentionActiveUserRes && retentionActiveUserRes.data.length > 0) {
- // 活跃用户留存数
- activeUsers = retentionActiveUserRes.data[0].total_users
- // 活跃用户留存率
- activeUserRate = parseFloat((activeUsers * 100 / thisDayActiveUsers).toFixed(2))
- }
- }
- //新增用户编号
- const thisDayNewUids = await uniIDUsers.getUserIds(resultLog.appid, platformInfo.code, channelInfo.channel_code, versionInfo.version, {
- $gte: startTime,
- $lte: endTime
- })
- //新增用户留存率
- let newUserRate = 0
- //新增用户留存数
- let newUsers = 0
- if (thisDayNewUids.length > 0) {
- // 现在依然活跃的用户数
- const retentionNewUserRes = await this.aggregate(userSessionLog.tableName, {
- project: {
- appid: 1,
- version: 1,
- platform: 1,
- channel: 1,
- uid: 1,
- create_time: 1
- },
- match: {
- appid: resultLog.appid,
- version: versionInfo.version,
- platform: platformInfo.code,
- channel: channelInfo.channel_code,
- uid: {
- $in: thisDayNewUids
- },
- create_time: {
- $gte: lastTimeInfo.startTime,
- $lte: lastTimeInfo.endTime
- }
- },
- group: [{
- _id: {
- uid: '$uid'
- }
- }, {
- _id: {},
- total_users: {
- $sum: 1
- }
- }]
- })
- if (retentionNewUserRes && retentionNewUserRes.data.length > 0) {
- // 新增用户留存数
- newUsers = retentionNewUserRes.data[0].total_users
- // 新增用户留存率
- newUserRate = parseFloat((newUsers * 100 / thisDayNewUids.length).toFixed(2))
- }
- }
- // 数据更新
- const retentionData = resultLog.retention
- const dataKey = type.substr(0, 1) + '_' + day
- if (!retentionData.active_user) {
- retentionData.active_user = {}
- }
- retentionData.active_user[dataKey] = {
- user_count: activeUsers,
- user_rate: activeUserRate
- }
- if (!retentionData.new_user) {
- retentionData.new_user = {}
- }
- retentionData.new_user[dataKey] = {
- user_count: newUsers,
- user_rate: newUserRate
- }
- if (this.debug) {
- console.log('retentionData', JSON.stringify(retentionData))
- }
- res = await this.update(this.tableName, {
- retention: retentionData
- }, {
- _id: resultLog._id
- })
- }
- if (res && res.updated) {
- return {
- code: 0,
- msg: 'success'
- }
- } else {
- return {
- code: 500,
- msg: 'retention data update failed'
- }
- }
- }
- /**
- * 用户周/月留存数据填充
- * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
- * @param {Date|Time} date 指定日期或时间戳
- * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
- */
- async userRetentionFillWeekOrMonth(type, day, date) {
- if (!['week', 'month'].includes(type)) {
- return {
- code: 301,
- msg: 'Type error:' + type
- }
- }
- const dateTime = new DateTime()
- const {
- startTime,
- endTime
- } = dateTime.getTimeDimensionByType(type, 0 - day, date)
- if (!startTime || !endTime) {
- return {
- code: 1001,
- msg: 'The statistic time get failed'
- }
- }
- // 截止时间范围
- const lastTimeInfo = dateTime.getTimeDimensionByType(type, 0, date)
- // 获取当时批次的统计日志
- const resultLogRes = await this.selectAll(this.tableName, {
- dimension: type,
- start_time: startTime,
- end_time: endTime
- })
- if (this.debug) {
- console.log('resultLogRes', JSON.stringify(resultLogRes))
- }
- if (!resultLogRes || resultLogRes.data.length === 0) {
- if (this.debug) {
- console.log('Not found this session log --' + type + ':' + day + ', start:' + startTime +
- ',endTime:' + endTime)
- }
- return {
- code: 1000,
- msg: 'Not found this session log'
- }
- }
- const activeUserObj = new ActiveUsers()
- const uniIDUsers = new UniIDUsers()
- const platform = new Platform()
- const channel = new Channel()
- const version = new Version()
- let res = null
- //活跃用户留存率
- let activeUserRate
- //活跃用户留存数
- let activeUsers
- //新增用户留存率
- let newUserRate
- //新增用户留存数
- let newUsers
- for (const resultIndex in resultLogRes.data) {
- const resultLog = resultLogRes.data[resultIndex]
- // 平台信息
- let platformInfo = null
- if (this.platforms && this.platforms[resultLog.platform_id]) {
- platformInfo = this.platforms[resultLog.platform_id]
- } else {
- platformInfo = await this.getById(platform.tableName, resultLog.platform_id)
- if (!platformInfo || platformInfo.length === 0) {
- platformInfo.code = ''
- }
- this.platforms[resultLog.platform_id] = platformInfo
- if (this.debug) {
- console.log('platformInfo', JSON.stringify(platformInfo))
- }
- }
- // 渠道信息
- let channelInfo = null
- if (this.channels && this.channels[resultLog.channel_id]) {
- channelInfo = this.channels[resultLog.channel_id]
- } else {
- channelInfo = await this.getById(channel.tableName, resultLog.channel_id)
- if (!channelInfo || channelInfo.length === 0) {
- channelInfo.channel_code = ''
- }
- this.channels[resultLog.channel_id] = channelInfo
- if (this.debug) {
- console.log('channelInfo', JSON.stringify(channelInfo))
- }
- }
- // 版本信息
- let versionInfo = null
- if (this.versions && this.versions[resultLog.version_id]) {
- versionInfo = this.versions[resultLog.version_id]
- } else {
- versionInfo = await this.getById(version.tableName, resultLog.version_id, false)
- if (!versionInfo || versionInfo.length === 0) {
- versionInfo.version = ''
- }
- this.versions[resultLog.version_id] = versionInfo
- if (this.debug) {
- console.log('versionInfo', JSON.stringify(versionInfo))
- }
- }
- // 获取该批次的活跃用户数
- const activeUserRes = await this.selectAll(activeUserObj.tableName, {
- appid: resultLog.appid,
- platform_id: resultLog.platform_id,
- channel_id: resultLog.channel_id,
- version_id: resultLog.version_id,
- dimension: type,
- create_time: {
- $gte: startTime,
- $lte: endTime
- }
- }, {
- uid: 1
- })
- if (this.debug) {
- console.log('activeUserRes', JSON.stringify(activeUserRes))
- }
- activeUserRate = 0
- activeUsers = 0
- if (activeUserRes && activeUserRes.data.length > 0) {
- const thisDayactiveUsers = activeUserRes.data.length
- const thisDayActiveDeviceIds = []
- for (const tau in activeUserRes.data) {
- thisDayActiveDeviceIds.push(activeUserRes.data[tau].uid)
- }
- if (this.debug) {
- console.log('thisDayActiveDeviceIds', JSON.stringify(thisDayActiveDeviceIds))
- }
- // 留存活跃用户数
- const retentionactiveUserRes = await this.getCollection(activeUserObj.tableName).where({
- appid: resultLog.appid,
- platform_id: resultLog.platform_id,
- channel_id: resultLog.channel_id,
- version_id: resultLog.version_id,
- uid: {
- $in: thisDayActiveDeviceIds
- },
- dimension: type,
- create_time: {
- $gte: lastTimeInfo.startTime,
- $lte: lastTimeInfo.endTime
- }
- }).count()
- if (this.debug) {
- console.log('retentionactiveUserRes', JSON.stringify(retentionactiveUserRes))
- }
- if (retentionactiveUserRes && retentionactiveUserRes.total > 0) {
- // 活跃用户留存数
- activeUsers = retentionactiveUserRes.total
- // 活跃用户留存率
- activeUserRate = parseFloat((activeUsers * 100 / thisDayactiveUsers).toFixed(2))
- }
- }
- //新增用户编号
- const thisDayNewUids = await uniIDUsers.getUserIds(resultLog.appid, platformInfo.code, channelInfo.channel_code, versionInfo.version, {
- $gte: startTime,
- $lte: endTime
- })
- // 新增用户留存率
- newUserRate = 0
- // 新增用户留存数
- newUsers = 0
- if(thisDayNewUids && thisDayNewUids.length > 0) {
- // 新增用户留存数
- const retentionnewUserRes = await this.getCollection(activeUserObj.tableName).where({
- appid: resultLog.appid,
- platform_id: resultLog.platform_id,
- channel_id: resultLog.channel_id,
- version_id: resultLog.version_id,
- uid: {
- $in: thisDayNewUids
- },
- dimension: type,
- create_time: {
- $gte: lastTimeInfo.startTime,
- $lte: lastTimeInfo.endTime
- }
- }).count()
- if (retentionnewUserRes && retentionnewUserRes.total > 0) {
- // 新增用户留存数
- newUsers = retentionnewUserRes.total
- // 新增用户留存率
- newUserRate = parseFloat((newUsers * 100 / thisDayNewUids.length).toFixed(2))
- }
- }
- // 数据更新
- const retentionData = resultLog.retention
- const dataKey = type.substr(0, 1) + '_' + day
- if (!retentionData.active_user) {
- retentionData.active_user = {}
- }
- retentionData.active_user[dataKey] = {
- user_count: activeUsers,
- user_rate: activeUserRate
- }
- if (!retentionData.new_user) {
- retentionData.new_user = {}
- }
- retentionData.new_user[dataKey] = {
- user_count: newUsers,
- user_rate: newUserRate
- }
- if (this.debug) {
- console.log('retentionData', JSON.stringify(retentionData))
- }
- res = await this.update(this.tableName, {
- retention: retentionData
- }, {
- _id: resultLog._id
- })
- }
- if (res && res.updated) {
- return {
- code: 0,
- msg: 'success'
- }
- } else {
- return {
- code: 500,
- msg: 'retention data update failed'
- }
- }
- }
- /**
- * 清理实时统计的日志
- * @param {Number} days 实时统计日志保留天数
- */
- async cleanHourLog(days = 7) {
- console.log('clean hour logs - day:', days)
- const dateTime = new DateTime()
- const res = await this.delete(this.tableName, {
- dimension: 'hour',
- start_time: {
- $lt: dateTime.getTimeBySetDays(0 - days)
- }
- })
- if (!res.code) {
- console.log('clean hour logs - res:', res)
- }
- }
- }
|