mirror of
https://github.com/cna-bootcamp/lifesub.git
synced 2026-06-12 20:49:09 +00:00
release
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user