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
Expand Up @@ -8,13 +8,12 @@
import com.devkor.ifive.nadab.domain.user.core.entity.User;
import com.devkor.ifive.nadab.domain.user.core.repository.UserRepository;
import com.devkor.ifive.nadab.global.exception.NotFoundException;
import com.devkor.ifive.nadab.global.shared.util.TodayDateTimeProvider;
import com.devkor.ifive.nadab.global.shared.util.dto.TodayDateTimeRangeDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneId;

@Service
@RequiredArgsConstructor
Expand All @@ -29,21 +28,12 @@ public DailyReportResponse getDailyReport(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new NotFoundException("사용자를 찾을 수 없습니다. id: " + id));

LocalDate today = LocalDate.now(ZoneId.of("Asia/Seoul"));
TodayDateTimeRangeDto range = TodayDateTimeProvider.getRange();

OffsetDateTime startOfToday =
today.atStartOfDay(ZoneId.of("Asia/Seoul"))
.toOffsetDateTime();

OffsetDateTime startOfTomorrow =
today.plusDays(1)
.atStartOfDay(ZoneId.of("Asia/Seoul"))
.toOffsetDateTime();

AnswerEntry entry = answerEntryRepository.findByUserAndCreatedAtBetween(user, startOfToday, startOfTomorrow)
AnswerEntry entry = answerEntryRepository.findByUserAndCreatedAtBetween(user, range.startOfToday(), range.startOfTomorrow())
.orElseThrow(() -> new NotFoundException("오늘의 답변 항목을 찾을 수 없습니다. userId: " + id));

DailyReport report = dailyReportRepository.findByAnswerEntryAndCreatedAtBetween(entry, startOfToday, startOfTomorrow)
DailyReport report = dailyReportRepository.findByAnswerEntryAndCreatedAtBetween(entry, range.startOfToday(), range.startOfTomorrow())
.orElseThrow(() -> new NotFoundException("오늘의 리포트를 찾을 수 없습니다. userId: " + id));

return new DailyReportResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,47 @@
import com.devkor.ifive.nadab.domain.dailyreport.core.entity.AnswerEntry;
import com.devkor.ifive.nadab.domain.dailyreport.infra.DailyReportLlmClient;
import com.devkor.ifive.nadab.domain.question.core.entity.DailyQuestion;
import com.devkor.ifive.nadab.domain.question.core.entity.UserDailyQuestion;
import com.devkor.ifive.nadab.domain.question.core.repository.DailyQuestionRepository;
import com.devkor.ifive.nadab.domain.question.core.repository.UserDailyQuestionRepository;
import com.devkor.ifive.nadab.domain.user.core.entity.User;
import com.devkor.ifive.nadab.domain.user.core.repository.UserRepository;
import com.devkor.ifive.nadab.global.exception.BadRequestException;
import com.devkor.ifive.nadab.global.exception.NotFoundException;

import com.devkor.ifive.nadab.global.shared.util.TodayDateTimeProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;

@Service
@RequiredArgsConstructor
public class DailyReportService {

private final UserRepository userRepository;
private final DailyQuestionRepository dailyQuestionRepository;
private final UserDailyQuestionRepository userDailyQuestionRepository;

private final DailyReportTxService dailyReportTxService;

private final DailyReportLlmClient dailyReportLlmClient;


public CreateDailyReportResponse generateDailyReport(Long userId, DailyReportRequest request) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new NotFoundException("사용자를 찾을 수 없습니다. id: " + userId));

DailyQuestion question = dailyQuestionRepository.findById(request.questionId())
.orElseThrow(() -> new NotFoundException("질문을 찾을 수 없습니다. id: " + request.questionId()));

LocalDate today = TodayDateTimeProvider.getTodayDate();
UserDailyQuestion udq = userDailyQuestionRepository.findByUserIdAndDate(userId, today)
.orElseThrow(() -> new BadRequestException("오늘의 질문이 사용자에게 할당되지 않았습니다. date: " + today));
if (!udq.getDailyQuestion().getId().equals(request.questionId())) {
throw new BadRequestException("요청의 질문이 사용자에게 할당된 오늘의 질문과 일치하지 않습니다. 할당된 questionId: " + udq.getDailyQuestion().getId());
}

PrepareDailyResultDto prep = dailyReportTxService.prepareDaily(user, question, request.answer());

AnswerEntry answerEntry = prep.entry();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@

import com.devkor.ifive.nadab.domain.question.core.entity.DailyQuestion;
import com.devkor.ifive.nadab.domain.user.core.entity.User;
import com.devkor.ifive.nadab.global.shared.entity.SoftDeletableEntity;
import com.devkor.ifive.nadab.global.shared.entity.AuditableEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDate;

