diff --git a/src/common/objects/response-message.object.ts b/src/common/objects/response-message.object.ts index d00a631..f309bc8 100644 --- a/src/common/objects/response-message.object.ts +++ b/src/common/objects/response-message.object.ts @@ -21,4 +21,5 @@ export const RESPONSE_MESSAGE: { // plants READ_PLANT_INFORMATION_SUCCESS: '식물 단계 조회 성공', READ_PLANT_WATER_LOG_SUCCESS: '식물 물주기 기록 조회 성공', + CREATE_PLANT_WATER_LOG_SUCCESS: '식물 물주기 생성 성공', }; diff --git a/src/constants/swagger/index.ts b/src/constants/swagger/index.ts index 8bdc276..f5e9d34 100644 --- a/src/constants/swagger/index.ts +++ b/src/constants/swagger/index.ts @@ -1,8 +1,17 @@ import { SIGNIN_DESCRIPTION } from './auth'; -import { PLANT_INFORMATION, PLANT_WATER_LOG } from './plants'; +import { + PLANT_INFORMATION, + GET_PLANT_WATER_LOG, + CREATE_PLANT_WATER, +} from './plants'; export const ERROR_DESCRIPTION = { INTERNAL_SERVER_ERROR: 'Internal Server Error', }; -export { SIGNIN_DESCRIPTION, PLANT_INFORMATION, PLANT_WATER_LOG }; +export { + SIGNIN_DESCRIPTION, + PLANT_INFORMATION, + GET_PLANT_WATER_LOG, + CREATE_PLANT_WATER, +}; diff --git a/src/constants/swagger/plants.ts b/src/constants/swagger/plants.ts index 93a6720..fbd782e 100644 --- a/src/constants/swagger/plants.ts +++ b/src/constants/swagger/plants.ts @@ -27,7 +27,7 @@ export const PLANT_INFORMATION = { }, }; -export const PLANT_WATER_LOG = { +export const GET_PLANT_WATER_LOG = { API_OPERATION: { SUMMARY: '식물 물주기 조회 API', DESCRIPTION: '식물별 물주기 기록을 조회합니다.', @@ -49,3 +49,23 @@ export const PLANT_WATER_LOG = { BAD_REQUEST: 'Bad Request - 요청 id 가 없거나, number가 아닌 경우 등', }, }; + +export const CREATE_PLANT_WATER = { + API_OPERATION: { + SUMMARY: '식물 물주기 생성 API', + DESCRIPTION: '식물별 물주기를 생성합니다.', + }, + API_PARAM: { + NAME: 'id', + DESCRIPTION: 'user_plant_id', + }, + DTO_DESCRIPTION: { + RESPONSE: { + ID: 'water id', + else: '더 작성해야 함', + }, + }, + ERROR_DESCRIPTION: { + BAD_REQUEST: 'Bad Request - 해당 날짜에 이미 물 준 경우', + }, +}; diff --git a/src/plants/dto/plantwaterlog.dto.ts b/src/plants/dto/plantwaterlog.dto.ts new file mode 100644 index 0000000..ad440fd --- /dev/null +++ b/src/plants/dto/plantwaterlog.dto.ts @@ -0,0 +1,52 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; +import { ResponseSuccessDto } from 'src/common/dto/response-success.dto'; +import { GET_PLANT_WATER_LOG } from 'src/constants/swagger'; + +const DTO_RESPONSE_DESCRIPTION = GET_PLANT_WATER_LOG.DTO_DESCRIPTION.RESPONSE; + +export class PlantWaterLogData { + @ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.REVIEWS.ID }) + id: number; + + @ApiProperty({ + description: DTO_RESPONSE_DESCRIPTION.REVIEWS.CREATEDAT, + }) + wateringDate: string; + + @ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.REVIEWS.REVIEW }) + review: string; + + @ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.REVIEWS.REVIEW }) + keywords?: string[]; +} + +export class ResponseGetPlantWaterLogData { + @ApiProperty({ isArray: true, type: PlantWaterLogData }) + reviews: PlantWaterLogData[]; +} + +export class ResponseGetPlantWaterLogDto extends ResponseSuccessDto { + @ApiProperty() + data: ResponseGetPlantWaterLogData; +} + +export class CreatePlantWaterLogDto { + @ApiProperty() + keywords: string[]; + + @ApiProperty() + @IsString() + review: string; +} + +export class ResponseCreatePlantWaterDto { + @ApiProperty() + id: number; + + @ApiProperty() + userPlantId: number; + + @ApiProperty() + wateringDate: Date; +} diff --git a/src/plants/dto/response-plantwaterlog.dto.ts b/src/plants/dto/response-plantwaterlog.dto.ts deleted file mode 100644 index e5c8505..0000000 --- a/src/plants/dto/response-plantwaterlog.dto.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { ResponseSuccessDto } from 'src/common/dto/response-success.dto'; -import { PLANT_WATER_LOG } from 'src/constants/swagger'; - -const DTO_RESPONSE_DESCRIPTION = PLANT_WATER_LOG.DTO_DESCRIPTION.RESPONSE; - -export class PlantWaterReviewData { - @ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.REVIEWS.ID }) - id: number; - - @ApiProperty({ - description: DTO_RESPONSE_DESCRIPTION.REVIEWS.CREATEDAT, - }) - wateringDate: string; - - @ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.REVIEWS.REVIEW }) - review: string; -} - -export class ResponsePlantWaterLogData { - @ApiProperty() - reviews: PlantWaterReviewData[]; -} - -export class ResponsePlantWaterLogDto extends ResponseSuccessDto { - @ApiProperty() - data: ResponsePlantWaterLogData; -} diff --git a/src/plants/plants.controller.ts b/src/plants/plants.controller.ts index 0117ca9..718d4fd 100644 --- a/src/plants/plants.controller.ts +++ b/src/plants/plants.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, HttpStatus, Param } from '@nestjs/common'; +import { Body, Controller, Get, HttpStatus, Param, Post } from '@nestjs/common'; import { ApiBadRequestResponse, ApiInternalServerErrorResponse, @@ -15,11 +15,16 @@ import { CommonParamsDto } from 'src/common/dto/common-params.dto'; import { ERROR_DESCRIPTION, PLANT_INFORMATION, - PLANT_WATER_LOG, + GET_PLANT_WATER_LOG, + CREATE_PLANT_WATER, } from 'src/constants/swagger'; import { wrapSuccess } from 'src/utils/success'; import { RESPONSE_MESSAGE } from 'src/common/objects'; -import { ResponsePlantWaterLogDto } from './dto/response-plantwaterlog.dto'; +import { + ResponseCreatePlantWaterDto, + ResponseGetPlantWaterLogDto, +} from './dto/plantwaterlog.dto'; +import { badRequest } from 'src/utils/error'; @Controller('plants') @ApiTags('Plants') @@ -61,22 +66,22 @@ export class PlantsController { @Get(':id/water') @ApiOperation({ - summary: PLANT_WATER_LOG.API_OPERATION.SUMMARY, - description: PLANT_WATER_LOG.API_OPERATION.DESCRIPTION, + summary: GET_PLANT_WATER_LOG.API_OPERATION.SUMMARY, + description: GET_PLANT_WATER_LOG.API_OPERATION.DESCRIPTION, }) @ApiParam({ type: Number, - name: PLANT_WATER_LOG.API_PARAM.NAME, + name: GET_PLANT_WATER_LOG.API_PARAM.NAME, required: true, - description: PLANT_WATER_LOG.API_PARAM.DESCRIPTION, + description: GET_PLANT_WATER_LOG.API_PARAM.DESCRIPTION, }) - @ApiOkResponse({ type: ResponsePlantWaterLogDto }) + @ApiOkResponse({ type: ResponseGetPlantWaterLogDto }) @ApiBadRequestResponse({ - description: PLANT_WATER_LOG.ERROR_DESCRIPTION.BAD_REQUEST, + description: GET_PLANT_WATER_LOG.ERROR_DESCRIPTION.BAD_REQUEST, }) async getPlantWaterLog( @Param() { id }: CommonParamsDto, - ): Promise { + ): Promise { const data = await this.plantsService.getPlantWaterLog(id); return wrapSuccess( @@ -85,4 +90,31 @@ export class PlantsController { data, ); } + + @Post(':id/water') + @ApiOperation({ + summary: CREATE_PLANT_WATER.API_OPERATION.SUMMARY, + description: CREATE_PLANT_WATER.API_OPERATION.DESCRIPTION, + }) + @ApiParam({ + type: Number, + name: CREATE_PLANT_WATER.API_PARAM.NAME, + required: true, + description: CREATE_PLANT_WATER.API_PARAM.DESCRIPTION, + }) + @ApiOkResponse({ type: ResponseCreatePlantWaterDto }) + @ApiBadRequestResponse({ + description: CREATE_PLANT_WATER.ERROR_DESCRIPTION.BAD_REQUEST, + }) + async postPlantWaterLog( + @Param() { id }: CommonParamsDto, + ): Promise { + const data = await this.plantsService.createPlantWater(id); + + return wrapSuccess( + HttpStatus.OK, + RESPONSE_MESSAGE.CREATE_PLANT_WATER_LOG_SUCCESS, + data, + ); + } } diff --git a/src/plants/plants.service.ts b/src/plants/plants.service.ts index 599d3d0..5475589 100644 --- a/src/plants/plants.service.ts +++ b/src/plants/plants.service.ts @@ -1,11 +1,15 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from 'src/prisma.service'; -import { notFound } from 'src/utils/error'; +import { notFound, badRequest } from 'src/utils/error'; import { renameObjectKey } from 'src/utils/object'; import { ResponsePlantInformationData } from './dto/response-plantInformation.dto'; -import { ResponsePlantWaterLogData } from './dto/response-plantwaterlog.dto'; +import { + ResponseGetPlantWaterLogData, + ResponseCreatePlantWaterDto, +} from './dto/plantwaterlog.dto'; import * as dayjs from 'dayjs'; +import { getCurrentSeoulTime } from 'src/utils/date'; @Injectable() export class PlantsService { @@ -41,7 +45,7 @@ export class PlantsService { ); } - async getPlantWaterLog(id: number): Promise { + async getPlantWaterLog(id: number): Promise { const reviews = await this.prisma.water.findMany({ where: { userPlantId: id, isDeleted: false }, select: { @@ -65,4 +69,56 @@ export class PlantsService { return { reviews: result }; } + + async createPlantWater(id: number): Promise { + const today: Date = getCurrentSeoulTime(); + + const startOfDay = new Date(today); + startOfDay.setUTCHours(0, 0, 0, 0); + + const endOfDay = new Date(startOfDay); + endOfDay.setDate(endOfDay.getDate() + 1); + + const checkTodayWatering = await this.prisma.water.findFirst({ + where: { + userPlantId: id, + wateringDate: { + gte: startOfDay, + lt: endOfDay, + }, + }, + }); + + if (checkTodayWatering) { + throw badRequest(); + } + const waterData = { + userPlantId: id, + wateringDate: today, + }; + + const water = await this.prisma.water.create({ + data: waterData, + }); + + const updateUserPlant = await this.prisma.userPlant.update({ + where: { id }, + data: { + loveGauge: { + increment: 0.5, + }, + waterCount: { + increment: 1, + }, + }, + }); + + const result = { + id: water.id, + userPlantId: water.userPlantId, + wateringDate: water.wateringDate, + }; + + return result; + } } diff --git a/src/utils/date.ts b/src/utils/date.ts new file mode 100644 index 0000000..a801968 --- /dev/null +++ b/src/utils/date.ts @@ -0,0 +1,11 @@ +export function getCurrentSeoulTime(): Date { + const seoulTimeZone = 'Asia/Seoul'; + const currentTime: Date = new Date(); + + const seoulTime: Date = new Date( + currentTime.toLocaleString('en-US', { timeZone: seoulTimeZone }), + ); + + seoulTime.setHours(seoulTime.getHours() + 9); + return seoulTime; +} diff --git a/src/utils/validation.ts b/src/utils/validation.ts index 830f925..1d80537 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -2,6 +2,7 @@ import { Logger } from '@nestjs/common'; import { CreateSigninDto } from 'src/auth/dto/create-signin.dto'; import { CustomException } from 'src/exceptions'; import { badRequest } from './error'; +import { CreatePlantWaterLogDto } from 'src/plants/dto/plantwaterlog.dto'; export class Validation { private readonly logger = new Logger(Validation.name);