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..faffc0e4 --- /dev/null +++ b/src/main/java/umc/th/juinjang/api/limjang/controller/request/NotePatchRequestV2.java @@ -0,0 +1,62 @@ +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() { + if (isAddressAllEmpty()) { + return Address.empty(); + } + return Address.create(roadAddress, addressDetail, bcode, sido, sigungu, bname1, bname2); + } + + private boolean isAddressAllEmpty() { + return isBlank(roadAddress) + && isBlank(addressDetail) + && isBlank(bcode) + && isBlank(sido) + && isBlank(sigungu) + && isBlank(bname1) + && isBlank(bname2); + } + + private boolean isBlank(String s) { + return s == null || s.isBlank(); + } +} 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/auth/jwt/JwtExceptionFilter.java b/src/main/java/umc/th/juinjang/auth/jwt/JwtExceptionFilter.java index 7ecc8ce3..17603c2a 100644 --- a/src/main/java/umc/th/juinjang/auth/jwt/JwtExceptionFilter.java +++ b/src/main/java/umc/th/juinjang/auth/jwt/JwtExceptionFilter.java @@ -1,8 +1,17 @@ package umc.th.juinjang.auth.jwt; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + import io.jsonwebtoken.ExpiredJwtException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -10,58 +19,50 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; -import umc.th.juinjang.common.ExceptionHandler; import umc.th.juinjang.common.code.status.ErrorStatus; -import java.io.IOException; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; - @Slf4j @Component @RequiredArgsConstructor public class JwtExceptionFilter extends OncePerRequestFilter { - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - try{ - log.info("exception filter"); - doFilter(request,response,filterChain); - log.info("jwt success"); - } catch (NullPointerException e) { - final Map body = new HashMap<>(); - final ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new JavaTimeModule()); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // 예외에 맞는 HTTP 상태 코드 설정 - response.setContentType("application/json"); - body.put("timestamp", LocalDateTime.now()); - body.put("code", ErrorStatus.TOKEN_EMPTY.getCode()); - body.put("error", "Bad Request"); - body.put("message", ErrorStatus.TOKEN_EMPTY.getMessage()); // 예외에 맞는 메시지 설정 - body.put("path", request.getRequestURI()); + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + try { + log.info("exception filter"); + filterChain.doFilter(request, response); + log.info("jwt success"); + } catch (NullPointerException e) { + final Map body = new HashMap<>(); + final ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // 예외에 맞는 HTTP 상태 코드 설정 + response.setContentType("application/json"); + body.put("timestamp", LocalDateTime.now()); + body.put("code", ErrorStatus.TOKEN_EMPTY.getCode()); + body.put("error", "Bad Request"); + body.put("message", ErrorStatus.TOKEN_EMPTY.getMessage()); // 예외에 맞는 메시지 설정 + body.put("path", request.getRequestURI()); - mapper.writeValue(response.getOutputStream(), body); - logger.info("jwt exception nullpointer"); - throw new ExceptionHandler(ErrorStatus.TOKEN_EMPTY); - } - catch (ExpiredJwtException e) { - final Map body = new HashMap<>(); - final ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new JavaTimeModule()); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - response.setStatus(419); - response.setContentType("application/json"); - body.put("timestamp", LocalDateTime.now()); - body.put("code", ErrorStatus.TOKEN_UNAUTHORIZED.getCode()); - body.put("error", "Unauthorized"); - body.put("message", ErrorStatus.TOKEN_UNAUTHORIZED.getMessage()); - body.put("path", request.getRequestURI()); + mapper.writeValue(response.getOutputStream(), body); + logger.info("jwt exception nullpointer"); + // throw new ExceptionHandler(ErrorStatus.TOKEN_EMPTY); + } catch (ExpiredJwtException e) { + final Map body = new HashMap<>(); + final ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + response.setStatus(419); + response.setContentType("application/json"); + body.put("timestamp", LocalDateTime.now()); + body.put("code", ErrorStatus.TOKEN_UNAUTHORIZED.getCode()); + body.put("error", "Unauthorized"); + body.put("message", ErrorStatus.TOKEN_UNAUTHORIZED.getMessage()); + body.put("path", request.getRequestURI()); - mapper.writeValue(response.getOutputStream(), body); - } - } + mapper.writeValue(response.getOutputStream(), body); + } + } } 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 + } }