Commit 551fe6bc authored by qinhaitao's avatar qinhaitao

refactor: 💡 数据优化

parent b1a4368f
...@@ -4,7 +4,7 @@ import { USER_DB_NAME, ACCESS_DB_NAME, AWARDS_DB_NAME } from '../db' ...@@ -4,7 +4,7 @@ import { USER_DB_NAME, ACCESS_DB_NAME, AWARDS_DB_NAME } from '../db'
import { resultsModel, dateFormatter, transformBeijingDate, BaseDao } from '../sdk' import { resultsModel, dateFormatter, transformBeijingDate, BaseDao } from '../sdk'
import { CODE_TYPES } from '../constants' import { CODE_TYPES } from '../constants'
import * as xlsx from 'node-xlsx' import * as xlsx from 'node-xlsx'
import { services, registeInfos } from '../decorator/common' import { checkParams, services } from '../decorator/common'
import { StatService } from '../service' import { StatService } from '../service'
export interface IGetStatsParams { export interface IGetStatsParams {
...@@ -26,154 +26,21 @@ export default class StatController { ...@@ -26,154 +26,21 @@ export default class StatController {
@services([StatService]) @services([StatService])
async addStat(context: IContext<IAddStatsParams>, { userInfo }: IControllerInfos, [statService]: [StatService]) { async addStat(context: IContext<IAddStatsParams>, { userInfo }: IControllerInfos, [statService]: [StatService]) {
const { type } = context.data const { type } = context.data
await statService.addStat(type, userInfo) const result = await statService.addStat(type, userInfo)
return resultsModel.success(true) return resultsModel.success(result)
} }
/** /**
* 统计数据 * 统计数据
*/ */
async getStats(context: IContext<IGetStatsParams>) { @checkParams(['activityId', 'startDay', 'endDay'])
@services([StatService])
async getStats(context: IContext<IGetStatsParams>, {}: IControllerInfos, [statService]: [StatService]) {
const { activityId, startDay, endDay } = context.data const { activityId, startDay, endDay } = context.data
const userDao: IBaseDao = new BaseDao(context, USER_DB_NAME) const xlsxData = await statService.getStats(activityId, startDay, endDay)
const accessDao: IBaseDao = new BaseDao(context, ACCESS_DB_NAME)
const oneDay = 24 * 3600 * 1000
const xlsxData = []
try {
let day = startDay
while (new Date(day).getTime() <= new Date(endDay).getTime()) {
// 访问PV
const PV = await accessDao.count({ activityId, createDay: day })
// 访问UV
const UV = (
await accessDao.aggregate([
{ $match: { activityId, createDay: day } },
{ $group: { _id: '$openId', count: { $sum: 1 } } },
{ $count: 'uv' }
])
)?.[0]?.uv
// 新增UV
const newUV = await userDao.count({
activityId,
createDay: day
})
// 已入会(老会员)PV
const vipPV = await accessDao.count({
activityId,
createDay: day,
//@ts-ignore
$or: [{ 'member.flag': true }, { 'member.newMember': true }],
'member.bememberDay': {
$not: {
$eq: day
}
}
})
// 已入会(老会员)UV
const vipUV = (
await accessDao.aggregate(
[
{
$match: {
activityId,
createDay: day,
$or: [{ 'member.flag': true }, { 'member.newMember': true }],
'member.bememberDay': {
$not: {
$eq: day
}
}
}
},
{ $project: { openId: true } },
{ $group: { _id: '$openId', count: { $sum: 1 } } },
{ $count: 'uv' }
],
{ allowDiskUse: true }
)
)?.[0]?.uv
// 未入会PV
const noVipPV = await accessDao.count({ activityId, createDay: day, isVip: false })
// 未入会UV
const noVipUV = (
await accessDao.aggregate([
{ $match: { activityId, createDay: day, isVip: false } },
{ $group: { _id: '$openId', count: { $sum: 1 } } }
])
).length
// 新增入会UV
const newVipUV = await userDao.count({
activityId,
'member.newMember': true,
'member.bememberDay': day
})
const newFollowUV = await userDao.count({
activityId,
'follow.newFollow': true,
'follow.followDay': day
})
// 根据任务类型获取完成任务的人数
// example: await getTaskCompleteUV('collectGoods', day)
const getTaskCompleteUV = async (task: string, day: string) => {
return await userDao.count({
activityId,
[`taskInfo.${day}.${task}`]: {
$exists: true
}
})
}
const keyValueMapper = {
时间: day,
访问PV: PV,
访问UV: UV,
新增UV: newUV,
已入会PV: vipPV,
已入会UV: vipUV,
未入会PV: noVipPV,
未入会UV: noVipUV,
新入会UV: newVipUV,
新增关注UV: newFollowUV
//收藏商品UV: await getTaskCompleteUV('collectGoods', day)
}
console.log(keyValueMapper, 'xlsxData')
if (day === startDay) {
xlsxData.push(Object.keys(keyValueMapper))
}
// @ts-ignore
xlsxData.push(Object.values(keyValueMapper))
let tommorrow = new Date(day).getTime() + oneDay
day = dateFormatter(transformBeijingDate(tommorrow), 'yyyy/MM/dd')
}
let buffer = xlsx.build([ const result = await statService.buildExcel(startDay, endDay, xlsxData)
{
name: `数据统计${startDay.replace(/\//g, '-')}-${endDay.replace(/\//g, '-')}`,
data: xlsxData
}
])
let result = await context.cloud.file.uploadFile({ return resultsModel.success(result)
fileContent: buffer,
fileName: `数据统计${startDay.replace(/\//g, '-')}-${endDay.replace(/\//g, '-')}.xlsx`
})
if (result.url) {
result.url = result.url.replace('http', 'https').replace('-internal', '')
}
return resultsModel.success(result)
} catch (e) {
console.log('上传文件出错', e)
// 打印日志
return resultsModel.error(CODE_TYPES.SYSTEM_ERROR, `上传文件错误`)
}
} }
} }
...@@ -82,11 +82,9 @@ export default class Task { ...@@ -82,11 +82,9 @@ export default class Task {
const { rewardsKey } = context.data const { rewardsKey } = context.data
const { taskType } = context.data const { taskType } = context.data
const rewards = await taskService.receiveTaskRewards(taskType, rewardsKey, userInfo) const rewardsResult = await taskService.receiveTaskRewards(taskType, rewardsKey, userInfo)
return resultsModel.success({ return resultsModel.success(rewardsResult)
rewards
})
} }
/** /**
......
...@@ -4,17 +4,24 @@ ...@@ -4,17 +4,24 @@
* @format * @format
*/ */
import { BaseDao } from '../sdk' import { BaseDao, dateFormatter, transformBeijingDate } from '../sdk'
import { STAT_DB_NAME } from '../db' import { STAT_DB_NAME, ACCESS_DB_NAME, USER_DB_NAME, AWARDS_DB_NAME } from '../db'
import * as xlsx from 'node-xlsx'
import { getToday } from '../utils' import { getToday } from '../utils'
import { STAT_TYPE } from '../constants' import { STAT_TYPE } from '../constants'
export default class StatService { export default class StatService {
context: IContext<any> context: IContext<any>
statdao: IBaseDao statdao: IBaseDao
accessdao: IBaseDao
userdao: IBaseDao
awardsdao: IBaseDao
constructor(context: IContext<IParams>) { constructor(context: IContext<IParams>) {
this.context = context this.context = context
this.statdao = new BaseDao(context, STAT_DB_NAME) this.statdao = new BaseDao(context, STAT_DB_NAME)
this.accessdao = new BaseDao(context, ACCESS_DB_NAME)
this.userdao = new BaseDao(context, USER_DB_NAME)
this.awardsdao = new BaseDao(context, AWARDS_DB_NAME)
} }
/** /**
...@@ -42,6 +49,118 @@ export default class StatService { ...@@ -42,6 +49,118 @@ export default class StatService {
}) })
} }
async getStats(activityId: string, startDay: string, endDay: string): Promise<any> {
let day = startDay
const oneDay = 24 * 3600 * 1000
let xlsxData = []
let curDayTimestamp = new Date(day).getTime()
const endDayTimestamp = new Date(endDay).getTime()
while (curDayTimestamp <= endDayTimestamp) {
// 访问PV
const PV = await this.accessdao.count({ activityId, createDay: day })
// 访问UV
const UV = await this.userdao.count({ activityId, [`login.${day}`]: { $exists: true } })
// 新增UV
const newUV = await this.userdao.count({ activityId, createDay: day })
// 已入会(老会员)PV
const vipPV = await this.accessdao.count({
activityId,
createDay: day,
//@ts-ignore
$or: [{ 'member.flag': true }, { 'member.newMember': true }],
'member.bememberDay': { $not: { $eq: day } }
})
// 已入会(老会员)UV
const vipUV = (
await this.accessdao.aggregate([
{
$match: {
activityId,
createDay: day,
$or: [{ 'member.flag': true }, { 'member.newMember': true }],
'member.bememberDay': { $not: { $eq: day } }
}
},
{ $project: { openId: true } },
{ $group: { _id: '$openId', count: { $sum: 1 } } },
{ $count: 'uv' }
])
)?.[0]?.uv
// 未入会PV
const noVipPV = await this.accessdao.count({ activityId, createDay: day, isVip: false })
// 未入会UV
const noVipUV = (
await this.accessdao.aggregate([
{ $match: { activityId, createDay: day, isVip: false } },
{ $project: { openId: true } },
{ $group: { _id: '$openId', count: { $sum: 1 } } }
])
).length
// 新增入会UV
const newVipUV = await this.userdao.count({ activityId, 'member.newMember': true, 'member.bememberDay': day })
const newFollowUV = await this.userdao.count({ activityId, 'follow.newFollow': true, 'follow.followDay': day })
// 根据任务类型获取完成任务的人数
// example: await getTaskCompleteUV('collectGoods', day)
const getTaskCompleteUV = async (task: string, day: string) => {
return await this.userdao.count({
activityId,
[`taskInfo.${day}.${task}`]: { $exists: true }
})
}
const keyValueMapper = {
时间: day,
访问PV: PV,
访问UV: UV,
新增UV: newUV,
已入会PV: vipPV,
已入会UV: vipUV,
未入会PV: noVipPV,
未入会UV: noVipUV,
新入会UV: newVipUV,
新增关注UV: newFollowUV
//收藏商品UV: await getTaskCompleteUV('collectGoods', day)
}
console.log(keyValueMapper, 'xlsxData')
if (day === startDay) {
xlsxData.push(Object.keys(keyValueMapper))
}
// @ts-ignore
xlsxData.push(Object.values(keyValueMapper))
const tommorrow = new Date(day).getTime() + oneDay
curDayTimestamp = tommorrow
day = dateFormatter(transformBeijingDate(tommorrow), 'yyyy/MM/dd')
}
return xlsxData
}
async buildExcel(startDay: string, endDay: string, xlsxData: []) {
let buffer = xlsx.build([
{
name: `数据统计${startDay.replace(/\//g, '-')}-${endDay.replace(/\//g, '-')}`,
data: xlsxData
}
])
let result = await this.context.cloud.file.uploadFile({
fileContent: buffer,
fileName: `数据统计${startDay.replace(/\//g, '-')}-${endDay.replace(/\//g, '-')}.xlsx`
})
if (result.url) {
result.url = result.url.replace('http', 'https').replace('-internal', '')
}
return result
}
async getStatCountByquery(query: IMongoQuery) { async getStatCountByquery(query: IMongoQuery) {
return await this.statdao.count(query) return await this.statdao.count(query)
} }
......
...@@ -79,7 +79,13 @@ export default class TaskService extends UserService { ...@@ -79,7 +79,13 @@ export default class TaskService extends UserService {
* @return {number} rewards * @return {number} rewards
* @memberof TaskService * @memberof TaskService
*/ */
async receiveTaskRewards(taskType: ITaskType, awardsTargetKey: string, userInfo: IUserInfo): Promise<number> { async receiveTaskRewards(
taskType: ITaskType,
awardsTargetKey: string,
userInfo: IUserInfo
): Promise<{
rewards: number
}> {
const waitReceiveTimes = userInfo?.remainTimes?.[taskType] const waitReceiveTimes = userInfo?.remainTimes?.[taskType]
await this.updateUser(userInfo._id, { await this.updateUser(userInfo._id, {
...@@ -91,7 +97,9 @@ export default class TaskService extends UserService { ...@@ -91,7 +97,9 @@ export default class TaskService extends UserService {
} }
}) })
return waitReceiveTimes return {
rewards: waitReceiveTimes
}
} }
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment