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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.ject.studytrip.mission.application.dto;

import com.ject.studytrip.mission.domain.model.DailyMission;

public record DailyMissionInfo(Long dailyMissionId, MissionInfo missionInfo) {
public static DailyMissionInfo from(DailyMission dailyMission) {
return new DailyMissionInfo(
dailyMission.getId(), MissionInfo.from(dailyMission.getMission()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
public record MissionInfo(
Long missionId,
String missionName,
String missionMemo,
int missionOrder,
boolean completed,
String createdAt,
Expand All @@ -15,6 +16,7 @@ public static MissionInfo from(Mission mission) {
return new MissionInfo(
mission.getId(),
mission.getName(),
mission.getMemo(),
mission.getMissionOrder(),
mission.isCompleted(),
DateUtil.formatDateTime(mission.getCreatedAt()),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.ject.studytrip.mission.application.service;

import com.ject.studytrip.mission.domain.factory.DailyMissionFactory;
import com.ject.studytrip.mission.domain.model.DailyMission;
import com.ject.studytrip.mission.domain.model.Mission;
import com.ject.studytrip.mission.domain.policy.DailyMissionPolicy;
import com.ject.studytrip.mission.domain.repository.DailyMissionQueryRepository;
import com.ject.studytrip.mission.domain.repository.DailyMissionRepository;
import com.ject.studytrip.trip.domain.model.DailyGoal;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class DailyMissionService {
private final DailyMissionRepository dailyMissionRepository;
private final DailyMissionQueryRepository dailyMissionQueryRepository;

public List<DailyMission> createDailyMissions(DailyGoal dailyGoal, List<Mission> missions) {
List<DailyMission> dailyMissions =
missions.stream()
.map(mission -> DailyMissionFactory.create(mission, dailyGoal))
.toList();

return dailyMissionRepository.saveAll(dailyMissions);
}

public void deleteDailyMission(DailyMission dailyMission) {
dailyMission.updateDeletedAt();
}

public List<DailyMission> getValidDailyMissionsByIds(
Long dailyGoalId, List<Long> dailyMissionIds) {
List<DailyMission> dailyMissions = dailyMissionRepository.findAllByIdIn(dailyMissionIds);

DailyMissionPolicy.validateExistAll(dailyMissions, dailyMissionIds);
dailyMissions.forEach(
dailyMission -> {
DailyMissionPolicy.validateBelongsToDailyGoal(dailyMission, dailyGoalId);
DailyMissionPolicy.validateNotDeleted(dailyMission);
});

return dailyMissions;
}

public List<DailyMission> getDailyMissionsByDailyGoal(Long dailyGoalId) {
return dailyMissionQueryRepository.findAllByDailyGoalIdFetchJoinMission(dailyGoalId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.ject.studytrip.mission.domain.factory.MissionFactory;
import com.ject.studytrip.mission.domain.model.Mission;
import com.ject.studytrip.mission.domain.policy.MissionPolicy;
import com.ject.studytrip.mission.domain.repository.MissionQueryRepository;
import com.ject.studytrip.mission.domain.repository.MissionRepository;
import com.ject.studytrip.mission.presentation.dto.request.CreateMissionRequest;
import com.ject.studytrip.mission.presentation.dto.request.UpdateMissionOrderRequest;
Expand All @@ -23,6 +24,7 @@
@RequiredArgsConstructor
public class MissionService {
private final MissionRepository missionRepository;
private final MissionQueryRepository missionQueryRepository;

@Transactional
public Mission createMission(Stamp stamp, CreateMissionRequest request) {
Expand Down Expand Up @@ -98,6 +100,23 @@ public Mission getValidMission(Long stampId, Long missionId) {
return mission;
}

public List<Mission> getValidMissionsWithStamp(List<Long> missionIds) {
List<Mission> missions = missionQueryRepository.findAllByIdsInFetchJoinStamp(missionIds);

MissionPolicy.validateExistAll(missions, missionIds);
missions.forEach(
mission -> {
MissionPolicy.validateNotDeleted(mission);
MissionPolicy.validateCompleted(mission);
});

return missions;
}

public void validateMissionBelongsToStamp(Long stampId, Mission mission) {
MissionPolicy.validateMissionBelongsToStamp(stampId, mission);
}

private void validateMissionIsActiveAndBelongsToStamp(Long stampId, Mission mission) {
MissionPolicy.validateMissionBelongsToStamp(stampId, mission);
MissionPolicy.validateNotDeleted(mission);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.ject.studytrip.mission.domain.error;

import com.ject.studytrip.global.exception.error.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@RequiredArgsConstructor
public enum DailyMissionErrorCode implements ErrorCode {
// 400
DAILY_MISSION_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "이미 삭제된 데일리 미션입니다."),

// 403
DAILY_MISSION_NOT_BELONG_TO_DAILY_GOAL(
HttpStatus.FORBIDDEN, "해당 데일리 미션은 요청한 데일리 목표에 속하지 않습니다."),

// 404
DAILY_MISSION_NOT_FOUND(HttpStatus.NOT_FOUND, "요청한 데일리 미션이 존재하지 않습니다."),
;

private final HttpStatus status;
private final String message;

@Override
public String getName() {
return this.name();
}

@Override
public HttpStatus getStatus() {
return this.status;
}

@Override
public String getMessage() {
return this.message;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public enum MissionErrorCode implements ErrorCode {
MISSION_ORDER_IDS_NOT_MATCHED(HttpStatus.BAD_REQUEST, "요청한 미션 ID 목록이 기존 미션 목록과 일치하지 않습니다."),
MISSION_ALREADY_DELETED(HttpStatus.BAD_REQUEST, "해당 미션은 이미 삭제되었습니다."),
MISSION_ORDER_ALREADY_EXISTS(HttpStatus.BAD_REQUEST, "이미 존재하는 미션 순서입니다."),
MISSION_ALREADY_COMPLETED(HttpStatus.BAD_REQUEST, "이미 완료된 미션입니다."),

// 403
MISSION_NOT_BELONGS_TO_STAMP(HttpStatus.FORBIDDEN, "해당 미션은 요청한 스탬프에 속하지 않습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.ject.studytrip.mission.domain.factory;

import com.ject.studytrip.mission.domain.model.DailyMission;
import com.ject.studytrip.mission.domain.model.Mission;
import com.ject.studytrip.trip.domain.model.DailyGoal;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DailyMissionFactory {
public static DailyMission create(Mission mission, DailyGoal dailyGoal) {
return DailyMission.of(mission, dailyGoal);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.ject.studytrip.global.common.entity.BaseTimeEntity;
import com.ject.studytrip.trip.domain.model.DailyGoal;
import jakarta.persistence.*;
import java.time.LocalDateTime;
import lombok.*;

@Entity
Expand All @@ -27,4 +28,8 @@ public class DailyMission extends BaseTimeEntity {
public static DailyMission of(Mission mission, DailyGoal dailyGoal) {
return DailyMission.builder().mission(mission).dailyGoal(dailyGoal).build();
}

public void updateDeletedAt() {
this.deletedAt = LocalDateTime.now();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ public void updateMissionOrder(int missionOrder) {
public void updateDeletedAt() {
this.deletedAt = LocalDateTime.now();
}

public void updateCompleted() {
this.completed = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.ject.studytrip.mission.domain.policy;

import com.ject.studytrip.global.exception.CustomException;
import com.ject.studytrip.mission.domain.error.DailyMissionErrorCode;
import com.ject.studytrip.mission.domain.model.DailyMission;
import java.util.List;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DailyMissionPolicy {
public static void validateExistAll(
List<DailyMission> foundDailyMissions, List<Long> requestedIds) {
boolean isEquals = foundDailyMissions.size() == requestedIds.size();
if (!isEquals) throw new CustomException(DailyMissionErrorCode.DAILY_MISSION_NOT_FOUND);
}

public static void validateBelongsToDailyGoal(DailyMission dailyMission, Long dailyGoalId) {
if (!dailyMission.getDailyGoal().getId().equals(dailyGoalId))
throw new CustomException(DailyMissionErrorCode.DAILY_MISSION_NOT_BELONG_TO_DAILY_GOAL);
}

public static void validateNotDeleted(DailyMission dailyMission) {
if (dailyMission.getDeletedAt() != null)
throw new CustomException(DailyMissionErrorCode.DAILY_MISSION_ALREADY_DELETED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,16 @@ public static void validateOrderNotDuplicated(boolean exists) {
throw new CustomException(MissionErrorCode.MISSION_ORDER_ALREADY_EXISTS);
}
}

public static void validateCompleted(Mission mission) {
if (mission.isCompleted())
throw new CustomException(MissionErrorCode.MISSION_ALREADY_COMPLETED);
}

public static void validateExistAll(List<Mission> foundMissions, List<Long> requestedIds) {
boolean isEquals = foundMissions.size() == requestedIds.size();
if (!isEquals) {
throw new CustomException(MissionErrorCode.MISSION_NOT_FOUND);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ject.studytrip.mission.domain.repository;

import com.ject.studytrip.mission.domain.model.DailyMission;
import java.util.List;

public interface DailyMissionQueryRepository {
List<DailyMission> findAllByDailyGoalIdFetchJoinMission(Long dailyGoalId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.ject.studytrip.mission.domain.repository;

import com.ject.studytrip.mission.domain.model.DailyMission;
import java.util.List;

public interface DailyMissionRepository {
DailyMission save(DailyMission dailyMission);

List<DailyMission> saveAll(List<DailyMission> dailyMissions);

List<DailyMission> findAllByIdIn(List<Long> ids);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ject.studytrip.mission.domain.repository;

import com.ject.studytrip.mission.domain.model.Mission;
import java.util.List;

public interface MissionQueryRepository {
List<Mission> findAllByIdsInFetchJoinStamp(List<Long> ids);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ject.studytrip.mission.infra.jpa;

import com.ject.studytrip.mission.domain.model.DailyMission;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface DailyMissionJpaRepository extends JpaRepository<DailyMission, Long> {
List<DailyMission> findAllByIdIn(List<Long> ids);

List<DailyMission> findAllByDailyGoalIdAndDeletedAtIsNull(Long dailyGoalId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ject.studytrip.mission.infra.jpa;

import com.ject.studytrip.mission.domain.model.DailyMission;
import com.ject.studytrip.mission.domain.model.QDailyMission;
import com.ject.studytrip.mission.domain.model.QMission;
import com.ject.studytrip.mission.domain.repository.DailyMissionQueryRepository;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class DailyMissionQueryRepositoryAdapter implements DailyMissionQueryRepository {
private final JPAQueryFactory queryFactory;
private final QDailyMission dailyMission = QDailyMission.dailyMission;
private final QMission mission = QMission.mission;

@Override
public List<DailyMission> findAllByDailyGoalIdFetchJoinMission(Long dailyGoalId) {
return queryFactory
.selectFrom(dailyMission)
.join(dailyMission.mission, mission)
.fetchJoin()
.where(dailyMission.dailyGoal.id.eq(dailyGoalId), dailyMission.deletedAt.isNull())
.fetch();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ject.studytrip.mission.infra.jpa;

import com.ject.studytrip.mission.domain.model.DailyMission;
import com.ject.studytrip.mission.domain.repository.DailyMissionRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class DailyMissionRepositoryAdapter implements DailyMissionRepository {
private final DailyMissionJpaRepository dailyMissionJpaRepository;

@Override
public DailyMission save(DailyMission dailyMission) {
return dailyMissionJpaRepository.save(dailyMission);
}

@Override
public List<DailyMission> saveAll(List<DailyMission> dailyMissions) {
return dailyMissionJpaRepository.saveAll(dailyMissions);
}

@Override
public List<DailyMission> findAllByIdIn(List<Long> ids) {
return dailyMissionJpaRepository.findAllByIdIn(ids);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ject.studytrip.mission.infra.jpa;

import com.ject.studytrip.mission.domain.model.Mission;
import com.ject.studytrip.mission.domain.model.QMission;
import com.ject.studytrip.mission.domain.repository.MissionQueryRepository;
import com.ject.studytrip.stamp.domain.model.QStamp;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class MissionQueryRepositoryAdapter implements MissionQueryRepository {
private final JPAQueryFactory queryFactory;
private final QMission mission = QMission.mission;
private final QStamp stamp = QStamp.stamp;

@Override
public List<Mission> findAllByIdsInFetchJoinStamp(List<Long> ids) {
return queryFactory
.selectFrom(mission)
.join(mission.stamp, stamp)
.fetchJoin()
.where(mission.id.in(ids))
.fetch();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ject.studytrip.pomodoro.application.dto;

import com.ject.studytrip.pomodoro.domain.model.Pomodoro;

public record PomodoroInfo(Long pomodoroId, int focusDurationInMinute) {
public static PomodoroInfo from(Pomodoro pomodoro) {
return new PomodoroInfo(pomodoro.getId(), pomodoro.getFocusDurationInSeconds() / 60);
}
}
Loading