Skip to content

Commit

Permalink
Merge pull request #190 from boostcampwm2023/feat/188-yearly-shape-ra…
Browse files Browse the repository at this point in the history
…nking-api

[Feat] 연도별 모양 순위 API 구현
  • Loading branch information
mingxoxo authored Dec 4, 2023
2 parents 9ddde9c + f5ae0e5 commit 24836e0
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 13 deletions.
8 changes: 8 additions & 0 deletions BE/src/stat/dto/stat.shapes.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class ShapeInfoDto {
uuid: number;
count: number;
}

export class StatShapeDto {
[key: string]: ({ rank: number } & ShapeInfoDto) | {};
}
9 changes: 9 additions & 0 deletions BE/src/stat/stat.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { JwtAuthGuard } from "src/auth/guard/auth.jwt-guard";
import { User } from "src/auth/users.entity";
import { StatService } from "./stat.service";
import { StatTagDto } from "./dto/stat.tags.dto";
import { StatShapeDto } from "./dto/stat.shapes.dto";

@Controller("stat")
@UseGuards(JwtAuthGuard)
Expand All @@ -23,4 +24,12 @@ export class StatController {
): Promise<StatTagDto> {
return this.statService.getTopThreeTagsByUser(year, user.id);
}

@Get("/shapes-rank/:year")
async getShapesRank(
@Param("year", ParseIntPipe) year: number,
@GetUser() user: User,
): Promise<StatShapeDto> {
return this.statService.getTopThreeShapesByUser(year, user.id);
}
}
46 changes: 34 additions & 12 deletions BE/src/stat/stat.service.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,65 @@
import { Injectable } from "@nestjs/common";
import { Diary } from "src/diaries/diaries.entity";
import { StatTagDto, TagInfoDto } from "./dto/stat.tags.dto";
import { ShapeInfoDto, StatShapeDto } from "./dto/stat.shapes.dto";

@Injectable()
export class StatService {
async getTopThreeTagsByUser(
year: number,
userId: number,
): Promise<StatTagDto> {
const result: TagInfoDto[] = await this.fetchTopThreeTagsByUser(
const result: TagInfoDto[] = await this.fetchTopThreeByUser<TagInfoDto>(
year,
userId,
"tags",
["tags.name", "tags.id"],
["tags.name as tag", "tags.id as id"],
);
return this.getFormatResult(result);
}

private async fetchTopThreeTagsByUser(
async getTopThreeShapesByUser(
year: number,
userId: number,
): Promise<TagInfoDto[]> {
return await Diary.createQueryBuilder("diary")
.select("tags.name", "tag")
.addSelect("tags.id", "id")
): Promise<StatShapeDto> {
const result: ShapeInfoDto[] = await this.fetchTopThreeByUser<ShapeInfoDto>(
year,
userId,
"shape",
["shape.uuid"],
["shape.uuid as uuid"],
);
return this.getFormatResult(result);
}

private async fetchTopThreeByUser<T extends TagInfoDto | ShapeInfoDto>(
year: number,
userId: number,
joinTable: string,
groupByFields: any,
selectFields: any,
): Promise<T[]> {
return Diary.createQueryBuilder("diary")
.select(selectFields)
.addSelect("COUNT(*)", "count")
.innerJoin("diary.tags", "tags")
.innerJoin(`diary.${joinTable}`, joinTable)
.where("diary.user = :userId", { userId })
.andWhere("YEAR(diary.date) = :year", { year })
.groupBy("tags.name")
.addGroupBy("tags.id")
.groupBy(groupByFields)
.orderBy("count", "DESC")
.take(3)
.limit(3)
.getRawMany();
}

private getFormatResult(result: TagInfoDto[]): StatTagDto {
private getFormatResult(
result: TagInfoDto[] | ShapeInfoDto[],
): StatTagDto | StatShapeDto {
const keys = ["first", "second", "third"];
const formattedResult = {};
result.forEach((item, index) => {
result.forEach((item: TagInfoDto | ShapeInfoDto, index: number) => {
const rank = index + 1;
item.count = Number(item.count);
formattedResult[keys[index]] = { rank, ...item };
});

Expand Down
48 changes: 47 additions & 1 deletion BE/test/int/stat.service.int-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ describe("StatService 통합 테스트", () => {
service = module.get<StatService>(StatService);
});

afterEach(async () => {
await jest.clearAllMocks();
});

describe("getTopThreeTagsByUser 메서드", () => {
it("메서드 정상 요청", async () => {
const year = 2023;
Expand All @@ -27,7 +31,7 @@ describe("StatService 통합 테스트", () => {
groupBy: jest.fn().mockReturnThis(),
addGroupBy: jest.fn().mockReturnThis(),
orderBy: jest.fn().mockReturnThis(),
take: jest.fn().mockReturnThis(),
limit: jest.fn().mockReturnThis(),
getRawMany: jest.fn().mockResolvedValue([
{ tag: "태그1", id: 1, count: 10 },
{ tag: "태그2", id: 2, count: 9 },
Expand All @@ -48,4 +52,46 @@ describe("StatService 통합 테스트", () => {
});
});
});

describe("getTopThreeShapesByUser 메서드", () => {
it("메서드 정상 요청", async () => {
const year = 2023;
const userId = 1;

const mockQueryBuilder = {
select: jest.fn().mockReturnThis(),
addSelect: jest.fn().mockReturnThis(),
innerJoin: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
andWhere: jest.fn().mockReturnThis(),
groupBy: jest.fn().mockReturnThis(),
addGroupBy: jest.fn().mockReturnThis(),
orderBy: jest.fn().mockReturnThis(),
limit: jest.fn().mockReturnThis(),
getRawMany: jest.fn().mockResolvedValue([
{ uuid: "5d94024c-0f41-4e42-b766-a4f298766f67", count: 6 },
{ uuid: "5c670cee-f1e1-42ee-9b97-1ff5f561049e", count: 5 },
]),
};

jest
.spyOn(Diary, "createQueryBuilder")
.mockReturnValue(mockQueryBuilder as any);

const result = await service.getTopThreeShapesByUser(year, userId);

expect(result).toEqual({
first: {
rank: 1,
uuid: "5d94024c-0f41-4e42-b766-a4f298766f67",
count: 6,
},
second: {
rank: 2,
uuid: "5c670cee-f1e1-42ee-9b97-1ff5f561049e",
count: 5,
},
});
});
});
});

0 comments on commit 24836e0

Please sign in to comment.