Skip to content

Commit

Permalink
feat: โœจ dailyAliveUserCountRecords ์ถ”๊ฐ€
Browse files Browse the repository at this point in the history
- close #392
  • Loading branch information
jpham005 committed Nov 14, 2023
1 parent ed9d4f7 commit 5586db2
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 2 deletions.
28 changes: 28 additions & 0 deletions app/src/page/home/user/home.user.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DateWrapper } from 'src/dateWrapper/dateWrapper';
import { HttpExceptionFilter } from 'src/http-exception.filter';
import { HomeUserService } from './home.user.service';
import {
GetHomeUserAliveUserCountRecordsArgs,
GetHomeUserBlackholedCountRecordsArgs,
HomeUser,
IntPerCircle,
Expand All @@ -29,6 +30,33 @@ export class HomeUserResolver {
return {};
}

@ResolveField((_returns) => [IntRecord])
async dailyAliveUserCountRecords(
@Args() { last }: GetHomeUserAliveUserCountRecordsArgs,
): Promise<IntRecord[]> {
const nextDay = new DateWrapper().startOfDate().moveDate(1).toDate();
const start = new DateWrapper()
.startOfDate()
.moveDate(1 - last)
.toDate();

const cacheKey = `homeUserDailyAliveUserCountRecords:${start.getTime()}:${nextDay.getTime()}`;

const cached = await this.cacheUtilService.get<IntRecord[]>(cacheKey);
if (cached) {
return cached;
}

const result = await this.homeUserService.dailyAliveUserCountRecords({
start,
end: nextDay,
});

await this.cacheUtilService.set(cacheKey, result, DateWrapper.MIN);

return result;
}

@ResolveField((_returns) => [IntRecord])
async aliveUserCountRecords(): Promise<IntRecord[]> {
return await this.homeUserService.aliveUserCountRecords();
Expand Down
146 changes: 145 additions & 1 deletion app/src/page/home/user/home.user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
blackholedUserFilterByDateRange,
} from 'src/api/cursusUser/db/cursusUser.database.query';
import type { cursus_user } from 'src/api/cursusUser/db/cursusUser.database.schema';
import { promo } from 'src/api/promo/db/promo.database.schema';
import { QuestsUserService } from 'src/api/questsUser/questsUser.service';
import { CacheOnReturn } from 'src/cache/decrators/onReturn/cache.decorator.onReturn.symbol';
import { assertExist } from 'src/common/assertExist';
import type { Rate } from 'src/common/models/common.rate.model';
import type { UserRank } from 'src/common/models/common.user.model';
import type { IntRecord } from 'src/common/models/common.valueRecord.model';
Expand All @@ -32,6 +32,150 @@ export class HomeUserService {
private readonly runtimeConfig: ConfigType<typeof RUNTIME_CONFIG>,
) {}

// todo: ๋‚˜์ค‘์— ๋ณ„๋„ service ๋กœ ๋ถ„๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
async dailyAliveUserCountRecords({
start,
end,
}: DateRange): Promise<IntRecord[]> {
const totalUserIOs = await this.cursusUserService
.aggregate<{ date: Date; count: number }>()
.match(
blackholedUserFilterByDateRange({
start: new Date(0),
end,
}),
)
.group({
_id: {
$dateFromParts: {
year: {
$year: {
date: '$blackholedAt',
timezone: this.runtimeConfig.TIMEZONE,
},
},
month: {
$month: {
date: '$blackholedAt',
timezone: this.runtimeConfig.TIMEZONE,
},
},
day: {
$dayOfMonth: {
date: '$blackholedAt',
timezone: this.runtimeConfig.TIMEZONE,
},
},
timezone: this.runtimeConfig.TIMEZONE,
},
},
count: { $sum: -1 },
})
.project({
_id: 0,
date: '$_id',
count: 1,
})
.unionWith({
// todo: collection ๋ถ„๋ฆฌ
coll: 'transfer_ins',
pipeline: [
{
$match: {
date: {
$gte: start,
$lt: end,
},
},
},
{
$group: {
_id: '$date',
count: { $count: {} },
},
},
{
$project: {
_id: 0,
date: '$_id',
count: 1,
},
},
],
})
.unionWith({
coll: `${promo.name}s`,
pipeline: [
{
$match: {
beginAt: {
$lt: end,
},
},
},
{
$project: {
_id: 0,
date: {
$dateFromParts: {
year: {
$year: {
date: '$beginAt',
timezone: this.runtimeConfig.TIMEZONE,
},
},
month: {
$month: {
date: '$beginAt',
timezone: this.runtimeConfig.TIMEZONE,
},
},
day: {
$dayOfMonth: {
date: '$beginAt',
timezone: this.runtimeConfig.TIMEZONE,
},
},
timezone: this.runtimeConfig.TIMEZONE,
},
},
count: '$userCount',
},
},
],
})
.group({
_id: '$date',
count: { $sum: '$count' },
})
.sort({ _id: 1 })
.project({
_id: 0,
date: '$_id',
count: 1,
});

const startDateIndex = totalUserIOs.findIndex(({ date }) => date >= start);
const beforeStart = totalUserIOs
.slice(0, startDateIndex)
.reduce((acc, { count }) => acc + count, 0);

return totalUserIOs.slice(startDateIndex).reduce(
({ prev, records }, { date, count }) => {
records.push({
at: date,
value: prev + count,
});

return {
prev: prev + count,
records,
};
},
{ prev: beforeStart, records: new Array<IntRecord>() },
).records;
}

@CacheOnReturn()
async aliveUserCountRecords(): Promise<IntRecord[]> {
const now = new DateWrapper();
Expand Down
13 changes: 13 additions & 0 deletions app/src/page/home/user/models/home.user.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ export class UserCountPerLevel {

@ObjectType()
export class HomeUser {
// todo: api version header ๋ฅผ ๋ฐ›๋Š” ๋“ฑ์˜ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ์กด field ๋ช…์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค๋งŒ,
// ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ํ˜„์žฌ ์ž‘์—…์„ ํ•  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ ‡๊ฒŒ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
@Field((_type) => [IntRecord])
dailyAliveUserCountRecords: IntRecord[];

@Field((_type) => [IntRecord], { deprecationReason: 'v0.10.0' })
aliveUserCountRecords: IntRecord[];

@Field((_type) => [UserCountPerLevel])
Expand Down Expand Up @@ -52,6 +57,14 @@ export class HomeUser {
averageDurationPerCircle: IntPerCircle[];
}

@ArgsType()
export class GetHomeUserAliveUserCountRecordsArgs {
@Min(1)
@Max(750)
@Field()
last: number;
}

@ArgsType()
export class GetHomeUserBlackholedCountRecordsArgs {
@Min(1)
Expand Down
3 changes: 2 additions & 1 deletion app/src/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ type UserCountPerLevel {
}

type HomeUser {
aliveUserCountRecords: [IntRecord!]!
dailyAliveUserCountRecords(last: Int!): [IntRecord!]!
aliveUserCountRecords: [IntRecord!]! @deprecated(reason: "v0.10.0")
userCountPerLevel: [UserCountPerLevel!]!
memberRate: Rate!
blackholedRate: Rate!
Expand Down

0 comments on commit 5586db2

Please sign in to comment.