Skip to content

Commit

Permalink
feat: ✨ getMyInfo 에 beginAt, level, myRecentActivity 추가
Browse files Browse the repository at this point in the history
기존에 묶지 않고 제공하는 field 들로 인해 type 의 가독성이 떨어진다고 생각하여 myRecentActivity 를 추가했습니다.

- close #367
  • Loading branch information
jpham005 committed Oct 26, 2023
1 parent fd5ec84 commit 65c1921
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 78 deletions.
72 changes: 65 additions & 7 deletions app/src/page/myInfo/models/myInfo.model.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { Field, Float, ObjectType } from '@nestjs/graphql';
import { UserPreview } from 'src/common/models/common.user.model';
import { UserTeam } from 'src/page/personal/general/models/personal.general.model';

export type MyInfoRoot = {
type SeoulStudentInfo = {
userPreview: UserPreview;
displayname: string;
level: number;
beginAt: Date;
};

@ObjectType()
export class MyInfo {
@Field()
type SeoulUserInfo = {
userPreview: UserPreview;

@Field()
displayname: string;
level?: number;
beginAt?: Date;
};

export type MyInfoRoot = SeoulStudentInfo | SeoulUserInfo;

@ObjectType()
export class MyRecentActivity {
@Field()
isNewMember: boolean;

Expand All @@ -33,3 +38,56 @@ export class MyInfo {
@Field({ nullable: true })
evalCountRank?: number;
}

@ObjectType()
export class MyInfo {
@Field()
userPreview: UserPreview;

@Field()
displayname: string;

@Field((_type) => Float, { nullable: true })
level?: number;

@Field({ nullable: true })
beginAt?: Date;

@Field({ nullable: true })
myRecentActivity?: MyRecentActivity;

@Field({
deprecationReason: 'deprecated at v0.9.0, recentActivity 를 사용하세요',
})
isNewMember: boolean;

@Field({
nullable: true,
deprecationReason: 'deprecated at v0.9.0, recentActivity 를 사용하세요',
})
lastValidatedTeam?: UserTeam;

@Field({
nullable: true,
deprecationReason: 'deprecated at v0.9.0, recentActivity 를 사용하세요',
})
blackholedAt?: Date;

@Field({
nullable: true,
deprecationReason: 'deprecated at v0.9.0, recentActivity 를 사용하세요',
})
experienceRank?: number;

@Field({
nullable: true,
deprecationReason: 'deprecated at v0.9.0, recentActivity 를 사용하세요',
})
scoreRank?: number;

@Field({
nullable: true,
deprecationReason: 'deprecated at v0.9.0, recentActivity 를 사용하세요',
})
evalCountRank?: number;
}
2 changes: 0 additions & 2 deletions app/src/page/myInfo/myInfo.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ 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 { UserModule } from 'src/api/user/user.module';
import { DateRangeModule } from 'src/dateRange/dateRange.module';
import { MyInfoResolver } from './myInfo.resolver';
import { MyInfoService } from './myInfo.service';

Expand All @@ -19,7 +18,6 @@ import { MyInfoService } from './myInfo.service';
ScaleTeamModule,
QuestsUserModule,
UserModule,
DateRangeModule,
],
providers: [MyInfoResolver, MyInfoService],
})
Expand Down
13 changes: 10 additions & 3 deletions app/src/page/myInfo/myInfo.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { UseFilters, UseGuards } from '@nestjs/common';
import { Int, Query, ResolveField, Resolver } from '@nestjs/graphql';
import { Int, Query, ResolveField, Resolver, Root } from '@nestjs/graphql';
import { MyUserId } from 'src/auth/myContext';
import { StatAuthGuard } from 'src/auth/statAuthGuard';
import { HttpExceptionFilter } from 'src/http-exception.filter';
import { UserTeam } from '../personal/general/models/personal.general.model';
import { MyInfo, MyInfoRoot } from './models/myInfo.model';
import { MyInfo, MyInfoRoot, MyRecentActivity } from './models/myInfo.model';
import { MyInfoService } from './myInfo.service';

@UseFilters(HttpExceptionFilter)
Expand All @@ -18,14 +18,21 @@ export class MyInfoResolver {
return await this.myInfoService.myInfoRoot(myUserId);
}

@ResolveField((_returns) => MyRecentActivity, { nullable: true })
async myRecentActivity(
@Root() myInfoRoot?: MyInfoRoot,
): Promise<MyRecentActivity | null> {
return await this.myInfoService.myRecentActivity(myInfoRoot);
}

