@startuml
!theme mono

title User Service 클래스 다이어그램 (상세)

' ====================
' 공통 컴포넌트 (참조)
' ====================
package "com.kt.event.common" <<rectangle>> {
    abstract class BaseTimeEntity {
        - createdAt: LocalDateTime
        - updatedAt: LocalDateTime
    }

    interface ErrorCode {
        + getCode(): String
        + getMessage(): String
    }

    class BusinessException extends RuntimeException {
        - errorCode: ErrorCode
        + getErrorCode(): ErrorCode
    }

    interface JwtTokenProvider {
        + createAccessToken(): String
        + validateToken(): boolean
        + getExpirationFromToken(): Date
    }
}

package "com.kt.event.user" {

    ' ====================
    ' Presentation Layer
    ' ====================
    package "controller" {
        class UserController {
            - userService: UserService
            - authenticationService: AuthenticationService

            ' UFR-USER-010: 회원가입
            + register(request: RegisterRequest): ResponseEntity<RegisterResponse>

            ' UFR-USER-020: 로그인
            + login(request: LoginRequest): ResponseEntity<LoginResponse>

            ' UFR-USER-040: 로그아웃
            + logout(authHeader: String): ResponseEntity<LogoutResponse>

            ' UFR-USER-030: 프로필 관리
            + getProfile(principal: UserPrincipal): ResponseEntity<ProfileResponse>
            + updateProfile(principal: UserPrincipal, request: UpdateProfileRequest): ResponseEntity<ProfileResponse>
            + changePassword(principal: UserPrincipal, request: ChangePasswordRequest): ResponseEntity<Void>
        }
    }

    ' ====================
    ' Business Layer (Service)
    ' ====================
    package "service" {
        interface UserService {
            + register(request: RegisterRequest): RegisterResponse
            + getProfile(userId: UUID): ProfileResponse
            + updateProfile(userId: UUID, request: UpdateProfileRequest): ProfileResponse
            + changePassword(userId: UUID, request: ChangePasswordRequest): void
            + updateLastLoginAt(userId: UUID): void
        }

        interface AuthenticationService {
            + login(request: LoginRequest): LoginResponse
            + logout(token: String): LogoutResponse
        }
    }

    package "service.impl" {
        class UserServiceImpl implements UserService {
            - userRepository: UserRepository
            - storeRepository: StoreRepository
            - passwordEncoder: PasswordEncoder
            - jwtTokenProvider: JwtTokenProvider
            - redisTemplate: RedisTemplate<String, Object>

            ' UFR-USER-010: 회원가입
            + register(request: RegisterRequest): RegisterResponse

            ' UFR-USER-030: 프로필 관리
            + getProfile(userId: UUID): ProfileResponse
            + updateProfile(userId: UUID, request: UpdateProfileRequest): ProfileResponse
            + changePassword(userId: UUID, request: ChangePasswordRequest): void

            ' UFR-USER-020: 로그인 시각 업데이트
            + updateLastLoginAt(userId: UUID): void

            ' 내부 메소드
            - saveSession(token: String, userId: UUID, role: String): void
        }

        class AuthenticationServiceImpl implements AuthenticationService {
            - userRepository: UserRepository
            - storeRepository: StoreRepository
            - passwordEncoder: PasswordEncoder
            - jwtTokenProvider: JwtTokenProvider
            - userService: UserService
            - redisTemplate: RedisTemplate<String, Object>

            ' UFR-USER-020: 로그인
            + login(request: LoginRequest): LoginResponse

            ' UFR-USER-040: 로그아웃
            + logout(token: String): LogoutResponse

            ' 내부 메소드
            - saveSession(token: String, userId: UUID, role: String): void
        }
    }

    ' ====================
    ' Data Access Layer
    ' ====================
    package "repository" {
        interface UserRepository extends JpaRepository {
            + findByEmail(email: String): Optional<User>
            + findByPhoneNumber(phoneNumber: String): Optional<User>
            + existsByEmail(email: String): boolean
            + existsByPhoneNumber(phoneNumber: String): boolean
            + updateLastLoginAt(userId: UUID, lastLoginAt: LocalDateTime): void
        }

        interface StoreRepository extends JpaRepository {
            + findByUserId(userId: UUID): Optional<Store>
        }
    }

    ' ====================
    ' Domain Layer
    ' ====================
    package "entity" {
        class User extends BaseTimeEntity {
            - id: UUID
            - name: String
            - phoneNumber: String
            - email: String
            - passwordHash: String
            - role: UserRole
            - status: UserStatus
            - lastLoginAt: LocalDateTime
            - store: Store

            ' 비즈니스 로직
            + updateLastLoginAt(): void
            + changePassword(newPasswordHash: String): void
            + updateProfile(name: String, email: String, phoneNumber: String): void
            + setStore(store: Store): void
        }

        enum UserRole {
            OWNER
            ADMIN
        }

        enum UserStatus {
            ACTIVE
            INACTIVE
            LOCKED
            WITHDRAWN
        }

        class Store extends BaseTimeEntity {
            - id: UUID
            - name: String
            - industry: String
            - address: String
            - businessHours: String
            - user: User

            ' 비즈니스 로직
            + updateInfo(name: String, industry: String, address: String, businessHours: String): void
            ~ setUser(user: User): void
        }
    }

    ' ====================
    ' DTO Layer
    ' ====================
    package "dto.request" {
        class RegisterRequest {
            - name: String
            - phoneNumber: String
            - email: String
            - password: String
            - storeName: String
            - industry: String
            - address: String
            - businessHours: String
        }

        class LoginRequest {
            - email: String
            - password: String
        }

        class UpdateProfileRequest {
            - name: String
            - email: String
            - phoneNumber: String
            - storeName: String
            - industry: String
            - address: String
            - businessHours: String
        }

        class ChangePasswordRequest {
            - currentPassword: String
            - newPassword: String
        }
    }

    package "dto.response" {
        class RegisterResponse {
            - token: String
            - userId: UUID
            - userName: String
            - storeId: UUID
            - storeName: String
        }

        class LoginResponse {
            - token: String
            - userId: UUID
            - userName: String
            - role: String
            - email: String
        }

        class LogoutResponse {
            - success: boolean
            - message: String
        }

        class ProfileResponse {
            - userId: UUID
            - userName: String
            - phoneNumber: String
            - email: String
            - role: String
            - storeId: UUID
            - storeName: String
            - industry: String
            - address: String
            - businessHours: String
            - createdAt: LocalDateTime
            - lastLoginAt: LocalDateTime
        }
    }

    ' ====================
    ' Exception Layer
    ' ====================
    package "exception" {
        enum UserErrorCode {
            USER_DUPLICATE_EMAIL
            USER_DUPLICATE_PHONE
            USER_NOT_FOUND
            AUTH_FAILED
            AUTH_INVALID_TOKEN
            AUTH_TOKEN_EXPIRED
            AUTH_UNAUTHORIZED
            PWD_INVALID_CURRENT
            PWD_SAME_AS_CURRENT

            - errorCode: ErrorCode
            + getCode(): String
            + getMessage(): String
        }
    }

    ' ====================
    ' Configuration Layer
    ' ====================
    package "config" {
        class SecurityConfig {
            - jwtTokenProvider: JwtTokenProvider
            - allowedOrigins: String

            + filterChain(http: HttpSecurity): SecurityFilterChain
            + corsConfigurationSource(): CorsConfigurationSource
            + passwordEncoder(): PasswordEncoder
        }

        class RedisConfig {
            - redisHost: String
            - redisPort: int

            + redisConnectionFactory(): RedisConnectionFactory
            + redisTemplate(): RedisTemplate<String, Object>
        }

        class AsyncConfig {
            + taskExecutor(): Executor
        }

        class SwaggerConfig {
            + customOpenAPI(): OpenAPI
        }
    }
}

