Skip to content

Commit

Permalink
feat: ✨ daily logtime module 추가를 통해 logtimeRecords 의 last 지원 범위 증가
Browse files Browse the repository at this point in the history
  • Loading branch information
jpham005 committed Nov 10, 2023
1 parent ac99a3d commit 4f8a37e
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 42 deletions.
14 changes: 14 additions & 0 deletions app/src/dailyLogtime/dailyLogtime.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { DateRange } from 'src/dateRange/dtos/dateRange.dto';

export type UserLogtimeRecordsByDateRangeInput = {
userId: number;
} & DateRange;

export type UserLogtimeRecordByDateRangeOutput = {
/**
*
* @description %Y-%m Date 형식
*/
yearMonth: string;
value: number;
};
20 changes: 20 additions & 0 deletions app/src/dailyLogtime/dailyLogtime.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { DailyLogtimeService } from './dailyLogtime.service';
import { DailyLogtimeDaoImpl } from './db/dailyLogtime.database.dao';
import {
dailyLogtimeSchema,
daily_logtimes,
} from './db/dailyLogtime.database.schema';

@Module({
imports: [
MongooseModule.forFeature([
{ name: daily_logtimes.name, schema: dailyLogtimeSchema },
]),
],
providers: [DailyLogtimeService, DailyLogtimeDaoImpl],
exports: [DailyLogtimeService],
})
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class DailyLogtimeModule {}
61 changes: 61 additions & 0 deletions app/src/dailyLogtime/dailyLogtime.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Test } from '@nestjs/testing';
import {
UserLogtimeRecordByDateRangeOutput,
UserLogtimeRecordsByDateRangeInput,
} from './dailyLogtime.dto';
import { DailyLogtimeService } from './dailyLogtime.service';
import { DailyLogtimeDaoImpl } from './db/dailyLogtime.database.dao';

describe(DailyLogtimeService.name, () => {
let dailyLogtimeService: DailyLogtimeService;
let testExistUserId = 1;
let testDateRange = {
start: new Date('2021-01-01T15:00:00.000Z'),
end: new Date('2021-02-31T15:00:00.000Z'),
};

beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
providers: [DailyLogtimeService],
})
.useMocker((token) => {
if (token === DailyLogtimeDaoImpl) {
return {
userLogtimeRecordsByDateRange: (
args: UserLogtimeRecordsByDateRangeInput,
): UserLogtimeRecordByDateRangeOutput[] => {
if (args.userId !== testExistUserId) {
return [];
}

return [
{
yearMonth: '2021-01',
value: 1,
},
{
yearMonth: '2021-02',
value: 2,
},
];
},
};
}
})
.compile();

dailyLogtimeService = moduleRef.get(DailyLogtimeService);
});

it('반환 타입이 IntRecord[] 인지 확인한다.', async () => {
const result = await dailyLogtimeService.userLogtimeRecordsByDateRange(
testExistUserId,
testDateRange,
);

result.forEach((curr) => {
expect(isNaN(curr.at.getTime())).toBe(false);
expect(typeof curr.value === 'number').toBe(true);
});
});
});
30 changes: 30 additions & 0 deletions app/src/dailyLogtime/dailyLogtime.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Injectable } from '@nestjs/common';
import type { IntRecord } from 'src/common/models/common.valueRecord.model';
import type { DateRange } from 'src/dateRange/dtos/dateRange.dto';
import { DailyLogtimeDaoImpl } from './db/dailyLogtime.database.dao';

@Injectable()
export class DailyLogtimeService {
constructor(private readonly dailyLogtimeDao: DailyLogtimeDaoImpl) {}

async userLogtimeRecordsByDateRange(
userId: number,
{ start, end }: DateRange,
): Promise<IntRecord[]> {
const userLogtimeRecords =
await this.dailyLogtimeDao.userLogtimeRecordsByDateRange({
userId,
start,
end,
});

return userLogtimeRecords.map(({ yearMonth, value }) => {
const [year, month] = yearMonth.split('-').map(Number);

return {
at: new Date(year, month - 1),
value,
};
});
}
}
60 changes: 60 additions & 0 deletions app/src/dailyLogtime/db/dailyLogtime.database.dao.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Inject, Injectable } from '@nestjs/common';
import type { ConfigType } from '@nestjs/config';
import { InjectModel } from '@nestjs/mongoose';
import type { Model } from 'mongoose';
import { RUNTIME_CONFIG } from 'src/config/runtime';
import type {
UserLogtimeRecordByDateRangeOutput,
UserLogtimeRecordsByDateRangeInput,
} from '../dailyLogtime.dto';
import { daily_logtimes } from './dailyLogtime.database.schema';