@Entity
@Table(name = "answer_entries")
@Table(
name = "answer_entries",
uniqueConstraints = {
@UniqueConstraint(name = "uq_answer_entries_user_id_date", columnNames = {"user_id", "date"})
}
)
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class AnswerEntry extends SoftDeletableEntity {
public class AnswerEntry extends AuditableEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -29,15 +36,20 @@ public class AnswerEntry extends SoftDeletableEntity {
@Column(name = "content", length = 500, nullable = false)
private String content;

public static AnswerEntry create(User user, DailyQuestion question, String content) {
@Column(name = "date", nullable = false)
private LocalDate date;

public static AnswerEntry create(User user, DailyQuestion question, String content, LocalDate date) {
AnswerEntry e = new AnswerEntry();
e.user = user;
e.question = question;
e.content = content;
e.date = date;
return e;
}

public void updateContent(String content) {
this.content = content;
onUpdate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.time.OffsetDateTime;

@Entity
@Table(name = "daily_reports")
@Table(
name = "daily_reports",
uniqueConstraints = {
@UniqueConstraint(name = "uq_daily_reports_answer_entry_id_date", columnNames = {"answer_entry_id", "date"})
}
)
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class DailyReport extends CreatableEntity {
Expand All @@ -36,16 +42,20 @@ public class DailyReport extends CreatableEntity {
@Column(name = "analyzed_at")
private OffsetDateTime analyzedAt;

public static DailyReport create(AnswerEntry answerEntry, Emotion emotion, String content, DailyReportStatus status) {
@Column(name = "date", nullable = false)
private LocalDate date;

public static DailyReport create(AnswerEntry answerEntry, Emotion emotion, String content, LocalDate date,DailyReportStatus status) {
DailyReport dr = new DailyReport();
dr.answerEntry = answerEntry;
dr.emotion = emotion;
dr.content = content;
dr.date = date;
dr.status = status;
return dr;
}

public static DailyReport createPending(AnswerEntry answerEntry) {
return create(answerEntry, null, null, DailyReportStatus.PENDING);
public static DailyReport createPending(AnswerEntry answerEntry, LocalDate date) {
return create(answerEntry, null, null, date,DailyReportStatus.PENDING);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ public interface AnswerEntryRepository extends JpaRepository<AnswerEntry, Long>
from AnswerEntry a
where a.user.id = :userId
and a.question.id = :questionId
and a.deletedAt is null
""")
boolean existsActiveAnswer(
@Param("userId") Long userId,
@Param("questionId") Long questionId
);

Optional<AnswerEntry> findByUserAndCreatedAtBetween(User user, OffsetDateTime start, OffsetDateTime end);

boolean existsByUserAndCreatedAtBetween(User user, OffsetDateTime start, OffsetDateTime end);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,32 @@
import com.devkor.ifive.nadab.domain.dailyreport.core.repository.AnswerEntryRepository;
import com.devkor.ifive.nadab.domain.question.core.entity.DailyQuestion;
import com.devkor.ifive.nadab.domain.user.core.entity.User;
import com.devkor.ifive.nadab.global.shared.util.TodayDateTimeProvider;
import com.devkor.ifive.nadab.global.shared.util.dto.TodayDateTimeRangeDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneId;


@Service
@RequiredArgsConstructor
public class AnswerEntryService {

private final AnswerEntryRepository answerEntryRepository;


@Transactional(propagation = Propagation.REQUIRES_NEW)
public AnswerEntry getOrCreateTodayAnswerEntry(User user, DailyQuestion dq, String answerText) {

LocalDate today = LocalDate.now(ZoneId.of("Asia/Seoul"));

OffsetDateTime startOfToday =
today.atStartOfDay(ZoneId.of("Asia/Seoul"))
.toOffsetDateTime();
TodayDateTimeRangeDto range = TodayDateTimeProvider.getRange();

OffsetDateTime startOfTomorrow =
today.plusDays(1)
.atStartOfDay(ZoneId.of("Asia/Seoul"))
.toOffsetDateTime();
LocalDate today = TodayDateTimeProvider.getTodayDate();

return answerEntryRepository.findByUserAndCreatedAtBetween(user, startOfToday, startOfTomorrow)
.orElseGet(() -> answerEntryRepository.save(AnswerEntry.create(user, dq, answerText)));
return answerEntryRepository.findByUserAndCreatedAtBetween(user, range.startOfToday(), range.startOfTomorrow())
.orElseGet(() -> answerEntryRepository.save(AnswerEntry.create(user, dq, answerText, today)));
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,32 @@
import com.devkor.ifive.nadab.domain.dailyreport.core.entity.DailyReportStatus;
import com.devkor.ifive.nadab.domain.dailyreport.core.repository.DailyReportRepository;
import com.devkor.ifive.nadab.global.exception.ConflictException;
import com.devkor.ifive.nadab.global.shared.util.TodayDateTimeProvider;
import com.devkor.ifive.nadab.global.shared.util.dto.TodayDateTimeRangeDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneId;


@Service
@RequiredArgsConstructor
public class PendingDailyReportService {

private final DailyReportRepository dailyReportRepository;


@Transactional(propagation = Propagation.REQUIRES_NEW)
public DailyReport getOrCreatePendingDailyReport(AnswerEntry entry) {

LocalDate today = LocalDate.now(ZoneId.of("Asia/Seoul"));

OffsetDateTime startOfToday =
today.atStartOfDay(ZoneId.of("Asia/Seoul"))
.toOffsetDateTime();
TodayDateTimeRangeDto range = TodayDateTimeProvider.getRange();

OffsetDateTime startOfTomorrow =
today.plusDays(1)
.atStartOfDay(ZoneId.of("Asia/Seoul"))
.toOffsetDateTime();
LocalDate today = TodayDateTimeProvider.getTodayDate();

DailyReport report = dailyReportRepository.findByAnswerEntryAndCreatedAtBetween(entry, startOfToday, startOfTomorrow)
.orElseGet(() -> dailyReportRepository.save(DailyReport.createPending(entry)));
DailyReport report = dailyReportRepository.findByAnswerEntryAndCreatedAtBetween(entry, range.startOfToday(), range.startOfTomorrow())
.orElseGet(() -> dailyReportRepository.save(DailyReport.createPending(entry, today)));

if (report.getStatus() == DailyReportStatus.COMPLETED) {
throw new ConflictException("이미 작성된 일간 리포트가 존재합니다. reportId: " + report.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import com.devkor.ifive.nadab.global.exception.BadRequestException;
import com.devkor.ifive.nadab.global.exception.ConflictException;
import com.devkor.ifive.nadab.global.exception.NotFoundException;
import com.devkor.ifive.nadab.global.shared.util.TodayDateTimeProvider;
import com.devkor.ifive.nadab.global.shared.util.dto.TodayDateTimeRangeDto;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
Expand All @@ -28,8 +30,6 @@
@Transactional
public class QuestionCommandService {

private static final ZoneId KST = ZoneId.of("Asia/Seoul");

private final UserRepository userRepository;
private final UserDailyQuestionRepository userDailyQuestionRepository;
private final UserInterestRepository userInterestRepository;
Expand All @@ -41,11 +41,11 @@ public class QuestionCommandService {
private final DailyQuestionSelector dailyQuestionSelector;

public DailyQuestionResponse getOrCreateTodayQuestion(Long userId) {
LocalDate today = LocalDate.now(KST);

User user = userRepository.findById(userId)
.orElseThrow(() -> new NotFoundException("사용자를 찾을 수 없습니다. id: " + userId));

LocalDate today = TodayDateTimeProvider.getTodayDate();
UserDailyQuestion udq = userDailyQuestionRepository.findByUserIdAndDate(userId, today)
.orElseGet(() -> this.createTodayQuestion(userId, today));

Expand Down Expand Up @@ -104,7 +104,9 @@ public UserDailyQuestion createTodayQuestion(Long userId, LocalDate todayKst) {
* - 이미 답변한 질문은 제외
*/
public DailyQuestionResponse rerollTodayQuestion(Long userId) {
LocalDate today = LocalDate.now(KST);
LocalDate today = TodayDateTimeProvider.getTodayDate();

TodayDateTimeRangeDto range = TodayDateTimeProvider.getRange();

UserDailyQuestion udq = userDailyQuestionRepository.findByUserIdAndDate(userId, today)
.orElseThrow(() -> new ConflictException("오늘의 첫 질문이 아직 생성되지 않았습니다."));
Expand All @@ -115,6 +117,11 @@ public DailyQuestionResponse rerollTodayQuestion(Long userId) {

User user = udq.getUser();

boolean alreadyAnswered = answerEntryRepository.existsByUserAndCreatedAtBetween(user, range.startOfToday(), range.startOfTomorrow());
if (alreadyAnswered) {
throw new BadRequestException("오늘의 질문에 이미 답변을 작성한 후에는 질문을 새로 받을 수 없습니다.");
}

Long userInterestId = userInterestRepository.findInterestIdByUserId(userId)
.orElseThrow(() -> new NotFoundException("유저 관심 주제가 없습니다. id: " + userId));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ public interface DailyQuestionRepository extends JpaRepository<DailyQuestion, Lo
from DailyQuestion q
where q.interest.id = :interestId
and (:levelOnly is null or q.questionLevel = :levelOnly)
and q.deletedAt is null
and not exists (
select 1
from AnswerEntry a
where a.user.id = :userId
and a.question.id = q.id
and a.deletedAt is null
)
order by function('random')
""")
Expand All @@ -51,12 +51,12 @@ List<DailyQuestion> findRandomByInterestExcludingAnswered(
where q.interest.id = :interestId
and q.id <> :excludeId
and (:levelOnly is null or q.questionLevel = :levelOnly)
and q.deletedAt is null
and not exists (
select 1
from AnswerEntry a
where a.user.id = :userId
and a.question.id = q.id
and a.deletedAt is null
)
order by function('random')
""")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public abstract class AuditableEntity extends CreatableEntity {
@Column(name = "updated_at")
protected OffsetDateTime updatedAt;

// Timestamped의 onCreate를 오버라이드
// CreatableEntity의 onCreate를 오버라이드
@Override
protected void onCreate() {
super.onCreate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import java.time.OffsetDateTime;
import java.time.ZoneId;

/**
* OffsetDateTime을 서울 시간대의 LocalDate 또는 LocalDateTime으로 변환하는 유틸리티 클래스
*/
public class DateTimeConverter {

private static final ZoneId SEOUL = ZoneId.of("Asia/Seoul");
Expand Down
Loading