This commit is contained in:
ondal
2025-02-13 00:16:35 +09:00
parent 2b100e1bcf
commit 11a49c33a8
101 changed files with 518 additions and 118 deletions
+1
View File
@@ -0,0 +1 @@
com.unicorn.lifesub.mysub.infra.MySubApplication
@@ -5,22 +5,30 @@ spring:
application:
name: mysub-service
datasource:
url: ${POSTGRES_URL}
username: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:mysub}
username: ${POSTGRES_USER:admin}
password: ${POSTGRES_PASSWORD:Passw0rd}
driver-class-name: org.postgresql.Driver
# JPA 설정
jpa:
hibernate:
ddl-auto: ${JPA_DDL_AUTO:validate}
show-sql: ${JPA_SHOW_SQL:false}
ddl-auto: ${JPA_DDL_AUTO:update}
show-sql: ${JPA_SHOW_SQL:true}
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.PostgreSQLDialect
allowedorigins: ${ALLOWED_ORIGINS:*}
jwt:
secret-key: ${JWT_SECRET_KEY:8O2HQ13etL2BWZvYOiWsJ5uWFoLi6NBUG8divYVoCgtHVvlk3dqRksMl16toztDUeBTSIuOOPvHIrYq11G2BwQ}
allowed-origins: ${ALLOWED_ORIGINS:*}
springdoc:
swagger-ui:
path: /swagger-ui.html
api-docs:
path: /api-docs
logging:
level:
com.unicorn: DEBUG
org.hibernate.SQL: TRACE
@@ -2,8 +2,14 @@ package com.unicorn.lifesub.mysub.infra;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@SpringBootApplication
@SpringBootApplication(
scanBasePackages = {
"com.unicorn.lifesub.mysub",
"com.unicorn.lifesub.common"
}
)
public class MySubApplication {
public static void main(String[] args) {
SpringApplication.run(MySubApplication.class, args);
@@ -0,0 +1,128 @@
// File: lifesub/mysub-infra/src/main/java/com/unicorn/lifesub/mysub/infra/config/DataLoader.java
package com.unicorn.lifesub.mysub.infra.config;
import com.unicorn.lifesub.mysub.infra.gateway.entity.CategoryEntity;
import com.unicorn.lifesub.mysub.infra.gateway.entity.SubscriptionEntity;
import com.unicorn.lifesub.mysub.infra.gateway.repository.CategoryJpaRepository;
import com.unicorn.lifesub.mysub.infra.gateway.repository.SubscriptionJpaRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Slf4j
@Component
@RequiredArgsConstructor
public class DataLoader implements CommandLineRunner {
private final CategoryJpaRepository categoryRepository;
private final SubscriptionJpaRepository subscriptionRepository;
@Override
@Transactional
public void run(String... args) {
if (categoryRepository.count() == 0) {
loadCategories();
}
if (subscriptionRepository.count() == 0) {
loadSubscriptions();
}
}
private void loadCategories() {
log.info("Loading sample categories...");
List<CategoryEntity> categories = Arrays.asList(
CategoryEntity.builder()
.categoryId("OTT")
.name("OTT/동영상")
.build(),
CategoryEntity.builder()
.categoryId("MUSIC")
.name("음악")
.build(),
CategoryEntity.builder()
.categoryId("FOOD")
.name("식품")
.build(),
CategoryEntity.builder()
.categoryId("LIFE")
.name("생활")
.build(),
CategoryEntity.builder()
.categoryId("BEAUTY")
.name("뷰티")
.build(),
CategoryEntity.builder()
.categoryId("EDU")
.name("교육")
.build()
);
categoryRepository.saveAll(categories);
log.info("Sample categories loaded");
}
private void loadSubscriptions() {
log.info("Loading sample subscriptions...");
// 카테고리별 서비스 매핑
Map<String, List<SubscriptionEntity>> subscriptionsByCategory = Map.of(
"OTT", Arrays.asList(
createSubscription("넷플릭스", "전세계 최대 OTT 서비스", "OTT", 17000, 4, "/images/netflix.png"),
createSubscription("티빙", "국내 실시간 방송과 예능/드라마 VOD", "OTT", 13900, 4, "/images/tving.png"),
createSubscription("디즈니플러스", "디즈니, 픽사, 마블, 스타워즈 콘텐츠", "OTT", 9900, 4, "/images/disney.png")
),
"MUSIC", Arrays.asList(
createSubscription("멜론", "국내 최대 음원 스트리밍", "MUSIC", 10900, 1, "/images/melon.png"),
createSubscription("스포티파이", "전세계 음악 스트리밍", "MUSIC", 10900, 6, "/images/spotify.png"),
createSubscription("유튜브 뮤직", "유튜브 음원 스트리밍", "MUSIC", 8900, 5, "/images/youtube-music.png")
),
"FOOD", Arrays.asList(
createSubscription("쿠팡이츠", "식품 정기배송", "FOOD", 4900, 1, "/images/coupang-eats.png"),
createSubscription("마켓컬리", "신선식품 새벽배송", "FOOD", 4900, 1, "/images/kurly.png"),
createSubscription("배민", "배달음식 구독", "FOOD", 5900, 1, "/images/baemin.png")
),
"LIFE", Arrays.asList(
createSubscription("당근", "중고거래 프리미엄", "LIFE", 3900, 1, "/images/karrot.png"),
createSubscription("쿠팡 로켓와우", "무료배송 구독", "LIFE", 4900, 4, "/images/coupang.png"),
createSubscription("밀리의 서재", "전자책 구독", "LIFE", 9900, 1, "/images/millie.png")
),
"BEAUTY", Arrays.asList(
createSubscription("올리브영", "뷰티 정기구독", "BEAUTY", 15900, 1, "/images/oliveyoung.png"),
createSubscription("시코르", "화장품 구독박스", "BEAUTY", 29900, 1, "/images/chicor.png"),
createSubscription("롭스", "뷰티 멤버십", "BEAUTY", 19900, 1, "/images/lohbs.png")
),
"EDU", Arrays.asList(
createSubscription("클래스101", "취미/교양 클래스", "EDU", 19900, 1, "/images/class101.png"),
createSubscription("탈잉", "원데이 클래스", "EDU", 29900, 1, "/images/taling.png"),
createSubscription("캐치", "IT 실무 교육", "EDU", 99000, 1, "/images/catch.png")
)
);
// 모든 서비스 저장
subscriptionsByCategory.values().stream()
.flatMap(List::stream)
.forEach(subscriptionRepository::save);
log.info("Sample subscriptions loaded");
}
private SubscriptionEntity createSubscription(String name, String description, String category,
int price, int maxSharedUsers, String logoUrl) {
return SubscriptionEntity.builder()
.name(name)
.description(description)
.category(category)
.price(price)
.maxSharedUsers(maxSharedUsers)
.logoUrl(logoUrl)
.build();
}
}
@@ -24,7 +24,7 @@ import java.util.List;
public class SecurityConfig {
protected final JwtTokenProvider jwtTokenProvider;
@Value("${allowedorigins}")
@Value("${allowed-origins}")
private String allowedOrigins;
public SecurityConfig(JwtTokenProvider jwtTokenProvider) {
@@ -25,7 +25,7 @@ import java.util.stream.Collectors;
public class JwtTokenProvider {
private final Algorithm algorithm;
public JwtTokenProvider(@Value("${jwt.secret}") String secretKey) {
public JwtTokenProvider(@Value("${jwt.secret-key}") String secretKey) {
this.algorithm = Algorithm.HMAC512(secretKey);
}
@@ -5,6 +5,7 @@ import com.unicorn.lifesub.mysub.biz.dto.CategoryResponse;
import com.unicorn.lifesub.mysub.biz.dto.ServiceListResponse;
import com.unicorn.lifesub.mysub.biz.usecase.in.CategoryUseCase;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
@@ -17,6 +18,7 @@ import java.util.List;
@Tag(name = "구독 카테고리 API", description = "구독 카테고리 관련 API")
@RestController
@SecurityRequirement(name = "bearerAuth") //이 어노테이션이 없으면 요청 헤더에 Authorization헤더가 안 생김
@RequestMapping("/api/mysub")
@RequiredArgsConstructor
public class CategoryController {
@@ -6,6 +6,7 @@ import com.unicorn.lifesub.mysub.biz.dto.TotalFeeResponse;
import com.unicorn.lifesub.mysub.biz.usecase.in.MySubscriptionsUseCase;
import com.unicorn.lifesub.mysub.biz.usecase.in.TotalFeeUseCase;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
@@ -15,6 +16,7 @@ import java.util.List;
@Tag(name = "마이구독 API", description = "마이구독 관련 API")
@RestController
@SecurityRequirement(name = "bearerAuth") //이 어노테이션이 없으면 요청 헤더에 Authorization헤더가 안 생김
@RequestMapping("/api/mysub")
@RequiredArgsConstructor
public class MySubController {
@@ -6,6 +6,7 @@ import com.unicorn.lifesub.mysub.biz.usecase.in.CancelSubscriptionUseCase;
import com.unicorn.lifesub.mysub.biz.usecase.in.SubscribeUseCase;
import com.unicorn.lifesub.mysub.biz.usecase.in.SubscriptionDetailUseCase;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
@@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.*;
@Tag(name = "구독 서비스 API", description = "구독 서비스 관련 API")
@RestController
@SecurityRequirement(name = "bearerAuth") //이 어노테이션이 없으면 요청 헤더에 Authorization헤더가 안 생김
@RequestMapping("/api/mysub/services")
@RequiredArgsConstructor
public class ServiceController {
@@ -1,14 +1,14 @@
package com.unicorn.lifesub.mysub.infra.adapter;
package com.unicorn.lifesub.mysub.infra.gateway;
import com.unicorn.lifesub.common.exception.BusinessException;
import com.unicorn.lifesub.common.exception.ErrorCode;
import com.unicorn.lifesub.mysub.biz.domain.MySubscription;
import com.unicorn.lifesub.mysub.biz.usecase.out.MySubscriptionReader;
import com.unicorn.lifesub.mysub.biz.usecase.out.MySubscriptionWriter;
import com.unicorn.lifesub.mysub.infra.entity.MySubscriptionEntity;
import com.unicorn.lifesub.mysub.infra.entity.SubscriptionEntity;
import com.unicorn.lifesub.mysub.infra.repository.MySubscriptionJpaRepository;
import com.unicorn.lifesub.mysub.infra.repository.SubscriptionJpaRepository;
import com.unicorn.lifesub.mysub.infra.gateway.entity.MySubscriptionEntity;
import com.unicorn.lifesub.mysub.infra.gateway.entity.SubscriptionEntity;
import com.unicorn.lifesub.mysub.infra.gateway.repository.MySubscriptionJpaRepository;
import com.unicorn.lifesub.mysub.infra.gateway.repository.SubscriptionJpaRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@@ -18,7 +18,7 @@ import java.util.stream.Collectors;
@Component
@RequiredArgsConstructor
public class MySubscriptionAdapter implements MySubscriptionReader, MySubscriptionWriter {
public class MySubscriptionGateway implements MySubscriptionReader, MySubscriptionWriter {
private final MySubscriptionJpaRepository mySubscriptionRepository;
private final SubscriptionJpaRepository subscriptionRepository;
@@ -1,14 +1,12 @@
package com.unicorn.lifesub.mysub.infra.adapter;
package com.unicorn.lifesub.mysub.infra.gateway;
import com.unicorn.lifesub.common.exception.BusinessException;
import com.unicorn.lifesub.common.exception.ErrorCode;
import com.unicorn.lifesub.mysub.biz.domain.Subscription;
import com.unicorn.lifesub.mysub.biz.domain.Category;
import com.unicorn.lifesub.mysub.biz.usecase.out.SubscriptionReader;
import com.unicorn.lifesub.mysub.infra.entity.SubscriptionEntity;
import com.unicorn.lifesub.mysub.infra.entity.CategoryEntity;
import com.unicorn.lifesub.mysub.infra.repository.SubscriptionJpaRepository;
import com.unicorn.lifesub.mysub.infra.repository.CategoryJpaRepository;
import com.unicorn.lifesub.mysub.infra.gateway.entity.SubscriptionEntity;
import com.unicorn.lifesub.mysub.infra.gateway.entity.CategoryEntity;
import com.unicorn.lifesub.mysub.infra.gateway.repository.SubscriptionJpaRepository;
import com.unicorn.lifesub.mysub.infra.gateway.repository.CategoryJpaRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@@ -18,7 +16,7 @@ import java.util.stream.Collectors;
@Component
@RequiredArgsConstructor
public class SubscriptionAdapter implements SubscriptionReader {
public class SubscriptionGateway implements SubscriptionReader {
private final SubscriptionJpaRepository subscriptionRepository;
private final CategoryJpaRepository categoryRepository;
@@ -1,13 +1,13 @@
package com.unicorn.lifesub.mysub.infra.entity;
package com.unicorn.lifesub.mysub.infra.gateway.entity;
import com.unicorn.lifesub.common.entity.BaseTimeEntity;
import com.unicorn.lifesub.mysub.biz.domain.Category;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.persistence.Id;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
@Entity
@Table(name = "categories")
@@ -1,4 +1,4 @@
package com.unicorn.lifesub.mysub.infra.entity;
package com.unicorn.lifesub.mysub.infra.gateway.entity;
import com.unicorn.lifesub.mysub.biz.domain.MySubscription;
import jakarta.persistence.*;
@@ -1,4 +1,4 @@
package com.unicorn.lifesub.mysub.infra.entity;
package com.unicorn.lifesub.mysub.infra.gateway.entity;
import com.unicorn.lifesub.common.entity.BaseTimeEntity;
import com.unicorn.lifesub.mysub.biz.domain.Subscription;
@@ -1,6 +1,6 @@
package com.unicorn.lifesub.mysub.infra.repository;
package com.unicorn.lifesub.mysub.infra.gateway.repository;
import com.unicorn.lifesub.mysub.infra.entity.CategoryEntity;
import com.unicorn.lifesub.mysub.infra.gateway.entity.CategoryEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CategoryJpaRepository extends JpaRepository<CategoryEntity, String> {
@@ -1,6 +1,6 @@
package com.unicorn.lifesub.mysub.infra.repository;
package com.unicorn.lifesub.mysub.infra.gateway.repository;
import com.unicorn.lifesub.mysub.infra.entity.MySubscriptionEntity;
import com.unicorn.lifesub.mysub.infra.gateway.entity.MySubscriptionEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
@@ -1,6 +1,6 @@
package com.unicorn.lifesub.mysub.infra.repository;
package com.unicorn.lifesub.mysub.infra.gateway.repository;
import com.unicorn.lifesub.mysub.infra.entity.SubscriptionEntity;
import com.unicorn.lifesub.mysub.infra.gateway.entity.SubscriptionEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
+11 -6
View File
@@ -9,21 +9,26 @@ spring:
username: ${POSTGRES_USER:admin}
password: ${POSTGRES_PASSWORD:Passw0rd}
driver-class-name: org.postgresql.Driver
# JPA 설정
jpa:
hibernate:
ddl-auto: ${JPA_DDL_AUTO:update}
show-sql: ${JPA_SHOW_SQL:false}
show-sql: ${JPA_SHOW_SQL:true}
properties:
hibernate:
format_sql: true
allowedorigins: ${ALLOWED_ORIGINS:*}
dialect: org.hibernate.dialect.PostgreSQLDialect
jwt:
secret: ${JWT_SECRET:8O2HQ13etL2BWZvYOiWsJ5uWFoLi6NBUG8divYVoCgtHVvlk3dqRksMl16toztDUeBTSIuOOPvHIrYq11G2BwQ==}
secret-key: ${JWT_SECRET_KEY:8O2HQ13etL2BWZvYOiWsJ5uWFoLi6NBUG8divYVoCgtHVvlk3dqRksMl16toztDUeBTSIuOOPvHIrYq11G2BwQ}
allowed-origins: ${ALLOWED_ORIGINS:*}
springdoc:
swagger-ui:
path: /swagger-ui.html
api-docs:
path: /api-docs
logging:
level:
com.unicorn: DEBUG
org.hibernate.SQL: TRACE