@ResolveField((_returns) => Date, { nullable: true })
async blackholedAt(@MyUserId() myUserId: number): Promise<Date | null> {
return await this.myInfoService.blackholedAt(myUserId);
}

@ResolveField((_returns) => Boolean)
async isNewMember(@MyUserId() myUserId: number): Promise<boolean> {
return await this.myInfoService.isNewMember(myUserId);
return (await this.myInfoService.isNewMember(myUserId)) ?? false;
}

@ResolveField((_returns) => UserTeam, { nullable: true })
Expand Down
106 changes: 43 additions & 63 deletions app/src/page/myInfo/myInfo.service.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import type { FilterQuery } from 'mongoose';
import { Injectable } from '@nestjs/common';
import { CursusUserCacheService } from 'src/api/cursusUser/cursusUser.cache.service';
import { CursusUserService } from 'src/api/cursusUser/cursusUser.service';
import type { cursus_user } from 'src/api/cursusUser/db/cursusUser.database.schema';
import type { experience_user } from 'src/api/experienceUser/db/experienceUser.database.schema';
import { ExperienceUserCacheService } from 'src/api/experienceUser/experienceUser.cache.service';
import { ExperienceUserService } from 'src/api/experienceUser/experienceUser.service';
import type { quests_user } from 'src/api/questsUser/db/questsUser.database.schema';
import {
COMMON_CORE_QUEST_ID,
QuestsUserService,
} from 'src/api/questsUser/questsUser.service';
import type { scale_team } from 'src/api/scaleTeam/db/scaleTeam.database.schema';
import { ScaleTeamCacheService } from 'src/api/scaleTeam/scaleTeam.cache.service';
import { ScaleTeamService } from 'src/api/scaleTeam/scaleTeam.service';
import { scoreDateRangeFilter } from 'src/api/score/db/score.database.aggregate';
import { ScoreCacheService } from 'src/api/score/score.cache.service';
import { ScoreService } from 'src/api/score/score.service';
import { TeamService } from 'src/api/team/team.service';
import { UserService } from 'src/api/user/user.service';
import { CacheOnReturn } from 'src/cache/decrators/onReturn/cache.decorator.onReturn.symbol';
import { findUserRank } from 'src/common/findUserRank';
import { DateRangeService } from 'src/dateRange/dateRange.service';
import { DateTemplate } from 'src/dateRange/dtos/dateRange.dto';
import { DateWrapper } from 'src/dateWrapper/dateWrapper';
import type { UserTeam } from '../personal/general/models/personal.general.model';
import type { MyInfoRoot } from './models/myInfo.model';
import type { MyInfoRoot, MyRecentActivity } from './models/myInfo.model';

@Injectable()
export class MyInfoService {
Expand All @@ -34,14 +25,10 @@ export class MyInfoService {
private readonly cursusUserCacheService: CursusUserCacheService,
private readonly questsUserService: QuestsUserService,
private readonly teamService: TeamService,
private readonly experienceUserService: ExperienceUserService,
private readonly experienceUserCacheService: ExperienceUserCacheService,
private readonly scoreService: ScoreService,
private readonly scoreCacheService: ScoreCacheService,
private readonly scaleTeamService: ScaleTeamService,
private readonly scaleTeamCacheService: ScaleTeamCacheService,
private readonly userService: UserService,
private readonly dateRangeService: DateRangeService,
) {}

@CacheOnReturn()
Expand All @@ -57,6 +44,8 @@ export class MyInfoService {
imgUrl: cachedUserFullProfile.cursusUser.user.image.link,
},
displayname: cachedUserFullProfile.cursusUser.user.displayname,
beginAt: cachedUserFullProfile.cursusUser.beginAt,
level: cachedUserFullProfile.cursusUser.level,
};
}

Expand Down Expand Up @@ -89,6 +78,40 @@ export class MyInfoService {
return null;
}

@CacheOnReturn()
async myRecentActivity(
myInfoRoot?: MyInfoRoot,
): Promise<MyRecentActivity | null> {
// todo: isStudent 같은 field 를 제공하는 것도 방법일듯
if (!myInfoRoot || !myInfoRoot.beginAt) {
return null;
}

const userId = myInfoRoot.userPreview.id;

const cachedUserFullProfile =
await this.cursusUserCacheService.getUserFullProfile(userId);

if (!cachedUserFullProfile) {
return null;
}

const isNewMember = await this.isNewMember(userId);
if (isNewMember === null) {
return null;
}

return {
isNewMember,
lastValidatedTeam: (await this.lastValidatedTeam(userId)) ?? undefined,
blackholedAt: cachedUserFullProfile.cursusUser.blackholedAt,
experienceRank: await this.experienceRank(userId),
scoreRank: await this.scoreRank(userId),
evalCountRank: await this.evalCountRank(userId),
};
}

