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
59 changes: 59 additions & 0 deletions src/main/java/com/opus/opus/docs/asciidoc/member.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,62 @@ include::{snippets}/update-profile-visibility/http-response.adoc[]

.Request Fields
include::{snippets}/update-profile-visibility/request-fields.adoc[]

== `GET`: 나의 댓글 조회

NOTE: 사용자가 작성한 전체 댓글 목록을 필터링/정렬/페이지네이션과 함께 조회합니다.

.HTTP Request
include::{snippets}/get-my-comments/http-request.adoc[]

.HTTP Response
include::{snippets}/get-my-comments/http-response.adoc[]

.Query Parameters
include::{snippets}/get-my-comments/query-parameters.adoc[]

.Response Body's Fields
include::{snippets}/get-my-comments/response-fields.adoc[]

=== ⚠️ 실패 케이스

.❌ Case 1: 날짜 범위 불완전 (startDate와 endDate를 모두 입력해주세요)

[%collapsible]

====

include::{snippets}/get-my-comments-fail-date-range/http-request.adoc[]

include::{snippets}/get-my-comments-fail-date-range/http-response.adoc[]

====

== `GET`: 나의 좋아요 미리보기

NOTE: 나의 활동 탭에서 보여줄 최근 좋아요한 프로젝트 미리보기를 조회합니다. createdAt 최신순으로 고정 3개를 반환합니다.

.HTTP Request
include::{snippets}/get-my-like-preview/http-request.adoc[]

.HTTP Response
include::{snippets}/get-my-like-preview/http-response.adoc[]

.Response Body's Fields
include::{snippets}/get-my-like-preview/response-fields.adoc[]

== `GET`: 나의 좋아요 전체 조회

NOTE: 사용자가 좋아요한 전체 프로젝트 목록을 필터링/정렬/페이지네이션과 함께 조회합니다.

.HTTP Request
include::{snippets}/get-my-liked-projects/http-request.adoc[]

.HTTP Response
include::{snippets}/get-my-liked-projects/http-response.adoc[]

.Query Parameters
include::{snippets}/get-my-liked-projects/query-parameters.adoc[]

.Response Body's Fields
include::{snippets}/get-my-liked-projects/response-fields.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public ResponseEntity<List<ContestCategoryResponse>> getAllContestCategories() {
return ResponseEntity.ok(response);
}

