Skip to content

Commit

Permalink
Feat: 콜백 서비스 정책 적용 그리고 이용 내역 조회 기능 (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
zzoe2346 authored Oct 14, 2024
1 parent 720af06 commit 66866c9
Show file tree
Hide file tree
Showing 13 changed files with 336 additions and 33 deletions.
2 changes: 2 additions & 0 deletions src/main/java/com/example/sinitto/SinittoApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableJpaAuditing
@EnableConfigurationProperties(KakaoProperties.class)
@EnableScheduling
public class SinittoApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.sinitto.callback.controller;

import com.example.sinitto.callback.dto.CallbackResponse;
import com.example.sinitto.callback.dto.CallbackUsageHistoryResponse;
import com.example.sinitto.callback.service.CallbackService;
import com.example.sinitto.common.annotation.MemberId;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -25,10 +26,10 @@ public CallbackController(CallbackService callbackService) {

@Operation(summary = "콜백 전화 리스트 보기(페이지)", description = "시니어가 요청한 콜백전화를 페이징으로 보여줍니다.")
@GetMapping
public ResponseEntity<Page<CallbackResponse>> getCallbackList(@MemberId Long memberId,
@PageableDefault(sort = "postTime", direction = Sort.Direction.DESC) Pageable pageable) {
public ResponseEntity<Page<CallbackResponse>> getWaitingCallbackList(@MemberId Long memberId,
@PageableDefault(sort = "postTime", direction = Sort.Direction.DESC) Pageable pageable) {

return ResponseEntity.ok(callbackService.getCallbacks(memberId, pageable));
return ResponseEntity.ok(callbackService.getWaitingCallbacks(memberId, pageable));
}

@Operation(summary = "진행 상태인 콜백을 완료 대기 상태로 전환(시니또가)", description = "시니또가 수락한 콜백 수행을 완료했을때 이 api 호출하면 완료 대기 상태로 변합니다.")
Expand All @@ -45,7 +46,7 @@ public ResponseEntity<Void> pendingCompleteCallback(@MemberId Long memberId,
public ResponseEntity<Void> completeCallback(@MemberId Long memberId,
@PathVariable Long callbackId) {

callbackService.complete(memberId, callbackId);
callbackService.changeCallbackStatusToCompleteByGuard(memberId, callbackId);
return ResponseEntity.ok().build();
}

Expand Down Expand Up @@ -79,4 +80,12 @@ public ResponseEntity<CallbackResponse> getAcceptedCallback(@MemberId Long membe

return ResponseEntity.ok(callbackService.getAcceptedCallback(memberId));
}

@Operation(summary = "보호자의 콜백 이용 내역 조회", description = "보호자에게 연관된 모든 콜백 내역을 조회합니다. 기본적으로 최신 내역 부터 조회됩니다.")
@GetMapping("/guard/requested")
public ResponseEntity<Page<CallbackUsageHistoryResponse>> getAcceptedCallback(@MemberId Long memberId,
@PageableDefault(sort = "postTime", direction = Sort.Direction.DESC) Pageable pageable) {

return ResponseEntity.ok(callbackService.getCallbackHistoryOfGuard(memberId, pageable));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.sinitto.callback.dto;

import java.time.LocalDateTime;

public record CallbackUsageHistoryResponse(
Long callbackId,
String seniorName,
LocalDateTime postTime,
String status
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class Callback {
private Long id;
@CreatedDate
private LocalDateTime postTime;
private LocalDateTime pendingCompleteTime;
@NotNull
@Enumerated(EnumType.STRING)
private Callback.Status status;
Expand Down Expand Up @@ -140,6 +141,14 @@ public Long getAssignedMemberId() {
return assignedMemberId;
}

public LocalDateTime getPendingCompleteTime() {
return pendingCompleteTime;
}

public void setPendingCompleteTime(LocalDateTime pendingCompleteTime) {
this.pendingCompleteTime = pendingCompleteTime;
}

public enum Status {
WAITING,
IN_PROGRESS,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.sinitto.callback.exception;

public class MemberHasInProgressCallbackException extends ConflictException {

public MemberHasInProgressCallbackException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package com.example.sinitto.callback.repository;

import com.example.sinitto.callback.entity.Callback;
import com.example.sinitto.member.entity.Senior;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

public interface CallbackRepository extends JpaRepository<Callback, Long> {

Page<Callback> findAll(Pageable pageable);
Page<Callback> findAllByStatus(Callback.Status status, Pageable pageable);

Optional<Callback> findByAssignedMemberIdAndStatus(Long memberId, Callback.Status status);

boolean existsByAssignedMemberIdAndStatus(Long memberId, Callback.Status status);

Page<Callback> findAllBySeniorIn(List<Senior> seniors, Pageable pageable);

List<Callback> findAllByStatusAndPendingCompleteTimeBefore(Callback.Status status, LocalDateTime dateTime);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.sinitto.callback.service;

import com.example.sinitto.callback.dto.CallbackResponse;
import com.example.sinitto.callback.dto.CallbackUsageHistoryResponse;
import com.example.sinitto.callback.entity.Callback;
import com.example.sinitto.callback.exception.*;
import com.example.sinitto.callback.repository.CallbackRepository;
Expand All @@ -16,13 +17,18 @@
import com.example.sinitto.point.repository.PointRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class CallbackService {

public static final int CALLBACK_PRICE = 1500;
private static final int CALLBACK_PRICE = 1500;
private static final int DAYS_FOR_AUTO_COMPLETE = 2;
private static final String SUCCESS_MESSAGE = "감사합니다. 잠시만 기다려주세요.";
private static final String FAIL_MESSAGE_NOT_ENROLLED = "등록된 사용자가 아닙니다. 서비스 이용이 불가합니다.";
private static final String FAIL_MESSAGE_NOT_ENOUGH_POINT = "포인트가 부족합니다. 서비스 이용이 불가합니다.";
Expand All @@ -41,11 +47,11 @@ public CallbackService(CallbackRepository callbackRepository, MemberRepository m
}

@Transactional(readOnly = true)
public Page<CallbackResponse> getCallbacks(Long memberId, Pageable pageable) {
public Page<CallbackResponse> getWaitingCallbacks(Long memberId, Pageable pageable) {

checkAuthorization(memberId);

return callbackRepository.findAll(pageable)
return callbackRepository.findAllByStatus(Callback.Status.WAITING, pageable)
.map((callback) -> new CallbackResponse(callback.getId(), callback.getSeniorName(), callback.getPostTime(), callback.getStatus(), callback.getSeniorId()));
}

Expand All @@ -54,6 +60,10 @@ public void accept(Long memberId, Long callbackId) {

checkAuthorization(memberId);

if (callbackRepository.existsByAssignedMemberIdAndStatus(memberId, Callback.Status.IN_PROGRESS)) {
throw new MemberHasInProgressCallbackException("이 요청을 한 시니또는 이미 진행중인 콜백이 있습니다.");
}

Callback callback = getCallbackOrThrow(callbackId);

callback.assignMember(memberId);
Expand All @@ -70,10 +80,11 @@ public void pendingComplete(Long memberId, Long callbackId) {
checkAssignment(memberId, callback.getAssignedMemberId());

callback.changeStatusToPendingComplete();
callback.setPendingCompleteTime(LocalDateTime.now());
}

@Transactional
public void complete(Long memberId, Long callbackId) {
public void changeCallbackStatusToCompleteByGuard(Long memberId, Long callbackId) {

Callback callback = getCallbackOrThrow(callbackId);

Expand All @@ -84,12 +95,33 @@ public void complete(Long memberId, Long callbackId) {
throw new GuardMismatchException("이 API를 요청한 보호자는 이 콜백을 요청 한 시니어의 보호자가 아닙니다.");
}

Point sinittoPoint = pointRepository.findByMemberId(callback.getAssignedMemberId())
earnPointForSinitto(callback.getAssignedMemberId());
callback.changeStatusToComplete();
}

private void earnPointForSinitto(Long sinittoMemberId) {

Point sinittoPoint = pointRepository.findByMemberId(sinittoMemberId)
.orElseThrow(() -> new PointNotFoundException("포인트 적립 받을 시니또와 연관된 포인트가 없습니다"));

sinittoPoint.earn(CALLBACK_PRICE);

pointLogRepository.save(new PointLog(PointLog.Content.COMPLETE_CALLBACK_AND_EARN.getMessage(), sinittoPoint.getMember(), CALLBACK_PRICE, PointLog.Status.EARN));
}

callback.changeStatusToComplete();
@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);

for (Callback callback : callbacks) {

earnPointForSinitto(callback.getAssignedMemberId());
callback.changeStatusToComplete();
}
}

@Transactional
Expand Down Expand Up @@ -176,4 +208,17 @@ private void checkAssignment(Long memberId, Long assignedMemberId) {
throw new NotAssignedException("이 콜백에 할당된 시니또가 아닙니다");
}
}

@Transactional(readOnly = true)
public Page<CallbackUsageHistoryResponse> getCallbackHistoryOfGuard(Long memberId, Pageable pageable) {

Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new NotMemberException("멤버가 아닙니다"));

List<Senior> seniors = seniorRepository.findAllByMember(member);


return callbackRepository.findAllBySeniorIn(seniors, pageable)
.map(callback -> new CallbackUsageHistoryResponse(callback.getId(), callback.getSeniorName(), callback.getPostTime(), callback.getStatus()));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.sinitto.guard.repository;

import com.example.sinitto.member.entity.Member;
import com.example.sinitto.member.entity.Senior;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
Expand All @@ -14,4 +15,6 @@ public interface SeniorRepository extends JpaRepository<Senior, Long> {
Optional<Senior> findByIdAndMemberId(Long Id, Long memberId);

Optional<Senior> findByPhoneNumber(String phoneNumber);

List<Senior> findAllByMember(Member member);
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public String showAllChargeRequest(Model model) {
}
model.addAttribute("logWithDepositMessages", logWithDepositMessages);

return "/point/charge";
return "point/charge";
}

@PostMapping("/admin/point/charge/waiting/{pointLogId}")
Expand Down Expand Up @@ -101,7 +101,7 @@ public String showAllWithdrawRequest(Model model) {
}
model.addAttribute("logWithBankInfos", logWithBankInfos);

return "/point/withdraw";
return "point/withdraw";
}

@PostMapping("/admin/point/withdraw/waiting/{pointLogId}")
Expand Down
5 changes: 1 addition & 4 deletions src/main/java/com/example/sinitto/point/entity/Point.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ public void deduct(int priceToDeduct) {

public boolean isSufficientForDeduction(int priceToDeduct) {

if (this.price < priceToDeduct) {
return false;
}
return true;
return this.price >= priceToDeduct;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

Expand All @@ -27,14 +33,14 @@ class CallbackTest {
@Autowired
private SeniorRepository seniorRepository;
private Callback testCallback;
private Member testMember;
private Senior testSenior;

@BeforeEach
void setUp() {
Member testMember = new Member("member", "01043214321", "[email protected]", true);
memberRepository.save(testMember);
testMember = memberRepository.save(new Member("member", "01043214321", "[email protected]", true));

Senior testSenior = new Senior("senior", "01012341234", testMember);
seniorRepository.save(testSenior);
testSenior = seniorRepository.save(new Senior("senior", "01012341234", testMember));

testCallback = callbackRepository.save(new Callback(Callback.Status.WAITING, testSenior));
}
Expand Down Expand Up @@ -264,4 +270,23 @@ void changeStatusToComplete_fail2() {
assertThrows(AlreadyInProgressException.class, () -> testCallback.changeStatusToComplete());
}

@Test
@DisplayName("보호자에 연관된 다수의 시니어 리스트를 얻은후, 다수의 시니어들이 신청한 콜백 요청 기록 조회 테스트(CallbackService 의 getCallbackHistoryOfGuard() 테스트나 마찬가지)")
void getSeniorListAndReadCallbackHistoryOfSeniors() {
//given
Senior testSenior2 = new Senior("senior2", "01099999999", testMember);
seniorRepository.save(testSenior2);

callbackRepository.save(new Callback(Callback.Status.WAITING, testSenior2));
callbackRepository.save(new Callback(Callback.Status.COMPLETE, testSenior2));
Pageable pageable = PageRequest.of(0, 1, Sort.by(Sort.Direction.DESC, "postTime"));
//when
Page<Callback> result = callbackRepository.findAllBySeniorIn(List.of(testSenior, testSenior2), pageable);

//then
assertEquals(3, result.getTotalPages());
assertEquals(testSenior2, result.getContent().getFirst().getSenior());
assertEquals(Callback.Status.COMPLETE.toString(), result.getContent().getFirst().getStatus());
}

}
Loading

0 comments on commit 66866c9

Please sign in to comment.