Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.7.0 #320

Merged
merged 6 commits into from
Feb 12, 2024
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.depromeet.domain.follow.application;

import static com.depromeet.domain.common.constants.PushNotificationConstants.*;
import static com.depromeet.global.common.constants.PushNotificationConstants.*;

import com.depromeet.domain.follow.dao.MemberRelationRepository;
import com.depromeet.domain.follow.domain.MemberRelation;
Expand All @@ -13,10 +13,10 @@
import com.depromeet.domain.mission.domain.Mission;
import com.depromeet.domain.missionRecord.domain.ImageUploadStatus;
import com.depromeet.domain.missionRecord.domain.MissionRecord;
import com.depromeet.domain.notification.application.FcmService;
import com.depromeet.domain.notification.dao.NotificationRepository;
import com.depromeet.domain.notification.domain.Notification;
import com.depromeet.domain.notification.domain.NotificationType;
import com.depromeet.global.config.fcm.FcmService;
import com.depromeet.global.error.exception.CustomException;
import com.depromeet.global.error.exception.ErrorCode;
import com.depromeet.global.util.MemberUtil;
Expand All @@ -38,6 +38,7 @@ public class FollowService {

public void createFollow(FollowCreateRequest request) {
final Member currentMember = memberUtil.getCurrentMember();
validateSelfFollow(currentMember.getId(), request.targetId());
Member targetMember = getTargetMember(request.targetId());

boolean existMemberRelation =
Expand Down Expand Up @@ -177,6 +178,12 @@ && isToday(record.getStartedAt()))
return result;
}

private void validateSelfFollow(Long expectedId, Long actualId) {
if (expectedId.equals(actualId)) {
throw new CustomException(ErrorCode.FOLLOW_SELF_NOT_ALLOWED);
}
}

