Skip to content
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

#136 일자별 매출 통계 API 구현 #199

Merged
merged 6 commits into from
Jan 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/docs/asciidoc/coupon/coupon-api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ include::{snippets}/coupon-backoffice-controller-docs-test/modify-coupon-request

== 쿠폰 현황 통계

쿠폰 현환 통계 API
쿠폰 현황 통계 API

=== HttpRequest

Expand All @@ -121,3 +121,19 @@ include::{snippets}/coupon-backoffice-controller-docs-test/coupon-statistics-tes

include::{snippets}/coupon-backoffice-controller-docs-test/coupon-statistics-test/http-response.adoc[]
include::{snippets}/coupon-backoffice-controller-docs-test/coupon-statistics-test/response-fields.adoc[]


[[Statistics-Revenue]]

== 일주일 매출 현황 통계

최근 일주일 매출 현황 통계 API

=== HttpRequest

include::{snippets}/coupon-backoffice-controller-docs-test/revenue-statistics-test/http-request.adoc[]

=== HttpResponse

include::{snippets}/coupon-backoffice-controller-docs-test/revenue-statistics-test/http-response.adoc[]
include::{snippets}/coupon-backoffice-controller-docs-test/revenue-statistics-test/response-fields.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import jakarta.persistence.OneToOne;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand Down Expand Up @@ -84,4 +85,22 @@ public Accommodation(
this.images = images;
this.rooms = rooms;
}

// 숙소의 ID로 동등 비교를 하기 위해 추가함.
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Accommodation that = (Accommodation) o;
return Objects.equals(getId(), that.getId());
}