' ====================
' Layer 간 의존성 관계
' ====================

' Controller → Service
UserController --> UserService : uses
UserController --> AuthenticationService : uses

' Service → Repository
UserServiceImpl --> UserRepository : uses
UserServiceImpl --> StoreRepository : uses
AuthenticationServiceImpl --> UserRepository : uses
AuthenticationServiceImpl --> StoreRepository : uses
AuthenticationServiceImpl --> UserService : uses

' Service → Entity (도메인 로직 호출)
UserServiceImpl ..> User : creates/updates
UserServiceImpl ..> Store : creates/updates
AuthenticationServiceImpl ..> User : reads

' Repository → Entity
UserRepository --> User : manages
StoreRepository --> Store : manages

' Service → DTO
UserServiceImpl ..> RegisterRequest : receives
UserServiceImpl ..> UpdateProfileRequest : receives
UserServiceImpl ..> ChangePasswordRequest : receives
UserServiceImpl ..> RegisterResponse : returns
UserServiceImpl ..> ProfileResponse : returns
AuthenticationServiceImpl ..> LoginRequest : receives
AuthenticationServiceImpl ..> LoginResponse : returns
AuthenticationServiceImpl ..> LogoutResponse : returns

' Controller → DTO
UserController ..> RegisterRequest : receives
UserController ..> LoginRequest : receives
UserController ..> UpdateProfileRequest : receives
UserController ..> ChangePasswordRequest : receives
UserController ..> RegisterResponse : returns
UserController ..> LoginResponse : returns
UserController ..> LogoutResponse : returns
UserController ..> ProfileResponse : returns

