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

Add(.remind): 피드백 가능한 계획 목록 조회 API 구현 #199

Open
wants to merge 12 commits into
base: dev
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions src/main/java/me/ajaja/global/common/BaseTime.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ public boolean isWithinAMonth(BaseTime time) {
return zonedDateTime.isBefore(time.oneMonthLater()) && zonedDateTime.isAfter(time.zonedDateTime);
}

public long betweenDays(BaseTime compare) {
return Duration.between(this.instant, compare.instant).toDays();
}

private static LocalDateTime parseDateTime(int year, int month, int date, int hour, int minute) {
return LocalDateTime.of(year, month, date, hour, minute);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package me.ajaja.module.feedback.application;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import me.ajaja.global.common.BaseTime;
import me.ajaja.module.feedback.domain.Feedback;
import me.ajaja.module.feedback.domain.FeedbackQueryRepository;
import me.ajaja.module.feedback.dto.FeedbackResponse;
import me.ajaja.module.feedback.mapper.FeedbackMapper;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class LoadEvaluableFeedbacksService {
private final FeedbackQueryRepository feedbackQueryRepository;
private final LoadEvaluableTargetsService loadEvaluableTargetsService;
private final FeedbackMapper mapper;

public List<FeedbackResponse.EvaluableFeedback> loadEvaluableFeedbacksByUserId(Long userId) {
List<Feedback> feedbacks = feedbackQueryRepository.findAllFeedbacksByUserId(userId);

return loadEvaluableTargetsService.findEvaluableTargetsByUserId(userId).stream()
.filter(feedback -> isNotEvaluate(feedbacks, feedback.planId(), feedback.period()))
.map(mapper::toResponse)
.toList();
}

private boolean isNotEvaluate(List<Feedback> feedbacks, Long planId, BaseTime period) {
return feedbacks.stream().noneMatch(feedback ->
feedback.getPlanId().equals(planId) && period.isWithinAMonth(feedback.getCreatedAt()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package me.ajaja.module.feedback.application;

import java.util.List;

import me.ajaja.module.feedback.application.model.UpdatableFeedback;

public interface LoadEvaluableTargetsService { // todo: 헥사고날 아키텍쳐 적용 후 네이밍 변경
List<UpdatableFeedback> findEvaluableTargetsByUserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ public class LoadFeedbackService {
private final FeedbackQueryRepository feedbackQueryRepository;

public List<Feedback> loadFeedback(Long planId) {
return feedbackQueryRepository.findAllFeedbackByPlanId(planId);
return feedbackQueryRepository.findAllFeedbacksByPlanId(planId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void updateFeedback(Long userId, Long planId, int rate, String message) {
Plan plan = findTargetPort.findByUserIdAndPlanId(userId, planId);
BaseTime now = BaseTime.now();

BaseTime period = plan.getFeedbackPeriod(now);
BaseTime period = plan.findFeedbackPeriod(now);
checkExistFeedback(planId, period);

Feedback feedback = Feedback.create(userId, planId, rate, message);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package me.ajaja.module.feedback.application.model;

import me.ajaja.global.common.BaseTime;

public record UpdatableFeedback(
String title,
Long planId,
BaseTime period
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import me.ajaja.module.feedback.infra.model.FeedbackInfo;

public interface FeedbackQueryRepository {
List<Feedback> findAllFeedbackByPlanId(Long planId);
List<Feedback> findAllFeedbacksByPlanId(Long planId);

boolean existByPlanIdAndPeriod(Long feedbackId, BaseTime period);
List<Feedback> findAllFeedbacksByUserId(Long userId);

boolean existByPlanIdAndPeriod(Long planId, BaseTime period);

List<FeedbackInfo> findFeedbackInfosByPlanId(Long planId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,13 @@ public static class RemindFeedback {
private final int endDate;
private final boolean reminded;
}

@Data
public static class EvaluableFeedback {
private final String title;
private final Long planId;
private final long remainPeriod;
private final int month;
private final int date;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class FeedbackQueryRepositoryImpl implements FeedbackQueryRepository {
private final FeedbackMapper mapper;

@Override
public List<Feedback> findAllFeedbackByPlanId(Long planId) {
public List<Feedback> findAllFeedbacksByPlanId(Long planId) {
List<FeedbackEntity> entities = queryFactory.selectFrom(feedbackEntity)
.where(feedbackEntity.planId.eq(planId))
.orderBy(feedbackEntity.createdAt.asc())
Expand All @@ -35,6 +35,17 @@ public List<Feedback> findAllFeedbackByPlanId(Long planId) {
return mapper.toDomain(entities);
}

@Override
public List<Feedback> findAllFeedbacksByUserId(Long userId) {
List<FeedbackEntity> entities = queryFactory.selectFrom(feedbackEntity)
.where(feedbackEntity.userId.eq(userId)
.and(feedbackEntity.createdAt.year().eq(BaseTime.now().getYear())))
Copy link
Member

@Hejow Hejow Mar 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

최저수준에서 직접적으로 application 코드를 사용하라는 의도는 아니였습니다!
repository의 재사용성을 고려해서 시간은 외부에서 받도록!!

.orderBy(feedbackEntity.planId.asc())
.fetch();

return mapper.toDomain(entities);
Comment on lines +40 to +46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 곳에서는 stream()으로 잘 넘기시는데 여기서는 달라서 코드에 통일성이 없어 보여요!
아마 원인이 mapper의 문제인 것 같은데 toDomain()으로 넘기는 것과 달리 결과는 List로 받으니 mapper 네이밍의 문제인 것 같습니다!

}

@Override
public boolean existByPlanIdAndPeriod(Long planId, BaseTime period) {
return queryFactory.selectFrom(feedbackEntity)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import me.ajaja.global.common.BaseTime;
import me.ajaja.module.feedback.application.model.PlanFeedbackInfo;
import me.ajaja.module.feedback.application.model.UpdatableFeedback;
import me.ajaja.module.feedback.domain.Feedback;
import me.ajaja.module.feedback.dto.FeedbackResponse;
import me.ajaja.module.feedback.infra.FeedbackEntity;
Expand Down Expand Up @@ -59,4 +60,9 @@ default boolean isReminded(BaseTime sendDate) {
BaseTime now = BaseTime.now();
return now.isAfter(sendDate);
}

@Mapping(target = "remainPeriod", expression = "java(feedback.period().betweenDays(BaseTime.now()))")
@Mapping(target = "month", expression = "java(feedback.period().getMonth())")
@Mapping(target = "date", expression = "java(feedback.period().getDate())")
FeedbackResponse.EvaluableFeedback toResponse(UpdatableFeedback feedback);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static org.springframework.http.HttpStatus.*;

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -14,6 +16,7 @@
import me.ajaja.global.common.AjajaResponse;
import me.ajaja.global.security.annotation.Authorization;
import me.ajaja.global.util.SecurityUtil;
import me.ajaja.module.feedback.application.LoadEvaluableFeedbacksService;
import me.ajaja.module.feedback.application.LoadFeedbackInfoService;
import me.ajaja.module.feedback.application.UpdateFeedbackService;
import me.ajaja.module.feedback.dto.FeedbackRequest;
Expand All @@ -25,6 +28,7 @@
public class FeedbackController {
private final UpdateFeedbackService updateFeedbackService;
private final LoadFeedbackInfoService loadFeedbackInfoService;
private final LoadEvaluableFeedbacksService loadEvaluableFeedbacksService;

@Authorization
@PostMapping("/{planId}")
Expand All @@ -46,4 +50,14 @@ public AjajaResponse<FeedbackResponse.FeedbackInfo> getFeedbackInfo(@PathVariabl
FeedbackResponse.FeedbackInfo feedbackInfo = loadFeedbackInfoService.loadFeedbackInfoByPlanId(userId, planId);
return AjajaResponse.ok(feedbackInfo);
}

@Authorization
@GetMapping("/evaluables")
@ResponseStatus(OK)
public AjajaResponse<List<FeedbackResponse.EvaluableFeedback>> getEvaluableFeedbacks() {
Long userId = SecurityUtil.getUserId();
List<FeedbackResponse.EvaluableFeedback> feedbacks
= loadEvaluableFeedbacksService.loadEvaluableFeedbacksByUserId(userId);
return AjajaResponse.ok(feedbacks);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package me.ajaja.module.plan.adapter.out.persistence;

import static me.ajaja.module.plan.adapter.out.persistence.model.QPlanEntity.*;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.querydsl.jpa.impl.JPAQueryFactory;

import lombok.RequiredArgsConstructor;
import me.ajaja.global.common.BaseTime;
import me.ajaja.module.plan.application.port.out.FindEvaluablePlansPort;
import me.ajaja.module.plan.domain.Plan;
import me.ajaja.module.plan.mapper.PlanMapper;

@Repository
@RequiredArgsConstructor
public class FindEvaluablePlansAdapter implements FindEvaluablePlansPort {
private final JPAQueryFactory queryFactory;
private final PlanMapper mapper;

@Override
public List<Plan> findEvaluablePlansByUserIdAndTime(Long id, BaseTime now) {
return queryFactory.selectFrom(planEntity)
.where(planEntity.userId.eq(id)
.and(planEntity.createdAt.year().eq(now.getYear())))
.fetch()
.stream()
.map(mapper::toDomain)
.toList();
}
Comment on lines +24 to +32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네이밍과 쿼리 사이 연관성이 없는 것 같아요..!
그리고 querydsl을 이렇게 쓸거면 JPA로만 처리할 수 있을 것 같아요!

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package me.ajaja.module.plan.application;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import me.ajaja.global.common.BaseTime;
import me.ajaja.module.feedback.application.LoadEvaluableTargetsService;
import me.ajaja.module.feedback.application.model.UpdatableFeedback;
import me.ajaja.module.plan.application.port.out.FindEvaluablePlansPort;
import me.ajaja.module.plan.mapper.PlanMapper;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class LoadEvaluablePlansService implements LoadEvaluableTargetsService {
private FindEvaluablePlansPort findEvaluablePlansPort;
private PlanMapper mapper;

@Override
public List<UpdatableFeedback> findEvaluableTargetsByUserId(Long userId) {
BaseTime now = BaseTime.now();
Copy link
Member

@Hejow Hejow Mar 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

method 네이밍이 괜찮아서 바로 사용해도 괜찮을 것 같아요!

return findEvaluablePlansPort.findEvaluablePlansByUserIdAndTime(userId, now).stream()
.filter(plan -> plan.isWithinPeriod(now))
.map(mapper::toFeedbackModel)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package me.ajaja.module.plan.application.port.out;

import java.util.List;

import me.ajaja.global.common.BaseTime;
import me.ajaja.module.plan.domain.Plan;

public interface FindEvaluablePlansPort {
List<Plan> findEvaluablePlansByUserIdAndTime(Long id, BaseTime now);
}
10 changes: 10 additions & 0 deletions src/main/java/me/ajaja/module/plan/domain/Message.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import me.ajaja.global.common.BaseTime;
import me.ajaja.global.common.SelfValidating;

@Getter
Expand All @@ -26,4 +27,13 @@ public Message(String content, int remindMonth, int remindDay) {
this.remindDate = new RemindDate(remindMonth, remindDay);
this.validateSelf();
}

BaseTime parsePeriod(int year, int remindTime) {
return BaseTime.parse(
year,
remindDate.getRemindMonth(),
remindDate.getRemindDay(),
remindTime
);
}
}
21 changes: 9 additions & 12 deletions src/main/java/me/ajaja/module/plan/domain/Plan.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,17 @@ public void disable() {
this.status = status.disable();
}

public BaseTime getFeedbackPeriod(BaseTime current) {
public BaseTime findFeedbackPeriod(BaseTime now) {
return this.messages.stream()
.filter(message -> current.isWithinAMonth(
BaseTime.parse(
this.createdAt.getYear(),
message.getRemindDate().getRemindMonth(),
message.getRemindDate().getRemindDay(),
this.getRemindTime()))
)
.filter(message -> isWithinPeriod(now))
.findAny()
.map(message -> BaseTime.parse(this.createdAt.getYear(),
message.getRemindDate().getRemindMonth(),
message.getRemindDate().getRemindDay(),
this.getRemindTime()))
.map(message -> message.parsePeriod(this.createdAt.getYear(), this.getRemindTime()))
.orElseThrow(() -> new AjajaException(EXPIRED_FEEDBACK));
}

public boolean isWithinPeriod(BaseTime now) {
return this.messages.stream().anyMatch(message -> now.isWithinAMonth(
message.parsePeriod(this.createdAt.getYear(), this.getRemindTime())
Comment on lines +154 to +155
Copy link
Member

@Hejow Hejow Mar 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가독성이 떨어져서 라인을 좀 바꿀까요?? 아니면 저처럼 map 쓰셔도 괜찮구!

));
}
}
11 changes: 11 additions & 0 deletions src/main/java/me/ajaja/module/plan/domain/RemindInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,22 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import me.ajaja.global.common.SelfValidating;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RemindInfo extends SelfValidating<RemindInfo> {
@Getter
@RequiredArgsConstructor
private enum RemindTime {
MORNING(9),
AFTERNOON(13),
EVENING(22);

private final int time;
}

Comment on lines +15 to +24
Copy link
Member

@Hejow Hejow Mar 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 내부에서 쓰도록 변경했다면, 다음과 같이 불필요한 코드라인 수를 줄일 수 있을 것 같아요!
또한 외부에서 안쓰이니까 접근지정자를 개선할 수 있을 것 같구요!

public int getRemindTime() {
    // before
    return remindTime.getTime();

    // after
    return remindTime.time;
}

@Positive
private int remindTotalPeriod;

Expand Down
14 changes: 0 additions & 14 deletions src/main/java/me/ajaja/module/plan/domain/RemindTime.java

This file was deleted.

10 changes: 10 additions & 0 deletions src/main/java/me/ajaja/module/plan/mapper/PlanMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import me.ajaja.module.ajaja.infra.AjajaEntity;
import me.ajaja.module.feedback.application.model.FeedbackPeriod;
import me.ajaja.module.feedback.application.model.PlanFeedbackInfo;
import me.ajaja.module.feedback.application.model.UpdatableFeedback;
import me.ajaja.module.plan.adapter.out.persistence.model.PlanEntity;
import me.ajaja.module.plan.domain.Content;
import me.ajaja.module.plan.domain.Message;
Expand Down Expand Up @@ -143,4 +144,13 @@ default List<FeedbackPeriod> getFeedbackPeriods(List<Message> messages) {
.map(remindDate -> new FeedbackPeriod(remindDate.getRemindMonth(), remindDate.getRemindDay()))
.toList();
}

@Mapping(source = "id", target = "planId")
@Mapping(source = "content.title", target = "title")
@Mapping(target = "period", expression = "java(getRemindPeriod(plan))")
UpdatableFeedback toFeedbackModel(Plan plan);

default BaseTime getRemindPeriod(Plan plan) {
return plan.findFeedbackPeriod(BaseTime.now());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public RemindResponse.RemindInfo findByUserIdAndPlanId(Long userId, Long planId)
Plan plan = findTargetPort.findByUserIdAndPlanId(userId, planId);

List<RemindResponse.Message> messages = plan.getMessages()
.stream().map(mapper::toMessage)
.stream().map(message ->
mapper.toMessage(message, plan.getCreatedAt().getYear(), plan.getRemindTime()))
.toList();

return mapper.toRemindInfo(plan, messages);
Expand Down
Loading
Loading