Files
core/src/modules/analyze/analyze.service.ts
2022-04-17 21:04:37 +08:00

298 lines
6.2 KiB
TypeScript

import dayjs from 'dayjs'
import { merge } from 'lodash'
import type { PipelineStage } from 'mongoose'
import { Injectable } from '@nestjs/common'
import type { ReturnModelType } from '@typegoose/typegoose'
import { RedisKeys } from '~/constants/cache.constant'
import type { CacheService } from '~/processors/cache/cache.service'
import { InjectModel } from '~/transformers/model.transformer'
import { getRedisKey } from '~/utils/redis.util'
import { OptionModel } from '../configs/configs.model'
import { AnalyzeModel } from './analyze.model'
@Injectable()
export class AnalyzeService {
constructor(
@InjectModel(OptionModel)
private readonly options: ReturnModelType<typeof OptionModel>,
@InjectModel(AnalyzeModel)
private readonly analyzeModel: MongooseModel<AnalyzeModel>,
private readonly cacheService: CacheService,
) {}
public get model() {
return this.analyzeModel
}
async getRangeAnalyzeData(
from = new Date('2020-1-1'),
to = new Date(),
options?: {
limit?: number
page?: number
},
) {
const { limit = 50, page = 1 } = options || {}
const condition = {
$and: [
{
timestamp: {
$gte: from,
},
},
{
timestamp: {
$lte: to,
},
},
],
}
return await this.analyzeModel.paginate(condition, {
sort: { timestamp: -1 },
page,
limit,
})
}
async getCallTime() {
const callTime =
(
await this.options
.findOne({
name: 'apiCallTime',
})
.lean()
)?.value || 0
const uv =
(
await this.options
.findOne({
name: 'uv',
})
.lean()
)?.value || 0
return { callTime, uv }
}
async cleanAnalyzeRange(range: { from?: Date; to?: Date }) {
const { from, to } = range
await this.analyzeModel.deleteMany({
$and: [
{
timestamp: {
$gte: from,
},
},
{
timestamp: {
$lte: to,
},
},
],
})
}
async getIpAndPvAggregate(
type: 'day' | 'week' | 'month' | 'all',
returnObj?: boolean,
) {
let cond = {}
const now = dayjs()
const beforeDawn = now.set('minute', 0).set('second', 0).set('hour', 0)
switch (type) {
case 'day': {
cond = {
timestamp: {
$gte: beforeDawn.toDate(),
},
}
break
}
case 'month': {
cond = {
timestamp: {
$gte: beforeDawn.set('day', -30).toDate(),
},
}
break
}
case 'week': {
cond = {
timestamp: {
$gte: beforeDawn.set('day', -7).toDate(),
},
}
break
}
case 'all':
default: {
break
}
}
const [res, res2] = await Promise.all([
this.analyzeModel.aggregate([
{ $match: cond },
{
$project: {
_id: 1,
timestamp: 1,
hour: {
$dateToString: {
format: '%H',
date: { $subtract: ['$timestamp', 0] },
timezone: '+08:00',
},
},
date: {
$dateToString: {
format: '%Y-%m-%d',
date: { $subtract: ['$timestamp', 0] },
timezone: '+08:00',
},
},
},
},
{
$group: {
_id: type === 'day' ? '$hour' : '$date',
pv: {
$sum: 1,
},
},
},
{
$project: {
_id: 0,
...(type === 'day' ? { hour: '$_id' } : { date: '$_id' }),
pv: 1,
},
},
{
$sort: {
date: -1,
},
},
]),
this.analyzeModel.aggregate([
{ $match: cond },
{
$project: {
_id: 1,
timestamp: 1,
ip: 1,
hour: {
$dateToString: {
format: '%H',
date: { $subtract: ['$timestamp', 0] },
timezone: '+08:00',
},
},
date: {
$dateToString: {
format: '%Y-%m-%d',
date: { $subtract: ['$timestamp', 0] },
timezone: '+08:00',
},
},
},
},
{
$group: {
_id:
type === 'day'
? { ip: '$ip', hour: '$hour' }
: { ip: '$ip', date: '$date' },
},
},
{
$group: {
_id: type === 'day' ? '$_id.hour' : '$_id.date',
ip: {
$sum: 1,
},
},
},
{
$project: {
_id: 0,
...(type === 'day' ? { hour: '$_id' } : { date: '$_id' }),
ip: 1,
},
},
{
$sort: {
date: -1,
},
},
]),
])
const arr = merge(res, res2)
if (returnObj) {
const obj = {}
for (const item of arr) {
obj[item.hour || item.date] = item
}
return obj
}
return arr
}
async getRangeOfTopPathVisitor(from?: Date, to?: Date): Promise<any[]> {
from = from ?? new Date(new Date().getTime() - 1000 * 24 * 3600 * 7)
to = to ?? new Date()
const pipeline: PipelineStage[] = [
{
$match: {
timestamp: {
$gte: from,
$lte: to,
},
},
},
{
$group: {
_id: '$path',
count: {
$sum: 1,
},
},
},
{
$sort: {
count: -1,
},
},
{
$project: {
_id: 0,
path: '$_id',
count: 1,
},
},
]
const res = await this.analyzeModel.aggregate(pipeline).exec()
return res
}
async getTodayAccessIp(): Promise<string[]> {
const redis = this.cacheService.getClient()
const fromRedisIps = await redis.smembers(getRedisKey(RedisKeys.AccessIp))
return fromRedisIps
}
}