Skip to content

Commit

Permalink
feat: 종료미션 보관함 API 구현 (#178)
Browse files Browse the repository at this point in the history
* feat: 종료미션 중간 커밋 2024.01.15

* feat: 종료미션 보관함 v1

* fix: long casting 이슈 해결

* feat: 2024.01.18 임시 커밋(스케줄러 작업 이후 진행)

* fix: spotlessApply

* fix: durationStatus 조건으로 변경

* fix: 종료미션 조건 수정

* fix: spotlessApply

* feat: 종료미션 slice -> list

* feat: 종료미션 달성률 포함 추가 및 개선

* fix: 날짜 between 계산 수정
  • Loading branch information
char-yb authored Feb 8, 2024
1 parent 33daf3a commit ca85e55
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.depromeet.domain.mission.application.MissionService;
import com.depromeet.domain.mission.dto.request.MissionCreateRequest;
import com.depromeet.domain.mission.dto.request.MissionUpdateRequest;
import com.depromeet.domain.mission.dto.response.FinishedMissionResponse;
import com.depromeet.domain.mission.dto.response.FollowMissionFindAllResponse;
import com.depromeet.domain.mission.dto.response.MissionCreateResponse;
import com.depromeet.domain.mission.dto.response.MissionFindAllResponse;
Expand Down Expand Up @@ -66,6 +67,12 @@ public MissionRecordSummaryResponse missionRecordFindSummary() {
return missionService.findSummaryMissionRecord();
}

@Operation(summary = "종료미션 보관함", description = "종료된 미션 리스트를 조회합니다.")
@GetMapping("/finished")
public List<FinishedMissionResponse> missionFindAllFinished() {
return missionService.findAllFinishedMission();
}

@Operation(summary = "번개 스택 조회", description = "완료한 미션 대상으로 번개 스택을 조회합니다.")
@GetMapping("/symbol/{memberId}")
public MissionSymbolStackResponse missionSymbolStackFind(@PathVariable Long memberId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.depromeet.global.error.exception.CustomException;
import com.depromeet.global.error.exception.ErrorCode;
import com.depromeet.global.util.MemberUtil;
import com.depromeet.global.util.SecurityUtil;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
Expand All @@ -35,6 +36,7 @@ public class MissionService {
private final MissionRecordTtlRepository missionRecordTtlRepository;
private final MemberRelationRepository memberRelationRepository;
private final MemberUtil memberUtil;
private final SecurityUtil securityUtil;

public MissionCreateResponse createMission(MissionCreateRequest missionCreateRequest) {
Mission mission = createMissionEntity(missionCreateRequest);
Expand Down Expand Up @@ -116,8 +118,8 @@ public MissionRecordSummaryResponse findSummaryMissionRecord() {
missions.stream()
.mapToLong(
mission ->
Duration.between(mission.getStartedAt().minusDays(1), today)
.toDays())
Duration.between(mission.getStartedAt(), today).toDays()
+ 1)
.sum();
// Duration을 초로 바꾸고 합산
long sumDuration =
Expand Down Expand Up @@ -201,6 +203,39 @@ public MissionSymbolStackResponse findMissionSymbolStack(Long memberId) {
return MissionSymbolStackResponse.of(symbolStack);
}

@Transactional(readOnly = true)
public List<FinishedMissionResponse> findAllFinishedMission() {
Long currentMemberId = securityUtil.getCurrentMemberId();

List<Mission> finishedMissions = missionRepository.findAllFinishedMission(currentMemberId);

return finishedMissions.stream()
.map(
mission -> {
long totalMissionDay =
Duration.between(
mission.getStartedAt(),
mission.getFinishedAt())
.toDays()
+ 1;
long completeCount =
mission.getMissionRecords().stream()
.filter(
missionRecord ->
missionRecord
.getUploadStatus()
.equals(
ImageUploadStatus
.COMPLETE))
.count();

return FinishedMissionResponse.of(
mission,
calculateMissionAttainRate(completeCount, totalMissionDay));
})
.toList();
}

public MissionUpdateResponse updateMission(
MissionUpdateRequest missionUpdateRequest, Long missionId) {
Mission mission =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface MissionRepositoryCustom {
List<Mission> findMissionsWithRecordsByRelations(Long memberId, boolean existsMemberRelations);

void updateFinishedDurationStatus(LocalDateTime today);

List<Mission> findAllFinishedMission(Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.stereotype.Repository;

@Repository
Expand Down Expand Up @@ -70,6 +73,18 @@ public void updateFinishedDurationStatus(LocalDateTime today) {
.execute();
}

@Override
public List<Mission> findAllFinishedMission(Long memberId) {
return jpaQueryFactory
.selectFrom(mission)
.leftJoin(mission.missionRecords, missionRecord)
.fetchJoin()
.where(memberIdEq(memberId), durationStatusFinishedEq())
.orderBy(mission.finishedAt.desc())
.fetch();
}

// 미션의 사용자 id 조건 검증 메서드
private BooleanExpression memberIdEq(Long memberId) {
return memberId == null ? null : mission.member.id.eq(memberId);
}
Expand All @@ -83,4 +98,27 @@ private BooleanExpression visibilityByRelations(boolean existsRelations) {
private BooleanExpression durationStatusInProgress() {
return mission.durationStatus.in(DurationStatus.IN_PROGRESS);
}

// lastId보다 작은 미션 id 찾는 조건 메서드 (lastId 가 있다면 마지막 요청)
private BooleanExpression ltMissionId(Long lastId) {
return lastId == null ? null : mission.id.lt(lastId);
}

private BooleanExpression durationStatusFinishedEq() {
return mission.durationStatus.eq(DurationStatus.FINISHED);
}

// 무한 스크롤 방식 처리하는 메서드
private Slice<Mission> checkLastPage(int size, List<Mission> result) {

boolean hasNext = false;

// 조회한 결과 개수가 요청한 페이지 사이즈보다 크면 뒤에 더 있음, next = true
if (result.size() > size) {
hasNext = true;
result.remove(size);
}
Pageable pageable = Pageable.unpaged();
return new SliceImpl<>(result, pageable, hasNext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.depromeet.domain.mission.dto.response;

import com.depromeet.domain.mission.domain.Mission;
import com.depromeet.domain.mission.domain.MissionCategory;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;

public record FinishedMissionResponse(
@Schema(description = "미션 ID", defaultValue = "1") Long missionId,
@Schema(description = "미션 이름", defaultValue = "default name") String name,
@Schema(description = "미션 내용", defaultValue = "default content") String content,
@Schema(description = "미션 카테고리", defaultValue = "STUDY") MissionCategory category,
@Schema(description = "미션 달성률", defaultValue = "1.1") double missionAttainRate,
@JsonFormat(
shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd HH:mm:ss",
timezone = "Asia/Seoul")
@Schema(
description = "미션 시작 시간",
defaultValue = "2024-01-01 00:34: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-15 00:34:00",
type = "string")
LocalDateTime finishedAt) {
public static FinishedMissionResponse of(Mission mission, double missionAttainRate) {
return new FinishedMissionResponse(
mission.getId(),
mission.getName(),
mission.getContent(),
mission.getCategory(),
missionAttainRate,
mission.getStartedAt(),
mission.getFinishedAt());
}
}

0 comments on commit ca85e55

Please sign in to comment.