changlishe
changlishe
  • 发布:2022-12-03 16:49
  • 更新:2022-12-09 01:52
  • 阅读:299

【报Bug】uni-stat 2.0 关于周的统计存在问题,本周活跃设备总是只返回昨天的活跃设备

分类:uniCloud

产品分类: uniCloud/App

操作步骤:

点击周活

预期结果:

获取周活跃设备

实际结果:

获取到昨天的活跃设备

bug描述:

以今天时间 2022-12-03 16:19 星期六为例,如果获取本周活跃设备,获取到的设备是昨天 2022-12-02 00:00:00 到现在的活跃设备,而不是本周的活跃设备。
下面是 uni-stat 的 mod/activeDevices.js 的代码

const dateDimension = dateTime.getTimeDimensionByType('day', -1, date)  
this.startTime = dateDimension.startTime  
// 是否在本周内已存在  
        const datetime = new DateTime()  
        const dateDimension = datetime.getTimeDimensionByType('week', 0, this.startTime)  

        // 取出本周已经存储的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)  
            }  
        }

其中 this.startTime 是昨天的时间
而在 lib/date.js 可以看到 getTimeDimensionByType 函数

/**  
     * 根据指定的时间类型获取时间范围  
     * @param {String} type 时间类型 hour:小时 day:天 week:周 month:月  
     * @param {Number} offset 时间的偏移量  
     * @param {Date|Time} thistime 指定的日期或时间戳  
     * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定日期初始时间戳(当天00:00:00的时间戳)   
     */  
    getTimeDimensionByType(type, offset = 0, thistime, getAll = false) {  
        let startTime = 0  
        let endTime = 0  
        switch (type) {  
            case 'hour': {  
                startTime = this.getTimeBySetHours(offset, thistime, getAll)  
                endTime = getAll ? startTime : startTime + 3599999  
                break  
            }  
            case 'day': {  
                startTime = this.getTimeBySetDays(offset, thistime, getAll)  
                endTime = getAll ? startTime : startTime + 86399999  
                break  
            }  
            case 'week': {  
                startTime = this.getTimeBySetWeek(offset, thistime, getAll)  
                endTime = getAll ? startTime + 86400000 * 6 : startTime + 86400000 * 6 + 86399999  
                break  
            }  
            case 'month': {  
                startTime = this.getTimeBySetMonth(offset, thistime, getAll)  
                const date = this.getDateObj(this.getDate('Y-m-d H:i:s', startTime))  
                const nextMonthFirstDayTime = new Date(date.getFullYear(), date.getMonth() + 1, 1).getTime()  
                endTime = getAll ? nextMonthFirstDayTime - 86400000 : this.getTimeByDateAndTimezone(  
                    nextMonthFirstDayTime) - 1  
                break  
            }  
        }  
        return {  
            startTime,  
            endTime  
        }  
    }

而这个问题,应该是出在 getTimeBySetWeek 上

/**  
    /**  
     * 根据设置的周数获取指定日期N周后(前)的时间戳  
     * @param {Number} weeks 周数  
     * @param {Date|Time} time 指定的日期或时间戳  
     * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定日期初始时间戳(当天00:00:00的时间戳)   
     */  
    getTimeBySetWeek(weeks, time, getAll = false) {  
        const date = this.getDateObj(time)  
        const dateInfo = this.getTimeInfo(time)  
        const day = dateInfo.nWeek  
        const offsetDays = 1 - day  
        if (weeks) {  
            weeks = weeks * 7 + offsetDays  
        }  
        date.setDate(date.getDate() + weeks)  
        let startTime = date.getTime()  
        if (!getAll) {  
            const realdate = this.getDate('Y-m-d 00:00:00', startTime)  
            startTime = this.getTimeByDateAndTimezone(realdate)  
        }  
        return startTime  
    }

如果 weeks 传入为 0,其中 time 是昨天的时间,最终计算的 startTime 其实是昨天的时间,也就是 2022-12-02 00:00:00。也就是本周统计的时间会从昨天凌晨时间算起,而不是由当前周的周一算起,这样在获取本周活跃设备时,只是获取到昨天的活跃设备。
作为比较我也查看了关于获取本月的活跃设备数的代码

// 取出本月已经存储的device_id  
        const dateMonthDimension = datetime.getTimeDimensionByType('month', 0, this.startTime)  
        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)  
            }  
        }  

/**  
     * 根据设置的月数获取指定日期N月后(前)的时间戳  
     * @param {Number} monthes 月数  
     * @param {Date|Time} time 指定的日期或时间戳  
     * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定日期初始时间戳(当天00:00:00的时间戳)   
     */  
    getTimeBySetMonth(monthes, time, getAll = false) {  
        const date = this.getDateObj(time)  
        date.setMonth(date.getMonth() + monthes)  
        let startTime = date.getTime()  
        if (!getAll) {  
            const realdate = this.getDate('Y-m-01 00:00:00', startTime)  
            startTime = this.getTimeByDateAndTimezone(realdate)  
        }  
        return startTime  
    }

