Skip to content

Commit

Permalink
refactor : 병렬스트림 사용 시 Thread safe 한 자료구조 사용 및 파라미터로 Collection 넘기지 말고…
Browse files Browse the repository at this point in the history
… 반환타입으로 사용하기
  • Loading branch information
codesejin committed Apr 26, 2024
1 parent 5024c1d commit c931928
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@ public class CouponIssueController {

@PostMapping("/{eventId}/issues-sync")
public ResponseEntity<ResponseDTO<String>> syncIssue(@PathVariable final long eventId,
@RequestParam final long couponId,
@RequestParam final long memberId) throws InterruptedException {
@RequestParam final long couponId,
@RequestParam final long memberId) throws InterruptedException {
LocalDateTime currentDateTime = LocalDateTime.now();
return ResponseEntity.status(HttpStatus.CREATED).body(couponIssueRequestService.syncIssueCoupon(currentDateTime, eventId, couponId, memberId));
}

@PostMapping("/{eventId}/issues-async")
public ResponseEntity<ResponseDTO<String>> asyncIssue(@PathVariable final long eventId,
@RequestParam final long couponId,
@RequestParam final long memberId) {
@RequestParam final long couponId,
@RequestParam final long memberId) {
LocalDateTime currentDateTime = LocalDateTime.now();
return ResponseEntity.status(HttpStatus.CREATED).body(couponIssueRequestService.asyncIssueCoupon(currentDateTime, eventId, couponId, memberId));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package com.flab.offcoupon.domain.vo.persistence.statistics;

import java.math.BigDecimal;
import java.time.YearMonth;

/**
* MyBatis에서 여러개의 반환 값을 전달 받기 위한 VO
*
* 월별 주문 통계를 조회하기 위해 사용
*/
public record MonthlyOrderStatisticsVo(
int month,
YearMonth yearMonth,
long totalOrderCnt,
BigDecimal totalPaymentPrice,
long totalCouponUseCnt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,24 @@
import lombok.Getter;

import java.math.BigDecimal;
import java.time.YearMonth;


@Getter
@AllArgsConstructor
public final class MonthlyOrderStatistics {

private final int month;
private final YearMonth yearMonth;
private final long totalOrderCnt;
private final BigDecimal totalPaymentPrice;
private final long totalCouponUseCnt;
private final BigDecimal totalCouponPrice;

public MonthlyOrderStatistics(MonthlyOrderStatisticsVo vo) {
this.month = vo.month();
this.totalOrderCnt = vo.totalOrderCnt();
this.totalPaymentPrice = vo.totalPaymentPrice();
this.totalCouponUseCnt = vo.totalCouponUseCnt();
this.totalCouponPrice = vo.totalCouponPrice();
this.yearMonth = (vo != null) ? vo.yearMonth() : null;
this.totalOrderCnt = (vo != null) ? vo.totalOrderCnt() : 0;
this.totalPaymentPrice = (vo != null) ? vo.totalPaymentPrice() : BigDecimal.ZERO;
this.totalCouponUseCnt = (vo != null) ? vo.totalCouponUseCnt() : 0;
this.totalCouponPrice = (vo != null) ? vo.totalCouponPrice() : BigDecimal.ZERO;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public interface StatisticsRepository {
* <p>
* 다음과 같은 통계를 월별로 조회합니다:
* <ol>
* <li>조회하는 월</li>
* <li>조회하는 연도와 월</li>
* <li>주문 수량 총합</li>
* <li>주문 총 금액</li>
* <li>주문에 사용된 쿠폰 수량 총합</li>
Expand Down
70 changes: 48 additions & 22 deletions src/main/java/com/flab/offcoupon/service/StatisticsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@

import java.time.LocalDate;
import java.time.YearMonth;
import java.util.ArrayList;
import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.List;
import java.util.stream.IntStream;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.LongStream;

import static com.flab.offcoupon.exception.statistics.StatisticsErrorMessage.START_MUST_BE_BEFORE_THANT_END;

Expand All @@ -39,14 +40,14 @@ public ResponseDTO<List<MonthlyOrderStatistics>> getMonthlyOrderStatistics(Stati
LocalDate startedAt = request.getStartedAt();
LocalDate endedAt = request.getEndedAt();
validateStartDateIsBeforeEndDate(startedAt, endedAt);
List<MonthlyOrderStatistics> monthlyStatisticsList = new ArrayList<>();
getMonthlyStatistics(startedAt, endedAt, monthlyStatisticsList);
List<MonthlyOrderStatistics> monthlyStatisticsList = getMonthlyStatistics(startedAt, endedAt);

// 월별로 정렬
monthlyStatisticsList.sort(Comparator.comparing(MonthlyOrderStatistics::getMonth));
monthlyStatisticsList.sort(Comparator.comparing(MonthlyOrderStatistics::getYearMonth));

return ResponseDTO.getSuccessResult(monthlyStatisticsList);
}

/**
* 시작일과 종료일 간의 유효성을 검사하여 시작일이 종료일보다 이전인지 확인합니다.
*
Expand All @@ -55,19 +56,21 @@ public ResponseDTO<List<MonthlyOrderStatistics>> getMonthlyOrderStatistics(Stati
* @throws LocalDateBadRequestException 시작일이 종료일보다 이후인 경우 발생하는 예외
*/
private void validateStartDateIsBeforeEndDate(LocalDate startedAt, LocalDate endedAt) {
if(startedAt.isAfter(endedAt)) {
if (startedAt.isAfter(endedAt)) {
throw new LocalDateBadRequestException(START_MUST_BE_BEFORE_THANT_END.formatted(startedAt, endedAt));
}
}

/**
* 시작일부터 종료일까지 월 별 주문 통계를 조회하는 메서드입니다.
*
* @param startedAt 조회 시작일
* @param endedAt 조회 종료일
* @param monthlyStatisticsList 월 별 주문 통계 목록
* @param startedAt 조회 시작일
* @param endedAt 조회 종료일
*/
private void getMonthlyStatistics(LocalDate startedAt, LocalDate endedAt, List<MonthlyOrderStatistics> monthlyStatisticsList) {
IntStream.range(startedAt.getMonthValue(), endedAt.getMonthValue() + 1)
private List<MonthlyOrderStatistics> getMonthlyStatistics(LocalDate startedAt, LocalDate endedAt) {
List<MonthlyOrderStatistics> monthlyStatisticsList = new CopyOnWriteArrayList<>();
YearMonth basedYearMonth = YearMonth.of(startedAt.getYear(), startedAt.getMonth());
LongStream.range(0L, countMonthDifference(startedAt, endedAt) + 1L)
.parallel()
.forEach(i -> {
/**
Expand All @@ -76,21 +79,44 @@ private void getMonthlyStatistics(LocalDate startedAt, LocalDate endedAt, List<M
* <li>2024년 1월 28일부터 1월 31일</li>
* <li>2024년 2월 1일부터 2월 29일</li>
* <li>2024년 3월 1일부터 3월 10일</li>
*
* ex. 2024년 12월 12일 부터 2025년 1월 16일까지 조회할 경우
* <li>2024년 12월 12일부터 12월 31일</li>
* <li>2024년 1월 1일부터 1월 16일</li>
*/
LocalDate starDate = (i == startedAt.getMonthValue()) ? startedAt : LocalDate.of(startedAt.getYear(), i, 1);
LocalDate monthEnd = (i == endedAt.getMonthValue()) ? endedAt : getLastDayOfMonth(endedAt.getYear(), i);
MonthlyStatisticsParameterVo parameterVo = new MonthlyStatisticsParameterVo(starDate, monthEnd);
List<MonthlyOrderStatisticsVo> monthlyStatisticsVoList = statisticsRepository.getMonthlyOrderStatistics(parameterVo);
List<MonthlyOrderStatistics> monthlyStatistics = convertToDTO(monthlyStatisticsVoList);
monthlyStatisticsList.addAll(monthlyStatistics);
YearMonth currentYearMonth = basedYearMonth.plusMonths(i);
LocalDate startDate = (isSameYearMonth(currentYearMonth, startedAt)) ?
startedAt : LocalDate.of(currentYearMonth.getYear(), currentYearMonth.getMonth(), 1);
LocalDate endDate = (isSameYearMonth(currentYearMonth, endedAt) ?
endedAt : currentYearMonth.atEndOfMonth());
MonthlyStatisticsParameterVo parameterVo = new MonthlyStatisticsParameterVo(startDate, endDate);
List<MonthlyOrderStatistics> monthlyOrderStatisticsList = convertToDTO(statisticsRepository.getMonthlyOrderStatistics(parameterVo));
monthlyStatisticsList.addAll(monthlyOrderStatisticsList);
});
return monthlyStatisticsList;
}

private LocalDate getLastDayOfMonth(int year, int month) {
// 연도와 월 정보를 가지고 YearMonth 객체 생성
YearMonth yearMonth = YearMonth.of(year, month);
// 해당 월의 마지막 날짜를 반환
return yearMonth.atEndOfMonth();
/**
* 현재 월과 대상 날짜가 같은지 확인하는 메서드입니다.
* @param currentYearMonth 현재 월
* @param target 대상 날짜
* @return 현재 월과 대상 날짜가 같은지 여부
*/
private boolean isSameYearMonth(YearMonth currentYearMonth, LocalDate target) {
return currentYearMonth.equals(YearMonth.of(target.getYear(), target.getMonthValue()));
}

/**
* 시작일부터 종료일까지의 월 차이를 계산하는 메서드입니다.
* 병렬 스트림에서 사용하기 위해 long 타입으로 반환합니다.
* @param startedAt 시작일
* @param endedAt 종료일
* @return 시작일부터 종료일까지의 월 차이
*/
private long countMonthDifference(LocalDate startedAt, LocalDate endedAt) {
YearMonth start = YearMonth.of(startedAt.getYear(), startedAt.getMonth());
YearMonth end = YearMonth.of(endedAt.getYear(), endedAt.getMonth());
return ChronoUnit.MONTHS.between(start, end);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/main/resources/mapper/StatisticsMapper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

<mapper namespace="com.flab.offcoupon.repository.mysql.StatisticsRepository">
<select id="getMonthlyOrderStatistics" parameterType="MonthlyStatisticsParameterVo" resultType="MonthlyOrderStatisticsVo">
select Month(od.created_at) as MONTH,
select DATE_FORMAT(od.created_at,'%Y-%m') as YearMonth,
count(od.id) as totalOrderCnt,
SUM(od.total_payment_price) as totalPaymentPrice,
count(oc.id) as totalCouponUseCnt,
SUM(od.total_discount_price) as total_discount_price
from order_detail od
left join order_coupon oc on od.id = oc.order_id
where od.created_at BETWEEN #{startedAt} and #{endedAt}
group by MONTH
order by MONTH;
group by YearMonth
order by YearMonth;
</select>
</mapper>

0 comments on commit c931928

Please sign in to comment.