-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: feed 탭 리스트 구현 * feat: 프로필 피드 탭 구현 * feat: 피드 탭 닉네임 추가 * feat: 피드 탭 v2 * fix: 마이프로필 피드 공개 여부 예외 처리 * refactor: FeedOneResponse class -> record * refactor: feed/me API 개선 * refactor: 피드 탭 리스트 limit 100 제한 * fix: 리뷰 반영 * fix: targetId -> memberId * fix: 타인 프로필 공개여부 조건 추가 * fix: Transactional 추가 * fix: getter id가 아닌 member 프록시 객체 리스트로 수정 * fix: @uwoobeat 리뷰 반영 * fix: 주석 삭제
- Loading branch information
Showing
10 changed files
with
337 additions
and
0 deletions.
There are no files selected for viewing
34 changes: 34 additions & 0 deletions
34
src/main/java/com/depromeet/domain/feed/api/FeedController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.depromeet.domain.feed.api; | ||
|
||
import com.depromeet.domain.feed.application.FeedService; | ||
import com.depromeet.domain.feed.dto.response.FeedOneByProfileResponse; | ||
import com.depromeet.domain.feed.dto.response.FeedOneResponse; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@Tag(name = "6. [피드]", description = "피드 관련 API입니다.") | ||
@RequestMapping("/feed") | ||
@RequiredArgsConstructor | ||
public class FeedController { | ||
|
||
private final FeedService feedService; | ||
|
||
@Operation(summary = "피드 탭", description = "피드 탭을 조회합니다.") | ||
@GetMapping("/me") | ||
public List<FeedOneResponse> feedFindAll() { | ||
return feedService.findAllFeed(); | ||
} | ||
|
||
@Operation(summary = "프로필 피드", description = "피드 탭을 조회합니다.") | ||
@GetMapping("/{memberId}") | ||
public List<FeedOneByProfileResponse> feedFindAllByTargetId(@PathVariable Long memberId) { | ||
return feedService.findAllFeedByTargetId(memberId); | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
src/main/java/com/depromeet/domain/feed/application/FeedService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package com.depromeet.domain.feed.application; | ||
|
||
import com.depromeet.domain.feed.dto.response.FeedOneByProfileResponse; | ||
import com.depromeet.domain.feed.dto.response.FeedOneResponse; | ||
import com.depromeet.domain.follow.dao.MemberRelationRepository; | ||
import com.depromeet.domain.follow.domain.MemberRelation; | ||
import com.depromeet.domain.member.domain.Member; | ||
import com.depromeet.domain.mission.domain.MissionVisibility; | ||
import com.depromeet.domain.missionRecord.dao.MissionRecordRepository; | ||
import com.depromeet.domain.missionRecord.domain.MissionRecord; | ||
import com.depromeet.global.util.MemberUtil; | ||
import com.depromeet.global.util.SecurityUtil; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
// TODO: Redis 사용해서 캐싱 작업 필요 | ||
@Service | ||
@RequiredArgsConstructor | ||
@Transactional | ||
public class FeedService { | ||
private final MemberUtil memberUtil; | ||
private final MissionRecordRepository missionRecordRepository; | ||
private final MemberRelationRepository memberRelationRepository; | ||
private final SecurityUtil securityUtil; | ||
|
||
@Transactional(readOnly = true) | ||
public List<FeedOneResponse> findAllFeed() { | ||
final Member currentMember = memberUtil.getCurrentMember(); | ||
List<Member> members = | ||
memberRelationRepository.findAllBySourceId(currentMember.getId()).stream() | ||
.map(MemberRelation::getTarget) | ||
.collect(Collectors.toList()); | ||
members.add(currentMember); | ||
|
||
return missionRecordRepository.findFeedAll(members); | ||
} | ||
|
||
@Transactional(readOnly = true) | ||
public List<FeedOneByProfileResponse> findAllFeedByTargetId(Long targetId) { | ||
final Long sourceId = securityUtil.getCurrentMemberId(); | ||
|
||
if (isMyFeedRequired(targetId, sourceId)) { | ||
return findFeedByOtherMember(sourceId, targetId); | ||
} | ||
return findFeedByCurrentMember(sourceId); | ||
} | ||
|
||
private boolean isMyFeedRequired(Long targetId, Long sourceId) { | ||
return !targetId.equals(sourceId); | ||
} | ||
|
||
private List<FeedOneByProfileResponse> findFeedByOtherMember(Long sourceId, Long targetId) { | ||
final Member targetMember = memberUtil.getMemberByMemberId(targetId); | ||
|
||
// 팔로우 관계 true: visibility.FOLLOW and ALL, false: visibility.ALL only | ||
boolean isMemberRelationExistsWithMe = | ||
memberRelationRepository.existsBySourceIdAndTargetId( | ||
sourceId, targetMember.getId()); | ||
List<MissionVisibility> visibilities = | ||
determineVisibilityConditionsByRelationsWithMe(isMemberRelationExistsWithMe); | ||
List<MissionRecord> feedAllByMemberId = | ||
missionRecordRepository.findFeedAllByMemberId(targetId, visibilities); | ||
return extractFeedResponses(feedAllByMemberId); | ||
} | ||
|
||
private List<FeedOneByProfileResponse> findFeedByCurrentMember(Long sourceId) { | ||
List<MissionVisibility> visibilities = | ||
List.of(MissionVisibility.NONE, MissionVisibility.FOLLOWER, MissionVisibility.ALL); | ||
List<MissionRecord> feedAllByMemberId = | ||
missionRecordRepository.findFeedAllByMemberId(sourceId, visibilities); | ||
return extractFeedResponses(feedAllByMemberId); | ||
} | ||
|
||
private List<FeedOneByProfileResponse> extractFeedResponses(List<MissionRecord> records) { | ||
return records.stream().map(FeedOneByProfileResponse::of).toList(); | ||
} | ||
|
||
private List<MissionVisibility> determineVisibilityConditionsByRelationsWithMe( | ||
boolean isMemberRelationExistsWithMe) { | ||
List<MissionVisibility> visibilities = new ArrayList<>(); | ||
visibilities.add(MissionVisibility.ALL); | ||
|
||
if (isMemberRelationExistsWithMe) { | ||
visibilities.add(MissionVisibility.FOLLOWER); | ||
} | ||
return visibilities; | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
src/main/java/com/depromeet/domain/feed/dto/response/FeedOneByProfileResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.depromeet.domain.feed.dto.response; | ||
|
||
import com.depromeet.domain.missionRecord.domain.MissionRecord; | ||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import java.time.Duration; | ||
import java.time.LocalDateTime; | ||
|
||
public record FeedOneByProfileResponse( | ||
@Schema(description = "미션 ID", defaultValue = "1") Long missionId, | ||
@Schema(description = "미션 기록 ID", defaultValue = "1") Long recordId, | ||
@Schema(description = "미션 이름", defaultValue = "default name") String name, | ||
@Schema( | ||
description = "미션 기록 인증 사진 Url", | ||
defaultValue = "https://image.10mm.today/default.png") | ||
String recordImageUrl, | ||
@Schema(description = "미션 수행한 시간", defaultValue = "21") long duration, | ||
@Schema(description = "미션 시작한 지 N일차", defaultValue = "3") long sinceDay, | ||
@JsonFormat( | ||
shape = JsonFormat.Shape.STRING, | ||
pattern = "yyyy-MM-dd HH:mm:ss", | ||
timezone = "Asia/Seoul") | ||
@Schema( | ||
description = "미션 기록 시작 시간", | ||
defaultValue = "2023-01-06 00:00:00", | ||
type = "string") | ||
LocalDateTime startedAt, | ||
@JsonFormat( | ||
shape = JsonFormat.Shape.STRING, | ||
pattern = "yyyy-MM-dd HH:mm:ss", | ||
timezone = "Asia/Seoul") | ||
@Schema( | ||
description = "미션 기록 종료 시간", | ||
defaultValue = "2024-01-20 00:34:00", | ||
type = "string") | ||
LocalDateTime finishedAt) { | ||
|
||
public static FeedOneByProfileResponse of(MissionRecord record) { | ||
return new FeedOneByProfileResponse( | ||
record.getMission().getId(), | ||
record.getId(), | ||
record.getMission().getName(), | ||
record.getImageUrl(), | ||
record.getDuration().toMinutes(), | ||
Duration.between(record.getStartedAt(), LocalDateTime.now()).toDays() + 1, | ||
record.getStartedAt(), | ||
record.getFinishedAt()); | ||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
src/main/java/com/depromeet/domain/feed/dto/response/FeedOneResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package com.depromeet.domain.feed.dto.response; | ||
|
||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
import com.querydsl.core.annotations.QueryProjection; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import java.time.Duration; | ||
import java.time.LocalDateTime; | ||
|
||
public record FeedOneResponse( | ||
@Schema(description = "작성자 ID", defaultValue = "1") Long memberId, | ||
@Schema(description = "작성자 닉네임", defaultValue = "default name") String nickname, | ||
@Schema(description = "작성자 프로필 이미지", defaultValue = "https://image.10mm.today/default.png") | ||
String profileImage, | ||
@Schema(description = "미션 ID", defaultValue = "1") Long missionId, | ||
@Schema(description = "미션 이름", defaultValue = "default name") String name, | ||
@Schema(description = "미션 기록 ID", defaultValue = "1") Long recordId, | ||
@Schema(description = "미션 일지 내용", defaultValue = "default remark") String remark, | ||
@Schema( | ||
description = "미션 기록 인증 사진 Url", | ||
defaultValue = "https://image.10mm.today/default.png") | ||
String recordImageUrl, | ||
@Schema(description = "미션 수행한 시간", defaultValue = "21") long duration, | ||
@Schema(description = "미션 시작한 지 N일차", defaultValue = "3") long sinceDay, | ||
@JsonFormat( | ||
shape = JsonFormat.Shape.STRING, | ||
pattern = "yyyy-MM-dd HH:mm:ss", | ||
timezone = "Asia/Seoul") | ||
@Schema( | ||
description = "미션 기록 시작 시간", | ||
defaultValue = "2024-01-06 00:00:00", | ||
type = "string") | ||
LocalDateTime startedAt, | ||
@JsonFormat( | ||
shape = JsonFormat.Shape.STRING, | ||
pattern = "yyyy-MM-dd HH:mm:ss", | ||
timezone = "Asia/Seoul") | ||
@Schema( | ||
description = "미션 기록 종료 시간", | ||
defaultValue = "2024-01-20 00:34:00", | ||
type = "string") | ||
LocalDateTime finishedAt) { | ||
@QueryProjection | ||
public FeedOneResponse( | ||
Long memberId, | ||
String nickname, | ||
String profileImage, | ||
Long missionId, | ||
String name, | ||
Long recordId, | ||
String remark, | ||
String recordImageUrl, | ||
Duration duration, | ||
LocalDateTime startedAt, | ||
LocalDateTime finishedAt) { | ||
this( | ||
memberId, | ||
nickname, | ||
profileImage, | ||
missionId, | ||
name, | ||
recordId, | ||
remark, | ||
recordImageUrl, | ||
duration.toMinutes(), | ||
Duration.between(startedAt, LocalDateTime.now()).toDays() + 1, | ||
startedAt, | ||
finishedAt); | ||
} | ||
|
||
public static FeedOneResponse of( | ||
Long memberId, | ||
String nickname, | ||
String profileImage, | ||
Long missionId, | ||
String name, | ||
Long recordId, | ||
String remark, | ||
String recordImageUrl, | ||
Duration duration, | ||
LocalDateTime startedAt, | ||
LocalDateTime finishedAt) { | ||
return new FeedOneResponse( | ||
memberId, | ||
nickname, | ||
profileImage, | ||
missionId, | ||
name, | ||
recordId, | ||
remark, | ||
recordImageUrl, | ||
duration.toMinutes(), | ||
Duration.between(startedAt, LocalDateTime.now()).toDays() + 1, | ||
startedAt, | ||
finishedAt); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
src/main/java/com/depromeet/domain/mission/dao/MissionRepositoryImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.