Skip to content

Commit

Permalink
Feat: 메인 식물 리스트 조회 API (#40)
Browse files Browse the repository at this point in the history
* Feat: 메인 식물 리스트 조회 응닶값 세팅

* Update issue templates

* Feat: 메인 화면 랜덤 description 함수 추가

* Chore: Gauge로 통일

* Feat: 메인 식물 리스트 전체 조회 API

* Chore: getUserPlants로 함수 이름 변경

* Chore: class 대문자로 변경

* Chore: 프리티어 적용

---------

Co-authored-by: 장서현 <[email protected]>
  • Loading branch information
seohyun-106 and 장서현 authored Dec 17, 2023
1 parent 11c790b commit 3cc8379
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 38 deletions.
27 changes: 27 additions & 0 deletions .github/ISSUE_TEMPLATE/fix-.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
name: 'Fix:'
about: fix
title: ''
labels: ''
assignees: ''

---

---
name: Fix:
about: fix
title: 'Fix: ~'
labels: ''
assignees: ''

---

## 🚅 Issue 한 줄 요약

<!-- 고쳐야 할 기능에 대한 내용을 짧게 설명해주세요. -->

## 🤷 Issue 세부 내용

<!-- 해야 할 일들을 적어주세요. -->

- [ ] todo!
1 change: 1 addition & 0 deletions src/common/objects/response-message.object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ export const RESPONSE_MESSAGE: {
UPDATE_PLANT_DETAIL_SUCCESS: '식물 카드 업데이트 성공',
READ_PLANT_INFORMATION_SUCCESS: '식물 단계 조회 성공',
READ_PLANT_WATER_LOG_SUCCESS: '식물 물주기 기록 조회 성공',
READ_PLANTS_SUCCESS: '메인 식물 리스트 조회 성공',
};
2 changes: 2 additions & 0 deletions src/constants/swagger/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
UPDATE_PLANT_DETAIL,
READ_PLANT_INFORMATION,
READ_PLANT_WATER_LOG,
READ_PLANTS,
} from './plants';

export const ERROR_DESCRIPTION = {
Expand All @@ -16,4 +17,5 @@ export {
UPDATE_PLANT_DETAIL,
READ_PLANT_INFORMATION,
READ_PLANT_WATER_LOG,
READ_PLANTS,
};
27 changes: 26 additions & 1 deletion src/constants/swagger/plants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const READ_PLANT_DETAIL = {
PLANT_NAME: '식물 이름',
LEVEL_NAME: '식물 레벨 이름',
STATUS_MESSAGE: '식물 상태 메시지',
STATUS_GAGUE: '식물 상태 게이지',
STATUS_GAUGE: '식물 상태 게이지',
},
},
ERROR_DESCRIPTION: {
Expand Down Expand Up @@ -112,3 +112,28 @@ export const READ_PLANT_WATER_LOG = {
BAD_REQUEST: 'Bad Request - 요청 id 가 없거나, number가 아닌 경우 등',
},
};

export const READ_PLANTS = {
API_OPERATION: {
summary: '메인 식물 리스트 조회 API',
description: '유저의 전체 식물 리스트를 조회합니다.',
},
DTO_DESCRIPTION: {
RESPONSE: {
PLANTS: {
ID: 'userPlant id',
PLANT_TYPE: '식물 종류 - rose, sun, stuki, min, blue, ojya',
D_DAY: '물주기까지 남은 일수',
NICKNAME: '식물 닉네임',
DESCRIPTION: '식물 묘사 - ex) 바짝바짝 목이 마른',
LEVEL_NAME: '식물 레벨 이름',
CIRCLE_IMAGE: '식물 원형 이미지 url',
MAIN_IMAGE: '식물 레벨별 메인 이미지 url',
LOVE_GAUGE: '식물 애정도',
// IS_WATERED: '오늘 식물에게 물을 준 적 있는지 여부'
},
PLANT_COUNT: '식물 총 갯수',
},
},
ERROR_DESCRIPTION: {},
};
12 changes: 6 additions & 6 deletions src/plants/constants/plant-status.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
export const PLANT_STATUS: Record<
string,
{ statusMessage: string; statusGague: number }
{ statusMessage: string; statusGauge: number }
> = {
healthy: {
statusMessage: '힘이 솟아요',
statusGague: 1,
statusGauge: 1,
},
waterDay: {
statusMessage: '물 주는 날이에요!',
statusGague: 1,
statusGauge: 1,
},
happy: {
statusMessage: '기분이 좋아요',
statusGague: 0.75,
statusGauge: 0.75,
},
thirsty: {
statusMessage: '갈증나요',
statusGague: 0.5,
statusGauge: 0.5,
},
veryThirsty: {
statusMessage: '물 주세요',
statusGague: 0.25,
statusGauge: 0.25,
},
};