export type DailyLogtimeDao = {
userLogtimeRecordsByDateRange: (
args: UserLogtimeRecordsByDateRangeInput,
) => Promise<UserLogtimeRecordByDateRangeOutput[]>;
};

@Injectable()
export class DailyLogtimeDaoImpl implements DailyLogtimeDao {
constructor(
@InjectModel(daily_logtimes.name)
private readonly dailyLogtime: Model<daily_logtimes>,
@Inject(RUNTIME_CONFIG.KEY)
private readonly runtimeConfig: ConfigType<typeof RUNTIME_CONFIG>,
) {}

async userLogtimeRecordsByDateRange({
userId,
start,
end,
}: UserLogtimeRecordsByDateRangeInput): Promise<
UserLogtimeRecordByDateRangeOutput[]
> {
return await this.dailyLogtime
.aggregate<UserLogtimeRecordByDateRangeOutput>()
.match({
userId,
date: {
$gte: start,
$lte: end,
},
})
.group({
_id: {
$dateToString: {
date: '$date',
format: '%Y-%m',
timezone: this.runtimeConfig.TIMEZONE,
},
},
value: { $sum: '$value' },
})
.sort({ _id: 1 })
.project({
_id: 0,
yearMonth: '$_id',
value: 1,
});
}
}
13 changes: 11 additions & 2 deletions app/src/page/personal/general/models/personal.general.model.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Field, Float, ObjectType } from '@nestjs/graphql';
import { ArgsType, Field, Float, ObjectType } from '@nestjs/graphql';
import { Max, Min } from 'class-validator';
import { ProjectPreview } from 'src/common/models/common.project.model';
import { IntRecord } from 'src/common/models/common.valueRecord.model';
import { DateRanged } from 'src/dateRange/models/dateRange.model';
import { TeamStatus } from 'src/page/teamInfo/models/teamInfo.status.model';
import { DailyActivity } from './personal.general.dailyActivity.model';
import { Character } from '../character/models/personal.general.character.model';
import { DailyActivity } from './personal.general.dailyActivity.model';
import { UserProfile } from './personal.general.userProfile.model';

@ObjectType()
Expand Down Expand Up @@ -97,6 +98,14 @@ export class UserScoreInfo {
rankInTotal: number;
}

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

