diff --git a/src/main/java/com/hyetaekon/hyetaekon/answer/controller/AnswerController.java b/src/main/java/com/hyetaekon/hyetaekon/answer/controller/AnswerController.java index c56a59d..45545c0 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/answer/controller/AnswerController.java +++ b/src/main/java/com/hyetaekon/hyetaekon/answer/controller/AnswerController.java @@ -9,12 +9,9 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; -import java.util.List; - @RestController @RequestMapping("/api/posts/{postId}/answers") @RequiredArgsConstructor @@ -26,7 +23,7 @@ public class AnswerController { // 게시글의 답변 목록 조회 @GetMapping public ResponseEntity> getAnswersByPostId( - @PathVariable Long postId, + @PathVariable("postId") Long postId, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size) { @@ -38,7 +35,7 @@ public ResponseEntity> getAnswersByPostId( // 답변 작성 @PostMapping public ResponseEntity createAnswer( - @PathVariable Long postId, + @PathVariable("postId") Long postId, @RequestBody AnswerDto answerDto, @AuthenticationPrincipal CustomUserDetails userDetails) { AnswerDto createdAnswer = answerService.createAnswer(postId, answerDto, userDetails.getId()); @@ -48,19 +45,19 @@ public ResponseEntity createAnswer( // 답변 채택 @PutMapping("/{answerId}/select") public ResponseEntity selectAnswer( - @PathVariable Long postId, - @PathVariable Long answerId, + @PathVariable("postId") Long postId, + @PathVariable("answerId") Long answerId, @AuthenticationPrincipal CustomUserDetails userDetails) { // 로그인한 사용자의 ID를 서비스에 전달 answerService.selectAnswer(postId, answerId, userDetails.getId()); return ResponseEntity.ok().build(); } - // 답변 삭제 (관리자만 가능) + // 답변 삭제 (관리자와 작성자만 가능) @DeleteMapping("/{answerId}") public ResponseEntity deleteAnswer( - @PathVariable Long postId, - @PathVariable Long answerId, + @PathVariable("postId") Long postId, + @PathVariable("answerId") Long answerId, @AuthenticationPrincipal CustomUserDetails userDetails) { answerService.deleteAnswer(postId, answerId, userDetails.getId(), userDetails.getRole()); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/hyetaekon/hyetaekon/answer/dto/AnswerDto.java b/src/main/java/com/hyetaekon/hyetaekon/answer/dto/AnswerDto.java index d3bd5ce..20ca3a0 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/answer/dto/AnswerDto.java +++ b/src/main/java/com/hyetaekon/hyetaekon/answer/dto/AnswerDto.java @@ -8,6 +8,7 @@ public class AnswerDto { private Long id; private Long postId; private Long userId; + private String nickname; private String content; private LocalDateTime createdAt; private boolean selected; diff --git a/src/main/java/com/hyetaekon/hyetaekon/answer/mapper/AnswerMapper.java b/src/main/java/com/hyetaekon/hyetaekon/answer/mapper/AnswerMapper.java index 6ba8dc8..d7342b7 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/answer/mapper/AnswerMapper.java +++ b/src/main/java/com/hyetaekon/hyetaekon/answer/mapper/AnswerMapper.java @@ -9,6 +9,9 @@ @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface AnswerMapper { @Mapping(target = "content", expression = "java(answer.getDisplayContent())") + @Mapping(source = "user.id", target = "userId") + @Mapping(source = "post.id", target = "postId") + @Mapping(source = "user.nickname", target = "nickname") AnswerDto toDto(Answer answer); Answer toEntity(AnswerDto answerDto); diff --git a/src/main/java/com/hyetaekon/hyetaekon/answer/repository/AnswerRepository.java b/src/main/java/com/hyetaekon/hyetaekon/answer/repository/AnswerRepository.java index caee8c8..e75c670 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/answer/repository/AnswerRepository.java +++ b/src/main/java/com/hyetaekon/hyetaekon/answer/repository/AnswerRepository.java @@ -7,8 +7,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import java.util.List; - @Repository public interface AnswerRepository extends JpaRepository { // 페이지네이션 적용한 답변 목록 조회 (post 객체 사용) diff --git a/src/main/java/com/hyetaekon/hyetaekon/answer/service/AnswerService.java b/src/main/java/com/hyetaekon/hyetaekon/answer/service/AnswerService.java index 1f39d63..775279c 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/answer/service/AnswerService.java +++ b/src/main/java/com/hyetaekon/hyetaekon/answer/service/AnswerService.java @@ -12,7 +12,6 @@ import com.hyetaekon.hyetaekon.user.entity.User; import com.hyetaekon.hyetaekon.user.repository.UserRepository; import com.hyetaekon.hyetaekon.user.service.UserPointService; -import jakarta.persistence.EntityNotFoundException; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -20,10 +19,6 @@ import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; -import java.time.LocalDateTime; -import java.util.List; -import java.util.stream.Collectors; - @Service @RequiredArgsConstructor public class AnswerService { diff --git a/src/main/java/com/hyetaekon/hyetaekon/bookmark/service/BookmarkService.java b/src/main/java/com/hyetaekon/hyetaekon/bookmark/service/BookmarkService.java index 7e38739..6ffc1f6 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/bookmark/service/BookmarkService.java +++ b/src/main/java/com/hyetaekon/hyetaekon/bookmark/service/BookmarkService.java @@ -6,6 +6,7 @@ import com.hyetaekon.hyetaekon.publicservice.entity.PublicService; import com.hyetaekon.hyetaekon.publicservice.repository.PublicServiceRepository; import com.hyetaekon.hyetaekon.publicservice.service.PublicServiceHandler; +import com.hyetaekon.hyetaekon.publicservice.service.mongodb.ServiceMatchedHandler; import com.hyetaekon.hyetaekon.user.entity.User; import com.hyetaekon.hyetaekon.user.repository.UserRepository; import jakarta.transaction.Transactional; @@ -21,7 +22,7 @@ public class BookmarkService { private final BookmarkRepository bookmarkRepository; private final UserRepository userRepository; private final PublicServiceRepository publicServiceRepository; - private final PublicServiceHandler publicServiceHandler; + private final ServiceMatchedHandler serviceMatchedHandler; public void addBookmark(String serviceId, Long userId) { User user = userRepository.findById(userId) @@ -45,6 +46,8 @@ public void addBookmark(String serviceId, Long userId) { // 북마크 수 증가 publicService.increaseBookmarkCount(); publicServiceRepository.save(publicService); + // 캐시 무효화 추가 + serviceMatchedHandler.refreshMatchedServicesCache(userId); } @Transactional @@ -58,5 +61,7 @@ public void removeBookmark(String serviceId, Long userId) { PublicService publicService = bookmark.getPublicService(); publicService.decreaseBookmarkCount(); publicServiceRepository.save(publicService); + // 캐시 무효화 추가 + serviceMatchedHandler.refreshMatchedServicesCache(userId); } } diff --git a/src/main/java/com/hyetaekon/hyetaekon/comment/controller/CommentController.java b/src/main/java/com/hyetaekon/hyetaekon/comment/controller/CommentController.java index 7be3783..940f140 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/comment/controller/CommentController.java +++ b/src/main/java/com/hyetaekon/hyetaekon/comment/controller/CommentController.java @@ -1,7 +1,6 @@ package com.hyetaekon.hyetaekon.comment.controller; import com.hyetaekon.hyetaekon.comment.dto.CommentCreateRequestDto; -import com.hyetaekon.hyetaekon.comment.dto.CommentDto; import com.hyetaekon.hyetaekon.comment.dto.CommentListResponseDto; import com.hyetaekon.hyetaekon.comment.service.CommentService; import com.hyetaekon.hyetaekon.common.jwt.CustomUserDetails; @@ -9,7 +8,6 @@ import org.springframework.data.domain.Page; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/hyetaekon/hyetaekon/comment/dto/CommentDto.java b/src/main/java/com/hyetaekon/hyetaekon/comment/dto/CommentDto.java deleted file mode 100644 index 28017cb..0000000 --- a/src/main/java/com/hyetaekon/hyetaekon/comment/dto/CommentDto.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.hyetaekon.hyetaekon.comment.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class CommentDto { - private Long id; - private Long postId; - private Long parentId; // 대댓글일 경우 부모 댓글 ID - private String content; - private Long userId; - private String nickname; - private boolean deleted; - private LocalDateTime createdAt; -} diff --git a/src/main/java/com/hyetaekon/hyetaekon/comment/mapper/CommentMapper.java b/src/main/java/com/hyetaekon/hyetaekon/comment/mapper/CommentMapper.java index beee62e..f08134a 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/comment/mapper/CommentMapper.java +++ b/src/main/java/com/hyetaekon/hyetaekon/comment/mapper/CommentMapper.java @@ -1,13 +1,11 @@ package com.hyetaekon.hyetaekon.comment.mapper; import com.hyetaekon.hyetaekon.comment.dto.CommentCreateRequestDto; -import com.hyetaekon.hyetaekon.comment.dto.CommentDto; import com.hyetaekon.hyetaekon.comment.dto.CommentListResponseDto; import com.hyetaekon.hyetaekon.comment.entity.Comment; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; -import org.mapstruct.factory.Mappers; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface CommentMapper { diff --git a/src/main/java/com/hyetaekon/hyetaekon/common/util/BaseEntity.java b/src/main/java/com/hyetaekon/hyetaekon/common/util/BaseEntity.java index 06a0b25..362555c 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/common/util/BaseEntity.java +++ b/src/main/java/com/hyetaekon/hyetaekon/common/util/BaseEntity.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.Setter; import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import java.time.LocalDateTime; @@ -20,6 +21,10 @@ public class BaseEntity { @Column(name = "created_at", nullable = false, updatable = false, columnDefinition = "DATETIME(0)") private LocalDateTime createdAt; + @LastModifiedDate + @Column(name = "modified_at", nullable = false, columnDefinition = "DATETIME(0)") + private LocalDateTime modifiedAt; + @Column(name = "deleted_at") private LocalDateTime deletedAt; diff --git a/src/main/java/com/hyetaekon/hyetaekon/post/controller/PostController.java b/src/main/java/com/hyetaekon/hyetaekon/post/controller/PostController.java index 7342376..019266c 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/post/controller/PostController.java +++ b/src/main/java/com/hyetaekon/hyetaekon/post/controller/PostController.java @@ -25,7 +25,7 @@ public class PostController { // PostType에 해당하는 게시글 목록 조회 @GetMapping("/type") public ResponseEntity> getPosts( - @RequestParam(required = false, defaultValue = "전체") String postType, + @RequestParam(required = false, defaultValue = "ALL") String postType, @RequestParam(required = false) String keyword, // 🔥 제목 검색 추가 @RequestParam(defaultValue = "createdAt") String sortBy, // 🔥 정렬 키워드 추가 @RequestParam(defaultValue = "DESC") String direction, // 🔥 정렬 방향 추가 diff --git a/src/main/java/com/hyetaekon/hyetaekon/post/entity/PostType.java b/src/main/java/com/hyetaekon/hyetaekon/post/entity/PostType.java index c704720..905a890 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/post/entity/PostType.java +++ b/src/main/java/com/hyetaekon/hyetaekon/post/entity/PostType.java @@ -27,4 +27,19 @@ public static PostType fromKoreanName(String koreanName) { // 일치하는 이름이 없거나 null인 경우 기본값으로 ALL 반환 return ALL; } + + /** + * 클라이언트에서 영문 타입 코드로 전송된 PostType을 찾습니다. + */ + public static PostType fromString(String typeCode) { + if (typeCode == null || typeCode.trim().isEmpty()) { + return ALL; // 기본값 + } + + try { + return PostType.valueOf(typeCode.toUpperCase()); + } catch (IllegalArgumentException e) { + return ALL; + } + } } diff --git a/src/main/java/com/hyetaekon/hyetaekon/post/service/PostService.java b/src/main/java/com/hyetaekon/hyetaekon/post/service/PostService.java index f6dc0c1..7fa4b4e 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/post/service/PostService.java +++ b/src/main/java/com/hyetaekon/hyetaekon/post/service/PostService.java @@ -128,7 +128,8 @@ public PostDetailResponseDto createPost(PostCreateRequestDto requestDto, Long us User user = userRepository.findById(userId) .orElseThrow(() -> new EntityNotFoundException("사용자를 찾을 수 없습니다: " + userId)); - PostType postType = PostType.fromKoreanName(requestDto.getPostType()); + PostType postType = PostType.fromString(requestDto.getPostType()); + log.info("Received postType: '{}'", requestDto.getPostType()); Post post = postMapper.toEntity(requestDto); post.setUser(user); @@ -165,9 +166,9 @@ public PostDetailResponseDto updatePost(Long postId, PostUpdateRequestDto update throw new AccessDeniedException("게시글 수정 권한이 없습니다"); } - // PostType 변환 + // 영문 타입 코드로 PostType 찾기 if (updateDto.getPostType() != null) { - PostType postType = PostType.fromKoreanName(updateDto.getPostType()); + PostType postType = PostType.fromString(updateDto.getPostType()); post.setPostType(postType); } diff --git a/src/main/java/com/hyetaekon/hyetaekon/publicservice/service/mongodb/ServiceMatchedHandler.java b/src/main/java/com/hyetaekon/hyetaekon/publicservice/service/mongodb/ServiceMatchedHandler.java index b88b15f..499d2b8 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/publicservice/service/mongodb/ServiceMatchedHandler.java +++ b/src/main/java/com/hyetaekon/hyetaekon/publicservice/service/mongodb/ServiceMatchedHandler.java @@ -13,6 +13,7 @@ import com.hyetaekon.hyetaekon.userInterest.repository.UserInterestRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -88,4 +89,8 @@ public List getPersonalizedServices(Long userId, i .collect(Collectors.toList()); } + @CacheEvict(value = "matchedServices", key = "#userId") + public void refreshMatchedServicesCache(Long userId) { + // 캐시 갱신 메서드 + } } diff --git a/src/main/java/com/hyetaekon/hyetaekon/recommend/entity/Recommend.java b/src/main/java/com/hyetaekon/hyetaekon/recommend/entity/Recommend.java index 6ec90f0..d6bc086 100644 --- a/src/main/java/com/hyetaekon/hyetaekon/recommend/entity/Recommend.java +++ b/src/main/java/com/hyetaekon/hyetaekon/recommend/entity/Recommend.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; +import com.hyetaekon.hyetaekon.common.util.BaseEntity; import com.hyetaekon.hyetaekon.post.entity.Post; import com.hyetaekon.hyetaekon.user.entity.User; import jakarta.persistence.*; @@ -15,7 +16,7 @@ @Builder(toBuilder = true) @NoArgsConstructor @AllArgsConstructor -public class Recommend { +public class Recommend extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;