You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
canteen/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/mod/activeDevices.js

529 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* @class ActiveDevices 活跃设备模型 - 每日跑批合并,仅添加本周/本月首次访问的设备。
*/
const BaseMod = require('./base')
const Platform = require('./platform')
const Channel = require('./channel')
const Version = require('./version')
const SessionLog = require('./sessionLog')
const {
DateTime,
UniCrypto
} = require('../lib')
module.exports = class ActiveDevices extends BaseMod {
constructor() {
super()
this.tableName = 'active-devices'
this.platforms = []
this.channels = []
this.versions = []
}
/**
* @desc 活跃设备统计 - 为周统计/月统计提供周活/月活数据
* @param {date|time} date
* @param {bool} reset
*/
async stat(date, reset) {
const dateTime = new DateTime()
const dateDimension = dateTime.getTimeDimensionByType('day', -1, date)
this.startTime = dateDimension.startTime
// 查看当前时间段数据是否已存在,防止重复生成
if (!reset) {
const checkRes = await this.getCollection(this.tableName).where({
create_time: {
$gte: dateDimension.startTime,
$lte: dateDimension.endTime
}
}).get()
if (checkRes.data.length > 0) {
console.log('data have exists')
return {
code: 1003,
msg: 'Devices data in this time have already existed'
}
}
} else {
const delRes = await this.delete(this.tableName, {
create_time: {
$gte: dateDimension.startTime,
$lte: dateDimension.endTime
}
})
console.log('Delete old data result:', JSON.stringify(delRes))
}
const sessionLog = new SessionLog()
const statRes = await this.aggregate(sessionLog.tableName, {
project: {
appid: 1,
version: 1,
platform: 1,
channel: 1,
is_first_visit: 1,
create_time: 1,
device_id: 1
},
match: {
create_time: {
$gte: dateDimension.startTime,
$lte: dateDimension.endTime
}
},
group: {
_id: {
appid: '$appid',
version: '$version',
platform: '$platform',
channel: '$channel',
device_id: '$device_id'
},
is_new: {
$max: '$is_first_visit'
},
create_time: {
$min: '$create_time'
}
},
sort: {
create_time: 1
},
getAll: true
})
let res = {
code: 0,
msg: 'success'
}
// if (this.debug) {
// console.log('statRes', JSON.stringify(statRes))
// }
if (statRes.data.length > 0) {
const uniCrypto = new UniCrypto()
// 同应用、平台、渠道、版本的数据合并
const statData = [];
let statKey;
let data
const statOldRes = await this.aggregate(sessionLog.tableName, {
project: {
appid: 1,
version: 1,
platform: 1,
channel: 1,
is_first_visit: 1,
create_time: 1,
old_device_id: 1
},
match: {
create_time: {
$gte: dateDimension.startTime,
$lte: dateDimension.endTime
},
old_device_id: {$exists: true}
},
group: {
_id: {
appid: '$appid',
version: '$version',
platform: '$platform',
channel: '$channel',
old_device_id: '$old_device_id'
},
create_time: {
$min: '$create_time'
}
},
sort: {
create_time: 1
},
getAll: true
})
if (this.debug) {
console.log('statOldRes', JSON.stringify(statOldRes))
}
for (const sti in statRes.data) {
data = statRes.data[sti]
statKey = uniCrypto.md5(data._id.appid + data._id.platform + data._id.version + data._id
.channel)
if (!statData[statKey]) {
statData[statKey] = {
appid: data._id.appid,
platform: data._id.platform,
version: data._id.version,
channel: data._id.channel,
device_ids: [],
old_device_ids: [],
info: [],
old_info: []
}
statData[statKey].device_ids.push(data._id.device_id)
statData[statKey].info[data._id.device_id] = {
is_new: data.is_new,
create_time: data.create_time
}
} else {
statData[statKey].device_ids.push(data._id.device_id)
statData[statKey].info[data._id.device_id] = {
is_new: data.is_new,
create_time: data.create_time
}
}
}
if(statOldRes.data.length) {
const oldDeviceIds = []
for(const osti in statOldRes.data) {
if(!statOldRes.data[osti]._id.old_device_id) {
continue
}
oldDeviceIds.push(statOldRes.data[osti]._id.old_device_id)
}
if(oldDeviceIds.length) {
const statOldDidRes = await this.aggregate(sessionLog.tableName, {
project: {
appid: 1,
version: 1,
platform: 1,
channel: 1,
is_first_visit: 1,
create_time: 1,
device_id: 1
},
match: {
create_time: {
$gte: dateDimension.startTime,
$lte: dateDimension.endTime
},
device_id: {$in: oldDeviceIds}
},
group: {
_id: {
appid: '$appid',
version: '$version',
platform: '$platform',
channel: '$channel',
old_device_id: '$device_id'
},
create_time: {
$min: '$create_time'
}
},
sort: {
create_time: 1
},
getAll: true
})
if(statOldDidRes.data.length){
for(const osti in statOldDidRes.data) {
data = statOldDidRes.data[osti]
statKey = uniCrypto.md5(data._id.appid + data._id.platform + data._id.version + data._id
.channel)
if(!data._id.old_device_id) {
continue
}
if (!statData[statKey]) {
statData[statKey] = {
appid: data._id.appid,
platform: data._id.platform,
version: data._id.version,
channel: data._id.channel,
device_ids: [],
old_device_ids: [],
old_info: []
}
statData[statKey].old_device_ids.push(data._id.old_device_id)
} else {
statData[statKey].old_device_ids.push(data._id.old_device_id)
}
if(!statData[statKey].old_info[data._id.old_device_id]) {
statData[statKey].old_info[data._id.old_device_id] = {
create_time: data.create_time
}
}
}
}
}
}
this.fillData = []
for (const sk in statData) {
await this.getFillData(statData[sk])
}
if (this.fillData.length > 0) {
res = await this.batchInsert(this.tableName, this.fillData)
}
}
return res
}
/**
* 获取填充数据
* @param {Object} data
*/
async getFillData(data) {
// 平台信息
let platformInfo = null
if (this.platforms && this.platforms[data.platform]) {
platformInfo = this.platforms[data.platform]
} else {
const platform = new Platform()
platformInfo = await platform.getPlatformAndCreate(data.platform, null)
if (!platformInfo || platformInfo.length === 0) {
platformInfo._id = ''
}
this.platforms[data.platform] = platformInfo
if (this.debug) {
console.log('platformInfo', JSON.stringify(platformInfo))
}
}
// 渠道信息
let channelInfo = null
const channelKey = data.appid + '_' + platformInfo._id + '_' + data.channel
if (this.channels && this.channels[channelKey]) {
channelInfo = this.channels[channelKey]
} else {
const channel = new Channel()
channelInfo = await channel.getChannelAndCreate(data.appid, platformInfo._id, data.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.appid + '_' + data.platform + '_' + data.version
if (this.versions && this.versions[versionKey]) {
versionInfo = this.versions[versionKey]
} else {
const version = new Version()
versionInfo = await version.getVersionAndCreate(data.appid, data.platform, data.version)
if (!versionInfo || versionInfo.length === 0) {
versionInfo._id = ''
}
this.versions[versionKey] = versionInfo
if (this.debug) {
console.log('versionInfo', JSON.stringify(versionInfo))
}
}
const datetime = new DateTime()
const dateDimension = datetime.getTimeDimensionByType('week', 0, this.startTime)
const dateMonthDimension = datetime.getTimeDimensionByType('month', 0, this.startTime)
if(data.device_ids) {
// 取出本周已经存储的device_id
const weekHaveDeviceList = []
const haveWeekList = await this.selectAll(this.tableName, {
appid: data.appid,
version_id: versionInfo._id,
platform_id: platformInfo._id,
channel_id: channelInfo._id,
device_id: {
$in: data.device_ids
},
dimension: 'week',
create_time: {
$gte: dateDimension.startTime,
$lte: dateDimension.endTime
}
}, {
device_id: 1
})
if (haveWeekList.data.length > 0) {
for (const hui in haveWeekList.data) {
weekHaveDeviceList.push(haveWeekList.data[hui].device_id)
}
}
if (this.debug) {
console.log('weekHaveDeviceList', JSON.stringify(weekHaveDeviceList))
}
// 取出本月已经存储的device_id
const monthHaveDeviceList = []
const haveMonthList = await this.selectAll(this.tableName, {
appid: data.appid,
version_id: versionInfo._id,
platform_id: platformInfo._id,
channel_id: channelInfo._id,
device_id: {
$in: data.device_ids
},
dimension: 'month',
create_time: {
$gte: dateMonthDimension.startTime,
$lte: dateMonthDimension.endTime
}
}, {
device_id: 1
})
if (haveMonthList.data.length > 0) {
for (const hui in haveMonthList.data) {
monthHaveDeviceList.push(haveMonthList.data[hui].device_id)
}
}
if (this.debug) {
console.log('monthHaveDeviceList', JSON.stringify(monthHaveDeviceList))
}
//数据填充
for (const ui in data.device_ids) {
//周活跃数据填充
if (!weekHaveDeviceList.includes(data.device_ids[ui])) {
this.fillData.push({
appid: data.appid,
platform_id: platformInfo._id,
channel_id: channelInfo._id,
version_id: versionInfo._id,
is_new: data.info[data.device_ids[ui]].is_new,
device_id: data.device_ids[ui],
dimension: 'week',
create_time: data.info[data.device_ids[ui]].create_time
})
}
//月活跃数据填充
if (!monthHaveDeviceList.includes(data.device_ids[ui])) {
this.fillData.push({
appid: data.appid,
platform_id: platformInfo._id,
channel_id: channelInfo._id,
version_id: versionInfo._id,
is_new: data.info[data.device_ids[ui]].is_new,
device_id: data.device_ids[ui],
dimension: 'month',
create_time: data.info[data.device_ids[ui]].create_time
})
}
}
}
if(data.old_device_ids) {
// 取出本周已经存储的old_device_id
const weekHaveOldDeviceList = []
const haveOldWeekList = await this.selectAll(this.tableName, {
appid: data.appid,
version_id: versionInfo._id,
platform_id: platformInfo._id,
channel_id: channelInfo._id,
device_id: {
$in: data.old_device_ids
},
dimension: 'week-old',
create_time: {
$gte: dateDimension.startTime,
$lte: dateDimension.endTime
}
}, {
device_id: 1
})
if (haveOldWeekList.data.length > 0) {
for (const hui in haveOldWeekList.data) {
weekHaveOldDeviceList.push(haveOldWeekList.data[hui].device_id)
}
}
if (this.debug) {
console.log('weekHaveOldDeviceList', JSON.stringify(weekHaveOldDeviceList))
}
// 取出本月已经存储的old_device_id
const monthHaveOldDeviceList = []
const haveOldMonthList = await this.selectAll(this.tableName, {
appid: data.appid,
version_id: versionInfo._id,
platform_id: platformInfo._id,
channel_id: channelInfo._id,
device_id: {
$in: data.old_device_ids
},
dimension: 'month-old',
create_time: {
$gte: dateMonthDimension.startTime,
$lte: dateMonthDimension.endTime
}
}, {
device_id: 1
})
if (haveOldMonthList.data.length > 0) {
for (const hui in haveOldMonthList.data) {
monthHaveOldDeviceList.push(haveOldMonthList.data[hui].device_id)
}
}
if (this.debug) {
console.log('monthHaveOldDeviceList', JSON.stringify(monthHaveOldDeviceList))
}
//数据填充
for (const ui in data.old_device_ids) {
//周活跃数据填充
if (!weekHaveOldDeviceList.includes(data.old_device_ids[ui])) {
this.fillData.push({
appid: data.appid,
platform_id: platformInfo._id,
channel_id: channelInfo._id,
version_id: versionInfo._id,
is_new: 0,
device_id: data.old_device_ids[ui],
dimension: 'week-old',
create_time: data.old_info[data.old_device_ids[ui]].create_time
})
}
//月活跃数据填充
if (!monthHaveOldDeviceList.includes(data.old_device_ids[ui])) {
this.fillData.push({
appid: data.appid,
platform_id: platformInfo._id,
channel_id: channelInfo._id,
version_id: versionInfo._id,
is_new: 0,
device_id: data.old_device_ids[ui],
dimension: 'month-old',
create_time: data.old_info[data.old_device_ids[ui]].create_time
})
}
}
}
return true
}
/**
* 日志清理,此处日志为临时数据并不需要自定义清理,默认为固定值即可
*/
async clean() {
// 清除周数据周留存统计最高需要10周数据多余的为无用数据
const weeks = 10
console.log('Clean device\'s weekly logs - week:', weeks)
const dateTime = new DateTime()
const res = await this.delete(this.tableName, {
dimension: 'week',
create_time: {
$lt: dateTime.getTimeBySetWeek(0 - weeks)
}
})
if (!res.code) {
console.log('Clean device\'s weekly logs - res:', res)
}
// 清除月数据月留存统计最高需要10个月数据多余的为无用数据
const monthes = 10
console.log('Clean device\'s monthly logs - month:', monthes)
const monthRes = await this.delete(this.tableName, {
dimension: 'month',
create_time: {
$lt: dateTime.getTimeBySetMonth(0 - monthes)
}
})
if (!monthRes.code) {
console.log('Clean device\'s monthly logs - res:', res)
}
return monthRes
}
}