Skip to content

Commit

Permalink
v1.5.0 (#301)
Browse files Browse the repository at this point in the history
  • Loading branch information
uwoobeat authored Feb 9, 2024
2 parents 9547a40 + 0eff937 commit 1906dcf
Show file tree
Hide file tree
Showing 27 changed files with 626 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public SocialLoginResponse socialLoginMember(IdTokenRequest request, OauthProvid

TokenPairResponse loginResponse = getLoginResponse(member);

return SocialLoginResponse.from(loginResponse);
return SocialLoginResponse.from(member, loginResponse);
}

private Member fetchOrCreate(OidcUser oidcUser) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package com.depromeet.domain.auth.dto.response;

import com.depromeet.domain.member.domain.Member;
import io.swagger.v3.oas.annotations.media.Schema;

public record SocialLoginResponse(
@Schema(description = "멤버 ID", defaultValue = "1") Long memberId,
@Schema(description = "엑세스 토큰", defaultValue = "accessToken") String accessToken,
@Schema(description = "리프레시 토큰", defaultValue = "refreshToken") String refreshToken,
@Schema(description = "게스트 여부", defaultValue = "false") boolean isGuest) {

public static SocialLoginResponse from(TokenPairResponse tokenPairResponse) {
public static SocialLoginResponse from(Member member, TokenPairResponse tokenPairResponse) {
return new SocialLoginResponse(
tokenPairResponse.accessToken(), tokenPairResponse.refreshToken(), false);
member.getId(),
tokenPairResponse.accessToken(),
tokenPairResponse.refreshToken(),
false);
}
}
34 changes: 34 additions & 0 deletions src/main/java/com/depromeet/domain/feed/api/FeedController.java
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);
}
}
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;
}
}
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());
}
}
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
import com.depromeet.domain.follow.application.FollowService;
import com.depromeet.domain.follow.dto.request.FollowCreateRequest;
import com.depromeet.domain.follow.dto.request.FollowDeleteRequest;
import com.depromeet.domain.follow.dto.response.FollowFindMeInfoResponse;
import com.depromeet.domain.follow.dto.response.FollowFindTargetInfoResponse;
import com.depromeet.domain.follow.dto.response.FollowListResponse;
import com.depromeet.domain.follow.dto.response.MemberFollowedResponse;
import com.depromeet.domain.follow.dto.response.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
Expand Down Expand Up @@ -63,4 +60,10 @@ public List<MemberFollowedResponse> followedUserFindAll() {
public FollowListResponse followList(@PathVariable Long targetId) {
return followService.findFollowList(targetId);
}

@DeleteMapping({"/{targetId}"})
@Operation(summary = "팔로워 삭제", description = "내 팔로워 목록 중 targetId로 팔로워를 삭제합니다.")
public ResponseEntity<FollowerDeletedResponse> followerDelete(@PathVariable Long targetId) {
return ResponseEntity.ok(followService.deleteFollower(targetId));
}
}
Loading

0 comments on commit 1906dcf

Please sign in to comment.