diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..f3f29f4 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,47 @@ +name: Publish Account Service API Package + +on: + push: + tags: + - 'v*' + +permissions: + contents: read + packages: write + +jobs: + publish-to-github-packages: + runs-on: ubuntu-latest + + steps: + - name: 저장소 코드 가져오기 (Checkout) + uses: actions/checkout@v4 + + - name: JDK 21 설치 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + - name: Gradle 캐시 설정 + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('account-service/**/*.gradle*', 'account-service/**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: gradlew 실행 권한 부여 + working-directory: account-service + run: chmod +x ./gradlew + + - name: 태그 이름에서 버전 정보 추출 (e.g. v1.0.0 -> 1.0.0) + run: echo "VERSION_TAG=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + + - name: Gradle로 GitHub Packages에 배포 + working-directory: account-service + run: ./gradlew publish -Pversion=${{ env.VERSION_TAG }} + env: + GITHUB_ACTOR: ${{ github.actor }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/account-service/account-service-api/build.gradle b/account-service/account-service-api/build.gradle index 69a4897..bfef8f5 100644 --- a/account-service/account-service-api/build.gradle +++ b/account-service/account-service-api/build.gradle @@ -1,9 +1,51 @@ +plugins { + id 'java-library' + id 'maven-publish' +} + +group = 'com.github.DongHyeonKa' +version = '1.0.0' + repositories { mavenCentral() } +ext { + set('springCloudVersion', "2022.0.4") + set('eventuateTramVersion', "0.35.0.RELEASE") + set('eventuateTramSagasVersion', "0.24.0.RELEASE") + set('eventuateCommonVersion', "0.19.0.RELEASE") +} + dependencies { - + // Eventuate Tram Aggregate Domain Events + implementation "io.eventuate.tram.core:eventuate-tram-aggregate-domain-events:${eventuateTramVersion}" + // Validation + implementation 'org.springframework.boot:spring-boot-starter-validation' + // Spring Web + implementation 'org.springframework.boot:spring-boot-starter-web' + // Lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' +} + +publishing { + publications { + mavenJava(MavenPublication) { + artifactId = 'account-service-api' + from components.java + } + } + repositories { + maven { + name = "GithubPackages" + url = "https://maven.pkg.github.com/DongHyeonKa/Account-server" + credentials { + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") + } + } + } } tasks.named('test') { diff --git a/account-service/account-service-api/src/main/java/com/synapse/account_service_api/event/SubscriptionCreatedEvent.java b/account-service/account-service-api/src/main/java/com/synapse/account_service_api/event/SubscriptionCreatedEvent.java new file mode 100644 index 0000000..14f792c --- /dev/null +++ b/account-service/account-service-api/src/main/java/com/synapse/account_service_api/event/SubscriptionCreatedEvent.java @@ -0,0 +1,38 @@ +package com.synapse.account_service_api.event; + +import java.util.UUID; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PUBLIC) +public class SubscriptionCreatedEvent implements SubscriptionDomainEvent { + private UUID subscriptionId; + private UUID userId; + private String subscriptionTier; + private int maxSubscriptionCount; + private String nextRenewalDate; + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } +} diff --git a/account-service/account-service-api/src/main/java/com/synapse/account_service_api/event/SubscriptionDomainEvent.java b/account-service/account-service-api/src/main/java/com/synapse/account_service_api/event/SubscriptionDomainEvent.java new file mode 100644 index 0000000..b80f598 --- /dev/null +++ b/account-service/account-service-api/src/main/java/com/synapse/account_service_api/event/SubscriptionDomainEvent.java @@ -0,0 +1,7 @@ +package com.synapse.account_service_api.event; + +import io.eventuate.tram.events.common.DomainEvent; + +public interface SubscriptionDomainEvent extends DomainEvent { + +} diff --git a/account-service/account-service/build.gradle b/account-service/account-service/build.gradle index a0fc512..2b32b7f 100644 --- a/account-service/account-service/build.gradle +++ b/account-service/account-service/build.gradle @@ -4,13 +4,41 @@ repositories { dependencies { implementation project(':account-service-api') - + // Simple DSL + implementation "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-orchestration-simple-dsl-starter:${eventuateTramSagasVersion}" + // Saga Participant + implementation "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-participant-starter:${eventuateTramSagasVersion}" + // Eventuate Tram JDBC + implementation "io.eventuate.tram.core:eventuate-tram-spring-jdbc-kafka:${eventuateTramVersion}" + // Security + implementation 'org.springframework.boot:spring-boot-starter-security' + // Auth0 + implementation 'com.auth0:java-jwt:4.4.0' + // JPA + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + // PostgreSQL + runtimeOnly 'org.postgresql:postgresql' + // OAuth2 Client + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + // OAuth2 Resource Server + implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' //Spring Actuator implementation 'org.springframework.boot:spring-boot-starter-actuator' //Spring Prometheus implementation 'io.micrometer:micrometer-registry-prometheus' //Redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' + //QueryDSL + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + // test + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testImplementation 'org.springframework.security:spring-security-test' + //H2 Database + implementation 'com.h2database:h2' } def generated = 'src/main/generated' diff --git a/account-service/account-service/src/main/java/com/synapse/account_service/domain/entity/Member.java b/account-service/account-service/src/main/java/com/synapse/account_service/domain/entity/Member.java index 52d9c25..699baf9 100644 --- a/account-service/account-service/src/main/java/com/synapse/account_service/domain/entity/Member.java +++ b/account-service/account-service/src/main/java/com/synapse/account_service/domain/entity/Member.java @@ -21,7 +21,6 @@ @Index(name = "idx_member_username", columnList = "username") }) public class Member extends BaseEntity { - @Id @Column(name = "member_id", columnDefinition = "uuid") private UUID id; @@ -76,9 +75,9 @@ public static ResultWithDomainEvents register( .role(MemberRole.USER) .build(); - MemberRegisteredEvent event = new MemberRegisteredEvent(userId, email, username); + MemberRegisteredEvent memberEvent = new MemberRegisteredEvent(userId, email, username); - return new ResultWithDomainEvents<>(member, event); + return new ResultWithDomainEvents<>(member, memberEvent); } public static ResultWithDomainEvents linkSocialAccount( @@ -91,9 +90,9 @@ public static ResultWithDomainEvents linkSocialAccoun .role(MemberRole.USER) .build(); - SocialAccountLinkedEvent event = new SocialAccountLinkedEvent(userId, provider, registrationId); + SocialAccountLinkedEvent socialEvent = new SocialAccountLinkedEvent(userId, provider, registrationId); - return new ResultWithDomainEvents<>(member, event); + return new ResultWithDomainEvents<>(member, socialEvent); } public void setSubscription(Subscription subscription) { diff --git a/account-service/account-service/src/main/java/com/synapse/account_service/domain/entity/Subscription.java b/account-service/account-service/src/main/java/com/synapse/account_service/domain/entity/Subscription.java index 5dbe665..90d3f84 100644 --- a/account-service/account-service/src/main/java/com/synapse/account_service/domain/entity/Subscription.java +++ b/account-service/account-service/src/main/java/com/synapse/account_service/domain/entity/Subscription.java @@ -1,11 +1,15 @@ package com.synapse.account_service.domain.entity; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.UUID; import com.synapse.account_service.domain.common.BaseTimeEntity; import com.synapse.account_service.domain.enums.SubscriptionTier; +import com.synapse.account_service_api.event.SubscriptionCreatedEvent; +import com.synapse.account_service_api.event.SubscriptionDomainEvent; +import io.eventuate.tram.events.aggregates.ResultWithDomainEvents; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; @@ -17,8 +21,10 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @Table(name = "subscription") public class Subscription extends BaseTimeEntity { + + private static final DateTimeFormatter FORMATTING_PATTERN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + @Id - @GeneratedValue(strategy = GenerationType.UUID) @Column(name = "subscription_id") private UUID id; @@ -34,12 +40,34 @@ public class Subscription extends BaseTimeEntity { private ZonedDateTime nextRenewalDate; @Builder - public Subscription(Member member, SubscriptionTier tier, ZonedDateTime nextRenewalDate) { + public Subscription(UUID id, Member member, SubscriptionTier tier, ZonedDateTime nextRenewalDate) { + this.id = id; this.member = member; this.tier = tier; this.nextRenewalDate = nextRenewalDate; } + public static ResultWithDomainEvents register( + UUID userId, Member member, SubscriptionTier tier, ZonedDateTime nextRenewalDate + ) { + Subscription subscription = Subscription.builder() + .id(UUID.randomUUID()) + .member(member) + .tier(tier) + .nextRenewalDate(nextRenewalDate) + .build(); + + SubscriptionCreatedEvent subscriptionEvent = new SubscriptionCreatedEvent( + subscription.getId(), + userId, + tier.name(), + tier.getMaxSubscriptionCount(), + nextRenewalDate.format(FORMATTING_PATTERN) + ); + + return new ResultWithDomainEvents<>(subscription, subscriptionEvent); + } + protected void setMemberInternal(Member member) { this.member = member; } diff --git a/account-service/account-service/src/main/java/com/synapse/account_service/domain/enums/SubscriptionTier.java b/account-service/account-service/src/main/java/com/synapse/account_service/domain/enums/SubscriptionTier.java index 74b23f2..52710f6 100644 --- a/account-service/account-service/src/main/java/com/synapse/account_service/domain/enums/SubscriptionTier.java +++ b/account-service/account-service/src/main/java/com/synapse/account_service/domain/enums/SubscriptionTier.java @@ -1,6 +1,48 @@ package com.synapse.account_service.domain.enums; +import java.util.Collections; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + public enum SubscriptionTier { - FREE, - PRO + FREE("FREE", 0, 3), + PRO("PRO", 0, 100) + ; + + private final String SubscriptionTierName; + private final int currentSubscriptionCount; + private final int maxSubscriptionCount; + + private SubscriptionTier(String SubscriptionTierName, int currentSubscriptionCount, int maxSubscriptionCount) { + this.SubscriptionTierName = SubscriptionTierName; + this.currentSubscriptionCount = currentSubscriptionCount; + this.maxSubscriptionCount = maxSubscriptionCount; + } + + public String getSubscriptionTierName() { + return SubscriptionTierName; + } + + public int getCurrentSubscriptionCount() { + return currentSubscriptionCount; + } + + public int getMaxSubscriptionCount() { + return maxSubscriptionCount; + } + + public static final Map TIER_MAP = Collections.unmodifiableMap( + Stream.of(values()) + .collect(Collectors.toMap(SubscriptionTier::getSubscriptionTierName, Function.identity())) + ); + + public static SubscriptionTier fromTier(String tierName) { + SubscriptionTier result = TIER_MAP.get(tierName); + if(result == null) { + throw new IllegalArgumentException("일치하는 티어 타입이 없습니다." + tierName); + } + return result; + } } diff --git a/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/configuration/EventuateConfig.java b/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/configuration/EventuateConfig.java index 802a896..c21da0e 100644 --- a/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/configuration/EventuateConfig.java +++ b/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/configuration/EventuateConfig.java @@ -2,18 +2,25 @@ import io.eventuate.tram.events.publisher.DomainEventPublisher; import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -// import com.synapse.account_service.eventuate.publisher.MemberDomainEventPublisher; +import com.synapse.account_service.eventuate.publisher.MemberDomainEventPublisher; +import com.synapse.account_service.eventuate.publisher.SubscriptionDomainEventPublisher; -// @Configuration -// @Import(TramEventsPublisherConfiguration.class) -// public class EventuateConfig { -// @Bean -// public MemberDomainEventPublisher memberDomainEventPublisher(DomainEventPublisher eventPublisher) { -// return new MemberDomainEventPublisher(eventPublisher); -// } -// } +@Configuration +@Import({TramEventsPublisherConfiguration.class, TramJdbcKafkaConfiguration.class}) +public class EventuateConfig { + @Bean + public MemberDomainEventPublisher memberDomainEventPublisher(DomainEventPublisher eventPublisher) { + return new MemberDomainEventPublisher(eventPublisher); + } + + @Bean + public SubscriptionDomainEventPublisher subscriptionDomainEventPublisher(DomainEventPublisher eventPublisher) { + return new SubscriptionDomainEventPublisher(eventPublisher); + } +} diff --git a/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/publisher/MemberDomainEventPublisher.java b/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/publisher/MemberDomainEventPublisher.java index 8f4ceea..53d003e 100644 --- a/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/publisher/MemberDomainEventPublisher.java +++ b/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/publisher/MemberDomainEventPublisher.java @@ -6,9 +6,9 @@ import io.eventuate.tram.events.aggregates.AbstractAggregateDomainEventPublisher; import io.eventuate.tram.events.publisher.DomainEventPublisher; -// public class MemberDomainEventPublisher extends AbstractAggregateDomainEventPublisher { +public class MemberDomainEventPublisher extends AbstractAggregateDomainEventPublisher { -// public MemberDomainEventPublisher(DomainEventPublisher eventPublisher) { -// super(eventPublisher, Member.class, Member::getId); -// } -// } + public MemberDomainEventPublisher(DomainEventPublisher eventPublisher) { + super(eventPublisher, Member.class, Member::getId); + } +} diff --git a/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/publisher/SubscriptionDomainEventPublisher.java b/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/publisher/SubscriptionDomainEventPublisher.java new file mode 100644 index 0000000..71b6c05 --- /dev/null +++ b/account-service/account-service/src/main/java/com/synapse/account_service/eventuate/publisher/SubscriptionDomainEventPublisher.java @@ -0,0 +1,15 @@ +package com.synapse.account_service.eventuate.publisher; + +import com.synapse.account_service.domain.entity.Subscription; +import com.synapse.account_service_api.event.SubscriptionDomainEvent; + +import io.eventuate.tram.events.aggregates.AbstractAggregateDomainEventPublisher; +import io.eventuate.tram.events.publisher.DomainEventPublisher; + +public class SubscriptionDomainEventPublisher extends AbstractAggregateDomainEventPublisher{ + + public SubscriptionDomainEventPublisher(DomainEventPublisher eventPublisher) { + super(eventPublisher, Subscription.class, Subscription::getId); + } + +} diff --git a/account-service/account-service/src/main/java/com/synapse/account_service/exception/GlobalExceptionHandler.java b/account-service/account-service/src/main/java/com/synapse/account_service/exception/GlobalExceptionHandler.java index 6c16bde..246230b 100644 --- a/account-service/account-service/src/main/java/com/synapse/account_service/exception/GlobalExceptionHandler.java +++ b/account-service/account-service/src/main/java/com/synapse/account_service/exception/GlobalExceptionHandler.java @@ -55,6 +55,7 @@ private void logError(Exception e, HttpServletRequest request) { log.error(String.format(LOG_FORMAT_ERROR_PATTERN, request.getMethod(), request.getRequestURI(), + e.getClass().getSimpleName(), e.getClass().getName(), e.getMessage())); } diff --git a/account-service/account-service/src/main/java/com/synapse/account_service/service/AccountService.java b/account-service/account-service/src/main/java/com/synapse/account_service/service/AccountService.java index 05bfb56..1829efe 100644 --- a/account-service/account-service/src/main/java/com/synapse/account_service/service/AccountService.java +++ b/account-service/account-service/src/main/java/com/synapse/account_service/service/AccountService.java @@ -13,13 +13,15 @@ import com.synapse.account_service.domain.entity.Subscription; import com.synapse.account_service.domain.enums.SubscriptionTier; import com.synapse.account_service.domain.repository.MemberRepository; -// import com.synapse.account_service.eventuate.publisher.MemberDomainEventPublisher; +import com.synapse.account_service.eventuate.publisher.MemberDomainEventPublisher; +import com.synapse.account_service.eventuate.publisher.SubscriptionDomainEventPublisher; import com.synapse.account_service.exception.DuplicatedException; import com.synapse.account_service.exception.ExceptionType; import com.synapse.account_service_api.dto.request.SignUpRequest; import com.synapse.account_service_api.dto.response.SignUpResponse; import com.synapse.account_service_api.event.MemberDomainEvent; +import com.synapse.account_service_api.event.SubscriptionDomainEvent; import io.eventuate.tram.events.aggregates.ResultWithDomainEvents; import lombok.RequiredArgsConstructor; @@ -34,7 +36,9 @@ public class AccountService { private final MemberRepository memberRepository; private final PasswordEncoder passwordEncoder; - // private final MemberDomainEventPublisher memberDomainEventPublisher; + private final MemberDomainEventPublisher memberDomainEventPublisher; + private final SubscriptionDomainEventPublisher subscriptionDomainEventPublisher; + @Transactional public SignUpResponse registerMember(SignUpRequest request) { @@ -56,28 +60,31 @@ public SignUpResponse registerMember(SignUpRequest request) { Member memberResult = memberAndEvents.result; - createAndSetDefaultSubscription(memberResult); - memberRepository.save(memberResult); - // memberDomainEventPublisher.publish(memberResult, memberAndEvents.events); + memberDomainEventPublisher.publish(memberResult, memberAndEvents.events); + publishSubscriptionEvent(memberResult); return new SignUpResponse( memberResult.getId(), memberResult.getEmail(), - memberResult.getUsername(), + memberResult.getUsername(), memberResult.getRole().name() ); } - private void createAndSetDefaultSubscription(Member member) { - ZonedDateTime nextRenewalDate = ZonedDateTime.now(ZoneId.systemDefault()).plusDays(1).with(LocalTime.MIDNIGHT); // 무료 사용자는 자정 초기화 + private void publishSubscriptionEvent(Member memberResult) { + ZonedDateTime nextRenewalDate = ZonedDateTime.now(ZoneId.systemDefault()).plusDays(1).with(LocalTime.MIDNIGHT); - Subscription freeSubscription = Subscription.builder() - .tier(SubscriptionTier.FREE) - .nextRenewalDate(nextRenewalDate) - .build(); - - member.setSubscription(freeSubscription); + ResultWithDomainEvents subscriptionAndEvents = Subscription.register( + memberResult.getId(), + memberResult, + SubscriptionTier.FREE, + nextRenewalDate); + + Subscription subscriptionResult = subscriptionAndEvents.result; + + memberResult.setSubscription(subscriptionResult); + subscriptionDomainEventPublisher.publish(subscriptionResult, subscriptionAndEvents.events); } } diff --git a/account-service/account-service/src/main/java/com/synapse/account_service/service/MemberRegistrationService.java b/account-service/account-service/src/main/java/com/synapse/account_service/service/MemberRegistrationService.java index f884510..a07e459 100644 --- a/account-service/account-service/src/main/java/com/synapse/account_service/service/MemberRegistrationService.java +++ b/account-service/account-service/src/main/java/com/synapse/account_service/service/MemberRegistrationService.java @@ -14,8 +14,10 @@ import com.synapse.account_service.domain.entity.Subscription; import com.synapse.account_service.domain.enums.SubscriptionTier; import com.synapse.account_service.domain.repository.MemberRepository; -// import com.synapse.account_service.eventuate.publisher.MemberDomainEventPublisher; +import com.synapse.account_service.eventuate.publisher.MemberDomainEventPublisher; +import com.synapse.account_service.eventuate.publisher.SubscriptionDomainEventPublisher; import com.synapse.account_service_api.event.MemberDomainEvent; +import com.synapse.account_service_api.event.SubscriptionDomainEvent; import io.eventuate.tram.events.aggregates.ResultWithDomainEvents; import lombok.RequiredArgsConstructor; @@ -26,7 +28,8 @@ public class MemberRegistrationService { private final MemberRepository memberRepository; - // private final MemberDomainEventPublisher memberDomainEventPublisher; + private final MemberDomainEventPublisher memberDomainEventPublisher; + private final SubscriptionDomainEventPublisher subscriptionDomainEventPublisher; @Transactional public Member registerOauthUser(String provider, ProviderUser providerUser) { @@ -53,23 +56,26 @@ public Member registerOauthUser(String provider, ProviderUser providerUser) { Member memberResult = memberAndEvents.result; - createAndSetDefaultSubscription(memberResult); - memberRepository.save(memberResult); - // memberDomainEventPublisher.publish(memberResult, memberAndEvents.events); + memberDomainEventPublisher.publish(memberResult, memberAndEvents.events); + publishSubscriptionEvent(memberResult); return memberResult; } - private void createAndSetDefaultSubscription(Member member) { - ZonedDateTime nextRenewalDate = ZonedDateTime.now(ZoneId.systemDefault()).plusDays(1).with(LocalTime.MIDNIGHT); // 무료 사용자는 자정 초기화 + private void publishSubscriptionEvent(Member memberResult) { + ZonedDateTime nextRenewalDate = ZonedDateTime.now(ZoneId.systemDefault()).plusDays(1).with(LocalTime.MIDNIGHT); + + ResultWithDomainEvents subscriptionAndEvents = Subscription.register( + memberResult.getId(), + memberResult, + SubscriptionTier.FREE, + nextRenewalDate); + + Subscription subscriptionResult = subscriptionAndEvents.result; - Subscription freeSubscription = Subscription.builder() - .tier(SubscriptionTier.FREE) - .nextRenewalDate(nextRenewalDate) - .build(); - - member.setSubscription(freeSubscription); + memberResult.setSubscription(subscriptionResult); + subscriptionDomainEventPublisher.publish(subscriptionResult, subscriptionAndEvents.events); } } diff --git a/account-service/account-service/src/main/resources/application-local.yml b/account-service/account-service/src/main/resources/application-local.yml index 4dc757a..7421c0f 100644 --- a/account-service/account-service/src/main/resources/application-local.yml +++ b/account-service/account-service/src/main/resources/application-local.yml @@ -1,4 +1,8 @@ spring: + jackson: + deserialization: + adjust-dates-to-context-time-zone: false + datasource: driver-class-name: org.postgresql.Driver url: jdbc:postgresql://${local-db.postgres.host}:${local-db.postgres.port}/${local-db.postgres.name} @@ -26,7 +30,7 @@ spring: highlight: sql: true hbm2ddl: - auto: update + auto: create dialect: org.hibernate.dialect.PostgreSQLDialect jdbc: batch_size: 30 @@ -78,10 +82,10 @@ eventuate: auto-offset-reset: latest enable-auto-commit: true destinations: - accountService: - destination: accountService + paymentService: + destination: paymentService dlq: - destination: accountService-dlq + destination: paymentService-dlq kafka: message: serializer: io.eventuate.messaging.kafka.basic.consumer.DefaultKafkaMessageSerializer diff --git a/account-service/account-service/src/main/resources/db/eventualMessage.sql b/account-service/account-service/src/main/resources/db/eventualMessage.sql new file mode 100644 index 0000000..7cd9681 --- /dev/null +++ b/account-service/account-service/src/main/resources/db/eventualMessage.sql @@ -0,0 +1,2 @@ +ALTER TABLE eventuate.message +ADD COLUMN IF NOT EXISTS message_partition SMALLINT; diff --git a/account-service/account-service/src/test/java/com/synapse/account_service/service/AccountServiceTest.java b/account-service/account-service/src/test/java/com/synapse/account_service/service/AccountServiceTest.java index a5d6608..f2feaf5 100644 --- a/account-service/account-service/src/test/java/com/synapse/account_service/service/AccountServiceTest.java +++ b/account-service/account-service/src/test/java/com/synapse/account_service/service/AccountServiceTest.java @@ -11,19 +11,23 @@ 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 org.springframework.security.crypto.password.PasswordEncoder; -import com.synapse.account_service.TestConfig; import com.synapse.account_service.domain.entity.Member; import com.synapse.account_service.domain.repository.MemberRepository; +import com.synapse.account_service.eventuate.publisher.MemberDomainEventPublisher; +import com.synapse.account_service.eventuate.publisher.SubscriptionDomainEventPublisher; import com.synapse.account_service_api.dto.request.SignUpRequest; import com.synapse.account_service_api.dto.response.SignUpResponse; import com.synapse.account_service.exception.DuplicatedException; -public class AccountServiceTest extends TestConfig { +@ExtendWith(MockitoExtension.class) +public class AccountServiceTest { @InjectMocks private AccountService accountService; @@ -34,6 +38,12 @@ public class AccountServiceTest extends TestConfig { @Mock private PasswordEncoder passwordEncoder; + @Mock + private MemberDomainEventPublisher memberDomainEventPublisher; + + @Mock + private SubscriptionDomainEventPublisher subscriptionDomainEventPublisher; + @Test @DisplayName("회원가입 성공") void signUp_success() { diff --git a/account-service/account-service/src/test/java/com/synapse/account_service/service/MemberRegistrationServiceTest.java b/account-service/account-service/src/test/java/com/synapse/account_service/service/MemberRegistrationServiceTest.java index 812c7e5..5595f6d 100644 --- a/account-service/account-service/src/test/java/com/synapse/account_service/service/MemberRegistrationServiceTest.java +++ b/account-service/account-service/src/test/java/com/synapse/account_service/service/MemberRegistrationServiceTest.java @@ -16,18 +16,22 @@ 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 org.springframework.security.oauth2.core.user.DefaultOAuth2User; -import com.synapse.account_service.TestConfig; import com.synapse.account_service.domain.Attributes; import com.synapse.account_service.domain.ProviderUser; import com.synapse.account_service.domain.entity.Member; import com.synapse.account_service.domain.repository.MemberRepository; import com.synapse.account_service.domain.socials.GoogleUser; +import com.synapse.account_service.eventuate.publisher.MemberDomainEventPublisher; +import com.synapse.account_service.eventuate.publisher.SubscriptionDomainEventPublisher; -class MemberRegistrationServiceTest extends TestConfig { +@ExtendWith(MockitoExtension.class) +class MemberRegistrationServiceTest { @InjectMocks private MemberRegistrationService memberRegistrationService; @@ -35,6 +39,12 @@ class MemberRegistrationServiceTest extends TestConfig { @Mock private MemberRepository memberRepository; + @Mock + private MemberDomainEventPublisher memberDomainEventPublisher; + + @Mock + private SubscriptionDomainEventPublisher subscriptionDomainEventPublisher; + private final String provider = "google"; private final String providerId = "123456789"; private final String email = "test@example.com"; @@ -114,4 +124,4 @@ void registerOauthUser_whenSocialAccountExists_returnsMember() { verify(existingMember, never()).linkSocialAccount(anyString(), anyString()); assertThat(result).isEqualTo(existingMember); } -} +} diff --git a/account-service/build.gradle b/account-service/build.gradle index 0426ef9..6ff6b21 100644 --- a/account-service/build.gradle +++ b/account-service/build.gradle @@ -31,57 +31,25 @@ subprojects{ } ext { - set('springCloudVersion', "2022.0.5") + set('springCloudVersion', "2022.0.4") set('eventuateTramVersion', "0.35.0.RELEASE") set('eventuateTramSagasVersion', "0.24.0.RELEASE") set('eventuateCommonVersion', "0.19.0.RELEASE") } dependencies { - // Simple DSL - implementation "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-orchestration-simple-dsl-starter:${eventuateTramSagasVersion}" - // Saga Participant - implementation "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-participant-starter:${eventuateTramSagasVersion}" - // Eventuate Tram Consumer - implementation "io.eventuate.tram.core:eventuate-tram-spring-consumer-kafka:${eventuateTramVersion}" - // Eventuate Tram Producer JDBC - implementation "io.eventuate.tram.core:eventuate-tram-spring-producer-jdbc:${eventuateTramVersion}" - // Eventuate Tram Consumer JDBC - implementation "io.eventuate.tram.core:eventuate-tram-spring-consumer-jdbc:${eventuateTramVersion}" // Eventuate Tram Aggregate Domain Events implementation "io.eventuate.tram.core:eventuate-tram-aggregate-domain-events:${eventuateTramVersion}" - // Security - implementation 'org.springframework.boot:spring-boot-starter-security' - // Auth0 - implementation 'com.auth0:java-jwt:4.4.0' - // JPA - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - // PostgreSQL - runtimeOnly 'org.postgresql:postgresql' - // OAuth2 Client - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' - // OAuth2 Resource Server - implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' + // Validation + implementation 'org.springframework.boot:spring-boot-starter-validation' + // Spring Web + implementation 'org.springframework.boot:spring-boot-starter-web' // Lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' // Test testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.springframework.security:spring-security-test' - // Spring Web - implementation 'org.springframework.boot:spring-boot-starter-web' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - //H2 Database - implementation 'com.h2database:h2' - // Validation - implementation 'org.springframework.boot:spring-boot-starter-validation' - //QueryDSL - implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' - annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" - annotationProcessor "jakarta.annotation:jakarta.annotation-api" - annotationProcessor "jakarta.persistence:jakarta.persistence-api" } }