@Override
public int hashCode() {
return Objects.hash(getId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package com.backoffice.upjuyanolja.domain.coupon.config;

import com.backoffice.upjuyanolja.domain.accommodation.entity.Accommodation;
import com.backoffice.upjuyanolja.domain.accommodation.exception.AccommodationNotFoundException;
import com.backoffice.upjuyanolja.domain.accommodation.repository.AccommodationRepository;
import com.backoffice.upjuyanolja.domain.coupon.dto.statistics.CouponStatisticsInterface;
import com.backoffice.upjuyanolja.domain.coupon.dto.statistics.RevenueStatisticsInterface;
import com.backoffice.upjuyanolja.domain.coupon.entity.CouponStatistics;
import com.backoffice.upjuyanolja.domain.coupon.entity.RevenueStatistics;
import com.backoffice.upjuyanolja.domain.coupon.entity.RevenueTotal;
import com.backoffice.upjuyanolja.domain.coupon.repository.CouponStatisticsRepository;
import com.backoffice.upjuyanolja.domain.coupon.repository.RevenueStatisticsRepository;
import com.backoffice.upjuyanolja.domain.coupon.repository.RevenueTotalRepository;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.transaction.annotation.Transactional;


@Slf4j
@Configuration
@RequiredArgsConstructor
@Transactional
@Profile("prod")
public class MockStatisticsInit {

private final CouponStatisticsRepository couponStatisticsRepository;
private final RevenueStatisticsRepository revenueStatisticsRepository;
private final RevenueTotalRepository revenueTotalRepository;
private final AccommodationRepository accommodationRepository;

// Todo: 테스트용. 발표 끝나면 파일 삭제
@Bean
ApplicationRunner init() {
return new ApplicationRunner() {
@Override
public void run(ApplicationArguments args) throws Exception {
if (!couponStatisticsRepository.existsById(1L)) {
makeCouponStatistics();
}
if (!revenueStatisticsRepository.existsById(1L)) {
createRevenueStatistics();
}
}
};
}

public void makeCouponStatistics() {
List<CouponStatisticsInterface> result = couponStatisticsRepository.createStatistics();
List<CouponStatistics> statisticsList = new ArrayList<>();
for (CouponStatisticsInterface statistics : result) {
statisticsList.add(createCouponStatistics(statistics));
}
couponStatisticsRepository.saveAll(statisticsList);
log.info("쿠폰 통계 생성 성공. 총 {}건.", statisticsList.size());
}

private CouponStatistics createCouponStatistics(CouponStatisticsInterface statistics) {
Accommodation accommodation = accommodationRepository.findById(statistics.getId())
.orElseThrow(AccommodationNotFoundException::new);
return CouponStatistics.builder()
.accommodation(accommodation)
.stock(statistics.getStock())
.total(statistics.getTotal())
.used(statistics.getUsed())
.build();
}

public void createRevenueStatistics() {
// 1. 최근 일주일 일자별 통계를 구한다.
LocalDate now = LocalDate.now(ZoneId.of("Asia/Seoul"));
LocalDate endDate = now.minusDays(1);
LocalDate startDate = now.minusWeeks(1).minusDays(1);

List<RevenueStatisticsInterface> results = revenueStatisticsRepository
.createRevenueStatistics(startDate, endDate);

List<RevenueStatistics> revenueStatistics = new ArrayList<>();

// 2. for-loop 도는 동안 숙소별로 일주일간의 매출 합계를 구한다.
Map<Accommodation, long[]> revenueTotal = new HashMap<>();
for (RevenueStatisticsInterface result : results) {
Accommodation accommodation = accommodationRepository.findById(result.getId())
.orElseThrow(AccommodationNotFoundException::new);
revenueTotal.putIfAbsent(accommodation, new long[2]);
long[] sum = revenueTotal.get(accommodation);
sum[0] += result.getCouponRevenue();
sum[1] += result.getRegularRevenue();
revenueStatistics.add(createRevenueStatistics(result, accommodation));
}
revenueStatisticsRepository.saveAll(revenueStatistics);
log.info("최근 일주일 일자별 매출 통계 생성 성공. 총 {}건.", results.size());

// 3. 일주일간의 매출 유형벌 합계로 성장률을 구하고 매출 합계 통계에 저장한다.
List<RevenueTotal> revenueTotals = new ArrayList<>();
for (var totals: revenueTotal.entrySet()) {
RevenueTotal revenueSum = getRevenueSum(totals);
revenueTotals.add(revenueSum);
}
revenueTotalRepository.saveAll(revenueTotals);
log.info("매출 합계 통계 생성 성공. 총 {}건.", revenueTotals.size());
}

private static RevenueTotal getRevenueSum(Entry<Accommodation, long[]> totals) {
Accommodation accommodation = totals.getKey();
long[] value = totals.getValue();
long couponTotal = value[0];
long regularTotal = value[1];
long difference = couponTotal - regularTotal;
double growthRate = ((regularTotal + difference) / regularTotal) * 100.0;
log.info("매출 상승 비율: {}", growthRate);
RevenueTotal revenueSum = RevenueTotal.builder()
.accommodation(accommodation)
.couponTotal(couponTotal)
.regularTotal(regularTotal)
.growthRate(growthRate)
.build();
return revenueSum;
}

private RevenueStatistics createRevenueStatistics(
RevenueStatisticsInterface result, Accommodation accommodation
) {
return RevenueStatistics.builder()
.accommodation(accommodation)
.revenueDate(result.getRevenueDate())
.couponRevenue(result.getCouponRevenue())
.regularRevenue(result.getRegularRevenue())
.build();
}
}

Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.backoffice.upjuyanolja.domain.coupon.controller;

import com.backoffice.upjuyanolja.domain.accommodation.dto.response.CouponStatisticsResponse;
import com.backoffice.upjuyanolja.domain.coupon.dto.request.backoffice.CouponAddRequest;
import com.backoffice.upjuyanolja.domain.coupon.dto.request.backoffice.CouponDeleteRequest;
import com.backoffice.upjuyanolja.domain.coupon.dto.request.backoffice.CouponMakeRequest;
import com.backoffice.upjuyanolja.domain.coupon.dto.request.backoffice.CouponModifyRequest;
import com.backoffice.upjuyanolja.domain.coupon.dto.response.backoffice.CouponMakeViewResponse;
import com.backoffice.upjuyanolja.domain.coupon.dto.response.backoffice.CouponManageResponse;
import com.backoffice.upjuyanolja.domain.coupon.dto.response.backoffice.CouponStatisticsResponse;
import com.backoffice.upjuyanolja.domain.coupon.dto.response.backoffice.RevenueStatisticsResponse;
import com.backoffice.upjuyanolja.domain.coupon.service.CouponBackofficeService;
import com.backoffice.upjuyanolja.domain.coupon.service.CouponStatisticsService;
import com.backoffice.upjuyanolja.domain.member.service.MemberGetService;
Expand Down Expand Up @@ -37,6 +38,7 @@ public class CouponBackofficeController {
private final CouponBackofficeService couponService;
private final CouponStatisticsService couponStatisticsService;
private final SecurityUtil securityUtil;
private final MemberGetService memberGetService;

@GetMapping("/buy/{accommodationId}")
public ResponseEntity<CouponMakeViewResponse> responseRoomsView(
Expand Down Expand Up @@ -131,7 +133,7 @@ public ResponseEntity<Object> deleteCoupon(
}

@GetMapping("/statistics/{accommodationId}")
public ResponseEntity<CouponStatisticsResponse> getStatistics(
public ResponseEntity<CouponStatisticsResponse> getCouponStatistics(
@PathVariable(name = "accommodationId") @Min(1) Long accommodationId
) {
long currentMemberId = securityUtil.getCurrentMemberId();
Expand All @@ -143,4 +145,17 @@ public ResponseEntity<CouponStatisticsResponse> getStatistics(
return ResponseEntity.status(HttpStatus.OK).body(result);
}

@GetMapping("/revenue/{accommodationId}")
public ResponseEntity<RevenueStatisticsResponse> getRevenueStatistics(
@PathVariable(name = "accommodationId") @Min(1) Long accommodationId
) {
long currentMemberId = securityUtil.getCurrentMemberId();
couponService.validateAccommodationRequest(
accommodationId, currentMemberId);

String ownerName = memberGetService.getMember(currentMemberId).name();
RevenueStatisticsResponse result = couponStatisticsService
.getRevenueStatistics(accommodationId, ownerName);
return ResponseEntity.status(HttpStatus.OK).body(result);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.backoffice.upjuyanolja.domain.accommodation.dto.response;
package com.backoffice.upjuyanolja.domain.coupon.dto.response.backoffice;

import com.backoffice.upjuyanolja.domain.coupon.entity.CouponStatistics;
import lombok.Builder;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.backoffice.upjuyanolja.domain.coupon.dto.response.backoffice;

import lombok.Builder;

@Builder
public record RevenueInfo(
String revenueDate,
long couponRevenue,
long normalRevenue
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.backoffice.upjuyanolja.domain.coupon.dto.response.backoffice;

import java.util.List;
import java.util.Objects;
import lombok.Builder;

@Builder
public record RevenueStatisticsResponse(
Long accommodationId,
List<RevenueInfo> revenue,
String couponMessage
) {

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RevenueStatisticsResponse that = (RevenueStatisticsResponse) o;
return Objects.equals(accommodationId, that.accommodationId);
}

@Override
public int hashCode() {
return Objects.hash(accommodationId);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.backoffice.upjuyanolja.domain.coupon.dto;
package com.backoffice.upjuyanolja.domain.coupon.dto.statistics;

public interface CouponStatisticsInterface {
Long getId();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.backoffice.upjuyanolja.domain.coupon.dto.statistics;

import java.time.LocalDate;

public interface RevenueStatisticsInterface {

long getId();

LocalDate getRevenueDate();

long getCouponRevenue();

long getRegularRevenue();
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class CouponStatistics extends BaseTime {
name = "accommodation_id",
unique = true,
foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
@Comment("숙소 식별자")
private Accommodation accommodation;

@Setter
Expand Down
Loading
Loading