|
|
/**
|
|
|
* @class UniStatDataStat uni统计-数据统计调度处理模块
|
|
|
* @function cron 数据统计定时任务处理函数
|
|
|
* @function stat 数据统计调度处理函数
|
|
|
* @function cleanLog 日志清理调度处理函数
|
|
|
*/
|
|
|
const {
|
|
|
DateTime
|
|
|
} = require('./lib')
|
|
|
|
|
|
const {
|
|
|
sleep
|
|
|
} = require('../shared')
|
|
|
|
|
|
const {
|
|
|
BaseMod,
|
|
|
SessionLog,
|
|
|
PageLog,
|
|
|
EventLog,
|
|
|
ShareLog,
|
|
|
ErrorLog,
|
|
|
StatResult,
|
|
|
ActiveDevices,
|
|
|
ActiveUsers,
|
|
|
PageResult,
|
|
|
EventResult,
|
|
|
ErrorResult,
|
|
|
Loyalty,
|
|
|
RunErrors,
|
|
|
UserSessionLog,
|
|
|
uniPay,
|
|
|
Setting
|
|
|
} = require('./mod')
|
|
|
class UniStatDataStat {
|
|
|
/**
|
|
|
* 数据统计定时任务处理函数
|
|
|
* @param {Object} context 服务器请求上下文参数
|
|
|
*/
|
|
|
async cron(context) {
|
|
|
const baseMod = new BaseMod()
|
|
|
const dateTime = new DateTime()
|
|
|
console.log('Cron start time: ', dateTime.getDate('Y-m-d H:i:s'))
|
|
|
|
|
|
// const setting = new Setting();
|
|
|
// let settingValue = await setting.getSetting()
|
|
|
// if (settingValue.mode === "close") {
|
|
|
// // 如果关闭了统计任务,则任务直接结束
|
|
|
// return {
|
|
|
// code: 0,
|
|
|
// msg: 'Task is close',
|
|
|
// }
|
|
|
// } else if (settingValue.mode === "auto") {
|
|
|
// // 如果开启了节能模式,则判断N天内是否有设备访问记录
|
|
|
// let runKey = await setting.checkAutoRun(settingValue);
|
|
|
// if (!runKey) {
|
|
|
// return {
|
|
|
// code: 0,
|
|
|
// msg: 'Task is auto close',
|
|
|
// }
|
|
|
// }
|
|
|
// }
|
|
|
|
|
|
//获取运行参数
|
|
|
const timeInfo = dateTime.getTimeInfo(null, false)
|
|
|
const cronConfig = baseMod.getConfig('cron')
|
|
|
const cronMin = baseMod.getConfig('cronMin')
|
|
|
const realtimeStat = baseMod.getConfig('realtimeStat')
|
|
|
// 数据跑批
|
|
|
let res = null
|
|
|
if (cronConfig && cronConfig.length > 0) {
|
|
|
for (var mi in cronConfig) {
|
|
|
const currCronConfig = cronConfig[mi]
|
|
|
const cronType = currCronConfig.type
|
|
|
const cronTime = currCronConfig.time.split(' ')
|
|
|
const cronDimension = currCronConfig.dimension
|
|
|
|
|
|
//未开启分钟级定时任务,则设置为小时级定时任务
|
|
|
if (cronTime.length === 4 && !cronMin) {
|
|
|
cronTime.splice(3, 1)
|
|
|
}
|
|
|
|
|
|
if (baseMod.debug) {
|
|
|
console.log('cronTime', cronTime)
|
|
|
}
|
|
|
//精度为分钟级的定时任务
|
|
|
if (cronTime.length === 4) {
|
|
|
if (cronTime[0] !== '*') {
|
|
|
//周统计任务
|
|
|
if (timeInfo.nWeek == cronTime[0] && timeInfo.nHour == cronTime[2] && timeInfo.nMinutes ==
|
|
|
cronTime[3]) {
|
|
|
let dimension = cronDimension || 'week';
|
|
|
console.log(cronType + `--${dimension} run`)
|
|
|
res = await this.stat({
|
|
|
type: cronType,
|
|
|
dimension: cronDimension,
|
|
|
config: currCronConfig
|
|
|
})
|
|
|
}
|
|
|
} else if (cronTime[1] !== '*') {
|
|
|
//月统计任务(包含季度统计任务和年统计任务)
|
|
|
if (timeInfo.nDay == cronTime[1] && timeInfo.nHour == cronTime[2] && timeInfo.nMinutes ==
|
|
|
cronTime[3]) {
|
|
|
let dimension = cronDimension || 'month';
|
|
|
console.log(cronType + `--${dimension} run`)
|
|
|
res = await this.stat({
|
|
|
type: cronType,
|
|
|
dimension: dimension,
|
|
|
config: currCronConfig
|
|
|
})
|
|
|
}
|
|
|
} else if (cronTime[2] !== '*') {
|
|
|
//日统计任务
|
|
|
if (timeInfo.nHour == cronTime[2] && timeInfo.nMinutes == cronTime[3]) {
|
|
|
let dimension = cronDimension || 'day';
|
|
|
console.log(cronType + `--${dimension} run`)
|
|
|
res = await this.stat({
|
|
|
type: cronType,
|
|
|
dimension: dimension,
|
|
|
config: currCronConfig
|
|
|
})
|
|
|
}
|
|
|
} else if (cronTime[3] !== '*') {
|
|
|
//实时统计任务
|
|
|
if (timeInfo.nMinutes == cronTime[3] && realtimeStat) {
|
|
|
let dimension = cronDimension || 'hour';
|
|
|
console.log(cronType + `--${dimension} run`)
|
|
|
res = await this.stat({
|
|
|
type: cronType,
|
|
|
dimension: dimension,
|
|
|
config: currCronConfig
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
//精度为小时级的定时任务
|
|
|
else if (cronTime.length === 3) {
|
|
|
if (cronTime[0] !== '*') {
|
|
|
//周统计任务
|
|
|
if (timeInfo.nWeek == cronTime[0] && timeInfo.nHour == cronTime[2]) {
|
|
|
let dimension = cronDimension || 'week';
|
|
|
console.log(cronType + `--${dimension} run`)
|
|
|
res = await this.stat({
|
|
|
type: cronType,
|
|
|
dimension: dimension,
|
|
|
config: currCronConfig
|
|
|
})
|
|
|
}
|
|
|
} else if (cronTime[1] !== '*') {
|
|
|
//月统计任务(包含季度统计任务和年统计任务)
|
|
|
if (timeInfo.nDay == cronTime[1] && timeInfo.nHour == cronTime[2]) {
|
|
|
let dimension = cronDimension || 'month';
|
|
|
console.log(cronType + `--${dimension} run`)
|
|
|
res = await this.stat({
|
|
|
type: cronType,
|
|
|
dimension: dimension,
|
|
|
config: currCronConfig
|
|
|
})
|
|
|
}
|
|
|
} else if (cronTime[2] !== '*') {
|
|
|
//日统计任务
|
|
|
if (timeInfo.nHour == cronTime[2]) {
|
|
|
let dimension = cronDimension || 'day';
|
|
|
console.log(cronType + `--${dimension} run`)
|
|
|
res = await this.stat({
|
|
|
type: cronType,
|
|
|
dimension: dimension,
|
|
|
config: currCronConfig
|
|
|
})
|
|
|
}
|
|
|
} else {
|
|
|
//实时统计任务
|
|
|
if (realtimeStat) {
|
|
|
let dimension = cronDimension || 'hour';
|
|
|
console.log(cronType + `--${dimension} run`)
|
|
|
res = await this.stat({
|
|
|
type: cronType,
|
|
|
dimension: dimension,
|
|
|
config: currCronConfig
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
console.error('Cron configuration error')
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
console.log('Cron end time: ', dateTime.getDate('Y-m-d H:i:s'))
|
|
|
return {
|
|
|
code: 0,
|
|
|
msg: 'Task have done',
|
|
|
lastCronResult: res
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 数据统计调度处理函数
|
|
|
* @param {Object} params 统计参数
|
|
|
*/
|
|
|
async stat(params) {
|
|
|
const {
|
|
|
type,
|
|
|
dimension,
|
|
|
date,
|
|
|
reset,
|
|
|
config
|
|
|
} = params
|
|
|
let res = {
|
|
|
code: 0,
|
|
|
msg: 'success'
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
switch (type) {
|
|
|
// 基础统计
|
|
|
case 'stat': {
|
|
|
const resultStat = new StatResult()
|
|
|
res = await resultStat.stat(dimension, date, reset)
|
|
|
break
|
|
|
}
|
|
|
// 活跃设备统计归集
|
|
|
case 'active-device': {
|
|
|
const activeDevices = new ActiveDevices()
|
|
|
res = await activeDevices.stat(date, reset)
|
|
|
break
|
|
|
}
|
|
|
// 活跃用户统计归集
|
|
|
case 'active-user': {
|
|
|
const activeUsers = new ActiveUsers()
|
|
|
res = await activeUsers.stat(date, reset)
|
|
|
break
|
|
|
}
|
|
|
// 设备留存统计
|
|
|
case 'retention-device': {
|
|
|
const retentionStat = new StatResult()
|
|
|
res = await retentionStat.retentionStat(dimension, date)
|
|
|
break
|
|
|
}
|
|
|
// 用户留存统计
|
|
|
case 'retention-user': {
|
|
|
const retentionStat = new StatResult()
|
|
|
res = await retentionStat.retentionStat(dimension, date, 'user')
|
|
|
break
|
|
|
}
|
|
|
// 页面统计
|
|
|
case 'page': {
|
|
|
const pageStat = new PageResult()
|
|
|
res = await pageStat.stat(dimension, date, reset)
|
|
|
break
|
|
|
}
|
|
|
// 事件统计
|
|
|
case 'event': {
|
|
|
const eventStat = new EventResult()
|
|
|
res = await eventStat.stat(dimension, date, reset)
|
|
|
break
|
|
|
}
|
|
|
// 错误统计
|
|
|
case 'error': {
|
|
|
const errorStat = new ErrorResult()
|
|
|
res = await errorStat.stat(dimension, date, reset)
|
|
|
break
|
|
|
}
|
|
|
// 设备忠诚度统计
|
|
|
case 'loyalty': {
|
|
|
const loyaltyStat = new Loyalty()
|
|
|
res = await loyaltyStat.stat(dimension, date, reset)
|
|
|
break
|
|
|
}
|
|
|
// 日志清理
|
|
|
case 'clean': {
|
|
|
res = await this.cleanLog()
|
|
|
}
|
|
|
// 支付统计
|
|
|
case 'pay-result': {
|
|
|
const paymentResult = new uniPay.PayResult()
|
|
|
res = await paymentResult.stat(dimension, date, reset, config)
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
} catch (e) {
|
|
|
const maxTryTimes = 2
|
|
|
if (!this.tryTimes) {
|
|
|
this.tryTimes = 1
|
|
|
} else {
|
|
|
this.tryTimes++
|
|
|
}
|
|
|
|
|
|
//报错则重新尝试2次, 解决部分云服务器偶现连接超时问题
|
|
|
if (this.tryTimes <= maxTryTimes) {
|
|
|
//休眠1秒后重新调用
|
|
|
await sleep(1000)
|
|
|
params.reset = true
|
|
|
res = await this.stat(params)
|
|
|
} else {
|
|
|
// 2次尝试失败后记录错误
|
|
|
console.error('server error: ' + e)
|
|
|
const runError = new RunErrors()
|
|
|
runError.create({
|
|
|
mod: 'stat',
|
|
|
params: params,
|
|
|
error: e,
|
|
|
create_time: new DateTime().getTime()
|
|
|
})
|
|
|
|
|
|
res = {
|
|
|
code: 500,
|
|
|
msg: 'server error' + e
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return res
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 日志清理调度处理函数
|
|
|
*/
|
|
|
async cleanLog() {
|
|
|
const baseMod = new BaseMod()
|
|
|
const cleanLog = baseMod.getConfig('cleanLog')
|
|
|
if (!cleanLog || !cleanLog.open) {
|
|
|
return {
|
|
|
code: 100,
|
|
|
msg: 'The log cleanup service has not been turned on'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const res = {
|
|
|
code: 0,
|
|
|
msg: 'success',
|
|
|
data: {}
|
|
|
}
|
|
|
|
|
|
// 会话日志
|
|
|
if (cleanLog.reserveDays.sessionLog > 0) {
|
|
|
const sessionLog = new SessionLog()
|
|
|
res.data.sessionLog = await sessionLog.clean(cleanLog.reserveDays.sessionLog)
|
|
|
}
|
|
|
|
|
|
// 用户会话日志
|
|
|
if (cleanLog.reserveDays.userSessionLog > 0) {
|
|
|
const userSessionLog = new UserSessionLog()
|
|
|
res.data.userSessionLog = await userSessionLog.clean(cleanLog.reserveDays.userSessionLog)
|
|
|
}
|
|
|
|
|
|
// 页面日志
|
|
|
if (cleanLog.reserveDays.pageLog > 0) {
|
|
|
const pageLog = new PageLog()
|
|
|
res.data.pageLog = await pageLog.clean(cleanLog.reserveDays.pageLog)
|
|
|
}
|
|
|
|
|
|
// 事件日志
|
|
|
if (cleanLog.reserveDays.eventLog > 0) {
|
|
|
const eventLog = new EventLog()
|
|
|
res.data.eventLog = await eventLog.clean(cleanLog.reserveDays.eventLog)
|
|
|
}
|
|
|
|
|
|
// 分享日志
|
|
|
if (cleanLog.reserveDays.shareLog > 0) {
|
|
|
const shareLog = new ShareLog()
|
|
|
res.data.shareLog = await shareLog.clean(cleanLog.reserveDays.shareLog)
|
|
|
}
|
|
|
|
|
|
// 错误日志
|
|
|
if (cleanLog.reserveDays.errorLog > 0) {
|
|
|
const errorLog = new ErrorLog()
|
|
|
res.data.errorLog = await errorLog.clean(cleanLog.reserveDays.errorLog)
|
|
|
}
|
|
|
|
|
|
// 活跃设备日志
|
|
|
const activeDevicesLog = new ActiveDevices()
|
|
|
res.data.activeDevicesLog = await activeDevicesLog.clean()
|
|
|
|
|
|
// 活跃用户日志
|
|
|
const activeUsersLog = new ActiveUsers()
|
|
|
res.data.activeUsersLog = await activeUsersLog.clean()
|
|
|
|
|
|
// 实时统计日志
|
|
|
const resultHourLog = new StatResult()
|
|
|
res.data.resultHourLog = await resultHourLog.cleanHourLog()
|
|
|
|
|
|
//原生应用崩溃日志
|
|
|
const appCrashLogs = new AppCrashLogs()
|
|
|
res.data.appCrashLogs = await appCrashLogs.clean()
|
|
|
|
|
|
return res
|
|
|
}
|
|
|
}
|
|
|
|
|
|
module.exports = UniStatDataStat
|