@GetMapping("/sidebar")
@GetMapping("/categories/sidebar")
public ResponseEntity<List<SidebarResponse>> getSidebar() {
final List<SidebarResponse> response = contestCategoryQueryService.getSidebar();
return ResponseEntity.ok(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,25 +82,6 @@ public class ContestQueryService {
private final TeamContestAwardConvenience teamContestAwardConvenience;
private final FileConvenience fileConvenience;

private static List<ContestRankingResponse> applyDenseRanking(List<TeamRankingResult> votesPerTeam) {
List<ContestRankingResponse> responseList = new ArrayList<>();
int curRank = 0; // 현재 순위
long prevCount = -1; // 이전 팀 투표 수
for (TeamRankingResult result : votesPerTeam) {
// 이전 팀과 투표 수가 다르면 순위 증가, 같으면 순위 유지
if (prevCount != result.voteCount()) {
curRank++;
}
prevCount = result.voteCount();

responseList.add(
new ContestRankingResponse(curRank, result.teamId(), result.teamName(), result.projectName(),
result.trackName(), result.voteCount()));
}

return responseList;
}

private static List<ContestRankingResponse> applyRanking(List<TeamRankingResult> votesPerTeam) {
List<ContestRankingResponse> responseList = new ArrayList<>();

Comment thread
pykido marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@
import com.opus.opus.modules.member.application.dto.response.EmailFindResponse;
import com.opus.opus.modules.member.application.dto.response.MyProjectResponse;
import com.opus.opus.modules.member.domain.dao.MyVoteResponse;
import com.opus.opus.modules.member.application.dto.response.MyCommentResponse;
import com.opus.opus.modules.member.application.dto.response.MyLikePreviewResponse;
import com.opus.opus.modules.member.application.dto.response.MyLikedProjectResponse;
import com.opus.opus.modules.member.application.dto.response.SignInResponse;
import jakarta.servlet.http.HttpServletResponse;
import com.opus.opus.modules.member.domain.Member;
import com.opus.opus.modules.team.application.dto.ImageResponse;
import jakarta.validation.Valid;
import java.util.List;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.Resource;
import org.springframework.data.domain.Page;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
Expand All @@ -38,6 +44,7 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
Expand Down Expand Up @@ -188,4 +195,37 @@ public ResponseEntity<List<MyVoteResponse>> getMyVotes(@LoginMember final Member
final List<MyVoteResponse> responses = memberQueryService.getMyVotes(member.getId());
return ResponseEntity.ok(responses);
}

@GetMapping("/members/me/comments")
@Secured({"ROLE_회원", "ROLE_관리자"})
public ResponseEntity<Page<MyCommentResponse>> getMyComments(@LoginMember final Member member,
@RequestParam(defaultValue = "latest") final String sort,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") final LocalDate startDate,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") final LocalDate endDate,
@RequestParam(defaultValue = "0") final int page,
@RequestParam(defaultValue = "10") final int size) {
final Page<MyCommentResponse> response = memberQueryService.getMyComments(member.getId(), sort, startDate, endDate, page, size);
return ResponseEntity.ok(response);
}

@GetMapping("/members/me/likes/preview")
@Secured({"ROLE_회원", "ROLE_관리자"})
public ResponseEntity<List<MyLikePreviewResponse>> getMyLikePreview(@LoginMember final Member member) {
Comment thread
pykido marked this conversation as resolved.
final List<MyLikePreviewResponse> response = memberQueryService.getMyLikePreview(member.getId());
return ResponseEntity.ok(response);
}

@GetMapping("/members/me/likes")
@Secured({"ROLE_회원", "ROLE_관리자"})
public ResponseEntity<Page<MyLikedProjectResponse>> getMyLikedProjects(@LoginMember final Member member,
@RequestParam(defaultValue = "latest") final String sort,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") final LocalDate startDate,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") final LocalDate endDate,
@RequestParam(required = false) final Long categoryId,
@RequestParam(required = false) final Long contestId,
Comment thread
pykido marked this conversation as resolved.
@RequestParam(defaultValue = "0") final int page,
@RequestParam(defaultValue = "12") final int size) {
Comment thread
pykido marked this conversation as resolved.
final Page<MyLikedProjectResponse> response = memberQueryService.getMyLikedProjects(member.getId(), sort, startDate, endDate, categoryId, contestId, page, size);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,43 @@
import com.opus.opus.global.util.FileStorageUtil;
import com.opus.opus.modules.file.application.convenience.FileConvenience;
import com.opus.opus.modules.file.domain.File;
import static com.opus.opus.modules.member.exception.MemberExceptionType.INVALID_DATE_ORDER;
import static com.opus.opus.modules.member.exception.MemberExceptionType.INVALID_DATE_RANGE;
import static com.opus.opus.modules.member.exception.MemberExceptionType.INVALID_SORT_VALUE;
import static org.springframework.data.domain.Sort.Direction.ASC;
import static org.springframework.data.domain.Sort.Direction.DESC;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;

import com.opus.opus.modules.member.application.convenience.MemberConvenience;
import com.opus.opus.modules.member.application.dto.response.AccountInfoResponse;
import com.opus.opus.modules.member.application.dto.request.SearchConditionRequest;
import com.opus.opus.modules.member.application.dto.response.EmailFindResponse;
import com.opus.opus.modules.member.application.dto.response.MyProjectResponse;
import com.opus.opus.modules.member.domain.dao.MyVoteResponse;
import com.opus.opus.modules.member.application.dto.response.MyCommentResponse;
import com.opus.opus.modules.member.application.dto.response.MyLikePreviewResponse;
import com.opus.opus.modules.member.application.dto.response.MyLikedProjectResponse;
import com.opus.opus.modules.member.domain.Member;
import com.opus.opus.modules.team.domain.dao.MyProjectFlatResult;
import com.opus.opus.modules.team.domain.dao.TeamMemberRepository;
import com.opus.opus.modules.team.domain.dao.TeamVoteRepository;
import java.util.LinkedHashMap;
import java.util.List;
import com.opus.opus.modules.member.exception.MemberException;
import com.opus.opus.modules.team.domain.dao.TeamCommentRepository;
import com.opus.opus.modules.team.domain.dao.TeamLikeRepository;
import java.time.LocalDate;
import java.time.LocalDateTime;
import com.opus.opus.modules.team.application.dto.ImageResponse;
import lombok.RequiredArgsConstructor;
import org.antlr.v4.runtime.misc.Pair;
import org.springframework.core.io.Resource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -32,9 +51,13 @@
@Transactional(readOnly = true)
public class MemberQueryService {

private static final int LIKE_PREVIEW_SIZE = 3;

private final MemberConvenience memberConvenience;
private final FileConvenience fileConvenience;
private final FileStorageUtil fileStorageUtil;
private final TeamCommentRepository teamCommentRepository;
private final TeamLikeRepository teamLikeRepository;

private final TeamMemberRepository teamMemberRepository;
private final TeamVoteRepository teamVoteRepository;
Expand Down Expand Up @@ -66,4 +89,58 @@ public List<MyProjectResponse> getMyProjects(final Long memberId) {
public List<MyVoteResponse> getMyVotes(final Long memberId) {
return teamVoteRepository.findMyVotes(memberId);
}

public Page<MyCommentResponse> getMyComments(final Long memberId, final String sort,
final LocalDate startDate, final LocalDate endDate,
final int page, final int size) {
validateDateRange(startDate, endDate);
final SearchConditionRequest condition = createSearchCondition(sort, startDate, endDate, page, size);
return teamCommentRepository.findMyComments(memberId, condition.startDateTime(), condition.endDateTime(), condition.pageable())
.map(MyCommentResponse::from);
}

public List<MyLikePreviewResponse> getMyLikePreview(final Long memberId) {
final Pageable pageable = PageRequest.of(0, LIKE_PREVIEW_SIZE);
return teamLikeRepository.findMyRecentLikedProjects(memberId, pageable).stream()
.map(MyLikePreviewResponse::from)
.toList();
}

public Page<MyLikedProjectResponse> getMyLikedProjects(final Long memberId, final String sort,
final LocalDate startDate, final LocalDate endDate,
final Long categoryId, final Long contestId,
final int page, final int size) {
validateDateRange(startDate, endDate);
final SearchConditionRequest searchCondition = createSearchCondition(sort, startDate, endDate, page, size);
return teamLikeRepository.findMyLikedProjects(memberId, searchCondition.startDateTime(), searchCondition.endDateTime(), categoryId, contestId, searchCondition.pageable())
.map(MyLikedProjectResponse::from);
}

private SearchConditionRequest createSearchCondition(final String sort, final LocalDate startDate, final LocalDate endDate, final int page, final int size) {
final Sort.Direction direction = parseSortDirection(sort);
final Pageable pageable = PageRequest.of(page, size, Sort.by(direction, "createdAt"));
final LocalDateTime startDateTime = startDate != null ? startDate.atStartOfDay() : null;
final LocalDateTime endDateTime = endDate != null ? endDate.plusDays(1).atStartOfDay() : null;
return new SearchConditionRequest(pageable, startDateTime, endDateTime);
}

private Sort.Direction parseSortDirection(final String sort) {
if (sort == null || "latest".equalsIgnoreCase(sort)) {
return DESC;
}
if ("oldest".equalsIgnoreCase(sort)) {
return ASC;
}
throw new MemberException(INVALID_SORT_VALUE);
}

private void validateDateRange(final LocalDate startDate, final LocalDate endDate) {
if ((startDate == null) != (endDate == null)) {
throw new MemberException(INVALID_DATE_RANGE);
}
if (startDate != null && startDate.isAfter(endDate)) {
throw new MemberException(INVALID_DATE_ORDER);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.opus.opus.modules.member.application.dto.request;

import java.time.LocalDateTime;
import org.springframework.data.domain.Pageable;

public record SearchConditionRequest(
Pageable pageable,
LocalDateTime startDateTime,
LocalDateTime endDateTime
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.opus.opus.modules.member.application.dto.response;

import com.opus.opus.modules.team.domain.dao.projection.MyCommentProjection;
import java.time.LocalDateTime;

public record MyCommentResponse(
CommentInfo comment,
ProjectInfo project
) {

public static MyCommentResponse from(final MyCommentProjection result) {
return new MyCommentResponse(
new CommentInfo(
result.getCommentId(),
result.getContent(),
result.getCreatedAt(),
result.getMemberName()
),
new ProjectInfo(
result.getContestId(),
result.getContestName(),
result.getCategoryName(),
result.getTrackName(),
result.getTeamId(),
result.getTeamName(),
result.getProjectName(),
result.getOverview()
)
);
}

public record CommentInfo(
Long commentId,
String content,
LocalDateTime createdAt,
String memberName
) {
}

public record ProjectInfo(
Long contestId,
String contestName,
String categoryName,
String trackName,
Long teamId,
String teamName,
String projectName,
String overview
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.opus.opus.modules.member.application.dto.response;

import com.opus.opus.modules.team.domain.dao.MyLikedProjectResult;

public record MyLikePreviewResponse(
Long teamId,
String teamName,
Long contestId,
String projectName,
String contestName
) {

public static MyLikePreviewResponse from(final MyLikedProjectResult result) {
return new MyLikePreviewResponse(
result.teamId(),
result.teamName(),
result.contestId(),
result.projectName(),
result.contestName()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.opus.opus.modules.member.application.dto.response;

import com.opus.opus.modules.team.domain.dao.MyLikedProjectResult;

public record MyLikedProjectResponse(
Long teamId,
String teamName,
String projectName,
Long contestId,
String contestName
) {

public static MyLikedProjectResponse from(final MyLikedProjectResult result) {
return new MyLikedProjectResponse(
result.teamId(),
result.teamName(),
result.projectName(),
result.contestId(),
result.contestName()
);
}
}
Loading