private boolean isToday(LocalDateTime dateTime) {
LocalDateTime today = LocalDateTime.now();
return dateTime.toLocalDate().isEqual(today.toLocalDate());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.depromeet.domain.member.application;

import static com.depromeet.domain.common.constants.PushNotificationConstants.*;
import static com.depromeet.global.common.constants.PushNotificationConstants.*;

import com.depromeet.domain.auth.dao.RefreshTokenRepository;
import com.depromeet.domain.auth.dto.request.UsernameCheckRequest;
Expand All @@ -16,7 +16,7 @@
import com.depromeet.domain.member.dto.response.MemberFindOneResponse;
import com.depromeet.domain.member.dto.response.MemberSearchResponse;
import com.depromeet.domain.member.dto.response.MemberSocialInfoResponse;
import com.depromeet.global.config.fcm.FcmService;
import com.depromeet.domain.notification.application.FcmService;
import com.depromeet.global.error.exception.CustomException;
import com.depromeet.global.error.exception.ErrorCode;
import com.depromeet.global.util.MemberUtil;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.depromeet.domain.member.dto;

import com.depromeet.domain.member.domain.Member;

public record MemberProfileDto(Long memberId, String nickname, String profileImageUrl) {

public static MemberProfileDto from(Member member) {
return new MemberProfileDto(
member.getId(),
member.getProfile().getNickname(),
member.getProfile().getProfileImageUrl());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,17 @@
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;
import com.depromeet.domain.mission.dto.response.MissionFindResponse;
import com.depromeet.domain.mission.dto.response.MissionSymbolStackResponse;
import com.depromeet.domain.mission.dto.response.MissionUpdateResponse;
import com.depromeet.domain.mission.dto.response.*;
import com.depromeet.domain.missionRecord.dto.response.MissionRecordSummaryResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@Tag(name = "2. [미션]", description = "미션 관련 API입니다.")
@RestController
Expand Down Expand Up @@ -67,6 +55,12 @@ public MissionRecordSummaryResponse missionRecordFindSummary() {
return missionService.findSummaryMissionRecord();
}

@Operation(summary = "미션 전체 현황 - 리스트", description = "년, 월, 일을 입력받아 해당 날짜의 미션 리스트를 조회합니다.")
@GetMapping("/summary-list")
public MissionSummaryListResponse missionSummaryList(@RequestParam LocalDate date) {
return missionService.findSummaryList(date);
}

@Operation(summary = "종료미션 보관함", description = "종료된 미션 리스트를 조회합니다.")
@GetMapping("/finished")
public List<FinishedMissionResponse> missionFindAllFinished() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -303,4 +304,52 @@ public List<MissionRecord> findCompletedMissionRecords(List<Mission> missions) {
missionRecord.getUploadStatus() == ImageUploadStatus.COMPLETE)
.toList();
}

@Transactional(readOnly = true)
public MissionSummaryListResponse findSummaryList(LocalDate date) {
final Member currentMember = memberUtil.getCurrentMember();
List<Mission> missions =
missionRepository.findMissionsWithRecordsByDate(date, currentMember.getId());

List<MissionSummaryItem> result =
missions.stream()
.map(mission -> getMissionSummaryItem(mission))
.sorted(
Comparator.comparing(MissionSummaryItem::missionStatus)
.reversed()
.thenComparing(
Comparator.comparing(MissionSummaryItem::finishedAt)
.reversed()))
.collect(Collectors.toList());

result.sort(
Comparator.comparing(MissionSummaryItem::missionStatus)
.reversed()
.thenComparing(
Comparator.comparing(MissionSummaryItem::finishedAt).reversed()));

long missionAllCount = missions.size();
long missionCompleteCount =
result.stream()
.filter(
missionSummaryItem ->
missionSummaryItem.missionStatus()
== MissionStatus.COMPLETED)
.count();
long missionNoneCount = missionAllCount - missionCompleteCount;
return MissionSummaryListResponse.of(
missionAllCount, missionCompleteCount, missionNoneCount, result);
}

private static MissionSummaryItem getMissionSummaryItem(Mission mission) {
boolean isCompleted =
mission.getMissionRecords().stream()
.anyMatch(
missionRecord ->
missionRecord.getUploadStatus()
== ImageUploadStatus.COMPLETE);
return isCompleted
? MissionSummaryItem.of(mission, MissionStatus.COMPLETED)
: MissionSummaryItem.of(mission, MissionStatus.NONE);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.depromeet.domain.mission.dao;

import com.depromeet.domain.mission.domain.Mission;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

Expand All @@ -15,4 +16,6 @@ public interface MissionRepositoryCustom {
void updateFinishedDurationStatus(LocalDateTime today);

List<Mission> findAllFinishedMission(Long memberId);

List<Mission> findMissionsWithRecordsByDate(LocalDate date, Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -84,6 +85,22 @@ public List<Mission> findAllFinishedMission(Long memberId) {
.fetch();
}

@Override
public List<Mission> findMissionsWithRecordsByDate(LocalDate date, Long memberId) {
LocalDateTime startedAt = date.atTime(0, 0, 0);
LocalDateTime finishedAt = startedAt.plusDays(1);
JPAQuery<Mission> query =
jpaQueryFactory
.selectFrom(mission)
.leftJoin(mission.missionRecords, missionRecord)
.where(
memberIdEq(memberId),
mission.startedAt.loe(startedAt),
mission.finishedAt.goe(finishedAt))
.fetchJoin();
return query.fetch();
}

// 미션의 사용자 id 조건 검증 메서드
private BooleanExpression memberIdEq(Long memberId) {
return memberId == null ? null : mission.member.id.eq(memberId);
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/depromeet/domain/mission/domain/Mission.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.depromeet.domain.common.model.BaseTimeEntity;
import com.depromeet.domain.member.domain.Member;
import com.depromeet.domain.missionRecord.domain.ImageUploadStatus;
import com.depromeet.domain.missionRecord.domain.MissionRecord;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
Expand Down Expand Up @@ -121,4 +122,16 @@ public void updateMission(String name, String content, MissionVisibility visibil
this.content = content;
this.visibility = visibility;
}

public boolean isCompletedMissionToday() {
return this.getMissionRecords().stream()
.filter(
record ->
record.getStartedAt()
.toLocalDate()
.equals(LocalDateTime.now().toLocalDate())
&& record.getUploadStatus() == ImageUploadStatus.COMPLETE)
.findFirst()
.isPresent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.depromeet.domain.mission.dto.response;

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

public record MissionSummaryItem(
@Schema(description = "미션 ID", defaultValue = "1") Long missionId,
@Schema(description = "미션 이름", defaultValue = "default name") String name,
@Schema(description = "미션 카테고리", defaultValue = "STUDY") MissionCategory category,
@Schema(description = "미션 공개여부", defaultValue = "ALL") MissionVisibility visibility,
@Schema(description = "미션 상태", defaultValue = "1") MissionStatus missionStatus,
@JsonFormat(
shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd HH:mm:ss",
timezone = "Asia/Seoul")
@Schema(
description = "미션 종료 시간",
defaultValue = "2024-01-03 00:34:00",
type = "string")
LocalDateTime finishedAt) {

public static MissionSummaryItem of(Mission mission, MissionStatus missionStatus) {
return new MissionSummaryItem(
mission.getId(),
mission.getName(),
mission.getCategory(),
mission.getVisibility(),
missionStatus,
mission.getFinishedAt());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.depromeet.domain.mission.dto.response;

import java.util.List;

public record MissionSummaryListResponse(
long missionAllCount,
long missionCompleteCount,
long missionNoneCount,
List<MissionSummaryItem> missionSummaryItems) {
public static MissionSummaryListResponse of(
long missionAllCount,
long missionCompleteCount,
long missionNoneCount,
List<MissionSummaryItem> missionSummaryItems) {
return new MissionSummaryListResponse(
missionAllCount, missionCompleteCount, missionNoneCount, missionSummaryItems);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,28 @@ public MissionRecordFindOneResponse findOneMissionRecord(Long recordId) {

@Transactional(readOnly = true)
public MissionRecordCalendarResponse findAllMissionRecord(Long missionId, YearMonth yearMonth) {
final Member member = memberUtil.getCurrentMember();
List<MissionRecord> missionRecords =
missionRecordRepository.findAllByMissionIdAndYearMonth(missionId, yearMonth);
List<MissionRecordFindResponse> missionRecordFindResponses =
missionRecords.stream().map(MissionRecordFindResponse::from).toList();
Mission mission = findMissionById(missionId);

UrgingStatus urgingStatus = getUrgingStatus(mission, member);

return MissionRecordCalendarResponse.of(
mission.getStartedAt(), mission.getFinishedAt(), missionRecordFindResponses);
mission.getStartedAt(),
mission.getFinishedAt(),
missionRecordFindResponses,
urgingStatus);
}

private UrgingStatus getUrgingStatus(Mission mission, Member member) {
if (member.getId().equals(mission.getMember().getId())
|| mission.isCompletedMissionToday()) {
return UrgingStatus.NONE;
}
return UrgingStatus.URGING;
}

public MissionRecordUpdateResponse updateMissionRecord(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ public List<FeedOneResponse> findFeedAll(List<Member> members) {
missionRecord.remark,
missionRecord.imageUrl,
missionRecord.duration,
missionRecord.startedAt,
missionRecord.finishedAt))
mission.startedAt,
mission.finishedAt))
.from(missionRecord)
.leftJoin(missionRecord.mission, mission)
.on(mission.id.eq(missionRecord.mission.id))
Expand All @@ -79,7 +79,7 @@ public List<FeedOneResponse> findFeedAll(List<Member> members) {
missionRecord.mission.visibility.in(
MissionVisibility.FOLLOWER, MissionVisibility.ALL),
missionRecord.uploadStatus.eq(ImageUploadStatus.COMPLETE))
.orderBy(missionRecord.startedAt.desc())
.orderBy(missionRecord.finishedAt.desc())
.limit(FEED_TAB_LIMIT)
.fetch();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ public record MissionRecordCalendarResponse(
defaultValue = "2024-01-15 00:34:00",
type = "string")
LocalDateTime missionFinishedAt,
@Schema(description = "미션 기록들") List<MissionRecordFindResponse> missionRecords) {
@Schema(description = "미션 기록들") List<MissionRecordFindResponse> missionRecords,
@Schema(description = "재촉하기 여부") UrgingStatus urgingStatus) {
public static MissionRecordCalendarResponse of(
LocalDateTime missionStartedAt,
LocalDateTime missionFinishedAt,
List<MissionRecordFindResponse> missionRecords) {
List<MissionRecordFindResponse> missionRecords,
UrgingStatus urgingStatus) {
return new MissionRecordCalendarResponse(
missionStartedAt, missionFinishedAt, missionRecords);
missionStartedAt, missionFinishedAt, missionRecords, urgingStatus);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.depromeet.domain.missionRecord.dto.response;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum UrgingStatus {
URGING,
NONE,
;
}
Loading
Loading