diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cac9c3e..6847c9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: # 어떤 이벤트와 브랜치에서 트리거 될건지 # push: # branches: [ "main" ] pull_request: - branches: [ "main", "dev" ] + branches: [ "deploy", "main", "dev" ] jobs: build: diff --git a/src/main/java/com/dangsim/chat/controller/ChatMessageStompController.java b/src/main/java/com/dangsim/chat/controller/ChatMessageStompController.java index 7372337..eb3ee0c 100644 --- a/src/main/java/com/dangsim/chat/controller/ChatMessageStompController.java +++ b/src/main/java/com/dangsim/chat/controller/ChatMessageStompController.java @@ -1,7 +1,5 @@ package com.dangsim.chat.controller; -import java.security.Principal; - import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.stereotype.Controller; @@ -21,10 +19,10 @@ public class ChatMessageStompController { @MessageMapping("/chat-rooms/{chatRoomId}") public void createChatMessage( @DestinationVariable Long chatRoomId, - Principal principal, @Valid CreateChatMessageRequest request ) { - Long userId = Long.valueOf(principal.getName()); + + Long userId = request.senderId(); chatMessageService.createChatMessage(request, userId, chatRoomId); } diff --git a/src/main/java/com/dangsim/chat/dto/ChatRoomMapper.java b/src/main/java/com/dangsim/chat/dto/ChatRoomMapper.java index 39d2098..58b3cb7 100644 --- a/src/main/java/com/dangsim/chat/dto/ChatRoomMapper.java +++ b/src/main/java/com/dangsim/chat/dto/ChatRoomMapper.java @@ -37,12 +37,13 @@ public static TaskInfoResponse toTaskInfoResponse(Task task) { } public static ChatRoomInfoResponse toChatRoomInfoResponse(Long chatRoomId, User chatPartner, - TaskInfoResponse taskInfoResponse) { + TaskInfoResponse taskInfoResponse, Long userId) { return new ChatRoomInfoResponse( chatRoomId, taskInfoResponse, chatPartner.getId(), - chatPartner.getNickname() + chatPartner.getNickname(), + userId ); } @@ -56,9 +57,9 @@ public static String getThumbNail(List imageUrls) { public static boolean isCompletedTask(Task task) { if (Objects.equals(task.getStatus(), TASK_COMPLETE)) { - return false; + return true; } - return true; + return false; } } diff --git a/src/main/java/com/dangsim/chat/dto/request/CreateChatMessageRequest.java b/src/main/java/com/dangsim/chat/dto/request/CreateChatMessageRequest.java index 0ddee86..44851f6 100644 --- a/src/main/java/com/dangsim/chat/dto/request/CreateChatMessageRequest.java +++ b/src/main/java/com/dangsim/chat/dto/request/CreateChatMessageRequest.java @@ -11,6 +11,8 @@ public record CreateChatMessageRequest( MessageType type, @Size(max = 300, message = "최대 300자까지 입력이 가능합니다.") @NotBlank - String content + String content, + @NotNull + Long senderId ) { } diff --git a/src/main/java/com/dangsim/chat/dto/response/ChatRoomInfoResponse.java b/src/main/java/com/dangsim/chat/dto/response/ChatRoomInfoResponse.java index 9fa6d77..cb2d3db 100644 --- a/src/main/java/com/dangsim/chat/dto/response/ChatRoomInfoResponse.java +++ b/src/main/java/com/dangsim/chat/dto/response/ChatRoomInfoResponse.java @@ -6,6 +6,7 @@ public record ChatRoomInfoResponse( Long chatRoomId, TaskInfoResponse taskInfo, Long chatPartnerId, - String partnerNickname + String partnerNickname, + Long myId ) { } diff --git a/src/main/java/com/dangsim/chat/dto/response/ChatRoomSimpleResponse.java b/src/main/java/com/dangsim/chat/dto/response/ChatRoomSimpleResponse.java index 75a7d80..d17706f 100644 --- a/src/main/java/com/dangsim/chat/dto/response/ChatRoomSimpleResponse.java +++ b/src/main/java/com/dangsim/chat/dto/response/ChatRoomSimpleResponse.java @@ -1,5 +1,7 @@ package com.dangsim.chat.dto.response; +import java.util.Objects; + import com.dangsim.chat.entity.ChatMessage; import com.dangsim.chat.entity.ChatRoom; import com.dangsim.common.util.DateTimeFormatUtils; @@ -12,7 +14,8 @@ public record ChatRoomSimpleResponse( String nickname, String content, String timestamp, - Boolean isRead + Boolean isRead, + Long chatMessageId ) { @QueryProjection @@ -21,9 +24,35 @@ public ChatRoomSimpleResponse(ChatRoom chatRoom, ChatMessage lastChatMessage, Us chatRoom.getId(), partner.getId(), partner.getNickname(), - lastChatMessage.getMessage(), - DateTimeFormatUtils.formatDateTime(lastChatMessage.getCreatedAt()), - lastChatMessage.isRead() + initChatMessage(lastChatMessage), + initTimeStamp(lastChatMessage), + initIsRead(lastChatMessage), + lastChatMessage.getId() ); } + + private static final String DEFAULT = "채팅을 시작해보세요"; + + public static String initChatMessage(ChatMessage lastChatMessage) { + if (Objects.isNull(lastChatMessage)) { + return DEFAULT; + } + return lastChatMessage.getMessage(); + } + + public static String initTimeStamp(ChatMessage lastChatMessage) { + if (Objects.isNull(lastChatMessage)) { + return ""; + } + return DateTimeFormatUtils.formatDateTime(lastChatMessage.getCreatedAt()); + } + + public static Boolean initIsRead(ChatMessage lastChatMessage) { + if (Objects.isNull(lastChatMessage)) { + return false; + } + return lastChatMessage.isRead(); + } + } + diff --git a/src/main/java/com/dangsim/chat/repository/ChatRoomQueryRepositoryImpl.java b/src/main/java/com/dangsim/chat/repository/ChatRoomQueryRepositoryImpl.java index aa57042..dd87ee0 100644 --- a/src/main/java/com/dangsim/chat/repository/ChatRoomQueryRepositoryImpl.java +++ b/src/main/java/com/dangsim/chat/repository/ChatRoomQueryRepositoryImpl.java @@ -33,15 +33,26 @@ public class ChatRoomQueryRepositoryImpl implements ChatRoomQueryRepository { private final JPAQueryFactory queryFactory; @Override - public CursorPageResponse findChatRoomsByCursor(String cursor, int size, Long userId) { + public CursorPageResponse findChatRoomsByCursor( + String cursor, int size, Long userId) { QChatMessage lastMessage = new QChatMessage("lastMessage"); QUser partner = new QUser("partner"); + BooleanExpression isRequester = chatRoom.requester.id.eq(userId) + .and(partner.id.eq(chatRoom.performer.id)); + BooleanExpression isPerformer = chatRoom.performer.id.eq(userId) + .and(partner.id.eq(chatRoom.requester.id)); + BooleanExpression partnerJoinCondition = isRequester.or(isPerformer); + + BooleanExpression cursorFilter = null; + if (cursor != null && !cursor.isBlank()) { + cursorFilter = lastMessage.id.lt(Long.parseLong(cursor)); + } + List chatRooms = queryFactory .select(new QChatRoomSimpleResponse(chatRoom, lastMessage, partner)) .from(chatRoom) - //최근 메시지 조인 .leftJoin(lastMessage) .on(lastMessage.chatRoomId.eq(chatRoom.id), lastMessage.id.eq( @@ -50,17 +61,14 @@ public CursorPageResponse findChatRoomsByCursor(String c .from(chatMessage) .where(chatMessage.chatRoomId.eq(chatRoom.id)) )) - //상대방 유저 조인 .leftJoin(partner) - .on( - chatRoom.requester.id.eq(userId).and(partner.id.eq(chatRoom.performer.id)) - .or(chatRoom.performer.id.eq(userId).and(partner.id.eq(chatRoom.requester.id))) - ) + .on(partnerJoinCondition) .where( - chatRoom.requester.id.eq(userId).or(chatRoom.performer.id.eq(userId)), - isBeforeCursor(lastMessage.createdAt, cursor) + chatRoom.requester.id.eq(userId) + .or(chatRoom.performer.id.eq(userId)), + cursorFilter ) - .orderBy(lastMessage.createdAt.desc()) + .orderBy(lastMessage.id.desc()) .limit(size + 1) .fetch(); @@ -74,27 +82,43 @@ public CursorPageResponse findChatRoomsByCursor(String c @Override public CursorPageResponse findChatMessagesByCursor(Long chatRoomId, String cursor, int size, Long userId) { - List allMessages = queryFactory .select(new QChatMessageDetailResponse(chatMessage)) .from(chatMessage) .where( chatMessage.chatRoomId.eq(chatRoomId), - isBeforeCursor(chatMessage.createdAt, cursor) + chatMessageCursorFilter(cursor) ) - .orderBy(chatMessage.createdAt.desc()) + .orderBy(chatMessage.id.desc()) .limit(size + 1) .fetch(); boolean hasNext = allMessages.size() > size; List pageMessages = subLastPage(allMessages, hasNext); String nextCursor = getNextCursorByMessage(pageMessages, hasNext); + System.out.println("nextCursor = " + nextCursor); ChatRoomDetailResponse detailResponse = new ChatRoomDetailResponse(chatRoomId, pageMessages); return new CursorPageResponse<>(Collections.singletonList(detailResponse), nextCursor, hasNext); } + private BooleanExpression chatRoomCursorFilter(String cursor) { + if (Objects.isNull(cursor) || cursor.isBlank()) { + return null; + } + + return chatRoom.id.lt(Long.parseLong(cursor)); + } + + private BooleanExpression chatMessageCursorFilter(String cursor) { + if (Objects.isNull(cursor) || cursor.isBlank()) { + return null; + } + + return chatMessage.id.lt(Long.parseLong(cursor)); + } + private BooleanExpression isBeforeCursor(DateTimePath dateTimePath, String cursor) { if (cursor == null) { return null; @@ -106,14 +130,14 @@ private String getNextCursor(List chatrooms, boolean has if (Objects.isNull(chatrooms) || chatrooms.isEmpty() || !hasNext) { return null; } - return chatrooms.get(chatrooms.size() - 1).timestamp(); + return String.valueOf(chatrooms.get(chatrooms.size() - 1).chatMessageId()); } private String getNextCursorByMessage(List messages, boolean hasNext) { if (Objects.isNull(messages) || messages.isEmpty() || !hasNext) { return null; } - return messages.get(messages.size() - 1).timeStamp(); + return String.valueOf(messages.get(messages.size() - 1).messageId()); } private List subLastPage(List list, boolean hasNext) { diff --git a/src/main/java/com/dangsim/chat/service/ChatRoomService.java b/src/main/java/com/dangsim/chat/service/ChatRoomService.java index 1ba9f29..6f32935 100644 --- a/src/main/java/com/dangsim/chat/service/ChatRoomService.java +++ b/src/main/java/com/dangsim/chat/service/ChatRoomService.java @@ -1,8 +1,5 @@ package com.dangsim.chat.service; -import java.time.LocalDateTime; -import java.util.Objects; - import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -17,7 +14,6 @@ import com.dangsim.chat.repository.ChatRoomRepository; import com.dangsim.common.CursorPageResponse; import com.dangsim.common.exception.runtime.BaseException; -import com.dangsim.common.util.DateTimeFormatUtils; import com.dangsim.task.dto.response.TaskInfoResponse; import com.dangsim.task.entity.Task; import com.dangsim.task.exception.TaskErrorCode; @@ -51,18 +47,13 @@ public ChatRoomResponse createChatRoom(@Valid CreateChatRoomRequest request, Use @Transactional(readOnly = true) public CursorPageResponse getChatRoomsByCursor(String cursor, int size, Long userId) { - if (Objects.isNull(cursor) || cursor.isBlank()) { - cursor = DateTimeFormatUtils.formatDateTime(LocalDateTime.now()); - } return chatRoomRepository.findChatRoomsByCursor(cursor, size, userId); } public CursorPageResponse getChatMessagesByCursor(Long chatRoomId, String cursor, int size, Long userId) { - if (Objects.isNull(cursor) || cursor.isBlank()) { - cursor = DateTimeFormatUtils.formatDateTime(LocalDateTime.now()); - } + return chatRoomRepository.findChatMessagesByCursor(chatRoomId, cursor, size, userId); } @@ -84,7 +75,7 @@ public ChatRoomInfoResponse getChatRoomInfo(Long chatRoomId, Long userId) { .orElseThrow(() -> new BaseException(UserErrorCode.USER_NOT_FOUND)); return ChatRoomMapper.toChatRoomInfoResponse(chatRoomId, chatPartner, - taskInfoResponse); + taskInfoResponse, userId); } } diff --git a/src/main/java/com/dangsim/common/config/CorsConfig.java b/src/main/java/com/dangsim/common/config/CorsConfig.java index 7b47ac1..b7f40a8 100644 --- a/src/main/java/com/dangsim/common/config/CorsConfig.java +++ b/src/main/java/com/dangsim/common/config/CorsConfig.java @@ -14,4 +14,4 @@ // } // }; // } -// } +// } \ No newline at end of file diff --git a/src/main/java/com/dangsim/common/config/SecurityConfig.java b/src/main/java/com/dangsim/common/config/SecurityConfig.java index 1da9d5a..65b8419 100644 --- a/src/main/java/com/dangsim/common/config/SecurityConfig.java +++ b/src/main/java/com/dangsim/common/config/SecurityConfig.java @@ -7,6 +7,7 @@ import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; @@ -21,6 +22,14 @@ @EnableWebSecurity public class SecurityConfig { + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return web -> web.ignoring() + .requestMatchers( + "/error", "/favicon.ico", "/ws/**" + ); + } + @Bean public SecurityFilterChain filterChain(HttpSecurity http, JwtProvider jwtProvider, UserRepository userRepository) throws Exception { @@ -32,6 +41,7 @@ public SecurityFilterChain filterChain(HttpSecurity http, JwtProvider jwtProvide // .anyRequest().permitAll() .requestMatchers("/api/auth/**").permitAll() .requestMatchers(HttpMethod.GET, "/api/tasks").permitAll() + .requestMatchers("/ws-chat/**").permitAll() .requestMatchers( "/swagger-ui/**", "/v3/api-docs/**", @@ -53,7 +63,8 @@ public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(List.of( "http://localhost:3000", - "https://dangsim-fe.pages.dev" + "https://dangsim-fe.pages.dev", + "/ws/**" )); configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")); configuration.setAllowedHeaders(List.of("*")); diff --git a/src/main/java/com/dangsim/common/config/WebSocketConfig.java b/src/main/java/com/dangsim/common/config/WebSocketConfig.java index 9239a9b..c4a9ab5 100644 --- a/src/main/java/com/dangsim/common/config/WebSocketConfig.java +++ b/src/main/java/com/dangsim/common/config/WebSocketConfig.java @@ -21,8 +21,8 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws-chat") //웹소켓 연결 주소 - .setAllowedOriginPatterns("*") //cors 허용 - .withSockJS(); + .setAllowedOrigins("http://localhost:3000"); //cors 허용 + // .withSockJS(); } @Override diff --git a/src/main/java/com/dangsim/common/interceptor/StompChannelInterceptor.java b/src/main/java/com/dangsim/common/interceptor/StompChannelInterceptor.java index 5efb904..c0696ab 100644 --- a/src/main/java/com/dangsim/common/interceptor/StompChannelInterceptor.java +++ b/src/main/java/com/dangsim/common/interceptor/StompChannelInterceptor.java @@ -46,6 +46,7 @@ public Message preSend(Message message, MessageChannel channel) { Long userId = jwtProvider.getUserIdFromToken(token); Principal principal = () -> String.valueOf(userId); accessor.setUser(principal); + accessor.setLeaveMutable(true); } return message; } diff --git a/src/main/java/com/dangsim/pg/service/PaymentGatewayService.java b/src/main/java/com/dangsim/pg/service/PaymentGatewayService.java index 1091f39..8916626 100644 --- a/src/main/java/com/dangsim/pg/service/PaymentGatewayService.java +++ b/src/main/java/com/dangsim/pg/service/PaymentGatewayService.java @@ -20,6 +20,8 @@ import com.dangsim.common.exception.runtime.BaseException; import com.dangsim.payment.entity.Payment; +import com.dangsim.payment.entity.PaymentStatus; +import com.dangsim.payment.exception.PaymentErrorCode; import com.dangsim.payment.repository.PaymentRepository; import com.dangsim.pg.dto.InicisResponse; import com.dangsim.pg.dto.PortOneTokenResponse; @@ -36,129 +38,131 @@ @RequiredArgsConstructor public class PaymentGatewayService { - @Value("${portone.imp_key}") - private String apiKey; - - @Value("${portone.imp_secret}") - private String apiSecret; - - private final RestTemplate restTemplate; - - private final PaymentRepository paymentRepository; - private final PaymentGatewayRepository paymentGatewayRepository; - - public String getAccessToken() { - String url = "https://api.iamport.kr/users/getToken"; // 포트원 토큰 발급 API URL : 여기에 post 요청 - - Map body = new HashMap<>(); // 서버에 보낼 데이터를 담을 Map - body.put("imp_key", apiKey); - body.put("imp_secret", apiSecret); - - // http 요청 body에 json을 보내기 위해 Java 객체를 JSON 문자열로 변환 - // portone api는 json 형태를 요구하기 때문에 body를 json 형태로 변환 - ObjectMapper objectMapper = new ObjectMapper(); - String requestBody; - try { - requestBody = objectMapper.writeValueAsString(body); - } catch (Exception e) { - throw new BaseException(PaymentGatewayErrorCode.JSON_CONVERT_FAILED); - } - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity<>(requestBody, headers); - - ResponseEntity response = restTemplate.postForEntity(url, entity, PortOneTokenResponse.class); - - PortOneTokenResponse responseBody = response.getBody(); - if (responseBody == null || responseBody.getCode() != 0) { - throw new BaseException(PaymentGatewayErrorCode.PAYMENT_VERIFICATION_FAILED); - } - - return response.getBody().getResponse().getAccess_token(); - } - - @Transactional - public void verifyPaymentDetail(String impUid, String merchantUid) { - String token = getAccessToken(); - - String PORTONE_PAYMENT_LOOKUP_URL = "https://api.iamport.kr/payments/"; - String url = PORTONE_PAYMENT_LOOKUP_URL + impUid; - - HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(token); - HttpEntity entity = new HttpEntity<>(headers); - - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, InicisResponse.class); - - InicisResponse.Response paymentData = response.getBody().getResponse(); - - BigDecimal portOneAmount = BigDecimal.valueOf(paymentData.getAmount()); - - Payment payment = paymentRepository.findByMerchantUid(merchantUid) -// Payment payment = paymentRepository.findByMerchantUid(paymentData.getMerchant_uid()) - .orElseThrow(() -> new BaseException(PaymentGatewayErrorCode.PAYMENT_NOT_FOUND)); - - PaymentGateway paymentGateway = PaymentGateway.of( - paymentData.getImp_uid(), -// paymentData.getMerchant_uid(), - merchantUid, - paymentData.getPay_method(), - paymentData.getPg_provider(), - paymentData.getPg_tid(), - paymentData.getPg_id(), - BigDecimal.valueOf(paymentData.getAmount()), - paymentData.getCurrency(), - paymentData.getApply_num(), - paymentData.getBuyer_name(), - paymentData.getCard_code(), - paymentData.getCard_name(), - paymentData.getCard_quota(), - paymentData.getCard_number(), - PaymentGatewayStatus.valueOf(paymentData.getStatus().toUpperCase()), - paymentData.getCard_type(), - parseDateTimePG(paymentData.getStarted_at()), - parseDateTimePG(paymentData.getPaid_at()), - paymentData.getCanceled_at() == null ? null : parseDateTimePG(paymentData.getCanceled_at()), - parseDateTimePG(paymentData.getFailed_at()), - payment - ); - - validatePaymentAmount(paymentGateway, portOneAmount); - paymentGatewayRepository.save(paymentGateway); - } - - public void validatePaymentAmount(PaymentGateway paymentGateway, BigDecimal portOneAmount) { - if (paymentGateway == null) { - throw new IllegalArgumentException("PaymentGateway 정보가 없습니다."); - } - - Payment payment = paymentGateway.getPayment(); - if (payment == null) { - throw new IllegalArgumentException("PaymentGateway에 연결된 Payment 정보가 없습니다."); - } - - Task task = payment.getTask(); - if (task == null) { - throw new IllegalArgumentException("Payment에 연결된 Task 정보가 없습니다."); - } - - BigDecimal taskReward = task.getReward(); - if (taskReward == null || portOneAmount == null) { - throw new IllegalArgumentException("Task 리워드 값 또는 결제 금액이 null입니다."); - } - - if (taskReward.compareTo(portOneAmount) != 0) { - throw new IllegalArgumentException("Task 리워드 금액과 결제 금액이 일치하지 않습니다."); - } - } - - public void updatePaymentAndTaskStatus(String merchantUid) { - Payment payment = paymentRepository.findByMerchantUid(merchantUid) - .orElseThrow(() -> new IllegalArgumentException("해당 merchantUid의 결제를 찾을 수 없습니다.")); - - // 상태 업데이트 - payment.updatePaymentSuccessStatus(PaymentStatus.PAYMENT_SUCCESSES); - payment.getTask().updateStatus(TaskStatus.TASK_IN_PROGRESS); - } + @Value("${portone.imp_key}") + private String apiKey; + + @Value("${portone.imp_secret}") + private String apiSecret; + + private final RestTemplate restTemplate; + + private final PaymentRepository paymentRepository; + private final PaymentGatewayRepository paymentGatewayRepository; + + public String getAccessToken() { + String url = "https://api.iamport.kr/users/getToken"; // 포트원 토큰 발급 API URL : 여기에 post 요청 + + Map body = new HashMap<>(); // 서버에 보낼 데이터를 담을 Map + body.put("imp_key", apiKey); + body.put("imp_secret", apiSecret); + + // http 요청 body에 json을 보내기 위해 Java 객체를 JSON 문자열로 변환 + // portone api는 json 형태를 요구하기 때문에 body를 json 형태로 변환 + ObjectMapper objectMapper = new ObjectMapper(); + String requestBody; + try { + requestBody = objectMapper.writeValueAsString(body); + } catch (Exception e) { + throw new BaseException(PaymentGatewayErrorCode.JSON_CONVERT_FAILED); + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(requestBody, headers); + + ResponseEntity response = restTemplate.postForEntity(url, entity, + PortOneTokenResponse.class); + + PortOneTokenResponse responseBody = response.getBody(); + if (responseBody == null || responseBody.getCode() != 0) { + throw new BaseException(PaymentGatewayErrorCode.PAYMENT_VERIFICATION_FAILED); + } + + return response.getBody().getResponse().getAccess_token(); + } + + @Transactional + public void verifyPaymentDetail(String impUid, String merchantUid) { + String token = getAccessToken(); + + String PORTONE_PAYMENT_LOOKUP_URL = "https://api.iamport.kr/payments/"; + String url = PORTONE_PAYMENT_LOOKUP_URL + impUid; + + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(token); + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, + InicisResponse.class); + + InicisResponse.Response paymentData = response.getBody().getResponse(); + + BigDecimal portOneAmount = BigDecimal.valueOf(paymentData.getAmount()); + + Payment payment = paymentRepository.findByMerchantUid(merchantUid) + // Payment payment = paymentRepository.findByMerchantUid(paymentData.getMerchant_uid()) + .orElseThrow(() -> new BaseException(PaymentGatewayErrorCode.PAYMENT_NOT_FOUND)); + + PaymentGateway paymentGateway = PaymentGateway.of( + paymentData.getImp_uid(), + // paymentData.getMerchant_uid(), + merchantUid, + paymentData.getPay_method(), + paymentData.getPg_provider(), + paymentData.getPg_tid(), + paymentData.getPg_id(), + BigDecimal.valueOf(paymentData.getAmount()), + paymentData.getCurrency(), + paymentData.getApply_num(), + paymentData.getBuyer_name(), + paymentData.getCard_code(), + paymentData.getCard_name(), + paymentData.getCard_quota(), + paymentData.getCard_number(), + PaymentGatewayStatus.valueOf(paymentData.getStatus().toUpperCase()), + paymentData.getCard_type(), + parseDateTimePG(paymentData.getStarted_at()), + parseDateTimePG(paymentData.getPaid_at()), + paymentData.getCanceled_at() == null ? null : parseDateTimePG(paymentData.getCanceled_at()), + parseDateTimePG(paymentData.getFailed_at()), + payment + ); + + validatePaymentAmount(paymentGateway, portOneAmount); + paymentGatewayRepository.save(paymentGateway); + } + + public void validatePaymentAmount(PaymentGateway paymentGateway, BigDecimal portOneAmount) { + if (paymentGateway == null) { + throw new IllegalArgumentException("PaymentGateway 정보가 없습니다."); + } + + Payment payment = paymentGateway.getPayment(); + if (payment == null) { + throw new IllegalArgumentException("PaymentGateway에 연결된 Payment 정보가 없습니다."); + } + + Task task = payment.getTask(); + if (task == null) { + throw new IllegalArgumentException("Payment에 연결된 Task 정보가 없습니다."); + } + + BigDecimal taskReward = task.getReward(); + if (taskReward == null || portOneAmount == null) { + throw new IllegalArgumentException("Task 리워드 값 또는 결제 금액이 null입니다."); + } + + if (taskReward.compareTo(portOneAmount) != 0) { + throw new IllegalArgumentException("Task 리워드 금액과 결제 금액이 일치하지 않습니다."); + } + } + + @Transactional + public void updatePaymentAndTaskStatus(String merchantUid) { + Payment payment = paymentRepository.findByMerchantUid(merchantUid) + .orElseThrow(() -> new BaseException(PaymentErrorCode.NOT_FOUND_PAYMENT)); + + // 상태 업데이트 + payment.updatePaymentSuccessStatus(PaymentStatus.PAYMENT_SUCCESSES); + } } \ No newline at end of file diff --git a/src/main/java/com/dangsim/reward/controller/RewardController.java b/src/main/java/com/dangsim/reward/controller/RewardController.java new file mode 100644 index 0000000..461cda3 --- /dev/null +++ b/src/main/java/com/dangsim/reward/controller/RewardController.java @@ -0,0 +1,23 @@ +package com.dangsim.reward.controller; + +import com.dangsim.reward.dto.response.RewardChatResponse; +import com.dangsim.reward.service.RewardService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class RewardController { + private final RewardService rewardService; + + // "심부름 수행 버튼 클릭시" + @PostMapping("/api/reward/chat/{chatId}") + public RewardChatResponse rewardToPerformer(@PathVariable Long chatId) { + + rewardService.updateRewardByTaskCompleteBtn(chatId); + + return rewardService.rewardByChatId(chatId); + } +} \ No newline at end of file diff --git a/src/main/java/com/dangsim/reward/dto/request/RewardChatRequest.java b/src/main/java/com/dangsim/reward/dto/request/RewardChatRequest.java new file mode 100644 index 0000000..471e1dc --- /dev/null +++ b/src/main/java/com/dangsim/reward/dto/request/RewardChatRequest.java @@ -0,0 +1,10 @@ +package com.dangsim.reward.dto.request; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class RewardChatRequest { // 프론트 채팅방이 전달하는 id + private Long chatId; +} diff --git a/src/main/java/com/dangsim/reward/dto/response/RewardChatResponse.java b/src/main/java/com/dangsim/reward/dto/response/RewardChatResponse.java new file mode 100644 index 0000000..04e1219 --- /dev/null +++ b/src/main/java/com/dangsim/reward/dto/response/RewardChatResponse.java @@ -0,0 +1,28 @@ +package com.dangsim.reward.dto.response; + +import com.dangsim.reward.entity.Reward; +import lombok.Builder; +import lombok.Getter; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Getter +@Builder +public class RewardChatResponse { + private Long performerId; + private BigDecimal beforeReward; + private BigDecimal amount; + private BigDecimal afterReward; + private LocalDateTime completedAt; + + public static RewardChatResponse from(Reward statement) { + return RewardChatResponse.builder() + .performerId(statement.getUser().getId()) + .beforeReward(statement.getBeforeReward()) + .amount(statement.getAmount()) + .afterReward(statement.getBeforeReward().add(statement.getAmount())) + .completedAt(statement.getCompletedAt()) + .build(); + } +} diff --git a/src/main/java/com/dangsim/reward/entity/Reward.java b/src/main/java/com/dangsim/reward/entity/Reward.java new file mode 100644 index 0000000..2ee55b8 --- /dev/null +++ b/src/main/java/com/dangsim/reward/entity/Reward.java @@ -0,0 +1,82 @@ +package com.dangsim.reward.entity; + +import com.dangsim.common.entity.BaseEntity; +import com.dangsim.task.entity.Task; +import com.dangsim.user.entity.User; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import static lombok.AccessLevel.PRIVATE; + +@Entity +@Table(name = "reward_statement") +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Reward extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "reward_statement_id") + private Long id; + + // 수행자가 가지고 있던 리워드 값 + @NotNull + @Column(name = "before_reward", nullable = false) + private BigDecimal beforeReward; + + // task의 리워드 값 + @NotNull + @Column(name = "amount", nullable = false) + private BigDecimal amount; + + // 수행자의 최종 리워드 값 + @NotNull + @Column(name = "after_reward", nullable = false) + private BigDecimal afterReward; + + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "task_id", nullable = false, foreignKey = @ForeignKey(name = "fk_reward_task")) + private Task task; + + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(name = "fk_reward_user")) + private User user; + + @NotNull + @Column(name = "requested_at", nullable = false) + private LocalDateTime requestedAt; + + @Column(name = "completed_at") + private LocalDateTime completedAt; + + @Builder(access = PRIVATE) + public Reward(BigDecimal amount, Task task, User user, LocalDateTime completedAt) { + this.amount = amount; + this.task = task; + this.user = user; + this.requestedAt = LocalDateTime.now(); + this.completedAt = completedAt; + } + + public static Reward of(BigDecimal amount, Task task, User user, LocalDateTime completedAt) { + return Reward.builder() + .amount(amount) + .task(task) + .user(user) + .requestedAt(LocalDateTime.now()) + .completedAt(completedAt) + .build(); + } + + public void markCompleted() { + this.completedAt = LocalDateTime.now(); + } +} diff --git a/src/main/java/com/dangsim/reward/repository/RewardRepository.java b/src/main/java/com/dangsim/reward/repository/RewardRepository.java new file mode 100644 index 0000000..28dd2ba --- /dev/null +++ b/src/main/java/com/dangsim/reward/repository/RewardRepository.java @@ -0,0 +1,12 @@ +package com.dangsim.reward.repository; + +import com.dangsim.reward.entity.Reward; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface RewardRepository extends JpaRepository { + Optional findTopByTaskIdAndUserIdOrderByCreatedAtDesc(Long taskId, Long userId); +} \ No newline at end of file diff --git a/src/main/java/com/dangsim/reward/service/RewardService.java b/src/main/java/com/dangsim/reward/service/RewardService.java new file mode 100644 index 0000000..e725b72 --- /dev/null +++ b/src/main/java/com/dangsim/reward/service/RewardService.java @@ -0,0 +1,93 @@ +package com.dangsim.reward.service; + +import com.dangsim.chat.entity.ChatRoom; +import com.dangsim.chat.repository.ChatRoomRepository; +import com.dangsim.payment.entity.Payment; +import com.dangsim.payment.repository.PaymentRepository; +import com.dangsim.reward.dto.response.RewardChatResponse; +import com.dangsim.reward.entity.Reward; +import com.dangsim.reward.repository.RewardRepository; +import com.dangsim.task.entity.Task; +import com.dangsim.task.repository.TaskRepository; +import com.dangsim.user.entity.User; +import com.dangsim.user.repository.UserRepository; +import jakarta.persistence.EntityNotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Service +@RequiredArgsConstructor +public class RewardService { + + private final ChatRoomRepository chatRoomRepository; + private final TaskRepository taskRepository; + private final PaymentRepository paymentRepository; + private final UserRepository userRepository; + private final RewardRepository rewardStatementRepository; + + /** + * 1. 실제 리워드 지급 로직 (심부름 완료 버튼 클릭 시 호출) + */ + @Transactional + public Reward updateRewardByTaskCompleteBtn(Long chatId) { + // 1. chatId → ChatRoom + ChatRoom chatRoom = chatRoomRepository.findById(chatId) + .orElseThrow(() -> new EntityNotFoundException("해당 채팅방을 찾을 수 없습니다.")); + + // 2. ChatRoom → Task + Task task = chatRoom.getTask(); + Long taskId = task.getId(); + + // 3. Task → 리워드, 마감 시간 + BigDecimal rewardAmount = task.getReward(); + LocalDateTime deadline = task.getDeadline(); + + // 4. Task → Payment → performerId + Payment payment = paymentRepository.findByTaskId(taskId) + .orElseThrow(() -> new EntityNotFoundException("해당 Task에 대한 결제 정보를 찾을 수 없습니다.")); + Long performerId = payment.getPerformer().getId(); + + // 5. performer → User + User performer = userRepository.findById(performerId) + .orElseThrow(() -> new EntityNotFoundException("해당 수행자 유저를 찾을 수 없습니다.")); + + BigDecimal beforeReward = performer.getReward(); + BigDecimal afterReward = beforeReward.add(rewardAmount); + + // 6. User reward 값 업데이트 + performer.updateReward(afterReward); + userRepository.save(performer); + + // 7. 정산 내역 저장 + Reward statement = Reward.of( + rewardAmount, + task, + performer, + LocalDateTime.now() + ); + rewardStatementRepository.save(statement); + + return statement; + } + + /** + * 2. 리워드 정산 후 결과 반환 + */ + @Transactional(readOnly = true) + public RewardChatResponse rewardByChatId (Long chatId){ + ChatRoom chatRoom = chatRoomRepository.findById(chatId) + .orElseThrow(() -> new EntityNotFoundException("해당 채팅방을 찾을 수 없습니다.")); + Long taskId = chatRoom.getTask().getId(); + Long performerId = chatRoom.getPerformer().getId(); + + Reward statement = rewardStatementRepository + .findTopByTaskIdAndUserIdOrderByCreatedAtDesc(taskId, performerId) + .orElseThrow(() -> new EntityNotFoundException("정산 내역을 찾을 수 없습니다.")); + + return RewardChatResponse.from(statement); + } + } diff --git a/src/main/java/com/dangsim/rewardRefund/entity/RewardRefundEntity.java b/src/main/java/com/dangsim/rewardRefund/entity/RewardRefund.java similarity index 85% rename from src/main/java/com/dangsim/rewardRefund/entity/RewardRefundEntity.java rename to src/main/java/com/dangsim/rewardRefund/entity/RewardRefund.java index c9805a8..0b0fad5 100644 --- a/src/main/java/com/dangsim/rewardRefund/entity/RewardRefundEntity.java +++ b/src/main/java/com/dangsim/rewardRefund/entity/RewardRefund.java @@ -32,7 +32,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class RewardRefundEntity { +public class RewardRefund { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -76,8 +76,8 @@ public class RewardRefundEntity { private LocalDateTime completedAt; @Builder(access = PRIVATE) - private RewardRefundEntity(User user, BigDecimal amount, String bankName, String bankAccount, String holderName, - RewardRefundStatus status) { + private RewardRefund(User user, BigDecimal amount, String bankName, String bankAccount, String holderName, + RewardRefundStatus status) { this.user = user; this.amount = amount; this.bankName = bankName; @@ -87,9 +87,9 @@ private RewardRefundEntity(User user, BigDecimal amount, String bankName, String this.requestedAt = LocalDateTime.now(); } - public static RewardRefundEntity of(User user, BigDecimal amount, String bankName, String bankAccount, - String holderName, RewardRefundStatus status) { - return RewardRefundEntity.builder() + public static RewardRefund of(User user, BigDecimal amount, String bankName, String bankAccount, + String holderName, RewardRefundStatus status) { + return RewardRefund.builder() .user(user) .amount(amount) .bankName(bankName) diff --git a/src/main/java/com/dangsim/rewardRefund/repository/RewardRefundRepository.java b/src/main/java/com/dangsim/rewardRefund/repository/RewardRefundRepository.java index bbdaec9..75c4a3d 100644 --- a/src/main/java/com/dangsim/rewardRefund/repository/RewardRefundRepository.java +++ b/src/main/java/com/dangsim/rewardRefund/repository/RewardRefundRepository.java @@ -1,11 +1,10 @@ package com.dangsim.rewardRefund.repository; +import com.dangsim.rewardRefund.entity.RewardRefund; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import com.dangsim.rewardRefund.entity.RewardRefundEntity; - @Repository -public interface RewardRefundRepository extends JpaRepository { +public interface RewardRefundRepository extends JpaRepository { } \ No newline at end of file diff --git a/src/main/java/com/dangsim/rewardRefund/service/RewardRefundService.java b/src/main/java/com/dangsim/rewardRefund/service/RewardRefundService.java index 64b42b9..07ba8f4 100644 --- a/src/main/java/com/dangsim/rewardRefund/service/RewardRefundService.java +++ b/src/main/java/com/dangsim/rewardRefund/service/RewardRefundService.java @@ -4,12 +4,12 @@ import java.math.BigDecimal; +import com.dangsim.rewardRefund.entity.RewardRefund; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.dangsim.common.exception.runtime.BaseException; import com.dangsim.rewardRefund.dto.request.RewardRefundRequest; -import com.dangsim.rewardRefund.entity.RewardRefundEntity; import com.dangsim.rewardRefund.entity.RewardRefundStatus; import com.dangsim.rewardRefund.repository.RewardRefundRepository; import com.dangsim.user.entity.User; @@ -40,7 +40,7 @@ public void requestRefund(Long userId, RewardRefundRequest requestDto) { throw new BaseException(REFUND_AMOUNT_EXCEEDS_BALANCE); } - RewardRefundEntity rewardRequest = RewardRefundEntity.of( + RewardRefund rewardRequest = RewardRefund.of( user, requestAmount, requestDto.getBankName(), diff --git a/src/main/java/com/dangsim/task/repository/TaskQueryRepositoryImpl.java b/src/main/java/com/dangsim/task/repository/TaskQueryRepositoryImpl.java index f93710e..121b58c 100644 --- a/src/main/java/com/dangsim/task/repository/TaskQueryRepositoryImpl.java +++ b/src/main/java/com/dangsim/task/repository/TaskQueryRepositoryImpl.java @@ -1,5 +1,6 @@ package com.dangsim.task.repository; +import static com.dangsim.payment.entity.QPayment.*; import static com.dangsim.task.entity.QTask.*; import java.util.Collections; @@ -9,6 +10,7 @@ import org.springframework.stereotype.Repository; import com.dangsim.common.CursorPageResponse; +import com.dangsim.payment.entity.PaymentStatus; import com.dangsim.task.dto.response.QTaskSimpleResponseDto; import com.dangsim.task.dto.response.TaskSimpleResponseDto; import com.dangsim.task.entity.TaskStatus; @@ -33,9 +35,13 @@ public CursorPageResponse findTasksByCursor(String cursor List items = queryFactory .select(new QTaskSimpleResponseDto(task)) .from(task) - .where(cursorFilter(cursor), + .join(payment) + .on(payment.task.eq(task)) + .where( + cursorFilter(cursor), isAddressEq(user), - task.status.eq(TaskStatus.TASK_NOT_ASSIGNED) + task.status.eq(TaskStatus.TASK_NOT_ASSIGNED), + payment.status.eq(PaymentStatus.PAYMENT_SUCCESSES) ) .orderBy(task.id.desc()) .limit(size + 1) diff --git a/src/main/java/com/dangsim/task/service/TaskService.java b/src/main/java/com/dangsim/task/service/TaskService.java index 9760d3a..85f4729 100644 --- a/src/main/java/com/dangsim/task/service/TaskService.java +++ b/src/main/java/com/dangsim/task/service/TaskService.java @@ -9,7 +9,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.dangsim.chat.entity.ChatMessage; import com.dangsim.chat.entity.ChatRoom; +import com.dangsim.chat.repository.ChatMessageRepository; import com.dangsim.chat.repository.ChatRoomRepository; import com.dangsim.common.CursorPageResponse; import com.dangsim.common.exception.runtime.BaseException; @@ -37,9 +39,12 @@ @RequiredArgsConstructor public class TaskService { + private static final String HELLO_CHAT_MESSAGE = "채팅을 시작해보세요"; + private final TaskRepository taskRepository; private final PaymentRepository paymentRepository; private final ChatRoomRepository chatRoomRepository; + private final ChatMessageRepository chatMessageRepository; @Transactional public TaskResponseDto createTask(TaskRequestDto requestDto, User user) { @@ -50,7 +55,7 @@ public TaskResponseDto createTask(TaskRequestDto requestDto, User user) { String merchantUid = IdentifierUtils.generateMerchantUid(saveTask.getId(), LocalDateTime.now()); - paymentRepository.save(Payment.of(saveTask.getReward(), PAYMENT_SUCCESSES, merchantUid, saveTask, user)); + paymentRepository.save(Payment.of(saveTask.getReward(), PAYMENT_WAITING, merchantUid, saveTask, user)); return TaskMapper.toTaskResponseDto(saveTask, merchantUid); } @@ -113,6 +118,9 @@ public TaskMatchResponse matchPerformer(Long taskId, User performer) { ChatRoom savedChatRoom = chatRoomRepository.save(ChatRoom.of(findTask, findTask.getUser(), performer)); + ChatMessage chatMessage = ChatMessage.of(savedChatRoom.getId(), performer.getId(), HELLO_CHAT_MESSAGE); + chatMessageRepository.save(chatMessage); + findTask.updateStatus(TASK_IN_PROGRESS); return TaskMapper.toTaskMatchResponse(savedChatRoom); diff --git a/src/test/java/com/dangsim/common/fixture/PaymentFixture.java b/src/test/java/com/dangsim/common/fixture/PaymentFixture.java index 284e9eb..89068d3 100644 --- a/src/test/java/com/dangsim/common/fixture/PaymentFixture.java +++ b/src/test/java/com/dangsim/common/fixture/PaymentFixture.java @@ -36,4 +36,15 @@ public static Payment payment(PaymentStatus status, Task task, User requester, U payment.updatePerformer(performer); return payment; } + + public static Payment payment(PaymentStatus status, Task task, User requester) { + Payment payment = Payment.of( + BigDecimal.ONE, + status, + "merchantUid", + task, + requester + ); + return payment; + } } diff --git a/src/test/java/com/dangsim/reward/service/RewardServiceTest.java b/src/test/java/com/dangsim/reward/service/RewardServiceTest.java new file mode 100644 index 0000000..08da2c0 --- /dev/null +++ b/src/test/java/com/dangsim/reward/service/RewardServiceTest.java @@ -0,0 +1,80 @@ +//package com.dangsim.reward.service; +// +//import com.dangsim.chat.entity.ChatRoom; +//import com.dangsim.chat.repository.ChatRoomRepository; +//import com.dangsim.payment.entity.Payment; +//import com.dangsim.payment.repository.PaymentRepository; +//import com.dangsim.reward.entity.Reward; +//import com.dangsim.reward.repository.RewardRepository; +//import com.dangsim.task.entity.Task; +//import com.dangsim.task.repository.TaskRepository; +//import com.dangsim.user.entity.User; +//import com.dangsim.user.repository.UserRepository; +//import org.junit.jupiter.api.DisplayName; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.extension.ExtendWith; +//import org.mockito.InjectMocks; +//import org.mockito.Mock; +//import org.mockito.junit.jupiter.MockitoExtension; +// +//import java.math.BigDecimal; +//import java.time.LocalDateTime; +//import java.util.Optional; +// +//import static org.assertj.core.api.Assertions.assertThat; +//import static org.mockito.BDDMockito.given; +// +// +//@ExtendWith(MockitoExtension.class) +//public class RewardServiceTest { +// @Mock +// private ChatRoomRepository chatRoomRepository; +// +// @Mock +// private TaskRepository taskRepository; +// +// @Mock +// private PaymentRepository paymentRepository; +// +// @Mock +// private UserRepository userRepository; +// +// @Mock +// private RewardRepository rewardRepository; +// +// @InjectMocks +// private RewardService rewardService; +// +// @DisplayName("심부름 완료 버튼 클릭 시 수행자의 리워드가 정상 지급된다.") +// @Test +// void updateRewardByTaskCompleteBtn_success() { +// // given +// Long chatId = 1L; +// Long taskId = 10L; +// Long performerId = 20L; +// +// BigDecimal rewardAmount = BigDecimal.valueOf(1000); +// BigDecimal initialUserReward = BigDecimal.valueOf(5000); +// BigDecimal expectedReward = initialUserReward.add(rewardAmount); +// +// User performer = UserFixture.create(performerId, performerRewardBefore); +// Task task = TaskFixture.create(taskId, rewardAmount, LocalDateTime.now().plusDays(1)); +// ChatRoom chatRoom = ChatRoomFixture.create(chatId, task); +// Payment payment = PaymentFixture.create(100L, task, performer); +// +// given(chatRoomRepository.findById(chatId)).willReturn(Optional.of(chatRoom)); +// given(paymentRepository.findByTaskId(taskId)).willReturn(Optional.of(payment)); +// given(userRepository.findById(performerId)).willReturn(Optional.of(performer)); +// given(rewardRepository.save(org.mockito.ArgumentMatchers.any(Reward.class))) +// .willAnswer(invocation -> invocation.getArgument(0)); +// +// // when +// Reward result = rewardService.updateRewardByTaskCompleteBtn(chatId); +// +// // then +// assertThat(result.getUser().getReward()).isEqualByComparingTo(expectedReward); +// assertThat(result.getAmount()).isEqualByComparingTo(rewardAmount); +// assertThat(result.getTask().getId()).isEqualTo(taskId); +// } +// +//} diff --git a/src/test/java/com/dangsim/rewardRefund/service/RewardRefundServiceTest.java b/src/test/java/com/dangsim/rewardRefund/service/RewardRefundServiceTest.java index a0f188d..d6b0199 100644 --- a/src/test/java/com/dangsim/rewardRefund/service/RewardRefundServiceTest.java +++ b/src/test/java/com/dangsim/rewardRefund/service/RewardRefundServiceTest.java @@ -8,6 +8,7 @@ import java.math.BigDecimal; import java.util.Optional; +import com.dangsim.rewardRefund.entity.RewardRefund; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -18,7 +19,6 @@ import com.dangsim.common.exception.runtime.BaseException; import com.dangsim.common.fixture.UserFixture; import com.dangsim.rewardRefund.dto.request.RewardRefundRequest; -import com.dangsim.rewardRefund.entity.RewardRefundEntity; import com.dangsim.rewardRefund.repository.RewardRefundRepository; import com.dangsim.user.entity.Role; import com.dangsim.user.entity.User; @@ -113,7 +113,7 @@ void successRefundRequest() { RewardRefundRequest request = new RewardRefundRequest(requestAmount, BANK_NAME, ACCOUNT_NUMBER, ACCOUNT_HOLDER); given(userRepository.findById(userId)).willReturn(Optional.of(user)); - given(rewardRefundRepository.save(any(RewardRefundEntity.class))).willAnswer( + given(rewardRefundRepository.save(any(RewardRefund.class))).willAnswer( invocation -> invocation.getArgument(0)); // when @@ -121,6 +121,6 @@ void successRefundRequest() { // then assertThat(user.getReward()).isEqualByComparingTo(initialReward.subtract(requestAmount)); - then(rewardRefundRepository).should(times(1)).save(any(RewardRefundEntity.class)); + then(rewardRefundRepository).should(times(1)).save(any(RewardRefund.class)); } } diff --git a/src/test/java/com/dangsim/task/service/TaskServiceTest.java b/src/test/java/com/dangsim/task/service/TaskServiceTest.java index 06cb0e0..60f1034 100644 --- a/src/test/java/com/dangsim/task/service/TaskServiceTest.java +++ b/src/test/java/com/dangsim/task/service/TaskServiceTest.java @@ -13,9 +13,6 @@ import java.util.List; import java.util.Optional; -import com.dangsim.payment.entity.PaymentStatus; -import com.dangsim.pg.repository.PaymentGatewayRepository; -import com.dangsim.pg.service.PaymentGatewayService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -25,6 +22,7 @@ import org.springframework.test.util.ReflectionTestUtils; import com.dangsim.chat.entity.ChatRoom; +import com.dangsim.chat.repository.ChatMessageRepository; import com.dangsim.chat.repository.ChatRoomRepository; import com.dangsim.common.exception.runtime.BaseException; import com.dangsim.common.fixture.ChatRoomFixture; @@ -33,8 +31,11 @@ import com.dangsim.common.fixture.UserFixture; import com.dangsim.common.util.DateTimeFormatUtils; import com.dangsim.payment.entity.Payment; +import com.dangsim.payment.entity.PaymentStatus; import com.dangsim.payment.exception.PaymentErrorCode; import com.dangsim.payment.repository.PaymentRepository; +import com.dangsim.pg.repository.PaymentGatewayRepository; +import com.dangsim.pg.service.PaymentGatewayService; import com.dangsim.task.dto.request.TaskRequestDto; import com.dangsim.task.dto.response.TaskDeleteResponse; import com.dangsim.task.dto.response.TaskDetailsResponseDto; @@ -65,6 +66,9 @@ public class TaskServiceTest { @Mock PaymentGatewayRepository paymentGatewayRepository; + @Mock + ChatMessageRepository chatMessageRepository; + @InjectMocks PaymentGatewayService paymentGatewayService; @@ -322,19 +326,21 @@ void updatePaymentAndTaskStatus_successfullyUpdatesStatuses() { User requester = UserFixture.user(Role.USER, BigDecimal.ZERO); ReflectionTestUtils.setField(requester, "id", 1L); + User performer = UserFixture.user(Role.USER, BigDecimal.ZERO); + ReflectionTestUtils.setField(requester, "id", 2L); + Task task = TaskFixture.task(title, content, requester); + ReflectionTestUtils.setField(task, "id", 1L); ReflectionTestUtils.setField(task, "status", TaskStatus.TASK_NOT_ASSIGNED); - Payment payment = mock(Payment.class); + Payment payment = PaymentFixture.payment(task, requester, performer); given(paymentRepository.findByMerchantUid(merchantUid)).willReturn(Optional.of(payment)); - given(payment.getTask()).willReturn(task); - // when + // when paymentGatewayService.updatePaymentAndTaskStatus(merchantUid); // then - verify(payment).updatePaymentSuccessStatus(PaymentStatus.PAYMENT_SUCCESSES); - assertThat(task.getStatus()).isEqualTo(TaskStatus.TASK_IN_PROGRESS); + assertThat(payment.getStatus()).isEqualTo(PAYMENT_SUCCESSES); } @DisplayName("수행자를 매칭할 때 심부름 상태가 완료 상태라면 예외가 발생한다.")