' Entity 관계
User "1" -- "0..1" Store : has >
User +-- UserRole
User +-- UserStatus

' Exception
UserServiceImpl ..> UserErrorCode : throws
AuthenticationServiceImpl ..> UserErrorCode : throws
UserErrorCode --> ErrorCode : wraps

' Configuration
SecurityConfig --> JwtTokenProvider : uses
SecurityConfig ..> PasswordEncoder : creates
UserServiceImpl --> PasswordEncoder : uses
AuthenticationServiceImpl --> PasswordEncoder : uses

' Common 컴포넌트 사용
User --|> BaseTimeEntity
Store --|> BaseTimeEntity
UserServiceImpl ..> JwtTokenProvider : uses
AuthenticationServiceImpl ..> JwtTokenProvider : uses
UserServiceImpl ..> BusinessException : throws
AuthenticationServiceImpl ..> BusinessException : throws

' Notes
note top of UserController
  <b>Presentation Layer</b>
  - REST API 엔드포인트 제공
  - 요청/응답 DTO 변환
  - 인증 정보 추출 (UserPrincipal)
  - Swagger 문서화
end note

note top of UserService
  <b>Business Layer</b>
  - 비즈니스 로직 처리
  - 트랜잭션 관리
  - 도메인 객체 조작
  - 검증 및 예외 처리
end note

note top of UserRepository
  <b>Data Access Layer</b>
  - JPA 기반 데이터 액세스
  - CRUD 및 커스텀 쿼리
  - 트랜잭션 경계
end note

note top of User
  <b>Domain Layer</b>
  - 핵심 비즈니스 엔티티
  - 도메인 로직 포함
  - 불변성 및 일관성 보장
end note

note right of UserServiceImpl
  <b>핵심 기능</b>

  1. 회원가입 (register)
     - 중복 검증 (이메일, 전화번호)
     - 비밀번호 해싱
     - User/Store 생성
     - JWT 토큰 발급
     - Redis 세션 저장

  2. 프로필 관리
     - 프로필 조회/수정
     - 비밀번호 변경 (현재 비밀번호 검증)

  3. 로그인 시각 업데이트
     - 비동기 처리 (@Async)
end note

note right of AuthenticationServiceImpl
  <b>핵심 기능</b>

  1. 로그인 (login)
     - 이메일/비밀번호 검증
     - JWT 토큰 발급
     - Redis 세션 저장
     - 최종 로그인 시각 업데이트

  2. 로그아웃 (logout)
     - JWT 토큰 검증
     - Redis 세션 삭제
     - JWT Blacklist 추가
end note

note bottom of User
  <b>User-Store 관계</b>

  - OneToOne 양방향 관계
  - User가 Store를 소유
  - Cascade ALL, Orphan Removal
  - Lazy Loading
end note

@enduml