如果是本月的话,那么 startTime 是 2022-12-01 00: 00: 00,这个月活跃设备数的获取是没有问题的。
目前问题就出现在周统计上,统计上只统计了从昨天开始的活跃设备,而不是从周一起的活跃设备

2022-12-03 16:49 负责人:DCloud_云服务_JRP 分享
已邀请:
DCloud_云服务_JRP

DCloud_云服务_JRP

文档和注释中应该都有写, activeDevices.js 这个模块是用来每天归档活跃设备的,供stat模块计算周活月活使用,并不是用来直接计算周活月活,这么做的原因是云数据库查询资源有时间限制,直接从日志表中计算周活月活很容易就超时

  • changlishe (作者)

    代码注释写了[仅添加本周/本月首次访问的设备],我贴的这两段代码是为了获得 haveWeekList 和 haveMonthList,根据后续的代码目的应该是用来筛选掉当天非本周/本月首次访问的设备,然后对当天的活跃设备归档。在筛选月统计时,我看确实是从月初开始筛选起的,作为对照周的筛选我以为也是从周一开始筛选起的,但是我发现是从昨天筛选起的,可能会出现本周早些天登录的设备也归档了。

    2022-12-09 00:15

changlishe

changlishe (作者)

如果抛去掉关于统计的问题,getTimeBySetWeek 这个函数本身可能是有问题的

/**  
     * 根据设置的周数获取指定日期N周后(前)的时间戳  
     * @param {Number} weeks 周数  
     * @param {Date|Time} time 指定的日期或时间戳  
     * @param {Boolean} getAll 是否获取完整时间戳,为 false 时返回指定日期初始时间戳(当天00:00:00的时间戳)   
     */  
    getTimeBySetWeek(weeks, time, getAll = false) {  
        const date = this.getDateObj(time)  
        const dateInfo = this.getTimeInfo(time)  
        const day = dateInfo.nWeek  
        const offsetDays = 1 - day  
        if (weeks) {  
            weeks = weeks * 7 + offsetDays  
        }  
        date.setDate(date.getDate() + weeks)  
        let startTime = date.getTime()  
        if (!getAll) {  
            const realdate = this.getDate('Y-m-d 00:00:00', startTime)  
            startTime = this.getTimeByDateAndTimezone(realdate)  
        }  
        return startTime  
    }

如果是 getTimeBySetWeek(0, new Date('2022-12-09')) 获取的结果是 2022/12/9 00:00:00 (不是本周一,而是当天凌晨)
如果是 getTimeBySetWeek(1, new Date('2022-12-09')) 获取的结果是 2022/12/12 00:00:00 (下周一)
如果是 getTimeBySetWeek(-1, new Date('2022-12-09')) 获取的结果是 2022/11/28 00:00:00 (上周一)
作为对比可以看一下 getTimeBySetMonth 函数
如果是 getTimeBySetMonth(0, new Date('2022-12-09')) 获取的结果是 2022/12/1 00:00:00 (本月)
如果是 getTimeBySetMonth(1, new Date('2022-12-09')) 获取的结果是 2023/1/1 00:00:00 (下一个月)
如果是 getTimeBySetMonth(-1, new Date('2022-12-09')) 获取的结果是 2022/11/1 00:00:00 (上一个月)

当然不清楚这个是否是故而设计的,但是同一个文件下还有一个问题代码

/**  
     * 时间格式转换  
     * @param {String} format 展示格式如:Y-m-d H:i:s  
     * @param {Time} time 时间戳  
     */  
    dateFormat(format, time) {  
        const timeInfo = this.getTimeInfo(time)  
        format = format || this.defaultDateFormat  
        let date = format  
        if (format) {  
            date = date.replace(/Y/, timeInfo.nYear)  
        }  
        if (format.indexOf('m') !== false) {  
            date = date.replace(/m/, timeInfo.nMonth)  
        }  
        if (format.indexOf('d') !== false) {  
            date = date.replace(/d/, timeInfo.nDay)  
        }  
        if (format.indexOf('H') !== false) {  
            date = date.replace(/H/, timeInfo.nHour)  
        }  
        if (format.indexOf('i') !== false) {  
            date = date.replace(/i/, timeInfo.nMinutes)  
        }  
        if (format.indexOf('s') !== false) {  
            date = date.replace(/s/, timeInfo.nSeconds)  
        }  
        return date  
    }

这种代码肯定是过不了审查的
[代码链接]https://github.com/dcloudio/uni-admin/blob/master/uniCloud-aliyun/cloudfunctions/common/uni-stat/stat/lib/date.js

  • DCloud_云服务_JRP

    dateFormat 函数确实有问题,之前已经调整过了但是还没有发布

    2022-12-12 10:12

该问题目前已经被锁定, 无法添加新回复