Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ bin/
out/
!**/src/main/**/out/
!**/src/test/**/out/
src/main/resources/oci

### NetBeans ###
/nbproject/private/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public GetPostResponse create(Long councilId, PostRequest dto) {
NormalizedDateTime normalized = dto.category().validateAndNormalize(dto);

//Place 객체 생성
Place place = placeService.findOrCreatePlace(dto);
Place place = placeService.findOrCreatePlace(dto.place());

StudentCouncilPost post = studentCouncilPostMapper.createStudentCouncilPost(
writer, place, dto, normalized.startDateTime(), normalized.endDateTime()
Expand Down Expand Up @@ -214,7 +214,7 @@ public GetPostResponse update(Long councilId, Long postId, PostRequest dto) {

Place place = post.getPlace();
if (dto.place() != null && (place == null || !dto.place().placeName().equals(place.getPlaceName()))) {
place = placeService.findOrCreatePlace(dto);
place = placeService.findOrCreatePlace(dto.place());
}

post.update(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,46 @@ List<StudentCouncilPost> findPinsInBounds(
@Param("maxLng") Double maxLng,
@Param("now") LocalDateTime now
);

@Query(value = """
SELECT scp.*
FROM student_council_post scp
JOIN student_councils sc
ON scp.writer_id = sc.student_council_id
JOIN places p
ON scp.place_id = p.place_id
LEFT JOIN reviews r
ON r.place_id = p.place_id
AND r.created_at >= :from
WHERE scp.start_date_time <= :now
AND scp.end_date_time >= :now
AND (
(sc.council_type = 'MAJOR_COUNCIL' AND sc.major_id = :majorId)
OR (sc.council_type = 'COLLEGE_COUNCIL' AND sc.college_id = :collegeId)
OR (sc.council_type = 'SCHOOL_COUNCIL' AND sc.school_id = :schoolId)
)
GROUP BY scp.id
ORDER BY COUNT(r.id) DESC
LIMIT 3;

""", nativeQuery = true)
List<StudentCouncilPost> findTop3RecommendedPartnershipPlaces(
@Param("majorId") Long majorId,
@Param("collegeId") Long collegeId,
@Param("schoolId") Long schoolId,
@Param("from") LocalDateTime from,
@Param("now") LocalDateTime now
);

@Query("""
SELECT scp
FROM StudentCouncilPost scp
WHERE scp.place.placeId IN :placeIds
AND scp.writer.id = :writerId
ORDER BY scp.createdAt DESC
""")
List<StudentCouncilPost> findRepresentativePosts(
@Param("placeIds") List<Long> placeIds
);

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record SavedPlaceInfo(

Expand All @@ -32,6 +33,7 @@ public record SavedPlaceInfo(
String telephone,

@Schema(description = "위도/경도")
@NotNull
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분은 지워주세요!! 해당 부분이 존재할 경우 게시글 생성 시 오류가 생길 수 있습니다!!

Coordinate coordinate,

@Schema(description = "이미지 url")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.campus.campus.domain.councilpost.application.dto.request.PostRequest;
import com.campus.campus.domain.place.application.dto.response.LikeResponse;
import com.campus.campus.domain.place.application.dto.response.SavedPlaceInfo;
import com.campus.campus.domain.place.application.dto.response.SearchCandidateResponse;
Expand Down Expand Up @@ -113,8 +112,7 @@ public List<SavedPlaceInfo> search(double lat, double lng, String keyword) {
}

@Transactional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이미 해당 메서드가 사용되는 상위 메서드들에 트랜잭션 어노테이션이 붙어있어요. 따라서 해당 메서드에 트랜잭션 어노테이션을 붙여 중복되게 할 필요는 없을 것 같아요!!

public Place findOrCreatePlace(PostRequest request) {
SavedPlaceInfo place = request.place();
public Place findOrCreatePlace(SavedPlaceInfo place) {
String placeKey = place.placeKey();

//이미 Place 존재하는지 확인 후 없으면 객체 생성 후 저장
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.campus.campus.domain.review.application.dto.request;

import java.util.List;

import com.campus.campus.domain.place.application.dto.response.SavedPlaceInfo;

Comment on lines +5 to +6
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -t f ReviewRequest.java

Repository: our-campUS/our-campUS-BE

Length of output: 192


🏁 Script executed:

find . -type f -name "SavedPlaceInfo.java"

Repository: our-campUS/our-campUS-BE

Length of output: 159


🏁 Script executed:

find . -type f -name "PlaceRequest.java"

Repository: our-campUS/our-campUS-BE

Length of output: 50


🏁 Script executed:

cat -n src/main/java/com/campus/campus/domain/review/application/dto/request/ReviewRequest.java

Repository: our-campUS/our-campUS-BE

Length of output: 1899


🏁 Script executed:

cat -n src/main/java/com/campus/campus/domain/place/application/dto/response/SavedPlaceInfo.java

Repository: our-campUS/our-campUS-BE

Length of output: 1418


요청 DTO가 응답 DTO(SavedPlaceInfo)에 의존하는 구조는 결합도가 높고 검증 범위가 제한적입니다.

SavedPlaceInfoplaceName, placeKey, address, category에만 @NotBlank 검증이 적용되어 있고, link, telephone, coordinate, imgUrls 필드는 검증 제약이 없습니다. 따라서 @Valid가 적용되더라도 모든 입력 필드를 검증하지 못합니다. 리뷰 작성용 별도의 PlaceRequest DTO를 분리하거나, SavedPlaceInfo의 모든 필수 필드에 명시적 검증 제약을 추가하는 것을 권장합니다.

🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/review/application/dto/request/ReviewRequest.java
around lines 5 - 6, ReviewRequest가 응답 DTO인 SavedPlaceInfo에 의존해 검증 범위가 제한되어 있으니,
리뷰용으로 별도 PlaceRequest DTO를 생성하거나 SavedPlaceInfo의 모든 리뷰에 필요한 필수 필드에 명시적
검증(@NotBlank, @NotNull 등)을 추가하여 결합도를 낮추고 검증을 완전하게 만드세요; 구체적으로는 ReviewRequest에서
SavedPlaceInfo를 참조하는 부분을 찾아(예: ReviewRequest.place 필드/생성자) PlaceRequest로 교체하거나
SavedPlaceInfo의 link, telephone, coordinate, imgUrls 등에 적절한 제약을 추가하고
ReviewController/Service에서 @Valid가 적용되도록 보장하세요.

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public record ReviewRequest(

@NotNull
@Size(min = 20, message = "리뷰 내용은 최소 20자 이상이어야 합니다.")
@Schema(example = "아주 정말 맛있습니다. 저의 완전 짱 또간집. 꼭꼮꼬꼬꼭 가세요.")
String content,

@NotNull
@Schema(example = "3.5")
double star,

List<String> imageUrls,

@Schema(example =
"""
"placeName": "숙명여자대학교",
"placeKey": "string",
"address": "서울특별시 용산구 청파로47길 99",
"category": "교육,학문>대학교",
"link": "https://map.naver.com/v5/search/%EC%88%99%EB%AA%85%EC%97%AC%EC%9E%90%EB%8C%80%ED%95%99%EA%B5%90+%EC%A0%9C1%EC%BA%A0%ED%8D%BC%EC%8A%A4?c=37.545947,126.964578,15,0,0,0,dh",
"telephone": "010-1234-1234",
"coordinate": {
"latitude": 0.1,
"longitude": 0.1
},
"imgUrls": [
"string"
]
""",
description = "/search API에서 반환된 결과 중 하나를 선택")
Comment on lines +29 to +45
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "ReviewRequest.java" | head -20

Repository: our-campUS/our-campUS-BE

Length of output: 158


🏁 Script executed:

cat -n ./src/main/java/com/campus/campus/domain/review/application/dto/request/ReviewRequest.java | sed -n '20,50p'

Repository: our-campUS/our-campUS-BE

Length of output: 1120


🏁 Script executed:

find . -type f -name "SavedPlaceInfo.java" | head -5

Repository: our-campUS/our-campUS-BE

Length of output: 159


🏁 Script executed:

cat -n ./src/main/java/com/campus/campus/domain/place/application/dto/response/SavedPlaceInfo.java

Repository: our-campUS/our-campUS-BE

Length of output: 1418


@Schema(example = """ ... """)의 JSON 예시가 유효하지 않아 Swagger UI에서 오류를 야기할 수 있습니다.

최상위 {}를 포함하여 유효한 JSON 형식으로 수정이 필요합니다.

제안 수정
 	@Schema(example =
 		"""
-			"placeName": "숙명여자대학교",
-			      "placeKey": "string",
-			      "address": "서울특별시 용산구 청파로47길 99",
-			      "category": "교육,학문>대학교",
-			      "link": "https://map.naver.com/v5/search/%EC%88%99%EB%AA%85%EC%97%AC%EC%9E%90%EB%8C%80%ED%95%99%EA%B5%90+%EC%A0%9C1%EC%BA%A0%ED%8D%BC%EC%8A%A4?c=37.545947,126.964578,15,0,0,0,dh",
-			      "telephone": "010-1234-1234",
-			      "coordinate": {
-			        "latitude": 0.1,
-			        "longitude": 0.1
-			      },
-			      "imgUrls": [
-			        "string"
-			      ]
+			{
+			  "placeName": "숙명여자대학교",
+			  "placeKey": "string",
+			  "address": "서울특별시 용산구 청파로47길 99",
+			  "category": "교육,학문>대학교",
+			  "link": "https://map.naver.com/v5/search/%EC%88%99%EB%AA%85%EC%97%AC%EC%9E%90%EB%8C%80%ED%95%99%EA%B5%90+%EC%A0%9C1%EC%BA%A0%ED%8D%BC%EC%8A%A4?c=37.545947,126.964578,15,0,0,0,dh",
+			  "telephone": "010-1234-1234",
+			  "coordinate": { "latitude": 0.1, "longitude": 0.1 },
+			  "imgUrls": ["string"]
+			}
 			""",
 		description = "/search API에서 반환된 결과 중 하나를 선택")
🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/review/application/dto/request/ReviewRequest.java
around lines 29 - 45, The @Schema(example = """...""") JSON in ReviewRequest is
not a valid top-level JSON object; update the example inside the triple-quoted
string in the ReviewRequest class's @Schema annotation to be a valid JSON object
(wrap the existing fields with top-level { ... }, ensure all keys/values are
properly quoted and commas are correct) so Swagger can parse and render it;
locate the @Schema on the ReviewRequest DTO and replace the current invalid
example with a syntactically valid JSON example.

@NotNull
@Valid
SavedPlaceInfo place
Comment on lines +29 to +48
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

@Schema 예시 JSON 형식이 올바르지 않습니다.

place 필드의 @Schema 예시가 JSON 객체의 외부 중괄호 {}가 누락되어 있어 Swagger UI에서 잘못된 형식으로 표시될 수 있습니다.

📝 수정 제안
 	@Schema(example =
 		"""
-			"placeName": "숙명여자대학교",
+			{
+			  "placeName": "숙명여자대학교",
 			      "placeKey": "string",
 			      "address": "서울특별시 용산구 청파로47길 99",
 			      "category": "교육,학문>대학교",
 			      "link": "https://map.naver.com/v5/search/...",
 			      "telephone": "010-1234-1234",
 			      "coordinate": {
 			        "latitude": 0.1,
 			        "longitude": 0.1
 			      },
 			      "imgUrls": [
 			        "string"
 			      ]
-			""",
+			}
+			""",
 		description = "/search API에서 반환된 결과 중 하나를 선택")
🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/review/application/dto/request/ReviewRequest.java
around lines 25 - 44, The @Schema example for the SavedPlaceInfo 'place' field
in ReviewRequest is missing the outer JSON braces; update the @Schema(example =
"""...""") value in class ReviewRequest (field: place) to wrap the shown content
with enclosing { ... } so the example becomes a valid JSON object, keeping the
existing multi-line string and description unchanged.


) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.campus.campus.domain.review.application.dto.response;

import java.util.List;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class CursorPageReviewResponse<ReviewResponse> {

private List<ReviewResponse> items;
private String nextCursorCreatedAt;
private Long nextCursorId;
private boolean hasNext;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.campus.campus.domain.review.application.dto.response;

import lombok.Builder;

@Builder
public record PlaceReviewRankResponse(
Long placeId,
String placeName,
String category,
String partnership,
String thumbnailUrl
// double distance
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.campus.campus.domain.review.application.dto.response;

import lombok.Builder;

@Builder
public record RankingScope(
String scope,
long rank //n번째 리뷰
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.campus.campus.domain.review.application.dto.response;

import lombok.Builder;

@Builder
public record ReviewCreateResponse(
ReviewResponse review,
ReviewCreateResult result,
ReviewRankingResponse ranking

) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.campus.campus.domain.review.application.dto.response;

import lombok.Builder;

@Builder
public record ReviewCreateResult(
boolean isFirstReviewOfPlace,
int userReviewCountOfPlace,
// int NumberOfUserStamp,
String message
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 메시지는 왜 넣으신건가요?

) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.campus.campus.domain.review.application.dto.response;

public record ReviewRankingResponse(
RankingScope major,
RankingScope college,
RankingScope school
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.campus.campus.domain.review.application.dto.response;

import java.time.LocalDate;
import java.util.List;

import lombok.Builder;

@Builder
public record ReviewResponse(
Long id,
Long userId,
String userName,
LocalDate createDate,
Long placeId,
String content,
double star,
List<String> imageUrls
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.campus.campus.domain.review.application.exception;

import org.springframework.http.HttpStatus;

import com.campus.campus.global.common.exception.ErrorCodeInterface;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ErrorCode implements ErrorCodeInterface {

REVIEW_NOT_FOUND(2700, HttpStatus.NOT_FOUND, "게시글을 찾을 수 없습니다."),
NOT_REVIEW_WRITER(2701, HttpStatus.FORBIDDEN, "작성자만 해당 작업을 수행할 수 있습니다.");

private final int code;
private final HttpStatus status;
private final String message;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.campus.campus.domain.review.application.exception;

import com.campus.campus.global.common.exception.ApplicationException;

public class NotUserWriterException extends ApplicationException {
public NotUserWriterException() {
super(ErrorCode.NOT_REVIEW_WRITER);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.campus.campus.domain.review.application.exception;

import com.campus.campus.global.common.exception.ApplicationException;

public class ReviewNotFoundException extends ApplicationException {
public ReviewNotFoundException() {
super(ErrorCode.REVIEW_NOT_FOUND);
}
}
Loading