Expand Down
16 changes: 0 additions & 16 deletions src/plants/dto/read-plants.dto.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/plants/dto/response-plant-detail.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export class ResponsePlantDetailData {
@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.STATUS_MESSAGE })
statusMessage: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.STATUS_GAGUE })
statusGague: number;
@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.STATUS_GAUGE })
statusGauge: number;
}

export class ResponsePlantDetailDto extends ResponseSuccessDto {
Expand Down
50 changes: 50 additions & 0 deletions src/plants/dto/response-plants.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ApiProperty } from '@nestjs/swagger';
import { ResponseSuccessDto } from 'src/common/dto/response-success.dto';
import { READ_PLANTS } from 'src/constants/swagger';

const DTO_RESPONSE_DESCRIPTION = READ_PLANTS.DTO_DESCRIPTION.RESPONSE;

export class UserPlantData {
@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANTS.ID })
id: number;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANTS.PLANT_TYPE })
plantType: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANTS.D_DAY })
dDay: number;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANTS.NICKNAME })
nickname: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANTS.DESCRIPTION })
description: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANTS.LEVEL_NAME })
levelName: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANTS.CIRCLE_IMAGE })
circleImage: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANTS.MAIN_IMAGE })
mainImage: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANTS.LOVE_GAUGE })
loveGauge: number;

// @ApiProperty({description: DTO_RESPONSE_DESCRIPTION.PLANTS.IS_WATERED})
// isWatered: boolean;
}

export class ResponseUserPlantsData {
@ApiProperty({ isArray: true, type: UserPlantData })
userPlants: UserPlantData[];

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANT_COUNT })
userPlantsCount: number;
}

export class ResponseUserPlantsDto extends ResponseSuccessDto {
@ApiProperty()
data: ResponseUserPlantsData;
}
15 changes: 15 additions & 0 deletions src/plants/plants.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import {
UPDATE_PLANT_DETAIL,
READ_PLANT_INFORMATION,
READ_PLANT_WATER_LOG,
READ_PLANTS,
} from 'src/constants/swagger';
import { wrapSuccess } from 'src/utils/success';
import { RESPONSE_MESSAGE } from 'src/common/objects';
import { ResponsePlantWaterLogDto } from './dto/response-plant-water-log.dto';
import { ResponsePlantDetailDto } from './dto/response-plant-detail.dto';
import { ResponseUserPlantsDto } from './dto/response-plants.dto';
import { UpdatePlantDetailDto } from './dto/update-plant-detail.dto';
import { ResponseSuccessDto } from 'src/common/dto/response-success.dto';

Expand Down Expand Up @@ -118,4 +120,17 @@ export class PlantsController {
data,
);
}

