Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ out/

### 환경 변수 ###
.env

###claude###
.claude
.serena
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package devkor.com.teamcback.domain.bookmark.dto.response;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import devkor.com.teamcback.domain.bookmark.entity.Bookmark;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@JsonIgnoreProperties
public class CreateBookmarkRes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public class BookmarkService {
* 즐겨찾기 업데이트
*/
@Transactional
@UpdateScore(addScore = 1)
public CreateBookmarkRes createBookmark(Long userId, CreateBookmarkReq req) {
User user = findUser(userId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ public interface FileRepository extends JpaRepository<File, Long>, CustomFileRep
List<File> findTop3AllByFileUuidOrderBySortNumAsc(String fileUuid);

File findByFileUuidAndSortNum(String fileUuid, Long sortNum);

boolean existsByFileUuid(String fileUuid);
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ public CommonResponse<List<SearchReviewImageRes>> getReviewPlaceDetailImages(
return CommonResponse.success(reviewService.getReviewPlaceDetailImages(placeId, lastFileId));
}

// TODO: 리뷰 작성 시 포인트 부여
@Operation(summary = "리뷰 작성",
description = "식당, 카페에 대한 리뷰를 작성")
@ApiResponses(value = {
Expand Down Expand Up @@ -114,7 +113,6 @@ public CommonResponse<ModifyReviewRes> modifyReview(
return CommonResponse.success(reviewService.modifyReview(userDetail.getUser().getUserId(), reviewId, modifyReviewReq));
}

// TODO: 리뷰 삭제 시 포인트 제거
@Operation(summary = "리뷰 삭제",
description = "식당, 카페에 대한 리뷰를 삭제")
@ApiResponses(value = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
package devkor.com.teamcback.domain.review.dto.response;

import devkor.com.teamcback.global.response.ScoreUpdateResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;

@Schema(description = "리뷰 생성 응답 dto")
@Getter
public class CreateReviewRes {
public class CreateReviewRes implements ScoreUpdateResponse {
private Long reviewId;

@Setter
@Schema(description = "레벨업 여부")
private boolean isLevelUp;

@Setter
@Schema(description = "현재 점수")
private Long currentScore;

@Setter
@Schema(description = "점수 획득 여부")
private boolean scoreGained;

public CreateReviewRes(Long id) {
this.reviewId = id;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
package devkor.com.teamcback.domain.review.dto.response;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import devkor.com.teamcback.global.response.ScoreUpdateResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;

@Setter
@Schema(description = "리뷰 삭제 응답 dto")
@JsonIgnoreProperties
public class DeleteReviewRes {
@Getter
public class DeleteReviewRes implements ScoreUpdateResponse {

@Schema(description = "레벨업 여부")
private boolean isLevelUp;

@Schema(description = "현재 점수")
private Long currentScore;

@Schema(description = "점수 획득 여부 (삭제 시에는 항상 false)")
private boolean scoreGained;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
package devkor.com.teamcback.domain.review.dto.response;

import devkor.com.teamcback.global.response.ScoreUpdateResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;

@Schema(description = "리뷰 수정 응답 dto")
@Getter
public class ModifyReviewRes {
public class ModifyReviewRes implements ScoreUpdateResponse {
private Long reviewId;

@Setter
@Schema(description = "레벨업 여부")
private boolean isLevelUp;

@Setter
@Schema(description = "현재 점수")
private Long currentScore;

@Setter
@Schema(description = "점수 획득 여부")
private boolean scoreGained;

public ModifyReviewRes(Long id) {
this.reviewId = id;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class Review extends BaseEntity {

@Setter
@Column(nullable = false)
private boolean isReported = false;
private boolean isReported = false; // 신고 여부

@ManyToOne
@JoinColumn(name = "user_id")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@

import devkor.com.teamcback.domain.place.entity.Place;
import devkor.com.teamcback.domain.review.entity.Review;
import devkor.com.teamcback.domain.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.time.LocalDateTime;
import java.util.List;

public interface ReviewRepository extends JpaRepository<Review, Long> {

List<Review> findAllByPlaceOrderByCreatedAtDesc(Place place);

/**
* 특정 사용자가 특정 장소에 특정 기간 내 작성한 리뷰 존재 여부 확인
* (같은 장소 하루 1개 리뷰 제한용)
*/
boolean existsByUserAndPlaceAndCreatedAtBetween(User user, Place place, LocalDateTime start, LocalDateTime end);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import devkor.com.teamcback.domain.search.dto.response.SearchPlaceReviewTagRes;
import devkor.com.teamcback.domain.user.entity.User;
import devkor.com.teamcback.domain.user.repository.UserRepository;
import devkor.com.teamcback.global.annotation.UpdateScore;
import devkor.com.teamcback.global.exception.exception.GlobalException;
import devkor.com.teamcback.global.response.ResultCode;
import devkor.com.teamcback.infra.s3.FilePath;
Expand Down Expand Up @@ -128,9 +129,17 @@ public List<SearchReviewImageRes> getReviewPlaceDetailImages(Long placeId, Long

/**
* 리뷰 작성
* - 기본 점수: +3점 (별점)
* - 한줄평 10글자 이상: +7점
* - 사진 1장 이상: +3점
* - 같은 장소 하루 1개 리뷰 제한
*/
@Transactional
@UpdateScore(dynamic = true)
public CreateReviewRes createReview(Long userId, Long placeId, CreateReviewReq createReviewReq) {
// 한줄평 길이 검증 (작성했다면 10글자 이상)
validateCommentLength(createReviewReq.getComment());

// 사용자 검색
User user = findUserById(userId);

Expand Down Expand Up @@ -175,9 +184,15 @@ public GetReviewRes getReview(Long reviewId) {

/**
* 리뷰 수정
* - 한줄평/사진 추가 시 추가 점수 부여
* - 한줄평/사진 제거 시 해당 점수 차감
*/
@Transactional
@UpdateScore(dynamic = true)
public ModifyReviewRes modifyReview(Long userId, Long reviewId, @Valid ModifyReviewReq modifyReviewReq) {
// 한줄평 길이 검증 (작성했다면 10글자 이상)
validateCommentLength(modifyReviewReq.getComment());

// 사용자 검색
User user = findUserById(userId);

Expand Down Expand Up @@ -210,8 +225,10 @@ public ModifyReviewRes modifyReview(Long userId, Long reviewId, @Valid ModifyRev

/**
* 리뷰 삭제
* - 삭제한 리뷰의 점수만큼 차감 (최소 0점)
*/
@Transactional
@UpdateScore(dynamic = true)
public DeleteReviewRes deleteReview(Long userId, Long reviewId) {
// 사용자 검색
User user = findUserById(userId);
Expand Down Expand Up @@ -283,6 +300,15 @@ private void validateUser(User user, Review review) {
}
}

/**
* 한줄평 길이 검증 (작성했다면 10글자 이상)
*/
private void validateCommentLength(String comment) {
if(comment != null && !comment.isBlank() && comment.length() < 10) {
throw new GlobalException(ResultCode.COMMENT_TOO_SHORT);
}
}

/**
* 리뷰 태그 저장
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
package devkor.com.teamcback.domain.suggestion.dto.response;

import devkor.com.teamcback.domain.suggestion.entity.Suggestion;
import devkor.com.teamcback.global.response.ScoreUpdateResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;

@Schema(description = "건의 생성 완료")
@Getter
public class CreateSuggestionRes {
public class CreateSuggestionRes implements ScoreUpdateResponse {
private Long suggestionId;
@Setter
@Schema(description = "레벨업 여부", example = "false")
private boolean isLevelUp;
@Setter
@Schema(description = "현재 점수", example = "15")
private Long currentScore;
@Setter
@Schema(description = "점수 획득 여부", example = "true")
private boolean scoreGained;

public CreateSuggestionRes(Suggestion suggestion) {
this.suggestionId = suggestion.getId();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import devkor.com.teamcback.domain.suggestion.entity.Suggestion;
import devkor.com.teamcback.domain.suggestion.entity.SuggestionType;
import devkor.com.teamcback.domain.user.entity.User;
import java.time.LocalDateTime;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -16,4 +17,6 @@ public interface SuggestionRepository extends JpaRepository<Suggestion, Long> {
Page<Suggestion> findBySuggestionTypeAndIsSolved(Pageable pageable, SuggestionType type, Boolean isSolved);

List<Suggestion> findByUser(User user);

long countByUserAndCreatedAtBetween(User user, LocalDateTime start, LocalDateTime end);
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class SuggestionService {
* 건의 생성
*/
@Transactional
@UpdateScore(addScore = 3)
@UpdateScore(addScore = 10)
public CreateSuggestionRes createSuggestion(Long userId, CreateSuggestionReq req, List<MultipartFile> images) {
User user = null;
if(userId != null) user = findUser(userId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
package devkor.com.teamcback.domain.vote.dto.response;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import devkor.com.teamcback.global.response.ScoreUpdateResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;

@Getter
@Schema(description = "투표 내용 저장 완료")
@JsonIgnoreProperties
public class SaveVoteRecordRes {
public class SaveVoteRecordRes implements ScoreUpdateResponse {
@Setter
@Schema(description = "레벨업 여부", example = "false")
private boolean isLevelUp;
@Setter
@Schema(description = "현재 점수", example = "15")
private Long currentScore;
@Setter
@Schema(description = "점수 획득 여부", example = "true")
private boolean scoreGained;
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public GetVoteRes getVoteByPlace(Long voteTopicId, Long placeId) {
/**
* 투표 저장
*/
@UpdateScore(addScore = 3)
@UpdateScore(addScore = 5)
@Transactional
public SaveVoteRecordRes saveVoteRecord(Long userId, SaveVoteRecordReq req) {
if(userId == null) throw new GlobalException(FORBIDDEN);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,12 @@
@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface UpdateScore {
int addScore();
int addScore() default 0;

/**
* 동적 점수 계산 여부
* true인 경우 addScore 무시하고 Request 내용 기반으로 점수 계산
* (리뷰 생성/수정/삭제 등에서 사용)
*/
boolean dynamic() default false;
}
Loading