diff --git a/src/main/java/umc/th/juinjang/api/limjang/controller/NoteControllerV2.java b/src/main/java/umc/th/juinjang/api/limjang/controller/NoteControllerV2.java index 6d13b69a..325095de 100644 --- a/src/main/java/umc/th/juinjang/api/limjang/controller/NoteControllerV2.java +++ b/src/main/java/umc/th/juinjang/api/limjang/controller/NoteControllerV2.java @@ -17,6 +17,7 @@ import umc.th.juinjang.api.limjang.controller.parameter.LimjangSortOptions; import umc.th.juinjang.api.limjang.controller.request.NoteInitRequest; import umc.th.juinjang.api.limjang.controller.request.NotePatchRequest; +import umc.th.juinjang.api.limjang.controller.request.NotePatchRequestV2; import umc.th.juinjang.api.limjang.controller.request.NotePostRequest; import umc.th.juinjang.api.limjang.service.NoteCommandServiceV2; import umc.th.juinjang.api.limjang.service.NoteQueryServiceV2; @@ -71,9 +72,9 @@ public ApiResponse updateNote(@PathVariable(name = "noteId") Long noteId, @Operation(summary = "임장 수정 API V2 - UI/UX 리팩토링") @PatchMapping("/notes/init/{noteId}") public ApiResponse updateNoteV2(@PathVariable(name = "noteId") Long noteId, - @RequestBody @Valid NotePatchRequest request, + @RequestBody @Valid NotePatchRequestV2 request, @AuthenticationPrincipal Member member) { - noteCommandService.updateNoteV2(noteId, request); + noteCommandService.updateNoteInitV2(noteId, request); return ApiResponse.onSuccess(null); } diff --git a/src/main/java/umc/th/juinjang/api/limjang/controller/request/NotePatchRequestV2.java b/src/main/java/umc/th/juinjang/api/limjang/controller/request/NotePatchRequestV2.java new file mode 100644 index 00000000..518f175d --- /dev/null +++ b/src/main/java/umc/th/juinjang/api/limjang/controller/request/NotePatchRequestV2.java @@ -0,0 +1,68 @@ +package umc.th.juinjang.api.limjang.controller.request; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import umc.th.juinjang.domain.limjang.model.Address; +import umc.th.juinjang.domain.limjang.model.LimjangPrice; +import umc.th.juinjang.domain.limjang.model.LimjangPriceType; +import umc.th.juinjang.domain.limjang.model.LimjangPurpose; +import umc.th.juinjang.domain.limjang.repository.NotePriceFactory; + +public record NotePatchRequestV2( + @NotNull + LimjangPriceType priceType, + + @Pattern(regexp = "^[0-9]+$", message = "가격은 숫자만 입력해야 합니다.") + String price, + @Pattern(regexp = "^[0-9]+$", message = "가격은 숫자만 입력해야 합니다.") + String monthlyRent, + + String roadAddress, + String addressDetail, + + String bcode, + + String nickname, + + String floor, + Integer pyong, //null 처리를 위해 Integer로 변경 + String sido, + String sigungu, + String bname1, + String bname2 +) { + + public LimjangPrice toUpdatedPrice(LimjangPurpose purpose) { + if (price == null && monthlyRent == null) { + return LimjangPrice.empty(); + } + return NotePriceFactory.create(purpose, priceType, price, monthlyRent); + } + + public Address toUpdatedAddress() { + // 둘 다 없음 -> 삭제(주소 null 처리 유도) + if (isDeleteAddressIntent()) { + return Address.empty(); + } + + // 상세주소만 -> 정책상 금지 + if (isDetailOnly()) { + throw new IllegalArgumentException("본주소 없이 상세주소만 입력할 수 없습니다."); + } + + // 본주소가 있으면 생성 + return Address.create(roadAddress, addressDetail, bcode, sido, sigungu, bname1, bname2); + } + + private boolean isBlank(String s) { + return s == null || s.isBlank(); + } + + private boolean isDeleteAddressIntent() { // 4번 + return isBlank(roadAddress) && isBlank(addressDetail); + } + + private boolean isDetailOnly() { // 3번 + return isBlank(roadAddress) && !isBlank(addressDetail); + } +} diff --git a/src/main/java/umc/th/juinjang/api/limjang/service/NoteCommandServiceV2.java b/src/main/java/umc/th/juinjang/api/limjang/service/NoteCommandServiceV2.java index 78b3137d..04003227 100644 --- a/src/main/java/umc/th/juinjang/api/limjang/service/NoteCommandServiceV2.java +++ b/src/main/java/umc/th/juinjang/api/limjang/service/NoteCommandServiceV2.java @@ -8,6 +8,7 @@ import umc.th.juinjang.api.address.service.AddressUpdater; import umc.th.juinjang.api.limjang.controller.request.NoteInitRequest; import umc.th.juinjang.api.limjang.controller.request.NotePatchRequest; +import umc.th.juinjang.api.limjang.controller.request.NotePatchRequestV2; import umc.th.juinjang.api.limjang.controller.request.NotePostRequest; import umc.th.juinjang.api.limjang.service.response.NotePostResponse; import umc.th.juinjang.common.code.status.ErrorStatus; @@ -74,6 +75,37 @@ public void updateNoteV2(Long noteId, NotePatchRequest request) { note.updateNote(request.nickname(), request.priceType(), request.floor(), request.pyong()); } + @Transactional + public void updateNoteInitV2(Long noteId, NotePatchRequestV2 request) { + try { + Limjang note = noteFinder.getNoteByIdWhereDeletedIsFalse(noteId); + + validatePriceType(note.getPurpose(), request.priceType()); + + LimjangPrice newPrice = request.toUpdatedPrice(note.getPurpose()); + Address newAddress = request.toUpdatedAddress(); + + Address currentAddress = note.getAddressEntity(); + if (newAddress.isEmpty()) { + if (currentAddress != null) { + note.setAddressEntity(null); + } + } else { + if (currentAddress != null) { + currentAddress.update(newAddress); + } else { + addressUpdater.save(newAddress); + note.setAddressEntity(newAddress); + } + } + + note.getLimjangPrice().updateLimjangPrice(newPrice); + note.updateNote(request.nickname(), request.priceType(), request.floor(), request.pyong()); + } catch (Exception e) { + e.printStackTrace(); + } + } + private void validatePriceType(LimjangPurpose purposeType, LimjangPriceType priceType) { if ( (purposeType == LimjangPurpose.RESIDENTIAL_PURPOSE && priceType == LimjangPriceType.MARKET_PRICE) || diff --git a/src/main/java/umc/th/juinjang/domain/limjang/model/Address.java b/src/main/java/umc/th/juinjang/domain/limjang/model/Address.java index 063f51f8..dda98913 100644 --- a/src/main/java/umc/th/juinjang/domain/limjang/model/Address.java +++ b/src/main/java/umc/th/juinjang/domain/limjang/model/Address.java @@ -1,6 +1,5 @@ package umc.th.juinjang.domain.limjang.model; -import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -87,4 +86,22 @@ public void update(Address newAddress) { public String getFullAddress() { return this.roadAddress + " " + this.getAddressDetail(); } + + public static Address empty() { + return Address.builder().build(); // 모든 필드 null + } + + public boolean isEmpty() { + return isBlank(roadAddress) + && isBlank(addressDetail) + && isBlank(bcode) + && isBlank(sido) + && isBlank(sigungo) + && isBlank(bname1) + && isBlank(bname2); + } + + private boolean isBlank(String s) { + return s == null || s.isBlank(); + } } diff --git a/src/main/java/umc/th/juinjang/domain/limjang/model/Limjang.java b/src/main/java/umc/th/juinjang/domain/limjang/model/Limjang.java index 445c926c..7ae36c45 100644 --- a/src/main/java/umc/th/juinjang/domain/limjang/model/Limjang.java +++ b/src/main/java/umc/th/juinjang/domain/limjang/model/Limjang.java @@ -199,8 +199,10 @@ public static Limjang initNote(Member member, LimjangPrice price, LimjangPurpose .build(); } - public void updateNote(String nickname, LimjangPriceType limjangPriceType, String floor, int pyong) { - this.nickname = nickname; + public void updateNote(String nickname, LimjangPriceType limjangPriceType, String floor, Integer pyong) { + if (nickname != null) { + this.nickname = nickname; + } this.priceType = limjangPriceType; this.floor = floor; this.pyong = pyong; diff --git a/src/main/java/umc/th/juinjang/domain/limjang/model/LimjangPrice.java b/src/main/java/umc/th/juinjang/domain/limjang/model/LimjangPrice.java index d6756aa6..0f95c7d0 100644 --- a/src/main/java/umc/th/juinjang/domain/limjang/model/LimjangPrice.java +++ b/src/main/java/umc/th/juinjang/domain/limjang/model/LimjangPrice.java @@ -1,9 +1,12 @@ package umc.th.juinjang.domain.limjang.model; -import java.util.ArrayList; -import java.util.List; - -import jakarta.persistence.*; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -57,4 +60,8 @@ public String getPrice(LimjangPriceType priceType, LimjangPurpose purpose) { } return null; } + + public static LimjangPrice empty() { + return LimjangPrice.builder().build(); // 모든 price 필드 null + } }