Skip to content

Commit c490ddb

Browse files
authored
Merge pull request #44 from CapstoneProjectCMC/payment-service
Payment service
2 parents 08226d6 + eb11c56 commit c490ddb

File tree

8 files changed

+221
-0
lines changed

8 files changed

+221
-0
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.codecampus.payment.controller;
2+
3+
import com.codecampus.payment.dto.common.ApiResponse;
4+
import com.codecampus.payment.service.PaymentStatisticService;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.web.bind.annotation.GetMapping;
7+
import org.springframework.web.bind.annotation.RequestMapping;
8+
import org.springframework.web.bind.annotation.RequestParam;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
@RestController
12+
@RequestMapping("/payment-statistics")
13+
@RequiredArgsConstructor
14+
public class PaymentStatisticController {
15+
private final PaymentStatisticService paymentStatisticService;
16+
17+
@GetMapping("/daily-deposit")
18+
public ApiResponse<?> getDailyDepositSummary(
19+
@RequestParam int year, // 2004 < year < curent year
20+
@RequestParam int month // 1 <= month <= 12
21+
) {
22+
return ApiResponse.builder()
23+
.message("Lấy thống kê thành công!")
24+
.result(paymentStatisticService.getDailyDepositSummaryByMonth(year, month))
25+
.build();
26+
}
27+
28+
@GetMapping("/daily-statistic")
29+
public ApiResponse<?> getDailystatisticSummary(
30+
@RequestParam int year, // 2004 < year < curent year
31+
@RequestParam int month // 1 <= month <= 12
32+
) {
33+
return ApiResponse.builder()
34+
.message("Lấy thống kê thành công!")
35+
.result(paymentStatisticService.getDailyStatisticSummary(year, month))
36+
.build();
37+
}
38+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.codecampus.payment.dto.response;
2+
3+
import java.math.BigDecimal;
4+
import java.time.LocalDate;
5+
6+
public record DailyDepositSummaryResponse (
7+
LocalDate day,
8+
BigDecimal totalAmount
9+
) {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.codecampus.payment.dto.response;
2+
3+
import java.math.BigDecimal;
4+
import java.time.LocalDate;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Getter;
7+
import lombok.Setter;
8+
9+
public record DailyStatisticSummaryResponse(
10+
LocalDate day,
11+
BigDecimal depositAmount,
12+
BigDecimal purchaseAmount,
13+
BigDecimal walletBalance
14+
) {}

payment-service/src/main/java/com/codecampus/payment/exception/ErrorCode.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ public enum ErrorCode {
5959
PURCHASED_ITEM(4059103, NOT_ACCEPTABLE.toString(), "Đã mua sản phẩm!",
6060
BAD_REQUEST),
6161

62+
// 406 - Input Validation Error
63+
MONTH_INVALID(4069101, BAD_REQUEST_STATUS, "Tháng không hợp lệ!", BAD_REQUEST),
64+
65+
YEAR_INVALID(4069102, BAD_REQUEST_STATUS, "Năm không hợp lệ!", BAD_REQUEST),
66+
6267
// 409 - Conflict
6368
USER_ALREADY_EXISTS(4099101, CONFLICT_STATUS, "Người dùng đã tồn tại!",
6469
CONFLICT),

payment-service/src/main/java/com/codecampus/payment/repository/PaymentTransactionRepository.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package com.codecampus.payment.repository;
22

33
import com.codecampus.payment.entity.PaymentTransaction;
4+
import com.codecampus.payment.repository.projection.DailyDepositSummaryProjection;
5+
import com.codecampus.payment.repository.projection.DailyStatisticSummaryProjection;
6+
import java.time.Instant;
7+
import java.util.List;
48
import java.util.Optional;
59
import org.springframework.data.domain.Page;
610
import org.springframework.data.domain.Pageable;
711
import org.springframework.data.jpa.repository.JpaRepository;
12+
import org.springframework.data.jpa.repository.Query;
13+
import org.springframework.data.repository.query.Param;
814

915
public interface PaymentTransactionRepository
1016
extends JpaRepository<PaymentTransaction, String> {
@@ -15,4 +21,53 @@ Page<PaymentTransaction> findAllByUserIdOrderByCreatedAtDesc(
1521
boolean existsByReferenceCode(String referenceCode);
1622

1723
Optional<PaymentTransaction> findByReferenceCode(String referenceCode);
24+
25+
@Query(
26+
value = """
27+
SELECT
28+
CAST(paid_at AS DATE) AS day,
29+
SUM(amount) AS totalAmount
30+
FROM payment_transaction
31+
WHERE paid_at BETWEEN :startDate AND :endDate
32+
AND status = 'SUCCESS'
33+
AND type = 0
34+
GROUP BY CAST(paid_at AS DATE)
35+
ORDER BY CAST(paid_at AS DATE)
36+
""",
37+
nativeQuery = true
38+
)
39+
List<DailyDepositSummaryProjection> getDailyDepositSummary(Instant startDate, Instant endDate);
40+
41+
@Query(value = """
42+
WITH opening_balance AS (
43+
SELECT COALESCE(SUM(CASE WHEN pt.type = 0 THEN pt.amount ELSE -pt.amount END), 0) AS balance
44+
FROM payment_transaction pt
45+
WHERE pt.user_id = :userId
46+
AND pt.status = 'SUCCESS'
47+
AND pt.paid_at < :startDate
48+
)
49+
SELECT
50+
d.day,
51+
d.depositAmount,
52+
d.purchaseAmount,
53+
ob.balance + SUM(d.depositAmount - d.purchaseAmount)
54+
OVER (ORDER BY d.day ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS walletBalance
55+
FROM (
56+
SELECT
57+
CAST(pt.paid_at AS DATE) AS day,
58+
SUM(CASE WHEN pt.type = 0 THEN pt.amount ELSE 0 END) AS depositAmount,
59+
SUM(CASE WHEN pt.type = 1 THEN pt.amount ELSE 0 END) AS purchaseAmount
60+
FROM payment_transaction pt
61+
WHERE pt.user_id = :userId
62+
AND pt.status = 'SUCCESS'
63+
AND pt.paid_at BETWEEN :startDate AND :endDate
64+
GROUP BY CAST(pt.paid_at AS DATE)
65+
) d
66+
CROSS JOIN opening_balance ob
67+
ORDER BY d.day;
68+
""", nativeQuery = true)
69+
List<DailyStatisticSummaryProjection> getDailyDepositSummaryByUser(
70+
String userId,
71+
Instant startDate,
72+
Instant endDate);
1873
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.codecampus.payment.repository.projection;
2+
3+
import java.math.BigDecimal;
4+
import java.time.LocalDate;
5+
6+
public interface DailyDepositSummaryProjection {
7+
LocalDate getDay();
8+
BigDecimal getTotalAmount();
9+
}
10+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.codecampus.payment.repository.projection;
2+
3+
import com.codecampus.payment.constant.TransactionEnum;
4+
import java.math.BigDecimal;
5+
import java.time.LocalDate;
6+
7+
public interface DailyStatisticSummaryProjection {
8+
LocalDate getDay();
9+
BigDecimal getDepositAmount();
10+
BigDecimal getPurchaseAmount();
11+
BigDecimal getWalletBalance();
12+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.codecampus.payment.service;
2+
3+
import com.codecampus.payment.dto.response.DailyDepositSummaryResponse;
4+
import com.codecampus.payment.dto.response.DailyStatisticSummaryResponse;
5+
import com.codecampus.payment.exception.AppException;
6+
import com.codecampus.payment.exception.ErrorCode;
7+
import com.codecampus.payment.helper.AuthenticationHelper;
8+
import com.codecampus.payment.repository.PaymentTransactionRepository;
9+
import java.time.Instant;
10+
import java.time.LocalDate;
11+
import java.time.Year;
12+
import java.time.YearMonth;
13+
import java.time.ZoneOffset;
14+
import java.util.List;
15+
import lombok.RequiredArgsConstructor;
16+
import org.springframework.stereotype.Service;
17+
18+
@Service
19+
@RequiredArgsConstructor
20+
public class PaymentStatisticService {
21+
private final PaymentTransactionRepository paymentTransactionRepository;
22+
23+
public List<DailyDepositSummaryResponse> getDailyDepositSummaryByMonth(int year, int month) {
24+
if (!AuthenticationHelper.getMyRoles().contains("ADMIN")) {
25+
throw new AppException(ErrorCode.UNAUTHORIZED);
26+
}
27+
if (year < 2004 || year > Year.now().getValue() ) {
28+
throw new AppException(ErrorCode.YEAR_INVALID);
29+
}
30+
if (month < 1 || month > 12) {
31+
throw new AppException(ErrorCode.MONTH_INVALID);
32+
}
33+
34+
35+
YearMonth ym = YearMonth.of(year, month);
36+
LocalDate start = ym.atDay(1);
37+
LocalDate end = ym.atEndOfMonth();
38+
39+
Instant startDate = start.atStartOfDay().toInstant(ZoneOffset.UTC);
40+
Instant endDate = end.plusDays(1).atStartOfDay().toInstant(ZoneOffset.UTC);
41+
42+
return paymentTransactionRepository.getDailyDepositSummary(startDate, endDate)
43+
.stream()
44+
.map(p -> new DailyDepositSummaryResponse(
45+
p.getDay(),
46+
p.getTotalAmount()
47+
))
48+
.toList();
49+
}
50+
51+
public List<DailyStatisticSummaryResponse> getDailyStatisticSummary(int year, int month) {
52+
if (year < 2004 || year > Year.now().getValue() ) {
53+
throw new AppException(ErrorCode.YEAR_INVALID);
54+
}
55+
if (month < 1 || month > 12) {
56+
throw new AppException(ErrorCode.MONTH_INVALID);
57+
}
58+
String userId = AuthenticationHelper.getMyUserId();
59+
60+
YearMonth ym = YearMonth.of(year, month);
61+
LocalDate start = ym.atDay(1);
62+
LocalDate end = ym.atEndOfMonth();
63+
64+
Instant startDate = start.atStartOfDay().toInstant(ZoneOffset.UTC);
65+
Instant endDate = end.plusDays(1).atStartOfDay().toInstant(ZoneOffset.UTC);
66+
67+
return paymentTransactionRepository.getDailyDepositSummaryByUser(userId, startDate, endDate)
68+
.stream()
69+
.map(p -> new DailyStatisticSummaryResponse(
70+
p.getDay(),
71+
p.getPurchaseAmount(),
72+
p.getDepositAmount(),
73+
p.getWalletBalance()
74+
))
75+
.toList();
76+
}
77+
78+
}

0 commit comments

Comments
 (0)