Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
18998d8
feat: 학교 데이터 캐싱 및 초기 로딩 기능 추가
polyglot-k Jul 10, 2025
a08cb25
refactor: PaymentRequest에서 applicationIds를 applicationSchoolIds로 변경
polyglot-k Jul 10, 2025
7eead85
refactor: PreparePaymentRequest에서 applicationId를 applicationSchoolIds…
polyglot-k Jul 10, 2025
52d1541
feat: RedisConfig 클래스 추가 및 RedisTemplate 설정
polyglot-k Jul 10, 2025
fdc800a
feat: SchoolApplicationProjection 및 SchoolRepository에 학교 이름별 카운트 쿼리 추가
polyglot-k Jul 10, 2025
f929d47
style: 코드 스타일 개선을 위한 공백 정리 및 임포트 정리
polyglot-k Jul 10, 2025
4a7c8d4
refactor: applicationId를 applicationSchoolId로 변경
polyglot-k Jul 10, 2025
7190c9a
refactor: ApplicationSchoolJpaRepository 삭제 및 관련 쿼리 제거
polyglot-k Jul 10, 2025
4756527
refactor: applicationIds를 applicationSchoolIds로 변경
polyglot-k Jul 10, 2025
3b0d4f1
refactor: applicationId를 applicationSchoolId로 변경 및 관련 리포지토리 수정
polyglot-k Jul 10, 2025
d374e46
refactor: ApplicationSchoolJpaRepository 임포트 위치 수정
polyglot-k Jul 10, 2025
9833ef1
refactor: ApplicationSchoolJpaRepository 임포트 위치 수정
polyglot-k Jul 10, 2025
91ec011
refactor: applicationId를 applicationSchoolId로 변경하여 쿼리 수정
polyglot-k Jul 10, 2025
52c700e
fix: 파일 끝에 개행 추가
polyglot-k Jul 10, 2025
1dde35c
refactor: existsByUserIdAndSchoolId 메서드 시그니처 수정 및 불필요한 임포트 제거
polyglot-k Jul 10, 2025
ed28177
feat: RetryConfig 클래스 추가 및 Spring Retry 활성화
polyglot-k Jul 10, 2025
3b8bc02
fix: null 체크 추가하여 decreaseApplicationCount 메서드 안정성 향상
polyglot-k Jul 10, 2025
a324f73
fix: 변수 이름을 명확하게 변경하여 가독성 향상
polyglot-k Jul 10, 2025
de95afe
fix: Item 클래스의 applicationSchoolId 변수 이름 수정 및 메시지 변경
polyglot-k Jul 10, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import life.mosu.mosuserver.domain.application.AdmissionTicketImageJpaRepository;
import life.mosu.mosuserver.domain.application.ApplicationJpaEntity;
import life.mosu.mosuserver.domain.application.ApplicationJpaRepository;
import life.mosu.mosuserver.domain.application.ApplicationSchoolJpaRepository;
import life.mosu.mosuserver.domain.applicationschool.ApplicationSchoolJpaEntity;
import life.mosu.mosuserver.domain.applicationschool.ApplicationSchoolJpaRepository;
import life.mosu.mosuserver.domain.school.SchoolJpaEntity;
import life.mosu.mosuserver.domain.school.SchoolJpaRepository;
import life.mosu.mosuserver.global.exception.CustomRuntimeException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import life.mosu.mosuserver.domain.application.AdmissionTicketImageJpaRepository;
import life.mosu.mosuserver.domain.application.ApplicationJpaEntity;
import life.mosu.mosuserver.domain.application.ApplicationJpaRepository;
import life.mosu.mosuserver.domain.application.ApplicationSchoolJpaRepository;
import life.mosu.mosuserver.domain.application.Subject;
import life.mosu.mosuserver.domain.applicationschool.ApplicationSchoolJpaEntity;
import life.mosu.mosuserver.domain.applicationschool.ApplicationSchoolJpaRepository;
import life.mosu.mosuserver.domain.profile.ProfileJpaEntity;
import life.mosu.mosuserver.domain.profile.ProfileJpaRepository;
import life.mosu.mosuserver.domain.refund.RefundJpaRepository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import life.mosu.mosuserver.domain.payment.PaymentStatus;