// todo: deprecate 이후 안쓰는 함수 삭제 및 private method 로 변경
@CacheOnReturn()
async blackholedAt(userId: number): Promise<Date | null> {
const cachedUserFullProfile =
Expand All @@ -105,7 +128,7 @@ export class MyInfoService {
}

@CacheOnReturn()
async isNewMember(userId: number): Promise<boolean> {
async isNewMember(userId: number): Promise<boolean | null> {
const questsUser: Pick<quests_user, 'validatedAt'> | null =
await this.questsUserService.findOneAndLean({
filter: {
Expand All @@ -116,7 +139,7 @@ export class MyInfoService {
});

if (!questsUser) {
throw new NotFoundException();
return null;
}

if (!questsUser.validatedAt) {
Expand Down Expand Up @@ -152,22 +175,7 @@ export class MyInfoService {
userId,
);

if (cachedRank) {
return cachedRank.rank;
}

const dateRange = this.dateRangeService.dateRangeFromTemplate(
DateTemplate.CURR_WEEK,
);

const dateFilter: FilterQuery<experience_user> = {
createdAt: this.dateRangeService.aggrFilterFromDateRange(dateRange),
};

const expIncreamentRanking =
await this.experienceUserService.increamentRanking(dateFilter);

return findUserRank(expIncreamentRanking, userId)?.rank;
return cachedRank?.rank;
}

async scoreRank(userId: number): Promise<number | undefined> {
Expand All @@ -176,19 +184,7 @@ export class MyInfoService {
userId,
);

if (cachedRank) {
return cachedRank.rank;
}

const dateRange = this.dateRangeService.dateRangeFromTemplate(
DateTemplate.CURR_WEEK,
);

const ranking = await this.scoreService.scoreRanking({
filter: scoreDateRangeFilter(dateRange),
});

return findUserRank(ranking, userId)?.rank;
return cachedRank?.rank;
}

async evalCountRank(userId: number): Promise<number | undefined> {
Expand All @@ -197,22 +193,6 @@ export class MyInfoService {
userId,
);

if (cachedRanking) {
return cachedRanking.rank;
}

const dateRange = this.dateRangeService.dateRangeFromTemplate(
DateTemplate.CURR_WEEK,
);

const dateFilter: FilterQuery<scale_team> = {
beginAt: this.dateRangeService.aggrFilterFromDateRange(dateRange),
};

const evalCountRanking = await this.scaleTeamService.evalCountRanking(
dateFilter,
);

return findUserRank(evalCountRanking, userId)?.rank;
return cachedRanking?.rank;
}
}
18 changes: 15 additions & 3 deletions app/src/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -401,9 +401,7 @@ type PersonalGeneral {
character: Character
}

type MyInfo {
userPreview: UserPreview!
displayname: String!
type MyRecentActivity {
isNewMember: Boolean!
lastValidatedTeam: UserTeam
blackholedAt: DateTime
Expand All @@ -412,6 +410,20 @@ type MyInfo {
evalCountRank: Int
}

type MyInfo {
userPreview: UserPreview!
displayname: String!
level: Float
beginAt: DateTime
myRecentActivity: MyRecentActivity
isNewMember: Boolean! @deprecated(reason: "deprecated at v0.9.0, recentActivity 를 사용하세요")
lastValidatedTeam: UserTeam @deprecated(reason: "deprecated at v0.9.0, recentActivity 를 사용하세요")
blackholedAt: DateTime @deprecated(reason: "deprecated at v0.9.0, recentActivity 를 사용하세요")
experienceRank: Int @deprecated(reason: "deprecated at v0.9.0, recentActivity 를 사용하세요")
scoreRank: Int @deprecated(reason: "deprecated at v0.9.0, recentActivity 를 사용하세요")
evalCountRank: Int @deprecated(reason: "deprecated at v0.9.0, recentActivity 를 사용하세요")
}

type PersonalEval {
userProfile: UserProfile!
correctionPoint: Int!
Expand Down

0 comments on commit 65c1921

Please sign in to comment.