export type PersonalGeneralRoot = Pick<
PersonalGeneral,
'userProfile' | 'beginAt' | 'blackholedAt' | 'wallet'
Expand Down
2 changes: 2 additions & 0 deletions app/src/page/personal/general/personal.general.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ScaleTeamModule } from 'src/api/scaleTeam/scaleTeam.module';
import { ScoreModule } from 'src/api/score/score.module';
import { TeamModule } from 'src/api/team/team.module';
import { DailyActivityModule } from 'src/dailyActivity/dailyActivity.module';
import { DailyLogtimeModule } from 'src/dailyLogtime/dailyLogtime.module';
import { DateRangeModule } from 'src/dateRange/dateRange.module';
import { PersonalUtilModule } from '../util/personal.util.module';
import { PersonalGeneralCharacterModule } from './character/persoanl.general.character.module';
Expand All @@ -30,6 +31,7 @@ import { PersonalGeneralService } from './personal.general.service';
CoalitionsUserModule,
DailyActivityModule,
DateRangeModule,
DailyLogtimeModule,
],
providers: [PersonalGeneralResolver, PersonalGeneralService],
})
Expand Down
29 changes: 24 additions & 5 deletions app/src/page/personal/general/personal.general.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { UseFilters, UseGuards } from '@nestjs/common';
import { Args, Query, ResolveField, Resolver, Root } from '@nestjs/graphql';
import { MyUserId } from 'src/auth/myContext';
import { StatAuthGuard } from 'src/auth/statAuthGuard';
import { CacheUtilService } from 'src/cache/cache.util.service';
import { IntRecord } from 'src/common/models/common.valueRecord.model';
import { DailyActivityService } from 'src/dailyActivity/dailyActivity.service';
import { DailyLogtimeService } from 'src/dailyLogtime/dailyLogtime.service';
import { DateRangeService } from 'src/dateRange/dateRange.service';
import { DateTemplateArgs } from 'src/dateRange/dtos/dateRange.dto';
import { DateWrapper } from 'src/dateWrapper/dateWrapper';
Expand All @@ -16,6 +18,7 @@ import {
GetDailyActivitiesArgs,
} from './models/personal.general.dailyActivity.model';
import {
GetLogtimeRecordsArgs,
LevelRecord,
PersonalGeneral,
PersonalGeneralRoot,
Expand All @@ -25,7 +28,6 @@ import {
UserTeamInfo,
} from './models/personal.general.model';
import { PersonalGeneralService } from './personal.general.service';
import { CacheUtilService } from 'src/cache/cache.util.service';

@UseFilters(HttpExceptionFilter)
@UseGuards(StatAuthGuard)
Expand All @@ -37,6 +39,7 @@ export class PersonalGeneralResolver {
private readonly personalGeneralCharacterService: PersonalGeneralCharacterService,
private readonly dateRangeService: DateRangeService,
private readonly dailyActiviyService: DailyActivityService,
private readonly dailyLogtimeService: DailyLogtimeService,
private readonly cacheUtilService: CacheUtilService,
) {}

Expand Down Expand Up @@ -64,15 +67,31 @@ export class PersonalGeneralResolver {
return await this.personalGeneralService.scoreInfo(root.userProfile.id);
}

@ResolveField((_returns) => [IntRecord], { description: '1 ~ 24 개월' })
@ResolveField((_returns) => [IntRecord], { description: '1 ~ 120 개월' })
async logtimeRecords(
@Root() root: PersonalGeneralRoot,
@Args('last') last: number,
@Args() { last }: GetLogtimeRecordsArgs,
): Promise<IntRecord[]> {
return await this.personalGeneralService.logtimeRecords(
const currMonth = DateWrapper.currMonth().toDate();
const startMonth = new DateWrapper(currMonth).moveMonth(-last + 1).toDate();

const cacheKey = `logtimeRecords:${
root.userProfile.id
}:${startMonth.toISOString()}:${currMonth.toISOString()}`;

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

const result = await this.dailyLogtimeService.userLogtimeRecordsByDateRange(
root.userProfile.id,
Math.max(1, Math.min(last, 24)),
{ start: startMonth, end: currMonth },
);

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

return result;
}

@ResolveField((_returns) => PreferredTimeDateRanged)
Expand Down
23 changes: 0 additions & 23 deletions app/src/page/personal/general/personal.general.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { ScoreCacheService } from 'src/api/score/score.cache.service';
import { TeamService } from 'src/api/team/team.service';
import { CacheOnReturn } from 'src/cache/decrators/onReturn/cache.decorator.onReturn.symbol';
import type { IntDateRanged } from 'src/common/models/common.dateRanaged.model';
import { IntRecord } from 'src/common/models/common.valueRecord.model';
import { DateRangeService } from 'src/dateRange/dateRange.service';
import { DateTemplate, type DateRange } from 'src/dateRange/dtos/dateRange.dto';
import { DateWrapper } from 'src/dateWrapper/dateWrapper';
Expand Down Expand Up @@ -139,28 +138,6 @@ export class PersonalGeneralService {
return await this.logtimeByDateRange(userId, dateRange);
}

async logtimeRecords(userId: number, last: number): Promise<IntRecord[]> {
const ret: IntRecord[] = [];
const startDate = new DateWrapper().startOfMonth().moveMonth(1 - last);

for (let i = 0; i < last; i++) {
const currStartDate = startDate.moveMonth(i).toDate();
const currEndDate = startDate.moveMonth(i + 1).toDate();

const [curr] = await this.locationService.logtimeRanking(
{
start: currStartDate,
end: currEndDate,
},
{ 'user.id': userId },
);

ret.push({ at: currStartDate, value: curr?.value ?? 0 });
}

return ret;
}

private async preferredTime(
userId: number,
filter: FilterQuery<location>,
Expand Down
24 changes: 12 additions & 12 deletions app/src/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,18 @@ type LeaderboardScore {
byDateTemplate(pageSize: Int! = 10, pageNumber: Int! = 1, promo: Int, coalitionId: Int, dateTemplate: DateTemplate!): LeaderboardElementDateRanged!
}

type CharacterType {
name: String!
description: String!
color: String!
}

type Character {
name: String!
types: [CharacterType!]!
imgUrl: String!
}

type DailyActivity {
date: DateTime!
records: [DailyActivityRecord!]!
Expand All @@ -333,18 +345,6 @@ type DailyDefaultRecord {
at: DateTime!
}

type CharacterType {
name: String!
description: String!
color: String!
}

type Character {
name: String!
types: [CharacterType!]!
imgUrl: String!
}

type UserTeam {
id: Int!
name: String!
Expand Down

0 comments on commit 4f8a37e

Please sign in to comment.