Conversation
📝 WalkthroughWalkthroughAdds full menu management (CRUD + sold-out), image pre-upload and temp→perm S3 moves, Menu soft-delete, controllers, DTOs, converters, services (command/query), repository query additions, S3Service validations/move, validation handlers, and new success/error status codes. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant Controller as MenuController
participant CommandSvc as MenuCommandService
participant StoreRepo as StoreRepository
participant S3 as S3Service
participant MenuRepo as MenuRepository
Client->>Controller: POST /api/v1/stores/{id}/menus/images (file)
Controller->>CommandSvc: uploadImage(storeId, file)
CommandSvc->>StoreRepo: find/verify store
StoreRepo-->>CommandSvc: store
CommandSvc->>S3: upload to temp/
S3-->>CommandSvc: tempKey + url
CommandSvc-->>Controller: ImageUploadDto
Client->>Controller: POST /api/v1/stores/{id}/menus (create DTO with tempKey)
Controller->>CommandSvc: createMenus(storeId, dto)
CommandSvc->>S3: moveObject(tempKey, permKey)
S3-->>CommandSvc: copy & delete result
CommandSvc->>MenuRepo: save(menu entities)
MenuRepo-->>CommandSvc: persisted menus
CommandSvc-->>Controller: MenuCreateDto
Controller-->>Client: 201 Created
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/com/eatsfine/eatsfine/domain/image/status/ImageErrorStatus.java (1)
25-42: Bug:isSuccessshould befalsefor error statuses.Both
getReason()andgetReasonHttpStatus()setisSuccess(true), but this is an error status enum. Error responses should indicate failure. Compare withMenuErrorStatusandStoreErrorStatuswhich correctly useisSuccess(false).🐛 Proposed fix
`@Override` public ErrorReasonDto getReason() { return ErrorReasonDto.builder() - .isSuccess(true) + .isSuccess(false) .message(message) .code(code) .build(); } `@Override` public ErrorReasonDto getReasonHttpStatus() { return ErrorReasonDto.builder() .httpStatus(httpStatus) - .isSuccess(true) + .isSuccess(false) .code(code) .message(message) .build(); }
🤖 Fix all issues with AI agents
In `@src/main/java/com/eatsfine/eatsfine/domain/menu/entity/Menu.java`:
- Around line 20-21: Replace the deprecated Hibernate annotation `@Where` on the
Menu entity with `@SQLRestriction`: locate the Menu class where `@SQLDelete`(sql =
"UPDATE menu SET deleted_at = NOW() WHERE id = ?") and `@Where`(clause =
"deleted_at IS NULL") are applied, remove `@Where` and add `@SQLRestriction`(clause
= "deleted_at IS NULL") so the soft-delete filter uses the Hibernate 6.3+
compatible annotation while keeping the existing `@SQLDelete` behavior and the
deleted_at column semantics.
In
`@src/main/java/com/eatsfine/eatsfine/domain/menu/service/MenuCommandServiceImpl.java`:
- Around line 197-212: The deleteImage method allows deletion of orphan images
because it only verifies ownership when a Menu is found; to fix, call the
existing store ownership check (e.g., findAndVerifyStore(storeId)) at the start
of deleteImage so the caller is authorized before any deletion, keep the later
verifyMenuBelongsToStore(menu, storeId) for linked images, and only after
successful store verification proceed to menuRepository.findByImageKey(...) and
s3Service.deleteByKey(imageKey); update any imports/usage so findAndVerifyStore
is used consistently in MenuCommandServiceImpl.deleteImage.
In
`@src/main/java/com/eatsfine/eatsfine/domain/menu/service/MenuQueryServiceImpl.java`:
- Around line 25-39: The stream currently maps all menus returned by
storeRepository.findByIdWithMenus into MenuResDto.MenuDetailDto, which can
include soft-deleted menus because the repository's left join fetch may bypass
the `@Where` filter; either update the repository query used by findByIdWithMenus
to explicitly exclude deleted menus (e.g., "select s from Store s left join
fetch s.menus m where s.id = :id and m.deletedAt IS NULL") or filter them out in
MenuQueryServiceImpl by changing the mapping stream to filter(menu ->
menu.getDeletedAt() == null) (or the equivalent isDeleted/isDeletedAt check)
before mapping to MenuResDto.MenuDetailDto so
s3Service.toUrl(menu.getImageKey()) and other fields are only called for
non-deleted menus.
In
`@src/main/java/com/eatsfine/eatsfine/domain/menu/status/MenuSuccessStatus.java`:
- Around line 19-23: The enum constants in MenuSuccessStatus
(_MENU_CREATE_SUCCESS, _MENU_DELETE_SUCCESS, _MENU_UPDATE_SUCCESS,
_SOLD_OUT_UPDATE_SUCCESS, _MENU_LIST_SUCCESS) use inconsistent code strings
(e.g., "MENU2002", "MENU2003", "MENU2004", "MENU2005")—remove the extra digit so
all codes follow the same three-character pattern (e.g., "MENU200", "MENU201",
"MENU202", "MENU203", "MENU204") and update each constant's code string
accordingly to restore consistent formatting across the enum.
In
`@src/main/java/com/eatsfine/eatsfine/domain/store/repository/StoreRepository.java`:
- Around line 13-17: The JPQL in findByIdWithMenus currently fetches s.menus
with a JOIN FETCH but doesn't filter out soft-deleted Menu rows, so modify the
query used by the findByIdWithMenus method on Store to alias the menus (e.g.,
"left join fetch s.menus m") and add an explicit predicate "m.deletedAt IS NULL"
alongside "s.id = :id" so only non-deleted Menu entities are returned when
loading Store.menus.
🧹 Nitpick comments (12)
src/main/java/com/eatsfine/eatsfine/global/apiPayload/handler/GeneralExceptionAdvice.java (2)
39-49: Consider defensive check and aggregating all validation errors.Line 47 uses
.get(0)which assumes the error list is non-empty. While this is expected behavior whenMethodArgumentNotValidExceptionis thrown, a defensive check would be safer. Additionally, returning only the first error message forces users to fix issues one-by-one instead of seeing all problems at once.♻️ Optional: defensive check + aggregate all messages
protected ResponseEntity<Object> handleMethodArgumentNotValid( MethodArgumentNotValidException e, HttpHeaders headers, HttpStatusCode status, WebRequest request ) { - String errorMessage = e.getBindingResult().getAllErrors().get(0).getDefaultMessage(); + String errorMessage = e.getBindingResult().getAllErrors().stream() + .map(error -> error.getDefaultMessage()) + .filter(msg -> msg != null) + .findFirst() + .orElse("Validation failed"); return handleExceptionInternalFalse(e, ErrorStatus._BAD_REQUEST, headers, status, request, errorMessage); }Or to show all errors:
- String errorMessage = e.getBindingResult().getAllErrors().get(0).getDefaultMessage(); + String errorMessage = e.getBindingResult().getAllErrors().stream() + .map(error -> error.getDefaultMessage()) + .filter(msg -> msg != null) + .collect(Collectors.joining(", "));
51-57: Same defensive coding suggestion for ConstraintViolationException.Line 55 uses
.iterator().next()which will throwNoSuchElementExceptionif violations set is empty. While this shouldn't occur in practice, consider adding a guard.♻️ Optional: defensive check
`@ExceptionHandler` public ResponseEntity<Object> validation(ConstraintViolationException e, WebRequest request) { - // 첫 번째 에러 메시지만 추출 - String errorMessage = e.getConstraintViolations().iterator().next().getMessage(); + String errorMessage = e.getConstraintViolations().stream() + .map(v -> v.getMessage()) + .findFirst() + .orElse("Constraint violation"); return handleExceptionInternalFalse(e, ErrorStatus._BAD_REQUEST, HttpHeaders.EMPTY, ErrorStatus._BAD_REQUEST.getHttpStatus(), request, errorMessage); }src/main/java/com/eatsfine/eatsfine/domain/image/status/ImageErrorStatus.java (1)
12-17: Inconsistent naming convention for enum constants.The older constants (
EMPTY_FILE,INVALID_FILE_TYPE,S3_UPLOAD_FAILED) lack the underscore prefix, while the new ones use it (_IMAGE_NOT_FOUND,_INVALID_IMAGE_KEY). Consider aligning all constants to use the same convention for consistency with other error status enums in the codebase.src/main/java/com/eatsfine/eatsfine/domain/menu/entity/Menu.java (1)
52-52: Consider adding explicit@Columnannotation for consistency.Other fields in this entity explicitly define their column names. Adding
@Column(name = "deleted_at")would ensure consistency and make the mapping explicit, matching the column name referenced in@SQLDelete.♻️ Suggested change
- private LocalDateTime deletedAt; + `@Column`(name = "deleted_at") + private LocalDateTime deletedAt;src/main/java/com/eatsfine/eatsfine/domain/menu/converter/MenuConverter.java (1)
8-9: Consider making this a proper utility class.Utility classes with only static methods should typically be
finalwith a private constructor to prevent instantiation.♻️ Proposed refactor
-public class MenuConverter { +public final class MenuConverter { + + private MenuConverter() { + // Utility class - prevent instantiation + }src/main/java/com/eatsfine/eatsfine/global/s3/S3Service.java (1)
60-75: Consider wrapping S3 exceptions for consistency.The
copyObjectcall can throwS3Exceptionwhich will propagate as-is, while other S3 operations in this class wrap exceptions inImageException. Also, ifcopyObjectsucceeds butdeleteByKeyfails, a duplicate object will remain (orphan in destination). This is acceptable since it's a safe failure mode (no data loss), but worth documenting.♻️ Optional: wrap S3Exception for consistent error handling
public void moveObject(String sourceKey, String destinationKey) { if (sourceKey == null || destinationKey == null || sourceKey.isBlank() || destinationKey.isBlank()) { throw new ImageException(ImageErrorStatus._INVALID_IMAGE_KEY); } - // 1. 객체 복사 - s3Client.copyObject(CopyObjectRequest.builder() - .sourceBucket(bucket) - .sourceKey(sourceKey) - .destinationBucket(bucket) - .destinationKey(destinationKey) - .build()); + try { + // 1. 객체 복사 + s3Client.copyObject(CopyObjectRequest.builder() + .sourceBucket(bucket) + .sourceKey(sourceKey) + .destinationBucket(bucket) + .destinationKey(destinationKey) + .build()); + } catch (Exception e) { + throw new ImageException(ImageErrorStatus.S3_UPLOAD_FAILED); // or a new S3_COPY_FAILED status + } // 2. 원본(임시) 객체 삭제 deleteByKey(sourceKey); }src/main/java/com/eatsfine/eatsfine/domain/menu/dto/MenuReqDto.java (2)
44-53:@Size(min=1)allows whitespace-only names.For
MenuUpdateDto.name, using only@Size(min = 1)permits strings like" "(whitespace only). If an empty or whitespace-only name should be rejected when provided, consider adding@NotBlankor using a custom validator that only applies when the value is non-null.♻️ Option: Use Pattern to reject blank strings when provided
public record MenuUpdateDto( - `@Size`(min = 1, message = "메뉴 이름은 1글자 이상이어야 합니다.") + `@Size`(min = 1, message = "메뉴 이름은 1글자 이상이어야 합니다.") + `@Pattern`(regexp = "^(?!\\s*$).+", message = "메뉴 이름은 공백만으로 구성될 수 없습니다.") String name,Note: With this approach,
nullis still allowed (for partial updates where name is not being changed), but non-null values must contain at least one non-whitespace character.
38-42: Consider validating list elements are not null.
List<Long> menuIdscould contain null elements. If this is a concern, you can add@NotNullto the element type.♻️ Proposed change
public record MenuDeleteDto( `@NotNull` `@Size`(min = 1, message = "삭제할 메뉴를 최소 1개 이상 선택해주세요.") - List<Long> menuIds + List<@NotNull Long> menuIds ){}src/main/java/com/eatsfine/eatsfine/domain/menu/service/MenuQueryServiceImpl.java (1)
4-4: Remove unused import.
StoreResDtois imported but never used in this file.🧹 Proposed fix
-import com.eatsfine.eatsfine.domain.store.dto.StoreResDto;src/main/java/com/eatsfine/eatsfine/domain/menu/controller/MenuController.java (1)
61-67: Consider using POST for bulk delete instead of DELETE with request body.Using
@RequestBodywith HTTP DELETE is technically allowed but can cause issues:
- Some HTTP clients, proxies, and API gateways may strip the body from DELETE requests
- It's not idiomatic REST design for bulk operations
A common alternative is
POST /stores/{storeId}/menus/deleteorPOST /stores/{storeId}/menus:batchDeletefor bulk delete operations.src/main/java/com/eatsfine/eatsfine/domain/menu/service/MenuCommandServiceImpl.java (2)
130-155: Redundant null check insideifPresent.Line 137 checks
if (newImageKey != null && !newImageKey.isBlank()), but since this is insideifPresent,newImageKeyis guaranteed to be non-null. Only the blank check is necessary.🧹 Proposed simplification
Optional.ofNullable(dto.imageKey()).ifPresent(newImageKey -> { // 1. [Safety] 변경된 내용이 없으면 스킵 (프론트에서 기존 키를 그대로 보낸 경우) if (newImageKey.equals(menu.getImageKey())) { return; } // 2. 새로운 이미지가 있다면 영구 경로로 이동 (Temp -> Perm) - if (newImageKey != null && !newImageKey.isBlank()) { + if (!newImageKey.isBlank()) {
114-116: Unusedstorevariable.The
storevariable is assigned on line 115 but never used inupdateMenu. If store verification is the only purpose, consider callingfindAndVerifyStore(storeId)without assigning the result, or suppress the warning if intentional.🧹 Proposed fix
- Store store = findAndVerifyStore(storeId); + findAndVerifyStore(storeId);The same pattern applies to
updateSoldOutStatus(line 164) anduploadImage(line 184).
src/main/java/com/eatsfine/eatsfine/domain/menu/entity/Menu.java
Outdated
Show resolved
Hide resolved
src/main/java/com/eatsfine/eatsfine/domain/menu/service/MenuCommandServiceImpl.java
Show resolved
Hide resolved
src/main/java/com/eatsfine/eatsfine/domain/menu/service/MenuQueryServiceImpl.java
Show resolved
Hide resolved
| _MENU_CREATE_SUCCESS(HttpStatus.CREATED, "MENU202", "메뉴 생성 성공"), | ||
| _MENU_DELETE_SUCCESS(HttpStatus.OK, "MENU2002", "메뉴 삭제 성공"), | ||
| _MENU_UPDATE_SUCCESS(HttpStatus.OK, "MENU2003", "메뉴 수정 성공"), | ||
| _SOLD_OUT_UPDATE_SUCCESS(HttpStatus.OK, "MENU2005", "품절 여부 변경 성공"), | ||
| _MENU_LIST_SUCCESS(HttpStatus.OK, "MENU2004", "메뉴 조회 성공"), |
There was a problem hiding this comment.
Inconsistent status code format.
The codes MENU2002, MENU2003, MENU2004, MENU2005 appear to have an extra digit compared to the earlier codes (MENU200, MENU201, MENU202). This looks like a typo and could cause confusion in API documentation or client-side error handling.
🔧 Proposed fix for consistency
- _MENU_DELETE_SUCCESS(HttpStatus.OK, "MENU2002", "메뉴 삭제 성공"),
- _MENU_UPDATE_SUCCESS(HttpStatus.OK, "MENU2003", "메뉴 수정 성공"),
- _SOLD_OUT_UPDATE_SUCCESS(HttpStatus.OK, "MENU2005", "품절 여부 변경 성공"),
- _MENU_LIST_SUCCESS(HttpStatus.OK, "MENU2004", "메뉴 조회 성공"),
+ _MENU_DELETE_SUCCESS(HttpStatus.OK, "MENU203", "메뉴 삭제 성공"),
+ _MENU_UPDATE_SUCCESS(HttpStatus.OK, "MENU204", "메뉴 수정 성공"),
+ _SOLD_OUT_UPDATE_SUCCESS(HttpStatus.OK, "MENU206", "품절 여부 변경 성공"),
+ _MENU_LIST_SUCCESS(HttpStatus.OK, "MENU205", "메뉴 조회 성공"),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| _MENU_CREATE_SUCCESS(HttpStatus.CREATED, "MENU202", "메뉴 생성 성공"), | |
| _MENU_DELETE_SUCCESS(HttpStatus.OK, "MENU2002", "메뉴 삭제 성공"), | |
| _MENU_UPDATE_SUCCESS(HttpStatus.OK, "MENU2003", "메뉴 수정 성공"), | |
| _SOLD_OUT_UPDATE_SUCCESS(HttpStatus.OK, "MENU2005", "품절 여부 변경 성공"), | |
| _MENU_LIST_SUCCESS(HttpStatus.OK, "MENU2004", "메뉴 조회 성공"), | |
| _MENU_CREATE_SUCCESS(HttpStatus.CREATED, "MENU202", "메뉴 생성 성공"), | |
| _MENU_DELETE_SUCCESS(HttpStatus.OK, "MENU203", "메뉴 삭제 성공"), | |
| _MENU_UPDATE_SUCCESS(HttpStatus.OK, "MENU204", "메뉴 수정 성공"), | |
| _SOLD_OUT_UPDATE_SUCCESS(HttpStatus.OK, "MENU206", "품절 여부 변경 성공"), | |
| _MENU_LIST_SUCCESS(HttpStatus.OK, "MENU205", "메뉴 조회 성공"), |
🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/menu/status/MenuSuccessStatus.java`
around lines 19 - 23, The enum constants in MenuSuccessStatus
(_MENU_CREATE_SUCCESS, _MENU_DELETE_SUCCESS, _MENU_UPDATE_SUCCESS,
_SOLD_OUT_UPDATE_SUCCESS, _MENU_LIST_SUCCESS) use inconsistent code strings
(e.g., "MENU2002", "MENU2003", "MENU2004", "MENU2005")—remove the extra digit so
all codes follow the same three-character pattern (e.g., "MENU200", "MENU201",
"MENU202", "MENU203", "MENU204") and update each constant's code string
accordingly to restore consistent formatting across the enum.
src/main/java/com/eatsfine/eatsfine/domain/store/repository/StoreRepository.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/menu/controller/MenuController.java`:
- Around line 73-79: The controller route for menu image deletion in
MenuController currently uses
`@DeleteMapping`("/stores/{storeId}/menus/{menuId}/image") in the deleteMenuImage
method, but the PR and API spec expect DELETE /stores/{storeId}/menus/images;
update the controller route to match the agreed contract (or update the API
spec) — either change the mapping string on deleteMenuImage to
"/stores/{storeId}/menus/images" and adjust method parameters (remove or retain
`@PathVariable` Long menuId as per the contract) and any service method signature
calls (menuCommandService.deleteMenuImage) accordingly, and then update related
API docs/tests to reflect the chosen path.
In
`@src/main/java/com/eatsfine/eatsfine/domain/menu/service/MenuCommandServiceImpl.java`:
- Around line 39-60: The code in MenuCommandServiceImpl allows any non-blank
imageKey to be moved via s3Service.moveObject, enabling arbitrary object
moves/deletes; before calling s3Service.moveObject and menu.updateImageKey,
validate that tempImageKey begins with a strict, expected temp prefix (for
example "temp/menus/" or whatever convention your upload flow uses), otherwise
skip the move or throw a validation exception; apply the same validation in the
other occurrence (lines ~130-154) so only keys with the approved temp path are
accepted and moved to the permanent key.
| @Operation(summary = "등록된 메뉴 이미지 삭제 API", description = "이미 등록된 메뉴의 이미지를 삭제합니다.") | ||
| @DeleteMapping("/stores/{storeId}/menus/{menuId}/image") | ||
| public ApiResponse<MenuResDto.ImageDeleteDto> deleteMenuImage( | ||
| @PathVariable Long storeId, | ||
| @PathVariable Long menuId | ||
| ) { | ||
| return ApiResponse.of(MenuSuccessStatus._MENU_IMAGE_DELETE_SUCCESS, menuCommandService.deleteMenuImage(storeId, menuId)); |
There was a problem hiding this comment.
API path mismatch for menu image deletion.
PR objectives and summary list DELETE /stores/{storeId}/menus/images, but the controller exposes /stores/{storeId}/menus/{menuId}/image. Please align the route (or update the API contract/docs) to avoid client breakage.
🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/menu/controller/MenuController.java`
around lines 73 - 79, The controller route for menu image deletion in
MenuController currently uses
`@DeleteMapping`("/stores/{storeId}/menus/{menuId}/image") in the deleteMenuImage
method, but the PR and API spec expect DELETE /stores/{storeId}/menus/images;
update the controller route to match the agreed contract (or update the API
spec) — either change the mapping string on deleteMenuImage to
"/stores/{storeId}/menus/images" and adjust method parameters (remove or retain
`@PathVariable` Long menuId as per the contract) and any service method signature
calls (menuCommandService.deleteMenuImage) accordingly, and then update related
API docs/tests to reflect the chosen path.
| List<Menu> menus = dto.menus().stream() | ||
| .map(menuDto -> { | ||
| Menu menu = Menu.builder() | ||
| .name(menuDto.name()) | ||
| .description(menuDto.description()) | ||
| .price(menuDto.price()) | ||
| .menuCategory(menuDto.category()) | ||
| .build(); | ||
|
|
||
| // 임시 이미지 키가 있는 경우, 영구 경로로 이동하고 키를 설정 | ||
| String tempImageKey = menuDto.imageKey(); | ||
| if (tempImageKey != null && !tempImageKey.isBlank()) { | ||
| // 1. 새로운 영구 키 생성 | ||
| String extension = s3Service.extractExtension(tempImageKey); | ||
| String permanentImageKey = "stores/" + storeId + "/menus/" + UUID.randomUUID() + extension; | ||
|
|
||
| // 2. S3에서 객체 이동 (임시 -> 영구) | ||
| s3Service.moveObject(tempImageKey, permanentImageKey); | ||
|
|
||
| // 3. 엔티티에 영구 키 저장 | ||
| menu.updateImageKey(permanentImageKey); | ||
| } |
There was a problem hiding this comment.
Block arbitrary S3 object move/delete by validating temp image keys.
Right now any non-blank imageKey can be moved/deleted. If a client supplies a permanent key (or another object key) it can delete unrelated objects. Enforce a strict temp-path prefix (e.g., temp/menus/) before moving.
🔒 Suggested fix (validate temp keys before move)
String tempImageKey = menuDto.imageKey();
if (tempImageKey != null && !tempImageKey.isBlank()) {
+ validateTempImageKey(tempImageKey);
// 1. 새로운 영구 키 생성
String extension = s3Service.extractExtension(tempImageKey);
String permanentImageKey = "stores/" + storeId + "/menus/" + UUID.randomUUID() + extension;
@@
- if (newImageKey != null && !newImageKey.isBlank()) {
+ if (!newImageKey.isBlank()) {
+ validateTempImageKey(newImageKey);
String extension = s3Service.extractExtension(newImageKey);
String permanentImageKey = "stores/" + storeId + "/menus/" + UUID.randomUUID() + extension;
s3Service.moveObject(newImageKey, permanentImageKey);
@@
+ private void validateTempImageKey(String key) {
+ if (key == null || key.isBlank() || !key.startsWith("temp/menus/")) {
+ throw new ImageException(ImageErrorStatus._INVALID_IMAGE_KEY);
+ }
+ }Also applies to: 130-154
🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/menu/service/MenuCommandServiceImpl.java`
around lines 39 - 60, The code in MenuCommandServiceImpl allows any non-blank
imageKey to be moved via s3Service.moveObject, enabling arbitrary object
moves/deletes; before calling s3Service.moveObject and menu.updateImageKey,
validate that tempImageKey begins with a strict, expected temp prefix (for
example "temp/menus/" or whatever convention your upload flow uses), otherwise
skip the move or throw a validation exception; apply the same validation in the
other occurrence (lines ~130-154) so only keys with the approved temp path are
accepted and moved to the permanent key.
307149f to
8205906
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/menu/service/MenuCommandServiceImpl.java`:
- Around line 55-59: The S3 operations (s3Service.moveObject and
s3Service.deleteByKey) are executed immediately in createMenus, updateMenu, and
deleteMenus and must be deferred until the surrounding DB transaction
successfully commits; change each place where you currently call
s3Service.moveObject(...) or s3Service.deleteByKey(...) to instead register a
post-commit synchronization via
TransactionSynchronizationManager.registerSynchronization(...) that performs the
S3 move/delete after commit, while keeping the in-transaction updates such as
menu.updateImageKey(...), saveAll(...), updateMenu DB changes, and
deleteAll(...) intact so DB state is persisted before the actual S3 side-effects
run. Ensure the registered callback captures the needed keys (tempImageKey,
permanentImageKey) and handles exceptions/logging so failures in the post-commit
step do not affect the committed transaction.
In `@src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java`:
- Around line 93-97: The removeMenu() logic nulls the non-nullable FK by calling
menu.assignStore(null) which will violate constraints because the `@OneToMany`
List<Menu> menus lacks orphanRemoval=true; either add orphanRemoval = true to
the menus association on the Store entity (i.e., the field annotated
`@OneToMany`(mappedBy = "store", cascade = CascadeType.ALL) named menus) to allow
removing/menu soft-delete via JPA, or remove the menu.assignStore(null) call
from the removeMenu()/removeMenus() methods and instead delete via the Menu
repository so the `@SQLDelete` soft-delete executes; update the
Store.removeMenu(s) implementations accordingly to match the pattern used by
businessHours and tableImages.
In
`@src/main/java/com/eatsfine/eatsfine/domain/store/repository/StoreRepository.java`:
- Around line 13-18: The JPQL in StoreRepository's `@Query` using "left join fetch
s.menus m" combined with "where m.deletedAt IS NULL" turns the LEFT JOIN into an
INNER JOIN and drops stores with no menus; update the predicate in the Query so
it preserves left-join semantics by allowing null joined rows — e.g. change the
WHERE to include either m.deletedAt IS NULL OR m.id IS NULL (or equivalent null
check) for s.menus/m.deletedAt, or alternatively remove the fetch join and load
menus separately or use a Hibernate WITH clause if you prefer vendor-specific
syntax.
In `@src/main/java/com/eatsfine/eatsfine/global/s3/S3Service.java`:
- Around line 60-75: The moveObject method currently allows sourceKey ==
destinationKey which causes a no-op copy followed by deleteByKey(sourceKey); add
an explicit guard at the start of moveObject to detect equals (use String.equals
or Objects.equals) and throw an ImageException (e.g.,
ImageErrorStatus._INVALID_IMAGE_KEY or a new specific status) when sourceKey
equals destinationKey; this check should be placed alongside the existing
null/blank validation before calling s3Client.copyObject and deleteByKey to
prevent accidental deletion.
src/main/java/com/eatsfine/eatsfine/domain/menu/service/MenuCommandServiceImpl.java
Show resolved
Hide resolved
src/main/java/com/eatsfine/eatsfine/domain/store/repository/StoreRepository.java
Show resolved
Hide resolved
* Initial commit * [CHORE]: CI 스크립트 작성 * [CHORE]: CI 배포 프로필 구성 * [FEAT]: HealthCheck API 구현 및 테스트 추가 * [FEAT]: Docker 빌드 환경 구성 * [FEAT]: CD 배포 파이프라인 구성 * [FIX]: 도커 이미지 풀링 및 컨테이너 실행 부분 수정 * [FIX]: 헬스체크 실패 방지를 위한 앱 기동 대기 추가 * [FIX]: invalid duration 문자열 추가 * [FEAT]: 의존성 추가 * [FIX]: 대문자를 소문자로 변경 * [FIX]: 경로 대소문자 에러 수정 * [FIX]: CI 파이프라인 수정 * [FIX]: CI 파이프라인 원래대로 다시 수정 * [FIX]: 스크립트 수정 * [FIX]: 도커파일 수정 * [FIX]: 도커파일 수정 * [chore] 빌드 및 패키지 설정 변경 * [chore] 빌드 및 패키지 설정 변경 * [chore] 빌드 및 패키지 설정 변경 * [chore] 빌드 및 패키지 설정 변경 * [FEAT]: 로컬 개발 환경 구성을 위한 Docker Compose 및 설정 추가 * [FEAT]: BaseEntity 추가 * [FEAT]: 예약 도메인 엔티티(Booking) 기본 설정 * [FIX]: Entity에 식별자 추가 * [FIX]: 불필요한 import문 제거 * [FEAT]: BusinessHours 요일 enum 정의 * [FEAT]: BusinessHours 엔티티 초기 세팅 * [FEAT]: Region 엔티티 설계 * [FEAT]: TableImage 엔티티 초기 세팅 * [REFACTOR]: BusinessHours isClosed 필드를 isHoliday로 변경 * [REFACTOR]: BusinessHours Store 연관관계 적용 및 BaseEntity 상속 * [FEAT]: 가게 승인 상태 enum 추가 * [FEAT]: 가게 카테고리 enum 추가 * [FEAT]: Store 엔티티 설계 * [REFACTOR]: TableImage Store 연관관계 적용 및 BaseEntity 상속 * [FEAT]: StoreRepository 세팅 * [FEAT]: TableImageRepository 세팅 * [FEAT]: BusinessHoursRepository 세팅 * [FEAT]: RegionRepository 세팅 * [CHORE]: yml 파일 수정 * [REFACTOR]: Store와 BusinessHours 양방향 연관관계 및 편의 메서드 추가 * [FIX]: StoreTable 연관관계 주석처리 * [REFACTOR]: Store와 TableImage 양방향 연관관계 및 편의 메서드 추가 * [REFACTOR]: Store-TableImage 양방향 연관관계 삭제 편의 메서드 추가 및 생명주기 관리 설정 * [REFACTOR]: ApiResponse 실패 응답을 BaseErrorCode 기반으로 통합 * [FEAT]: HTTPS 적용 및 NGINX Blue-Green 무중단 배포 전환 로직 수정 * [FEAT]: HTTPS 적용 및 NGINX Blue-Green 무중단 배포 전환 로직 수정 * [FEAT] 예약 가능 시간대 및 테이블 조회 DTO 개발 * [FEAT] 예약 가능 시간대,테이블 조회 컨트롤러 개발 및 스웨거 설정 * [FEAT] BookingTable 엔티티 개발 * [FIX] Booking 엔티티 수정 (BookingTable 연관관계 고려) * [FEAT] BookingRepository- findReservedTableIds 메서드 구현 * [FIX] Store 엔티티 businessHours,tableImages 필드에 @Builder.Default 애노테이션 추가 * [FEAT] StoreTable 엔티티 기본 설정 * [FEAT] 예약 가능 시간 조회 시 '나눠 앉기' 동의 여부 로직 추가 * [FEAT] 테이블 유형 enum 추가 * [FEAT] TableLayout 엔티티 기본 설정 및 Store, StoreTable 연관관계 수정 * [FEAT] BusinessHours의 DayofWeek을 커스텀 enum이 아닌java.time 으로 변경 * [FIX] BusinessHours의 DayofWeek @Enumerated추가 * [FIX] rows 필드 db에서 오류 -> lows로 수정 * [FEAT] GET /api/v1/stores/{storeId}/bookings/available-times 구현 * [FEAT] GeneralException 구현 * [FIX] 로컬 개발 환경에서 https->http 사용 * [FEAT] User 엔티티 기본 설정 * [FEAT]: 식당 상세 조회 DTO 및 BusinessHours 변환 로직 추가 * [FEAT]: StoreSuccessStatus, StoreErrorStatus 추가 * [FEAT]: GeneralException(프로젝트 exception) 세팅 * [FEAT]: StoreException 세팅 * [FEAT]: 식당 상세 조회 API (Controller/Service) 구현 * [FEAT]: 전역 예외 핸들러(GeneralExceptionAdvice) 구현 * [REFACTOR]: BusinessHours 응답 DTO 및 컨버터 휴무 처리 단순화 * [FEAT] 예약 시간대 설정 후 테이블 조회 DTO 개발 * [FEAT] StoreTable에 Table의 위치 필드 추가 * [FEAT] 시간대 설정 후 예약 가능한 테이블 조회 서비스 구현 * [FEAT] 테이블 분리 허용 여부에 따라 테이블 조회 로직 수정 * [FEAT] GET /api/v1/stores/{storeId}/bookings/available-tables 구현 * [CHORE] Swagger 버전 변경 2.3.0 -> 2.8.1 * [FEAT] BookingException 및 ErrorStatus 추가 * [FEAT] 에러 핸들러 구현 * [FEAT] BookingController에 BookingException 포함 * 예전 테스트 코드 제거 * [REFACTOR] : BookingErrorStatus를 공통 ErrorStatus에서 Booking 도메인으로 분리 * [REFACTOR]: 전역 예외 핸들러 제거 * [REFACTOR]: BusinessHoursResDto.summary 응답 시간 포맷(HH:mm) 적용 * [REFACTOR]: BusinessHours 응답 DTO isClosed로 필드명 변경 * [FIX]: NGINX Blue-Green 자동 전환 배포 로직 수정 * [FIX]: NGINX Blue-Green 자동 전환 배포 로직 수정 (다시 복구) * Update database password in application-local.yml * [FEAT] : 예약 생성 DTO 개발 * [FIX]: NGINX Blue-Green 존재 여부 확인 후 stop 하도록 로직 수정 * [REFACTOR] : 예약 조회 DTO, 컨트롤러 구조 변경 * [FEAT] : BookingErrorStatus 추가 * [FEAT] : CreateBookingDTO에 isSplitAccepted 필드 추가 * [FEAT] : createdAt JPA 자동 설정 가능하게 애노테이션 추가 * [FEAT] : 비관적 락 적용해 테이블 조회하는 메서드 개발 * [FIX] : Post 방식 API ModelAttribute->RequestBody 변경 * [FIX] : boolingTables 필드에 @Builder.Default 애노테이션 추가 * [FEAT] : 예약 생성 비즈니스 로직 개발 * [FIX]: 배포 대상 판단 로직을 nginx upstream 기준으로 수정 * [FIX]: deploy.yml 로직 약간수정 * [FIX]: 기존으로 복구 및 로그 추가 * [FIX]: 기존으로 복구 * [FIX]: 배포 스크립트 Nginx 리로드 경로 * [FIX]: HTTPS 환경에서 깨지던 blue-green 배포 판단 로직 수정 * [FIX]: 로직 순서 수정 * [FIX]: 로직 순서 수정 * [FIX]: 코드 약간 수정 * [FIX]: 코드 약간 수정 * [FIX]: 코드 약간 수정 * [FIX]: 코드 약간 수정 * [FIX]: 코드 약간 수정 * [BUILD]: QueryDSL 설정 추가 (.gitignore, build.gradle) * [CHORE] : DB_PASSWORD 환경변수로 관리 * [FEAT] : UserRepository 임시 사용 * [FEAT] : BookingErrorStatus 추가 * [FEAT] : 결제 요청 관련 DTO 개발 * [FEAT] : StoreErrorStatus 추가 * [REFACTOR] : Swagger 테스트용 @ParameterObject 애노테이션 추가 * [REFACTOR] : 영업시간 정보 없을 시 예외 처리 로직 추가 * [FEAT] : Booking 엔티티에 depositAmount 필드 추가 및 confirm 메서드 추가 * [FIX] : 예약 상태가 PENDING 혹은 CONFIRMED인 테이블을 조회하도록 수정 * [FEAT] : 결제 완료 처리 API 개발 * [FIX] : JpaAuditing 설정 변경 * [FEAT] Swagger 운영 도메인 등록 * [FEAT]: 결제 요청 API 구현 및 Booking 연동 * [FEAT]: 가게 등록 및 Region 예외 추가 * [REFACTOR]: Store 엔티티 초기 구조 보정 * [FEAT]: StoreConverter 추가 * [FEAT]: Store/Region 성공 상태 코드 추가 * [FEAT]: 가게 등록 및 영업시간 요청 처리 * [FEAT]: 영업시간 검증 및 예외처리 * [REFACTOR]: 영업시간 및 이미지 필드 수정 * [FEAT]: 가게 검색 기본 구조 추가 * [FEAT]: QueryDSL 기반 가게 검색 구현 * [FEAT]: 실시간 영업 여부 체크 로직 구현 * [FEAT]: 가게 검색에 키워드 검색, 지역 필터 기능 추가 * [FEAT]: 가게별 예약금 로직 추가 * [FEAT]: 가게 테이블(StoreTable) 도메인 엔티티 설계 (#51) - StoreTable: Soft Delete 적용, 좌석 인원(min/max) 필드 세분화 - TableLayout: Soft Delete 적용, 배치도 활성/비활성 로직, Cascade 옵션 적용 - TableBlock: 예약 차단 시간 관리를 위한 엔티티 설계 - SlotStatus: 예약 시간대 상태 관리를 위한 Enum 및 DTO 구조 설계 - Common: DB 스키마 정합성을 위한 @column 제약조건 명시 * [FIX]: 간편결제만 놔두기 * [FEAT]: 토스페이먼츠 결제위젯 연동 및 승인 프로세스 구현 * [FEAT]: 독립적인 테스트 환경 구성을 위한 H2 DB 및 설정 추가해서 빌드 실패 해결 * [FIX]: username 삭제 * Feature/payment (#54) * [FIX]: 간편결제만 놔두기 * [FEAT]: 토스페이먼츠 결제위젯 연동 및 승인 프로세스 구현 * [FEAT]: 독립적인 테스트 환경 구성을 위한 H2 DB 및 설정 추가해서 빌드 실패 해결 * [FIX]: username 삭제 * [FEAT]: 식당 기본 정보, 영업시간 수정, 브레이크타임 설정 API 개발 (#55) * [FEAT]: 가게 기본 정보 수정 DTO 추가 * [FEAT]: StoreSuccessStatus에 가게 기본 정보 수정 성공 응답 추가 * [FEAT]: Store 엔티티에 가게 기본 정보 수정 메서드 추가 * [FEAT]: 가게 기본 정보 수정 로직 개발 * [FEAT]: StoreController에 가게 기본 정보 수정 API 추가 * [FEAT]: 영업시간 검증 메서드(생성용/수정용) 분리 * [FEAT]: 영업시간 수정 DTO 추가 * [FEAT]: 응답 컨버터 및 상태코드 추가 * [FEAT]: 영업 시간 수정 로직 개발 * [REFACTOR]: 영업시간 변경 응답 7일 모두 내려주도록 수정 및 필드명 변경 * [FEAT]: 브레이크타임 설정 DTO 및 응답코드 추가 * [FEAT]: 요청된 브레이크타임 유효성 검증 로직 개발 * [FEAT]: 브레이크타임 설정 로직 구현 * [FEAT]: 상세조회 응답에 브레이크타임 추가 * [FEAT]: 결제 승인 결과(성공/실패) 처리 * [REFACTOR]: Payment 예외 처리 리팩토링 * [FEAT]: 결제 취소(환불) API 구현 * [FEAT]: 결제 내역 조회 및 상세 조회 API 구현 * [FEAT]: 테이블 배치도 생성, 조회 API 구현 (#56) * [REFACTOR]: TableLayout SQLDelete 쿼리문 수정 및 Builder.Default 추가 * [FEAT]: TableLayoutException 세팅 * [FEAT]: TableLayout 성공/실패 응답코드 추가 * [FEAT]: Controller에 배치도 생성 API 추가 * [FEAT]: 배치도 DTO, Converter 추가 * [FEAT]: 테이블 배치도 생성 로직 개발 * [REFACTOR]: StoreTable SQLDelete 쿼리문 수정 * [REFACTOR]: TableLayout - StoreTable 연관관계 Cascade.REMOVE 추가 * [FEAT]: 테이블 배치도 조회 API 추가 * [FEAT]: 테이블 배치도 조회 로직 개발 * [REFACTOR]: 테이블 배치도 조회 응답 DTO, Converter에 reviewCount 필드 추가 * [FEAT]: 가게 이미지, 테이블 이미지(S3) 업로드/조회/삭제 기능 구현 (#61) * [REFACTOR]: url -> key로 변수명 변경 및 imageOrder 필드 추가 * [BUILD]: AWS S3 관련 의존성 추가 * [FEAT]: S3Config, S3Service 추가 * [FEAT]: 이미지 업로드 관련 예외 및 에러 상태코드 추가 * [FEAT]: 가게 메인 이미지 등록 응답 DTO 및 성공 상태코드 추가 * [FEAT]: 가게 메인 이미지 등록 로직 구현 * [FEAT]: 가게 매인 이미지 조회 응답 DTO 및 성공 상태코드 추가 * [FEAT]: 가게 메인 이미지 조회 로직 구현 * [FEAT]: 테이블 이미지 등록 응답 DTO 및 성공 상태코드 추가 * [FEAT]: 가게 테이블 이미지(TableImage) 등록 로직 구현 * [FEAT]: 가게 테이블 이미지(TableImage) 조회 로직 구현 * [REFACTOR]: 테이블 이미지 등록 및 조회 성공 코드 수정 * [FEAT]: 테이블 이미지 삭제 응답 DTO 및 성공 상태코드 추가 * [FEAT]: 가게 테이블 이미지(TableImage) 삭제 로직 구현 * [CHORE]: application.yml에 AWS S3 설정 추가 * [FIX]: StoreSuccessStatus 충돌 해결 * [FIX]: 에러 상태코드 중복 수정 * [CHORE]: application-test.yml에 aws s3 설정 추가 * [FIX]: S3 설정 위치 수정 * [REFACTOR]: AWS S3 설정값 환경변수 처리 * [FEAT]: 가게 테이블 생성 API 구현 (#62) * [FEAT]: StoreTableException 세팅 * [FEAT]: 테이블 생성 성공/실패 응답코드 추가 * [FEAT]: StoreTableController 가게 테이블 생성 API 및 명세 추가 * [FEAT]: 테이블 생성 DTO, Converter 추가 * [FEAT]: 테이블 생성 검증 로직 추가 * [FEAT]: 테이블 생성 검증 서비스 로직 추가 * [FIX]: 무중단 배포 구조 스크립트에서 단일 컨테이너 배포 구조 스크립트로 변경 * [FIX]: EC2 SSH 배포 방식 수정 및 GitHub Actions 안정화 * [FIX]: GitHub Actions SSH 포트 임시 오픈/회수 로직 정리 * [FIX]: 배포 워크플로우 main으로 변경 * [CHORE]: 불필요 도커 이미지 및 컨테이너 정리 * [FEAT]: 테이블 예약 시간대 목록 조회, 관리 API 구현 (#69) * [REFACTOR]: 테이블 슬롯 조회 생성/실패 응답 코드 추가 및 에러코드 통일 * [FEAT]: StoreTableController에 가게 슬롯 조회 API 및 명세 추가 * [FEAT]: TableBlockRepository 생성 * [FEAT]: BookingRepository에 JPQL 메소드 추가 * [FEAT]: 테이블 슬롯 조회 응답 DTO, Converter 추가 * [FEAT]: 테이블 슬롯 계산, 검증 로직 추가 * [FEAT]: 테이블 슬롯 조회 서비스 로직 추가 * [FEAT]: 테이블 슬롯 상태 변경 성공/실패 에러 핸들러 추가 * [FEAT]: TableBlockController에 테이블 슬롯 상태 변경 API 및 명세 추가 * [FEAT]: TableBlockRepository 특정 테이블 차단 내역 조회 메소드 추가 * [FEAT]: BookingRepository 테이블 예약 여부 조회 메소드 추가 * [FEAT]: 테이블 상태 변경 요청/응답 DTO, Converter 추가 * [FEAT]: 테이블 상태 변경시 검증 로직 추가 * [FEAT]: 테이블 상태 변경 서비스 로직 추가 * [FEAT]: 가게 등록 시 사업자번호 검증 추가 및 Region 엔티티 구조 변경 (#75) * [REFACTOR]: 주소(Region) 저장 체계 변경 * [FEAT]: 사업자번호 검증 API 관련 의존성 및 설정파일 추가 * [FEAT]: 사업자번호 검증 로직 추가 * [FEAT]: 가게 등록 요청 시 사업자번호 검증부 추가 * [REFACTOR]: WebClient -> RestClient로 변경 * [FEAT]: Real, Mock으로 사업자번호 검증 분리 * [CHORE]: 사업자번호 검증 성공 로그 추가 * [FEAT]: application.yml에 사업자번호 검증 api key 설정부 추가 * [REFACTOR]: 사업자번호 검증 코드 패키지명 변경 * [FIX]: test의 application.yml에 더미 키값 추가 * [CHORE]: 멈춰있는 컨테이너 전부 삭제 명령어 제거 * [FEAT]: DTO 분리 및 필드명 변경 * [REFACTOR]: 결제 승인 로직 개선 및 DTO 적용 * [REFACTOR]: / 붙여줌 * [FEAT]: 메뉴 엔티티 설계 (#82) * [FEAT]: 가게 테이블 상세 조회/수정/삭제 API 구현 (#77) * [FEAT]: 테이블 상세 정보 조회 성공 응답 코드 추가 * [FEAT]: 테이블 상세 정보 조회 api 및 명세 추가 * [FEAT]: 테이블 상세 정보 조회 응답 DTO, Converter 추가 * [FEAT]: 테이블 상세 정보 조회 서비스 로직 추가 * [FEAT]: 테이블 정보 수정 성공/실패 응답 코드 추가 * [FEAT]: 테이블 정보 수정 api 및 명세 추가 * [FEAT]: StoreTable 엔티티 편의 메소드 추가 * [FEAT]: 테이블 정보 수정 요청/응답 DTO, Converter 추가 * [FEAT]: 테이블 정보 수정 서비스 로직 구현 - 테이블 번호 수정 - 테이블 좌석 수 변경 - 테이블 유형 변경 * [FEAT]: 테이블 삭제 성공/실패 응답 코드 추가 * [FEAT]: 테이블 삭제 api 및 명세 추가 * [FEAT]: 테이블 삭제 응답 DTO, Converter 추가 * [FEAT]: 테이블 삭제 서비스 로직 구현 - 삭제 시 현재 시간을 기준으로 예약 존재 여부 확인 - 삭제 시 soft delete * 가게 개별 테이블 이미지 등록/삭제 API 구현 (#83) * [FEAT]: 테이블 이미지 업로드 API 및 명세 추가 * [FEAT]: StoreTable 이미지 업로드 메소드 추가 * [FEAT]: 테이블 이미지 업로드 응답 DTO, Converter 추가 * [FEAT]: 테이블 이미지 업로드 서비스 로직 추가 * [REFACTOR]: 테이블 상세 조회 시 테이블 이미지 URL을 포함하도록 수정 * [FEAT]: 테이블 이미지 삭제 API 및 명세 추가 * [FEAT]: StoreTable 테이블 이미지 삭제 메소드 추가 * [FEAT]: 테이블 이미지 삭제 응답 DTO, Converter 추가 * [FEAT]: 테이블 이미지 삭제 서비스 로직 추가 * [FEAT] : 예약 취소 API 개발 / 마이페이지 예약 조회 API 개발 (#85) * [FEAT] : 예약 취소 DTO 개발 * [FEAT] : 예약 취소 DTO 개발 * [FEAT] : 예약 취소 성공,실패 상태 추가 * [FIX] : 예약 상태 이름 변경 * [FEAT] : Booking 엔티티에 cancel 비지니스 메서드 추가 * [FEAT] : 예약 취소 API 구현 * [FEAT] : Booking 엔티티 취소 이유 필드와 취소 메서드 구현 * [FEAT] : BookingConverter 구현 * [FEAT] : 예약생성 응답 DTO에 paymentID, orderID 추가 * [CHORE] : 결제 요청 API 스웨거 설명 수정 * [REFACTOR] : 결제 승인 API에서 예약 상태도 변경하도록 코드 구조 변경 * [CHORE] : 결제 완료 처리 api 스웨거 설정 변경 * [REFACTOR] : 예약 생성 api에서 내부적으로 결제 요청 로직까지 진행하도록 코드 변경 * [FEAT] : 예약 엔티티에서 결제 완료된 결제키를 찾는 편의 메서드 개발 * [FEAT] : 예약 취소시 환불까지 연동되는 비즈니스로직 개발 * [FEAT] : 예약 조회 응답 DTO 개발 * [FEAT] : 예약 조회 Repository 개발 * [FEAT] : 예약 조회 Service 개발 * [FEAT] : 예약 조회 Controller 개발 * [CHORE] : application-local.yml 파일 설정 추가 * [FEAT] : BookingMenu 엔티티 개발 * [FEAT] : 예약 생성 요청 DTO 변경 * [FEAT] : 예약 생성 서비스 로직 변경(메뉴 고려) * [FIX] : 예약금, 결제금액 타입 Integer->Decimal로 변경 * [FIX] : 예약 조회 기본 페이지 1로 변경 * [FIX] : 불필요한 예약 완료 처리 api 삭제 * [REFACTOR] : 존재하지 않는 테이블을 포함해 에약을 생성하는 예외 처리 * [CHORE] : PAYMENT 컨트롤러 SWAGGER 설정 복원 * [FEAT] : 예약 취소 로직 예외 처리 추가 * [FIX] : 결제 금액 타입 Integer -> BigDecimal로 변경 * [FEAT]: 메뉴 관리 CRUD 구현 및 S3 수명주기 추가 (#87) * [FEAT]: 메뉴 도메인 DTO 추가 * [FEAT]: 메뉴 API 상태코드 추가 * [FEAT]: 메뉴, 메뉴 이미지 등록, 삭제 로직 추가 * [FEAT]: 메뉴 수정 DTO 추가 * [FEAT]: 메뉴 수정 성공 응답코드 추가 * [FEAT]: 메뉴 수정 로직 추가 * [REFACTOR]: S3 예외처리 세분화 * [FEAT]: 메뉴 조회 DTO 및 성공 응답코드 추가 * [FEAT]: 메뉴 조회 로직 추가 * [FEAT]: 메뉴 조회 시 N+1 방지를 위해 fetch join 쿼리 추가 * [FEAT]: 품절여부 변경 DTO 및 성공 응답코드 추가 * [FEAT]: 메뉴 품절여부 변경 로직 추가 * [FEAT]: 메뉴 삭제 시 soft delete 설정 * [FEAT]: @Valid, @RequestParam, @PathVaraible 유효성 검사 실패 시 에러 메시지 보이도록 메서드 추가 * [FEAT]: 이미지 선 업로드에 따른 S3 수명 주기 연동 로직 구현 * [REFACTOR]: 이미지 Url 리턴하도록 수정 * [FEAT]: 품절 여부 수정 요청이 기존과 동일하다면 바로 리턴하도록 조건 추가 * [REFACTOR]: 이미지 삭제 API를 이미 등록된 이미지 삭제하는 것으로 역할 수정 * [REFACTOR]: soft delete된 Menu는 안 가져오도록 쿼리 수정 * [REFACTOR]: @where -> @SQLRestriction 으로 수정 * [FIX]: isSuccess(false)로 수정 * [REFACTOR]: 예약금 정책 변경에 따라 minPrice 필드 삭제 및 로직 삭제 * [REFACTOR]: 남아있던 minPrice 사용하는 로직들 수정 * [FEAT]: 사용하지 않는 removeMenu 메서드 삭제 * [REFACTOR]: 메뉴가 없는 가게도 조회될 수 있도록 쿼리 조건 수정 * [REFACTOR]: 트랜잭션 커밋 이후에 S3에 접근할 수 있도록 로직 수정 * [FEAT]: moveObject()에서 이동 경로가 동일한 경우 예외처리 추가 * [FIX] : QClass 관련 빌드 에러 수정 --------- Co-authored-by: CHAN <150508884+zerochani@users.noreply.github.com> Co-authored-by: SungMinju <minju2928@naver.com> Co-authored-by: J_lisa&& <155425927+SungMinju@users.noreply.github.com> Co-authored-by: twodo0 <dopal0426@gmail.com> Co-authored-by: 손준규 <144649271+sonjunkyu@users.noreply.github.com>
💡 작업 개요
GET /api/v1/stores/{storeId}/menus (메뉴 조회 API)
POST /api/v1/stores/{storeId}/menus (메뉴 등록 API)
DELETE /api/v1/stores/{storeId}/menus (메뉴 삭제 API)
POST /api/v1/stores/{storeId}/menus/images (메뉴 이미지 선 업로드 API)
DELETE /api/v1/stores/{storeId}/menus/images (메뉴 이미지 삭제 API)
PATCH /api/v1/stores/{storeId}/menus/{menuId} (메뉴 수정 API)
PATCH /api/v1/stores/{storeId}/menus/{menuId}/sold-out (품절 여부 변경 API)
✅ 작업 내용
🧪 테스트 내용
📝 기타 참고 사항
Closes #84
Summary by CodeRabbit
New Features
Bug Fixes
Chores
Stability
✏️ Tip: You can customize this high-level summary in your review settings.