@Get()
@ApiOperation(READ_PLANTS.API_OPERATION)
@ApiOkResponse({ type: ResponseUserPlantsDto })
async getUserPlants(): Promise<ResponseUserPlantsDto> {
const data = await this.plantsService.getUserPlants(1);

return wrapSuccess(
HttpStatus.OK,
RESPONSE_MESSAGE.READ_PLANTS_SUCCESS,
data,
);
}
}
6 changes: 3 additions & 3 deletions src/plants/plants.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('PlantsService', () => {
mockUserPlantResponse as any,
);
jest
.spyOn(service, 'getPlantLevelNameByLoveGague')
.spyOn(service, 'getPlantLevelNameByLoveGauge')
.mockResolvedValueOnce({ levelName: '새싹' });

const result = await service.getUserPlantDetail(mockUserPlantId);
Expand All @@ -73,12 +73,12 @@ describe('PlantsService', () => {
it('존재하지 않는 userPlantId 가 주어지면 Not Found 에러를 반환한다.', async () => {
const mockFindUnique =
userPlantPrisma.findUnique.mockResolvedValueOnce(null);
jest.spyOn(service, 'getPlantLevelNameByLoveGague').mockImplementation();
jest.spyOn(service, 'getPlantLevelNameByLoveGauge').mockImplementation();

await expect(
service.getUserPlantDetail(mockUserPlantId),
).rejects.toThrowError(notFound());
expect(service.getPlantLevelNameByLoveGague).not.toHaveBeenCalled();
expect(service.getPlantLevelNameByLoveGauge).not.toHaveBeenCalled();
});
});

Expand Down
100 changes: 95 additions & 5 deletions src/plants/plants.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as utilDay from 'src/utils/day';
import { ResponsePlantDetailData } from './dto/response-plant-detail.dto';
import { ResponsePlantInformationData } from './dto/response-plant-information.dto';
import { ResponsePlantWaterLogData } from './dto/response-plant-water-log.dto';
import { ResponseUserPlantsData } from './dto/response-plants.dto';
import { UpdatePlantDetailDto } from './dto/update-plant-detail.dto';

@Injectable()
Expand Down Expand Up @@ -60,7 +61,7 @@ export class PlantsService {
const { wateringDate } = Water[0];
const { name: plantName, circleImageURL: plantImage } = plant;

const { levelName } = await this.getPlantLevelNameByLoveGague(
const { levelName } = await this.getPlantLevelNameByLoveGauge(
plantId,
loveGauge,
);
Expand All @@ -73,7 +74,7 @@ export class PlantsService {
const dDay: number = utilDay.calculateDday(new Date(), nextWateringDate);
const duration: number = -utilDay.calculateDday(new Date(), createdAt);

const { statusMessage, statusGague } = utilPlants.calculatePlantStatus(
const { statusMessage, statusGauge } = utilPlants.calculatePlantStatus(
plantId,
-dDay,
);
Expand All @@ -89,7 +90,7 @@ export class PlantsService {
plantImage,
levelName,
statusMessage,
statusGague,
statusGauge,
};
}

Expand All @@ -109,9 +110,9 @@ export class PlantsService {

async getPlantLevelNameByLoveGague(
plantId: number,
loveGague: number,
loveGauge: number,
): Promise<Pick<PlantLevel, 'levelName'>> {
const level = utilPlants.calculatePlantLevel(loveGague);
const level = utilPlants.calculatePlantLevel(loveGauge);

const plantLevel = await this.prisma.plantLevel.findUnique({
where: { id: plantId, level, isDeleted: false },
Expand Down Expand Up @@ -177,4 +178,93 @@ export class PlantsService {

return { reviews: result };
}

async getUserPlants(userId: number): Promise<ResponseUserPlantsData> {
const userPlants = await this.prisma.userPlant.findMany({
where: { userId, isDeleted: false },
select: {
id: true,
plantId: true,
nickname: true,
loveGauge: true,
waterCycle: true,
waterCount: true,
plant: {
select: {
name: true,
circleImageURL: true,
},
},
Water: {
select: {
wateringDate: true,
},
orderBy: {
wateringDate: 'desc',
},
take: 1,
},
},
});

if (!userPlants) {
const data = {
userPlants: [],
userPlantsCount: 0,
};
return data;
}

const processedUserPlants = await Promise.all(
userPlants.map(async (userPlant) => {
const nextWateringDate: Date = utilPlants.calculateNextWateringDate(
userPlant.Water[0].wateringDate,
userPlant.waterCycle,
);

const dDay: number = utilDay.calculateDday(
new Date(),
nextWateringDate,
);

const description = utilPlants.makeRandomDescription(
dDay,
userPlant.waterCount,
);

const levelName = await this.getPlantLevelNameByLoveGauge(
userPlant.plantId,
userPlant.loveGauge,
);

const mainImage = await this.prisma.plantLevel.findFirst({
where: {
plantId: userPlant.plantId,
level: utilPlants.calculatePlantLevel(userPlant.loveGauge),
},
select: { imageURL: true },
});

const data = {
id: userPlant.id,
plantType: userPlant.plant.name,
dDay,
nickname: userPlant.nickname,
description,
levelName: levelName.levelName,
circleImage: userPlant.plant.circleImageURL,
mainImage: mainImage.imageURL,
loveGauge: userPlant.loveGauge,
};
return data;
}),
);

const data = {
userPlants: processedUserPlants,
userPlantsCount: processedUserPlants.length,
};

return data;
}
}
Loading

0 comments on commit 3cc8379

Please sign in to comment.