-
Notifications
You must be signed in to change notification settings - Fork 2
MOSU-171 refactor: profile, 문의 답변 알림톡 기능 구현 및 cookie util 표준화 #172
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
Changes from all commits
be9f6c5
929e872
f73bdbf
d731e2e
162f858
8e356f5
6f1bf70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||||||||||||||||||||||||||||||||
| package life.mosu.mosuserver.application.inquiry; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| import life.mosu.mosuserver.application.inquiry.tx.InquiryAnswerContext; | ||||||||||||||||||||||||||||||||||||
| import life.mosu.mosuserver.application.inquiry.tx.InquiryAnswerTxEventFactory; | ||||||||||||||||||||||||||||||||||||
| import life.mosu.mosuserver.global.tx.TxEvent; | ||||||||||||||||||||||||||||||||||||
| import life.mosu.mosuserver.global.tx.TxEventPublisher; | ||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Service; | ||||||||||||||||||||||||||||||||||||
| import org.springframework.transaction.annotation.Transactional; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| @Slf4j | ||||||||||||||||||||||||||||||||||||
| @Service | ||||||||||||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||||||||||||
| public class InquiryAnswerTxService { | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| private final TxEventPublisher txEventPublisher; | ||||||||||||||||||||||||||||||||||||
| private final InquiryAnswerTxEventFactory eventFactory; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| @Transactional | ||||||||||||||||||||||||||||||||||||
| public void publishSuccessEvent(Long userId, Long inquiryId) { | ||||||||||||||||||||||||||||||||||||
| TxEvent<?> event = eventFactory.create(InquiryAnswerContext.ofSuccess(userId, inquiryId)); | ||||||||||||||||||||||||||||||||||||
| txEventPublisher.publish(event); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add parameter validation for business logic integrity. The method accepts Apply this diff to add validation: @Transactional
public void publishSuccessEvent(Long userId, Long inquiryId) {
+ if (userId == null || userId <= 0) {
+ throw new IllegalArgumentException("UserId must be a positive number");
+ }
+ if (inquiryId == null || inquiryId <= 0) {
+ throw new IllegalArgumentException("InquiryId must be a positive number");
+ }
+
TxEvent<?> event = eventFactory.create(InquiryAnswerContext.ofSuccess(userId, inquiryId));
txEventPublisher.publish(event);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package life.mosu.mosuserver.application.inquiry.tx; | ||
|
|
||
| public record InquiryAnswerContext( | ||
| Long userId, | ||
| Long inquiryId, | ||
| Boolean isSuccess | ||
| ) { | ||
|
|
||
| public static final InquiryAnswerContext ofSuccess(Long userId, Long inquiryId) { | ||
| return new InquiryAnswerContext(userId, inquiryId, true); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package life.mosu.mosuserver.application.inquiry.tx; | ||
|
|
||
| import life.mosu.mosuserver.global.tx.TxEvent; | ||
|
|
||
| public class InquiryAnswerTxEvent extends TxEvent<InquiryAnswerContext> { | ||
|
|
||
| public InquiryAnswerTxEvent(boolean isSuccess, InquiryAnswerContext context) { | ||
| super(isSuccess, context); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package life.mosu.mosuserver.application.inquiry.tx; | ||
|
|
||
| import life.mosu.mosuserver.global.tx.TxEvent; | ||
| import life.mosu.mosuserver.global.tx.TxEventFactory; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Component | ||
| public class InquiryAnswerTxEventFactory implements TxEventFactory<InquiryAnswerContext> { | ||
|
|
||
| @Override | ||
| public TxEvent<?> create(InquiryAnswerContext context) { | ||
| return new InquiryAnswerTxEvent(context.isSuccess(), context); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package life.mosu.mosuserver.application.inquiry.tx; | ||
|
|
||
| import life.mosu.mosuserver.infra.notify.NotifyEventPublisher; | ||
| import life.mosu.mosuserver.infra.notify.dto.NotificationEvent; | ||
| import life.mosu.mosuserver.infra.notify.dto.NotificationStatus; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.transaction.annotation.Propagation; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
| import org.springframework.transaction.event.TransactionPhase; | ||
| import org.springframework.transaction.event.TransactionalEventListener; | ||
|
|
||
| @Slf4j | ||
| @Component | ||
| @RequiredArgsConstructor | ||
| @Transactional(propagation = Propagation.NOT_SUPPORTED) | ||
| public class InquiryAnswerTxEventListener { | ||
|
|
||
| private final NotifyEventPublisher notifier; | ||
|
|
||
| @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
| public void afterCommitHandler(InquiryAnswerTxEvent event) { | ||
| InquiryAnswerContext ctx = event.getContext(); | ||
| log.debug("[AFTER_COMMIT] 문의 답변 등록 후 알림톡 발송 시작: userId={}, inquiryId={}", ctx.userId(), | ||
| ctx.inquiryId()); | ||
|
|
||
| sendNotification(ctx.userId(), ctx.inquiryId()); | ||
| } | ||
|
|
||
| private void sendNotification(Long userId, Long inquiryId) { | ||
| NotificationEvent notificationEvent = NotificationEvent.create( | ||
| NotificationStatus.INQUIRY_ANSWER_SUCCESS, userId, inquiryId); | ||
| notifier.notify(notificationEvent); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package life.mosu.mosuserver.application.profile; | ||
|
|
||
| import life.mosu.mosuserver.application.profile.tx.ProfileContext; | ||
| import life.mosu.mosuserver.application.profile.tx.ProfileTxEventFactory; | ||
| import life.mosu.mosuserver.global.tx.TxEvent; | ||
| import life.mosu.mosuserver.global.tx.TxEventPublisher; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @Slf4j | ||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class ProfileEventTxService { | ||
|
|
||
| private final TxEventPublisher txEventPublisher; | ||
| private final ProfileTxEventFactory eventFactory; | ||
|
|
||
| @Transactional | ||
| public void publishSuccessEvent(Long userId) { | ||
| TxEvent<?> event = eventFactory.create(ProfileContext.ofSuccess(userId)); | ||
| txEventPublisher.publish(event); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,24 +7,24 @@ | |||||||||||||||||
| import life.mosu.mosuserver.domain.user.UserRole; | ||||||||||||||||||
| import life.mosu.mosuserver.global.exception.CustomRuntimeException; | ||||||||||||||||||
| import life.mosu.mosuserver.global.exception.ErrorCode; | ||||||||||||||||||
| import life.mosu.mosuserver.infra.notify.NotifyEventPublisher; | ||||||||||||||||||
| import life.mosu.mosuserver.infra.notify.dto.NotificationEvent; | ||||||||||||||||||
| import life.mosu.mosuserver.infra.notify.dto.NotificationStatus; | ||||||||||||||||||
| import life.mosu.mosuserver.presentation.profile.dto.EditProfileRequest; | ||||||||||||||||||
| import life.mosu.mosuserver.presentation.profile.dto.ProfileDetailResponse; | ||||||||||||||||||
| import life.mosu.mosuserver.presentation.profile.dto.SignUpProfileRequest; | ||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||
| import org.springframework.stereotype.Service; | ||||||||||||||||||
| import org.springframework.transaction.annotation.Propagation; | ||||||||||||||||||
| import org.springframework.transaction.annotation.Transactional; | ||||||||||||||||||
|
|
||||||||||||||||||
| @Slf4j | ||||||||||||||||||
| @Service | ||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||
| public class ProfileService { | ||||||||||||||||||
|
|
||||||||||||||||||
| private final UserJpaRepository userRepository; | ||||||||||||||||||
| private final ProfileJpaRepository profileJpaRepository; | ||||||||||||||||||
| private final NotifyEventPublisher notifier; | ||||||||||||||||||
|
|
||||||||||||||||||
| private final ProfileEventTxService eventTxService; | ||||||||||||||||||
|
|
||||||||||||||||||
| @Transactional | ||||||||||||||||||
| public void registerProfile(Long userId, SignUpProfileRequest request) { | ||||||||||||||||||
|
|
@@ -33,13 +33,20 @@ public void registerProfile(Long userId, SignUpProfileRequest request) { | |||||||||||||||||
| ); | ||||||||||||||||||
| checkIfProfileExistsForUser(user); | ||||||||||||||||||
|
|
||||||||||||||||||
| ProfileJpaEntity profile = request.toEntity(userId); | ||||||||||||||||||
| profileJpaRepository.save(profile); | ||||||||||||||||||
| try { | ||||||||||||||||||
| ProfileJpaEntity profile = request.toEntity(userId); | ||||||||||||||||||
| profileJpaRepository.save(profile); | ||||||||||||||||||
|
|
||||||||||||||||||
| user.grantUserRole(); | ||||||||||||||||||
| syncUserInfoFromProfile(user, request); | ||||||||||||||||||
|
|
||||||||||||||||||
| eventTxService.publishSuccessEvent(userId); | ||||||||||||||||||
|
|
||||||||||||||||||
| user.grantUserRole(); | ||||||||||||||||||
| syncUserInfoFromProfile(user, request); | ||||||||||||||||||
| } catch (Exception ex) { | ||||||||||||||||||
| log.error("프로필 등록 실패: {}", ex.getMessage(), ex); | ||||||||||||||||||
| throw ex; | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+45
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the To ensure the transaction correctly rolls back on any failure, please catch
Suggested change
|
||||||||||||||||||
|
|
||||||||||||||||||
| sendNotification(); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| @Transactional | ||||||||||||||||||
|
|
@@ -71,10 +78,5 @@ private void syncUserInfoFromProfile(UserJpaEntity user, SignUpProfileRequest re | |||||||||||||||||
| request.phoneNumber(), request.birth()); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| private void sendNotification() { | ||||||||||||||||||
| NotificationEvent event = NotificationEvent.create(NotificationStatus.SIGN_UP_SUCCESS); | ||||||||||||||||||
| notifier.notify(event); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package life.mosu.mosuserver.application.profile.tx; | ||
|
|
||
| public record ProfileContext( | ||
| Long userId, | ||
| Boolean isSuccess | ||
| ) { | ||
|
|
||
| public static ProfileContext ofSuccess(Long userId) { | ||
| return new ProfileContext(userId, true); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package life.mosu.mosuserver.application.profile.tx; | ||
|
|
||
| import life.mosu.mosuserver.global.tx.TxEvent; | ||
|
|
||
| public class ProfileTxEvent extends TxEvent<ProfileContext> { | ||
|
|
||
| public ProfileTxEvent(boolean isSuccess, ProfileContext context) { | ||
| super(isSuccess, context); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package life.mosu.mosuserver.application.profile.tx; | ||
|
|
||
| import life.mosu.mosuserver.global.tx.TxEvent; | ||
| import life.mosu.mosuserver.global.tx.TxEventFactory; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Component | ||
| public class ProfileTxEventFactory implements TxEventFactory<ProfileContext> { | ||
|
|
||
| @Override | ||
| public TxEvent<?> create(ProfileContext context) { | ||
| return new ProfileTxEvent(context.isSuccess(), context); | ||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Catching the generic
Exceptionis risky here. If a checked exception were thrown inside thetryblock, Spring's default transactional behavior would lead to a commit of the transaction upon re-throwing the checked exception. This is likely not the intended behavior, as any failure should probably result in a rollback.To ensure correctness, it's safer to catch
RuntimeExceptionor wrap the caught exception in a newRuntimeException.