public record PaymentEvent(
List<Long> applicationIds,
List<Long> applicationSchoolIds,
String orderId,
PaymentStatus status,
Integer totalAmount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@ public class PaymentFailureHandler {
public void handlePaymentFailure(PaymentEvent event) {
List<PaymentJpaEntity> existingPayments = paymentRepository.findByOrderId(event.orderId());
Set<Long> existingAppIds = existingPayments.stream()
.map(PaymentJpaEntity::getApplicationId)
.map(PaymentJpaEntity::getApplicationSchoolId)
.collect(Collectors.toSet());

List<Long> missingAppIds = event.applicationIds().stream()
.filter(appId -> !existingAppIds.contains(appId))
List<Long> missingAppSchoolIds = event.applicationSchoolIds().stream()
.filter(appSchoolId -> !existingAppIds.contains(appSchoolId))
.toList();

// 상태 변경
existingPayments.forEach(payment -> payment.changeStatus(event.status()));

// 실패 신규 엔티티 생성
List<PaymentJpaEntity> newPayments = missingAppIds.stream()
.map(appId -> PaymentJpaEntity.ofFailure(
appId,
// 실패 신규 엔티티 생성 ( 배치 후속 처리 필요 )
List<PaymentJpaEntity> newPayments = missingAppSchoolIds.stream()
.map(appSchoolId -> PaymentJpaEntity.ofFailure(
appSchoolId,
event.orderId(),
event.status(),
event.totalAmount()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import static life.mosu.mosuserver.domain.discount.DiscountPolicy.FIXED_QUANTITY;

import java.util.List;
import life.mosu.mosuserver.domain.application.ApplicationJpaRepository;
import life.mosu.mosuserver.domain.applicationschool.ApplicationSchoolJpaRepository;
import life.mosu.mosuserver.domain.discount.DiscountPolicy;
import life.mosu.mosuserver.domain.payment.PaymentJpaEntity;
import life.mosu.mosuserver.domain.payment.PaymentRepository;
Expand All @@ -30,15 +30,13 @@
@RequiredArgsConstructor
public class PaymentService {

private final ApplicationJpaRepository applicationJpaRepository;

private final ApplicationSchoolJpaRepository applicationSchoolJpaRepository;
private final TossPaymentClient tossPayment;
private final OrderIdGenerator orderIdGenerator;
private final PaymentRepository paymentRepository;
private final ApplicationEventPublisher publisher;

public PaymentPrepareResponse prepare(PreparePaymentRequest request) {
// TODO: 인원 수 체크
/**
* 인원 수 redis에 동기화 -> 인원수가 넘어가면, application 까지 rollback
*/
Expand All @@ -53,20 +51,20 @@ public PaymentPrepareResponse prepare(PreparePaymentRequest request) {
@Retryable(retryFor = {HttpStatusCodeException.class})
public void confirm(PaymentRequest request) {
String orderId = request.orderId();
List<Long> applicationIds = request.applicationIds();
List<Long> applicationSchoolIds = request.applicationSchoolIds();
Integer amount = request.amount();
try {
checkApplicationsExist(request.applicationIds());
checkApplicationsExist(applicationSchoolIds);
verifyAmount(request.applicantSize(), request.amount());
checkDuplicatePayment(orderId);
ConfirmTossPaymentResponse response = confirmPaymentWithToss(request);
List<PaymentJpaEntity> paymentEntities = mapToPaymentEntities(request, response);
verifyPaymentSuccess(paymentEntities);
savePayments(paymentEntities);
publisher.publishEvent(PaymentEvent.ofSuccess(applicationIds, orderId, amount));
publisher.publishEvent(PaymentEvent.ofSuccess(applicationSchoolIds, orderId, amount));
} catch (Exception ex) {
log.error("error : {}", ex.getMessage());
publisher.publishEvent(PaymentEvent.ofFailed(applicationIds, orderId, amount));
publisher.publishEvent(PaymentEvent.ofFailed(applicationSchoolIds, orderId, amount));
throw ex;
}
}
Expand All @@ -87,7 +85,7 @@ public void cancel(String paymentId, CancelPaymentRequest request) {


private void checkApplicationsExist(List<Long> applicationIds) {
boolean existsAll = applicationJpaRepository.existsAllByIds(applicationIds,
boolean existsAll = applicationSchoolJpaRepository.existsAllByIds(applicationIds,
applicationIds.size());
if (!existsAll) {
log.warn("Application IDs not found: {}", applicationIds);
Expand Down Expand Up @@ -137,7 +135,7 @@ private ConfirmTossPaymentResponse confirmPaymentWithToss(PaymentRequest request

private List<PaymentJpaEntity> mapToPaymentEntities(PaymentRequest request,
ConfirmTossPaymentResponse response) {
return request.applicationIds().stream()
return request.applicationSchoolIds().stream()
.map(response::toEntity)
.toList();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package life.mosu.mosuserver.application.school;

import java.util.List;
import life.mosu.mosuserver.domain.school.SchoolApplicationProjection;
import life.mosu.mosuserver.domain.school.SchoolJpaEntity;
import life.mosu.mosuserver.domain.school.SchoolRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class SchoolQuotaCacheManager {

private static final String REDIS_KEY_SCHOOL_MAX_CAPACITY = "school:max_capacity:";
private static final String REDIS_KEY_SCHOOL_CURRENT_APPLICATIONS = "school:current_applications:";

private final RedisTemplate<String, Long> redisTemplate;
private final SchoolRepository schoolRepository;

public void cacheSchoolMaxCapacities() {
List<SchoolJpaEntity> schools = schoolRepository.findAll();
for (SchoolJpaEntity school : schools) {
String key = REDIS_KEY_SCHOOL_MAX_CAPACITY + school.getSchoolName();
redisTemplate.opsForValue().set(key, school.getCapacity());
}
}

public void cacheSchoolCurrentApplicationCounts() {
List<SchoolApplicationProjection> schoolApplications = schoolRepository.countBySchoolNameGroupBy();
for (SchoolApplicationProjection projection : schoolApplications) {
String key = REDIS_KEY_SCHOOL_CURRENT_APPLICATIONS + projection.schoolName();
redisTemplate.opsForValue().set(key, projection.count());
}
}

public Long getSchoolApplicationCounts(String schoolName) {
return redisTemplate.opsForValue()
.get(REDIS_KEY_SCHOOL_CURRENT_APPLICATIONS + schoolName);
}

public Long getSchoolCapacities(String schoolName) {
return redisTemplate.opsForValue()
.get(REDIS_KEY_SCHOOL_MAX_CAPACITY + schoolName);
}

public void increaseApplicationCount(String schoolName) {
String key = REDIS_KEY_SCHOOL_CURRENT_APPLICATIONS + schoolName;
redisTemplate.opsForValue().increment(key);
}

public void decreaseApplicationCount(String schoolName) {
String key = REDIS_KEY_SCHOOL_CURRENT_APPLICATIONS + schoolName;
Long currentValue = redisTemplate.opsForValue().get(key);
if (currentValue != null && currentValue > 0) {
redisTemplate.opsForValue().decrement(key);
}
}

public void preloadSchoolData() {
cacheSchoolMaxCapacities();
cacheSchoolCurrentApplicationCounts();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ private JPAQuery<Tuple> baseQuery() {
)
.from(applicationSchool)
.leftJoin(application).on(applicationSchool.applicationId.eq(application.id))
.leftJoin(payment).on(payment.applicationId.eq(application.id))
.leftJoin(payment).on(payment.applicationSchoolId.eq(applicationSchool.id))
.leftJoin(user).on(application.userId.eq(user.id))
.leftJoin(profile).on(profile.userId.eq(user.id))
.leftJoin(admissionTicketImage)
Expand Down Expand Up @@ -254,4 +254,4 @@ private String getAdmissionTicketImageUrl(String s3Key) {
Duration.ofMinutes(s3Properties.getPresignedUrlExpirationMinutes())
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ public ApplicationJpaEntity(
this.agreedToRefundPolicy = agreedToRefundPolicy;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface ApplicationJpaRepository extends JpaRepository<ApplicationJpaEntity, Long> {

@Query("SELECT COUNT(a) = :size FROM ApplicationJpaEntity a WHERE a.id IN :applicationIds")
boolean existsAllByIds(@Param("applicationIds") List<Long> applicationIds,
@Param("size") long size);

List<ApplicationJpaEntity> findAllByUserId(Long userId);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package life.mosu.mosuserver.domain.applicationschool;

import java.util.Collection;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface ApplicationSchoolJpaRepository extends
JpaRepository<ApplicationSchoolJpaEntity, Long> {


boolean existsByUserIdAndSchoolIdIn(Long userId, Collection<Long> schoolIds);
boolean existsByUserIdAndSchoolId(Long userId, Long schoolId);

List<ApplicationSchoolJpaEntity> findAllByApplicationId(Long applicationId);

boolean existsByApplicationId(Long applicationId);

@Query("SELECT COUNT(a) = :size FROM ApplicationSchoolJpaEntity a WHERE a.id IN :applicationSchoolIds")
boolean existsAllByIds(@Param("applicationSchoolIds") List<Long> applicationSchoolIds,
@Param("size") long size);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public class PaymentJpaEntity extends BaseTimeEntity {
@Column(name = "payment_id")
private Long id;

@Column(name = "application_id")
private Long applicationId;
@Column(name = "application_school_id")
private Long applicationSchoolId;

@Column(name = "payment_key")
private String paymentKey;
Expand All @@ -48,14 +48,14 @@ public class PaymentJpaEntity extends BaseTimeEntity {

@Builder(access = AccessLevel.PRIVATE)
private PaymentJpaEntity(
Long applicationId,
Long applicationSchoolId,
String paymentKey,
String orderId,
PaymentAmountVO paymentAmount,
PaymentStatus paymentStatus,
PaymentMethod paymentMethod
) {
this.applicationId = applicationId;
this.applicationSchoolId = applicationSchoolId;
this.paymentKey = paymentKey;
this.orderId = orderId;
this.paymentAmount = paymentAmount;
Expand All @@ -64,15 +64,15 @@ private PaymentJpaEntity(
}

public static PaymentJpaEntity of(
Long applicationId,
Long applicationSchoolId,
String paymentKey,
String orderId,
PaymentStatus paymentStatus,
PaymentAmountVO paymentAmount,
PaymentMethod paymentMethod
) {
return PaymentJpaEntity.builder()
.applicationId(applicationId)
.applicationSchoolId(applicationSchoolId)
.paymentKey(paymentKey)
.orderId(orderId)
.paymentStatus(paymentStatus)
Expand All @@ -82,14 +82,14 @@ public static PaymentJpaEntity of(
}

public static PaymentJpaEntity ofFailure(
Long applicationId,
Long applicationSchoolId,
String orderId,
PaymentStatus paymentStatus,
Integer totalAmount
) {
PaymentAmountVO paymentAmount = PaymentAmountVO.ofFailure(totalAmount);
return PaymentJpaEntity.builder()
.applicationId(applicationId)
.applicationSchoolId(applicationSchoolId)
.orderId(orderId)
.paymentStatus(paymentStatus)
.paymentAmount(paymentAmount)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package life.mosu.mosuserver.domain.school;

public record SchoolApplicationProjection(Long schoolId, String schoolName, Long count) {

}
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
package life.mosu.mosuserver.domain.school;


import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.LocalDate;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDate;

@Entity
@Getter
@Table(name="school")
@Table(name = "school")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SchoolJpaEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="school_id")
@Column(name = "school_id")
private Long id;

@Column(name = "school_name")
Expand All @@ -34,4 +41,12 @@ public class SchoolJpaEntity {
@Column(name = "capacity")
private Long capacity;

public SchoolJpaEntity(String schoolName, Area area, AddressJpaVO address, LocalDate examDate,
Long capacity) {
this.schoolName = schoolName;
this.area = area;
this.address = address;
this.examDate = examDate;
this.capacity = capacity;
}
}
Loading