-
Notifications
You must be signed in to change notification settings - Fork 0
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
Refactor: 포인트관련 로직을 포인트 서비스계층으로 통합, 개별 트랜잭션 적용, 테스트 코드 개선 #132
Changes from all commits
d77cff3
5c15109
e6a6982
3e865f0
7642b1f
b2f8984
7b5aa1a
97cd2f2
ec6622b
0084704
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 |
---|---|---|
|
@@ -13,10 +13,8 @@ | |
import com.example.sinitto.member.entity.Member; | ||
import com.example.sinitto.member.entity.Senior; | ||
import com.example.sinitto.member.repository.MemberRepository; | ||
import com.example.sinitto.point.entity.Point; | ||
import com.example.sinitto.point.entity.PointLog; | ||
import com.example.sinitto.point.repository.PointLogRepository; | ||
import com.example.sinitto.point.repository.PointRepository; | ||
import com.example.sinitto.point.service.PointService; | ||
import org.springframework.data.domain.Page; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.scheduling.annotation.Scheduled; | ||
|
@@ -38,21 +36,19 @@ public class CallbackService { | |
private final CallbackRepository callbackRepository; | ||
private final MemberRepository memberRepository; | ||
private final SeniorRepository seniorRepository; | ||
private final PointRepository pointRepository; | ||
private final PointLogRepository pointLogRepository; | ||
private final PointService pointService; | ||
|
||
public CallbackService(CallbackRepository callbackRepository, MemberRepository memberRepository, SeniorRepository seniorRepository, PointRepository pointRepository, PointLogRepository pointLogRepository) { | ||
public CallbackService(CallbackRepository callbackRepository, MemberRepository memberRepository, SeniorRepository seniorRepository, PointService pointService) { | ||
this.callbackRepository = callbackRepository; | ||
this.memberRepository = memberRepository; | ||
this.seniorRepository = seniorRepository; | ||
this.pointRepository = pointRepository; | ||
this.pointLogRepository = pointLogRepository; | ||
this.pointService = pointService; | ||
} | ||
|
||
@Transactional(readOnly = true) | ||
public Page<CallbackResponse> getWaitingCallbacks(Long memberId, Pageable pageable) { | ||
|
||
checkAuthorization(memberId); | ||
checkIsSinitto(memberId); | ||
|
||
return callbackRepository.findAllByStatus(Callback.Status.WAITING, pageable) | ||
.map((callback) -> new CallbackResponse(callback.getId(), callback.getSeniorName(), callback.getPostTime(), callback.getStatus(), callback.getSeniorId())); | ||
|
@@ -61,7 +57,7 @@ public Page<CallbackResponse> getWaitingCallbacks(Long memberId, Pageable pageab | |
@Transactional | ||
public void acceptCallbackBySinitto(Long memberId, Long callbackId) { | ||
|
||
checkAuthorization(memberId); | ||
checkIsSinitto(memberId); | ||
|
||
if (callbackRepository.existsByAssignedMemberIdAndStatus(memberId, Callback.Status.IN_PROGRESS)) { | ||
throw new ConflictException("이 요청을 한 시니또는 이미 진행중인 콜백이 있습니다."); | ||
|
@@ -76,7 +72,7 @@ public void acceptCallbackBySinitto(Long memberId, Long callbackId) { | |
@Transactional | ||
public void changeCallbackStatusToPendingCompleteBySinitto(Long memberId, Long callbackId) { | ||
|
||
checkAuthorization(memberId); | ||
checkIsSinitto(memberId); | ||
|
||
Callback callback = getCallbackOrThrow(callbackId); | ||
|
||
|
@@ -95,42 +91,36 @@ public void changeCallbackStatusToCompleteByGuard(Long memberId, Long callbackId | |
Long guardId = senior.getMember().getId(); | ||
|
||
if (!guardId.equals(memberId)) { | ||
throw new ForbiddenException("이 API를 요청한 보호자는 이 콜백을 요청 한 시니어의 보호자가 아닙니다."); | ||
throw new ForbiddenException("이 API 를 요청한 보호자는 이 콜백을 요청 한 시니어의 보호자가 아닙니다."); | ||
} | ||
|
||
earnPointForSinitto(callback.getAssignedMemberId()); | ||
pointService.earnPoint(callback.getAssignedMemberId(), CALLBACK_PRICE, PointLog.Content.COMPLETE_CALLBACK_AND_EARN); | ||
callback.changeStatusToComplete(); | ||
} | ||
|
||
private void earnPointForSinitto(Long sinittoMemberId) { | ||
|
||
Point sinittoPoint = pointRepository.findByMemberId(sinittoMemberId) | ||
.orElseThrow(() -> new NotFoundException("포인트 적립 받을 시니또와 연관된 포인트가 없습니다")); | ||
|
||
sinittoPoint.earn(CALLBACK_PRICE); | ||
|
||
pointLogRepository.save(new PointLog(PointLog.Content.COMPLETE_CALLBACK_AND_EARN.getMessage(), sinittoPoint.getMember(), CALLBACK_PRICE, PointLog.Status.EARN)); | ||
} | ||
|
||
@Scheduled(cron = "0 */10 * * * *") | ||
@Transactional | ||
public void changeOldPendingCompleteToCompleteByPolicy() { | ||
|
||
LocalDateTime referenceDateTimeForComplete = LocalDateTime.now().minusDays(DAYS_FOR_AUTO_COMPLETE); | ||
|
||
List<Callback> callbacks = callbackRepository.findAllByStatusAndPendingCompleteTimeBefore(Callback.Status.PENDING_COMPLETE, referenceDateTimeForComplete); | ||
List<Callback> callbacks = callbackRepository.findAllByStatusAndPendingCompleteTimeBetween(Callback.Status.PENDING_COMPLETE, referenceDateTimeForComplete.minusMinutes(5), referenceDateTimeForComplete.plusMinutes(5)); | ||
|
||
for (Callback callback : callbacks) { | ||
|
||
earnPointForSinitto(callback.getAssignedMemberId()); | ||
callback.changeStatusToComplete(); | ||
completeCallbackIndividually(callback); | ||
} | ||
} | ||
|
||
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. transaction의 크기를 줄일 수 있어서 좋은 것 같습니다!! |
||
@Transactional | ||
public void completeCallbackIndividually(Callback callback) { | ||
|
||
pointService.earnPoint(callback.getAssignedMemberId(), CALLBACK_PRICE, PointLog.Content.COMPLETE_CALLBACK_AND_EARN); | ||
callback.changeStatusToComplete(); | ||
} | ||
|
||
Comment on lines
+109
to
+119
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. 개인적으로는 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. 의견 감사합니다! 지호님 말씀대로 개별 트랜잭션이 콜백 상태를 좀 신속히? 저장시켜 좋은거 같네요. pointService 메서드 호출에 관한 말씀은 제가 잘 이해했는지 모르겠는데 나름 찾아본거 공유해드려보면, JVM 에서 최적화도 해주는것도 있고 메모리에 이미 올라가있는 객체의 메서드를 참조하는 방식이라 오버헤드가 발생하긴 하지만 엄청 크지는 않을걸로 예상이 됩니다. 또한 이 방식이 다소 리소스를 더 먹더라도 개발자 입장에서 유지보수 측면에서의 이득이 더 크다라는 관점에서 보면 좋은점도 있는거 같아요! 장단점이 다있어 보여요. 아니면 pointService 메서드 호출에서 포인트 DB 관련해서 오버헤드를 말씀하신 걸까요? 요 부분은 콜백 상태변화랑 포인트 적립/차감이 땔 수 없는 부분이라 어쩔수 없을거 같기는 합니다.🥲 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.
아하! 하나 배워갑니다 감사합니다 ㅎㅎ 해당 의견 관련한 부분 남긴게 맞습니다 👍 |
||
@Transactional | ||
public void cancelCallbackAssignmentBySinitto(Long memberId, Long callbackId) { | ||
|
||
checkAuthorization(memberId); | ||
checkIsSinitto(memberId); | ||
|
||
Callback callback = getCallbackOrThrow(callbackId); | ||
|
||
|
@@ -145,55 +135,40 @@ public String createCallbackByCall(String fromNumber) { | |
|
||
String phoneNumber = TwilioHelper.trimPhoneNumber(fromNumber); | ||
|
||
Senior senior = findSeniorByPhoneNumber(phoneNumber); | ||
Senior senior = seniorRepository.findByPhoneNumber(phoneNumber) | ||
.orElse(null); | ||
|
||
if (senior == null) { | ||
return TwilioHelper.convertMessageToTwiML(FAIL_MESSAGE_NOT_ENROLLED); | ||
} | ||
|
||
Point point = findPointWithWriteLock(senior.getMember().getId()); | ||
if (point == null || !point.isSufficientForDeduction(CALLBACK_PRICE)) { | ||
return TwilioHelper.convertMessageToTwiML(FAIL_MESSAGE_NOT_ENOUGH_POINT); | ||
} | ||
|
||
if (callbackRepository.existsBySeniorAndStatusIn(senior, List.of(Callback.Status.WAITING, Callback.Status.IN_PROGRESS))) { | ||
return TwilioHelper.convertMessageToTwiML(FAIL_MESSAGE_ALREADY_HAS_CALLBACK_IN_PROGRESS_OR_WAITING); | ||
} | ||
|
||
point.deduct(CALLBACK_PRICE); | ||
try { | ||
pointService.deductPoint(senior.getMember().getId(), CALLBACK_PRICE, PointLog.Content.SPEND_COMPLETE_CALLBACK); | ||
} catch (Exception e) { | ||
return TwilioHelper.convertMessageToTwiML(FAIL_MESSAGE_NOT_ENOUGH_POINT); | ||
} | ||
Comment on lines
+149
to
+153
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. point관련 로직이라 포괄적인 try-catch문 사용 좋은 것 같습니다 👍 |
||
|
||
pointLogRepository.save( | ||
new PointLog( | ||
PointLog.Content.SPEND_COMPLETE_CALLBACK.getMessage(), | ||
senior.getMember(), | ||
CALLBACK_PRICE, | ||
PointLog.Status.SPEND_COMPLETE) | ||
); | ||
callbackRepository.save(new Callback(Callback.Status.WAITING, senior)); | ||
|
||
return TwilioHelper.convertMessageToTwiML(SUCCESS_MESSAGE); | ||
} | ||
|
||
private Senior findSeniorByPhoneNumber(String phoneNumber) { | ||
return seniorRepository.findByPhoneNumber(phoneNumber) | ||
.orElse(null); | ||
} | ||
|
||
private Point findPointWithWriteLock(Long memberId) { | ||
return pointRepository.findByMemberIdWithWriteLock(memberId) | ||
.orElse(null); | ||
} | ||
|
||
@Transactional(readOnly = true) | ||
public CallbackResponse getAcceptedCallback(Long memberId) { | ||
|
||
checkAuthorization(memberId); | ||
checkIsSinitto(memberId); | ||
|
||
Callback callback = callbackRepository.findByAssignedMemberIdAndStatus(memberId, Callback.Status.IN_PROGRESS) | ||
.orElseThrow(() -> new NotFoundException("요청한 시니또에 할당된 콜백이 없습니다")); | ||
|
||
return new CallbackResponse(callback.getId(), callback.getSeniorName(), callback.getPostTime(), callback.getStatus(), callback.getSeniorId()); | ||
} | ||
|
||
private void checkAuthorization(Long memberId) { | ||
private void checkIsSinitto(Long memberId) { | ||
|
||
Member member = memberRepository.findById(memberId) | ||
.orElseThrow(() -> new NotFoundException("멤버가 아닙니다")); | ||
|
@@ -224,11 +199,11 @@ public Page<CallbackUsageHistoryResponse> getCallbackHistoryOfGuard(Long memberI | |
|
||
List<Senior> seniors = seniorRepository.findAllByMember(member); | ||
|
||
|
||
return callbackRepository.findAllBySeniorIn(seniors, pageable) | ||
.map(callback -> new CallbackUsageHistoryResponse(callback.getId(), callback.getSeniorName(), callback.getPostTime(), callback.getStatus())); | ||
} | ||
|
||
@Transactional(readOnly = true) | ||
public CallbackForSinittoResponse getCallbackForSinitto(Long memberId, Long callbackId) { | ||
|
||
Callback callback = callbackRepository.findById(callbackId) | ||
|
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.
👍