Skip to content
3 changes: 1 addition & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ dependencies {
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3'
implementation group: 'org.springframework.data', name: 'spring-data-jpa', version: '3.4.3'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '3.4.3'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.1'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
Expand All @@ -62,6 +60,7 @@ dependencies {
testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
runtimeOnly 'com.mysql:mysql-connector-j'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,151 +3,151 @@
import com.project.growfit.domain.Diet.dto.request.AddDietRequestDto;
import com.project.growfit.domain.Diet.dto.request.UpdateDietRequestDto;
import com.project.growfit.domain.Diet.dto.request.UpdateFoodListRequestDto;
import com.project.growfit.domain.Diet.dto.response.*;
import com.project.growfit.domain.Diet.entity.DietState;
import com.project.growfit.domain.Diet.entity.Sticker;
import com.project.growfit.domain.Diet.service.DietService;
import com.project.growfit.global.response.ResultCode;
import com.project.growfit.global.response.ResultResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/diet")
@Tag(name = "Diet API", description = "식단일지 관련 API")
@Tag(name = "식단 관련 API", description = "식단일지 관련 API입니다.")
public class DietController {

private final DietService dietService;

@Operation(summary = "음식 검색", description = "키워드로 음식을 검색합니다.")
@GetMapping("/food/search")
public ResponseEntity<?> searchFoods(@Parameter(description = "검색 키워드", example = "닭") @RequestParam String keyword,
@Parameter(description = "페이지 번호", example = "0") @RequestParam(defaultValue = "0") int page,
@Parameter(description = "페이지 크기", example = "10") @RequestParam(defaultValue = "10") int size) {
ResultResponse<?> resultResponse = dietService.searchFoods(keyword, page, size);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
public ResultResponse<List<FoodResponseDto>> searchFoods(@Parameter(description = "검색 키워드", example = "닭") @RequestParam @NotBlank(message = "검색 키워드를 입력해주세요.") String keyword,
@Parameter(description = "페이지 번호", example = "0") @RequestParam(defaultValue = "0") @Min(0) int page,
@Parameter(description = "페이지 크기", example = "10") @RequestParam(defaultValue = "10") @Min(1) int size) {

List<FoodResponseDto> dtos = dietService.searchFoods(keyword, page, size);

return dtos.isEmpty()
? ResultResponse.of(ResultCode.DIET_SEARCH_RESULT_EMPTY, dtos)
: ResultResponse.of(ResultCode.DIET_SEARCH_SUCCESS, dtos);
}

@Operation(summary = "음식 상세 조회", description = "특정 음식의 상세 정보를 조회합니다.")
@GetMapping("/food/{foodId}")
public ResponseEntity<?> getFoodDetail(@Parameter(description = "음식 ID", example = "1")
public ResultResponse<FoodResponseDto> getFoodDetail(@Parameter(description = "음식 ID", example = "1")
@PathVariable Long foodId) {
ResultResponse<?> resultResponse = dietService.getFoodDetail(foodId);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
FoodResponseDto dto = dietService.getFoodDetail(foodId);
return ResultResponse.of(ResultCode.DIET_DETAIL_RETRIEVAL_SUCCESS, dto);
}

@Operation(summary = "식단 추가", description = "식단을 추가합니다.")
@PostMapping(value = "/food")
public ResponseEntity<?> addDiet(@Valid @RequestBody AddDietRequestDto request) {
ResultResponse<?> resultResponse = dietService.addDiet(request);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
public ResultResponse<DietBasicDto>addDiet(@Valid @RequestBody AddDietRequestDto request) {
DietBasicDto dto = dietService.addDiet(request);

return ResultResponse.of(ResultCode.DIET_ADD_SUCCESS, dto);
}

@Operation(summary = "식단 수정", description = "식단을 수정합니다.")
@PutMapping("/food/{dietId}")
public ResponseEntity<?> updateDiet(@PathVariable Long dietId,
@PatchMapping("/food/{dietId}")
public ResultResponse<DietBasicDto> updateDiet(@PathVariable Long dietId,
@Valid @RequestBody UpdateDietRequestDto request){
ResultResponse<?> resultResponse = dietService.updateDiet(dietId, request);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
}
DietBasicDto dto = dietService.updateDiet(dietId, request);

@Operation(summary = "식단 시간 수정", description = "식단의 섭취 시간을 수정합니다.")
@PatchMapping("/food/{dietId}/time")
public ResponseEntity<?> updateDietTime(@PathVariable Long dietId,
@RequestParam String updateTime) {
ResultResponse<?> resultResponse = dietService.updateDietTime(dietId, updateTime);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
return ResultResponse.of(ResultCode.DIET_EDIT_SUCCESS, dto);
}

@Operation(summary = "식단 제거", description = "식단을 제거합니다.")
@DeleteMapping("/food/{dietId}")
public ResponseEntity<?> deleteDiet(@PathVariable Long dietId) {
ResultResponse<?> resultResponse = dietService.deleteDiet(dietId);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
public ResultResponse<DietBasicDto> deleteDiet(@PathVariable Long dietId) {
DietBasicDto dto = dietService.deleteDiet(dietId);

return ResultResponse.of(ResultCode.DIET_DELETE_SUCCESS, dto);
}

@Operation(summary = "식단 상세 조회", description = "식단 상세 정보를 조회합니다.")
@GetMapping("/food/info/{dietId}")
public ResponseEntity<?> getDietDetail(@PathVariable Long dietId) {
ResultResponse<?> resultResponse = dietService.getDietDetail(dietId);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
}
public ResultResponse<DietResponseDto> getDietDetail(@PathVariable Long dietId) {
DietResponseDto dto = dietService.getDietDetail(dietId);

@Operation(summary = "아이 식단 사진 추가", description = "아이가 저장된 식단에 사진을 추가합니다.")
@PostMapping(value = "/food/{dietId}/photo", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> uploadDietPhoto(@PathVariable Long dietId,
@RequestPart MultipartFile image) {
ResultResponse<?> resultResponse = dietService.uploadPhoto(dietId, image);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
return ResultResponse.of(ResultCode.DIET_DETAIL_RETRIEVAL_SUCCESS, dto);
}

@Operation(summary = "아이 식단 섭취 여부 등록", description = "식단 섭취 여부(MATCH/UNMATCH)를 등록합니다. 식단을 어길 시 UNMATCH, 식단을 지킬 시 MATCH")
@PatchMapping("/food/{dietId}/state")
public ResponseEntity<?> updateDietState(@PathVariable Long dietId,
@RequestParam DietState dietState) {
ResultResponse<?> result = dietService.updateDietState(dietId, dietState);
return ResponseEntity.ok(result);
@Operation(summary = "식단 완료 제출", description = "식단 사진과 식단 상태(MATCH/UNMATCH)를 함께 제출합니다.")
@PostMapping(value = "/food/{dietId}/submit", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResultResponse<DietBasicDto> submitDiet(
@PathVariable Long dietId,
@RequestPart(name = "image", required = false) MultipartFile image,
@RequestPart(name = "dietState") DietState dietState) {

DietBasicDto dto = dietService.submitDiet(dietId, image, dietState);
return ResultResponse.of(ResultCode.DIET_SUBMIT_SUCCESS, dto);
}

@Operation(summary = "식단 불이행 시 영양 정보 직접 입력", description = "아이 식단을 지키지 못한 경우 음식 리스트를 수정합니다.")
@PutMapping("/food/{dietId}/override")
public ResponseEntity<?> overrideDietNutrition(@PathVariable Long dietId,
@Valid @RequestBody UpdateFoodListRequestDto dto) {
ResultResponse<?> resultResponse = dietService.overrideDietNutrition(dietId, dto);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
public ResultResponse<DietBasicDto> overrideDietNutrition(@PathVariable Long dietId,
@Valid @RequestBody UpdateFoodListRequestDto request) {
DietBasicDto dto = dietService.overrideDietNutrition(dietId, request);

return ResultResponse.of(ResultCode.DIET_OVERRIDE_SUCCESS, dto);
}

@Operation(summary = "월별 식단 요약 조회", description = "월별 식단 작성 여부와 스티커 남김 여부를 조회합니다.")
@GetMapping("/daily/calendar")
public ResponseEntity<?> getCalendarOverview(@Parameter(description = "조회할 월 (yyyy-MM)", example = "2025-04")
public ResultResponse<MonthlyStickerResponseDto> getCalendarOverview(@Parameter(description = "조회할 월 (yyyy-MM)", example = "2025-04")
@RequestParam("month") String month) {
ResultResponse<?> resultResponse = dietService.getMonthlyStickersByParent(month);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
MonthlyStickerResponseDto dto = dietService.getMonthlyStickersByParent(month);
return dto.monthlyStickers().isEmpty()
? ResultResponse.of(ResultCode.DIET_DATE_EMPTY, dto)
: ResultResponse.of(ResultCode.CALENDAR_OVERVIEW_SUCCESS, dto);
}

@Operation(summary = "날짜별 식단 조회", description = "선택한 날짜의 식단을 조회합니다.")
@GetMapping("/daily")
public ResponseEntity<?> getDailyDietByDate(@Parameter(description = "조회할 날짜", example = "2025-04-10")
public ResultResponse<DailyDietResponseDto> getDailyDietByDate(@Parameter(description = "조회할 날짜", example = "2025-04-10")
@RequestParam("date") String date) {
ResultResponse<?> resultResponse = dietService.getDailyDietByDate(date);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
DailyDietResponseDto dto = dietService.getDailyDietByDate(date);
return ResultResponse.of(ResultCode.DAILY_DIET_RETRIEVAL_SUCCESS, dto);
}

@Operation(summary = "스티커 업데이트", description = "식단에 스티커를 추가합니다.")
@PostMapping("/daily/{dailyDietId}/sticker")
public ResponseEntity<?> markSticker(@PathVariable Long dailyDietId,
@RequestParam Sticker sticker) {
ResultResponse<?> resultResponse = dietService.markSticker(dailyDietId, sticker);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
}

@Operation(summary = "아이 식단 사진 삭제", description = "아이가 등록한 식단 사진을 삭제합니다.")
@DeleteMapping("/food/{dietId}/photo")
public ResponseEntity<?> deleteDietPhoto(@PathVariable Long dietId) {
ResultResponse<?> resultResponse = dietService.deletePhoto(dietId);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
public ResultResponse<DailyDietResponseDto> markSticker(@PathVariable Long dailyDietId,
@RequestParam Sticker sticker) {
DailyDietResponseDto dto = dietService.markSticker(dailyDietId, sticker);
return ResultResponse.of(ResultCode.STICKER_MARK_SUCCESS, dto);
}

@Operation(summary = "스티커 수정", description = "기존 스티커를 수정합니다.")
@PatchMapping("/daily/{dailyDietId}/sticker")
public ResponseEntity<?> updateSticker(@PathVariable Long dailyDietId,
public ResultResponse<DailyDietResponseDto> updateSticker(@PathVariable Long dailyDietId,
@RequestParam Sticker sticker) {
ResultResponse<?> result = dietService.updateSticker(dailyDietId, sticker);
return ResponseEntity.status(HttpStatus.OK).body(result);
DailyDietResponseDto dto = dietService.updateSticker(dailyDietId, sticker);
return ResultResponse.of(ResultCode.STICKER_UPDATE_SUCCESS, dto);
}

@Operation(summary = "스티커 삭제", description = "등록된 스티커를 삭제합니다.")
@DeleteMapping("/daily/{dailyDietId}/sticker")
public ResponseEntity<?> deleteSticker(@PathVariable Long dailyDietId) {
ResultResponse<?> resultResponse = dietService.deleteSticker(dailyDietId);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
public ResultResponse<DailyDietResponseDto> deleteSticker(@PathVariable Long dailyDietId) {
DailyDietResponseDto dto = dietService.deleteSticker(dailyDietId);
return ResultResponse.of(ResultCode.STICKER_DELETE_SUCCESS, dto);
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,68 @@
package com.project.growfit.domain.Diet.controller;

import com.project.growfit.domain.Diet.dto.request.SaveDietSetRequestDto;
import com.project.growfit.domain.Diet.dto.response.DietSetBasicDto;
import com.project.growfit.domain.Diet.dto.response.DietSetDetailResponseDto;
import com.project.growfit.domain.Diet.dto.response.DietSetResponseDto;
import com.project.growfit.domain.Diet.service.DietSetService;
import com.project.growfit.global.response.ResultCode;
import com.project.growfit.global.response.ResultResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/api/diet/set")
@Tag(name = "DietSet API", description = "식단 세트 API")
@Tag(name = "식단세트 관련 API", description = "식단세트 관련 API입니다.")
@RequiredArgsConstructor
public class DietSetController {

private final DietSetService dietSetService;

@Operation(summary = "식단 세트 저장", description = "사용자가 식단 세트를 저장합니다.")
@PostMapping
public ResponseEntity<?> saveDietSet(@Valid @RequestBody SaveDietSetRequestDto dto) {
ResultResponse<?> resultResponse = dietSetService.saveDietSet(dto);
public ResultResponse<DietSetBasicDto> saveDietSet(@Valid @RequestBody SaveDietSetRequestDto request) {
DietSetBasicDto dto = dietSetService.saveDietSet(request);

return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
}

@Operation(summary = "모든 식단 세트 조회", description = "사용자가 저장한 모든 식단 세트를 페이지 단위로 조회합니다.")
@GetMapping
public ResponseEntity<?> getDietSetList(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return ResponseEntity.ok(dietSetService.getAllDietSets(page, size));
}

@Operation(summary = "식단 세트 상세 조회", description = "사용자의 식단 세트의 상세 내용을 조회합니다.")
@GetMapping("{dietSetId}")
public ResponseEntity<?> getDietSetDetail(@PathVariable Long dietSetId) {
return ResponseEntity.ok(dietSetService.getDietSetDetail(dietSetId));
return ResultResponse.of(ResultCode.DIET_SET_SAVE_SUCCESS, dto);
}

@Operation(summary = "식단 세트 수정", description = "기존 식단 세트를 수정합니다.")
@PutMapping("/{dietSetId}")
public ResponseEntity<?> updateDietSet(
public ResultResponse<DietSetBasicDto> updateDietSet(
@PathVariable Long dietSetId,
@Valid @RequestBody SaveDietSetRequestDto dto) {
ResultResponse<?> resultResponse = dietSetService.updateDietSet(dietSetId, dto);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
@Valid @RequestBody SaveDietSetRequestDto request) {
DietSetBasicDto dto = dietSetService.updateDietSet(dietSetId, request);
return ResultResponse.of(ResultCode.DIET_SET_EDIT_SUCCESS, dto);
}

@Operation(summary = "식단 세트 삭제", description = "식단 세트를 삭제합니다.")
@DeleteMapping("/{dietSetId}")
public ResponseEntity<?> deleteDietSet(@PathVariable Long dietSetId) {
ResultResponse<?> resultResponse = dietSetService.deleteDietSet(dietSetId);
return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
public ResultResponse<DietSetBasicDto> deleteDietSet(@PathVariable Long dietSetId) {
DietSetBasicDto dto = dietSetService.deleteDietSet(dietSetId);
return ResultResponse.of(ResultCode.DIET_SET_DELETE_SUCCESS, dto);
}

@Operation(summary = "모든 식단 세트 조회", description = "사용자가 저장한 모든 식단 세트를 페이지 단위로 조회합니다.")
@GetMapping
public ResultResponse<Page<DietSetResponseDto>> getDietSetList(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Page<DietSetResponseDto> dto = dietSetService.getAllDietSets(page, size);

return ResultResponse.of(ResultCode.DIET_SET_LIST_SUCCESS, dto);
}

@Operation(summary = "식단 세트 상세 조회", description = "사용자의 식단 세트의 상세 내용을 조회합니다.")
@GetMapping("{dietSetId}")
public ResultResponse<DietSetDetailResponseDto> getDietSetDetail(@PathVariable Long dietSetId) {
DietSetDetailResponseDto dto = dietSetService.getDietSetDetail(dietSetId);

return ResultResponse.of(ResultCode.DIET_SET_RETRIEVAL_SUCCESS, dto);
}
}
Loading