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,25 @@
package com.mzc.secondproject.serverless.domain.opic.handler;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SNSEvent;
import com.google.gson.Gson;
import com.mzc.secondproject.serverless.domain.opic.service.EmailService;

public class EmailAsyncHandler implements RequestHandler<SNSEvent, Void> {
private final EmailService emailService = new EmailService();
private final Gson gson = new Gson();

@Override
public Void handleRequest(SNSEvent event, Context context) {
for (SNSEvent.SNSRecord record : event.getRecords()) {
String messageBody = record.getSNS().getMessage();
processMessage(messageBody);
}
return null;
}
private void processMessage(String body) {
// 메시지 파싱 및 타입 확인 (OPIC_REPORT_EMAIL)
// emailService.sendOPIcReportEmail 호출
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.mzc.secondproject.serverless.domain.opic.dto.response.CreateSessionResponse;
import com.mzc.secondproject.serverless.domain.opic.dto.response.FeedbackResponse;
import com.mzc.secondproject.serverless.domain.opic.dto.response.QuestionResponse;
import com.mzc.secondproject.serverless.domain.opic.dto.response.SessionReportResponse;
import com.mzc.secondproject.serverless.domain.opic.model.OPIcAnswer;
import com.mzc.secondproject.serverless.domain.opic.model.OPIcQuestion;
import com.mzc.secondproject.serverless.domain.opic.model.OPIcSession;
Expand Down Expand Up @@ -533,8 +534,8 @@ private APIGatewayProxyResponseEvent completeSession(APIGatewayProxyRequestEvent
String userName = CognitoUtil.extractNickname(event).orElse("학습자");

if (userEmail != null && !userEmail.isEmpty()) {
emailService.sendOPIcReportEmail(userEmail, userName, sessionReport);
logger.info("리포트 이메일 발송 완료: to={}", userEmail);
publishEmailToSNS(userEmail, userName, sessionReport);
logger.info("이메일 발송 SNS 요청 발행: to={}", userEmail);
}
} catch (Exception e) {
// 이메일 실패해도 세션 완료는 성공 처리
Expand All @@ -554,7 +555,29 @@ private APIGatewayProxyResponseEvent completeSession(APIGatewayProxyRequestEvent
return ResponseGenerator.ok("세션이 완료되었습니다.", sessionReport);
}

// ==================== 유틸리티 ====================
/**
* 이메일 발송용 SNS 메시지 발행
*/
private void publishEmailToSNS(String email, String userName, SessionReportResponse report) {
try {
String topicArn = System.getenv("NOTIFICATION_TOPIC_ARN");

Map<String, Object> message = new HashMap<>();
message.put("type", "OPIC_REPORT_EMAIL");
message.put("recipientEmail", email);
message.put("userName", userName);
message.put("report", report); // 세션 리포트 객체 전달

AwsClients.sns().publish(PublishRequest.builder()
.topicArn(topicArn)
.message(gson.toJson(message))
.build());
} catch (Exception e) {
logger.error("이메일 SNS 발행 실패", e);
}
}

// ==================== 유틸리티 ====================Z

/**
* 질문 음성 URL 생성 (Polly + S3 캐싱)
Expand Down
21 changes: 20 additions & 1 deletion ServerlessFunction/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1622,7 +1622,7 @@ Resources:
Method: POST
Auth:
Authorizer: CognitoAuthV2
# 답변 상태 조회 (폴링용) ← 새로 추가!
# 답변 상태 조회 (폴링용)
GetAnswerStatus:
Type: Api
Properties:
Expand Down Expand Up @@ -1687,6 +1687,25 @@ Resources:
Properties:
Topic: !Ref AnswerProcessTopic

# 이메일 발송 전용 비동기 Lambda
OPIcEmailFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-opic-email-handler"
Handler: com.mzc.secondproject.serverless.domain.opic.handler.EmailAsyncHandler::handleRequest
Events:
SNSEvent:
Type: SNS
Properties:
Topic: !Ref NotificationTopic
Policies:
- Statement:
- Effect: Allow
Action:
- ses:SendEmail
- ses:SendRawEmail
Resource: "*"

#############################################
# Speaking Lambda Functions
#############################################
Expand Down