Skip to content

Commit 3e53aaa

Browse files
authored
Merge pull request #260 from DevKor-github/feat/review
리뷰 기능 관련 api 추가
2 parents 8ae5243 + 95915e0 commit 3e53aaa

41 files changed

Lines changed: 1167 additions & 80 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
package devkor.com.teamcback.domain.common.repository;
22

33
import devkor.com.teamcback.domain.common.entity.File;
4+
import devkor.com.teamcback.domain.review.repository.CustomFileRepository;
45
import org.springframework.data.jpa.repository.JpaRepository;
56

67
import java.util.List;
78

8-
public interface FileRepository extends JpaRepository<File, Long> {
9+
public interface FileRepository extends JpaRepository<File, Long>, CustomFileRepository {
910
List<File> findAllByFileUuid(String fileUuid);
1011

12+
List<File> findTop10AllByFileUuidOrderBySortNumAsc(String fileUuid);
13+
14+
List<File> findTop5AllByFileUuidOrderBySortNumAsc(String fileUuid);
15+
16+
List<File> findTop3AllByFileUuidOrderBySortNumAsc(String fileUuid);
17+
1118
File findByFileUuidAndSortNum(String fileUuid, Long sortNum);
1219
}

src/main/java/devkor/com/teamcback/domain/common/util/FileUtil.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public void uploadThumb(File savedFile, MultipartFile file, Integer width, Integ
137137
InputStream inputStream = new ByteArrayInputStream(os.toByteArray());
138138

139139
// 저장
140-
String thumbSavedName = s3Util.uploadFile(inputStream, savedFile.getFileUuid(), filePath, file.getContentType());
140+
String thumbSavedName = s3Util.uploadFile(inputStream, savedFile.getFileOriginalName(), filePath, file.getContentType());
141141
savedFile.setThumbSavedName(thumbSavedName);
142142
}
143143

@@ -167,6 +167,22 @@ public void deleteFile(String fileUuid) {
167167
fileRepository.deleteAll(savedFileList);
168168
}
169169

170+
public List<File> getFiles(String fileUuid) {
171+
return fileRepository.findAllByFileUuid(fileUuid);
172+
}
173+
174+
public List<File> getTop10Files(String fileUuid) {
175+
return fileRepository.findTop10AllByFileUuidOrderBySortNumAsc(fileUuid);
176+
}
177+
178+
public List<File> getTop5Files(String fileUuid) {
179+
return fileRepository.findTop5AllByFileUuidOrderBySortNumAsc(fileUuid);
180+
}
181+
182+
public List<File> getTop3Files(String fileUuid) {
183+
return fileRepository.findTop3AllByFileUuidOrderBySortNumAsc(fileUuid);
184+
}
185+
170186
public List<String> getOriginalFiles(String fileUuid) {
171187
List<String> fileList = fileRepository.findAllByFileUuid(fileUuid).stream().map(File::getFileSavedName).toList();
172188

src/main/java/devkor/com/teamcback/domain/place/controller/AdminPlaceController.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package devkor.com.teamcback.domain.place.controller;
22

3-
import devkor.com.teamcback.domain.building.dto.response.SaveBuildingMainImageRes;
43
import devkor.com.teamcback.domain.place.dto.request.CreatePlaceReq;
54
import devkor.com.teamcback.domain.place.dto.request.ModifyPlaceReq;
65
import devkor.com.teamcback.domain.place.dto.response.CreatePlaceRes;
@@ -18,7 +17,6 @@
1817
import io.swagger.v3.oas.annotations.responses.ApiResponses;
1918
import jakarta.validation.Valid;
2019
import lombok.RequiredArgsConstructor;
21-
import org.springframework.http.MediaType;
2220
import org.springframework.web.bind.annotation.*;
2321
import org.springframework.web.multipart.MultipartFile;
2422

@@ -92,7 +90,7 @@ public CommonResponse<DeletePlaceRes> deletePlace(
9290
return CommonResponse.success(adminPlaceService.deletePlace(placeId));
9391
}
9492

95-
@PostMapping(value = "/{placeId}/image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
93+
// @PostMapping(value = "/{placeId}/image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
9694
@Operation(summary = "장소 사진 저장", description = "장소 사진 저장(첫 사진이 대표 사진)")
9795
@ApiResponses(value = {
9896
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),

src/main/java/devkor/com/teamcback/domain/place/entity/Place.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,17 @@ public class Place extends BaseEntity {
6565
private Integer maskIndex;
6666

6767
@Column(nullable = false)
68-
private Integer starSum = 0;
68+
private double starSum = 0;
6969

7070
@Column(nullable = false)
71-
private Integer starNum = 0;
71+
private int starNum = 0;
7272

7373
@Column
7474
private String contact; // 연락처 등
7575

76+
@Column
77+
private String foodType;
78+
7679
@ManyToOne
7780
@JoinColumn(name = "building_id")
7881
private Building building;

src/main/java/devkor/com/teamcback/domain/place/entity/PlaceImage.java

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/main/java/devkor/com/teamcback/domain/place/repository/PlaceImageRepository.java

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package devkor.com.teamcback.domain.review.controller;
2+
3+
import devkor.com.teamcback.domain.review.dto.request.CreateReviewReq;
4+
import devkor.com.teamcback.domain.review.dto.request.ModifyReviewReq;
5+
import devkor.com.teamcback.domain.review.dto.response.*;
6+
import devkor.com.teamcback.domain.review.service.ReviewService;
7+
import devkor.com.teamcback.global.response.CommonResponse;
8+
import devkor.com.teamcback.global.security.UserDetailsImpl;
9+
import io.swagger.v3.oas.annotations.Operation;
10+
import io.swagger.v3.oas.annotations.Parameter;
11+
import io.swagger.v3.oas.annotations.media.Content;
12+
import io.swagger.v3.oas.annotations.media.Schema;
13+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
14+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
15+
import jakarta.validation.Valid;
16+
import lombok.RequiredArgsConstructor;
17+
import org.springframework.http.MediaType;
18+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
19+
import org.springframework.web.bind.annotation.*;
20+
21+
import java.util.List;
22+
23+
24+
@RestController
25+
@RequiredArgsConstructor
26+
@RequestMapping("/api/reviews")
27+
public class ReviewController {
28+
29+
private final ReviewService reviewService;
30+
31+
@Operation(summary = "리뷰 태그 종류 검색")
32+
@ApiResponses(value = {
33+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
34+
@ApiResponse(responseCode = "401", description = "권한이 없습니다.",
35+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
36+
})
37+
@GetMapping("/tags")
38+
public CommonResponse<GetReviewTagListRes> getReviewTagList() {
39+
return CommonResponse.success(reviewService.getReviewTagList());
40+
}
41+
42+
@Operation(summary = "리뷰가 있는 장소의 리뷰 목록 포함 상세 검색",
43+
description = "식당, 카페")
44+
@ApiResponses(value = {
45+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
46+
@ApiResponse(responseCode = "401", description = "권한이 없습니다.",
47+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
48+
})
49+
@GetMapping("/places/{placeId}")
50+
public CommonResponse<GetReviewPlaceDetailRes> getReviewPlaceDetail(
51+
@Parameter(name = "placeId", description = "장소 ID") @PathVariable Long placeId) {
52+
53+
return CommonResponse.success(reviewService.getReviewPlaceDetail(placeId));
54+
}
55+
56+
@Operation(summary = "리뷰가 있는 장소의 리뷰 목록 포함 상세 검색 - 무한스크롤로 리뷰 사진 추가 조회",
57+
description = "식당, 카페")
58+
@ApiResponses(value = {
59+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
60+
@ApiResponse(responseCode = "401", description = "권한이 없습니다.",
61+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
62+
})
63+
@GetMapping("/places/{placeId}/images")
64+
public CommonResponse<List<SearchReviewImageRes>> getReviewPlaceDetailImages(
65+
@Parameter(name = "placeId", description = "장소 ID") @PathVariable Long placeId,
66+
@Parameter(name = "lastFileId", description = "마지막 조회한 사진 id") @RequestParam Long lastFileId) {
67+
68+
return CommonResponse.success(reviewService.getReviewPlaceDetailImages(placeId, lastFileId));
69+
}
70+
71+
// TODO: 리뷰 작성 시 포인트 부여
72+
@Operation(summary = "리뷰 작성",
73+
description = "식당, 카페에 대한 리뷰를 작성")
74+
@ApiResponses(value = {
75+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
76+
@ApiResponse(responseCode = "401", description = "권한이 없습니다.",
77+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
78+
})
79+
@PostMapping(value = "/places/{placeId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
80+
public CommonResponse<CreateReviewRes> createReview(
81+
@Parameter(description = "사용자정보", required = true) @AuthenticationPrincipal UserDetailsImpl userDetail,
82+
@Parameter(name = "placeId", description = "장소 ID") @PathVariable Long placeId,
83+
@Parameter(description = "리뷰 작성 내용", required = true) @Valid @ModelAttribute CreateReviewReq createReviewReq) {
84+
85+
return CommonResponse.success(reviewService.createReview(userDetail.getUser().getUserId(), placeId, createReviewReq));
86+
}
87+
88+
@Operation(summary = "리뷰 조회",
89+
description = "식당, 카페에 대한 리뷰 조회")
90+
@ApiResponses(value = {
91+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
92+
@ApiResponse(responseCode = "401", description = "권한이 없습니다.",
93+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
94+
})
95+
@GetMapping("/{reviewId}")
96+
public CommonResponse<GetReviewRes> getReview(
97+
@Parameter(name = "reviewId", description = "리뷰 ID") @PathVariable Long reviewId) {
98+
return CommonResponse.success(reviewService.getReview(reviewId));
99+
}
100+
101+
@Operation(summary = "리뷰 수정",
102+
description = "식당, 카페에 대한 리뷰를 수정")
103+
@ApiResponses(value = {
104+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
105+
@ApiResponse(responseCode = "401", description = "권한이 없습니다.",
106+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
107+
})
108+
@PutMapping(value = "/{reviewId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
109+
public CommonResponse<ModifyReviewRes> modifyReview(
110+
@Parameter(description = "사용자정보", required = true) @AuthenticationPrincipal UserDetailsImpl userDetail,
111+
@Parameter(name = "reviewId", description = "리뷰 ID") @PathVariable Long reviewId,
112+
@Parameter(description = "리뷰 작성 내용", required = true) @Valid @ModelAttribute ModifyReviewReq modifyReviewReq) {
113+
114+
return CommonResponse.success(reviewService.modifyReview(userDetail.getUser().getUserId(), reviewId, modifyReviewReq));
115+
}
116+
117+
// TODO: 리뷰 삭제 시 포인트 제거
118+
@Operation(summary = "리뷰 삭제",
119+
description = "식당, 카페에 대한 리뷰를 삭제")
120+
@ApiResponses(value = {
121+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
122+
@ApiResponse(responseCode = "401", description = "권한이 없습니다.",
123+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
124+
})
125+
@DeleteMapping( "/{reviewId}")
126+
public CommonResponse<DeleteReviewRes> deleteReview(
127+
@Parameter(description = "사용자정보", required = true) @AuthenticationPrincipal UserDetailsImpl userDetail,
128+
@Parameter(name = "reviewId", description = "리뷰 ID") @PathVariable Long reviewId) {
129+
return CommonResponse.success(reviewService.deleteReview(userDetail.getUser().getUserId(), reviewId));
130+
}
131+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package devkor.com.teamcback.domain.review.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.DecimalMin;
5+
import jakarta.validation.constraints.Size;
6+
import lombok.Getter;
7+
import lombok.Setter;
8+
import org.springframework.web.multipart.MultipartFile;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
@Schema(description = "저장할 리뷰 정보")
14+
@Getter
15+
@Setter
16+
public class CreateReviewReq {
17+
18+
@Schema(description = "별점", example = "1.5")
19+
@DecimalMin(value = "0.5", message = "별점을 입력해주세요.")
20+
private double score;
21+
22+
@Schema(description = "재방문 여부", example = "false")
23+
private boolean isRevisit = false;
24+
25+
@Schema(description = "리뷰 태그 리스트")
26+
@Size(max = 5, message = "태그는 최대 5개까지 가능합니다.")
27+
private List<Long> tagIds = new ArrayList<>();
28+
29+
@Schema(description = "한줄평", example = "맛있고 좋아요.")
30+
private String comment;
31+
32+
@Size(max = 3)
33+
@Schema(description = "첨부 사진")
34+
private List<MultipartFile> images = new ArrayList<>();
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package devkor.com.teamcback.domain.review.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.DecimalMin;
5+
import jakarta.validation.constraints.Size;
6+
import lombok.Getter;
7+
import lombok.Setter;
8+
import org.springframework.web.multipart.MultipartFile;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
@Schema(description = "수정할 리뷰 정보")
14+
@Getter
15+
@Setter
16+
public class ModifyReviewReq {
17+
18+
@Schema(description = "별점", example = "1.5")
19+
@DecimalMin(value = "0.5", message = "별점을 입력해주세요.")
20+
private double score;
21+
22+
@Schema(description = "재방문 여부", example = "false")
23+
private boolean isRevisit = false;
24+
25+
@Schema(description = "리뷰 태그 리스트")
26+
@Size(max = 5, message = "태그는 최대 5개까지 가능합니다.")
27+
private List<Long> tagIds = new ArrayList<>();
28+
29+
@Schema(description = "한줄평", example = "맛있고 좋아요.")
30+
private String comment;
31+
32+
@Size(max = 3)
33+
@Schema(description = "첨부 사진")
34+
private List<MultipartFile> images = new ArrayList<>();
35+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package devkor.com.teamcback.domain.review.dto.response;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.Getter;
5+
6+
@Schema(description = "리뷰 생성 응답 dto")
7+
@Getter
8+
public class CreateReviewRes {
9+
private Long reviewId;
10+
11+
public CreateReviewRes(Long id) {
12+
this.reviewId = id;
13+
}
14+
}

0 commit comments

Comments
 (0)