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
2 changes: 1 addition & 1 deletion .github/workflows/deploy-to-dev-ec2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ jobs:
ANTHROPIC_API_KEY="${{ secrets.ANTHROPIC_API_KEY }}" \
GOOGLE_GENAI_API_KEY="${{ secrets.GOOGLE_GENAI_API_KEY }}" \
FIREBASE_ADMIN_KEY="${{ secrets.FIREBASE_ADMIN_KEY }}" \
ADMIN_PAGE_PASSWORD="${{ secrets.ADMIN_PAGE_PASSWORD }}" \
ADMIN_PAGE_PASSWORD='${{ secrets.ADMIN_PAGE_PASSWORD }}' \
DEV_TEST_ACCOUNT_PASSWORD="${{ secrets.DEV_TEST_ACCOUNT_PASSWORD }}" \
nohup java -jar "$JAR_PATH" \
--spring.profiles.active=dev > app.log 2>&1 &
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-to-prod-ec2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ jobs:
ANTHROPIC_API_KEY="${{ secrets.ANTHROPIC_API_KEY }}" \
GOOGLE_GENAI_API_KEY="${{ secrets.GOOGLE_GENAI_API_KEY }}" \
FIREBASE_ADMIN_KEY="${{ secrets.FIREBASE_ADMIN_KEY }}" \
ADMIN_PAGE_PASSWORD="${{ secrets.ADMIN_PAGE_PASSWORD }}" \
ADMIN_PAGE_PASSWORD='${{ secrets.ADMIN_PAGE_PASSWORD }}' \
nohup java -jar "$JAR_PATH" \
--spring.profiles.active=prod > app-prod.log 2>&1 &

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public class AdminPageAuthCommandService {
private final AdminPageProperties adminPageProperties;

public void validatePassword(String rawPassword) {
byte[] input = rawPassword.getBytes(StandardCharsets.UTF_8);
byte[] expected = adminPageProperties.getPassword().getBytes(StandardCharsets.UTF_8);
byte[] input = rawPassword.strip().getBytes(StandardCharsets.UTF_8);
byte[] expected = adminPageProperties.getPassword().strip().getBytes(StandardCharsets.UTF_8);

if (!MessageDigest.isEqual(input, expected)) {
throw new UnauthorizedException(ErrorCode.ADMIN_PAGE_INVALID_PASSWORD);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
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.reportlog.application.ReportGenerationLogRecorder;
import com.devkor.ifive.nadab.domain.reportlog.core.entity.ReportGenerationStep;
import com.devkor.ifive.nadab.domain.reportlog.core.entity.ReportGenerationType;
import com.devkor.ifive.nadab.domain.user.core.entity.InterestCode;
import com.devkor.ifive.nadab.domain.user.core.entity.User;
import com.devkor.ifive.nadab.domain.user.core.repository.UserRepository;
Expand All @@ -25,6 +28,7 @@
import com.devkor.ifive.nadab.global.exception.BadRequestException;
import com.devkor.ifive.nadab.global.exception.NotFoundException;

import com.devkor.ifive.nadab.global.infra.llm.LlmProvider;
import com.devkor.ifive.nadab.global.shared.util.TodayDateTimeProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -38,6 +42,8 @@
@RequiredArgsConstructor
public class DailyReportService {

private static final String DAILY_REPORT_LLM_MODEL = "GPT_4_O_MINI";

private final UserRepository userRepository;
private final DailyQuestionRepository dailyQuestionRepository;
private final UserDailyQuestionRepository userDailyQuestionRepository;
Expand All @@ -46,6 +52,7 @@ public class DailyReportService {
private final ProfileImageService profileImageService;

private final DailyReportLlmClient dailyReportLlmClient;
private final ReportGenerationLogRecorder reportGenerationLogRecorder;

private final ApplicationEventPublisher eventPublisher;

Expand Down Expand Up @@ -86,11 +93,21 @@ public CreateDailyReportResponse generateDailyReport(Long userId, DailyReportReq
PrepareDailyResultDto prep = dailyReportTxService.prepareDaily(user, question, request.answer(), isDayPassed, request.objectKey());

AnswerEntry answerEntry = prep.entry();
Long generationLogId = reportGenerationLogRecorder.start(
userId,
ReportGenerationType.DAILY,
prep.reportId(),
ReportGenerationStep.DAILY_GENERATE,
LlmProvider.OPENAI,
DAILY_REPORT_LLM_MODEL
);

AiDailyReportResultDto dto;
try {
dto = dailyReportLlmClient.generate(question.getQuestionText(), answerEntry);
reportGenerationLogRecorder.succeed(generationLogId);
} catch (Exception e) {
reportGenerationLogRecorder.fail(generationLogId, e);
dailyReportTxService.failDaily(prep.reportId());
throw e;
}
Expand Down Expand Up @@ -174,4 +191,5 @@ private void validateWebpKey(String key, Long userId) {
private boolean isBlank(String s) {
return s == null || s.trim().isEmpty();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.devkor.ifive.nadab.global.exception.BadRequestException;
import com.devkor.ifive.nadab.global.exception.ai.AiResponseParseException;
import com.devkor.ifive.nadab.global.exception.ai.AiServiceUnavailableException;
import com.devkor.ifive.nadab.global.infra.llm.LlmExceptionMapper;
import com.devkor.ifive.nadab.global.infra.llm.LlmProvider;
import com.devkor.ifive.nadab.global.infra.llm.LlmRouter;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand Down Expand Up @@ -57,11 +58,16 @@ public AiDailyReportResultDto generate(String question, AnswerEntry answerEntry)

UserMessage userMessage = buildUserMessage(prompt, withImagePrompt,answerEntry);

String content = chatClient.prompt()
.messages(userMessage)
.options(options)
.call()
.content();
String content;
try {
content = chatClient.prompt()
.messages(userMessage)
.options(options)
.call()
.content();
} catch (Exception e) {
throw LlmExceptionMapper.toUnavailable(ErrorCode.AI_NO_RESPONSE, e);
}

if (content == null || content.trim().isEmpty()) {
throw new AiServiceUnavailableException(ErrorCode.AI_NO_RESPONSE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
import com.devkor.ifive.nadab.domain.monthlyreport.core.repository.MonthlyQueryRepository;
import com.devkor.ifive.nadab.domain.monthlyreport.core.service.MonthlyWeeklySummariesService;
import com.devkor.ifive.nadab.domain.monthlyreport.infra.MonthlyReportLlmClient;
import com.devkor.ifive.nadab.domain.reportlog.application.ReportGenerationLogRecorder;
import com.devkor.ifive.nadab.domain.reportlog.core.entity.ReportGenerationStep;
import com.devkor.ifive.nadab.domain.reportlog.core.entity.ReportGenerationType;
import com.devkor.ifive.nadab.domain.weeklyreport.application.helper.WeeklyEntriesAssembler;
import com.devkor.ifive.nadab.domain.weeklyreport.core.dto.DailyEntryDto;
import com.devkor.ifive.nadab.global.infra.llm.LlmProvider;
import com.devkor.ifive.nadab.global.shared.reportcontent.AiReportResultDto;
import com.devkor.ifive.nadab.global.shared.util.MonthRangeCalculator;
import com.devkor.ifive.nadab.global.shared.util.dto.MonthRangeDto;
Expand All @@ -27,18 +31,18 @@
@Slf4j
public class MonthlyReportGenerationListener {

private static final String MONTHLY_REPORT_LLM_MODEL = "GEMINI_2_5_FLASH";

private final MonthlyQueryRepository monthlyQueryRepository;

private final MonthlyReportLlmClient monthlyReportLlmClient;
private final MonthlyReportTxService monthlyReportTxService;
private final MonthlyWeeklySummariesService monthlyWeeklySummariesService;
private final ReportGenerationLogRecorder reportGenerationLogRecorder;
private final ApplicationEventPublisher eventPublisher;

private static final int MAX_LEN = 245;

@Async("monthlyReportTaskExecutor")
@TransactionalEventListener(phase =
TransactionPhase.AFTER_COMMIT)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(MonthlyReportGenerationRequestedEventDto event) {

MonthRangeDto range = MonthRangeCalculator.getLastMonthRange();
Expand All @@ -52,11 +56,25 @@ public void handle(MonthlyReportGenerationRequestedEventDto event) {
String weeklySummaries = monthlyWeeklySummariesService.buildWeeklySummaries(event.userId(), range);

AiReportResultDto dto;
Long generationLogId = reportGenerationLogRecorder.start(
event.userId(),
ReportGenerationType.MONTHLY,
event.reportId(),
ReportGenerationStep.MONTHLY_GENERATE,
LlmProvider.GEMINI,
MONTHLY_REPORT_LLM_MODEL
);
try {
// 트랜잭션 밖(백그라운드)에서 LLM 호출
dto = monthlyReportLlmClient.generate(
range.monthStartDate().toString(), range.monthEndDate().toString(), weeklySummaries, representativeEntries);
range.monthStartDate().toString(),
range.monthEndDate().toString(),
weeklySummaries,
representativeEntries
);
reportGenerationLogRecorder.succeed(generationLogId);
} catch (Exception e) {
reportGenerationLogRecorder.fail(generationLogId, e);
log.error("[MONTHLY_REPORT][LLM_FAILED] userId={}, reportId={}",
event.userId(), event.reportId(), e);

Expand All @@ -69,7 +87,14 @@ public void handle(MonthlyReportGenerationRequestedEventDto event) {
return;
}

// 성공 확정(별도 트랜잭션)
Long confirmLogId = reportGenerationLogRecorder.start(
event.userId(),
ReportGenerationType.MONTHLY,
event.reportId(),
ReportGenerationStep.MONTHLY_CONFIRM,
null,
null
);
try {
monthlyReportTxService.confirmMonthly(
event.reportId(),
Expand All @@ -81,8 +106,10 @@ public void handle(MonthlyReportGenerationRequestedEventDto event) {
eventPublisher.publishEvent(
new MonthlyReportCompletedEvent(event.reportId(), event.userId())
);
reportGenerationLogRecorder.succeed(confirmLogId);

} catch (Exception e) {
reportGenerationLogRecorder.fail(confirmLogId, e);
log.error("[MONTHLY_REPORT][CONFIRM_FAILED] userId={}, reportId={}, crystalLogId={}",
event.userId(), event.reportId(), event.crystalLogId(), e);

Expand All @@ -93,13 +120,5 @@ public void handle(MonthlyReportGenerationRequestedEventDto event) {
event.crystalLogId()
);
}

}

// 최대 길이 자르기
private String cut(String s) {
if (s == null) return null;
s = s.trim();
return (s.length() <= MAX_LEN) ? s : s.substring(0, MAX_LEN);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@
import com.devkor.ifive.nadab.domain.monthlyreport.infra.MonthlyReportImageStorage;
import com.devkor.ifive.nadab.domain.monthlyreport.infra.MonthlyReportLlmClientV2;
import com.devkor.ifive.nadab.domain.monthlyreport.infra.OpenAiImageClient;
import com.devkor.ifive.nadab.domain.reportlog.application.ReportGenerationLogRecorder;
import com.devkor.ifive.nadab.domain.reportlog.core.entity.ReportGenerationStep;
import com.devkor.ifive.nadab.domain.reportlog.core.entity.ReportGenerationType;
import com.devkor.ifive.nadab.domain.typereport.application.helper.TypeEmotionStatsCalculator;
import com.devkor.ifive.nadab.domain.typereport.core.content.TypeEmotionStatsContent;
import com.devkor.ifive.nadab.domain.weeklyreport.application.helper.WeeklyEntriesAssembler;
import com.devkor.ifive.nadab.domain.weeklyreport.core.dto.DailyEntryDto;
import com.devkor.ifive.nadab.global.infra.llm.LlmProvider;
import com.devkor.ifive.nadab.global.shared.util.MonthRangeCalculator;
import com.devkor.ifive.nadab.global.shared.util.dto.MonthRangeDto;
import lombok.RequiredArgsConstructor;
Expand All @@ -34,6 +38,8 @@
@Slf4j
public class MonthlyReportGenerationListenerV2 {

private static final String MONTHLY_REPORT_V2_LLM_MODEL = "GEMINI_2_5_FLASH";

private final MonthlyQueryRepository monthlyQueryRepository;

private final MonthlyReportLlmClientV2 monthlyReportLlmClientV2;
Expand All @@ -42,11 +48,11 @@ public class MonthlyReportGenerationListenerV2 {

private final MonthlyReportTxServiceV2 monthlyReportTxServiceV2;
private final MonthlyWeeklySummariesService monthlyWeeklySummariesService;
private final ReportGenerationLogRecorder reportGenerationLogRecorder;
private final ApplicationEventPublisher eventPublisher;

@Async("monthlyReportTaskExecutor")
@TransactionalEventListener(phase =
TransactionPhase.AFTER_COMMIT)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(MonthlyReportGenerationRequestedEventDtoV2 event) {

MonthRangeDto range = MonthRangeCalculator.getLastMonthRange();
Expand Down Expand Up @@ -103,6 +109,14 @@ public void handle(MonthlyReportGenerationRequestedEventDtoV2 event) {
}

AiMonthlyReportResultDto dto;
Long generationLogId = reportGenerationLogRecorder.start(
event.userId(),
ReportGenerationType.MONTHLY_V2,
event.reportId(),
ReportGenerationStep.MONTHLY_V2_GENERATE,
LlmProvider.GEMINI,
MONTHLY_REPORT_V2_LLM_MODEL
);
try {
// 트랜잭션 밖(백그라운드)에서 LLM 호출
dto = monthlyReportLlmClientV2.generate(
Expand All @@ -111,8 +125,11 @@ public void handle(MonthlyReportGenerationRequestedEventDtoV2 event) {
weeklySummaries,
representativeEntries,
emotionStats,
event.exists());
event.exists()
);
reportGenerationLogRecorder.succeed(generationLogId);
} catch (Exception e) {
reportGenerationLogRecorder.fail(generationLogId, e);
log.error("[MONTHLY_REPORT][LLM_FAILED] userId={}, reportId={}",
event.userId(), event.reportId(), e);

Expand All @@ -125,7 +142,14 @@ public void handle(MonthlyReportGenerationRequestedEventDtoV2 event) {
return;
}

// 텍스트 생성 성공 확정(별도 트랜잭션)
Long textConfirmLogId = reportGenerationLogRecorder.start(
event.userId(),
ReportGenerationType.MONTHLY_V2,
event.reportId(),
ReportGenerationStep.MONTHLY_V2_TEXT_CONFIRM,
null,
null
);
try {
monthlyReportTxServiceV2.confirmMonthlyText(
event.reportId(),
Expand All @@ -134,8 +158,10 @@ public void handle(MonthlyReportGenerationRequestedEventDtoV2 event) {
emotionStats,
interestStats
);
reportGenerationLogRecorder.succeed(textConfirmLogId);

} catch (Exception e) {
reportGenerationLogRecorder.fail(textConfirmLogId, e);
log.error("[MONTHLY_REPORT][TEXT_CONFIRM_FAILED] userId={}, reportId={}, crystalLogId={}",
event.userId(), event.reportId(), event.crystalLogId(), e);

Expand All @@ -148,16 +174,26 @@ public void handle(MonthlyReportGenerationRequestedEventDtoV2 event) {
return;
}

String imageKey = "";
String imageKey;
Long imageLogId = reportGenerationLogRecorder.start(
event.userId(),
ReportGenerationType.MONTHLY_V2,
event.reportId(),
ReportGenerationStep.MONTHLY_V2_IMAGE_GENERATE,
LlmProvider.OPENAI,
null
);
try {
String base64Image = openAiImageClient.generateBase64Image(event.userId(), dto, range);
imageKey = monthlyReportImageStorage.uploadBase64Webp(
event.userId(),
event.reportId(),
base64Image
);
reportGenerationLogRecorder.succeed(imageLogId);

} catch (Exception e) {
reportGenerationLogRecorder.fail(imageLogId, e);
log.error("[MONTHLY_REPORT][IMAGE_FAILED] userId={}, reportId={}, crystalLogId={}",
event.userId(), event.reportId(), event.crystalLogId(), e);

Expand All @@ -169,6 +205,14 @@ public void handle(MonthlyReportGenerationRequestedEventDtoV2 event) {
return;
}

Long confirmLogId = reportGenerationLogRecorder.start(
event.userId(),
ReportGenerationType.MONTHLY_V2,
event.reportId(),
ReportGenerationStep.MONTHLY_V2_CONFIRM,
null,
null
);
try {
monthlyReportTxServiceV2.confirmMonthly(
event.reportId(),
Expand All @@ -180,8 +224,10 @@ public void handle(MonthlyReportGenerationRequestedEventDtoV2 event) {
eventPublisher.publishEvent(
new MonthlyReportCompletedEvent(event.reportId(), event.userId())
);
reportGenerationLogRecorder.succeed(confirmLogId);

} catch (Exception e) {
reportGenerationLogRecorder.fail(confirmLogId, e);
log.error("[MONTHLY_REPORT][CONFIRM_FAILED] userId={}, reportId={}, crystalLogId={}",
event.userId(), event.reportId(), event.crystalLogId(), e);

Expand All @@ -194,4 +240,3 @@ public void handle(MonthlyReportGenerationRequestedEventDtoV2 event) {
}
}
}

Loading
Loading