This commit is contained in:
ondal
2025-02-13 18:42:46 +09:00
parent ba3405bff3
commit d7ca5994b4
48 changed files with 1071 additions and 7 deletions
+18
View File
@@ -0,0 +1,18 @@
서비스명|마이구독|마이구독|마이구독|마이구독|마이구독|마이구독|마이구독
마이크로서비스 이름|MySubscription|MySubscription|MySubscription|MySubscription|MySubscription|MySubscription|MySubscription
유저스토리 ID|MSS-005|MSS-010|MSS-020|MSS-025|MSS-030|MSS-035|MSS-040
유저스토리 제목|총 구독료 표시|나의 구독 목록|구독상세|구독하기|구독취소|구독 카테고리 표시|구독서비스 목록
Controller 이름|MySubController|MySubController|ServiceController|ServiceController|ServiceController|CategoryController|CategoryController
API 목적|총 구독료 조회|구독 목록 조회|구독 상세 조회|구독 신청|구독 취소|전체 카테고리 목록 조회|카테고리별 서비스 목록 조회
API Method|GET|GET|GET|POST|DELETE|GET|GET
API 그룹 Path|/api/mysub|/api/mysub|/api/mysub/services|/api/mysub/services|/api/mysub/services|/api/mysub|/api/mysub
API Path|/total-fee|/list|/{subscriptionId}|/{subscriptionId}/subscribe|/{subscriptionId}|/categories|/services
Path <변수유형> <변수명>|||Long subscriptionId|Long subscriptionId|Long subscriptionId||
Query Key|userId|userId||userId|||categoryId
Query <변수유형> <변수명>|String userId|String userId||String userId|||String categoryId
Request DTO 이름|||||||
Request DTO 배열 여부|||||||
Request DTO 구조|||||||
Response DTO 이름|TotalFeeResponse|MySubResponse|SubDetailResponse||null|CategoryResponse|ServiceListResponse
Response DTO 배열 여부|No|Yes|No||No|Yes|Yes
Response DTO 구조|Long totalFee; String feeLevel|String serviceName; String logoUrl|String serviceName; String logoUrl; String category; String description; int price; int maxSharedUsers||null|String categoryId; String categoryName|String serviceId; String serviceName; String description; int price; String logoUrl
+52
View File
@@ -0,0 +1,52 @@
!theme mono
title MySubscription Service - 데이터 모델
' Style configurations
skinparam linetype ortho
hide circle
entity "My_Subscriptions" as my_subs {
* id: bigint <<PK>>
--
userId: varchar(50)
subscriptionId: bigint <<FK>>
createdAt: timestamp
updatedAt: timestamp
}
entity "Subscriptions" as subs {
* id: bigint <<PK>>
--
name: varchar(100)
description: text
category: varchar(50)
price: decimal(15,2)
maxSharedUsers: integer
logoUrl: varchar(255)
createdAt: timestamp
updatedAt: timestamp
}
entity "Categories" as categories {
* categoryId: varchar(50) <<PK>>
--
name: varchar(100)
createdAt: timestamp
updatedAt: timestamp
}
' Relationships
my_subs }o--|| subs : subscriptionId
subs }o--|| categories : category-categoryId
note right of my_subs
사용자별 구독 정보 관리
end note
note right of subs
구독 서비스 정보 관리
end note
note right of categories
구독 카테고리 관리
end note
+101
View File
@@ -0,0 +1,101 @@
!theme mono
title 마이구독 서비스 - 내부 시퀀스 다이어그램
actor Client
participant "마이구독 컨트롤러\n(MySubController)" as MySubController
participant "서비스 컨트롤러\n(ServiceController)" as ServiceController
participant "카테고리 컨트롤러\n(CategoryController)" as CategoryController
participant "마이구독 서비스\n(MySubService)" as Service
database "마이구독 DB" as MySubDB
database "구독서비스 DB" as SubServiceDB
' 총 구독료 조회
Client -> MySubController: GET /api/mysub/total-fee\n[총 구독료 조회]
activate MySubController
MySubController -> Service: getTotalFee(userId)
activate Service
Service -> MySubDB: findMySubscriptions(userId)
MySubDB --> Service: List<MySubscription>
Service -> SubServiceDB: findSubscriptionsByIds(subIds)
SubServiceDB --> Service: List<Subscription>
Service --> MySubController: TotalFeeResponse
MySubController --> Client: HTTP Response\n(total fee, level)
deactivate Service
deactivate MySubController
' 나의 구독 목록 조회
Client -> MySubController: GET /api/mysub/list\n[구독 목록 조회]
activate MySubController
MySubController -> Service: getMySubscriptions(userId)
activate Service
Service -> MySubDB: findMySubscriptions(userId)
MySubDB --> Service: List<MySubscription>
Service --> MySubController: List<MySubResponse>
MySubController --> Client: HTTP Response\n(subscription list)
deactivate Service
deactivate MySubController
' 구독 상세 조회
Client -> ServiceController: GET /api/mysub/services/{subscriptionId}\n[구독 상세 조회]
activate ServiceController
ServiceController -> Service: getSubscriptionDetail(subscriptionId)
activate Service
Service -> SubServiceDB: findById(subscriptionId)
SubServiceDB --> Service: Subscription
Service --> ServiceController: SubDetailResponse
ServiceController --> Client: HTTP Response\n(subscription detail)
deactivate Service
deactivate ServiceController
' 구독 신청
Client -> ServiceController: POST /api/mysub/services/{subscriptionId}/subscribe\n[구독 신청]
activate ServiceController
ServiceController -> Service: subscribe(subscriptionId, userId)
activate Service
Service -> SubServiceDB: findById(subscriptionId)
Service -> MySubDB: save(mySubscription)
Service --> ServiceController: void
ServiceController --> Client: HTTP Response\n(success)
deactivate Service
deactivate ServiceController
' 구독 취소
Client -> ServiceController: DELETE /api/mysub/services/{subscriptionId}\n[구독 취소]
activate ServiceController
ServiceController -> Service: cancel(subscriptionId)
activate Service
Service -> MySubDB: delete(subscriptionId)
Service --> ServiceController: void
ServiceController --> Client: HTTP Response\n(success)
deactivate Service
deactivate ServiceController
' 카테고리 목록 조회
Client -> CategoryController: GET /api/mysub/categories\n[카테고리 목록 조회]
activate CategoryController
CategoryController -> Service: getAllCategories()
activate Service
Service -> SubServiceDB: findAllCategories()
SubServiceDB --> Service: List<Category>
Service --> CategoryController: List<CategoryResponse>
CategoryController --> Client: HTTP Response\n(category list)
deactivate Service
deactivate CategoryController
' 카테고리별 서비스 목록 조회
Client -> CategoryController: GET /api/mysub/services\n[카테고리별 서비스 목록 조회]
activate CategoryController
CategoryController -> Service: getServicesByCategory(categoryId)
activate Service
Service -> SubServiceDB: findByCategory(categoryId)
SubServiceDB --> Service: List<Subscription>
Service --> CategoryController: List<ServiceListResponse>
CategoryController --> Client: HTTP Response\n(service list)
deactivate Service
deactivate CategoryController
note right of Service
1. 비즈니스 로직 처리
2. 구독 관리
3. 카테고리 관리
end note
+260
View File
@@ -0,0 +1,260 @@
!theme mono
title MySubscription Service - Clean Architecture Class Diagram
' BIZ Layer
package "com.unicorn.lifesub.mysub.biz" {
package "usecase" {
package "in" {
interface TotalFeeUseCase {
+getTotalFee(userId: String): TotalFeeResponse
}
interface MySubscriptionsUseCase {
+getMySubscriptions(userId: String): List<MySubResponse>
}
interface SubscriptionDetailUseCase {
+getSubscriptionDetail(subscriptionId: Long): SubDetailResponse
}
interface SubscribeUseCase {
+subscribe(subscriptionId: Long, userId: String): void
}
interface CancelSubscriptionUseCase {
+cancel(subscriptionId: Long): void
}
interface CategoryUseCase {
+getAllCategories(): List<CategoryResponse>
+getServicesByCategory(categoryId: String): List<ServiceListResponse>
}
}
package "out" {
interface MySubscriptionReader {
+findByUserId(userId: String): List<MySubscription>
+findById(id: Long): Optional<MySubscription>
}
interface MySubscriptionWriter {
+save(userId: String, subscriptionId: Long): MySubscription
+delete(id: Long): void
}
interface SubscriptionReader {
+findById(id: Long): Optional<Subscription>
+findByCategory(category: String): List<Subscription>
+findAllCategories(): List<Category>
}
}
}
package "domain" {
class MySubscription {
-id: Long
-userId: String
-subscription: Subscription
+getPrice(): int
}
class Subscription {
-id: Long
-name: String
-description: String
-category: String
-price: int
-maxSharedUsers: int
-logoUrl: String
}
class Category {
-categoryId: String
-name: String
}
}
package "service" {
class MySubscriptionService {
-mySubscriptionReader: MySubscriptionReader
-mySubscriptionWriter: MySubscriptionWriter
-subscriptionReader: SubscriptionReader
-collectorThreshold: long
-addictThreshold: long
+getTotalFee(userId: String): TotalFeeResponse
+getMySubscriptions(userId: String): List<MySubResponse>
+getSubscriptionDetail(subscriptionId: Long): SubDetailResponse
+subscribe(subscriptionId: Long, userId: String): void
+cancel(subscriptionId: Long): void
+getAllCategories(): List<CategoryResponse>
+getServicesByCategory(categoryId: String): List<ServiceListResponse>
-calculateFeeLevel(totalFee: long): String
}
enum FeeLevel {
LIKFER
COLLECTOR
ADDICT
}
}
package "dto" {
class TotalFeeResponse {
-totalFee: Long
-feeLevel: String
}
class MySubResponse {
-id: Long
-serviceName: String
-logoUrl: String
}
class SubDetailResponse {
-serviceName: String
-logoUrl: String
-category: String
-description: String
-price: int
-maxSharedUsers: int
}
class CategoryResponse {
-categoryId: String
-categoryName: String
}
class ServiceListResponse {
-serviceId: String
-serviceName: String
-description: String
-price: int
-logoUrl: String
}
}
}
' INFRA Layer
package "com.unicorn.lifesub.mysub.infra" {
package "controller" {
class MySubController {
-totalFeeUseCase: TotalFeeUseCase
-mySubscriptionsUseCase: MySubscriptionsUseCase
+getTotalFee(userId: String): ResponseEntity<ApiResponse<TotalFeeResponse>>
+getMySubscriptions(userId: String): ResponseEntity<ApiResponse<List<MySubResponse>>>
}
class ServiceController {
-subscriptionDetailUseCase: SubscriptionDetailUseCase
-subscribeUseCase: SubscribeUseCase
-cancelSubscriptionUseCase: CancelSubscriptionUseCase
+getSubscriptionDetail(subscriptionId: Long): ResponseEntity<ApiResponse<SubDetailResponse>>
+subscribe(subscriptionId: Long, userId: String): ResponseEntity<ApiResponse<Void>>
+cancel(subscriptionId: Long): ResponseEntity<ApiResponse<Void>>
}
class CategoryController {
-categoryUseCase: CategoryUseCase
+getAllCategories(): ResponseEntity<ApiResponse<List<CategoryResponse>>>
+getServicesByCategory(categoryId: String): ResponseEntity<ApiResponse<List<ServiceListResponse>>>
}
}
package "gateway" {
class MySubscriptionGateway implements MySubscriptionReader, MySubscriptionWriter {
-mySubscriptionRepository: MySubscriptionJpaRepository
-subscriptionRepository: SubscriptionJpaRepository
}
class SubscriptionGateway implements SubscriptionReader {
-subscriptionRepository: SubscriptionJpaRepository
-categoryRepository: CategoryJpaRepository
}
package "repository" {
interface MySubscriptionJpaRepository {
+findByUserId(userId: String): List<MySubscriptionEntity>
+findBySubscription_Id(subscriptionId: Long): Optional<MySubscriptionEntity>
}
interface SubscriptionJpaRepository {
+findById(id: Long): Optional<SubscriptionEntity>
+findByCategory(category: String): List<SubscriptionEntity>
}
interface CategoryJpaRepository {
+findAll(): List<CategoryEntity>
}
}
package "entity" {
class MySubscriptionEntity {
-id: Long
-userId: String
-subscription: SubscriptionEntity
+toDomain(): MySubscription
}
class SubscriptionEntity {
-id: Long
-name: String
-description: String
-category: String
-price: int
-maxSharedUsers: int
-logoUrl: String
+toDomain(): Subscription
}
class CategoryEntity {
-categoryId: String
-name: String
+toDomain(): Category
}
}
}
package "config" {
class SecurityConfig {
-jwtTokenProvider: JwtTokenProvider
+securityFilterChain(http: HttpSecurity): SecurityFilterChain
+corsConfigurationSource(): CorsConfigurationSource
}
class SwaggerConfig {
+openAPI(): OpenAPI
}
class DataLoader {
-categoryRepository: CategoryJpaRepository
-subscriptionRepository: SubscriptionJpaRepository
+run(): void
}
}
}
' Relationships
MySubscriptionService ..|> TotalFeeUseCase
MySubscriptionService ..|> MySubscriptionsUseCase
MySubscriptionService ..|> SubscriptionDetailUseCase
MySubscriptionService ..|> SubscribeUseCase
MySubscriptionService ..|> CancelSubscriptionUseCase
MySubscriptionService ..|> CategoryUseCase
MySubController --> TotalFeeUseCase
MySubController --> MySubscriptionsUseCase
ServiceController --> SubscriptionDetailUseCase
ServiceController --> SubscribeUseCase
ServiceController --> CancelSubscriptionUseCase
CategoryController --> CategoryUseCase
MySubscriptionGateway --> MySubscriptionJpaRepository
MySubscriptionGateway --> SubscriptionJpaRepository
SubscriptionGateway --> SubscriptionJpaRepository
SubscriptionGateway --> CategoryJpaRepository
MySubscriptionEntity ..> MySubscription : converts to
SubscriptionEntity ..> Subscription : converts to
CategoryEntity ..> Category : converts to
MySubscription --> Subscription