Showing preview only (1,985K chars total). Download the full file or copy to clipboard to get everything.
Repository: Gosrock/DuDoong-Backend
Branch: dev
Commit: 1d7d7134fb4a
Files: 1032
Total size: 1.5 MB
Directory structure:
gitextract_57vblqu2/
├── .claude/
│ └── settings.local.json
├── .github/
│ ├── CODEOWNERS
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── BuildApiServer.yml
│ ├── BuildBatchServer.yml
│ ├── BuildSocketServer.yml
│ └── ci.yml
├── .gitignore
├── CLAUDE.md
├── DuDoong-Admin/
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── kotlin/
│ │ └── band/
│ │ └── gosrock/
│ │ └── admin/
│ │ ├── controller/
│ │ │ ├── AdminCommentController.kt
│ │ │ ├── AdminDashboardController.kt
│ │ │ ├── AdminEventController.kt
│ │ │ ├── AdminHostController.kt
│ │ │ ├── AdminOrderController.kt
│ │ │ ├── AdminRefundController.kt
│ │ │ └── AdminUserController.kt
│ │ ├── exception/
│ │ │ ├── AdminErrorCode.kt
│ │ │ └── AdminForbiddenException.kt
│ │ ├── model/
│ │ │ └── dto/
│ │ │ ├── request/
│ │ │ │ ├── AdminAddHostMemberRequest.kt
│ │ │ │ ├── AdminAdjustTicketStockRequest.kt
│ │ │ │ ├── AdminCancelOrderRequest.kt
│ │ │ │ ├── AdminChangeNameRequest.kt
│ │ │ │ ├── AdminRefundStatusRequest.kt
│ │ │ │ ├── AdminTransferMasterRequest.kt
│ │ │ │ ├── AdminUpdateEventRequest.kt
│ │ │ │ ├── AdminUpdateEventStatusRequest.kt
│ │ │ │ ├── AdminUpdateHostMemberRoleRequest.kt
│ │ │ │ ├── AdminUpdateHostPartnerRequest.kt
│ │ │ │ ├── AdminUpdateHostProfileRequest.kt
│ │ │ │ ├── AdminUpdateTicketItemRequest.kt
│ │ │ │ ├── AdminUpdateUserRoleRequest.kt
│ │ │ │ └── AdminUpdateUserStatusRequest.kt
│ │ │ └── response/
│ │ │ ├── AdminCommentResponse.kt
│ │ │ ├── AdminEventResponse.kt
│ │ │ ├── AdminHostDetailResponse.kt
│ │ │ ├── AdminHostMemberResponse.kt
│ │ │ ├── AdminHostResponse.kt
│ │ │ ├── AdminIssuedTicketResponse.kt
│ │ │ ├── AdminOrderResponse.kt
│ │ │ ├── AdminRefundResponse.kt
│ │ │ ├── AdminTicketItemResponse.kt
│ │ │ ├── AdminUserDetailResponse.kt
│ │ │ ├── AdminUserResponse.kt
│ │ │ └── DashboardResponse.kt
│ │ └── service/
│ │ ├── AdminAddHostMemberUseCase.kt
│ │ ├── AdminAdjustTicketStockUseCase.kt
│ │ ├── AdminAuthValidator.kt
│ │ ├── AdminCancelOrderUseCase.kt
│ │ ├── AdminChangeNameUseCase.kt
│ │ ├── AdminCompleteRefundUseCase.kt
│ │ ├── AdminDeleteCommentUseCase.kt
│ │ ├── AdminDeleteEventUseCase.kt
│ │ ├── AdminExcelService.kt
│ │ ├── AdminExportIssuedTicketsUseCase.kt
│ │ ├── AdminGetCommentsUseCase.kt
│ │ ├── AdminGetEventDetailUseCase.kt
│ │ ├── AdminGetEventsUseCase.kt
│ │ ├── AdminGetHostDetailUseCase.kt
│ │ ├── AdminGetHostEventsUseCase.kt
│ │ ├── AdminGetHostMembersUseCase.kt
│ │ ├── AdminGetHostsUseCase.kt
│ │ ├── AdminGetIssuedTicketsUseCase.kt
│ │ ├── AdminGetMeUseCase.kt
│ │ ├── AdminGetOrderDetailUseCase.kt
│ │ ├── AdminGetOrdersUseCase.kt
│ │ ├── AdminGetRefundDetailUseCase.kt
│ │ ├── AdminGetRefundsUseCase.kt
│ │ ├── AdminGetTicketItemsUseCase.kt
│ │ ├── AdminGetUserDetailUseCase.kt
│ │ ├── AdminGetUsersUseCase.kt
│ │ ├── AdminRemoveHostMemberUseCase.kt
│ │ ├── AdminTransferMasterUseCase.kt
│ │ ├── AdminUpdateEventStatusUseCase.kt
│ │ ├── AdminUpdateEventUseCase.kt
│ │ ├── AdminUpdateHostMemberRoleUseCase.kt
│ │ ├── AdminUpdateHostPartnerUseCase.kt
│ │ ├── AdminUpdateHostProfileUseCase.kt
│ │ ├── AdminUpdateRefundStatusUseCase.kt
│ │ ├── AdminUpdateTicketItemUseCase.kt
│ │ ├── AdminUpdateUserRoleUseCase.kt
│ │ ├── AdminUpdateUserStatusUseCase.kt
│ │ └── GetDashboardUseCase.kt
│ └── test/
│ └── kotlin/
│ └── band/
│ └── gosrock/
│ └── admin/
│ └── service/
│ ├── AdminAuthValidatorTest.kt
│ ├── AdminChangeNameUseCaseTest.kt
│ ├── AdminCompleteRefundUseCaseTest.kt
│ ├── AdminExcelServiceTest.kt
│ └── AdminUpdateRefundStatusUseCaseTest.kt
├── DuDoong-Api/
│ ├── Dockerfile
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── kotlin/
│ │ │ └── band/
│ │ │ └── gosrock/
│ │ │ ├── DuDoongApiServerApplication.kt
│ │ │ └── api/
│ │ │ ├── admin/
│ │ │ │ └── controller/
│ │ │ │ └── AdminAuthController.kt
│ │ │ ├── alimTalk/
│ │ │ │ ├── dto/
│ │ │ │ │ └── OrderAlimTalkDto.kt
│ │ │ │ ├── handler/
│ │ │ │ │ ├── DoneOrderEventAlimTalkHandler.kt
│ │ │ │ │ ├── RegisterUserEventAlimTalkHandler.kt
│ │ │ │ │ └── WithDrawOrderEventAlimTalkHandler.kt
│ │ │ │ └── service/
│ │ │ │ ├── SendDoneOrderAlimTalkService.kt
│ │ │ │ ├── SendRegisterAlimTalkService.kt
│ │ │ │ ├── SendWithdrawOrderAlimTalkService.kt
│ │ │ │ └── helper/
│ │ │ │ └── OrderAlimTalkInfoHelper.kt
│ │ │ ├── auth/
│ │ │ │ ├── controller/
│ │ │ │ │ └── AuthController.kt
│ │ │ │ ├── model/
│ │ │ │ │ └── dto/
│ │ │ │ │ ├── KakaoUserInfoDto.kt
│ │ │ │ │ ├── request/
│ │ │ │ │ │ └── RegisterRequest.kt
│ │ │ │ │ └── response/
│ │ │ │ │ ├── AvailableRegisterResponse.kt
│ │ │ │ │ ├── OauthLoginLinkResponse.kt
│ │ │ │ │ ├── OauthTokenResponse.kt
│ │ │ │ │ ├── OauthUserInfoResponse.kt
│ │ │ │ │ └── TokenAndUserResponse.kt
│ │ │ │ └── service/
│ │ │ │ ├── LocalDevLoginUseCase.kt
│ │ │ │ ├── LoginUseCase.kt
│ │ │ │ ├── LogoutUseCase.kt
│ │ │ │ ├── OauthUserInfoUseCase.kt
│ │ │ │ ├── RefreshUseCase.kt
│ │ │ │ ├── RegisterUseCase.kt
│ │ │ │ ├── WithDrawUseCase.kt
│ │ │ │ └── helper/
│ │ │ │ ├── CookieHelper.kt
│ │ │ │ ├── KakaoOauthHelper.kt
│ │ │ │ ├── OauthOIDCHelper.kt
│ │ │ │ └── TokenGenerateHelper.kt
│ │ │ ├── cart/
│ │ │ │ ├── controller/
│ │ │ │ │ └── CartController.kt
│ │ │ │ ├── docs/
│ │ │ │ │ └── CreateCartExceptionDocs.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ ├── request/
│ │ │ │ │ │ │ ├── AddCartLineDto.kt
│ │ │ │ │ │ │ ├── AddCartOptionAnswerDto.kt
│ │ │ │ │ │ │ └── AddCartRequest.kt
│ │ │ │ │ │ └── response/
│ │ │ │ │ │ ├── CartItemResponse.kt
│ │ │ │ │ │ └── CartResponse.kt
│ │ │ │ │ └── mapper/
│ │ │ │ │ └── CartMapper.kt
│ │ │ │ └── service/
│ │ │ │ ├── CheckOptionUseCase.kt
│ │ │ │ ├── CreateCartUseCase.kt
│ │ │ │ └── ReadCartUseCase.kt
│ │ │ ├── comment/
│ │ │ │ ├── controller/
│ │ │ │ │ └── CommentController.kt
│ │ │ │ ├── mapper/
│ │ │ │ │ └── CommentMapper.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── request/
│ │ │ │ │ │ └── CreateCommentRequest.kt
│ │ │ │ │ └── response/
│ │ │ │ │ ├── CreateCommentResponse.kt
│ │ │ │ │ ├── RetrieveCommentCountResponse.kt
│ │ │ │ │ ├── RetrieveCommentDTO.kt
│ │ │ │ │ ├── RetrieveCommentListResponse.kt
│ │ │ │ │ └── RetrieveRandomCommentResponse.kt
│ │ │ │ └── service/
│ │ │ │ ├── CreateCommentUseCase.kt
│ │ │ │ ├── DeleteCommentUseCase.kt
│ │ │ │ ├── RetrieveCommentCountUseCase.kt
│ │ │ │ ├── RetrieveCommentUseCase.kt
│ │ │ │ └── RetrieveRandomCommentUseCase.kt
│ │ │ ├── common/
│ │ │ │ ├── UserUtils.kt
│ │ │ │ ├── aop/
│ │ │ │ │ ├── hostPartner/
│ │ │ │ │ │ ├── HostPartnerAllowed.kt
│ │ │ │ │ │ ├── HostPartnerAop.kt
│ │ │ │ │ │ ├── HostPartnerCallTransaction.kt
│ │ │ │ │ │ ├── HostPartnerCallTransactionFactory.kt
│ │ │ │ │ │ ├── HostPartnerEventTransaction.kt
│ │ │ │ │ │ └── HostPartnerHostTransaction.kt
│ │ │ │ │ └── hostRole/
│ │ │ │ │ ├── FindHostFrom.kt
│ │ │ │ │ ├── HostCallTransactionFactory.kt
│ │ │ │ │ ├── HostQualification.kt
│ │ │ │ │ ├── HostRoleAop.kt
│ │ │ │ │ ├── HostRoleCallTransaction.kt
│ │ │ │ │ ├── HostRoleEventTransaction.kt
│ │ │ │ │ ├── HostRoleHostTransaction.kt
│ │ │ │ │ └── HostRolesAllowed.kt
│ │ │ │ ├── customizer/
│ │ │ │ │ └── EnumValuePropertyCustomizer.kt
│ │ │ │ ├── page/
│ │ │ │ │ └── PageResponse.kt
│ │ │ │ └── slice/
│ │ │ │ ├── SliceParam.kt
│ │ │ │ └── SliceResponse.kt
│ │ │ ├── config/
│ │ │ │ ├── ExampleHolder.kt
│ │ │ │ ├── HttpContentCacheFilter.kt
│ │ │ │ ├── MdcFilter.kt
│ │ │ │ ├── ServletFilterConfig.kt
│ │ │ │ ├── SwaggerConfig.kt
│ │ │ │ ├── WebMvcConfig.kt
│ │ │ │ ├── rateLimit/
│ │ │ │ │ ├── IPRateLimiter.kt
│ │ │ │ │ ├── ThrottlingInterceptor.kt
│ │ │ │ │ ├── ThrottlingWebConfigure.kt
│ │ │ │ │ └── UserRateLimiter.kt
│ │ │ │ ├── response/
│ │ │ │ │ ├── GlobalExceptionHandler.kt
│ │ │ │ │ └── SuccessResponseAdvice.kt
│ │ │ │ └── security/
│ │ │ │ ├── AccessDeniedFilter.kt
│ │ │ │ ├── AuthDetails.kt
│ │ │ │ ├── CorsConfig.kt
│ │ │ │ ├── CurrentUserIdResolver.kt
│ │ │ │ ├── JwtExceptionFilter.kt
│ │ │ │ ├── JwtTokenFilter.kt
│ │ │ │ ├── SecurityConfig.kt
│ │ │ │ └── SecurityUtils.kt
│ │ │ ├── coupon/
│ │ │ │ ├── controller/
│ │ │ │ │ └── CouponController.kt
│ │ │ │ ├── dto/
│ │ │ │ │ ├── reqeust/
│ │ │ │ │ │ └── CreateCouponCampaignRequest.kt
│ │ │ │ │ └── response/
│ │ │ │ │ ├── CreateCouponCampaignResponse.kt
│ │ │ │ │ ├── CreateUserCouponResponse.kt
│ │ │ │ │ ├── ReadIssuedCouponOrderResponse.kt
│ │ │ │ │ └── ReadIssuedCouponResponse.kt
│ │ │ │ ├── mapper/
│ │ │ │ │ ├── CouponCampaignMapper.kt
│ │ │ │ │ └── IssuedCouponMapper.kt
│ │ │ │ └── service/
│ │ │ │ ├── CreateCouponUseCase.kt
│ │ │ │ ├── CreateUserCouponUseCase.kt
│ │ │ │ └── ReadIssuedCouponUseCase.kt
│ │ │ ├── email/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── IssuedTicketMailDTO.kt
│ │ │ │ │ └── OrderMailDto.kt
│ │ │ │ ├── handler/
│ │ │ │ │ ├── CreateOrderEventEmailHandler.kt
│ │ │ │ │ ├── DoneOrderEventEmailHandler.kt
│ │ │ │ │ ├── EntranceIssuedTicketEventEmailHandler.kt
│ │ │ │ │ ├── RegisterUserEventEmailHandler.kt
│ │ │ │ │ └── WithDrawOrderEventEmailHandler.kt
│ │ │ │ └── service/
│ │ │ │ ├── EntranceIssuedTicketEmailService.kt
│ │ │ │ ├── HostMasterChangeEmailService.kt
│ │ │ │ ├── HostUserDisabledEmailService.kt
│ │ │ │ ├── HostUserInvitationEmailService.kt
│ │ │ │ ├── HostUserRoleChangeEmailService.kt
│ │ │ │ ├── IssuedTicketMailInfoHelper.kt
│ │ │ │ ├── OrderApproveConfirmEmailService.kt
│ │ │ │ ├── OrderApproveRequestEmailService.kt
│ │ │ │ ├── OrderMailInfoHelper.kt
│ │ │ │ ├── OrderPaymentDoneEmailService.kt
│ │ │ │ ├── OrderWithDrawCancelEmailService.kt
│ │ │ │ ├── OrderWithDrawRefundEmailService.kt
│ │ │ │ └── SendRegisterEmailService.kt
│ │ │ ├── event/
│ │ │ │ ├── controller/
│ │ │ │ │ └── EventController.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ ├── request/
│ │ │ │ │ │ │ ├── CreateEventRequest.kt
│ │ │ │ │ │ │ ├── UpdateEventBasicRequest.kt
│ │ │ │ │ │ │ ├── UpdateEventDetailRequest.kt
│ │ │ │ │ │ │ └── UpdateEventStatusRequest.kt
│ │ │ │ │ │ └── response/
│ │ │ │ │ │ ├── EventChecklistResponse.kt
│ │ │ │ │ │ ├── EventDetailResponse.kt
│ │ │ │ │ │ ├── EventProfileResponse.kt
│ │ │ │ │ │ └── EventResponse.kt
│ │ │ │ │ └── mapper/
│ │ │ │ │ └── EventMapper.kt
│ │ │ │ └── service/
│ │ │ │ ├── CreateEventUseCase.kt
│ │ │ │ ├── DeleteEventUseCase.kt
│ │ │ │ ├── OpenEventUseCase.kt
│ │ │ │ ├── ReadEventChecklistUseCase.kt
│ │ │ │ ├── ReadEventDetailUseCase.kt
│ │ │ │ ├── ReadUserEventProfilesUseCase.kt
│ │ │ │ ├── SearchEventsUseCase.kt
│ │ │ │ ├── UpdateEventBasicUseCase.kt
│ │ │ │ ├── UpdateEventDetailUseCase.kt
│ │ │ │ └── UpdateEventStatusUseCase.kt
│ │ │ ├── example/
│ │ │ │ ├── controller/
│ │ │ │ │ └── ExampleController.kt
│ │ │ │ ├── docs/
│ │ │ │ │ ├── ExampleException2Docs.kt
│ │ │ │ │ └── ExampleExceptionDocs.kt
│ │ │ │ ├── dto/
│ │ │ │ │ └── ExampleResponse.kt
│ │ │ │ └── service/
│ │ │ │ └── ExampleApiService.kt
│ │ │ ├── host/
│ │ │ │ ├── controller/
│ │ │ │ │ └── HostController.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ ├── request/
│ │ │ │ │ │ │ ├── CreateHostRequest.kt
│ │ │ │ │ │ │ ├── InviteHostRequest.kt
│ │ │ │ │ │ │ ├── TransferMasterRequest.kt
│ │ │ │ │ │ │ ├── UpdateHostRequest.kt
│ │ │ │ │ │ │ ├── UpdateHostSlackRequest.kt
│ │ │ │ │ │ │ └── UpdateHostUserRoleRequest.kt
│ │ │ │ │ │ └── response/
│ │ │ │ │ │ ├── HostDetailResponse.kt
│ │ │ │ │ │ ├── HostEventProfileResponse.kt
│ │ │ │ │ │ ├── HostProfileResponse.kt
│ │ │ │ │ │ └── HostResponse.kt
│ │ │ │ │ └── mapper/
│ │ │ │ │ └── HostMapper.kt
│ │ │ │ └── service/
│ │ │ │ ├── CreateHostUseCase.kt
│ │ │ │ ├── InviteHostUseCase.kt
│ │ │ │ ├── JoinHostUseCase.kt
│ │ │ │ ├── ReadHostEventsUseCase.kt
│ │ │ │ ├── ReadHostProfilesUseCase.kt
│ │ │ │ ├── ReadHostUseCase.kt
│ │ │ │ ├── ReadInviteUsersUseCase.kt
│ │ │ │ ├── RejectHostUseCase.kt
│ │ │ │ ├── TransferMasterUseCase.kt
│ │ │ │ ├── UpdateHostProfileUseCase.kt
│ │ │ │ ├── UpdateHostSlackUrlUseCase.kt
│ │ │ │ └── UpdateHostUserRoleUseCase.kt
│ │ │ ├── image/
│ │ │ │ ├── controller/
│ │ │ │ │ └── ImageController.kt
│ │ │ │ ├── dto/
│ │ │ │ │ ├── ImageUrlRequest.kt
│ │ │ │ │ └── ImageUrlResponse.kt
│ │ │ │ └── service/
│ │ │ │ └── GetImageUploadUrlUseCase.kt
│ │ │ ├── issuedTicket/
│ │ │ │ ├── controller/
│ │ │ │ │ ├── AdminIssuedTicketController.kt
│ │ │ │ │ └── IssuedTicketController.kt
│ │ │ │ ├── dto/
│ │ │ │ │ ├── request/
│ │ │ │ │ │ └── AdminIssuedTicketTableQueryRequest.kt
│ │ │ │ │ └── response/
│ │ │ │ │ ├── IssuedTicketAdminTableElement.kt
│ │ │ │ │ ├── RetrieveIssuedTicketDTO.kt
│ │ │ │ │ ├── RetrieveIssuedTicketDetailResponse.kt
│ │ │ │ │ └── RetrieveIssuedTicketListResponse.kt
│ │ │ │ ├── mapper/
│ │ │ │ │ └── IssuedTicketMapper.kt
│ │ │ │ └── service/
│ │ │ │ ├── EntranceIssuedTicketUseCase.kt
│ │ │ │ ├── ReadIssuedTicketUseCase.kt
│ │ │ │ └── ReadIssuedTicketsUseCase.kt
│ │ │ ├── order/
│ │ │ │ ├── controller/
│ │ │ │ │ ├── OrderAdminController.kt
│ │ │ │ │ └── OrderController.kt
│ │ │ │ ├── docs/
│ │ │ │ │ ├── ApproveOrderExceptionDocs.kt
│ │ │ │ │ ├── CancelOrderExceptionDocs.kt
│ │ │ │ │ ├── ConfirmOrderExceptionDocs.kt
│ │ │ │ │ ├── CreateOrderExceptionDocs.kt
│ │ │ │ │ ├── FreeOrderExceptionDocs.kt
│ │ │ │ │ └── RefundOrderExceptionDocs.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ ├── request/
│ │ │ │ │ │ │ ├── AdminOrderTableQueryRequest.kt
│ │ │ │ │ │ │ ├── CancelReasonRequest.kt
│ │ │ │ │ │ │ ├── ConfirmOrderRequest.kt
│ │ │ │ │ │ │ └── CreateOrderRequest.kt
│ │ │ │ │ │ └── response/
│ │ │ │ │ │ ├── CreateOrderResponse.kt
│ │ │ │ │ │ ├── OrderAdminTableElement.kt
│ │ │ │ │ │ ├── OrderBriefElement.kt
│ │ │ │ │ │ ├── OrderLineTicketResponse.kt
│ │ │ │ │ │ ├── OrderPaymentResponse.kt
│ │ │ │ │ │ ├── OrderResponse.kt
│ │ │ │ │ │ └── OrderTicketResponse.kt
│ │ │ │ │ └── mapper/
│ │ │ │ │ └── OrderMapper.kt
│ │ │ │ └── service/
│ │ │ │ ├── ApproveOrderUseCase.kt
│ │ │ │ ├── CancelOrderUseCase.kt
│ │ │ │ ├── ConfirmOrderUseCase.kt
│ │ │ │ ├── CreateOrderUseCase.kt
│ │ │ │ ├── CreateTossOrderUseCase.kt
│ │ │ │ ├── FreeOrderUseCase.kt
│ │ │ │ ├── ReadOrderUseCase.kt
│ │ │ │ ├── RefundOrderUseCase.kt
│ │ │ │ └── RefuseOrderUseCase.kt
│ │ │ ├── refund/
│ │ │ │ ├── controller/
│ │ │ │ │ └── RefundController.kt
│ │ │ │ ├── dto/
│ │ │ │ │ └── response/
│ │ │ │ │ └── RefundResponse.kt
│ │ │ │ └── service/
│ │ │ │ ├── CompleteRefundUseCase.kt
│ │ │ │ └── GetRefundsUseCase.kt
│ │ │ ├── slack/
│ │ │ │ ├── handler/
│ │ │ │ │ ├── event/
│ │ │ │ │ │ ├── EventContentChangeEventHandler.kt
│ │ │ │ │ │ ├── EventCreationEventHandler.kt
│ │ │ │ │ │ ├── EventDeletionEventHandler.kt
│ │ │ │ │ │ └── EventStatusChangeEventHandler.kt
│ │ │ │ │ ├── host/
│ │ │ │ │ │ ├── HostRegisterSlackEventHandler.kt
│ │ │ │ │ │ ├── HostUserDisabledEventHandler.kt
│ │ │ │ │ │ ├── HostUserInvitationEventHandler.kt
│ │ │ │ │ │ ├── HostUserJoinEventHandler.kt
│ │ │ │ │ │ └── HostUserRoleChangeEventHandler.kt
│ │ │ │ │ └── order/
│ │ │ │ │ ├── DudoongTicketCancelOrderEventHandler.kt
│ │ │ │ │ ├── DudoongTicketRefundOrderEventHandler.kt
│ │ │ │ │ ├── NewApproveOrderAlarmEventHandler.kt
│ │ │ │ │ ├── NewConfirmOrderAlarmEventHandler.kt
│ │ │ │ │ ├── OrderApprovedAlarmEventHandler.kt
│ │ │ │ │ └── WithDrawOrderEventHandler.kt
│ │ │ │ └── sender/
│ │ │ │ ├── SlackBootNotificationSender.kt
│ │ │ │ ├── SlackInternalErrorSender.kt
│ │ │ │ └── SlackThrottleErrorSender.kt
│ │ │ ├── statistic/
│ │ │ │ ├── controller/
│ │ │ │ │ └── AdminStatisticController.kt
│ │ │ │ ├── dto/
│ │ │ │ │ └── DashBoardStatisticResponse.kt
│ │ │ │ ├── query/
│ │ │ │ │ ├── IssuedTicketQueryRepository.kt
│ │ │ │ │ ├── OrderQueryRepository.kt
│ │ │ │ │ └── result/
│ │ │ │ │ ├── IssuedTicketStatistic.kt
│ │ │ │ │ └── OrderStatistic.kt
│ │ │ │ └── useCase/
│ │ │ │ └── StatisticUseCase.kt
│ │ │ ├── ticketItem/
│ │ │ │ ├── controller/
│ │ │ │ │ ├── TicketItemController.kt
│ │ │ │ │ └── TicketOptionController.kt
│ │ │ │ ├── dto/
│ │ │ │ │ ├── request/
│ │ │ │ │ │ ├── ApplyTicketOptionRequest.kt
│ │ │ │ │ │ ├── CreateTicketItemRequest.kt
│ │ │ │ │ │ ├── CreateTicketOptionRequest.kt
│ │ │ │ │ │ └── UnapplyTicketOptionRequest.kt
│ │ │ │ │ └── response/
│ │ │ │ │ ├── AppliedOptionGroupResponse.kt
│ │ │ │ │ ├── ApplyTicketOptionResponse.kt
│ │ │ │ │ ├── GetAppliedOptionGroupsResponse.kt
│ │ │ │ │ ├── GetEventOptionsResponse.kt
│ │ │ │ │ ├── GetEventTicketItemsResponse.kt
│ │ │ │ │ ├── GetTicketItemOptionsResponse.kt
│ │ │ │ │ ├── OptionGroupResponse.kt
│ │ │ │ │ ├── OptionResponse.kt
│ │ │ │ │ └── TicketItemResponse.kt
│ │ │ │ ├── mapper/
│ │ │ │ │ ├── TicketItemMapper.kt
│ │ │ │ │ └── TicketOptionMapper.kt
│ │ │ │ └── service/
│ │ │ │ ├── ApplyTicketOptionUseCase.kt
│ │ │ │ ├── CreateTicketItemUseCase.kt
│ │ │ │ ├── CreateTicketOptionUseCase.kt
│ │ │ │ ├── DeleteOptionGroupUseCase.kt
│ │ │ │ ├── DeleteTicketItemUseCase.kt
│ │ │ │ ├── GetAppliedOptionGroupsUseCase.kt
│ │ │ │ ├── GetEventOptionsUseCase.kt
│ │ │ │ ├── GetEventTicketItemsUseCase.kt
│ │ │ │ ├── GetTicketOptionsUseCase.kt
│ │ │ │ └── UnapplyTicketOptionUseCase.kt
│ │ │ └── user/
│ │ │ ├── controller/
│ │ │ │ └── UserController.kt
│ │ │ ├── model/
│ │ │ │ └── dto/
│ │ │ │ └── request/
│ │ │ │ └── ChangeNameRequest.kt
│ │ │ └── service/
│ │ │ ├── ChangeNameUseCase.kt
│ │ │ ├── MarketingUserUseCase.kt
│ │ │ └── ReadUserUseCase.kt
│ │ └── resources/
│ │ ├── application-local.yml
│ │ └── application.yml
│ └── test/
│ ├── java/
│ │ └── band/
│ │ └── gosrock/
│ │ ├── DuDoongApiServerApplication.java
│ │ └── api/
│ │ ├── email/
│ │ │ └── RegisterUserEventHandlerTest.java
│ │ └── supports/
│ │ ├── ApiIntegrateProfileResolver.java
│ │ ├── ApiIntegrateSpringBootTest.java
│ │ └── ApiIntegrateTestConfig.java
│ ├── kotlin/
│ │ └── band/
│ │ └── gosrock/
│ │ └── api/
│ │ ├── auth/
│ │ │ └── service/
│ │ │ └── helper/
│ │ │ └── CookieHelperTest.kt
│ │ ├── common/
│ │ │ └── aop/
│ │ │ └── hostRole/
│ │ │ └── HostRoleAopTest.kt
│ │ ├── refund/
│ │ │ └── service/
│ │ │ └── CompleteRefundUseCaseTest.kt
│ │ └── statistic/
│ │ └── query/
│ │ └── result/
│ │ ├── IssuedTicketStatisticTest.kt
│ │ └── OrderStatisticTest.kt
│ └── resources/
│ └── logback-test.xml
├── DuDoong-Batch/
│ ├── Dockerfile
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── kotlin/
│ │ └── band/
│ │ └── gosrock/
│ │ ├── BatchApplication.kt
│ │ ├── dto/
│ │ │ └── SettlementPDFDto.kt
│ │ ├── helper/
│ │ │ ├── SettlementEmailHelper.kt
│ │ │ ├── SettlementPdfHelper.kt
│ │ │ ├── excel/
│ │ │ │ ├── ExcelOrderDto.kt
│ │ │ │ └── ExcelOrderHelper.kt
│ │ │ └── slack/
│ │ │ ├── SlackEventExpirationSender.kt
│ │ │ └── SlackUserNotificationSender.kt
│ │ ├── job/
│ │ │ ├── EventExpiration.kt
│ │ │ ├── EventOrdersToExcel.kt
│ │ │ ├── EventSettlementAlimTalkToHost.kt
│ │ │ ├── EventSettlementEmailToAdmin.kt
│ │ │ ├── EventSettlementEmailToHost.kt
│ │ │ ├── EventSettlementPDF.kt
│ │ │ ├── EventSummarySettlement.kt
│ │ │ ├── EventTransactionSettlement.kt
│ │ │ └── SlackUserStatistic.kt
│ │ └── parameter/
│ │ ├── DateJobParameter.kt
│ │ ├── DateTimeJobParameter.kt
│ │ └── EventJobParameter.kt
│ └── resources/
│ └── application.yml
├── DuDoong-Common/
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── kotlin/
│ │ │ └── band/
│ │ │ └── gosrock/
│ │ │ └── common/
│ │ │ ├── DuDoongCommonApplication.kt
│ │ │ ├── annotation/
│ │ │ │ ├── Adaptor.kt
│ │ │ │ ├── ApiErrorCodeExample.kt
│ │ │ │ ├── ApiErrorExceptionsExample.kt
│ │ │ │ ├── CurrentUserId.kt
│ │ │ │ ├── DateFormat.kt
│ │ │ │ ├── DevelopOnlyApi.kt
│ │ │ │ ├── DisableSwaggerSecurity.kt
│ │ │ │ ├── DomainService.kt
│ │ │ │ ├── Enum.kt
│ │ │ │ ├── EnumClass.kt
│ │ │ │ ├── ExceptionDoc.kt
│ │ │ │ ├── ExplainError.kt
│ │ │ │ ├── Helper.kt
│ │ │ │ ├── Mapper.kt
│ │ │ │ ├── Phone.kt
│ │ │ │ ├── Policy.kt
│ │ │ │ ├── Port.kt
│ │ │ │ ├── UseCase.kt
│ │ │ │ └── Validator.kt
│ │ │ ├── aop/
│ │ │ │ └── ApiBlockingAspect.kt
│ │ │ ├── config/
│ │ │ │ └── ConfigurationPropertiesConfig.kt
│ │ │ ├── consts/
│ │ │ │ └── DuDoongStatic.kt
│ │ │ ├── deserializer/
│ │ │ │ └── CustomEnumDeserializer.kt
│ │ │ ├── dto/
│ │ │ │ ├── AccessTokenInfo.kt
│ │ │ │ ├── ErrorReason.kt
│ │ │ │ ├── ErrorResponse.kt
│ │ │ │ ├── OIDCDecodePayload.kt
│ │ │ │ └── SuccessResponse.kt
│ │ │ ├── exception/
│ │ │ │ ├── BadFileExtensionException.kt
│ │ │ │ ├── BadLockIdentifierException.kt
│ │ │ │ ├── BaseErrorCode.kt
│ │ │ │ ├── DuDoongCodeException.kt
│ │ │ │ ├── DuDoongDynamicException.kt
│ │ │ │ ├── ExpiredTokenException.kt
│ │ │ │ ├── GlobalErrorCode.kt
│ │ │ │ ├── InvalidTokenException.kt
│ │ │ │ ├── NotAvailableRedissonLockException.kt
│ │ │ │ ├── OtherServerBadRequestException.kt
│ │ │ │ ├── OtherServerExpiredTokenException.kt
│ │ │ │ ├── OtherServerForbiddenException.kt
│ │ │ │ ├── OtherServerInternalSeverErrorException.kt
│ │ │ │ ├── OtherServerNotFoundException.kt
│ │ │ │ ├── OtherServerUnauthorizedException.kt
│ │ │ │ ├── RefreshTokenExpiredException.kt
│ │ │ │ ├── SecurityContextNotFoundException.kt
│ │ │ │ └── TooManyRequestException.kt
│ │ │ ├── helper/
│ │ │ │ └── SpringEnvironmentHelper.kt
│ │ │ ├── interfaces/
│ │ │ │ └── SwaggerExampleExceptions.kt
│ │ │ ├── jwt/
│ │ │ │ ├── JwtOIDCProvider.kt
│ │ │ │ └── JwtTokenProvider.kt
│ │ │ ├── properties/
│ │ │ │ ├── JwtProperties.kt
│ │ │ │ ├── OauthProperties.kt
│ │ │ │ └── TossPaymentsProperties.kt
│ │ │ └── validator/
│ │ │ ├── EnumValidator.kt
│ │ │ └── PhoneValidator.kt
│ │ └── resources/
│ │ ├── application-common-local.yml
│ │ └── application-common.yml
│ └── test/
│ ├── kotlin/
│ │ └── band/
│ │ └── gosrock/
│ │ └── common/
│ │ ├── jwt/
│ │ │ └── JwtTokenProviderTest.kt
│ │ └── properties/
│ │ ├── JwtPropertiesTest.kt
│ │ └── TossPaymentsPropertiesTest.kt
│ └── resources/
│ ├── application.yml
│ └── logback-test.xml
├── DuDoong-Domain/
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── kotlin/
│ │ │ └── band/
│ │ │ └── gosrock/
│ │ │ └── domain/
│ │ │ ├── DomainPackageLocation.kt
│ │ │ ├── DuDoongDomainApplication.kt
│ │ │ ├── common/
│ │ │ │ ├── alarm/
│ │ │ │ │ ├── EventSlackAlarm.kt
│ │ │ │ │ ├── HostSlackAlarm.kt
│ │ │ │ │ ├── OrderKakaoTalkAlarm.kt
│ │ │ │ │ ├── SettlementKakaoTalkAlarm.kt
│ │ │ │ │ └── UserKakaoTalkAlarm.kt
│ │ │ │ ├── aop/
│ │ │ │ │ ├── domainEvent/
│ │ │ │ │ │ ├── DomainEvent.kt
│ │ │ │ │ │ ├── EventPublisherAspect.kt
│ │ │ │ │ │ └── Events.kt
│ │ │ │ │ └── redissonLock/
│ │ │ │ │ ├── CallTransaction.kt
│ │ │ │ │ ├── CallTransactionFactory.kt
│ │ │ │ │ ├── RedissonCallNewTransaction.kt
│ │ │ │ │ ├── RedissonCallSameTransaction.kt
│ │ │ │ │ ├── RedissonLock.kt
│ │ │ │ │ └── RedissonLockAop.kt
│ │ │ │ ├── converter/
│ │ │ │ │ └── BigDecimalScale6WithBankersRoundingConverter.kt
│ │ │ │ ├── dto/
│ │ │ │ │ └── ProfileViewDto.kt
│ │ │ │ ├── events/
│ │ │ │ │ ├── event/
│ │ │ │ │ │ ├── EventContentChangeEvent.kt
│ │ │ │ │ │ ├── EventCreationEvent.kt
│ │ │ │ │ │ ├── EventDeletionEvent.kt
│ │ │ │ │ │ └── EventStatusChangeEvent.kt
│ │ │ │ │ ├── host/
│ │ │ │ │ │ ├── HostRegisterSlackEvent.kt
│ │ │ │ │ │ ├── HostUserDisabledEvent.kt
│ │ │ │ │ │ ├── HostUserInvitationEvent.kt
│ │ │ │ │ │ ├── HostUserJoinEvent.kt
│ │ │ │ │ │ └── HostUserRoleChangeEvent.kt
│ │ │ │ │ ├── issuedTicket/
│ │ │ │ │ │ └── EntranceIssuedTicketEvent.kt
│ │ │ │ │ ├── order/
│ │ │ │ │ │ ├── CreateOrderEvent.kt
│ │ │ │ │ │ ├── DoneOrderEvent.kt
│ │ │ │ │ │ └── WithDrawOrderEvent.kt
│ │ │ │ │ └── user/
│ │ │ │ │ └── UserRegisterEvent.kt
│ │ │ │ ├── model/
│ │ │ │ │ └── BaseTimeEntity.kt
│ │ │ │ ├── util/
│ │ │ │ │ ├── PhoneNumberInstance.kt
│ │ │ │ │ ├── QueryDslUtil.kt
│ │ │ │ │ └── SliceUtil.kt
│ │ │ │ └── vo/
│ │ │ │ ├── AccountInfoVo.kt
│ │ │ │ ├── CommentInfoVo.kt
│ │ │ │ ├── DateTimePeriod.kt
│ │ │ │ ├── EventBasicVo.kt
│ │ │ │ ├── EventDetailVo.kt
│ │ │ │ ├── EventInfoVo.kt
│ │ │ │ ├── EventPlaceVo.kt
│ │ │ │ ├── EventProfileVo.kt
│ │ │ │ ├── HostInfoVo.kt
│ │ │ │ ├── HostProfileVo.kt
│ │ │ │ ├── HostUserVo.kt
│ │ │ │ ├── ImageVo.kt
│ │ │ │ ├── IssuedCouponInfoVo.kt
│ │ │ │ ├── IssuedTicketInfoVo.kt
│ │ │ │ ├── IssuedTicketOptionAnswerVo.kt
│ │ │ │ ├── Money.kt
│ │ │ │ ├── OptionAnswerVo.kt
│ │ │ │ ├── PhoneNumberVo.kt
│ │ │ │ ├── RefundInfoVo.kt
│ │ │ │ ├── UserInfoVo.kt
│ │ │ │ └── UserProfileVo.kt
│ │ │ ├── config/
│ │ │ │ ├── CustomAsyncExceptionHandler.kt
│ │ │ │ ├── EnableAsyncConfig.kt
│ │ │ │ ├── JpaConfig.kt
│ │ │ │ ├── MdcTaskDecorator.kt
│ │ │ │ └── QueryDslConfig.kt
│ │ │ └── domains/
│ │ │ ├── cart/
│ │ │ │ ├── adaptor/
│ │ │ │ │ └── CartAdaptor.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Cart.kt
│ │ │ │ │ ├── CartLineItem.kt
│ │ │ │ │ ├── CartOptionAnswer.kt
│ │ │ │ │ └── CartValidator.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── CartErrorCode.kt
│ │ │ │ │ ├── CartInvalidOptionAnswerException.kt
│ │ │ │ │ ├── CartItemNotOneTypeException.kt
│ │ │ │ │ ├── CartLineItemNotFoundException.kt
│ │ │ │ │ ├── CartNotAnswerAllOptionGroupException.kt
│ │ │ │ │ └── CartNotFoundException.kt
│ │ │ │ ├── repository/
│ │ │ │ │ ├── CartCustomRepository.kt
│ │ │ │ │ ├── CartCustomRepositoryImpl.kt
│ │ │ │ │ └── CartRepository.kt
│ │ │ │ └── service/
│ │ │ │ ├── CartDomainService.kt
│ │ │ │ └── DoneOrderEventHandler.kt
│ │ │ ├── comment/
│ │ │ │ ├── adaptor/
│ │ │ │ │ └── CommentAdaptor.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Comment.kt
│ │ │ │ │ └── CommentStatus.kt
│ │ │ │ ├── dto/
│ │ │ │ │ └── condition/
│ │ │ │ │ └── CommentCondition.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── CommentAlreadyDeleteException.kt
│ │ │ │ │ ├── CommentErrorCode.kt
│ │ │ │ │ ├── CommentNotFoundException.kt
│ │ │ │ │ ├── CommentNotMatchEventException.kt
│ │ │ │ │ └── RetrieveRandomCommentNotFoundException.kt
│ │ │ │ ├── repository/
│ │ │ │ │ ├── CommentCustomRepository.kt
│ │ │ │ │ ├── CommentCustomRepositoryImpl.kt
│ │ │ │ │ └── CommentRepository.kt
│ │ │ │ └── service/
│ │ │ │ └── CommentDomainService.kt
│ │ │ ├── coupon/
│ │ │ │ ├── adaptor/
│ │ │ │ │ ├── CouponCampaignAdaptor.kt
│ │ │ │ │ └── IssuedCouponAdaptor.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── ApplyTarget.kt
│ │ │ │ │ ├── CouponCampaign.kt
│ │ │ │ │ ├── CouponStockInfo.kt
│ │ │ │ │ ├── DiscountType.kt
│ │ │ │ │ └── IssuedCoupon.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── AlreadyExistCouponCampaignException.kt
│ │ │ │ │ ├── AlreadyIssuedCouponException.kt
│ │ │ │ │ ├── AlreadyRecoveredCouponException.kt
│ │ │ │ │ ├── AlreadyUsedCouponException.kt
│ │ │ │ │ ├── CouponCampaignNotFoundException.kt
│ │ │ │ │ ├── CouponErrorCode.kt
│ │ │ │ │ ├── CouponNotFoundException.kt
│ │ │ │ │ ├── NoCouponStockLeftException.kt
│ │ │ │ │ ├── NotIssuingCouponPeriodException.kt
│ │ │ │ │ ├── NotMyCouponException.kt
│ │ │ │ │ ├── SupplyLessThenDiscountException.kt
│ │ │ │ │ ├── SupplyLessThenMinimumException.kt
│ │ │ │ │ └── WrongDiscountAmountException.kt
│ │ │ │ ├── repository/
│ │ │ │ │ ├── CouponCampaignRepository.kt
│ │ │ │ │ ├── IssuedCouponCustomRepository.kt
│ │ │ │ │ ├── IssuedCouponCustomRepositoryImpl.kt
│ │ │ │ │ └── IssuedCouponRepository.kt
│ │ │ │ └── service/
│ │ │ │ ├── CreateCouponCampaignDomainService.kt
│ │ │ │ ├── CreateIssuedCouponDomainService.kt
│ │ │ │ ├── RecoveryCouponService.kt
│ │ │ │ ├── UseCouponService.kt
│ │ │ │ └── handler/
│ │ │ │ ├── CreateOrderCouponHandler.kt
│ │ │ │ └── WithDrawOrderCouponHandler.kt
│ │ │ ├── event/
│ │ │ │ ├── adaptor/
│ │ │ │ │ └── EventAdaptor.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Event.kt
│ │ │ │ │ ├── EventBasic.kt
│ │ │ │ │ ├── EventDetail.kt
│ │ │ │ │ ├── EventDetailImage.kt
│ │ │ │ │ ├── EventPlace.kt
│ │ │ │ │ └── EventStatus.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── AlreadyCalculatingStatusException.kt
│ │ │ │ │ ├── AlreadyCloseStatusException.kt
│ │ │ │ │ ├── AlreadyDeletedStatusException.kt
│ │ │ │ │ ├── AlreadyExistEventUrlNameException.kt
│ │ │ │ │ ├── AlreadyOpenStatusException.kt
│ │ │ │ │ ├── AlreadyPreparingStatusException.kt
│ │ │ │ │ ├── CannotDeleteByIssuedTicketException.kt
│ │ │ │ │ ├── CannotDeleteByOpenEventException.kt
│ │ │ │ │ ├── CannotModifyOpenEventException.kt
│ │ │ │ │ ├── CannotOpenEventException.kt
│ │ │ │ │ ├── EventCannotEndBeforeStartException.kt
│ │ │ │ │ ├── EventErrorCode.kt
│ │ │ │ │ ├── EventNotFoundException.kt
│ │ │ │ │ ├── EventNotOpenException.kt
│ │ │ │ │ ├── EventOpenTimeExpiredException.kt
│ │ │ │ │ ├── EventTicketingTimeIsPassedException.kt
│ │ │ │ │ ├── HostNotAuthEventException.kt
│ │ │ │ │ ├── InvalidEventStatusTransitionException.kt
│ │ │ │ │ └── UseOtherApiException.kt
│ │ │ │ ├── repository/
│ │ │ │ │ ├── EventCustomRepository.kt
│ │ │ │ │ ├── EventCustomRepositoryImpl.kt
│ │ │ │ │ ├── EventDetailImageRepository.kt
│ │ │ │ │ └── EventRepository.kt
│ │ │ │ └── service/
│ │ │ │ └── EventService.kt
│ │ │ ├── example/
│ │ │ │ ├── domain/
│ │ │ │ │ └── ExampleEntity.kt
│ │ │ │ ├── repository/
│ │ │ │ │ └── ExampleRepository.kt
│ │ │ │ └── service/
│ │ │ │ └── ExampleDomainService.kt
│ │ │ ├── host/
│ │ │ │ ├── adaptor/
│ │ │ │ │ └── HostAdaptor.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Host.kt
│ │ │ │ │ ├── HostProfile.kt
│ │ │ │ │ ├── HostRole.kt
│ │ │ │ │ └── HostUser.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── AlreadyJoinedHostException.kt
│ │ │ │ │ ├── CannotModifyMasterHostRoleException.kt
│ │ │ │ │ ├── DuplicateSlackUrlException.kt
│ │ │ │ │ ├── ForbiddenHostException.kt
│ │ │ │ │ ├── HostErrorCode.kt
│ │ │ │ │ ├── HostNotFoundException.kt
│ │ │ │ │ ├── HostUserNotFoundException.kt
│ │ │ │ │ ├── InvalidSlackUrlException.kt
│ │ │ │ │ ├── NotAcceptedHostException.kt
│ │ │ │ │ ├── NotManagerHostException.kt
│ │ │ │ │ ├── NotMasterHostException.kt
│ │ │ │ │ └── NotPartnerHostException.kt
│ │ │ │ ├── repository/
│ │ │ │ │ ├── HostCustomRepository.kt
│ │ │ │ │ ├── HostCustomRepositoryImpl.kt
│ │ │ │ │ └── HostRepository.kt
│ │ │ │ └── service/
│ │ │ │ └── HostService.kt
│ │ │ ├── issuedTicket/
│ │ │ │ ├── adaptor/
│ │ │ │ │ ├── IssuedTicketAdaptor.kt
│ │ │ │ │ └── IssuedTicketOptionAnswerAdaptor.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── IssuedTicket.kt
│ │ │ │ │ ├── IssuedTicketCancelReason.kt
│ │ │ │ │ ├── IssuedTicketItemInfoVo.kt
│ │ │ │ │ ├── IssuedTicketOptionAnswer.kt
│ │ │ │ │ ├── IssuedTicketStatus.kt
│ │ │ │ │ ├── IssuedTicketUserInfoVo.kt
│ │ │ │ │ ├── IssuedTickets.kt
│ │ │ │ │ └── IssuedTicketsStage.kt
│ │ │ │ ├── dto/
│ │ │ │ │ ├── condition/
│ │ │ │ │ │ └── IssuedTicketCondition.kt
│ │ │ │ │ ├── request/
│ │ │ │ │ │ ├── CreateIssuedTicketDTO.kt
│ │ │ │ │ │ ├── CreateIssuedTicketForDevDTO.kt
│ │ │ │ │ │ ├── CreateIssuedTicketRequestDTOs.kt
│ │ │ │ │ │ └── CreateIssuedTicketRequestForDev.kt
│ │ │ │ │ └── response/
│ │ │ │ │ ├── CreateIssuedTicketResponse.kt
│ │ │ │ │ ├── IssuedTicketDTO.kt
│ │ │ │ │ └── IssuedTicketPageDTO.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── CanNotCancelEntranceException.kt
│ │ │ │ │ ├── CanNotCancelException.kt
│ │ │ │ │ ├── CanNotEntranceException.kt
│ │ │ │ │ ├── IssuedTicketAlreadyEntranceException.kt
│ │ │ │ │ ├── IssuedTicketErrorCode.kt
│ │ │ │ │ ├── IssuedTicketNotFoundException.kt
│ │ │ │ │ ├── IssuedTicketNotMatchedEventException.kt
│ │ │ │ │ └── IssuedTicketUserNotMatchedException.kt
│ │ │ │ ├── repository/
│ │ │ │ │ ├── IssuedTicketCustomRepository.kt
│ │ │ │ │ ├── IssuedTicketCustomRepositoryImpl.kt
│ │ │ │ │ ├── IssuedTicketOptionAnswerRepository.kt
│ │ │ │ │ ├── IssuedTicketRepository.kt
│ │ │ │ │ └── condition/
│ │ │ │ │ └── FindEventIssuedTicketsCondition.kt
│ │ │ │ ├── service/
│ │ │ │ │ ├── IssuedTicketDomainService.kt
│ │ │ │ │ ├── OrderToIssuedTicketService.kt
│ │ │ │ │ └── handlers/
│ │ │ │ │ ├── OrderEventHandler.kt
│ │ │ │ │ └── WithdrawOrderEventHandler.kt
│ │ │ │ └── validator/
│ │ │ │ └── IssuedTicketValidator.kt
│ │ │ ├── order/
│ │ │ │ ├── adaptor/
│ │ │ │ │ └── OrderAdaptor.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Order.kt
│ │ │ │ │ ├── OrderCouponVo.kt
│ │ │ │ │ ├── OrderItemVo.kt
│ │ │ │ │ ├── OrderLineItem.kt
│ │ │ │ │ ├── OrderMethod.kt
│ │ │ │ │ ├── OrderOptionAnswer.kt
│ │ │ │ │ ├── OrderStatus.kt
│ │ │ │ │ ├── PaymentInfo.kt
│ │ │ │ │ ├── PaymentMethod.kt
│ │ │ │ │ ├── PgPaymentInfo.kt
│ │ │ │ │ ├── RefundStatus.kt
│ │ │ │ │ └── validator/
│ │ │ │ │ └── OrderValidator.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── ApproveWaitingOrderPurchaseLimitException.kt
│ │ │ │ │ ├── CanNotApproveDeletedUserOrderException.kt
│ │ │ │ │ ├── CanNotCancelOrderException.kt
│ │ │ │ │ ├── CanNotRefundOrderException.kt
│ │ │ │ │ ├── CanNotRefuseOrderException.kt
│ │ │ │ │ ├── InvalidOrderException.kt
│ │ │ │ │ ├── LessThanMinmumPaymentOrderException.kt
│ │ │ │ │ ├── NotApprovalOrderException.kt
│ │ │ │ │ ├── NotFreeOrderException.kt
│ │ │ │ │ ├── NotOwnerOrderException.kt
│ │ │ │ │ ├── NotPaymentOrderException.kt
│ │ │ │ │ ├── NotPendingOrderException.kt
│ │ │ │ │ ├── NotRefundAvailableDateOrderException.kt
│ │ │ │ │ ├── NotSupportedOrderMethodException.kt
│ │ │ │ │ ├── OrdeItemNotOneTypeException.kt
│ │ │ │ │ ├── OrderErrorCode.kt
│ │ │ │ │ ├── OrderItemOptionChangedException.kt
│ │ │ │ │ ├── OrderLineNotFountException.kt
│ │ │ │ │ └── OrderNotFoundException.kt
│ │ │ │ ├── repository/
│ │ │ │ │ ├── OrderCustomRepository.kt
│ │ │ │ │ ├── OrderCustomRepositoryImpl.kt
│ │ │ │ │ ├── OrderRepository.kt
│ │ │ │ │ └── condition/
│ │ │ │ │ ├── AdminTableOrderFilterType.kt
│ │ │ │ │ ├── AdminTableSearchType.kt
│ │ │ │ │ ├── FindEventOrdersCondition.kt
│ │ │ │ │ └── FindMyPageOrderCondition.kt
│ │ │ │ └── service/
│ │ │ │ ├── CreateOrderService.kt
│ │ │ │ ├── FreeOrderService.kt
│ │ │ │ ├── OrderApproveService.kt
│ │ │ │ ├── OrderConfirmService.kt
│ │ │ │ ├── OrderFactory.kt
│ │ │ │ ├── WithdrawOrderService.kt
│ │ │ │ ├── WithdrawPaymentService.kt
│ │ │ │ └── handler/
│ │ │ │ ├── ConfirmOrderFailHandler.kt
│ │ │ │ └── WithDrawOrderHandler.kt
│ │ │ ├── settlement/
│ │ │ │ ├── adaptor/
│ │ │ │ │ ├── EventSettlementAdaptor.kt
│ │ │ │ │ └── TransactionSettlementAdaptor.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── EventSettlement.kt
│ │ │ │ │ ├── EventSettlementStatus.kt
│ │ │ │ │ ├── SettlementFeeVo.kt
│ │ │ │ │ └── TransactionSettlement.kt
│ │ │ │ ├── repository/
│ │ │ │ │ ├── EventSettlementRepository.kt
│ │ │ │ │ └── TransactionSettlementRepository.kt
│ │ │ │ └── service/
│ │ │ │ └── EventSettlementDomainService.kt
│ │ │ ├── ticket_item/
│ │ │ │ ├── adaptor/
│ │ │ │ │ ├── OptionAdaptor.kt
│ │ │ │ │ ├── OptionGroupAdaptor.kt
│ │ │ │ │ └── TicketItemAdaptor.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── ItemOptionGroup.kt
│ │ │ │ │ ├── Option.kt
│ │ │ │ │ ├── OptionGroup.kt
│ │ │ │ │ ├── OptionGroupStatus.kt
│ │ │ │ │ ├── OptionGroupType.kt
│ │ │ │ │ ├── TicketItem.kt
│ │ │ │ │ ├── TicketItemStatus.kt
│ │ │ │ │ ├── TicketPayType.kt
│ │ │ │ │ └── TicketType.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── DuplicatedItemOptionGroupException.kt
│ │ │ │ │ ├── EmptyAccountInfoException.kt
│ │ │ │ │ ├── ForbiddenOptionChangeException.kt
│ │ │ │ │ ├── ForbiddenOptionGroupDeleteException.kt
│ │ │ │ │ ├── ForbiddenOptionPriceException.kt
│ │ │ │ │ ├── ForbiddenTicketItemDeleteException.kt
│ │ │ │ │ ├── InvalidOptionGroupException.kt
│ │ │ │ │ ├── InvalidOptionPriceException.kt
│ │ │ │ │ ├── InvalidPartnerException.kt
│ │ │ │ │ ├── InvalidTicketItemException.kt
│ │ │ │ │ ├── InvalidTicketPriceException.kt
│ │ │ │ │ ├── InvalidTicketTypeException.kt
│ │ │ │ │ ├── NotAppliedItemOptionGroupException.kt
│ │ │ │ │ ├── NotCorrectOptionAnswerException.kt
│ │ │ │ │ ├── OptionGroupNotFoundException.kt
│ │ │ │ │ ├── OptionNotFoundException.kt
│ │ │ │ │ ├── TicketItemErrorCode.kt
│ │ │ │ │ ├── TicketItemNotFoundException.kt
│ │ │ │ │ ├── TicketItemQuantityException.kt
│ │ │ │ │ ├── TicketItemQuantityLackException.kt
│ │ │ │ │ ├── TicketItemQuantityLargeException.kt
│ │ │ │ │ └── TicketPurchaseLimitException.kt
│ │ │ │ ├── repository/
│ │ │ │ │ ├── OptionGroupRepository.kt
│ │ │ │ │ ├── OptionRepository.kt
│ │ │ │ │ └── TicketItemRepository.kt
│ │ │ │ └── service/
│ │ │ │ ├── ItemOptionGroupService.kt
│ │ │ │ ├── TicketItemService.kt
│ │ │ │ └── TicketOptionService.kt
│ │ │ └── user/
│ │ │ ├── adaptor/
│ │ │ │ ├── RefreshTokenAdaptor.kt
│ │ │ │ └── UserAdaptor.kt
│ │ │ ├── domain/
│ │ │ │ ├── AccountRole.kt
│ │ │ │ ├── AccountState.kt
│ │ │ │ ├── OauthInfo.kt
│ │ │ │ ├── OauthProvider.kt
│ │ │ │ ├── Profile.kt
│ │ │ │ ├── RefreshTokenEntity.kt
│ │ │ │ └── User.kt
│ │ │ ├── exception/
│ │ │ │ ├── AlreadyDeletedUserException.kt
│ │ │ │ ├── AlreadySignUpUserException.kt
│ │ │ │ ├── EmptyPhoneNumException.kt
│ │ │ │ ├── ForbiddenUserException.kt
│ │ │ │ ├── UserErrorCode.kt
│ │ │ │ ├── UserNotFoundException.kt
│ │ │ │ └── UserPhoneNumberInvalidException.kt
│ │ │ ├── repository/
│ │ │ │ ├── RefreshTokenRepository.kt
│ │ │ │ └── UserRepository.kt
│ │ │ └── service/
│ │ │ └── UserDomainService.kt
│ │ └── resources/
│ │ ├── application-domain-local.yml
│ │ └── application-domain.yml
│ └── test/
│ ├── java/
│ │ └── band/
│ │ └── gosrock/
│ │ ├── domain/
│ │ │ ├── CunCurrencyExecutorService.java
│ │ │ ├── DisableDomainEvent.java
│ │ │ ├── DisableRedissonLock.java
│ │ │ ├── DomainIntegrateProfileResolver.java
│ │ │ ├── DomainIntegrateSpringBootTest.java
│ │ │ ├── DomainIntegrateTestConfig.java
│ │ │ ├── common/
│ │ │ │ ├── aop/
│ │ │ │ │ └── redissonLock/
│ │ │ │ │ └── RedissonLockAopTest.java
│ │ │ │ └── vo/
│ │ │ │ └── RefundInfoVoTest.java
│ │ │ └── domains/
│ │ │ ├── cart/
│ │ │ │ └── domain/
│ │ │ │ ├── CartLineItemTest.java
│ │ │ │ ├── CartOptionAnswerTest.java
│ │ │ │ ├── CartTest.java
│ │ │ │ └── CartValidatorTest.java
│ │ │ ├── coupon/
│ │ │ │ └── domain/
│ │ │ │ ├── CouponCampaignTest.java
│ │ │ │ ├── CouponStockInfoTest.java
│ │ │ │ └── IssuedCouponTest.java
│ │ │ ├── event/
│ │ │ │ └── EventTest.java
│ │ │ ├── host/
│ │ │ │ ├── domain/
│ │ │ │ │ ├── HostProfileTest.java
│ │ │ │ │ ├── HostRoleTest.java
│ │ │ │ │ ├── HostTest.java
│ │ │ │ │ └── HostUserTest.java
│ │ │ │ └── service/
│ │ │ │ ├── HostServiceConcurrencyFailureTest.java
│ │ │ │ └── HostServiceConcurrencyTest.java
│ │ │ ├── issuedTicket/
│ │ │ │ ├── adaptor/
│ │ │ │ │ └── IssuedTicketAdaptorTest.java
│ │ │ │ ├── domain/
│ │ │ │ │ ├── IssuedTicketItemInfoVoTest.java
│ │ │ │ │ ├── IssuedTicketOptionAnswerTest.java
│ │ │ │ │ ├── IssuedTicketTest.java
│ │ │ │ │ ├── IssuedTicketUserInfoVoTest.java
│ │ │ │ │ └── validator/
│ │ │ │ │ └── IssuedTicketValidatorTest.java
│ │ │ │ └── service/
│ │ │ │ ├── IssuedTicketDomainServiceTest.java
│ │ │ │ └── handlers/
│ │ │ │ ├── OrderEventHandlerTest.java
│ │ │ │ └── WithdrawOrderEventHandlerTest.java
│ │ │ └── order/
│ │ │ ├── domain/
│ │ │ │ ├── OrderLineItemTest.java
│ │ │ │ ├── OrderOptionAnswerTest.java
│ │ │ │ ├── OrderTest.java
│ │ │ │ └── validator/
│ │ │ │ └── OrderValidatorTest.java
│ │ │ └── service/
│ │ │ ├── OrderApproveServiceConcurrencyFailTest.java
│ │ │ ├── OrderApproveServiceConcurrencyTest.java
│ │ │ ├── OrderApproveServiceTest.java
│ │ │ ├── WithdrawOrderServiceTest.java
│ │ │ └── handler/
│ │ │ └── WithDrawOrderHandlerTest.java
│ │ └── domains/
│ │ └── user/
│ │ └── domain/
│ │ ├── OauthInfoTest.java
│ │ └── UserTest.java
│ ├── kotlin/
│ │ └── band/
│ │ └── gosrock/
│ │ └── domain/
│ │ ├── common/
│ │ │ └── vo/
│ │ │ ├── DateTimePeriodTest.kt
│ │ │ └── MoneyTest.kt
│ │ └── domains/
│ │ ├── cart/
│ │ │ └── service/
│ │ │ └── DoneOrderEventHandlerTest.kt
│ │ ├── event/
│ │ │ └── EventStatusTransitionTest.kt
│ │ ├── host/
│ │ │ └── HostTransferMasterTest.kt
│ │ ├── issuedTicket/
│ │ │ ├── IssuedTicketStatusTransitionTest.kt
│ │ │ ├── adaptor/
│ │ │ │ └── IssuedTicketAdaptorTest.kt
│ │ │ └── domain/
│ │ │ └── IssuedTicketItemInfoVoTest.kt
│ │ ├── order/
│ │ │ ├── OrderPaymentCalculationTest.kt
│ │ │ ├── OrderRefundTest.kt
│ │ │ └── service/
│ │ │ └── handler/
│ │ │ └── ConfirmOrderFailHandlerTest.kt
│ │ └── user/
│ │ └── domain/
│ │ └── ProfileTest.kt
│ └── resources/
│ ├── application-test.yml
│ ├── logback-test.xml
│ └── mockito-extensions/
│ └── org.mockito.plugins.MockMaker
├── DuDoong-Infrastructure/
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── kotlin/
│ │ │ └── band/
│ │ │ └── gosrock/
│ │ │ └── infrastructure/
│ │ │ ├── DuDoongInfraApplication.kt
│ │ │ ├── config/
│ │ │ │ ├── alilmTalk/
│ │ │ │ │ ├── NcpHelper.kt
│ │ │ │ │ └── dto/
│ │ │ │ │ ├── AlimTalkEventInfo.kt
│ │ │ │ │ ├── AlimTalkOrderInfo.kt
│ │ │ │ │ ├── AlimTalkUserInfo.kt
│ │ │ │ │ └── MessageDto.kt
│ │ │ │ ├── feign/
│ │ │ │ │ └── FeignCommonConfig.kt
│ │ │ │ ├── mail/
│ │ │ │ │ └── dto/
│ │ │ │ │ ├── EmailEventInfo.kt
│ │ │ │ │ ├── EmailIssuedTicketInfo.kt
│ │ │ │ │ ├── EmailOrderInfo.kt
│ │ │ │ │ └── EmailUserInfo.kt
│ │ │ │ ├── pdf/
│ │ │ │ │ ├── B64ImgReplacedElementFactory.kt
│ │ │ │ │ └── PdfRender.kt
│ │ │ │ ├── redis/
│ │ │ │ │ ├── RedisCacheConfig.kt
│ │ │ │ │ ├── RedisConfig.kt
│ │ │ │ │ └── RedissonConfig.kt
│ │ │ │ ├── s3/
│ │ │ │ │ ├── ImageFileExtension.kt
│ │ │ │ │ ├── ImageUrlDto.kt
│ │ │ │ │ ├── S3Config.kt
│ │ │ │ │ ├── S3PrivateFileService.kt
│ │ │ │ │ └── S3UploadPresignedUrlService.kt
│ │ │ │ ├── ses/
│ │ │ │ │ ├── AwsSesConfig.kt
│ │ │ │ │ ├── AwsSesUtils.kt
│ │ │ │ │ ├── RawEmailAttachmentDto.kt
│ │ │ │ │ └── SendRawEmailDto.kt
│ │ │ │ └── slack/
│ │ │ │ ├── SlackAsyncErrorSender.kt
│ │ │ │ ├── SlackErrorNotificationProvider.kt
│ │ │ │ ├── SlackHelper.kt
│ │ │ │ ├── SlackMessageProvider.kt
│ │ │ │ ├── SlackServiceNotificationProvider.kt
│ │ │ │ └── config/
│ │ │ │ └── SlackApiConfig.kt
│ │ │ └── outer/
│ │ │ └── api/
│ │ │ ├── BaseFeignClientPackage.kt
│ │ │ ├── alimTalk/
│ │ │ │ ├── client/
│ │ │ │ │ └── NcpClient.kt
│ │ │ │ └── config/
│ │ │ │ ├── NcpConfig.kt
│ │ │ │ └── NcpErrorDecoder.kt
│ │ │ ├── oauth/
│ │ │ │ ├── client/
│ │ │ │ │ ├── KakaoInfoClient.kt
│ │ │ │ │ └── KakaoOauthClient.kt
│ │ │ │ ├── config/
│ │ │ │ │ ├── KakaoInfoConfig.kt
│ │ │ │ │ ├── KakaoInfoErrorDecoder.kt
│ │ │ │ │ ├── KakaoKauthConfig.kt
│ │ │ │ │ └── KauthErrorDecoder.kt
│ │ │ │ ├── dto/
│ │ │ │ │ ├── KakaoInformationResponse.kt
│ │ │ │ │ ├── KakaoKauthErrorResponse.kt
│ │ │ │ │ ├── KakaoTokenResponse.kt
│ │ │ │ │ ├── OIDCPublicKeyDto.kt
│ │ │ │ │ ├── OIDCPublicKeysResponse.kt
│ │ │ │ │ └── UnlinkKaKaoTarget.kt
│ │ │ │ └── exception/
│ │ │ │ └── KakaoKauthErrorCode.kt
│ │ │ └── tossPayments/
│ │ │ ├── client/
│ │ │ │ ├── PaymentsCancelClient.kt
│ │ │ │ ├── PaymentsConfirmClient.kt
│ │ │ │ ├── PaymentsCreateClient.kt
│ │ │ │ ├── SettlementClient.kt
│ │ │ │ └── TransactionGetClient.kt
│ │ │ ├── config/
│ │ │ │ ├── FeignTossConfig.kt
│ │ │ │ ├── PaymentCancelErrorDecoder.kt
│ │ │ │ ├── PaymentConfirmErrorDecoder.kt
│ │ │ │ ├── PaymentCreateErrorDecoder.kt
│ │ │ │ ├── PaymentsCancelConfig.kt
│ │ │ │ ├── PaymentsConfirmConfig.kt
│ │ │ │ ├── PaymentsCreateConfig.kt
│ │ │ │ ├── TossErrorDecoder.kt
│ │ │ │ ├── TossHeaderConfig.kt
│ │ │ │ ├── TransactionGetConfig.kt
│ │ │ │ └── TransactionGetErrorDecoder.kt
│ │ │ ├── dto/
│ │ │ │ ├── request/
│ │ │ │ │ ├── CancelPaymentsRequest.kt
│ │ │ │ │ ├── ConfirmPaymentsRequest.kt
│ │ │ │ │ └── CreatePaymentsRequest.kt
│ │ │ │ └── response/
│ │ │ │ ├── CardAcquireStatus.kt
│ │ │ │ ├── CardCode.kt
│ │ │ │ ├── EasyPayCode.kt
│ │ │ │ ├── FeeCode.kt
│ │ │ │ ├── PaymentCheckout.kt
│ │ │ │ ├── PaymentEasyPay.kt
│ │ │ │ ├── PaymentReceipt.kt
│ │ │ │ ├── PaymentStatus.kt
│ │ │ │ ├── PaymentsCancels.kt
│ │ │ │ ├── PaymentsCard.kt
│ │ │ │ ├── PaymentsCardPromotion.kt
│ │ │ │ ├── PaymentsCashReceipt.kt
│ │ │ │ ├── PaymentsFailure.kt
│ │ │ │ ├── PaymentsResponse.kt
│ │ │ │ ├── SettlementFeeDto.kt
│ │ │ │ ├── SettlementResponse.kt
│ │ │ │ └── TossPaymentMethod.kt
│ │ │ └── exception/
│ │ │ ├── PaymentsCancelErrorCode.kt
│ │ │ ├── PaymentsConfirmErrorCode.kt
│ │ │ ├── PaymentsCreateErrorCode.kt
│ │ │ ├── PaymentsEnumNotMatchException.kt
│ │ │ ├── PaymentsUnHandleException.kt
│ │ │ ├── TossPaymentsErrorDto.kt
│ │ │ └── TransactionGetErrorCode.kt
│ │ └── resources/
│ │ ├── application-infrastructure.yml
│ │ └── templates/
│ │ ├── entranceIssuedTicket.html
│ │ ├── eventSettlement.html
│ │ ├── fragments/
│ │ │ ├── button.html
│ │ │ ├── divider.html
│ │ │ ├── footer.html
│ │ │ ├── header.html
│ │ │ ├── issuedTicketInfo.html
│ │ │ ├── orderInfo.html
│ │ │ ├── subTilte.html
│ │ │ └── title.html
│ │ ├── hostInvite.html
│ │ ├── layouts/
│ │ │ └── mailFormat.html
│ │ ├── orderApproveConfirm.html
│ │ ├── orderApproveRequest.html
│ │ ├── orderPaymentDone.html
│ │ ├── orderWithdrawCancel.html
│ │ ├── orderWithdrawRefund.html
│ │ ├── settlement.html
│ │ └── signUp.html
│ └── test/
│ ├── java/
│ │ └── band/
│ │ └── gosrock/
│ │ └── infrastructure/
│ │ ├── InfraIntegrateProfileResolver.java
│ │ ├── InfraIntegrateSpringBootTest.java
│ │ ├── InfraIntegrateTestConfig.java
│ │ ├── config/
│ │ │ ├── redis/
│ │ │ │ └── AutoConfigureTestFeign.java
│ │ │ └── s3/
│ │ │ └── S3UploadPresignedUrlServiceTest.java
│ │ └── outer/
│ │ └── api/
│ │ └── tossPayments/
│ │ ├── client/
│ │ │ ├── PaymentsCancelClientTest.java
│ │ │ ├── TossPaymentsClientTest.java
│ │ │ └── TossSettlementClientTest.java
│ │ └── config/
│ │ └── PaymentConfirmErrorDecoderTest.java
│ └── resources/
│ ├── application.yml
│ ├── logback-test.xml
│ └── payload/
│ └── settlement-response.json
├── LICENSE
├── README.md
├── build.gradle.kts
├── docker-compose.yml
├── e2e-tests/
│ ├── .gitignore
│ ├── README.md
│ ├── conftest.py
│ ├── requirements.txt
│ ├── test_01_auth.py
│ ├── test_02_host.py
│ ├── test_03_event.py
│ ├── test_04_ticket_item.py
│ ├── test_05_order_flow.py
│ ├── test_06_issued_ticket.py
│ ├── test_07_comment.py
│ ├── test_08_refund.py
│ ├── test_09_error_cases.py
│ ├── test_10_event_lifecycle.py
│ ├── test_11_multi_user.py
│ ├── test_12_host_management.py
│ ├── test_13_user_profile.py
│ ├── test_14_ticket_stock.py
│ ├── test_15_event_crud.py
│ ├── test_16_event_open_conditions.py
│ ├── test_17_event_modification_rules.py
│ ├── test_18_event_status_matrix.py
│ ├── test_19_host_invite.py
│ ├── test_20_ticket_options.py
│ ├── test_21_dudoong_ticket.py
│ ├── test_22_order_detail_verification.py
│ ├── test_23_refund_edge_cases.py
│ ├── test_24_admin_features.py
│ ├── test_25_coupon_flow.py
│ ├── test_26_order_edge_cases.py
│ ├── test_27_host_role_change.py
│ ├── test_28_ticket_quantity_public.py
│ ├── test_29_order_cancel.py
│ ├── test_30_order_before_open.py
│ ├── test_31_admin_auth_role.py
│ ├── test_32_admin_token_separation.py
│ ├── test_33_host_role_authorization.py
│ ├── test_34_admin_usecase_authorization.py
│ ├── test_35_user_nickname.py
│ ├── test_36_host_master_transfer.py
│ ├── test_37_order_cancel_reason.py
│ └── test_38_refund_api.py
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── lombok.config
├── scripts/
│ └── local-start.sh
└── settings.gradle.kts
================================================
FILE CONTENTS
================================================
================================================
FILE: .claude/settings.local.json
================================================
{
"permissions": {
"allow": [
"mcp__mysql__mysql_query"
]
}
}
================================================
FILE: .github/CODEOWNERS
================================================
* @ImNM @sanbonai06 @cofls6581 @gengminy @kim-wonjin
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## 개요
- close #issueNumber
## 작업사항
- 내용을 적어주세요.
## 변경로직
- 내용을 적어주세요.
================================================
FILE: .github/workflows/BuildApiServer.yml
================================================
name: Build Api Server
on:
push:
tags:
- Api-v*.*.*
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java-version: [ 21 ]
outputs:
version: ${{ steps.get_version.outputs.BRANCH_NAME }}
steps:
- name: Check Out The Repository
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java-version }}
distribution: 'temurin'
- name: Get the version
id: get_version
run: |
RELEASE_VERSION_WITHOUT_V="$(cut -d'v' -f2 <<< ${GITHUB_REF#refs/*/})"
echo "VERSION=$RELEASE_VERSION_WITHOUT_V" >> $GITHUB_OUTPUT
#테스트 수행용 도커 컴포즈
- name: Start containers
run: docker compose up -d
- name: Gradle Build
uses: gradle/gradle-build-action@v3
- name: Execute Gradle build
run: ./gradlew :DuDoong-Api:build --no-daemon
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: ./DuDoong-Api
push: true
tags: water0641/dudoong-api:${{ steps.get_version.outputs.VERSION }}
================================================
FILE: .github/workflows/BuildBatchServer.yml
================================================
name: Build Batch Server
on:
push:
tags:
- Batch-v*.*.*
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java-version: [ 21 ]
outputs:
version: ${{ steps.get_version.outputs.BRANCH_NAME }}
steps:
- name: Check Out The Repository
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java-version }}
distribution: 'temurin'
- name: Get the version
id: get_version
run: |
RELEASE_VERSION_WITHOUT_V="$(cut -d'v' -f2 <<< ${GITHUB_REF#refs/*/})"
echo "VERSION=$RELEASE_VERSION_WITHOUT_V" >> $GITHUB_OUTPUT
#테스트 수행용 도커 컴포즈
- name: Start containers
run: docker compose up -d
- name: Gradle Build
uses: gradle/gradle-build-action@v3
- name: Execute Gradle build
run: ./gradlew :DuDoong-Batch:build --no-daemon
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: ./DuDoong-Batch
push: true
tags: |
water0641/dudoong-batch:${{ steps.get_version.outputs.VERSION }}
water0641/dudoong-batch:latest
================================================
FILE: .github/workflows/BuildSocketServer.yml
================================================
name: Build Socket Server
on:
push:
tags:
- Socket-v*.*.*
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java-version: [ 17 ]
outputs:
version: ${{ steps.get_version.outputs.BRANCH_NAME }}
steps:
- name: Check Out The Repository
uses: actions/checkout@v3
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java-version }}
distribution: 'corretto'
- name: Get the version
id: get_version
run: |
RELEASE_VERSION_WITHOUT_V="$(cut -d'v' -f2 <<< ${GITHUB_REF#refs/*/})"
echo ::set-output name=VERSION::$RELEASE_VERSION_WITHOUT_V
#테스트 수행용 도커 컴포즈
- name: Start containers
run: docker compose up -d
- name: Gradle Build
uses: gradle/gradle-build-action@v2
- name: Execute Gradle build
run: ./gradlew :DuDoong-Socket:build --no-daemon
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: ./DuDoong-Socket
push: true
tags: water0641/dudoong-socket:${{ steps.get_version.outputs.VERSION }}
================================================
FILE: .github/workflows/ci.yml
================================================
name: ci
on:
pull_request:
branch: 'dev'
jobs:
ktlintCheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: SetUp JDK 21
uses: actions/setup-java@v4
with:
java-version: "21"
distribution: 'temurin'
- name: Gradle Caching
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
- name: ktlint check
run: ./gradlew ktlintCheck
================================================
FILE: .gitignore
================================================
DuDoong-Domain/HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
.env
.env.*
*.xlsx
**/src/main/generated/
.omc/
================================================
FILE: CLAUDE.md
================================================
# DuDoong Backend - Claude 컨텍스트
## 🎯 현재 진행 중인 작업
**Java → Kotlin 전체 마이그레이션**
마스터 트래킹 이슈: https://github.com/Gosrock/DuDoong-Backend/issues/582
---
## 🏗️ 프로젝트 아키텍처
### 기술 스택
- **언어**: Kotlin 1.9.22 (Java → Kotlin 마이그레이션 완료)
- **프레임워크**: Spring Boot 3.2.0
- **런타임**: Java 21
- **빌드**: Gradle 8.5 Kotlin DSL
- **DB**: MySQL + Spring Data JPA + QueryDSL
- **캐시/락**: Redis + Redisson (분산락)
- **외부 API**: Toss Payments (OpenFeign), AWS S3/SES, NCP AlimTalk, Slack API
- **인증**: Spring Security + JWT + Kakao OAuth
- **문서화**: SpringDoc OpenAPI (Swagger)
- **배치**: Spring Batch
- **실시간**: Spring WebSocket (STOMP)
- **코드 품질**: SonarQube, Spotless (google-java-format), Jacoco
### 멀티모듈 구조 (의존성 순서)
```
DuDoong-Backend/
├── DuDoong-Common/ # 최하위: JWT, 어노테이션, 예외처리, DTO, 유틸
├── DuDoong-Infrastructure/ # Common 의존: Redis, S3, SES, Feign, Slack, NCP
├── DuDoong-Domain/ # Common+Infra 의존: JPA 엔티티, 도메인 서비스, AOP
├── DuDoong-Api/ # 모든 모듈 의존: REST API, Security, Swagger
├── DuDoong-Socket/ # 모든 모듈 의존: WebSocket STOMP 서버
└── DuDoong-Batch/ # 모든 모듈 의존: Spring Batch 정산/알림/엑셀
```
### 도메인 영역 (DuDoong-Domain/domains/)
| 도메인 | 설명 |
|--------|------|
| `user` | 사용자 (Kakao OAuth 로그인) |
| `host` | 이벤트 주최자 |
| `event` | 이벤트 |
| `ticket_item` | 티켓 종류 |
| `order` | 주문/결제 (Toss Payments) |
| `cart` | 장바구니 |
| `coupon` | 쿠폰 |
| `issuedTicket` | 발급 티켓 (QR 입장 검증) |
| `settlement` | 정산 |
| `comment` | 댓글 |
### API 컨트롤러 목록
- `AuthController` - 카카오 OAuth 인증
- `UserController` - 유저 관리
- `HostController` - 호스트 관리
- `EventController` - 이벤트 CRUD
- `TicketItemController` / `TicketOptionController` - 티켓 종류
- `OrderController` / `OrderAdminController` - 주문/결제
- `CartController` - 장바구니
- `CouponController` - 쿠폰
- `IssuedTicketController` / `AdminIssuedTicketController` - 발급 티켓
- `ImageController` - S3 Pre-signed URL 이미지 업로드
- `AdminStatisticController` - 통계
- `CommentController` - 댓글
### GitHub 저장소
- **Remote**: https://github.com/Gosrock/DuDoong-Backend.git
- **기본 브랜치**: `dev`
---
## 🚀 Kotlin 마이그레이션 진행 상황
### 마이그레이션 전략: Bottom-up 방식
```
Phase 0 → Phase 1 → Phase 2 → Phase 3 → Phase 4
(Gradle) (Common) (Infra) (Domain) (Api)
↘ Phase 5 (Socket)
↘ Phase 6 (Batch)
```
### 이슈 목록
| Phase | 이슈 | 내용 | 상태 |
|-------|------|------|------|
| Master | [#582](https://github.com/Gosrock/DuDoong-Backend/issues/582) | 전체 트래킹 | 🔵 진행예정 |
| Phase 0 | [#583](https://github.com/Gosrock/DuDoong-Backend/issues/583) | Gradle Kotlin DSL 전환 | ⬜ 대기 |
| Phase 1-1 | [#584](https://github.com/Gosrock/DuDoong-Backend/issues/584) | Common: 어노테이션 & 유틸 | ⬜ 대기 |
| Phase 1-2 | [#585](https://github.com/Gosrock/DuDoong-Backend/issues/585) | Common: 예외처리 & DTO | ⬜ 대기 |
| Phase 1-3 | [#586](https://github.com/Gosrock/DuDoong-Backend/issues/586) | Common: JWT & Properties | ⬜ 대기 |
| Phase 2-1 | [#587](https://github.com/Gosrock/DuDoong-Backend/issues/587) | Infra: Redis & Redisson | ⬜ 대기 |
| Phase 2-2 | [#588](https://github.com/Gosrock/DuDoong-Backend/issues/588) | Infra: AWS S3 & SES | ⬜ 대기 |
| Phase 2-3 | [#589](https://github.com/Gosrock/DuDoong-Backend/issues/589) | Infra: Toss Payments Feign | ⬜ 대기 |
| Phase 2-4 | [#590](https://github.com/Gosrock/DuDoong-Backend/issues/590) | Infra: Slack & AlimTalk | ⬜ 대기 |
| Phase 3-1 | [#591](https://github.com/Gosrock/DuDoong-Backend/issues/591) | Domain: AOP & 도메인 이벤트 | ⬜ 대기 |
| Phase 3-2 | [#592](https://github.com/Gosrock/DuDoong-Backend/issues/592) | Domain: User & Host | ⬜ 대기 |
| Phase 3-3 | [#593](https://github.com/Gosrock/DuDoong-Backend/issues/593) | Domain: Event & TicketItem | ⬜ 대기 |
| Phase 3-4 | [#594](https://github.com/Gosrock/DuDoong-Backend/issues/594) | Domain: Order & Cart | ⬜ 대기 |
| Phase 3-5 | [#595](https://github.com/Gosrock/DuDoong-Backend/issues/595) | Domain: IssuedTicket & Coupon | ⬜ 대기 |
| Phase 3-6 | [#596](https://github.com/Gosrock/DuDoong-Backend/issues/596) | Domain: Settlement & Comment | ⬜ 대기 |
| Phase 4-1 | [#597](https://github.com/Gosrock/DuDoong-Backend/issues/597) | Api: Spring Security & Auth | ⬜ 대기 |
| Phase 4-2 | [#598](https://github.com/Gosrock/DuDoong-Backend/issues/598) | Api: User, Host, Event | ⬜ 대기 |
| Phase 4-3 | [#599](https://github.com/Gosrock/DuDoong-Backend/issues/599) | Api: Order, Cart, Coupon | ⬜ 대기 |
| Phase 4-4 | [#600](https://github.com/Gosrock/DuDoong-Backend/issues/600) | Api: IssuedTicket, TicketItem | ⬜ 대기 |
| Phase 4-5 | [#601](https://github.com/Gosrock/DuDoong-Backend/issues/601) | Api: Image, Statistic, 공통 | ⬜ 대기 |
| Phase 5 | [#602](https://github.com/Gosrock/DuDoong-Backend/issues/602) | Socket: WebSocket 서버 | ⬜ 대기 |
| Phase 6-1 | [#603](https://github.com/Gosrock/DuDoong-Backend/issues/603) | Batch: 정산 Job | ⬜ 대기 |
| Phase 6-2 | [#604](https://github.com/Gosrock/DuDoong-Backend/issues/604) | Batch: 만료/엑셀/Slack 통계 | ⬜ 대기 |
---
## 🔐 인증 및 권한 체계
### 인증 방식
- **로그인**: 카카오 OAuth (`/api/v1/auth/oauth/kakao`) 또는 로컬 개발용 (`/api/v1/auth/oauth/local/login`, dev 전용)
- **토큰 전달**: `accessToken` 쿠키 (기본) 또는 `Authorization: Bearer` 헤더
- **토큰 갱신**: `POST /api/v1/auth/token/refresh`
### 역할 (AccountRole)
| 역할 | 설명 |
|------|------|
| `USER` | 일반 유저 |
| `ADMIN` | 어드민 (내부 관리자) |
| `SUPER_ADMIN` | 최고 관리자 |
- **MANAGER 역할은 삭제됨** (호스트 멤버십의 HostRole과 혼동 방지)
- Role hierarchy: `SUPER_ADMIN > ADMIN > USER`
### API 접근 제어 (SecurityConfig)
| 경로 | 접근 권한 |
|------|----------|
| `/api/v1/auth/oauth/**` | permitAll |
| `/api/v1/events/{id}` (GET) | permitAll |
| `/api/v1/events/search` (GET) | permitAll |
| `/internal-api/**` | **ADMIN, SUPER_ADMIN만** |
| 나머지 `/api/**` | USER 이상 (인증 필수) |
### 호스트 권한 (HostRole AOP)
- `@HostRolesAllowed` AOP로 호스트 멤버십 기반 권한 체크
- **userId를 메서드 파라미터로 명시적 전달** (SecurityContext 미사용)
- `SUPER_ADMIN`은 호스트 멤버가 아니어도 모든 이벤트/호스트 접근 가능 (바이패스)
- HostQualification: `MASTER` > `MANAGER` > `GUEST`
### Admin API (internal-api)
- SecurityConfig 레벨: ADMIN/SUPER_ADMIN role 체크
- UseCase 레벨: `AdminAuthValidator`로 이중 권한 체크
- 읽기: `validateAdminOrAbove` (ADMIN+)
- 쓰기: `validateAdminOrAbove` (ADMIN+)
- 역할 변경: `validateSuperAdmin` (SUPER_ADMIN만)
- 어드민 전용 로그인 엔드포인트 없음 — 일반 로그인 쿠키 사용
### 어드민 토큰 관련 (레거시)
- `X-Admin-Token` 헤더, `aud:admin` JWT claim — **사용하지 않음**
- `AdminLoginUseCase`, `AdminLocalDevLoginUseCase` — **삭제됨**
- 어드민 접근은 쿠키 기반 + DB role 체크로 통일
---
## 🧪 로컬 개발 & E2E 테스트
### 서버 기동 (로컬 MySQL 사용)
```bash
# docker-compose 먼저 (MySQL + Redis)
docker compose up -d
# local 프로필로 기동 — 반드시 이 방식으로!
./gradlew :DuDoong-Api:bootRun --args='--spring.profiles.active=local,infrastructure,domain,domain-local,common,common-local'
```
**주의**: `local` 프로필 없이 기동하면 **H2 인메모리 DB**를 사용하게 되어 MySQL과 불일치 발생.
- `spring.profiles.group.local` = `infrastructure, domain-local, common-local`
- `domain-local` 프로필이 `jdbc:mysql://127.0.0.1:13306/dudoong` 설정
### E2E 테스트 (Python pytest)
```bash
cd e2e-tests
pytest -v # 전체 실행
pytest test_33* test_34* -v # 권한 테스트만
```
### 디버깅 팁
- API 안 되면 **프로필 먼저 확인** (`profiles are active` 로그)
- DB 연결 확인: 로그에서 `jdbc:mysql` vs `jdbc:h2:mem` 확인
- 403 나오면: DB에서 `account_role` 확인 + `hasAnyRole` 매칭 확인
- 한번에 안 되면 **curl로 한 단계씩 확인** (로그인 → DB 확인 → role 변경 → API 호출)
---
## 🔑 Kotlin 마이그레이션 핵심 원칙
1. **Lombok 제거**: `@Data` → `data class`, `@Builder` → named params + `copy()`, `@RequiredArgsConstructor` → 주생성자
2. **Null Safety**: `@Nullable`/`@NonNull` → Kotlin `?` / non-null 타입
3. **KAPT**: QueryDSL Q클래스 생성을 APT → KAPT로 전환
4. **JPA 엔티티**: `open class` 필수 (`allOpen` plugin), 기본 생성자 자동생성 (`noArg` plugin)
5. **Jackson**: `jackson-module-kotlin` 등록 필수
6. **Bean Validation**: `@field:NotBlank` 접두사 필수 (Kotlin data class)
7. **Spring Security**: Kotlin lambda DSL 활용
## 📋 PR 규칙
- 브랜치명: `feature/kotlin-migration-phase-{N}` 또는 `feature/kotlin-migration-phase-{N}-{name}`
- PR 본문: `close #이슈번호` 포함
- 각 Phase 완료 후 빌드(`./gradlew :{Module}:build`) 확인 필수
## ⚠️ 주요 주의사항
- Java ↔ Kotlin 혼재 허용 (마이그레이션 기간 중)
- CI (GitHub Actions) 빌드 항상 유지
- `lombok.config` 파일 → 최종 완료 시 삭제
- SonarQube 설정 → 마지막에 Kotlin 소스로 업데이트
================================================
FILE: DuDoong-Admin/build.gradle.kts
================================================
tasks.bootJar { enabled = false }
tasks.jar { enabled = true }
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0")
implementation(project(":DuDoong-Domain"))
implementation(project(":DuDoong-Common"))
implementation("org.apache.poi:poi:5.2.0")
implementation("org.apache.poi:poi-ooxml:5.2.0")
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminCommentController.kt
================================================
package band.gosrock.admin.controller
import band.gosrock.admin.model.dto.response.AdminCommentResponse
import band.gosrock.admin.service.AdminDeleteCommentUseCase
import band.gosrock.admin.service.AdminGetCommentsUseCase
import band.gosrock.common.annotation.CurrentUserId
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.web.PageableDefault
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/internal-api/v1/comments")
@SecurityRequirement(name = "admin-token")
@Tag(name = "Admin")
class AdminCommentController(
private val adminGetCommentsUseCase: AdminGetCommentsUseCase,
private val adminDeleteCommentUseCase: AdminDeleteCommentUseCase,
) {
@Operation(summary = "댓글 목록을 조회합니다.")
@GetMapping
fun getComments(
@CurrentUserId userId: Long,
@RequestParam(required = false) keyword: String?,
@RequestParam(required = false) eventId: Long?,
@PageableDefault(size = 20) pageable: Pageable,
): Page<AdminCommentResponse> {
return adminGetCommentsUseCase.execute(userId, keyword, eventId, pageable)
}
@Operation(summary = "댓글을 삭제합니다. (소프트 삭제)")
@DeleteMapping("/{commentId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun deleteComment(@CurrentUserId userId: Long, @PathVariable commentId: Long) {
adminDeleteCommentUseCase.execute(userId, commentId)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminDashboardController.kt
================================================
package band.gosrock.admin.controller
import band.gosrock.admin.model.dto.response.DashboardResponse
import band.gosrock.admin.service.GetDashboardUseCase
import band.gosrock.common.annotation.CurrentUserId
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import java.time.LocalDate
import org.springframework.format.annotation.DateTimeFormat
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/internal-api/v1")
@SecurityRequirement(name = "admin-token")
@Tag(name = "Admin")
class AdminDashboardController(
private val getDashboardUseCase: GetDashboardUseCase,
) {
@Operation(summary = "어드민 대시보드 통계를 조회합니다.")
@GetMapping("/dashboard")
fun getDashboard(
@CurrentUserId userId: Long,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
): DashboardResponse {
return getDashboardUseCase.execute(userId, startDate, endDate)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminEventController.kt
================================================
package band.gosrock.admin.controller
import band.gosrock.admin.model.dto.request.AdminAdjustTicketStockRequest
import band.gosrock.admin.model.dto.request.AdminUpdateEventRequest
import band.gosrock.admin.model.dto.request.AdminUpdateEventStatusRequest
import band.gosrock.admin.model.dto.request.AdminUpdateTicketItemRequest
import band.gosrock.admin.model.dto.response.AdminEventResponse
import band.gosrock.admin.model.dto.response.AdminIssuedTicketResponse
import band.gosrock.admin.model.dto.response.AdminTicketItemResponse
import band.gosrock.admin.service.AdminAdjustTicketStockUseCase
import band.gosrock.admin.service.AdminDeleteEventUseCase
import band.gosrock.admin.service.AdminExcelService
import band.gosrock.admin.service.AdminExportIssuedTicketsUseCase
import band.gosrock.admin.service.AdminGetEventDetailUseCase
import band.gosrock.admin.service.AdminGetEventsUseCase
import band.gosrock.admin.service.AdminGetIssuedTicketsUseCase
import band.gosrock.admin.service.AdminGetTicketItemsUseCase
import band.gosrock.admin.service.AdminUpdateEventStatusUseCase
import band.gosrock.admin.service.AdminUpdateEventUseCase
import band.gosrock.admin.service.AdminUpdateTicketItemUseCase
import band.gosrock.common.annotation.CurrentUserId
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.web.PageableDefault
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/internal-api/v1/events")
@SecurityRequirement(name = "admin-token")
@Tag(name = "Admin")
class AdminEventController(
private val adminGetEventsUseCase: AdminGetEventsUseCase,
private val adminGetEventDetailUseCase: AdminGetEventDetailUseCase,
private val adminDeleteEventUseCase: AdminDeleteEventUseCase,
private val adminUpdateEventStatusUseCase: AdminUpdateEventStatusUseCase,
private val adminUpdateEventUseCase: AdminUpdateEventUseCase,
private val adminGetIssuedTicketsUseCase: AdminGetIssuedTicketsUseCase,
private val adminExcelService: AdminExcelService,
private val adminExportIssuedTicketsUseCase: AdminExportIssuedTicketsUseCase,
private val adminGetTicketItemsUseCase: AdminGetTicketItemsUseCase,
private val adminUpdateTicketItemUseCase: AdminUpdateTicketItemUseCase,
private val adminAdjustTicketStockUseCase: AdminAdjustTicketStockUseCase,
) {
@Operation(summary = "이벤트 목록을 엑셀로 다운로드합니다.")
@GetMapping("/export")
fun exportEvents(
@CurrentUserId userId: Long,
@RequestParam(required = false) keyword: String?,
@RequestParam(required = false) status: String?,
): ResponseEntity<ByteArray> {
val events = adminGetEventsUseCase.executeAll(userId, keyword, status)
val bytes = adminExcelService.generateEventsExcel(events)
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=events.xlsx")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(bytes)
}
@Operation(summary = "이벤트 목록을 조회합니다.")
@GetMapping
fun getEvents(
@CurrentUserId userId: Long,
@RequestParam(required = false) keyword: String?,
@RequestParam(required = false) status: String?,
@PageableDefault(size = 20) pageable: Pageable,
): Page<AdminEventResponse> {
return adminGetEventsUseCase.execute(userId, keyword, status, pageable)
}
@Operation(summary = "이벤트 상세 정보를 조회합니다.")
@GetMapping("/{eventId}")
fun getEventDetail(@CurrentUserId userId: Long, @PathVariable eventId: Long): AdminEventResponse {
return adminGetEventDetailUseCase.execute(userId, eventId)
}
@Operation(summary = "이벤트를 소프트 삭제합니다.")
@DeleteMapping("/{eventId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun deleteEvent(@CurrentUserId userId: Long, @PathVariable eventId: Long) {
adminDeleteEventUseCase.execute(userId, eventId)
}
@Operation(summary = "이벤트 상태를 변경합니다. (어드민 전용, 밸리데이션 우회)")
@PatchMapping("/{eventId}/status")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun updateEventStatus(
@CurrentUserId userId: Long,
@PathVariable eventId: Long,
@RequestBody request: AdminUpdateEventStatusRequest,
) {
adminUpdateEventStatusUseCase.execute(userId, eventId, request)
}
@Operation(summary = "이벤트 정보를 수정합니다. (어드민 전용, OPEN 상태에서도 수정 가능)")
@PatchMapping("/{eventId}")
fun updateEvent(
@CurrentUserId userId: Long,
@PathVariable eventId: Long,
@RequestBody request: AdminUpdateEventRequest,
): AdminEventResponse {
return adminUpdateEventUseCase.execute(userId, eventId, request)
}
@Operation(summary = "이벤트별 발급 티켓 목록을 조회합니다.")
@GetMapping("/{eventId}/issued-tickets")
fun getIssuedTickets(
@CurrentUserId userId: Long,
@PathVariable eventId: Long,
@PageableDefault(size = 20) pageable: Pageable,
): Page<AdminIssuedTicketResponse> {
return adminGetIssuedTicketsUseCase.execute(userId, eventId, pageable)
}
@Operation(summary = "이벤트별 티켓 종류 목록을 조회합니다.")
@GetMapping("/{eventId}/ticket-items")
fun getTicketItems(@CurrentUserId userId: Long, @PathVariable eventId: Long): List<AdminTicketItemResponse> {
return adminGetTicketItemsUseCase.execute(userId, eventId)
}
@Operation(summary = "이벤트별 티켓 종류 목록을 엑셀로 다운로드합니다.")
@GetMapping("/{eventId}/ticket-items/export")
fun exportTicketItems(@CurrentUserId userId: Long, @PathVariable eventId: Long): ResponseEntity<ByteArray> {
val items = adminGetTicketItemsUseCase.execute(userId, eventId)
val bytes = adminExcelService.generateTicketItemsExcel(items)
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=ticket-items-${eventId}.xlsx")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(bytes)
}
@Operation(summary = "이벤트별 발급 티켓 목록을 엑셀로 다운로드합니다. (옵션 응답 동적 컬럼 포함)")
@GetMapping("/{eventId}/issued-tickets/export")
fun exportIssuedTickets(@CurrentUserId userId: Long, @PathVariable eventId: Long): ResponseEntity<ByteArray> {
val bytes = adminExportIssuedTicketsUseCase.execute(userId, eventId)
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=issued-tickets-${eventId}.xlsx")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(bytes)
}
@Operation(summary = "티켓 종류를 수정합니다. (어드민 전용, 이벤트 상태 체크 없이 수정 가능)")
@PatchMapping("/{eventId}/ticket-items/{ticketItemId}")
fun updateTicketItem(
@CurrentUserId userId: Long,
@PathVariable eventId: Long,
@PathVariable ticketItemId: Long,
@RequestBody request: AdminUpdateTicketItemRequest,
): AdminTicketItemResponse {
return adminUpdateTicketItemUseCase.execute(userId, eventId, ticketItemId, request)
}
@Operation(summary = "티켓 재고를 조정합니다. (어드민 전용, delta 양수=증가 음수=감소)")
@PostMapping("/{eventId}/ticket-items/{ticketItemId}/adjust-stock")
fun adjustTicketStock(
@CurrentUserId userId: Long,
@PathVariable eventId: Long,
@PathVariable ticketItemId: Long,
@RequestBody request: AdminAdjustTicketStockRequest,
): AdminTicketItemResponse {
return adminAdjustTicketStockUseCase.execute(userId, ticketItemId, request.delta)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminHostController.kt
================================================
package band.gosrock.admin.controller
import band.gosrock.admin.model.dto.request.AdminAddHostMemberRequest
import band.gosrock.admin.model.dto.request.AdminTransferMasterRequest
import band.gosrock.admin.model.dto.request.AdminUpdateHostMemberRoleRequest
import band.gosrock.admin.model.dto.request.AdminUpdateHostPartnerRequest
import band.gosrock.admin.model.dto.request.AdminUpdateHostProfileRequest
import band.gosrock.admin.model.dto.response.AdminEventResponse
import band.gosrock.admin.model.dto.response.AdminHostDetailResponse
import band.gosrock.admin.model.dto.response.AdminHostMemberResponse
import band.gosrock.admin.model.dto.response.AdminHostResponse
import band.gosrock.admin.service.AdminAddHostMemberUseCase
import band.gosrock.admin.service.AdminTransferMasterUseCase
import band.gosrock.admin.service.AdminGetHostDetailUseCase
import band.gosrock.admin.service.AdminGetHostEventsUseCase
import band.gosrock.admin.service.AdminGetHostMembersUseCase
import band.gosrock.admin.service.AdminGetHostsUseCase
import band.gosrock.admin.service.AdminRemoveHostMemberUseCase
import band.gosrock.admin.service.AdminUpdateHostMemberRoleUseCase
import band.gosrock.admin.service.AdminUpdateHostPartnerUseCase
import band.gosrock.admin.service.AdminUpdateHostProfileUseCase
import band.gosrock.common.annotation.CurrentUserId
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.web.PageableDefault
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/internal-api/v1/hosts")
@SecurityRequirement(name = "admin-token")
@Tag(name = "Admin")
class AdminHostController(
private val adminGetHostsUseCase: AdminGetHostsUseCase,
private val adminGetHostDetailUseCase: AdminGetHostDetailUseCase,
private val adminGetHostMembersUseCase: AdminGetHostMembersUseCase,
private val adminUpdateHostMemberRoleUseCase: AdminUpdateHostMemberRoleUseCase,
private val adminAddHostMemberUseCase: AdminAddHostMemberUseCase,
private val adminRemoveHostMemberUseCase: AdminRemoveHostMemberUseCase,
private val adminGetHostEventsUseCase: AdminGetHostEventsUseCase,
private val adminUpdateHostPartnerUseCase: AdminUpdateHostPartnerUseCase,
private val adminUpdateHostProfileUseCase: AdminUpdateHostProfileUseCase,
private val adminTransferMasterUseCase: AdminTransferMasterUseCase,
) {
@Operation(summary = "호스트 목록을 조회합니다.")
@GetMapping
fun getHosts(
@CurrentUserId userId: Long,
@RequestParam(required = false) keyword: String?,
@PageableDefault(size = 20) pageable: Pageable,
): Page<AdminHostResponse> {
return adminGetHostsUseCase.execute(userId, keyword, pageable)
}
@Operation(summary = "호스트 상세 정보를 조회합니다.")
@GetMapping("/{hostId}")
fun getHostDetail(@CurrentUserId userId: Long, @PathVariable hostId: Long): AdminHostDetailResponse {
return adminGetHostDetailUseCase.execute(userId, hostId)
}
@Operation(summary = "호스트 소속 멤버 목록을 조회합니다.")
@GetMapping("/{hostId}/members")
fun getHostMembers(@CurrentUserId userId: Long, @PathVariable hostId: Long): List<AdminHostMemberResponse> {
return adminGetHostMembersUseCase.execute(userId, hostId)
}
@Operation(summary = "호스트 멤버 역할을 변경합니다.")
@PatchMapping("/{hostId}/members/{targetUserId}/role")
fun updateHostMemberRole(
@CurrentUserId userId: Long,
@PathVariable hostId: Long,
@PathVariable targetUserId: Long,
@RequestBody request: AdminUpdateHostMemberRoleRequest,
): AdminHostMemberResponse {
return adminUpdateHostMemberRoleUseCase.execute(userId, hostId, targetUserId, request)
}
@Operation(summary = "호스트에 멤버를 추가합니다.")
@PostMapping("/{hostId}/members")
@ResponseStatus(HttpStatus.CREATED)
fun addHostMember(
@CurrentUserId userId: Long,
@PathVariable hostId: Long,
@RequestBody request: AdminAddHostMemberRequest,
): AdminHostMemberResponse {
return adminAddHostMemberUseCase.execute(userId, hostId, request)
}
@Operation(summary = "호스트에서 멤버를 제거합니다.")
@DeleteMapping("/{hostId}/members/{targetUserId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun removeHostMember(
@CurrentUserId userId: Long,
@PathVariable hostId: Long,
@PathVariable targetUserId: Long,
) {
adminRemoveHostMemberUseCase.execute(userId, hostId, targetUserId)
}
@Operation(summary = "호스트별 이벤트 목록을 조회합니다.")
@GetMapping("/{hostId}/events")
fun getHostEvents(
@CurrentUserId userId: Long,
@PathVariable hostId: Long,
@PageableDefault(size = 20) pageable: Pageable,
): Page<AdminEventResponse> {
return adminGetHostEventsUseCase.execute(userId, hostId, pageable)
}
@Operation(summary = "호스트의 파트너 여부를 변경합니다.")
@PatchMapping("/{hostId}/partner")
fun updateHostPartner(
@CurrentUserId userId: Long,
@PathVariable hostId: Long,
@RequestBody request: AdminUpdateHostPartnerRequest,
): AdminHostDetailResponse {
return adminUpdateHostPartnerUseCase.execute(userId, hostId, request)
}
@Operation(summary = "호스트 프로필을 수정합니다.")
@PatchMapping("/{hostId}/profile")
fun updateHostProfile(
@CurrentUserId userId: Long,
@PathVariable hostId: Long,
@RequestBody request: AdminUpdateHostProfileRequest,
): AdminHostDetailResponse {
return adminUpdateHostProfileUseCase.execute(userId, hostId, request)
}
@Operation(summary = "호스트 마스터 권한을 강제 양도합니다. (어드민)")
@PostMapping("/{hostId}/transfer-master")
fun transferMaster(
@CurrentUserId userId: Long,
@PathVariable hostId: Long,
@RequestBody request: AdminTransferMasterRequest,
): AdminHostDetailResponse {
return adminTransferMasterUseCase.execute(userId, hostId, request)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminOrderController.kt
================================================
package band.gosrock.admin.controller
import band.gosrock.admin.model.dto.request.AdminCancelOrderRequest
import band.gosrock.admin.model.dto.request.AdminRefundStatusRequest
import band.gosrock.admin.model.dto.response.AdminOrderResponse
import band.gosrock.admin.service.AdminCancelOrderUseCase
import band.gosrock.admin.service.AdminExcelService
import band.gosrock.admin.service.AdminGetOrderDetailUseCase
import band.gosrock.admin.service.AdminGetOrdersUseCase
import band.gosrock.admin.service.AdminUpdateRefundStatusUseCase
import band.gosrock.common.annotation.CurrentUserId
import band.gosrock.domain.domains.order.domain.OrderStatus
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.validation.Valid
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.web.PageableDefault
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/internal-api/v1/orders")
@SecurityRequirement(name = "admin-token")
@Tag(name = "Admin")
class AdminOrderController(
private val adminGetOrdersUseCase: AdminGetOrdersUseCase,
private val adminGetOrderDetailUseCase: AdminGetOrderDetailUseCase,
private val adminCancelOrderUseCase: AdminCancelOrderUseCase,
private val adminUpdateRefundStatusUseCase: AdminUpdateRefundStatusUseCase,
private val adminExcelService: AdminExcelService,
) {
@Operation(summary = "주문 목록을 엑셀로 다운로드합니다.")
@GetMapping("/export")
fun exportOrders(
@CurrentUserId userId: Long,
@RequestParam(required = false) keyword: String?,
@RequestParam(required = false) status: OrderStatus?,
@RequestParam(required = false) eventId: Long?,
): ResponseEntity<ByteArray> {
val orders = adminGetOrdersUseCase.executeAll(userId, keyword, status, eventId)
val bytes = adminExcelService.generateOrdersExcel(orders)
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=orders.xlsx")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(bytes)
}
@Operation(summary = "주문 목록을 조회합니다.")
@GetMapping
fun getOrders(
@CurrentUserId userId: Long,
@RequestParam(required = false) keyword: String?,
@RequestParam(required = false) status: OrderStatus?,
@RequestParam(required = false) eventId: Long?,
@PageableDefault(size = 20) pageable: Pageable,
): Page<AdminOrderResponse> {
return adminGetOrdersUseCase.execute(userId, keyword, status, eventId, pageable)
}
@Operation(summary = "주문 상세 정보를 조회합니다.")
@GetMapping("/{orderUuid}")
fun getOrderDetail(@CurrentUserId userId: Long, @PathVariable orderUuid: String): AdminOrderResponse {
return adminGetOrderDetailUseCase.execute(userId, orderUuid)
}
@Operation(summary = "주문을 취소합니다.")
@PostMapping("/{orderUuid}/cancel")
fun cancelOrder(
@CurrentUserId userId: Long,
@PathVariable orderUuid: String,
@RequestBody(required = false) request: AdminCancelOrderRequest?,
): AdminOrderResponse {
return adminCancelOrderUseCase.execute(userId, orderUuid, request?.reason)
}
@Operation(summary = "주문의 환불 상태를 변경합니다. (REFUND_COMPLETED)")
@PatchMapping("/{orderUuid}/refund-status")
fun updateRefundStatus(
@CurrentUserId userId: Long,
@PathVariable orderUuid: String,
@RequestBody @Valid request: AdminRefundStatusRequest,
): AdminOrderResponse {
return adminUpdateRefundStatusUseCase.execute(userId, orderUuid, request)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminRefundController.kt
================================================
package band.gosrock.admin.controller
import band.gosrock.admin.model.dto.response.AdminRefundResponse
import band.gosrock.admin.service.AdminCompleteRefundUseCase
import band.gosrock.admin.service.AdminGetRefundDetailUseCase
import band.gosrock.admin.service.AdminGetRefundsUseCase
import band.gosrock.common.annotation.CurrentUserId
import band.gosrock.domain.domains.order.domain.RefundStatus
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.web.PageableDefault
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/internal-api/v1/refunds")
@SecurityRequirement(name = "admin-token")
@Tag(name = "Admin")
class AdminRefundController(
private val adminGetRefundsUseCase: AdminGetRefundsUseCase,
private val adminGetRefundDetailUseCase: AdminGetRefundDetailUseCase,
private val adminCompleteRefundUseCase: AdminCompleteRefundUseCase,
) {
@Operation(summary = "전체 환불 목록을 조회합니다.")
@GetMapping
fun getRefunds(
@CurrentUserId userId: Long,
@RequestParam(required = false) refundStatus: RefundStatus?,
@RequestParam(required = false) eventId: Long?,
@RequestParam(required = false) keyword: String?,
@PageableDefault(size = 20) pageable: Pageable,
): Page<AdminRefundResponse> =
adminGetRefundsUseCase.execute(userId, refundStatus, eventId, keyword, pageable)
@Operation(summary = "환불 상세 정보를 조회합니다.")
@GetMapping("/{orderUuid}")
fun getRefundDetail(
@CurrentUserId userId: Long,
@PathVariable orderUuid: String,
): AdminRefundResponse =
adminGetRefundDetailUseCase.execute(userId, orderUuid)
@Operation(summary = "환불을 확인 처리합니다. (REFUND_COMPLETED)")
@PatchMapping("/{orderUuid}/complete")
fun completeRefund(
@CurrentUserId userId: Long,
@PathVariable orderUuid: String,
): AdminRefundResponse =
adminCompleteRefundUseCase.execute(userId, orderUuid)
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminUserController.kt
================================================
package band.gosrock.admin.controller
import band.gosrock.admin.model.dto.request.AdminChangeNameRequest
import band.gosrock.admin.model.dto.request.AdminUpdateUserRoleRequest
import band.gosrock.admin.model.dto.request.AdminUpdateUserStatusRequest
import band.gosrock.admin.model.dto.response.AdminUserDetailResponse
import band.gosrock.admin.model.dto.response.AdminUserResponse
import band.gosrock.admin.service.AdminChangeNameUseCase
import band.gosrock.admin.service.AdminExcelService
import band.gosrock.admin.service.AdminGetUserDetailUseCase
import band.gosrock.admin.service.AdminGetUsersUseCase
import band.gosrock.admin.service.AdminUpdateUserRoleUseCase
import band.gosrock.admin.service.AdminUpdateUserStatusUseCase
import band.gosrock.common.annotation.CurrentUserId
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.validation.Valid
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.web.PageableDefault
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/internal-api/v1/users")
@SecurityRequirement(name = "admin-token")
@Tag(name = "Admin")
class AdminUserController(
private val adminGetUsersUseCase: AdminGetUsersUseCase,
private val adminGetUserDetailUseCase: AdminGetUserDetailUseCase,
private val adminUpdateUserRoleUseCase: AdminUpdateUserRoleUseCase,
private val adminUpdateUserStatusUseCase: AdminUpdateUserStatusUseCase,
private val adminChangeNameUseCase: AdminChangeNameUseCase,
private val adminExcelService: AdminExcelService,
) {
@Operation(summary = "유저 목록을 엑셀로 다운로드합니다.")
@GetMapping("/export")
fun exportUsers(
@CurrentUserId currentUserId: Long,
@RequestParam(required = false) keyword: String?,
): ResponseEntity<ByteArray> {
val users = adminGetUsersUseCase.executeAll(currentUserId, keyword)
val bytes = adminExcelService.generateUsersExcel(users)
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=users.xlsx")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(bytes)
}
@Operation(summary = "유저 목록을 조회합니다.")
@GetMapping
fun getUsers(
@CurrentUserId currentUserId: Long,
@RequestParam(required = false) keyword: String?,
@PageableDefault(size = 20) pageable: Pageable,
): Page<AdminUserResponse> {
return adminGetUsersUseCase.execute(currentUserId, keyword, pageable)
}
@Operation(summary = "유저 상세 정보를 조회합니다.")
@GetMapping("/{userId}")
fun getUserDetail(
@CurrentUserId currentUserId: Long,
@PathVariable userId: Long,
): AdminUserDetailResponse {
return adminGetUserDetailUseCase.execute(currentUserId, userId)
}
@Operation(summary = "유저 역할을 변경합니다. (SUPER_ADMIN 전용)")
@PatchMapping("/{userId}/role")
fun updateUserRole(
@CurrentUserId currentUserId: Long,
@PathVariable userId: Long,
@RequestBody request: AdminUpdateUserRoleRequest,
): AdminUserResponse {
return adminUpdateUserRoleUseCase.execute(currentUserId, userId, request)
}
@Operation(summary = "유저 상태를 변경합니다.")
@PatchMapping("/{userId}/status")
fun updateUserStatus(
@CurrentUserId currentUserId: Long,
@PathVariable userId: Long,
@RequestBody request: AdminUpdateUserStatusRequest,
): AdminUserResponse {
return adminUpdateUserStatusUseCase.execute(currentUserId, userId, request)
}
@Operation(summary = "유저 이름 변경")
@PatchMapping("/{userId}/name")
fun changeUserName(
@CurrentUserId adminUserId: Long,
@PathVariable userId: Long,
@Valid @RequestBody request: AdminChangeNameRequest,
) {
adminChangeNameUseCase.execute(adminUserId, userId, request)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/exception/AdminErrorCode.kt
================================================
package band.gosrock.admin.exception
import band.gosrock.common.annotation.ExplainError
import band.gosrock.common.consts.DuDoongStatic.FORBIDDEN
import band.gosrock.common.dto.ErrorReason
import band.gosrock.common.exception.BaseErrorCode
import java.lang.reflect.Field
enum class AdminErrorCode(
private val status: Int,
private val code: String,
private val reason: String,
) : BaseErrorCode {
@ExplainError("어드민 권한이 없는 유저가 어드민 기능에 접근하려는 경우")
ADMIN_FORBIDDEN(FORBIDDEN, "ADMIN_403_1", "어드민 권한이 필요합니다. MANAGER 이상의 역할이 필요합니다."),
@ExplainError("SUPER_ADMIN 전용 기능에 일반 관리자가 접근하려는 경우")
ADMIN_SUPER_ADMIN_REQUIRED(FORBIDDEN, "ADMIN_403_2", "SUPER_ADMIN 권한이 필요합니다.");
override fun getErrorReason(): ErrorReason =
ErrorReason(status = status, code = code, reason = reason)
override fun getExplainError(): String {
val field: Field = this.javaClass.getField(this.name)
val annotation = field.getAnnotation(ExplainError::class.java)
return annotation?.value ?: reason
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/exception/AdminForbiddenException.kt
================================================
package band.gosrock.admin.exception
import band.gosrock.common.exception.DuDoongCodeException
class AdminForbiddenException private constructor() : DuDoongCodeException(AdminErrorCode.ADMIN_FORBIDDEN) {
companion object {
@JvmField
val EXCEPTION: DuDoongCodeException = AdminForbiddenException()
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminAddHostMemberRequest.kt
================================================
package band.gosrock.admin.model.dto.request
import band.gosrock.domain.domains.host.domain.HostRole
data class AdminAddHostMemberRequest(
val userId: Long,
val role: HostRole,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminAdjustTicketStockRequest.kt
================================================
package band.gosrock.admin.model.dto.request
data class AdminAdjustTicketStockRequest(
val delta: Long, // 양수 = 증가, 음수 = 감소
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminCancelOrderRequest.kt
================================================
package band.gosrock.admin.model.dto.request
data class AdminCancelOrderRequest(
val reason: String? = null,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminChangeNameRequest.kt
================================================
package band.gosrock.admin.model.dto.request
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size
data class AdminChangeNameRequest(
@field:NotBlank(message = "이름을 입력해주세요.")
@field:Size(min = 2, max = 7, message = "이름은 2~7자여야 합니다.")
val name: String,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminRefundStatusRequest.kt
================================================
package band.gosrock.admin.model.dto.request
import jakarta.validation.constraints.NotNull
data class AdminRefundStatusRequest(
@field:NotNull
val refundStatus: String,
val reason: String? = null,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminTransferMasterRequest.kt
================================================
package band.gosrock.admin.model.dto.request
import jakarta.validation.constraints.NotNull
/** 어드민 호스트 마스터 강제 양도 요청 DTO */
data class AdminTransferMasterRequest(
@field:NotNull(message = "양도 대상 유저 아이디를 입력해주세요")
val newMasterUserId: Long,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateEventRequest.kt
================================================
package band.gosrock.admin.model.dto.request
import java.time.LocalDateTime
data class AdminUpdateEventRequest(
val name: String? = null,
val startAt: LocalDateTime? = null,
val runTime: Int? = null,
val content: String? = null,
val placeName: String? = null,
val placeAddress: String? = null,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateEventStatusRequest.kt
================================================
package band.gosrock.admin.model.dto.request
import band.gosrock.domain.domains.event.domain.EventStatus
data class AdminUpdateEventStatusRequest(
val status: EventStatus,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateHostMemberRoleRequest.kt
================================================
package band.gosrock.admin.model.dto.request
import band.gosrock.domain.domains.host.domain.HostRole
data class AdminUpdateHostMemberRoleRequest(
val role: HostRole,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateHostPartnerRequest.kt
================================================
package band.gosrock.admin.model.dto.request
data class AdminUpdateHostPartnerRequest(
val partner: Boolean,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateHostProfileRequest.kt
================================================
package band.gosrock.admin.model.dto.request
data class AdminUpdateHostProfileRequest(
val name: String?,
val introduce: String?,
val contactEmail: String?,
val contactNumber: String?,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateTicketItemRequest.kt
================================================
package band.gosrock.admin.model.dto.request
import java.math.BigDecimal
data class AdminUpdateTicketItemRequest(
val name: String? = null,
val description: String? = null,
val price: BigDecimal? = null,
val quantity: Long? = null,
val purchaseLimit: Long? = null,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateUserRoleRequest.kt
================================================
package band.gosrock.admin.model.dto.request
import band.gosrock.domain.domains.user.domain.AccountRole
data class AdminUpdateUserRoleRequest(
val role: AccountRole,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateUserStatusRequest.kt
================================================
package band.gosrock.admin.model.dto.request
import band.gosrock.domain.domains.user.domain.AccountState
data class AdminUpdateUserStatusRequest(
val status: AccountState,
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminCommentResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.comment.domain.Comment
import band.gosrock.domain.domains.comment.domain.CommentStatus
import java.time.LocalDateTime
data class AdminCommentResponse(
val id: Long,
val userName: String?,
val eventName: String?,
val content: String?,
val commentStatus: CommentStatus?,
val createdAt: LocalDateTime?,
val userId: Long?,
val eventId: Long?,
) {
companion object {
fun of(comment: Comment, eventName: String?): AdminCommentResponse =
AdminCommentResponse(
id = comment.id!!,
userName = comment.nickName,
eventName = eventName,
content = comment.content,
commentStatus = comment.commentStatus,
createdAt = comment.createdAt,
userId = comment.user?.id,
eventId = comment.eventId,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminEventResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.event.domain.Event
import band.gosrock.domain.domains.event.domain.EventStatus
import java.time.LocalDateTime
data class AdminEventResponse(
val id: Long,
val name: String?,
val hostName: String?,
val status: EventStatus,
val startAt: LocalDateTime?,
val runTime: Long?,
val createdAt: LocalDateTime?,
val ticketItemCount: Int = 0,
val issuedTicketCount: Int = 0,
val totalOrderCount: Int = 0,
val content: String? = null,
val placeName: String? = null,
val placeAddress: String? = null,
val hostId: Long? = null,
val posterImageKey: String? = null,
val latitude: Double? = null,
val longitude: Double? = null,
) {
companion object {
fun of(event: Event, hostName: String?): AdminEventResponse =
AdminEventResponse(
id = event.id!!,
name = event.eventBasic?.name,
hostName = hostName,
status = event.status,
startAt = event.eventBasic?.startAt,
runTime = event.eventBasic?.runTime,
createdAt = event.createdAt,
hostId = event.hostId,
posterImageKey = event.eventDetail?.posterImage?.imageKey,
latitude = event.eventPlace?.latitude,
longitude = event.eventPlace?.longitude,
)
fun ofDetail(
event: Event,
hostName: String?,
ticketItemCount: Int,
issuedTicketCount: Int,
totalOrderCount: Int,
): AdminEventResponse =
AdminEventResponse(
id = event.id!!,
name = event.eventBasic?.name,
hostName = hostName,
status = event.status,
startAt = event.eventBasic?.startAt,
runTime = event.eventBasic?.runTime,
createdAt = event.createdAt,
ticketItemCount = ticketItemCount,
issuedTicketCount = issuedTicketCount,
totalOrderCount = totalOrderCount,
content = event.eventDetail?.content,
placeName = event.eventPlace?.placeName,
placeAddress = event.eventPlace?.placeAddress,
hostId = event.hostId,
posterImageKey = event.eventDetail?.posterImage?.imageKey,
latitude = event.eventPlace?.latitude,
longitude = event.eventPlace?.longitude,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminHostDetailResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.host.domain.Host
import java.time.LocalDateTime
data class AdminHostDetailResponse(
val id: Long,
val name: String?,
val introduce: String?,
val contactEmail: String?,
val contactNumber: String?,
val profileImage: String?,
val partner: Boolean,
val masterUserId: Long?,
val createdAt: LocalDateTime?,
val memberCount: Int,
val slackUrl: String?,
) {
companion object {
fun from(host: Host): AdminHostDetailResponse =
AdminHostDetailResponse(
id = host.id!!,
name = host.profile?.name,
introduce = host.profile?.introduce,
contactEmail = host.profile?.contactEmail,
contactNumber = host.profile?.contactNumber,
profileImage = host.profile?.profileImage?.imageKey,
partner = host.partner,
masterUserId = host.masterUserId,
createdAt = host.createdAt,
memberCount = host.hostUsers.size,
slackUrl = host.slackUrl,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminHostMemberResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.host.domain.HostRole
import band.gosrock.domain.domains.host.domain.HostUser
import java.time.LocalDateTime
data class AdminHostMemberResponse(
val userId: Long?,
val userName: String?,
val role: HostRole,
val active: Boolean,
val createdAt: LocalDateTime?,
) {
companion object {
fun of(hostUser: HostUser, userName: String?): AdminHostMemberResponse =
AdminHostMemberResponse(
userId = hostUser.userId,
userName = userName,
role = hostUser.role,
active = hostUser.active,
createdAt = hostUser.createdAt,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminHostResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.host.domain.Host
import java.time.LocalDateTime
data class AdminHostResponse(
val id: Long,
val name: String?,
val introduce: String?,
val contactEmail: String?,
val contactNumber: String?,
val profileImage: String?,
val partner: Boolean,
val masterUserId: Long?,
val createdAt: LocalDateTime?,
) {
companion object {
fun from(host: Host): AdminHostResponse =
AdminHostResponse(
id = host.id!!,
name = host.profile?.name,
introduce = host.profile?.introduce,
contactEmail = host.profile?.contactEmail,
contactNumber = host.profile?.contactNumber,
profileImage = host.profile?.profileImage?.imageKey,
partner = host.partner,
masterUserId = host.masterUserId,
createdAt = host.createdAt,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminIssuedTicketResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.issuedTicket.domain.IssuedTicket
import band.gosrock.domain.domains.issuedTicket.domain.IssuedTicketStatus
import java.time.LocalDateTime
data class AdminIssuedTicketResponse(
val id: Long,
val issuedTicketNo: String?,
val userName: String?,
val ticketName: String?,
val orderUuid: String?,
val enteredAt: LocalDateTime?,
val status: IssuedTicketStatus,
val createdAt: LocalDateTime?,
) {
companion object {
fun from(issuedTicket: IssuedTicket): AdminIssuedTicketResponse =
AdminIssuedTicketResponse(
id = issuedTicket.id!!,
issuedTicketNo = issuedTicket.issuedTicketNo,
userName = issuedTicket.userInfo?.userName,
ticketName = issuedTicket.itemInfo?.ticketName,
orderUuid = issuedTicket.orderUuid,
enteredAt = issuedTicket.enteredAt,
status = issuedTicket.issuedTicketStatus,
createdAt = issuedTicket.createdAt,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminOrderResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.order.domain.Order
import band.gosrock.domain.domains.order.domain.OrderStatus
import java.time.LocalDateTime
data class AdminOrderResponse(
val orderId: String?,
val userName: String?,
val eventName: String?,
val ticketName: String?,
val totalAmount: Long,
val orderStatus: OrderStatus,
val createdAt: LocalDateTime?,
val orderNo: String?,
val orderMethod: String?,
val userId: Long?,
val eventId: Long?,
val approvedAt: LocalDateTime?,
val withDrawAt: LocalDateTime?,
val paymentMethod: String?,
val receiptUrl: String?,
val supplyAmount: String?,
val discountAmount: String?,
val couponName: String?,
val failReason: String? = null,
val cancelReason: String? = null,
val refundStatus: String? = null,
val refundStatusChangedAt: String? = null,
) {
companion object {
fun of(order: Order, userName: String?, eventName: String?): AdminOrderResponse =
AdminOrderResponse(
orderId = order.uuid,
userName = userName,
eventName = eventName,
ticketName = order.orderName,
totalAmount = order.getTotalPaymentPrice().longValue(),
orderStatus = order.orderStatus,
createdAt = order.createdAt,
orderNo = order.orderNo,
orderMethod = order.orderMethod?.name,
userId = order.userId,
eventId = order.eventId,
approvedAt = order.approvedAt,
withDrawAt = order.withDrawAt,
paymentMethod = order.pgPaymentInfo.paymentMethod.name,
receiptUrl = order.pgPaymentInfo.receiptUrl,
supplyAmount = order.totalPaymentInfo?.supplyAmount?.toString(),
discountAmount = order.totalPaymentInfo?.discountAmount?.toString(),
couponName = order.orderCouponVo.name,
failReason = order.failReason,
cancelReason = order.cancelReason,
refundStatus = order.refundStatus.name,
refundStatusChangedAt = order.refundStatusChangedAt?.toString(),
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminRefundResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.order.domain.Order
import band.gosrock.domain.domains.order.domain.RefundStatus
import java.time.LocalDateTime
data class AdminRefundResponse(
val orderId: String?,
val orderNo: String?,
val userName: String?,
val eventName: String?,
val eventId: Long?,
val ticketName: String?,
val totalAmount: Long,
val cancelReason: String?,
val refundStatus: RefundStatus,
val refundStatusChangedAt: LocalDateTime?,
val withDrawAt: LocalDateTime?,
val createdAt: LocalDateTime?,
val userId: Long?,
) {
companion object {
fun of(order: Order, userName: String?, eventName: String?): AdminRefundResponse =
AdminRefundResponse(
orderId = order.uuid,
orderNo = order.orderNo,
userName = userName,
eventName = eventName,
eventId = order.eventId,
ticketName = order.orderName,
totalAmount = order.getTotalPaymentPrice().longValue(),
cancelReason = order.cancelReason,
refundStatus = order.refundStatus,
refundStatusChangedAt = order.refundStatusChangedAt,
withDrawAt = order.withDrawAt,
createdAt = order.createdAt,
userId = order.userId,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminTicketItemResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.ticket_item.domain.TicketItem
import band.gosrock.domain.domains.ticket_item.domain.TicketItemStatus
import band.gosrock.domain.domains.ticket_item.domain.TicketPayType
import band.gosrock.domain.domains.ticket_item.domain.TicketType
import java.math.BigDecimal
import java.time.LocalDateTime
data class AdminTicketItemResponse(
val id: Long,
val name: String?,
val description: String?,
val price: BigDecimal?,
val quantity: Long?,
val supplyCount: Long?,
val purchaseLimit: Long?,
val payType: TicketPayType?,
val type: TicketType?,
val isQuantityPublic: Boolean?,
val isSellable: Boolean?,
val saleStartAt: LocalDateTime?,
val saleEndAt: LocalDateTime?,
val ticketItemStatus: TicketItemStatus,
val eventId: Long?,
) {
companion object {
fun from(ticketItem: TicketItem): AdminTicketItemResponse =
AdminTicketItemResponse(
id = ticketItem.id!!,
name = ticketItem.name,
description = ticketItem.description,
price = ticketItem.price?.amount,
quantity = ticketItem.quantity,
supplyCount = ticketItem.supplyCount,
purchaseLimit = ticketItem.purchaseLimit,
payType = ticketItem.payType,
type = ticketItem.type,
isQuantityPublic = ticketItem.isQuantityPublic,
isSellable = ticketItem.isSellable,
saleStartAt = ticketItem.saleStartAt,
saleEndAt = ticketItem.saleEndAt,
ticketItemStatus = ticketItem.ticketItemStatus,
eventId = ticketItem.eventId,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminUserDetailResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.user.domain.AccountRole
import band.gosrock.domain.domains.user.domain.AccountState
import band.gosrock.domain.domains.user.domain.OauthProvider
import band.gosrock.domain.domains.user.domain.User
import java.time.LocalDateTime
data class AdminUserDetailResponse(
val id: Long,
val name: String?,
val email: String?,
val profileImage: String?,
val accountRole: AccountRole,
val accountState: AccountState,
val createdAt: LocalDateTime?,
val phoneNumber: String?,
val marketingAgree: Boolean,
val oauthProvider: OauthProvider?,
val lastLoginAt: LocalDateTime?,
val receiveMail: Boolean,
) {
companion object {
fun from(user: User): AdminUserDetailResponse =
AdminUserDetailResponse(
id = user.id!!,
name = user.profile?.name,
email = user.profile?.email,
profileImage = user.profile?.profileImage?.imageKey,
accountRole = user.accountRole,
accountState = user.accountState,
createdAt = user.createdAt,
phoneNumber = user.profile?.phoneNumberVo?.phoneNumber,
marketingAgree = user.marketingAgree,
oauthProvider = user.oauthInfo?.provider,
lastLoginAt = user.lastLoginAt,
receiveMail = user.receiveMail,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminUserResponse.kt
================================================
package band.gosrock.admin.model.dto.response
import band.gosrock.domain.domains.user.domain.AccountRole
import band.gosrock.domain.domains.user.domain.AccountState
import band.gosrock.domain.domains.user.domain.User
import java.time.LocalDateTime
data class AdminUserResponse(
val id: Long,
val name: String?,
val email: String?,
val profileImage: String?,
val accountRole: AccountRole,
val accountState: AccountState,
val createdAt: LocalDateTime?,
) {
companion object {
fun from(user: User): AdminUserResponse =
AdminUserResponse(
id = user.id!!,
name = user.profile?.name,
email = user.profile?.email,
profileImage = user.profile?.profileImage?.imageKey,
accountRole = user.accountRole,
accountState = user.accountState,
createdAt = user.createdAt,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/DashboardResponse.kt
================================================
package band.gosrock.admin.model.dto.response
data class DashboardResponse(
val totalUsers: Long,
val todayNewUsers: Long,
val todayOrders: Long,
val todayRevenue: Long,
val activeEvents: Long,
val todayRefunds: Long,
val recentOrders: List<AdminOrderResponse> = emptyList(),
val recentEvents: List<AdminEventResponse> = emptyList(),
)
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminAddHostMemberUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminAddHostMemberRequest
import band.gosrock.admin.model.dto.response.AdminHostMemberResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.host.domain.HostUser
import band.gosrock.domain.domains.host.repository.HostRepository
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminAddHostMemberUseCase(
private val hostAdaptor: HostAdaptor,
private val hostRepository: HostRepository,
private val userAdaptor: UserAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, hostId: Long, request: AdminAddHostMemberRequest): AdminHostMemberResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val host = hostAdaptor.findById(hostId)
val hostUser = HostUser(host, request.userId, request.role)
host.addHostUsers(setOf(hostUser))
hostRepository.save(host)
val userName = runCatching { userAdaptor.queryUser(request.userId).profile?.name }.getOrNull()
val savedHostUser = host.getHostUserByUserId(request.userId)
return AdminHostMemberResponse.of(savedHostUser, userName)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminAdjustTicketStockUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminTicketItemResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.common.aop.redissonLock.RedissonLock
import band.gosrock.domain.domains.ticket_item.adaptor.TicketItemAdaptor
@UseCase
class AdminAdjustTicketStockUseCase(
private val ticketItemAdaptor: TicketItemAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
@RedissonLock(LockName = "티켓관리", identifier = "ticketItemId")
fun execute(userId: Long, ticketItemId: Long, delta: Long): AdminTicketItemResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val ticketItem = ticketItemAdaptor.queryTicketItem(ticketItemId)
ticketItem.adminAdjustStock(delta)
ticketItemAdaptor.save(ticketItem)
return AdminTicketItemResponse.from(ticketItem)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminAuthValidator.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.exception.AdminForbiddenException
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import band.gosrock.domain.domains.user.domain.AccountRole
import band.gosrock.domain.domains.user.domain.User
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
@Component
class AdminAuthValidator(
private val userAdaptor: UserAdaptor,
) {
private val log = LoggerFactory.getLogger(javaClass)
fun validateAdminOrAbove(userId: Long): User {
val user = userAdaptor.queryUser(userId)
if (user.accountRole != AccountRole.ADMIN && user.accountRole != AccountRole.SUPER_ADMIN) {
log.info("[ADMIN-AUTH] DENIED - userId={}, role={}, required=ADMIN+", userId, user.accountRole)
throw AdminForbiddenException.EXCEPTION
}
log.info("[ADMIN-AUTH] GRANTED - userId={}, role={}", userId, user.accountRole)
return user
}
fun validateSuperAdmin(userId: Long): User {
val user = userAdaptor.queryUser(userId)
if (user.accountRole != AccountRole.SUPER_ADMIN) {
log.info("[ADMIN-AUTH] DENIED - userId={}, role={}, required=SUPER_ADMIN", userId, user.accountRole)
throw AdminForbiddenException.EXCEPTION
}
log.info("[ADMIN-AUTH] GRANTED - userId={}, role=SUPER_ADMIN", userId)
return user
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminCancelOrderUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminOrderResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.order.adaptor.OrderAdaptor
import band.gosrock.domain.domains.order.domain.validator.OrderValidator
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminCancelOrderUseCase(
private val orderAdaptor: OrderAdaptor,
private val orderValidator: OrderValidator,
private val userAdaptor: UserAdaptor,
private val eventAdaptor: EventAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, orderUuid: String, reason: String? = null): AdminOrderResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val order = orderAdaptor.findByOrderUuid(orderUuid)
order.cancel(orderValidator, reason)
val userName = order.userId?.let {
runCatching { userAdaptor.queryUser(it).profile?.name }.getOrNull()
}
val eventName = order.eventId?.let {
runCatching { eventAdaptor.findById(it).eventBasic?.name }.getOrNull()
}
return AdminOrderResponse.of(order, userName, eventName)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminChangeNameUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminChangeNameRequest
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminChangeNameUseCase(
private val adminAuthValidator: AdminAuthValidator,
private val userAdaptor: UserAdaptor,
) {
@Transactional
fun execute(adminUserId: Long, targetUserId: Long, request: AdminChangeNameRequest) {
adminAuthValidator.validateAdminOrAbove(adminUserId)
val user = userAdaptor.queryUser(targetUserId)
user.changeName(request.name)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminCompleteRefundUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminRefundResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.order.adaptor.OrderAdaptor
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminCompleteRefundUseCase(
private val orderAdaptor: OrderAdaptor,
private val userAdaptor: UserAdaptor,
private val eventAdaptor: EventAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, orderUuid: String): AdminRefundResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val order = orderAdaptor.findByOrderUuid(orderUuid)
order.completeRefund()
val userName = order.userId?.let {
runCatching { userAdaptor.queryUser(it).profile?.name }.getOrNull()
}
val eventName = order.eventId?.let {
runCatching { eventAdaptor.findById(it).eventBasic?.name }.getOrNull()
}
return AdminRefundResponse.of(order, userName, eventName)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminDeleteCommentUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.comment.adaptor.CommentAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminDeleteCommentUseCase(
private val commentAdaptor: CommentAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, commentId: Long) {
adminAuthValidator.validateAdminOrAbove(userId)
val comment = commentAdaptor.queryComment(commentId)
comment.delete()
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminDeleteEventUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.event.domain.EventStatus
import band.gosrock.domain.domains.event.repository.EventRepository
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminDeleteEventUseCase(
private val eventAdaptor: EventAdaptor,
private val eventRepository: EventRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, eventId: Long) {
adminAuthValidator.validateAdminOrAbove(userId)
val event = eventAdaptor.findById(eventId)
// 어드민은 밸리데이션 없이 직접 DELETED 상태로 변경
event.adminUpdateStatus(EventStatus.DELETED)
eventRepository.save(event)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminExcelService.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminEventResponse
import band.gosrock.admin.model.dto.response.AdminIssuedTicketResponse
import band.gosrock.admin.model.dto.response.AdminOrderResponse
import band.gosrock.admin.model.dto.response.AdminTicketItemResponse
import band.gosrock.admin.model.dto.response.AdminUserResponse
import band.gosrock.domain.domains.issuedTicket.domain.IssuedTicket
import band.gosrock.domain.domains.ticket_item.domain.Option
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.springframework.stereotype.Service
import java.io.ByteArrayOutputStream
@Service
class AdminExcelService {
fun generateOrdersExcel(orders: List<AdminOrderResponse>): ByteArray {
val workbook = XSSFWorkbook()
val sheet = workbook.createSheet("주문 목록")
val headerRow = sheet.createRow(0)
val headers = listOf(
"주문번호", "주문번호(읽기용)", "사용자", "이벤트", "티켓", "금액", "상태",
"주문방식", "결제수단", "승인일시", "취소일시", "할인금액", "쿠폰명", "주문일",
)
headers.forEachIndexed { i, h -> headerRow.createCell(i).setCellValue(h) }
orders.forEachIndexed { idx, order ->
val row = sheet.createRow(idx + 1)
var col = 0
row.createCell(col++).setCellValue(order.orderId ?: "")
row.createCell(col++).setCellValue(order.orderNo ?: "")
row.createCell(col++).setCellValue(order.userName ?: "")
row.createCell(col++).setCellValue(order.eventName ?: "")
row.createCell(col++).setCellValue(order.ticketName ?: "")
row.createCell(col++).setCellValue(order.totalAmount.toDouble())
row.createCell(col++).setCellValue(order.orderStatus.toString())
row.createCell(col++).setCellValue(order.orderMethod ?: "")
row.createCell(col++).setCellValue(order.paymentMethod ?: "")
row.createCell(col++).setCellValue(order.approvedAt?.toString() ?: "")
row.createCell(col++).setCellValue(order.withDrawAt?.toString() ?: "")
row.createCell(col++).setCellValue(order.discountAmount ?: "")
row.createCell(col++).setCellValue(order.couponName ?: "")
row.createCell(col++).setCellValue(order.createdAt?.toString() ?: "")
}
return toByteArray(workbook)
}
fun generateEventsExcel(events: List<AdminEventResponse>): ByteArray {
val workbook = XSSFWorkbook()
val sheet = workbook.createSheet("이벤트 목록")
val headerRow = sheet.createRow(0)
listOf("ID", "이벤트명", "호스트", "상태", "시작일", "런타임(분)", "생성일").forEachIndexed { i, h ->
headerRow.createCell(i).setCellValue(h)
}
events.forEachIndexed { idx, event ->
val row = sheet.createRow(idx + 1)
row.createCell(0).setCellValue(event.id.toDouble())
row.createCell(1).setCellValue(event.name ?: "")
row.createCell(2).setCellValue(event.hostName ?: "")
row.createCell(3).setCellValue(event.status.toString())
row.createCell(4).setCellValue(event.startAt?.toString() ?: "")
row.createCell(5).setCellValue(event.runTime?.toDouble() ?: 0.0)
row.createCell(6).setCellValue(event.createdAt?.toString() ?: "")
}
return toByteArray(workbook)
}
fun generateUsersExcel(users: List<AdminUserResponse>): ByteArray {
val workbook = XSSFWorkbook()
val sheet = workbook.createSheet("유저 목록")
val headerRow = sheet.createRow(0)
listOf("ID", "이름", "이메일", "역할", "상태", "가입일").forEachIndexed { i, h ->
headerRow.createCell(i).setCellValue(h)
}
users.forEachIndexed { idx, user ->
val row = sheet.createRow(idx + 1)
row.createCell(0).setCellValue(user.id.toDouble())
row.createCell(1).setCellValue(user.name ?: "")
row.createCell(2).setCellValue(user.email ?: "")
row.createCell(3).setCellValue(user.accountRole.toString())
row.createCell(4).setCellValue(user.accountState.toString())
row.createCell(5).setCellValue(user.createdAt?.toString() ?: "")
}
return toByteArray(workbook)
}
fun generateTicketItemsExcel(items: List<AdminTicketItemResponse>): ByteArray {
val workbook = XSSFWorkbook()
val sheet = workbook.createSheet("티켓 종류 목록")
val headerRow = sheet.createRow(0)
listOf("이름", "설명", "가격", "수량", "판매수", "구매제한", "타입", "상태").forEachIndexed { i, h ->
headerRow.createCell(i).setCellValue(h)
}
items.forEachIndexed { idx, item ->
val row = sheet.createRow(idx + 1)
row.createCell(0).setCellValue(item.name ?: "")
row.createCell(1).setCellValue(item.description ?: "")
row.createCell(2).setCellValue(item.price?.toDouble() ?: 0.0)
row.createCell(3).setCellValue(item.quantity?.toDouble() ?: 0.0)
row.createCell(4).setCellValue(item.supplyCount?.toDouble() ?: 0.0)
row.createCell(5).setCellValue(item.purchaseLimit?.toDouble() ?: 0.0)
row.createCell(6).setCellValue(item.type?.toString() ?: "")
row.createCell(7).setCellValue(item.ticketItemStatus.toString())
}
return toByteArray(workbook)
}
fun generateIssuedTicketsExcel(tickets: List<AdminIssuedTicketResponse>): ByteArray {
return generateIssuedTicketsExcelWithOptions(tickets, emptyList(), emptyList())
}
/**
* 발급 티켓 엑셀에 옵션 응답을 동적 컬럼으로 포함하여 생성합니다.
*
* @param tickets 발급 티켓 응답 목록
* @param options 이벤트에 등록된 옵션 목록 (동적 컬럼 헤더용)
* @param issuedTickets 발급 티켓 엔티티 목록 (옵션 응답 데이터 접근용)
*/
fun generateIssuedTicketsExcelWithOptions(
tickets: List<AdminIssuedTicketResponse>,
options: List<Option>,
issuedTickets: List<IssuedTicket>,
): ByteArray {
val workbook = XSSFWorkbook()
val sheet = workbook.createSheet("발급 티켓 목록")
val headerRow = sheet.createRow(0)
// 기본 헤더
val baseHeaders = listOf("티켓번호", "유저명", "티켓종류", "주문번호", "입장여부", "발급일")
baseHeaders.forEachIndexed { i, h -> headerRow.createCell(i).setCellValue(h) }
// 옵션 동적 헤더
val optionHeaders = options.map { it.getQuestionName() ?: "옵션(${it.id})" }
optionHeaders.forEachIndexed { i, h ->
headerRow.createCell(baseHeaders.size + i).setCellValue(h)
}
// IssuedTicket id -> IssuedTicket 맵 (옵션 응답 조회용)
val ticketEntityMap = issuedTickets.associateBy { it.id }
tickets.forEachIndexed { idx, ticket ->
val row = sheet.createRow(idx + 1)
var col = 0
row.createCell(col++).setCellValue(ticket.issuedTicketNo ?: "")
row.createCell(col++).setCellValue(ticket.userName ?: "")
row.createCell(col++).setCellValue(ticket.ticketName ?: "")
row.createCell(col++).setCellValue(ticket.orderUuid ?: "")
row.createCell(col++).setCellValue(if (ticket.enteredAt != null) "입장" else "미입장")
row.createCell(col++).setCellValue(ticket.createdAt?.toString() ?: "")
// 옵션 응답 채우기
if (options.isNotEmpty()) {
val entity = ticketEntityMap[ticket.id]
val answerMap = entity?.issuedTicketOptionAnswers
?.associateBy { it.optionId } ?: emptyMap()
options.forEach { option ->
val answer = answerMap[option.id]?.answer ?: ""
row.createCell(col++).setCellValue(answer)
}
}
}
return toByteArray(workbook)
}
private fun toByteArray(workbook: XSSFWorkbook): ByteArray {
val out = ByteArrayOutputStream()
workbook.write(out)
workbook.close()
return out.toByteArray()
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminExportIssuedTicketsUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.issuedTicket.repository.IssuedTicketRepository
import band.gosrock.domain.domains.ticket_item.adaptor.OptionGroupAdaptor
import band.gosrock.domain.domains.ticket_item.domain.Option
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminExportIssuedTicketsUseCase(
private val issuedTicketRepository: IssuedTicketRepository,
private val optionGroupAdaptor: OptionGroupAdaptor,
private val adminExcelService: AdminExcelService,
private val adminAuthValidator: AdminAuthValidator,
private val adminGetIssuedTicketsUseCase: AdminGetIssuedTicketsUseCase,
) {
fun execute(userId: Long, eventId: Long): ByteArray {
adminAuthValidator.validateAdminOrAbove(userId)
// 발급 티켓 엔티티 조회
val issuedTickets = issuedTicketRepository.findAllByEventId(eventId)
// 발급 티켓 응답 DTO 변환
val ticketResponses = adminGetIssuedTicketsUseCase.executeAll(userId, eventId)
// 이벤트의 옵션 그룹 → 옵션 목록 조회
val optionGroups = optionGroupAdaptor.findAllByEventId(eventId)
val options: List<Option> = optionGroups.flatMap { it.options }
return adminExcelService.generateIssuedTicketsExcelWithOptions(
tickets = ticketResponses,
options = options,
issuedTickets = issuedTickets,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetCommentsUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminCommentResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.comment.repository.CommentRepository
import band.gosrock.domain.domains.event.repository.EventRepository
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetCommentsUseCase(
private val commentRepository: CommentRepository,
private val eventRepository: EventRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, keyword: String?, eventId: Long?, pageable: Pageable): Page<AdminCommentResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
val commentPage = commentRepository.findAllForAdmin(keyword, eventId, pageable)
// batch fetch events to avoid N+1
val eventIds = commentPage.content.mapNotNull { it.eventId }
val eventMap = eventRepository.findAllByIdIn(eventIds).associateBy { it.id }
return commentPage.map { comment ->
val eventName = comment.eventId?.let { eventMap[it]?.eventBasic?.name }
AdminCommentResponse.of(comment, eventName)
}
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetEventDetailUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminEventResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.exception.EventNotFoundException
import band.gosrock.domain.domains.event.repository.EventRepository
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.issuedTicket.repository.IssuedTicketRepository
import band.gosrock.domain.domains.order.repository.OrderRepository
import band.gosrock.domain.domains.ticket_item.repository.TicketItemRepository
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetEventDetailUseCase(
private val eventRepository: EventRepository,
private val hostAdaptor: HostAdaptor,
private val ticketItemRepository: TicketItemRepository,
private val issuedTicketRepository: IssuedTicketRepository,
private val orderRepository: OrderRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, eventId: Long): AdminEventResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val event = eventRepository.findByIdForAdmin(eventId)
?: throw EventNotFoundException.EXCEPTION
val hostName = event.hostId?.let {
runCatching { hostAdaptor.findById(it).profile?.name }.getOrNull()
}
val ticketItemCount = ticketItemRepository.countByEventId(eventId).toInt()
val issuedTicketCount = issuedTicketRepository.countByEventId(eventId).toInt()
val totalOrderCount = orderRepository.countByEventId(eventId).toInt()
return AdminEventResponse.ofDetail(
event = event,
hostName = hostName,
ticketItemCount = ticketItemCount,
issuedTicketCount = issuedTicketCount,
totalOrderCount = totalOrderCount,
)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetEventsUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminEventResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.repository.EventRepository
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetEventsUseCase(
private val eventRepository: EventRepository,
private val hostAdaptor: HostAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
fun executeAll(userId: Long, keyword: String?, status: String?): List<AdminEventResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
return eventRepository.findAllForAdminNoPage(keyword, status)
.map { event ->
val hostName = event.hostId?.let {
runCatching { hostAdaptor.findById(it).profile?.name }.getOrNull()
}
AdminEventResponse.of(event, hostName)
}
}
fun execute(userId: Long, keyword: String?, status: String?, pageable: Pageable): Page<AdminEventResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
return eventRepository.findAllForAdmin(keyword, status, pageable)
.map { event ->
val hostName = event.hostId?.let {
runCatching { hostAdaptor.findById(it).profile?.name }.getOrNull()
}
AdminEventResponse.of(event, hostName)
}
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetHostDetailUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminHostDetailResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetHostDetailUseCase(
private val hostAdaptor: HostAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, hostId: Long): AdminHostDetailResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val host = hostAdaptor.findById(hostId)
return AdminHostDetailResponse.from(host)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetHostEventsUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminEventResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.repository.EventRepository
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetHostEventsUseCase(
private val eventRepository: EventRepository,
private val hostAdaptor: HostAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, hostId: Long, pageable: Pageable): Page<AdminEventResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
val host = hostAdaptor.findById(hostId)
val hostName = host.profile?.name
return eventRepository.findAllByHostId(hostId, pageable)
.map { AdminEventResponse.of(it, hostName) }
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetHostMembersUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminHostMemberResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetHostMembersUseCase(
private val hostAdaptor: HostAdaptor,
private val userAdaptor: UserAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, hostId: Long): List<AdminHostMemberResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
val host = hostAdaptor.findById(hostId)
val userIds = host.getHostUser_UserIds()
val userMap = userAdaptor.queryUserListByIdIn(userIds)
.associateBy { it.id }
return host.hostUsers.map { hostUser ->
val userName = hostUser.userId?.let { userMap[it]?.profile?.name }
AdminHostMemberResponse.of(hostUser, userName)
}
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetHostsUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminHostResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetHostsUseCase(
private val hostAdaptor: HostAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, keyword: String?, pageable: Pageable): Page<AdminHostResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
return hostAdaptor.findAllForAdmin(keyword, pageable)
.map { AdminHostResponse.from(it) }
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetIssuedTicketsUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminIssuedTicketResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.issuedTicket.repository.IssuedTicketRepository
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetIssuedTicketsUseCase(
private val issuedTicketRepository: IssuedTicketRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, eventId: Long, pageable: Pageable): Page<AdminIssuedTicketResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
return issuedTicketRepository.findAllByEventId(eventId, pageable)
.map { AdminIssuedTicketResponse.from(it) }
}
fun executeAll(userId: Long, eventId: Long): List<AdminIssuedTicketResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
return issuedTicketRepository.findAllByEventId(eventId)
.map { AdminIssuedTicketResponse.from(it) }
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetMeUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminUserDetailResponse
import band.gosrock.common.annotation.UseCase
@UseCase
class AdminGetMeUseCase(
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long): AdminUserDetailResponse {
val user = adminAuthValidator.validateAdminOrAbove(userId)
return AdminUserDetailResponse.from(user)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetOrderDetailUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminOrderResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.order.adaptor.OrderAdaptor
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetOrderDetailUseCase(
private val orderAdaptor: OrderAdaptor,
private val userAdaptor: UserAdaptor,
private val eventAdaptor: EventAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, orderUuid: String): AdminOrderResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val order = orderAdaptor.findByOrderUuid(orderUuid)
val userName = order.userId?.let {
runCatching { userAdaptor.queryUser(it).profile?.name }.getOrNull()
}
val eventName = order.eventId?.let {
runCatching { eventAdaptor.findById(it).eventBasic?.name }.getOrNull()
}
return AdminOrderResponse.of(order, userName, eventName)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetOrdersUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminOrderResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.repository.EventRepository
import band.gosrock.domain.domains.order.domain.OrderStatus
import band.gosrock.domain.domains.order.repository.OrderRepository
import band.gosrock.domain.domains.user.repository.UserRepository
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetOrdersUseCase(
private val orderRepository: OrderRepository,
private val userRepository: UserRepository,
private val eventRepository: EventRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
fun executeAll(userId: Long, keyword: String?, status: OrderStatus?, eventId: Long?): List<AdminOrderResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
val orders = orderRepository.findAllForAdminNoPage(keyword, status, eventId)
val userIds = orders.mapNotNull { it.userId }
val eventIds = orders.mapNotNull { it.eventId }
val userMap = userRepository.findAllByIdIn(userIds).associateBy { it.id }
val eventMap = eventRepository.findAllByIdIn(eventIds).associateBy { it.id }
return orders.map { order ->
val userName = order.userId?.let { userMap[it]?.profile?.name }
val eventName = order.eventId?.let { eventMap[it]?.eventBasic?.name }
AdminOrderResponse.of(order, userName, eventName)
}
}
fun execute(userId: Long, keyword: String?, status: OrderStatus?, eventId: Long?, pageable: Pageable): Page<AdminOrderResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
val orderPage = orderRepository.findAllForAdmin(keyword, status, eventId, pageable)
// batch fetch users and events to avoid N+1
val userIds = orderPage.content.mapNotNull { it.userId }
val eventIds = orderPage.content.mapNotNull { it.eventId }
val userMap = userRepository.findAllByIdIn(userIds).associateBy { it.id }
val eventMap = eventRepository.findAllByIdIn(eventIds).associateBy { it.id }
return orderPage.map { order ->
val userName = order.userId?.let { userMap[it]?.profile?.name }
val eventName = order.eventId?.let { eventMap[it]?.eventBasic?.name }
AdminOrderResponse.of(order, userName, eventName)
}
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetRefundDetailUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminRefundResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.order.adaptor.OrderAdaptor
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetRefundDetailUseCase(
private val orderAdaptor: OrderAdaptor,
private val userAdaptor: UserAdaptor,
private val eventAdaptor: EventAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, orderUuid: String): AdminRefundResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val order = orderAdaptor.findByOrderUuid(orderUuid)
val userName = order.userId?.let {
runCatching { userAdaptor.queryUser(it).profile?.name }.getOrNull()
}
val eventName = order.eventId?.let {
runCatching { eventAdaptor.findById(it).eventBasic?.name }.getOrNull()
}
return AdminRefundResponse.of(order, userName, eventName)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetRefundsUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminRefundResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.repository.EventRepository
import band.gosrock.domain.domains.order.adaptor.OrderAdaptor
import band.gosrock.domain.domains.order.domain.RefundStatus
import band.gosrock.domain.domains.user.repository.UserRepository
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetRefundsUseCase(
private val orderAdaptor: OrderAdaptor,
private val userRepository: UserRepository,
private val eventRepository: EventRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(
userId: Long,
refundStatus: RefundStatus?,
eventId: Long?,
keyword: String?,
pageable: Pageable,
): Page<AdminRefundResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
val orderPage = orderAdaptor.findRefunds(eventId, refundStatus, keyword, pageable)
val userIds = orderPage.content.mapNotNull { it.userId }
val eventIds = orderPage.content.mapNotNull { it.eventId }
val userMap = userRepository.findAllByIdIn(userIds).associateBy { it.id }
val eventMap = eventRepository.findAllByIdIn(eventIds).associateBy { it.id }
return orderPage.map { order ->
val userName = order.userId?.let { userMap[it]?.profile?.name }
val eventName = order.eventId?.let { eventMap[it]?.eventBasic?.name }
AdminRefundResponse.of(order, userName, eventName)
}
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetTicketItemsUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminTicketItemResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.ticket_item.adaptor.TicketItemAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetTicketItemsUseCase(
private val ticketItemAdaptor: TicketItemAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, eventId: Long): List<AdminTicketItemResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
return ticketItemAdaptor.findAllByEventId(eventId)
.map { AdminTicketItemResponse.from(it) }
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetUserDetailUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminUserDetailResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetUserDetailUseCase(
private val userAdaptor: UserAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, targetUserId: Long): AdminUserDetailResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val user = userAdaptor.queryUser(targetUserId)
return AdminUserDetailResponse.from(user)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetUsersUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminUserResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.user.repository.UserRepository
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class AdminGetUsersUseCase(
private val userRepository: UserRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
fun executeAll(userId: Long, keyword: String?): List<AdminUserResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
return userRepository.findAllByKeywordNoPage(keyword)
.map { AdminUserResponse.from(it) }
}
fun execute(userId: Long, keyword: String?, pageable: Pageable): Page<AdminUserResponse> {
adminAuthValidator.validateAdminOrAbove(userId)
return userRepository.findAllByKeyword(keyword, pageable)
.map { AdminUserResponse.from(it) }
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminRemoveHostMemberUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.host.repository.HostRepository
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminRemoveHostMemberUseCase(
private val hostAdaptor: HostAdaptor,
private val hostRepository: HostRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, hostId: Long, targetUserId: Long) {
adminAuthValidator.validateAdminOrAbove(userId)
val host = hostAdaptor.findById(hostId)
// 어드민이므로 active 여부 체크 없이 강제 제거
val hostUser = host.getHostUserByUserId(targetUserId)
host.hostUsers.remove(hostUser)
hostRepository.save(host)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminTransferMasterUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminTransferMasterRequest
import band.gosrock.admin.model.dto.response.AdminHostDetailResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.host.repository.HostRepository
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminTransferMasterUseCase(
private val adminAuthValidator: AdminAuthValidator,
private val hostAdaptor: HostAdaptor,
private val hostRepository: HostRepository,
) {
@Transactional
fun execute(adminUserId: Long, hostId: Long, request: AdminTransferMasterRequest): AdminHostDetailResponse {
adminAuthValidator.validateAdminOrAbove(adminUserId)
val host = hostAdaptor.findById(hostId)
host.forceTransferMaster(request.newMasterUserId)
hostRepository.save(host)
return AdminHostDetailResponse.from(host)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateEventStatusUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminUpdateEventStatusRequest
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.event.domain.EventStatus
import band.gosrock.domain.domains.event.repository.EventRepository
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminUpdateEventStatusUseCase(
private val eventAdaptor: EventAdaptor,
private val eventRepository: EventRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, eventId: Long, request: AdminUpdateEventStatusRequest) {
adminAuthValidator.validateAdminOrAbove(userId)
val event = eventAdaptor.findById(eventId)
// 어드민은 DELETED 제외 모든 상태로 직접 변경 가능 (밸리데이션 우회)
require(request.status != EventStatus.DELETED) {
"DELETED 상태는 DELETE 엔드포인트를 사용하세요."
}
event.adminUpdateStatus(request.status)
eventRepository.save(event)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateEventUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminUpdateEventRequest
import band.gosrock.admin.model.dto.response.AdminEventResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.event.repository.EventRepository
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminUpdateEventUseCase(
private val eventAdaptor: EventAdaptor,
private val eventRepository: EventRepository,
private val hostAdaptor: HostAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, eventId: Long, request: AdminUpdateEventRequest): AdminEventResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val event = eventAdaptor.findById(eventId)
// 어드민은 OPEN 상태에서도 수정 가능하도록 직접 필드 수정
event.adminUpdate(
name = request.name,
startAt = request.startAt,
runTime = request.runTime?.toLong(),
content = request.content,
placeName = request.placeName,
placeAddress = request.placeAddress,
)
eventRepository.save(event)
val hostName = event.hostId?.let {
runCatching { hostAdaptor.findById(it).profile?.name }.getOrNull()
}
return AdminEventResponse.of(event, hostName)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateHostMemberRoleUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminUpdateHostMemberRoleRequest
import band.gosrock.admin.model.dto.response.AdminHostMemberResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.host.repository.HostRepository
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminUpdateHostMemberRoleUseCase(
private val hostAdaptor: HostAdaptor,
private val hostRepository: HostRepository,
private val userAdaptor: UserAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, hostId: Long, targetUserId: Long, request: AdminUpdateHostMemberRoleRequest): AdminHostMemberResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val host = hostAdaptor.findById(hostId)
// 어드민이므로 마스터 권한 체크 건너뜀
host.setHostUserRole(targetUserId, request.role)
hostRepository.save(host)
val hostUser = host.getHostUserByUserId(targetUserId)
val userName = runCatching { userAdaptor.queryUser(targetUserId).profile?.name }.getOrNull()
return AdminHostMemberResponse.of(hostUser, userName)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateHostPartnerUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminUpdateHostPartnerRequest
import band.gosrock.admin.model.dto.response.AdminHostDetailResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.host.repository.HostRepository
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminUpdateHostPartnerUseCase(
private val hostAdaptor: HostAdaptor,
private val hostRepository: HostRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, hostId: Long, request: AdminUpdateHostPartnerRequest): AdminHostDetailResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val host = hostAdaptor.findById(hostId)
host.changePartner(request.partner)
hostRepository.save(host)
return AdminHostDetailResponse.from(host)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateHostProfileUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminUpdateHostProfileRequest
import band.gosrock.admin.model.dto.response.AdminHostDetailResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.host.domain.HostProfile
import band.gosrock.domain.domains.host.repository.HostRepository
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminUpdateHostProfileUseCase(
private val hostAdaptor: HostAdaptor,
private val hostRepository: HostRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, hostId: Long, request: AdminUpdateHostProfileRequest): AdminHostDetailResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val host = hostAdaptor.findById(hostId)
val current = host.profile
val updatedProfile = HostProfile(
name = request.name ?: current?.name,
introduce = request.introduce ?: current?.introduce,
profileImageKey = current?.profileImage?.imageKey,
contactEmail = request.contactEmail ?: current?.contactEmail,
contactNumber = request.contactNumber ?: current?.contactNumber,
)
host.updateProfile(updatedProfile)
hostRepository.save(host)
return AdminHostDetailResponse.from(host)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateRefundStatusUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminRefundStatusRequest
import band.gosrock.admin.model.dto.response.AdminOrderResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.order.adaptor.OrderAdaptor
import band.gosrock.domain.domains.order.domain.RefundStatus
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminUpdateRefundStatusUseCase(
private val orderAdaptor: OrderAdaptor,
private val userAdaptor: UserAdaptor,
private val eventAdaptor: EventAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, orderUuid: String, request: AdminRefundStatusRequest): AdminOrderResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val order = orderAdaptor.findByOrderUuid(orderUuid)
when (RefundStatus.valueOf(request.refundStatus)) {
RefundStatus.REFUND_COMPLETED -> order.completeRefund()
else -> throw IllegalArgumentException("허용되지 않는 환불 상태입니다: ${request.refundStatus}")
}
val userName = order.userId?.let {
runCatching { userAdaptor.queryUser(it).profile?.name }.getOrNull()
}
val eventName = order.eventId?.let {
runCatching { eventAdaptor.findById(it).eventBasic?.name }.getOrNull()
}
return AdminOrderResponse.of(order, userName, eventName)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateTicketItemUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminUpdateTicketItemRequest
import band.gosrock.admin.model.dto.response.AdminTicketItemResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.common.vo.Money
import band.gosrock.domain.domains.ticket_item.adaptor.TicketItemAdaptor
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminUpdateTicketItemUseCase(
private val ticketItemAdaptor: TicketItemAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, eventId: Long, ticketItemId: Long, request: AdminUpdateTicketItemRequest): AdminTicketItemResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val ticketItem = ticketItemAdaptor.queryTicketItem(ticketItemId)
ticketItem.validateEventId(eventId)
val money = request.price?.let { Money(it) }
// 어드민은 이벤트 상태 체크 없이 수정 가능
ticketItem.adminUpdate(
name = request.name,
description = request.description,
price = money,
quantity = request.quantity,
purchaseLimit = request.purchaseLimit,
)
ticketItemAdaptor.save(ticketItem)
return AdminTicketItemResponse.from(ticketItem)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateUserRoleUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminUpdateUserRoleRequest
import band.gosrock.admin.model.dto.response.AdminUserResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import band.gosrock.domain.domains.user.repository.UserRepository
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminUpdateUserRoleUseCase(
private val userAdaptor: UserAdaptor,
private val userRepository: UserRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(currentUserId: Long, targetUserId: Long, request: AdminUpdateUserRoleRequest): AdminUserResponse {
adminAuthValidator.validateSuperAdmin(currentUserId)
val targetUser = userAdaptor.queryUser(targetUserId)
targetUser.changeRole(request.role)
userRepository.save(targetUser)
return AdminUserResponse.from(targetUser)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateUserStatusUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminUpdateUserStatusRequest
import band.gosrock.admin.model.dto.response.AdminUserResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import band.gosrock.domain.domains.user.repository.UserRepository
import org.springframework.transaction.annotation.Transactional
@UseCase
class AdminUpdateUserStatusUseCase(
private val userAdaptor: UserAdaptor,
private val userRepository: UserRepository,
private val adminAuthValidator: AdminAuthValidator,
) {
@Transactional
fun execute(userId: Long, targetUserId: Long, request: AdminUpdateUserStatusRequest): AdminUserResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val targetUser = userAdaptor.queryUser(targetUserId)
targetUser.changeAccountState(request.status)
userRepository.save(targetUser)
return AdminUserResponse.from(targetUser)
}
}
================================================
FILE: DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/GetDashboardUseCase.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminEventResponse
import band.gosrock.admin.model.dto.response.AdminOrderResponse
import band.gosrock.admin.model.dto.response.DashboardResponse
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.event.domain.EventStatus
import band.gosrock.domain.domains.event.repository.EventRepository
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.order.domain.OrderStatus
import band.gosrock.domain.domains.order.repository.OrderRepository
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import band.gosrock.domain.domains.user.repository.UserRepository
import java.time.LocalDate
import java.time.LocalDateTime
import org.springframework.data.domain.PageRequest
import org.springframework.transaction.annotation.Transactional
@UseCase
@Transactional(readOnly = true)
class GetDashboardUseCase(
private val userRepository: UserRepository,
private val orderRepository: OrderRepository,
private val eventRepository: EventRepository,
private val userAdaptor: UserAdaptor,
private val hostAdaptor: HostAdaptor,
private val adminAuthValidator: AdminAuthValidator,
) {
fun execute(userId: Long, startDate: LocalDate? = null, endDate: LocalDate? = null): DashboardResponse {
adminAuthValidator.validateAdminOrAbove(userId)
val totalUsers = userRepository.count()
val activeEvents = eventRepository.countByStatusNative(EventStatus.OPEN.statusName)
val periodNewUsers: Long
val periodOrders: Long
val periodRevenue: Long
val periodRefunds: Long
if (startDate != null && endDate != null) {
val start: LocalDateTime = startDate.atStartOfDay()
val end: LocalDateTime = endDate.plusDays(1).atStartOfDay()
periodNewUsers = userRepository.countByCreatedAtBetween(start, end)
periodOrders = orderRepository.countByCreatedAtBetween(start, end)
periodRefunds = orderRepository.countByCreatedAtBetweenAndOrderStatus(start, end, OrderStatus.REFUND)
val confirmedOrders = orderRepository.findByCreatedAtBetweenAndOrderStatusIn(
start, end,
listOf(OrderStatus.CONFIRM, OrderStatus.APPROVED)
)
periodRevenue = confirmedOrders.sumOf { it.getTotalPaymentPrice().longValue() }
} else {
val todayStart: LocalDateTime = LocalDate.now().atStartOfDay()
periodNewUsers = userRepository.countByCreatedAtAfter(todayStart)
periodOrders = orderRepository.countByCreatedAtAfter(todayStart)
periodRefunds = orderRepository.countByCreatedAtAfterAndOrderStatus(todayStart, OrderStatus.REFUND)
val confirmedOrders = orderRepository.findByCreatedAtAfterAndOrderStatusIn(
todayStart,
listOf(OrderStatus.CONFIRM, OrderStatus.APPROVED)
)
periodRevenue = confirmedOrders.sumOf { it.getTotalPaymentPrice().longValue() }
}
// 최근 주문 5건
val recentOrders = orderRepository.findTopNByOrderByCreatedAtDesc(PageRequest.of(0, 5))
.map { order ->
val userName = order.userId?.let {
runCatching { userAdaptor.queryUser(it).profile?.name }.getOrNull()
}
val eventName = order.eventId?.let {
runCatching { eventRepository.findByIdForAdmin(it)?.eventBasic?.name }.getOrNull()
}
AdminOrderResponse.of(order, userName, eventName)
}
// 최근 이벤트 5건
val recentEvents = eventRepository.findTopNByOrderByCreatedAtDesc(5)
.map { event ->
val hostName = event.hostId?.let {
runCatching { hostAdaptor.findById(it).profile?.name }.getOrNull()
}
AdminEventResponse.of(event, hostName)
}
return DashboardResponse(
totalUsers = totalUsers,
todayNewUsers = periodNewUsers,
todayOrders = periodOrders,
todayRevenue = periodRevenue,
activeEvents = activeEvents,
todayRefunds = periodRefunds,
recentOrders = recentOrders,
recentEvents = recentEvents,
)
}
}
================================================
FILE: DuDoong-Admin/src/test/kotlin/band/gosrock/admin/service/AdminAuthValidatorTest.kt
================================================
package band.gosrock.admin.service
import band.gosrock.common.exception.DuDoongCodeException
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import band.gosrock.domain.domains.user.domain.AccountRole
import band.gosrock.domain.domains.user.domain.User
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.junit.jupiter.MockitoExtension
import org.springframework.test.util.ReflectionTestUtils
@ExtendWith(MockitoExtension::class)
@DisplayName("AdminAuthValidator")
class AdminAuthValidatorTest {
@Mock
private lateinit var userAdaptor: UserAdaptor
private lateinit var adminAuthValidator: AdminAuthValidator
@BeforeEach
fun setUp() {
adminAuthValidator = AdminAuthValidator(userAdaptor)
}
private fun createUser(userId: Long, role: AccountRole): User {
val user = User()
ReflectionTestUtils.setField(user, "id", userId)
ReflectionTestUtils.setField(user, "accountRole", role)
return user
}
@Nested
@DisplayName("validateAdminOrAbove")
inner class ValidateAdminOrAboveTest {
@Test
@DisplayName("USER 역할이면 예외 발생")
fun userRoleDenied() {
val user = createUser(1L, AccountRole.USER)
`when`(userAdaptor.queryUser(1L)).thenReturn(user)
assertThrows(DuDoongCodeException::class.java) {
adminAuthValidator.validateAdminOrAbove(1L)
}
}
@Test
@DisplayName("MANAGER 역할이면 예외 발생")
fun managerRoleDenied() {
val user = createUser(1L, AccountRole.MANAGER)
`when`(userAdaptor.queryUser(1L)).thenReturn(user)
assertThrows(DuDoongCodeException::class.java) {
adminAuthValidator.validateAdminOrAbove(1L)
}
}
@Test
@DisplayName("ADMIN 역할이면 허용")
fun adminRoleAllowed() {
val user = createUser(1L, AccountRole.ADMIN)
`when`(userAdaptor.queryUser(1L)).thenReturn(user)
val result = adminAuthValidator.validateAdminOrAbove(1L)
assertNotNull(result)
assertEquals(AccountRole.ADMIN, result.accountRole)
}
@Test
@DisplayName("SUPER_ADMIN 역할이면 허용")
fun superAdminRoleAllowed() {
val user = createUser(1L, AccountRole.SUPER_ADMIN)
`when`(userAdaptor.queryUser(1L)).thenReturn(user)
val result = adminAuthValidator.validateAdminOrAbove(1L)
assertNotNull(result)
assertEquals(AccountRole.SUPER_ADMIN, result.accountRole)
}
}
@Nested
@DisplayName("validateSuperAdmin")
inner class ValidateSuperAdminTest {
@Test
@DisplayName("USER 역할이면 예외 발생")
fun userRoleDenied() {
val user = createUser(1L, AccountRole.USER)
`when`(userAdaptor.queryUser(1L)).thenReturn(user)
assertThrows(DuDoongCodeException::class.java) {
adminAuthValidator.validateSuperAdmin(1L)
}
}
@Test
@DisplayName("MANAGER 역할이면 예외 발생")
fun managerRoleDenied() {
val user = createUser(1L, AccountRole.MANAGER)
`when`(userAdaptor.queryUser(1L)).thenReturn(user)
assertThrows(DuDoongCodeException::class.java) {
adminAuthValidator.validateSuperAdmin(1L)
}
}
@Test
@DisplayName("ADMIN 역할이면 예외 발생")
fun adminRoleDenied() {
val user = createUser(1L, AccountRole.ADMIN)
`when`(userAdaptor.queryUser(1L)).thenReturn(user)
assertThrows(DuDoongCodeException::class.java) {
adminAuthValidator.validateSuperAdmin(1L)
}
}
@Test
@DisplayName("SUPER_ADMIN 역할이면 허용")
fun superAdminRoleAllowed() {
val user = createUser(1L, AccountRole.SUPER_ADMIN)
`when`(userAdaptor.queryUser(1L)).thenReturn(user)
val result = adminAuthValidator.validateSuperAdmin(1L)
assertNotNull(result)
assertEquals(AccountRole.SUPER_ADMIN, result.accountRole)
}
}
}
================================================
FILE: DuDoong-Admin/src/test/kotlin/band/gosrock/admin/service/AdminChangeNameUseCaseTest.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminChangeNameRequest
import band.gosrock.common.exception.DuDoongCodeException
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import band.gosrock.domain.domains.user.domain.AccountRole
import band.gosrock.domain.domains.user.domain.Profile
import band.gosrock.domain.domains.user.domain.User
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.junit.jupiter.MockitoExtension
import org.springframework.test.util.ReflectionTestUtils
@ExtendWith(MockitoExtension::class)
@DisplayName("AdminChangeNameUseCase")
class AdminChangeNameUseCaseTest {
@Mock
private lateinit var userAdaptor: UserAdaptor
private lateinit var adminAuthValidator: AdminAuthValidator
private lateinit var adminChangeNameUseCase: AdminChangeNameUseCase
@BeforeEach
fun setUp() {
adminAuthValidator = AdminAuthValidator(userAdaptor)
adminChangeNameUseCase = AdminChangeNameUseCase(adminAuthValidator, userAdaptor)
}
private fun createUser(userId: Long, role: AccountRole, name: String = "기존이름"): User {
val user = User(profile = Profile(name = name))
ReflectionTestUtils.setField(user, "id", userId)
ReflectionTestUtils.setField(user, "accountRole", role)
return user
}
@Nested
@DisplayName("execute")
inner class ExecuteTest {
@Test
@DisplayName("ADMIN이 유저 이름을 변경하면 성공한다")
fun adminCanChangeName() {
val admin = createUser(1L, AccountRole.ADMIN)
val target = createUser(2L, AccountRole.USER, "기존이름")
`when`(userAdaptor.queryUser(1L)).thenReturn(admin)
`when`(userAdaptor.queryUser(2L)).thenReturn(target)
val request = AdminChangeNameRequest(name = "새이름입니다")
adminChangeNameUseCase.execute(1L, 2L, request)
assertEquals("새이름입니다", target.profile?.name)
}
@Test
@DisplayName("SUPER_ADMIN이 유저 이름을 변경하면 성공한다")
fun superAdminCanChangeName() {
val superAdmin = createUser(1L, AccountRole.SUPER_ADMIN)
val target = createUser(2L, AccountRole.USER, "기존이름")
`when`(userAdaptor.queryUser(1L)).thenReturn(superAdmin)
`when`(userAdaptor.queryUser(2L)).thenReturn(target)
val request = AdminChangeNameRequest(name = "변경됨")
adminChangeNameUseCase.execute(1L, 2L, request)
assertEquals("변경됨", target.profile?.name)
}
@Test
@DisplayName("USER 역할이면 예외가 발생한다")
fun userRoleDenied() {
val user = createUser(1L, AccountRole.USER)
`when`(userAdaptor.queryUser(1L)).thenReturn(user)
val request = AdminChangeNameRequest(name = "새이름입니다")
assertThrows(DuDoongCodeException::class.java) {
adminChangeNameUseCase.execute(1L, 2L, request)
}
}
}
}
================================================
FILE: DuDoong-Admin/src/test/kotlin/band/gosrock/admin/service/AdminCompleteRefundUseCaseTest.kt
================================================
package band.gosrock.admin.service
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.order.adaptor.OrderAdaptor
import band.gosrock.domain.domains.order.domain.Order
import band.gosrock.domain.domains.order.domain.OrderMethod
import band.gosrock.domain.domains.order.domain.OrderStatus
import band.gosrock.domain.domains.order.domain.RefundStatus
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.BDDMockito.given
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
@ExtendWith(MockitoExtension::class)
@DisplayName("AdminCompleteRefundUseCase")
class AdminCompleteRefundUseCaseTest {
@Mock
private lateinit var orderAdaptor: OrderAdaptor
@Mock
private lateinit var userAdaptor: UserAdaptor
@Mock
private lateinit var eventAdaptor: EventAdaptor
@Mock
private lateinit var adminAuthValidator: AdminAuthValidator
@InjectMocks
private lateinit var adminCompleteRefundUseCase: AdminCompleteRefundUseCase
@Test
@DisplayName("어드민 환불 확인 시 refundStatus가 REFUND_COMPLETED로 변경된다")
fun completeRefund() {
// given
val order = Order.forTest(
userId = 1L,
orderName = "테스트주문",
orderStatus = OrderStatus.CANCELED,
orderMethod = OrderMethod.PAYMENT,
eventId = 100L,
cancelReason = "단순 변심",
refundStatus = RefundStatus.REFUND_REQUESTED,
)
given(orderAdaptor.findByOrderUuid("test-uuid")).willReturn(order)
// when
val response = adminCompleteRefundUseCase.execute(1L, "test-uuid")
// then
assertEquals(RefundStatus.REFUND_COMPLETED, response.refundStatus)
assertEquals(RefundStatus.REFUND_COMPLETED, order.refundStatus)
assertNotNull(order.refundStatusChangedAt)
assertNotNull(response.userId)
assertEquals(1L, response.userId)
}
}
================================================
FILE: DuDoong-Admin/src/test/kotlin/band/gosrock/admin/service/AdminExcelServiceTest.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.response.AdminIssuedTicketResponse
import band.gosrock.admin.model.dto.response.AdminOrderResponse
import band.gosrock.domain.common.vo.Money
import band.gosrock.domain.domains.issuedTicket.domain.IssuedTicket
import band.gosrock.domain.domains.issuedTicket.domain.IssuedTicketOptionAnswer
import band.gosrock.domain.domains.issuedTicket.domain.IssuedTicketStatus
import band.gosrock.domain.domains.order.domain.OrderStatus
import band.gosrock.domain.domains.ticket_item.domain.Option
import band.gosrock.domain.domains.ticket_item.domain.OptionGroup
import band.gosrock.domain.domains.ticket_item.domain.OptionGroupType
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.test.util.ReflectionTestUtils
import java.io.ByteArrayInputStream
import java.time.LocalDateTime
@DisplayName("AdminExcelService")
class AdminExcelServiceTest {
private lateinit var adminExcelService: AdminExcelService
@BeforeEach
fun setUp() {
adminExcelService = AdminExcelService()
}
@Nested
@DisplayName("generateOrdersExcel")
inner class GenerateOrdersExcelTest {
@Test
@DisplayName("주문 엑셀 헤더 컬럼이 올바르게 생성된다")
fun headerColumnsAreCorrect() {
val bytes = adminExcelService.generateOrdersExcel(emptyList())
val workbook = XSSFWorkbook(ByteArrayInputStream(bytes))
val sheet = workbook.getSheetAt(0)
val headerRow = sheet.getRow(0)
val expectedHeaders = listOf(
"주문번호", "주문번호(읽기용)", "사용자", "이벤트", "티켓", "금액", "상태",
"주문방식", "결제수단", "승인일시", "취소일시", "할인금액", "쿠폰명", "주문일",
)
expectedHeaders.forEachIndexed { i, expected ->
assertEquals(expected, headerRow.getCell(i).stringCellValue, "헤더[$i]")
}
assertEquals(expectedHeaders.size, headerRow.lastCellNum.toInt())
workbook.close()
}
@Test
@DisplayName("주문 데이터 행이 올바르게 생성된다")
fun dataRowsAreCorrect() {
val now = LocalDateTime.of(2026, 3, 22, 10, 0)
val orders = listOf(
AdminOrderResponse(
orderId = "uuid-1",
orderNo = "R100001",
userName = "테스트유저",
eventName = "테스트이벤트",
ticketName = "일반 티켓",
totalAmount = 10000L,
orderStatus = OrderStatus.CONFIRM,
createdAt = now,
orderMethod = "PAYMENT",
userId = 1L,
eventId = 1L,
approvedAt = now.plusMinutes(5),
withDrawAt = null,
paymentMethod = "CARD",
receiptUrl = "https://example.com/receipt",
supplyAmount = "10000",
discountAmount = "1000",
couponName = "첫주문쿠폰",
),
)
val bytes = adminExcelService.generateOrdersExcel(orders)
val workbook = XSSFWorkbook(ByteArrayInputStream(bytes))
val sheet = workbook.getSheetAt(0)
val dataRow = sheet.getRow(1)
assertEquals("uuid-1", dataRow.getCell(0).stringCellValue)
assertEquals("R100001", dataRow.getCell(1).stringCellValue)
assertEquals("테스트유저", dataRow.getCell(2).stringCellValue)
assertEquals("테스트이벤트", dataRow.getCell(3).stringCellValue)
assertEquals("일반 티켓", dataRow.getCell(4).stringCellValue)
assertEquals(10000.0, dataRow.getCell(5).numericCellValue)
assertEquals("CONFIRM", dataRow.getCell(6).stringCellValue)
assertEquals("PAYMENT", dataRow.getCell(7).stringCellValue)
assertEquals("CARD", dataRow.getCell(8).stringCellValue)
assertEquals(now.plusMinutes(5).toString(), dataRow.getCell(9).stringCellValue)
assertEquals("", dataRow.getCell(10).stringCellValue) // withDrawAt null
assertEquals("1000", dataRow.getCell(11).stringCellValue)
assertEquals("첫주문쿠폰", dataRow.getCell(12).stringCellValue)
assertEquals(now.toString(), dataRow.getCell(13).stringCellValue)
workbook.close()
}
}
@Nested
@DisplayName("generateIssuedTicketsExcel")
inner class GenerateIssuedTicketsExcelTest {
@Test
@DisplayName("기본 발급 티켓 엑셀 헤더가 올바르게 생성된다")
fun basicHeaderColumnsAreCorrect() {
val bytes = adminExcelService.generateIssuedTicketsExcel(emptyList())
val workbook = XSSFWorkbook(ByteArrayInputStream(bytes))
val sheet = workbook.getSheetAt(0)
val headerRow = sheet.getRow(0)
val expectedHeaders = listOf("티켓번호", "유저명", "티켓종류", "주문번호", "입장여부", "발급일")
expectedHeaders.forEachIndexed { i, expected ->
assertEquals(expected, headerRow.getCell(i).stringCellValue, "헤더[$i]")
}
assertEquals(expectedHeaders.size, headerRow.lastCellNum.toInt())
workbook.close()
}
}
@Nested
@DisplayName("generateIssuedTicketsExcelWithOptions")
inner class GenerateIssuedTicketsExcelWithOptionsTest {
@Test
@DisplayName("옵션 동적 컬럼이 헤더에 추가된다")
fun optionDynamicHeadersAreAdded() {
val optionGroup = createOptionGroup(1L, "이름 수집용", OptionGroupType.SUBJECTIVE)
val option1 = createOption(10L, "참석자 이름", optionGroup)
val option2 = createOption(11L, "연락처", optionGroup)
val bytes = adminExcelService.generateIssuedTicketsExcelWithOptions(
tickets = emptyList(),
options = listOf(option1, option2),
issuedTickets = emptyList(),
)
val workbook = XSSFWorkbook(ByteArrayInputStream(bytes))
val sheet = workbook.getSheetAt(0)
val headerRow = sheet.getRow(0)
// 기본 6 컬럼 + 옵션 2 컬럼
assertEquals(8, headerRow.lastCellNum.toInt())
assertEquals("참석자 이름", headerRow.getCell(6).stringCellValue)
assertEquals("연락처", headerRow.getCell(7).stringCellValue)
workbook.close()
}
@Test
@DisplayName("옵션 응답이 올바르게 매핑된다")
fun optionAnswersAreMapped() {
val optionGroup = createOptionGroup(1L, "참석자 정보", OptionGroupType.SUBJECTIVE)
val option1 = createOption(10L, "참석자 이름", optionGroup)
val option2 = createOption(11L, "연락처", optionGroup)
val now = LocalDateTime.of(2026, 3, 22, 10, 0)
// IssuedTicket 엔티티 생성 (옵션 응답 포함)
val answer1 = IssuedTicketOptionAnswer(optionId = 10L, answer = "홍길동")
val answer2 = IssuedTicketOptionAnswer(optionId = 11L, answer = "010-1234-5678")
val issuedTicket = createIssuedTicket(100L, listOf(answer1, answer2))
val ticketResponse = AdminIssuedTicketResponse(
id = 100L,
issuedTicketNo = "T100001",
userName = "테스트유저",
ticketName = "일반 티켓",
orderUuid = "order-uuid-1",
enteredAt = null,
status = IssuedTicketStatus.ENTRANCE_INCOMPLETE,
createdAt = now,
)
val bytes = adminExcelService.generateIssuedTicketsExcelWithOptions(
tickets = listOf(ticketResponse),
options = listOf(option1, option2),
issuedTickets = listOf(issuedTicket),
)
val workbook = XSSFWorkbook(ByteArrayInputStream(bytes))
val sheet = workbook.getSheetAt(0)
val dataRow = sheet.getRow(1)
// 기본 컬럼 확인
assertEquals("T100001", dataRow.getCell(0).stringCellValue)
assertEquals("테스트유저", dataRow.getCell(1).stringCellValue)
assertEquals("일반 티켓", dataRow.getCell(2).stringCellValue)
assertEquals("order-uuid-1", dataRow.getCell(3).stringCellValue)
assertEquals("미입장", dataRow.getCell(4).stringCellValue)
// 옵션 응답 컬럼 확인
assertEquals("홍길동", dataRow.getCell(6).stringCellValue)
assertEquals("010-1234-5678", dataRow.getCell(7).stringCellValue)
workbook.close()
}
@Test
@DisplayName("옵션 응답이 없는 경우 빈 문자열로 채워진다")
fun missingOptionAnswersAreFilled() {
val optionGroup = createOptionGroup(1L, "참석자 정보", OptionGroupType.SUBJECTIVE)
val option1 = createOption(10L, "참석자 이름", optionGroup)
val option2 = createOption(11L, "연락처", optionGroup)
val now = LocalDateTime.of(2026, 3, 22, 10, 0)
// 옵션 응답이 하나만 있는 경우
val answer1 = IssuedTicketOptionAnswer(optionId = 10L, answer = "홍길동")
val issuedTicket = createIssuedTicket(100L, listOf(answer1))
val ticketResponse = AdminIssuedTicketResponse(
id = 100L,
issuedTicketNo = "T100001",
userName = "테스트유저",
ticketName = "일반 티켓",
orderUuid = "order-uuid-1",
enteredAt = null,
status = IssuedTicketStatus.ENTRANCE_INCOMPLETE,
createdAt = now,
)
val bytes = adminExcelService.generateIssuedTicketsExcelWithOptions(
tickets = listOf(ticketResponse),
options = listOf(option1, option2),
issuedTickets = listOf(issuedTicket),
)
val workbook = XSSFWorkbook(ByteArrayInputStream(bytes))
val sheet = workbook.getSheetAt(0)
val dataRow = sheet.getRow(1)
assertEquals("홍길동", dataRow.getCell(6).stringCellValue)
assertEquals("", dataRow.getCell(7).stringCellValue) // 응답 없음
workbook.close()
}
}
// --- Helper Methods ---
private fun createOptionGroup(id: Long, name: String, type: OptionGroupType): OptionGroup {
val optionGroup = OptionGroup(
eventId = 1L,
type = type,
name = name,
description = "설명",
isEssential = true,
)
ReflectionTestUtils.setField(optionGroup, "id", id)
return optionGroup
}
private fun createOption(id: Long, questionName: String, optionGroup: OptionGroup): Option {
val og = OptionGroup(
eventId = 1L,
type = optionGroup.type,
name = questionName,
description = optionGroup.description,
isEssential = optionGroup.isEssential,
)
ReflectionTestUtils.setField(og, "id", optionGroup.id)
val option = Option(answer = "", additionalPrice = Money.ZERO, optionGroup = og)
ReflectionTestUtils.setField(option, "id", id)
return option
}
private fun createIssuedTicket(id: Long, optionAnswers: List<IssuedTicketOptionAnswer>): IssuedTicket {
val ticket = IssuedTicket(
eventId = 1L,
initialOptionAnswers = optionAnswers,
)
ReflectionTestUtils.setField(ticket, "id", id)
return ticket
}
}
================================================
FILE: DuDoong-Admin/src/test/kotlin/band/gosrock/admin/service/AdminUpdateRefundStatusUseCaseTest.kt
================================================
package band.gosrock.admin.service
import band.gosrock.admin.model.dto.request.AdminRefundStatusRequest
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.order.adaptor.OrderAdaptor
import band.gosrock.domain.domains.order.domain.Order
import band.gosrock.domain.domains.order.domain.OrderMethod
import band.gosrock.domain.domains.order.domain.OrderStatus
import band.gosrock.domain.domains.order.domain.RefundStatus
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import band.gosrock.domain.domains.user.domain.AccountRole
import band.gosrock.domain.domains.user.domain.User
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.junit.jupiter.MockitoExtension
import org.springframework.test.util.ReflectionTestUtils
@ExtendWith(MockitoExtension::class)
@DisplayName("AdminUpdateRefundStatusUseCase")
class AdminUpdateRefundStatusUseCaseTest {
@Mock
private lateinit var orderAdaptor: OrderAdaptor
@Mock
private lateinit var userAdaptor: UserAdaptor
@Mock
private lateinit var eventAdaptor: EventAdaptor
@Mock
private lateinit var adminAuthValidator: AdminAuthValidator
private lateinit var useCase: AdminUpdateRefundStatusUseCase
private lateinit var order: Order
@BeforeEach
fun setUp() {
useCase = AdminUpdateRefundStatusUseCase(orderAdaptor, userAdaptor, eventAdaptor, adminAuthValidator)
order = Order.forTest(
userId = 10L,
orderName = "테스트주문",
orderStatus = OrderStatus.CANCELED,
orderMethod = OrderMethod.PAYMENT,
eventId = 100L,
cancelReason = "단순 변심",
refundStatus = RefundStatus.REFUND_REQUESTED,
)
}
private fun createAdminUser(): User {
val user = User()
ReflectionTestUtils.setField(user, "id", 1L)
ReflectionTestUtils.setField(user, "accountRole", AccountRole.ADMIN)
return user
}
@Test
@DisplayName("환불 완료 처리 시 refundStatus가 REFUND_COMPLETED로 변경된다")
fun completeRefund() {
val adminUser = createAdminUser()
`when`(adminAuthValidator.validateAdminOrAbove(1L)).thenReturn(adminUser)
`when`(orderAdaptor.findByOrderUuid("test-uuid")).thenReturn(order)
ReflectionTestUtils.setField(order, "uuid", "test-uuid")
val request = AdminRefundStatusRequest(refundStatus = "REFUND_COMPLETED")
useCase.execute(1L, "test-uuid", request)
assertEquals(RefundStatus.REFUND_COMPLETED, order.refundStatus)
assertNotNull(order.refundStatusChangedAt)
}
@Test
@DisplayName("허용되지 않는 refundStatus 값이면 예외가 발생한다")
fun invalidRefundStatus() {
val adminUser = createAdminUser()
`when`(adminAuthValidator.validateAdminOrAbove(1L)).thenReturn(adminUser)
`when`(orderAdaptor.findByOrderUuid("test-uuid")).thenReturn(order)
ReflectionTestUtils.setField(order, "uuid", "test-uuid")
val request = AdminRefundStatusRequest(refundStatus = "NONE")
assertThrows(IllegalArgumentException::class.java) {
useCase.execute(1L, "test-uuid", request)
}
}
}
================================================
FILE: DuDoong-Api/Dockerfile
================================================
FROM eclipse-temurin:21-jre-alpine
EXPOSE 8080
COPY ./build/libs/*.jar app.jar
ARG PROFILE=prod
ENV PROFILE=${PROFILE}
ENTRYPOINT ["java","-Dspring.profiles.active=${PROFILE}", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
================================================
FILE: DuDoong-Api/build.gradle.kts
================================================
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation(project(":DuDoong-Domain"))
implementation(project(":DuDoong-Common"))
implementation(project(":DuDoong-Infrastructure"))
implementation(project(":DuDoong-Admin"))
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/DuDoongApiServerApplication.kt
================================================
package band.gosrock
import org.slf4j.LoggerFactory
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.ApplicationListener
import org.springframework.context.annotation.Bean
import org.springframework.core.env.Environment
import org.springframework.web.filter.ForwardedHeaderFilter
@SpringBootApplication
class DuDoongApiServerApplication(
private val environment: Environment
) : ApplicationListener<ApplicationReadyEvent> {
private val log = LoggerFactory.getLogger(DuDoongApiServerApplication::class.java)
@Bean
fun forwardedHeaderFilter(): ForwardedHeaderFilter = ForwardedHeaderFilter()
override fun onApplicationEvent(event: ApplicationReadyEvent) {
log.info("applicationReady status${environment.activeProfiles.contentToString()}")
}
}
fun main(args: Array<String>) {
SpringApplication.run(DuDoongApiServerApplication::class.java, *args)
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/admin/controller/AdminAuthController.kt
================================================
package band.gosrock.api.admin.controller
import band.gosrock.admin.model.dto.response.AdminUserDetailResponse
import band.gosrock.admin.service.AdminGetMeUseCase
import band.gosrock.common.annotation.CurrentUserId
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/internal-api/v1/auth")
@Tag(name = "Admin Auth")
class AdminAuthController(
private val adminGetMeUseCase: AdminGetMeUseCase,
) {
@Operation(summary = "현재 어드민 유저 정보 조회")
@SecurityRequirement(name = "admin-token")
@GetMapping("/me")
fun getAdminMe(@CurrentUserId userId: Long): AdminUserDetailResponse {
return adminGetMeUseCase.execute(userId)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/dto/OrderAlimTalkDto.kt
================================================
package band.gosrock.api.alimTalk.dto
import band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkEventInfo
import band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkOrderInfo
import band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkUserInfo
data class OrderAlimTalkDto(
val userInfo: AlimTalkUserInfo,
val orderInfo: AlimTalkOrderInfo,
val eventInfo: AlimTalkEventInfo,
)
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/handler/DoneOrderEventAlimTalkHandler.kt
================================================
package band.gosrock.api.alimTalk.handler
import band.gosrock.api.alimTalk.service.SendDoneOrderAlimTalkService
import band.gosrock.api.alimTalk.service.helper.OrderAlimTalkInfoHelper
import band.gosrock.domain.common.events.order.DoneOrderEvent
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.order.adaptor.OrderAdaptor
import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import org.springframework.transaction.event.TransactionPhase
import org.springframework.transaction.event.TransactionalEventListener
@Component
class DoneOrderEventAlimTalkHandler(
private val sendDoneOrderAlimTalkService: SendDoneOrderAlimTalkService,
private val orderAlimTalkInfoHelper: OrderAlimTalkInfoHelper,
private val orderAdaptor: OrderAdaptor,
private val eventAdaptor: EventAdaptor,
private val hostAdaptor: HostAdaptor,
) {
private val log = LoggerFactory.getLogger(DoneOrderEventAlimTalkHandler::class.java)
@Async
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
@TransactionalEventListener(classes = [DoneOrderEvent::class], phase = TransactionPhase.AFTER_COMMIT)
fun handleDoneOrderEvent(doneOrderEvent: DoneOrderEvent) {
try {
log.info("${doneOrderEvent.orderUuid}주문 상태 완료, 파트너의 공연이면 알림톡 전송")
val order = orderAdaptor.findByOrderUuid(doneOrderEvent.orderUuid)
val event = eventAdaptor.findById(order.eventId!!)
val host = hostAdaptor.findById(event.hostId!!)
if (host.isPartnerHost()) {
val orderAlimTalkDto = orderAlimTalkInfoHelper.execute(order, event, host)
sendDoneOrderAlimTalkService.execute(orderAlimTalkDto)
log.info("${doneOrderEvent.orderUuid}주문 상태 완료, 파트너의 공연 알림톡 전송 완료")
}
} catch (e: Exception) {
log.warn("주문 완료 알림톡 전송 실패 (orderUuid=${doneOrderEvent.orderUuid}): ${e.message}")
}
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/handler/RegisterUserEventAlimTalkHandler.kt
================================================
package band.gosrock.api.alimTalk.handler
import band.gosrock.api.alimTalk.service.SendRegisterAlimTalkService
import band.gosrock.domain.common.events.user.UserRegisterEvent
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Component
import org.springframework.transaction.event.TransactionPhase
import org.springframework.transaction.event.TransactionalEventListener
@Component
class RegisterUserEventAlimTalkHandler(
private val userAdaptor: UserAdaptor,
private val sendRegisterAlimTalkService: SendRegisterAlimTalkService,
) {
private val log = LoggerFactory.getLogger(RegisterUserEventAlimTalkHandler::class.java)
@Async
@TransactionalEventListener(classes = [UserRegisterEvent::class], phase = TransactionPhase.AFTER_COMMIT)
fun handleRegisterUserEvent(userRegisterEvent: UserRegisterEvent) {
val userId = userRegisterEvent.userId
try {
val user = userAdaptor.queryUser(userId)
log.info("${userId}유저 등록")
val userInfo = user.toAlimTalkUserInfo()
sendRegisterAlimTalkService.execute(userInfo.userName, userInfo.phoneNum)
} catch (e: Exception) {
log.warn("유저 등록 알림톡 전송 실패 (userId=$userId): ${e.message}")
}
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/handler/WithDrawOrderEventAlimTalkHandler.kt
================================================
package band.gosrock.api.alimTalk.handler
import band.gosrock.api.alimTalk.service.SendWithdrawOrderAlimTalkService
import band.gosrock.api.alimTalk.service.helper.OrderAlimTalkInfoHelper
import band.gosrock.domain.common.events.order.WithDrawOrderEvent
import band.gosrock.domain.domains.event.adaptor.EventAdaptor
import band.gosrock.domain.domains.host.adaptor.HostAdaptor
import band.gosrock.domain.domains.order.adaptor.OrderAdaptor
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import org.springframework.transaction.event.TransactionPhase
import org.springframework.transaction.event.TransactionalEventListener
@Component
class WithDrawOrderEventAlimTalkHandler(
private val sendWithdrawOrderAlimTalkService: SendWithdrawOrderAlimTalkService,
private val orderAlimTalkInfoHelper: OrderAlimTalkInfoHelper,
private val orderAdaptor: OrderAdaptor,
private val eventAdaptor: EventAdaptor,
private val hostAdaptor: HostAdaptor,
) {
private val log = org.slf4j.LoggerFactory.getLogger(WithDrawOrderEventAlimTalkHandler::class.java)
@Async
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
@TransactionalEventListener(classes = [WithDrawOrderEvent::class], phase = TransactionPhase.AFTER_COMMIT)
fun handleWithDrawOrderEvent(withDrawOrderEvent: WithDrawOrderEvent) {
try {
val order = orderAdaptor.findByOrderUuid(withDrawOrderEvent.orderUuid)
val event = eventAdaptor.findById(order.eventId!!)
val host = hostAdaptor.findById(event.hostId!!)
if (host.isPartnerHost()) {
val orderAlimTalkDto = orderAlimTalkInfoHelper.execute(order, event, host)
sendWithdrawOrderAlimTalkService.execute(orderAlimTalkDto)
}
} catch (e: Exception) {
log.warn("주문 취소 알림톡 전송 실패 (orderUuid=${withDrawOrderEvent.orderUuid}): ${e.message}")
}
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/service/SendDoneOrderAlimTalkService.kt
================================================
package band.gosrock.api.alimTalk.service
import band.gosrock.api.alimTalk.dto.OrderAlimTalkDto
import band.gosrock.domain.common.alarm.OrderKakaoTalkAlarm
import band.gosrock.infrastructure.config.alilmTalk.NcpHelper
import org.springframework.stereotype.Service
@Service
class SendDoneOrderAlimTalkService(
private val ncpHelper: NcpHelper,
) {
fun execute(orderAlimTalkDto: OrderAlimTalkDto) {
val userInfo = orderAlimTalkDto.userInfo
val eventInfo = orderAlimTalkDto.eventInfo
val orderInfo = orderAlimTalkDto.orderInfo
val content = OrderKakaoTalkAlarm.creationOf(
userInfo.userName,
eventInfo.hostName,
eventInfo.eventName,
)
val headerContent = OrderKakaoTalkAlarm.creationHeaderOf()
ncpHelper.sendDoneOrderAlimTalk(
userInfo.phoneNum,
OrderKakaoTalkAlarm.creationTemplateCode(),
content,
headerContent,
orderInfo,
)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/service/SendRegisterAlimTalkService.kt
================================================
package band.gosrock.api.alimTalk.service
import band.gosrock.domain.common.alarm.UserKakaoTalkAlarm
import band.gosrock.infrastructure.config.alilmTalk.NcpHelper
import org.springframework.stereotype.Service
@Service
class SendRegisterAlimTalkService(
private val ncpHelper: NcpHelper,
) {
fun execute(userName: String, to: String) {
val content = UserKakaoTalkAlarm.creationOf(userName)
ncpHelper.sendButtonNcpAlimTalk(to, UserKakaoTalkAlarm.creationTemplateCode(), content)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/service/SendWithdrawOrderAlimTalkService.kt
================================================
package band.gosrock.api.alimTalk.service
import band.gosrock.api.alimTalk.dto.OrderAlimTalkDto
import band.gosrock.domain.common.alarm.OrderKakaoTalkAlarm
import band.gosrock.infrastructure.config.alilmTalk.NcpHelper
import org.springframework.stereotype.Service
@Service
class SendWithdrawOrderAlimTalkService(
private val ncpHelper: NcpHelper,
) {
fun execute(orderAlimTalkDto: OrderAlimTalkDto) {
val userInfo = orderAlimTalkDto.userInfo
val eventInfo = orderAlimTalkDto.eventInfo
val orderInfo = orderAlimTalkDto.orderInfo
val content = OrderKakaoTalkAlarm.deletionOf(
userInfo.userName,
eventInfo.hostName,
eventInfo.eventName,
)
val headerContent = OrderKakaoTalkAlarm.deletionHeaderOf()
ncpHelper.sendCancelOrderAlimTalk(
userInfo.phoneNum,
OrderKakaoTalkAlarm.deletionTemplateCode(),
content,
headerContent,
orderInfo,
)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/service/helper/OrderAlimTalkInfoHelper.kt
================================================
package band.gosrock.api.alimTalk.service.helper
import band.gosrock.api.alimTalk.dto.OrderAlimTalkDto
import band.gosrock.common.annotation.Helper
import band.gosrock.domain.domains.event.domain.Event
import band.gosrock.domain.domains.host.domain.Host
import band.gosrock.domain.domains.order.domain.Order
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkEventInfo
import org.springframework.transaction.annotation.Transactional
@Helper
@Transactional(readOnly = true)
class OrderAlimTalkInfoHelper(
private val userAdaptor: UserAdaptor,
) {
fun execute(order: Order, event: Event, host: Host): OrderAlimTalkDto {
val user = userAdaptor.queryUser(order.userId)
return OrderAlimTalkDto(
userInfo = user.toAlimTalkUserInfo(),
orderInfo = order.toAlimTalkOrderInfo(),
eventInfo = getEventInfo(event, host),
)
}
private fun getEventInfo(event: Event, host: Host): AlimTalkEventInfo =
AlimTalkEventInfo(host.profile!!.name!!, event.eventBasic!!.name!!)
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/controller/AuthController.kt
================================================
package band.gosrock.api.auth.controller
import band.gosrock.api.auth.model.dto.request.RegisterRequest
import band.gosrock.api.auth.model.dto.response.AvailableRegisterResponse
import band.gosrock.api.auth.model.dto.response.OauthLoginLinkResponse
import band.gosrock.api.auth.model.dto.response.OauthTokenResponse
import band.gosrock.api.auth.model.dto.response.OauthUserInfoResponse
import band.gosrock.api.auth.model.dto.response.TokenAndUserResponse
import band.gosrock.api.auth.service.LocalDevLoginUseCase
import band.gosrock.api.auth.service.LoginUseCase
import band.gosrock.api.auth.service.LogoutUseCase
import band.gosrock.api.auth.service.OauthUserInfoUseCase
import band.gosrock.api.auth.service.RefreshUseCase
import band.gosrock.api.auth.service.RegisterUseCase
import band.gosrock.api.auth.service.WithDrawUseCase
import band.gosrock.api.auth.service.helper.CookieHelper
import band.gosrock.api.config.rateLimit.UserRateLimiter
import band.gosrock.common.annotation.CurrentUserId
import band.gosrock.common.annotation.ApiErrorCodeExample
import band.gosrock.common.annotation.DevelopOnlyApi
import band.gosrock.infrastructure.outer.api.oauth.exception.KakaoKauthErrorCode
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid
import org.slf4j.LoggerFactory
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api/v1/auth")
@Tag(name = "1-1. [인증]")
class AuthController(
private val registerUseCase: RegisterUseCase,
private val loginUseCase: LoginUseCase,
private val refreshUseCase: RefreshUseCase,
private val oauthUserInfoUseCase: OauthUserInfoUseCase,
private val withDrawUseCase: WithDrawUseCase,
private val logoutUseCase: LogoutUseCase,
private val cookieHelper: CookieHelper,
private val rateLimiter: UserRateLimiter,
private val localDevLoginUseCase: LocalDevLoginUseCase
) {
private val log = LoggerFactory.getLogger(AuthController::class.java)
@Operation(summary = "kakao oauth 링크발급 (백엔드용 )", description = "kakao 링크를 받아볼수 있습니다.")
@Tag(name = "1-2. [카카오]")
@GetMapping("/oauth/kakao/link/test")
fun getKakaoOauthLinkTest(): OauthLoginLinkResponse =
registerUseCase.getKaKaoOauthLinkTest()
@Operation(summary = "kakao oauth 링크발급 (클라이언트용)", description = "kakao 링크를 받아볼수 있습니다.")
@Tag(name = "1-2. [카카오]")
@GetMapping("/oauth/kakao/link")
fun getKakaoOauthLink(
@RequestHeader(value = "referer", required = false) referer: String,
@RequestHeader(value = "host", required = false) host: String
): OauthLoginLinkResponse {
// 스테이징, prod 서버에 배포된 클라이언트에 해당
if (referer.contains(host)) {
log.info("/oauth/kakao$host")
val format = "https://$host"
if (referer.contains("admin")) {
return registerUseCase.getKaKaoOauthLink("$format/admin")
}
return registerUseCase.getKaKaoOauthLink(format)
} else if (referer.contains("5173")) {
return registerUseCase.getKaKaoOauthLink("$referer/admin")
}
return registerUseCase.getKaKaoOauthLink(referer)
}
@Operation(summary = "카카오 code 요청받는 곳입니다. referer,host는 건들이지 말아주세요!안보내셔도됩니다.")
@Tag(name = "1-2. [카카오]")
@GetMapping("/oauth/kakao")
@ApiErrorCodeExample(KakaoKauthErrorCode::class)
fun getCredentialFromKaKao(
@RequestParam("code") code: String,
@RequestHeader(value = "referer", required = false) referer: String,
@RequestHeader(value = "host", required = false) host: String
): OauthTokenResponse {
if (referer.contains(host)) {
log.info("/oauth/kakao$host")
val format = "https://$host"
if (referer.contains("admin")) {
return registerUseCase.getCredentialFromKaKao(code, "$format/admin")
}
return registerUseCase.getCredentialFromKaKao(code, format)
} else if (referer.contains("5173")) {
return registerUseCase.getCredentialFromKaKao(code, "$referer/admin")
}
return registerUseCase.getCredentialFromKaKao(code, referer)
}
@Operation(summary = "개발용 회원가입입니다 클라이언트가 몰라도 됩니다.", deprecated = true)
@Tag(name = "1-2. [카카오]")
@DevelopOnlyApi
@GetMapping("/oauth/kakao/develop")
fun developUserSign(@RequestParam("code") code: String): ResponseEntity<TokenAndUserResponse> {
val tokenAndUserResponse = registerUseCase.upsertKakaoOauthUser(code)
return ResponseEntity.ok()
.headers(cookieHelper.getTokenCookies(tokenAndUserResponse))
.body(tokenAndUserResponse)
}
@Operation(summary = "회원가입이 가능한지 id token 으로 확인합니다.")
@Tag(name = "1-2. [카카오]")
@GetMapping("/oauth/kakao/register/valid")
fun kakaoAuthCheckRegisterValid(
@RequestParam("id_token") token: String
): AvailableRegisterResponse =
registerUseCase.checkAvailableRegister(token)
@Operation(summary = "id_token 으로 회원가입을 합니다.")
@Tag(name = "1-2. [카카오]")
@PostMapping("/oauth/kakao/register")
fun kakaoAuthRegister(
@RequestParam("id_token") token: String,
@Valid @RequestBody registerRequest: RegisterRequest
): ResponseEntity<TokenAndUserResponse> {
val tokenAndUserResponse = registerUseCase.registerUserByOCIDToken(token, registerRequest)
return ResponseEntity.ok()
.headers(cookieHelper.getTokenCookies(tokenAndUserResponse))
.body(tokenAndUserResponse)
}
@Operation(summary = "id_token 으로 로그인을 합니다.")
@Tag(name = "1-2. [카카오]")
@PostMapping("/oauth/kakao/login")
fun kakaoOauthUserLogin(
@RequestParam("id_token") token: String
): ResponseEntity<TokenAndUserResponse> {
val tokenAndUserResponse = loginUseCase.execute(token)
return ResponseEntity.ok()
.headers(cookieHelper.getTokenCookies(tokenAndUserResponse))
.body(tokenAndUserResponse)
}
@Operation(summary = "accessToken 으로 oauth user 정보를 가져옵니다.")
@Tag(name = "1-2. [카카오]")
@PostMapping("/oauth/kakao/info")
fun kakaoOauthUserInfo(
@RequestParam("access_token") accessToken: String
): OauthUserInfoResponse =
oauthUserInfoUseCase.execute(accessToken)
@Operation(summary = "refreshToken 용입니다.")
@PostMapping("/token/refresh")
fun tokenRefresh(
request: HttpServletRequest,
@RequestParam(value = "token", required = false, defaultValue = "") refreshToken: String
): ResponseEntity<TokenAndUserResponse> {
val refreshTokenCookie = cookieHelper.getRefreshTokenFromRequest(request)
val tokenAndUserResponse = refreshUseCase.execute(refreshTokenCookie ?: refreshToken)
return ResponseEntity.ok()
.headers(cookieHelper.getTokenCookies(tokenAndUserResponse))
.body(tokenAndUserResponse)
}
@Operation(summary = "회원탈퇴를 합니다.")
@SecurityRequirement(name = "access-token")
@DeleteMapping("/me")
fun withDrawUser(@CurrentUserId userId: Long): ResponseEntity<Void> {
withDrawUseCase.execute(userId)
return ResponseEntity.ok().headers(cookieHelper.deleteCookies()).body(null)
}
@Operation(summary = "로그아웃을 합니다.")
@SecurityRequirement(name = "access-token")
@PostMapping("/logout")
fun logoutUser(@CurrentUserId userId: Long): ResponseEntity<Void> {
logoutUseCase.execute(userId)
return ResponseEntity.ok().headers(cookieHelper.deleteCookies()).body(null)
}
@Operation(summary = "로컬 개발용 즉시 로그인 (카카오 불필요)", deprecated = true)
@Tag(name = "1-2. [카카오]")
@DevelopOnlyApi
@PostMapping("/oauth/local/login")
fun localDevLogin(
@Valid @RequestBody registerRequest: RegisterRequest
): ResponseEntity<TokenAndUserResponse> {
val tokenAndUserResponse = localDevLoginUseCase.execute(registerRequest)
return ResponseEntity.ok()
.headers(cookieHelper.getTokenCookies(tokenAndUserResponse))
.body(tokenAndUserResponse)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/KakaoUserInfoDto.kt
================================================
package band.gosrock.api.auth.model.dto
import band.gosrock.domain.domains.user.domain.OauthInfo
import band.gosrock.domain.domains.user.domain.OauthProvider
import band.gosrock.domain.domains.user.domain.Profile
data class KakaoUserInfoDto(
val oauthId: String,
val email: String?,
val phoneNumber: String?,
val profileImage: String?,
val name: String?,
val oauthProvider: OauthProvider
) {
fun toProfile(): Profile = Profile(
profileImage = profileImage,
phoneNumber = phoneNumber,
name = name ?: "",
email = email ?: "",
)
fun toOauthInfo(): OauthInfo = OauthInfo(
oid = oauthId,
provider = oauthProvider,
)
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/request/RegisterRequest.kt
================================================
package band.gosrock.api.auth.model.dto.request
import band.gosrock.domain.domains.user.domain.Profile
import jakarta.validation.constraints.NotEmpty
data class RegisterRequest(
@field:NotEmpty
val email: String? = null,
val phoneNumber: String? = null,
val profileImage: String? = null,
@field:NotEmpty
val name: String? = null,
val marketingAgree: Boolean = false
) {
fun toProfile(): Profile = Profile(
profileImage = profileImage,
phoneNumber = phoneNumber,
name = name ?: "",
email = email ?: "",
)
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/response/AvailableRegisterResponse.kt
================================================
package band.gosrock.api.auth.model.dto.response
import io.swagger.v3.oas.annotations.media.Schema
data class AvailableRegisterResponse(
@Schema(description = "회원가입을 했던 유저인지에 대한 여부 , oauth 요청을 통해 처음 회원가입한경우 false임")
val canRegister: Boolean
)
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/response/OauthLoginLinkResponse.kt
================================================
package band.gosrock.api.auth.model.dto.response
data class OauthLoginLinkResponse(
val link: String
)
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/response/OauthTokenResponse.kt
================================================
package band.gosrock.api.auth.model.dto.response
import band.gosrock.infrastructure.outer.api.oauth.dto.KakaoTokenResponse
data class OauthTokenResponse(
val accessToken: String?,
val refreshToken: String?,
val idToken: String?
) {
companion object {
fun from(kakaoTokenResponse: KakaoTokenResponse): OauthTokenResponse =
OauthTokenResponse(
idToken = kakaoTokenResponse.idToken,
refreshToken = kakaoTokenResponse.refreshToken,
accessToken = kakaoTokenResponse.accessToken
)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/response/OauthUserInfoResponse.kt
================================================
package band.gosrock.api.auth.model.dto.response
import band.gosrock.api.auth.model.dto.KakaoUserInfoDto
data class OauthUserInfoResponse(
val email: String?,
val phoneNumber: String?,
val profileImage: String?,
val name: String?
) {
companion object {
fun from(kakaoUserInfoDto: KakaoUserInfoDto): OauthUserInfoResponse =
OauthUserInfoResponse(
email = kakaoUserInfoDto.email,
phoneNumber = kakaoUserInfoDto.phoneNumber,
profileImage = kakaoUserInfoDto.profileImage,
name = kakaoUserInfoDto.name
)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/response/TokenAndUserResponse.kt
================================================
package band.gosrock.api.auth.model.dto.response
import band.gosrock.domain.common.dto.ProfileViewDto
import io.swagger.v3.oas.annotations.media.Schema
data class TokenAndUserResponse(
@Schema(description = "어세스 토큰")
val accessToken: String,
val accessTokenAge: Long,
@Schema(description = "리프레쉬 토큰")
val refreshToken: String,
val refreshTokenAge: Long,
val userProfile: ProfileViewDto
)
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/LocalDevLoginUseCase.kt
================================================
package band.gosrock.api.auth.service
import band.gosrock.api.auth.model.dto.request.RegisterRequest
import band.gosrock.api.auth.model.dto.response.TokenAndUserResponse
import band.gosrock.api.auth.service.helper.TokenGenerateHelper
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.user.domain.OauthInfo
import band.gosrock.domain.domains.user.domain.OauthProvider
import band.gosrock.domain.domains.user.service.UserDomainService
@UseCase
class LocalDevLoginUseCase(
private val userDomainService: UserDomainService,
private val tokenGenerateHelper: TokenGenerateHelper
) {
fun execute(registerRequest: RegisterRequest): TokenAndUserResponse {
val oauthInfo = OauthInfo(
provider = OauthProvider.KAKAO,
oid = registerRequest.email ?: "anonymous",
)
val profile = registerRequest.toProfile()
val user = userDomainService.upsertUser(profile, oauthInfo)
return tokenGenerateHelper.execute(user)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/LoginUseCase.kt
================================================
package band.gosrock.api.auth.service
import band.gosrock.api.auth.model.dto.response.TokenAndUserResponse
import band.gosrock.api.auth.service.helper.KakaoOauthHelper
import band.gosrock.api.auth.service.helper.TokenGenerateHelper
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.user.service.UserDomainService
import org.slf4j.LoggerFactory
@UseCase
class LoginUseCase(
private val kakaoOauthHelper: KakaoOauthHelper,
private val userDomainService: UserDomainService,
private val tokenGenerateHelper: TokenGenerateHelper
) {
private val log = LoggerFactory.getLogger(LoginUseCase::class.java)
fun execute(idToken: String): TokenAndUserResponse {
log.info("[LoginUseCase][execute] 로그인 시도")
val oauthInfo = kakaoOauthHelper.getOauthInfoByIdToken(idToken)
val user = userDomainService.loginUser(oauthInfo)
log.info("[LoginUseCase][execute] 로그인 성공 userId={}", user.id)
return tokenGenerateHelper.execute(user)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/LogoutUseCase.kt
================================================
package band.gosrock.api.auth.service
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.user.adaptor.RefreshTokenAdaptor
@UseCase
class LogoutUseCase(
private val refreshTokenAdaptor: RefreshTokenAdaptor
) {
fun execute(userId: Long) {
refreshTokenAdaptor.deleteByUserId(userId)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/OauthUserInfoUseCase.kt
================================================
package band.gosrock.api.auth.service
import band.gosrock.api.auth.model.dto.response.OauthUserInfoResponse
import band.gosrock.api.auth.service.helper.KakaoOauthHelper
import band.gosrock.common.annotation.UseCase
@UseCase
class OauthUserInfoUseCase(
private val kakaoOauthHelper: KakaoOauthHelper
) {
fun execute(accessToken: String): OauthUserInfoResponse {
val oauthUserInfo = kakaoOauthHelper.getUserInfo(accessToken)
return OauthUserInfoResponse.from(oauthUserInfo)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/RefreshUseCase.kt
================================================
package band.gosrock.api.auth.service
import band.gosrock.api.auth.model.dto.response.TokenAndUserResponse
import band.gosrock.api.auth.service.helper.TokenGenerateHelper
import band.gosrock.common.annotation.UseCase
import band.gosrock.common.jwt.JwtTokenProvider
import band.gosrock.domain.domains.user.adaptor.RefreshTokenAdaptor
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import band.gosrock.domain.domains.user.service.UserDomainService
import org.slf4j.LoggerFactory
@UseCase
class RefreshUseCase(
private val userAdaptor: UserAdaptor,
private val jwtTokenProvider: JwtTokenProvider,
private val userDomainService: UserDomainService,
private val refreshTokenAdaptor: RefreshTokenAdaptor,
private val tokenGenerateHelper: TokenGenerateHelper
) {
private val log = LoggerFactory.getLogger(RefreshUseCase::class.java)
fun execute(refreshToken: String): TokenAndUserResponse {
val savedRefreshTokenEntity = refreshTokenAdaptor.queryRefreshToken(refreshToken)
val refreshUserId = jwtTokenProvider.parseRefreshToken(savedRefreshTokenEntity.refreshToken!!)
log.info("[RefreshUseCase][execute] 토큰 갱신 userId={}", refreshUserId)
val user = userAdaptor.queryUser(refreshUserId)
// 리프레쉬 시에도 last로그인 정보 업데이트
userDomainService.loginUser(user.oauthInfo!!)
return tokenGenerateHelper.execute(user)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/RegisterUseCase.kt
================================================
package band.gosrock.api.auth.service
import band.gosrock.api.auth.model.dto.request.RegisterRequest
import band.gosrock.api.auth.model.dto.response.AvailableRegisterResponse
import band.gosrock.api.auth.model.dto.response.OauthLoginLinkResponse
import band.gosrock.api.auth.model.dto.response.OauthTokenResponse
import band.gosrock.api.auth.model.dto.response.TokenAndUserResponse
import band.gosrock.api.auth.service.helper.KakaoOauthHelper
import band.gosrock.api.auth.service.helper.TokenGenerateHelper
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.user.service.UserDomainService
import org.slf4j.LoggerFactory
@UseCase
class RegisterUseCase(
private val kakaoOauthHelper: KakaoOauthHelper,
private val userDomainService: UserDomainService,
private val tokenGenerateHelper: TokenGenerateHelper
) {
private val log = LoggerFactory.getLogger(RegisterUseCase::class.java)
fun getKaKaoOauthLinkTest(): OauthLoginLinkResponse =
OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLinkTest())
fun getKaKaoOauthLink(referer: String): OauthLoginLinkResponse =
OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLink(referer))
fun upsertKakaoOauthUser(code: String): TokenAndUserResponse {
val oauthAccessToken = kakaoOauthHelper.getOauthTokenTest(code).accessToken
val oauthUserInfo = kakaoOauthHelper.getUserInfo(oauthAccessToken!!)
val profile = oauthUserInfo.toProfile()
val user = userDomainService.upsertUser(profile, oauthUserInfo.toOauthInfo())
return tokenGenerateHelper.execute(user)
}
fun checkAvailableRegister(idToken: String): AvailableRegisterResponse {
val oauthInfo = kakaoOauthHelper.getOauthInfoByIdToken(idToken)
return AvailableRegisterResponse(userDomainService.checkUserCanRegister(oauthInfo))
}
fun registerUserByOCIDToken(
idToken: String,
registerUserRequest: RegisterRequest
): TokenAndUserResponse {
log.info("[RegisterUseCase][registerUserByOCIDToken] 회원가입")
val oauthInfo = kakaoOauthHelper.getOauthInfoByIdToken(idToken)
val user = userDomainService.registerUser(
registerUserRequest.toProfile(),
oauthInfo,
registerUserRequest.marketingAgree
)
log.info("[RegisterUseCase][registerUserByOCIDToken] 회원가입 완료 userId={}", user.id)
return tokenGenerateHelper.execute(user)
}
fun getCredentialFromKaKao(code: String, referer: String): OauthTokenResponse =
OauthTokenResponse.from(kakaoOauthHelper.getOauthToken(code, referer))
fun getCredentialFromKaKaoTest(code: String): OauthTokenResponse =
OauthTokenResponse.from(kakaoOauthHelper.getOauthTokenTest(code))
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/WithDrawUseCase.kt
================================================
package band.gosrock.api.auth.service
import band.gosrock.api.auth.service.helper.KakaoOauthHelper
import band.gosrock.common.annotation.UseCase
import band.gosrock.domain.domains.user.adaptor.RefreshTokenAdaptor
import band.gosrock.domain.domains.user.adaptor.UserAdaptor
import band.gosrock.domain.domains.user.service.UserDomainService
import org.slf4j.LoggerFactory
import org.springframework.transaction.annotation.Transactional
@UseCase
class WithDrawUseCase(
private val refreshTokenAdaptor: RefreshTokenAdaptor,
private val userDomainService: UserDomainService,
private val userAdaptor: UserAdaptor,
private val kakaoOauthHelper: KakaoOauthHelper
) {
private val log = LoggerFactory.getLogger(WithDrawUseCase::class.java)
@Transactional
fun execute(userId: Long) {
log.info("[WithDrawUseCase][execute] 회원 탈퇴 userId={}", userId)
refreshTokenAdaptor.deleteByUserId(userId)
val user = userAdaptor.queryUser(userId)
val oid = user.oauthInfo!!.oid!!
userDomainService.withDrawUser(userId)
kakaoOauthHelper.unlink(oid)
}
}
================================================
FILE: DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/helper/CookieHelper.kt
================================================
package band.gosrock.api.auth.service.helper
import band.gosrock.api.auth.model.dto.response.TokenAndUserResponse
import band.gosrock.common.annotation.Helper
import band.gosrock.common.helper.SpringEnvironmentHelper
import jakarta.servlet.http.HttpServletRequest
import org.springframework.http.HttpHeaders
import org.springframework.http.ResponseCookie
@Helper
class CookieHelper(
private val springEnvironmentHelper: SpringEnvironmentHelper
) {
fun getAccessTokenName(): String = "accessToken"
fun getRefreshTokenName(): String = "refreshToken"
fun getRefreshTokenFromRequest(request: HttpServletRequest): String? =
request.cookies?.firstOrNull { it.name == getRefreshTokenName() }?.value
fun getTokenCookies(tokenAndUserResponse: TokenAndUserResponse): HttpHeaders {
val accessToken = buildCookie(
getAccessTokenName(),
tokenAndUserResponse.accessToken,
tokenAndUserResponse.accessTokenAge
)
val refreshToken = buildCookie(
getRefreshTokenName(),
tokenAndUserResponse.refreshToken,
tokenAndUserResponse.refreshTokenAge
)
return HttpHeaders().apply {
add(HttpHeaders.SET_COOKIE, accessToken.toString())
add(HttpHeaders.SET_COOKIE, refreshToken.toString())
}
}
fun deleteCookies(): HttpHeaders {
val accessToken = buildCookie(getAccessTokenName(), "", 0)
val refreshToken = buildCookie(getRefreshTokenName(), "", 0)
return HttpHeaders().apply {
add(HttpHeaders.SET_COOKIE, accessToken.toString())
add(HttpHeaders.SET_COOKIE, refreshToken.toString())
}
}
private fun buildCookie(name: String, value: String, maxAge: Long): ResponseCookie {
val i
gitextract_57vblqu2/ ├── .claude/ │ └── settings.local.json ├── .github/ │ ├── CODEOWNERS │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── BuildApiServer.yml │ ├── BuildBatchServer.yml │ ├── BuildSocketServer.yml │ └── ci.yml ├── .gitignore ├── CLAUDE.md ├── DuDoong-Admin/ │ ├── build.gradle.kts │ └── src/ │ ├── main/ │ │ └── kotlin/ │ │ └── band/ │ │ └── gosrock/ │ │ └── admin/ │ │ ├── controller/ │ │ │ ├── AdminCommentController.kt │ │ │ ├── AdminDashboardController.kt │ │ │ ├── AdminEventController.kt │ │ │ ├── AdminHostController.kt │ │ │ ├── AdminOrderController.kt │ │ │ ├── AdminRefundController.kt │ │ │ └── AdminUserController.kt │ │ ├── exception/ │ │ │ ├── AdminErrorCode.kt │ │ │ └── AdminForbiddenException.kt │ │ ├── model/ │ │ │ └── dto/ │ │ │ ├── request/ │ │ │ │ ├── AdminAddHostMemberRequest.kt │ │ │ │ ├── AdminAdjustTicketStockRequest.kt │ │ │ │ ├── AdminCancelOrderRequest.kt │ │ │ │ ├── AdminChangeNameRequest.kt │ │ │ │ ├── AdminRefundStatusRequest.kt │ │ │ │ ├── AdminTransferMasterRequest.kt │ │ │ │ ├── AdminUpdateEventRequest.kt │ │ │ │ ├── AdminUpdateEventStatusRequest.kt │ │ │ │ ├── AdminUpdateHostMemberRoleRequest.kt │ │ │ │ ├── AdminUpdateHostPartnerRequest.kt │ │ │ │ ├── AdminUpdateHostProfileRequest.kt │ │ │ │ ├── AdminUpdateTicketItemRequest.kt │ │ │ │ ├── AdminUpdateUserRoleRequest.kt │ │ │ │ └── AdminUpdateUserStatusRequest.kt │ │ │ └── response/ │ │ │ ├── AdminCommentResponse.kt │ │ │ ├── AdminEventResponse.kt │ │ │ ├── AdminHostDetailResponse.kt │ │ │ ├── AdminHostMemberResponse.kt │ │ │ ├── AdminHostResponse.kt │ │ │ ├── AdminIssuedTicketResponse.kt │ │ │ ├── AdminOrderResponse.kt │ │ │ ├── AdminRefundResponse.kt │ │ │ ├── AdminTicketItemResponse.kt │ │ │ ├── AdminUserDetailResponse.kt │ │ │ ├── AdminUserResponse.kt │ │ │ └── DashboardResponse.kt │ │ └── service/ │ │ ├── AdminAddHostMemberUseCase.kt │ │ ├── AdminAdjustTicketStockUseCase.kt │ │ ├── AdminAuthValidator.kt │ │ ├── AdminCancelOrderUseCase.kt │ │ ├── AdminChangeNameUseCase.kt │ │ ├── AdminCompleteRefundUseCase.kt │ │ ├── AdminDeleteCommentUseCase.kt │ │ ├── AdminDeleteEventUseCase.kt │ │ ├── AdminExcelService.kt │ │ ├── AdminExportIssuedTicketsUseCase.kt │ │ ├── AdminGetCommentsUseCase.kt │ │ ├── AdminGetEventDetailUseCase.kt │ │ ├── AdminGetEventsUseCase.kt │ │ ├── AdminGetHostDetailUseCase.kt │ │ ├── AdminGetHostEventsUseCase.kt │ │ ├── AdminGetHostMembersUseCase.kt │ │ ├── AdminGetHostsUseCase.kt │ │ ├── AdminGetIssuedTicketsUseCase.kt │ │ ├── AdminGetMeUseCase.kt │ │ ├── AdminGetOrderDetailUseCase.kt │ │ ├── AdminGetOrdersUseCase.kt │ │ ├── AdminGetRefundDetailUseCase.kt │ │ ├── AdminGetRefundsUseCase.kt │ │ ├── AdminGetTicketItemsUseCase.kt │ │ ├── AdminGetUserDetailUseCase.kt │ │ ├── AdminGetUsersUseCase.kt │ │ ├── AdminRemoveHostMemberUseCase.kt │ │ ├── AdminTransferMasterUseCase.kt │ │ ├── AdminUpdateEventStatusUseCase.kt │ │ ├── AdminUpdateEventUseCase.kt │ │ ├── AdminUpdateHostMemberRoleUseCase.kt │ │ ├── AdminUpdateHostPartnerUseCase.kt │ │ ├── AdminUpdateHostProfileUseCase.kt │ │ ├── AdminUpdateRefundStatusUseCase.kt │ │ ├── AdminUpdateTicketItemUseCase.kt │ │ ├── AdminUpdateUserRoleUseCase.kt │ │ ├── AdminUpdateUserStatusUseCase.kt │ │ └── GetDashboardUseCase.kt │ └── test/ │ └── kotlin/ │ └── band/ │ └── gosrock/ │ └── admin/ │ └── service/ │ ├── AdminAuthValidatorTest.kt │ ├── AdminChangeNameUseCaseTest.kt │ ├── AdminCompleteRefundUseCaseTest.kt │ ├── AdminExcelServiceTest.kt │ └── AdminUpdateRefundStatusUseCaseTest.kt ├── DuDoong-Api/ │ ├── Dockerfile │ ├── build.gradle.kts │ └── src/ │ ├── main/ │ │ ├── kotlin/ │ │ │ └── band/ │ │ │ └── gosrock/ │ │ │ ├── DuDoongApiServerApplication.kt │ │ │ └── api/ │ │ │ ├── admin/ │ │ │ │ └── controller/ │ │ │ │ └── AdminAuthController.kt │ │ │ ├── alimTalk/ │ │ │ │ ├── dto/ │ │ │ │ │ └── OrderAlimTalkDto.kt │ │ │ │ ├── handler/ │ │ │ │ │ ├── DoneOrderEventAlimTalkHandler.kt │ │ │ │ │ ├── RegisterUserEventAlimTalkHandler.kt │ │ │ │ │ └── WithDrawOrderEventAlimTalkHandler.kt │ │ │ │ └── service/ │ │ │ │ ├── SendDoneOrderAlimTalkService.kt │ │ │ │ ├── SendRegisterAlimTalkService.kt │ │ │ │ ├── SendWithdrawOrderAlimTalkService.kt │ │ │ │ └── helper/ │ │ │ │ └── OrderAlimTalkInfoHelper.kt │ │ │ ├── auth/ │ │ │ │ ├── controller/ │ │ │ │ │ └── AuthController.kt │ │ │ │ ├── model/ │ │ │ │ │ └── dto/ │ │ │ │ │ ├── KakaoUserInfoDto.kt │ │ │ │ │ ├── request/ │ │ │ │ │ │ └── RegisterRequest.kt │ │ │ │ │ └── response/ │ │ │ │ │ ├── AvailableRegisterResponse.kt │ │ │ │ │ ├── OauthLoginLinkResponse.kt │ │ │ │ │ ├── OauthTokenResponse.kt │ │ │ │ │ ├── OauthUserInfoResponse.kt │ │ │ │ │ └── TokenAndUserResponse.kt │ │ │ │ └── service/ │ │ │ │ ├── LocalDevLoginUseCase.kt │ │ │ │ ├── LoginUseCase.kt │ │ │ │ ├── LogoutUseCase.kt │ │ │ │ ├── OauthUserInfoUseCase.kt │ │ │ │ ├── RefreshUseCase.kt │ │ │ │ ├── RegisterUseCase.kt │ │ │ │ ├── WithDrawUseCase.kt │ │ │ │ └── helper/ │ │ │ │ ├── CookieHelper.kt │ │ │ │ ├── KakaoOauthHelper.kt │ │ │ │ ├── OauthOIDCHelper.kt │ │ │ │ └── TokenGenerateHelper.kt │ │ │ ├── cart/ │ │ │ │ ├── controller/ │ │ │ │ │ └── CartController.kt │ │ │ │ ├── docs/ │ │ │ │ │ └── CreateCartExceptionDocs.kt │ │ │ │ ├── model/ │ │ │ │ │ ├── dto/ │ │ │ │ │ │ ├── request/ │ │ │ │ │ │ │ ├── AddCartLineDto.kt │ │ │ │ │ │ │ ├── AddCartOptionAnswerDto.kt │ │ │ │ │ │ │ └── AddCartRequest.kt │ │ │ │ │ │ └── response/ │ │ │ │ │ │ ├── CartItemResponse.kt │ │ │ │ │ │ └── CartResponse.kt │ │ │ │ │ └── mapper/ │ │ │ │ │ └── CartMapper.kt │ │ │ │ └── service/ │ │ │ │ ├── CheckOptionUseCase.kt │ │ │ │ ├── CreateCartUseCase.kt │ │ │ │ └── ReadCartUseCase.kt │ │ │ ├── comment/ │ │ │ │ ├── controller/ │ │ │ │ │ └── CommentController.kt │ │ │ │ ├── mapper/ │ │ │ │ │ └── CommentMapper.kt │ │ │ │ ├── model/ │ │ │ │ │ ├── request/ │ │ │ │ │ │ └── CreateCommentRequest.kt │ │ │ │ │ └── response/ │ │ │ │ │ ├── CreateCommentResponse.kt │ │ │ │ │ ├── RetrieveCommentCountResponse.kt │ │ │ │ │ ├── RetrieveCommentDTO.kt │ │ │ │ │ ├── RetrieveCommentListResponse.kt │ │ │ │ │ └── RetrieveRandomCommentResponse.kt │ │ │ │ └── service/ │ │ │ │ ├── CreateCommentUseCase.kt │ │ │ │ ├── DeleteCommentUseCase.kt │ │ │ │ ├── RetrieveCommentCountUseCase.kt │ │ │ │ ├── RetrieveCommentUseCase.kt │ │ │ │ └── RetrieveRandomCommentUseCase.kt │ │ │ ├── common/ │ │ │ │ ├── UserUtils.kt │ │ │ │ ├── aop/ │ │ │ │ │ ├── hostPartner/ │ │ │ │ │ │ ├── HostPartnerAllowed.kt │ │ │ │ │ │ ├── HostPartnerAop.kt │ │ │ │ │ │ ├── HostPartnerCallTransaction.kt │ │ │ │ │ │ ├── HostPartnerCallTransactionFactory.kt │ │ │ │ │ │ ├── HostPartnerEventTransaction.kt │ │ │ │ │ │ └── HostPartnerHostTransaction.kt │ │ │ │ │ └── hostRole/ │ │ │ │ │ ├── FindHostFrom.kt │ │ │ │ │ ├── HostCallTransactionFactory.kt │ │ │ │ │ ├── HostQualification.kt │ │ │ │ │ ├── HostRoleAop.kt │ │ │ │ │ ├── HostRoleCallTransaction.kt │ │ │ │ │ ├── HostRoleEventTransaction.kt │ │ │ │ │ ├── HostRoleHostTransaction.kt │ │ │ │ │ └── HostRolesAllowed.kt │ │ │ │ ├── customizer/ │ │ │ │ │ └── EnumValuePropertyCustomizer.kt │ │ │ │ ├── page/ │ │ │ │ │ └── PageResponse.kt │ │ │ │ └── slice/ │ │ │ │ ├── SliceParam.kt │ │ │ │ └── SliceResponse.kt │ │ │ ├── config/ │ │ │ │ ├── ExampleHolder.kt │ │ │ │ ├── HttpContentCacheFilter.kt │ │ │ │ ├── MdcFilter.kt │ │ │ │ ├── ServletFilterConfig.kt │ │ │ │ ├── SwaggerConfig.kt │ │ │ │ ├── WebMvcConfig.kt │ │ │ │ ├── rateLimit/ │ │ │ │ │ ├── IPRateLimiter.kt │ │ │ │ │ ├── ThrottlingInterceptor.kt │ │ │ │ │ ├── ThrottlingWebConfigure.kt │ │ │ │ │ └── UserRateLimiter.kt │ │ │ │ ├── response/ │ │ │ │ │ ├── GlobalExceptionHandler.kt │ │ │ │ │ └── SuccessResponseAdvice.kt │ │ │ │ └── security/ │ │ │ │ ├── AccessDeniedFilter.kt │ │ │ │ ├── AuthDetails.kt │ │ │ │ ├── CorsConfig.kt │ │ │ │ ├── CurrentUserIdResolver.kt │ │ │ │ ├── JwtExceptionFilter.kt │ │ │ │ ├── JwtTokenFilter.kt │ │ │ │ ├── SecurityConfig.kt │ │ │ │ └── SecurityUtils.kt │ │ │ ├── coupon/ │ │ │ │ ├── controller/ │ │ │ │ │ └── CouponController.kt │ │ │ │ ├── dto/ │ │ │ │ │ ├── reqeust/ │ │ │ │ │ │ └── CreateCouponCampaignRequest.kt │ │ │ │ │ └── response/ │ │ │ │ │ ├── CreateCouponCampaignResponse.kt │ │ │ │ │ ├── CreateUserCouponResponse.kt │ │ │ │ │ ├── ReadIssuedCouponOrderResponse.kt │ │ │ │ │ └── ReadIssuedCouponResponse.kt │ │ │ │ ├── mapper/ │ │ │ │ │ ├── CouponCampaignMapper.kt │ │ │ │ │ └── IssuedCouponMapper.kt │ │ │ │ └── service/ │ │ │ │ ├── CreateCouponUseCase.kt │ │ │ │ ├── CreateUserCouponUseCase.kt │ │ │ │ └── ReadIssuedCouponUseCase.kt │ │ │ ├── email/ │ │ │ │ ├── dto/ │ │ │ │ │ ├── IssuedTicketMailDTO.kt │ │ │ │ │ └── OrderMailDto.kt │ │ │ │ ├── handler/ │ │ │ │ │ ├── CreateOrderEventEmailHandler.kt │ │ │ │ │ ├── DoneOrderEventEmailHandler.kt │ │ │ │ │ ├── EntranceIssuedTicketEventEmailHandler.kt │ │ │ │ │ ├── RegisterUserEventEmailHandler.kt │ │ │ │ │ └── WithDrawOrderEventEmailHandler.kt │ │ │ │ └── service/ │ │ │ │ ├── EntranceIssuedTicketEmailService.kt │ │ │ │ ├── HostMasterChangeEmailService.kt │ │ │ │ ├── HostUserDisabledEmailService.kt │ │ │ │ ├── HostUserInvitationEmailService.kt │ │ │ │ ├── HostUserRoleChangeEmailService.kt │ │ │ │ ├── IssuedTicketMailInfoHelper.kt │ │ │ │ ├── OrderApproveConfirmEmailService.kt │ │ │ │ ├── OrderApproveRequestEmailService.kt │ │ │ │ ├── OrderMailInfoHelper.kt │ │ │ │ ├── OrderPaymentDoneEmailService.kt │ │ │ │ ├── OrderWithDrawCancelEmailService.kt │ │ │ │ ├── OrderWithDrawRefundEmailService.kt │ │ │ │ └── SendRegisterEmailService.kt │ │ │ ├── event/ │ │ │ │ ├── controller/ │ │ │ │ │ └── EventController.kt │ │ │ │ ├── model/ │ │ │ │ │ ├── dto/ │ │ │ │ │ │ ├── request/ │ │ │ │ │ │ │ ├── CreateEventRequest.kt │ │ │ │ │ │ │ ├── UpdateEventBasicRequest.kt │ │ │ │ │ │ │ ├── UpdateEventDetailRequest.kt │ │ │ │ │ │ │ └── UpdateEventStatusRequest.kt │ │ │ │ │ │ └── response/ │ │ │ │ │ │ ├── EventChecklistResponse.kt │ │ │ │ │ │ ├── EventDetailResponse.kt │ │ │ │ │ │ ├── EventProfileResponse.kt │ │ │ │ │ │ └── EventResponse.kt │ │ │ │ │ └── mapper/ │ │ │ │ │ └── EventMapper.kt │ │ │ │ └── service/ │ │ │ │ ├── CreateEventUseCase.kt │ │ │ │ ├── DeleteEventUseCase.kt │ │ │ │ ├── OpenEventUseCase.kt │ │ │ │ ├── ReadEventChecklistUseCase.kt │ │ │ │ ├── ReadEventDetailUseCase.kt │ │ │ │ ├── ReadUserEventProfilesUseCase.kt │ │ │ │ ├── SearchEventsUseCase.kt │ │ │ │ ├── UpdateEventBasicUseCase.kt │ │ │ │ ├── UpdateEventDetailUseCase.kt │ │ │ │ └── UpdateEventStatusUseCase.kt │ │ │ ├── example/ │ │ │ │ ├── controller/ │ │ │ │ │ └── ExampleController.kt │ │ │ │ ├── docs/ │ │ │ │ │ ├── ExampleException2Docs.kt │ │ │ │ │ └── ExampleExceptionDocs.kt │ │ │ │ ├── dto/ │ │ │ │ │ └── ExampleResponse.kt │ │ │ │ └── service/ │ │ │ │ └── ExampleApiService.kt │ │ │ ├── host/ │ │ │ │ ├── controller/ │ │ │ │ │ └── HostController.kt │ │ │ │ ├── model/ │ │ │ │ │ ├── dto/ │ │ │ │ │ │ ├── request/ │ │ │ │ │ │ │ ├── CreateHostRequest.kt │ │ │ │ │ │ │ ├── InviteHostRequest.kt │ │ │ │ │ │ │ ├── TransferMasterRequest.kt │ │ │ │ │ │ │ ├── UpdateHostRequest.kt │ │ │ │ │ │ │ ├── UpdateHostSlackRequest.kt │ │ │ │ │ │ │ └── UpdateHostUserRoleRequest.kt │ │ │ │ │ │ └── response/ │ │ │ │ │ │ ├── HostDetailResponse.kt │ │ │ │ │ │ ├── HostEventProfileResponse.kt │ │ │ │ │ │ ├── HostProfileResponse.kt │ │ │ │ │ │ └── HostResponse.kt │ │ │ │ │ └── mapper/ │ │ │ │ │ └── HostMapper.kt │ │ │ │ └── service/ │ │ │ │ ├── CreateHostUseCase.kt │ │ │ │ ├── InviteHostUseCase.kt │ │ │ │ ├── JoinHostUseCase.kt │ │ │ │ ├── ReadHostEventsUseCase.kt │ │ │ │ ├── ReadHostProfilesUseCase.kt │ │ │ │ ├── ReadHostUseCase.kt │ │ │ │ ├── ReadInviteUsersUseCase.kt │ │ │ │ ├── RejectHostUseCase.kt │ │ │ │ ├── TransferMasterUseCase.kt │ │ │ │ ├── UpdateHostProfileUseCase.kt │ │ │ │ ├── UpdateHostSlackUrlUseCase.kt │ │ │ │ └── UpdateHostUserRoleUseCase.kt │ │ │ ├── image/ │ │ │ │ ├── controller/ │ │ │ │ │ └── ImageController.kt │ │ │ │ ├── dto/ │ │ │ │ │ ├── ImageUrlRequest.kt │ │ │ │ │ └── ImageUrlResponse.kt │ │ │ │ └── service/ │ │ │ │ └── GetImageUploadUrlUseCase.kt │ │ │ ├── issuedTicket/ │ │ │ │ ├── controller/ │ │ │ │ │ ├── AdminIssuedTicketController.kt │ │ │ │ │ └── IssuedTicketController.kt │ │ │ │ ├── dto/ │ │ │ │ │ ├── request/ │ │ │ │ │ │ └── AdminIssuedTicketTableQueryRequest.kt │ │ │ │ │ └── response/ │ │ │ │ │ ├── IssuedTicketAdminTableElement.kt │ │ │ │ │ ├── RetrieveIssuedTicketDTO.kt │ │ │ │ │ ├── RetrieveIssuedTicketDetailResponse.kt │ │ │ │ │ └── RetrieveIssuedTicketListResponse.kt │ │ │ │ ├── mapper/ │ │ │ │ │ └── IssuedTicketMapper.kt │ │ │ │ └── service/ │ │ │ │ ├── EntranceIssuedTicketUseCase.kt │ │ │ │ ├── ReadIssuedTicketUseCase.kt │ │ │ │ └── ReadIssuedTicketsUseCase.kt │ │ │ ├── order/ │ │ │ │ ├── controller/ │ │ │ │ │ ├── OrderAdminController.kt │ │ │ │ │ └── OrderController.kt │ │ │ │ ├── docs/ │ │ │ │ │ ├── ApproveOrderExceptionDocs.kt │ │ │ │ │ ├── CancelOrderExceptionDocs.kt │ │ │ │ │ ├── ConfirmOrderExceptionDocs.kt │ │ │ │ │ ├── CreateOrderExceptionDocs.kt │ │ │ │ │ ├── FreeOrderExceptionDocs.kt │ │ │ │ │ └── RefundOrderExceptionDocs.kt │ │ │ │ ├── model/ │ │ │ │ │ ├── dto/ │ │ │ │ │ │ ├── request/ │ │ │ │ │ │ │ ├── AdminOrderTableQueryRequest.kt │ │ │ │ │ │ │ ├── CancelReasonRequest.kt │ │ │ │ │ │ │ ├── ConfirmOrderRequest.kt │ │ │ │ │ │ │ └── CreateOrderRequest.kt │ │ │ │ │ │ └── response/ │ │ │ │ │ │ ├── CreateOrderResponse.kt │ │ │ │ │ │ ├── OrderAdminTableElement.kt │ │ │ │ │ │ ├── OrderBriefElement.kt │ │ │ │ │ │ ├── OrderLineTicketResponse.kt │ │ │ │ │ │ ├── OrderPaymentResponse.kt │ │ │ │ │ │ ├── OrderResponse.kt │ │ │ │ │ │ └── OrderTicketResponse.kt │ │ │ │ │ └── mapper/ │ │ │ │ │ └── OrderMapper.kt │ │ │ │ └── service/ │ │ │ │ ├── ApproveOrderUseCase.kt │ │ │ │ ├── CancelOrderUseCase.kt │ │ │ │ ├── ConfirmOrderUseCase.kt │ │ │ │ ├── CreateOrderUseCase.kt │ │ │ │ ├── CreateTossOrderUseCase.kt │ │ │ │ ├── FreeOrderUseCase.kt │ │ │ │ ├── ReadOrderUseCase.kt │ │ │ │ ├── RefundOrderUseCase.kt │ │ │ │ └── RefuseOrderUseCase.kt │ │ │ ├── refund/ │ │ │ │ ├── controller/ │ │ │ │ │ └── RefundController.kt │ │ │ │ ├── dto/ │ │ │ │ │ └── response/ │ │ │ │ │ └── RefundResponse.kt │ │ │ │ └── service/ │ │ │ │ ├── CompleteRefundUseCase.kt │ │ │ │ └── GetRefundsUseCase.kt │ │ │ ├── slack/ │ │ │ │ ├── handler/ │ │ │ │ │ ├── event/ │ │ │ │ │ │ ├── EventContentChangeEventHandler.kt │ │ │ │ │ │ ├── EventCreationEventHandler.kt │ │ │ │ │ │ ├── EventDeletionEventHandler.kt │ │ │ │ │ │ └── EventStatusChangeEventHandler.kt │ │ │ │ │ ├── host/ │ │ │ │ │ │ ├── HostRegisterSlackEventHandler.kt │ │ │ │ │ │ ├── HostUserDisabledEventHandler.kt │ │ │ │ │ │ ├── HostUserInvitationEventHandler.kt │ │ │ │ │ │ ├── HostUserJoinEventHandler.kt │ │ │ │ │ │ └── HostUserRoleChangeEventHandler.kt │ │ │ │ │ └── order/ │ │ │ │ │ ├── DudoongTicketCancelOrderEventHandler.kt │ │ │ │ │ ├── DudoongTicketRefundOrderEventHandler.kt │ │ │ │ │ ├── NewApproveOrderAlarmEventHandler.kt │ │ │ │ │ ├── NewConfirmOrderAlarmEventHandler.kt │ │ │ │ │ ├── OrderApprovedAlarmEventHandler.kt │ │ │ │ │ └── WithDrawOrderEventHandler.kt │ │ │ │ └── sender/ │ │ │ │ ├── SlackBootNotificationSender.kt │ │ │ │ ├── SlackInternalErrorSender.kt │ │ │ │ └── SlackThrottleErrorSender.kt │ │ │ ├── statistic/ │ │ │ │ ├── controller/ │ │ │ │ │ └── AdminStatisticController.kt │ │ │ │ ├── dto/ │ │ │ │ │ └── DashBoardStatisticResponse.kt │ │ │ │ ├── query/ │ │ │ │ │ ├── IssuedTicketQueryRepository.kt │ │ │ │ │ ├── OrderQueryRepository.kt │ │ │ │ │ └── result/ │ │ │ │ │ ├── IssuedTicketStatistic.kt │ │ │ │ │ └── OrderStatistic.kt │ │ │ │ └── useCase/ │ │ │ │ └── StatisticUseCase.kt │ │ │ ├── ticketItem/ │ │ │ │ ├── controller/ │ │ │ │ │ ├── TicketItemController.kt │ │ │ │ │ └── TicketOptionController.kt │ │ │ │ ├── dto/ │ │ │ │ │ ├── request/ │ │ │ │ │ │ ├── ApplyTicketOptionRequest.kt │ │ │ │ │ │ ├── CreateTicketItemRequest.kt │ │ │ │ │ │ ├── CreateTicketOptionRequest.kt │ │ │ │ │ │ └── UnapplyTicketOptionRequest.kt │ │ │ │ │ └── response/ │ │ │ │ │ ├── AppliedOptionGroupResponse.kt │ │ │ │ │ ├── ApplyTicketOptionResponse.kt │ │ │ │ │ ├── GetAppliedOptionGroupsResponse.kt │ │ │ │ │ ├── GetEventOptionsResponse.kt │ │ │ │ │ ├── GetEventTicketItemsResponse.kt │ │ │ │ │ ├── GetTicketItemOptionsResponse.kt │ │ │ │ │ ├── OptionGroupResponse.kt │ │ │ │ │ ├── OptionResponse.kt │ │ │ │ │ └── TicketItemResponse.kt │ │ │ │ ├── mapper/ │ │ │ │ │ ├── TicketItemMapper.kt │ │ │ │ │ └── TicketOptionMapper.kt │ │ │ │ └── service/ │ │ │ │ ├── ApplyTicketOptionUseCase.kt │ │ │ │ ├── CreateTicketItemUseCase.kt │ │ │ │ ├── CreateTicketOptionUseCase.kt │ │ │ │ ├── DeleteOptionGroupUseCase.kt │ │ │ │ ├── DeleteTicketItemUseCase.kt │ │ │ │ ├── GetAppliedOptionGroupsUseCase.kt │ │ │ │ ├── GetEventOptionsUseCase.kt │ │ │ │ ├── GetEventTicketItemsUseCase.kt │ │ │ │ ├── GetTicketOptionsUseCase.kt │ │ │ │ └── UnapplyTicketOptionUseCase.kt │ │ │ └── user/ │ │ │ ├── controller/ │ │ │ │ └── UserController.kt │ │ │ ├── model/ │ │ │ │ └── dto/ │ │ │ │ └── request/ │ │ │ │ └── ChangeNameRequest.kt │ │ │ └── service/ │ │ │ ├── ChangeNameUseCase.kt │ │ │ ├── MarketingUserUseCase.kt │ │ │ └── ReadUserUseCase.kt │ │ └── resources/ │ │ ├── application-local.yml │ │ └── application.yml │ └── test/ │ ├── java/ │ │ └── band/ │ │ └── gosrock/ │ │ ├── DuDoongApiServerApplication.java │ │ └── api/ │ │ ├── email/ │ │ │ └── RegisterUserEventHandlerTest.java │ │ └── supports/ │ │ ├── ApiIntegrateProfileResolver.java │ │ ├── ApiIntegrateSpringBootTest.java │ │ └── ApiIntegrateTestConfig.java │ ├── kotlin/ │ │ └── band/ │ │ └── gosrock/ │ │ └── api/ │ │ ├── auth/ │ │ │ └── service/ │ │ │ └── helper/ │ │ │ └── CookieHelperTest.kt │ │ ├── common/ │ │ │ └── aop/ │ │ │ └── hostRole/ │ │ │ └── HostRoleAopTest.kt │ │ ├── refund/ │ │ │ └── service/ │ │ │ └── CompleteRefundUseCaseTest.kt │ │ └── statistic/ │ │ └── query/ │ │ └── result/ │ │ ├── IssuedTicketStatisticTest.kt │ │ └── OrderStatisticTest.kt │ └── resources/ │ └── logback-test.xml ├── DuDoong-Batch/ │ ├── Dockerfile │ ├── build.gradle.kts │ └── src/ │ └── main/ │ ├── kotlin/ │ │ └── band/ │ │ └── gosrock/ │ │ ├── BatchApplication.kt │ │ ├── dto/ │ │ │ └── SettlementPDFDto.kt │ │ ├── helper/ │ │ │ ├── SettlementEmailHelper.kt │ │ │ ├── SettlementPdfHelper.kt │ │ │ ├── excel/ │ │ │ │ ├── ExcelOrderDto.kt │ │ │ │ └── ExcelOrderHelper.kt │ │ │ └── slack/ │ │ │ ├── SlackEventExpirationSender.kt │ │ │ └── SlackUserNotificationSender.kt │ │ ├── job/ │ │ │ ├── EventExpiration.kt │ │ │ ├── EventOrdersToExcel.kt │ │ │ ├── EventSettlementAlimTalkToHost.kt │ │ │ ├── EventSettlementEmailToAdmin.kt │ │ │ ├── EventSettlementEmailToHost.kt │ │ │ ├── EventSettlementPDF.kt │ │ │ ├── EventSummarySettlement.kt │ │ │ ├── EventTransactionSettlement.kt │ │ │ └── SlackUserStatistic.kt │ │ └── parameter/ │ │ ├── DateJobParameter.kt │ │ ├── DateTimeJobParameter.kt │ │ └── EventJobParameter.kt │ └── resources/ │ └── application.yml ├── DuDoong-Common/ │ ├── build.gradle.kts │ └── src/ │ ├── main/ │ │ ├── kotlin/ │ │ │ └── band/ │ │ │ └── gosrock/ │ │ │ └── common/ │ │ │ ├── DuDoongCommonApplication.kt │ │ │ ├── annotation/ │ │ │ │ ├── Adaptor.kt │ │ │ │ ├── ApiErrorCodeExample.kt │ │ │ │ ├── ApiErrorExceptionsExample.kt │ │ │ │ ├── CurrentUserId.kt │ │ │ │ ├── DateFormat.kt │ │ │ │ ├── DevelopOnlyApi.kt │ │ │ │ ├── DisableSwaggerSecurity.kt │ │ │ │ ├── DomainService.kt │ │ │ │ ├── Enum.kt │ │ │ │ ├── EnumClass.kt │ │ │ │ ├── ExceptionDoc.kt │ │ │ │ ├── ExplainError.kt │ │ │ │ ├── Helper.kt │ │ │ │ ├── Mapper.kt │ │ │ │ ├── Phone.kt │ │ │ │ ├── Policy.kt │ │ │ │ ├── Port.kt │ │ │ │ ├── UseCase.kt │ │ │ │ └── Validator.kt │ │ │ ├── aop/ │ │ │ │ └── ApiBlockingAspect.kt │ │ │ ├── config/ │ │ │ │ └── ConfigurationPropertiesConfig.kt │ │ │ ├── consts/ │ │ │ │ └── DuDoongStatic.kt │ │ │ ├── deserializer/ │ │ │ │ └── CustomEnumDeserializer.kt │ │ │ ├── dto/ │ │ │ │ ├── AccessTokenInfo.kt │ │ │ │ ├── ErrorReason.kt │ │ │ │ ├── ErrorResponse.kt │ │ │ │ ├── OIDCDecodePayload.kt │ │ │ │ └── SuccessResponse.kt │ │ │ ├── exception/ │ │ │ │ ├── BadFileExtensionException.kt │ │ │ │ ├── BadLockIdentifierException.kt │ │ │ │ ├── BaseErrorCode.kt │ │ │ │ ├── DuDoongCodeException.kt │ │ │ │ ├── DuDoongDynamicException.kt │ │ │ │ ├── ExpiredTokenException.kt │ │ │ │ ├── GlobalErrorCode.kt │ │ │ │ ├── InvalidTokenException.kt │ │ │ │ ├── NotAvailableRedissonLockException.kt │ │ │ │ ├── OtherServerBadRequestException.kt │ │ │ │ ├── OtherServerExpiredTokenException.kt │ │ │ │ ├── OtherServerForbiddenException.kt │ │ │ │ ├── OtherServerInternalSeverErrorException.kt │ │ │ │ ├── OtherServerNotFoundException.kt │ │ │ │ ├── OtherServerUnauthorizedException.kt │ │ │ │ ├── RefreshTokenExpiredException.kt │ │ │ │ ├── SecurityContextNotFoundException.kt │ │ │ │ └── TooManyRequestException.kt │ │ │ ├── helper/ │ │ │ │ └── SpringEnvironmentHelper.kt │ │ │ ├── interfaces/ │ │ │ │ └── SwaggerExampleExceptions.kt │ │ │ ├── jwt/ │ │ │ │ ├── JwtOIDCProvider.kt │ │ │ │ └── JwtTokenProvider.kt │ │ │ ├── properties/ │ │ │ │ ├── JwtProperties.kt │ │ │ │ ├── OauthProperties.kt │ │ │ │ └── TossPaymentsProperties.kt │ │ │ └── validator/ │ │ │ ├── EnumValidator.kt │ │ │ └── PhoneValidator.kt │ │ └── resources/ │ │ ├── application-common-local.yml │ │ └── application-common.yml │ └── test/ │ ├── kotlin/ │ │ └── band/ │ │ └── gosrock/ │ │ └── common/ │ │ ├── jwt/ │ │ │ └── JwtTokenProviderTest.kt │ │ └── properties/ │ │ ├── JwtPropertiesTest.kt │ │ └── TossPaymentsPropertiesTest.kt │ └── resources/ │ ├── application.yml │ └── logback-test.xml ├── DuDoong-Domain/ │ ├── build.gradle.kts │ └── src/ │ ├── main/ │ │ ├── kotlin/ │ │ │ └── band/ │ │ │ └── gosrock/ │ │ │ └── domain/ │ │ │ ├── DomainPackageLocation.kt │ │ │ ├── DuDoongDomainApplication.kt │ │ │ ├── common/ │ │ │ │ ├── alarm/ │ │ │ │ │ ├── EventSlackAlarm.kt │ │ │ │ │ ├── HostSlackAlarm.kt │ │ │ │ │ ├── OrderKakaoTalkAlarm.kt │ │ │ │ │ ├── SettlementKakaoTalkAlarm.kt │ │ │ │ │ └── UserKakaoTalkAlarm.kt │ │ │ │ ├── aop/ │ │ │ │ │ ├── domainEvent/ │ │ │ │ │ │ ├── DomainEvent.kt │ │ │ │ │ │ ├── EventPublisherAspect.kt │ │ │ │ │ │ └── Events.kt │ │ │ │ │ └── redissonLock/ │ │ │ │ │ ├── CallTransaction.kt │ │ │ │ │ ├── CallTransactionFactory.kt │ │ │ │ │ ├── RedissonCallNewTransaction.kt │ │ │ │ │ ├── RedissonCallSameTransaction.kt │ │ │ │ │ ├── RedissonLock.kt │ │ │ │ │ └── RedissonLockAop.kt │ │ │ │ ├── converter/ │ │ │ │ │ └── BigDecimalScale6WithBankersRoundingConverter.kt │ │ │ │ ├── dto/ │ │ │ │ │ └── ProfileViewDto.kt │ │ │ │ ├── events/ │ │ │ │ │ ├── event/ │ │ │ │ │ │ ├── EventContentChangeEvent.kt │ │ │ │ │ │ ├── EventCreationEvent.kt │ │ │ │ │ │ ├── EventDeletionEvent.kt │ │ │ │ │ │ └── EventStatusChangeEvent.kt │ │ │ │ │ ├── host/ │ │ │ │ │ │ ├── HostRegisterSlackEvent.kt │ │ │ │ │ │ ├── HostUserDisabledEvent.kt │ │ │ │ │ │ ├── HostUserInvitationEvent.kt │ │ │ │ │ │ ├── HostUserJoinEvent.kt │ │ │ │ │ │ └── HostUserRoleChangeEvent.kt │ │ │ │ │ ├── issuedTicket/ │ │ │ │ │ │ └── EntranceIssuedTicketEvent.kt │ │ │ │ │ ├── order/ │ │ │ │ │ │ ├── CreateOrderEvent.kt │ │ │ │ │ │ ├── DoneOrderEvent.kt │ │ │ │ │ │ └── WithDrawOrderEvent.kt │ │ │ │ │ └── user/ │ │ │ │ │ └── UserRegisterEvent.kt │ │ │ │ ├── model/ │ │ │ │ │ └── BaseTimeEntity.kt │ │ │ │ ├── util/ │ │ │ │ │ ├── PhoneNumberInstance.kt │ │ │ │ │ ├── QueryDslUtil.kt │ │ │ │ │ └── SliceUtil.kt │ │ │ │ └── vo/ │ │ │ │ ├── AccountInfoVo.kt │ │ │ │ ├── CommentInfoVo.kt │ │ │ │ ├── DateTimePeriod.kt │ │ │ │ ├── EventBasicVo.kt │ │ │ │ ├── EventDetailVo.kt │ │ │ │ ├── EventInfoVo.kt │ │ │ │ ├── EventPlaceVo.kt │ │ │ │ ├── EventProfileVo.kt │ │ │ │ ├── HostInfoVo.kt │ │ │ │ ├── HostProfileVo.kt │ │ │ │ ├── HostUserVo.kt │ │ │ │ ├── ImageVo.kt │ │ │ │ ├── IssuedCouponInfoVo.kt │ │ │ │ ├── IssuedTicketInfoVo.kt │ │ │ │ ├── IssuedTicketOptionAnswerVo.kt │ │ │ │ ├── Money.kt │ │ │ │ ├── OptionAnswerVo.kt │ │ │ │ ├── PhoneNumberVo.kt │ │ │ │ ├── RefundInfoVo.kt │ │ │ │ ├── UserInfoVo.kt │ │ │ │ └── UserProfileVo.kt │ │ │ ├── config/ │ │ │ │ ├── CustomAsyncExceptionHandler.kt │ │ │ │ ├── EnableAsyncConfig.kt │ │ │ │ ├── JpaConfig.kt │ │ │ │ ├── MdcTaskDecorator.kt │ │ │ │ └── QueryDslConfig.kt │ │ │ └── domains/ │ │ │ ├── cart/ │ │ │ │ ├── adaptor/ │ │ │ │ │ └── CartAdaptor.kt │ │ │ │ ├── domain/ │ │ │ │ │ ├── Cart.kt │ │ │ │ │ ├── CartLineItem.kt │ │ │ │ │ ├── CartOptionAnswer.kt │ │ │ │ │ └── CartValidator.kt │ │ │ │ ├── exception/ │ │ │ │ │ ├── CartErrorCode.kt │ │ │ │ │ ├── CartInvalidOptionAnswerException.kt │ │ │ │ │ ├── CartItemNotOneTypeException.kt │ │ │ │ │ ├── CartLineItemNotFoundException.kt │ │ │ │ │ ├── CartNotAnswerAllOptionGroupException.kt │ │ │ │ │ └── CartNotFoundException.kt │ │ │ │ ├── repository/ │ │ │ │ │ ├── CartCustomRepository.kt │ │ │ │ │ ├── CartCustomRepositoryImpl.kt │ │ │ │ │ └── CartRepository.kt │ │ │ │ └── service/ │ │ │ │ ├── CartDomainService.kt │ │ │ │ └── DoneOrderEventHandler.kt │ │ │ ├── comment/ │ │ │ │ ├── adaptor/ │ │ │ │ │ └── CommentAdaptor.kt │ │ │ │ ├── domain/ │ │ │ │ │ ├── Comment.kt │ │ │ │ │ └── CommentStatus.kt │ │ │ │ ├── dto/ │ │ │ │ │ └── condition/ │ │ │ │ │ └── CommentCondition.kt │ │ │ │ ├── exception/ │ │ │ │ │ ├── CommentAlreadyDeleteException.kt │ │ │ │ │ ├── CommentErrorCode.kt │ │ │ │ │ ├── CommentNotFoundException.kt │ │ │ │ │ ├── CommentNotMatchEventException.kt │ │ │ │ │ └── RetrieveRandomCommentNotFoundException.kt │ │ │ │ ├── repository/ │ │ │ │ │ ├── CommentCustomRepository.kt │ │ │ │ │ ├── CommentCustomRepositoryImpl.kt │ │ │ │ │ └── CommentRepository.kt │ │ │ │ └── service/ │ │ │ │ └── CommentDomainService.kt │ │ │ ├── coupon/ │ │ │ │ ├── adaptor/ │ │ │ │ │ ├── CouponCampaignAdaptor.kt │ │ │ │ │ └── IssuedCouponAdaptor.kt │ │ │ │ ├── domain/ │ │ │ │ │ ├── ApplyTarget.kt │ │ │ │ │ ├── CouponCampaign.kt │ │ │ │ │ ├── CouponStockInfo.kt │ │ │ │ │ ├── DiscountType.kt │ │ │ │ │ └── IssuedCoupon.kt │ │ │ │ ├── exception/ │ │ │ │ │ ├── AlreadyExistCouponCampaignException.kt │ │ │ │ │ ├── AlreadyIssuedCouponException.kt │ │ │ │ │ ├── AlreadyRecoveredCouponException.kt │ │ │ │ │ ├── AlreadyUsedCouponException.kt │ │ │ │ │ ├── CouponCampaignNotFoundException.kt │ │ │ │ │ ├── CouponErrorCode.kt │ │ │ │ │ ├── CouponNotFoundException.kt │ │ │ │ │ ├── NoCouponStockLeftException.kt │ │ │ │ │ ├── NotIssuingCouponPeriodException.kt │ │ │ │ │ ├── NotMyCouponException.kt │ │ │ │ │ ├── SupplyLessThenDiscountException.kt │ │ │ │ │ ├── SupplyLessThenMinimumException.kt │ │ │ │ │ └── WrongDiscountAmountException.kt │ │ │ │ ├── repository/ │ │ │ │ │ ├── CouponCampaignRepository.kt │ │ │ │ │ ├── IssuedCouponCustomRepository.kt │ │ │ │ │ ├── IssuedCouponCustomRepositoryImpl.kt │ │ │ │ │ └── IssuedCouponRepository.kt │ │ │ │ └── service/ │ │ │ │ ├── CreateCouponCampaignDomainService.kt │ │ │ │ ├── CreateIssuedCouponDomainService.kt │ │ │ │ ├── RecoveryCouponService.kt │ │ │ │ ├── UseCouponService.kt │ │ │ │ └── handler/ │ │ │ │ ├── CreateOrderCouponHandler.kt │ │ │ │ └── WithDrawOrderCouponHandler.kt │ │ │ ├── event/ │ │ │ │ ├── adaptor/ │ │ │ │ │ └── EventAdaptor.kt │ │ │ │ ├── domain/ │ │ │ │ │ ├── Event.kt │ │ │ │ │ ├── EventBasic.kt │ │ │ │ │ ├── EventDetail.kt │ │ │ │ │ ├── EventDetailImage.kt │ │ │ │ │ ├── EventPlace.kt │ │ │ │ │ └── EventStatus.kt │ │ │ │ ├── exception/ │ │ │ │ │ ├── AlreadyCalculatingStatusException.kt │ │ │ │ │ ├── AlreadyCloseStatusException.kt │ │ │ │ │ ├── AlreadyDeletedStatusException.kt │ │ │ │ │ ├── AlreadyExistEventUrlNameException.kt │ │ │ │ │ ├── AlreadyOpenStatusException.kt │ │ │ │ │ ├── AlreadyPreparingStatusException.kt │ │ │ │ │ ├── CannotDeleteByIssuedTicketException.kt │ │ │ │ │ ├── CannotDeleteByOpenEventException.kt │ │ │ │ │ ├── CannotModifyOpenEventException.kt │ │ │ │ │ ├── CannotOpenEventException.kt │ │ │ │ │ ├── EventCannotEndBeforeStartException.kt │ │ │ │ │ ├── EventErrorCode.kt │ │ │ │ │ ├── EventNotFoundException.kt │ │ │ │ │ ├── EventNotOpenException.kt │ │ │ │ │ ├── EventOpenTimeExpiredException.kt │ │ │ │ │ ├── EventTicketingTimeIsPassedException.kt │ │ │ │ │ ├── HostNotAuthEventException.kt │ │ │ │ │ ├── InvalidEventStatusTransitionException.kt │ │ │ │ │ └── UseOtherApiException.kt │ │ │ │ ├── repository/ │ │ │ │ │ ├── EventCustomRepository.kt │ │ │ │ │ ├── EventCustomRepositoryImpl.kt │ │ │ │ │ ├── EventDetailImageRepository.kt │ │ │ │ │ └── EventRepository.kt │ │ │ │ └── service/ │ │ │ │ └── EventService.kt │ │ │ ├── example/ │ │ │ │ ├── domain/ │ │ │ │ │ └── ExampleEntity.kt │ │ │ │ ├── repository/ │ │ │ │ │ └── ExampleRepository.kt │ │ │ │ └── service/ │ │ │ │ └── ExampleDomainService.kt │ │ │ ├── host/ │ │ │ │ ├── adaptor/ │ │ │ │ │ └── HostAdaptor.kt │ │ │ │ ├── domain/ │ │ │ │ │ ├── Host.kt │ │ │ │ │ ├── HostProfile.kt │ │ │ │ │ ├── HostRole.kt │ │ │ │ │ └── HostUser.kt │ │ │ │ ├── exception/ │ │ │ │ │ ├── AlreadyJoinedHostException.kt │ │ │ │ │ ├── CannotModifyMasterHostRoleException.kt │ │ │ │ │ ├── DuplicateSlackUrlException.kt │ │ │ │ │ ├── ForbiddenHostException.kt │ │ │ │ │ ├── HostErrorCode.kt │ │ │ │ │ ├── HostNotFoundException.kt │ │ │ │ │ ├── HostUserNotFoundException.kt │ │ │ │ │ ├── InvalidSlackUrlException.kt │ │ │ │ │ ├── NotAcceptedHostException.kt │ │ │ │ │ ├── NotManagerHostException.kt │ │ │ │ │ ├── NotMasterHostException.kt │ │ │ │ │ └── NotPartnerHostException.kt │ │ │ │ ├── repository/ │ │ │ │ │ ├── HostCustomRepository.kt │ │ │ │ │ ├── HostCustomRepositoryImpl.kt │ │ │ │ │ └── HostRepository.kt │ │ │ │ └── service/ │ │ │ │ └── HostService.kt │ │ │ ├── issuedTicket/ │ │ │ │ ├── adaptor/ │ │ │ │ │ ├── IssuedTicketAdaptor.kt │ │ │ │ │ └── IssuedTicketOptionAnswerAdaptor.kt │ │ │ │ ├── domain/ │ │ │ │ │ ├── IssuedTicket.kt │ │ │ │ │ ├── IssuedTicketCancelReason.kt │ │ │ │ │ ├── IssuedTicketItemInfoVo.kt │ │ │ │ │ ├── IssuedTicketOptionAnswer.kt │ │ │ │ │ ├── IssuedTicketStatus.kt │ │ │ │ │ ├── IssuedTicketUserInfoVo.kt │ │ │ │ │ ├── IssuedTickets.kt │ │ │ │ │ └── IssuedTicketsStage.kt │ │ │ │ ├── dto/ │ │ │ │ │ ├── condition/ │ │ │ │ │ │ └── IssuedTicketCondition.kt │ │ │ │ │ ├── request/ │ │ │ │ │ │ ├── CreateIssuedTicketDTO.kt │ │ │ │ │ │ ├── CreateIssuedTicketForDevDTO.kt │ │ │ │ │ │ ├── CreateIssuedTicketRequestDTOs.kt │ │ │ │ │ │ └── CreateIssuedTicketRequestForDev.kt │ │ │ │ │ └── response/ │ │ │ │ │ ├── CreateIssuedTicketResponse.kt │ │ │ │ │ ├── IssuedTicketDTO.kt │ │ │ │ │ └── IssuedTicketPageDTO.kt │ │ │ │ ├── exception/ │ │ │ │ │ ├── CanNotCancelEntranceException.kt │ │ │ │ │ ├── CanNotCancelException.kt │ │ │ │ │ ├── CanNotEntranceException.kt │ │ │ │ │ ├── IssuedTicketAlreadyEntranceException.kt │ │ │ │ │ ├── IssuedTicketErrorCode.kt │ │ │ │ │ ├── IssuedTicketNotFoundException.kt │ │ │ │ │ ├── IssuedTicketNotMatchedEventException.kt │ │ │ │ │ └── IssuedTicketUserNotMatchedException.kt │ │ │ │ ├── repository/ │ │ │ │ │ ├── IssuedTicketCustomRepository.kt │ │ │ │ │ ├── IssuedTicketCustomRepositoryImpl.kt │ │ │ │ │ ├── IssuedTicketOptionAnswerRepository.kt │ │ │ │ │ ├── IssuedTicketRepository.kt │ │ │ │ │ └── condition/ │ │ │ │ │ └── FindEventIssuedTicketsCondition.kt │ │ │ │ ├── service/ │ │ │ │ │ ├── IssuedTicketDomainService.kt │ │ │ │ │ ├── OrderToIssuedTicketService.kt │ │ │ │ │ └── handlers/ │ │ │ │ │ ├── OrderEventHandler.kt │ │ │ │ │ └── WithdrawOrderEventHandler.kt │ │ │ │ └── validator/ │ │ │ │ └── IssuedTicketValidator.kt │ │ │ ├── order/ │ │ │ │ ├── adaptor/ │ │ │ │ │ └── OrderAdaptor.kt │ │ │ │ ├── domain/ │ │ │ │ │ ├── Order.kt │ │ │ │ │ ├── OrderCouponVo.kt │ │ │ │ │ ├── OrderItemVo.kt │ │ │ │ │ ├── OrderLineItem.kt │ │ │ │ │ ├── OrderMethod.kt │ │ │ │ │ ├── OrderOptionAnswer.kt │ │ │ │ │ ├── OrderStatus.kt │ │ │ │ │ ├── PaymentInfo.kt │ │ │ │ │ ├── PaymentMethod.kt │ │ │ │ │ ├── PgPaymentInfo.kt │ │ │ │ │ ├── RefundStatus.kt │ │ │ │ │ └── validator/ │ │ │ │ │ └── OrderValidator.kt │ │ │ │ ├── exception/ │ │ │ │ │ ├── ApproveWaitingOrderPurchaseLimitException.kt │ │ │ │ │ ├── CanNotApproveDeletedUserOrderException.kt │ │ │ │ │ ├── CanNotCancelOrderException.kt │ │ │ │ │ ├── CanNotRefundOrderException.kt │ │ │ │ │ ├── CanNotRefuseOrderException.kt │ │ │ │ │ ├── InvalidOrderException.kt │ │ │ │ │ ├── LessThanMinmumPaymentOrderException.kt │ │ │ │ │ ├── NotApprovalOrderException.kt │ │ │ │ │ ├── NotFreeOrderException.kt │ │ │ │ │ ├── NotOwnerOrderException.kt │ │ │ │ │ ├── NotPaymentOrderException.kt │ │ │ │ │ ├── NotPendingOrderException.kt │ │ │ │ │ ├── NotRefundAvailableDateOrderException.kt │ │ │ │ │ ├── NotSupportedOrderMethodException.kt │ │ │ │ │ ├── OrdeItemNotOneTypeException.kt │ │ │ │ │ ├── OrderErrorCode.kt │ │ │ │ │ ├── OrderItemOptionChangedException.kt │ │ │ │ │ ├── OrderLineNotFountException.kt │ │ │ │ │ └── OrderNotFoundException.kt │ │ │ │ ├── repository/ │ │ │ │ │ ├── OrderCustomRepository.kt │ │ │ │ │ ├── OrderCustomRepositoryImpl.kt │ │ │ │ │ ├── OrderRepository.kt │ │ │ │ │ └── condition/ │ │ │ │ │ ├── AdminTableOrderFilterType.kt │ │ │ │ │ ├── AdminTableSearchType.kt │ │ │ │ │ ├── FindEventOrdersCondition.kt │ │ │ │ │ └── FindMyPageOrderCondition.kt │ │ │ │ └── service/ │ │ │ │ ├── CreateOrderService.kt │ │ │ │ ├── FreeOrderService.kt │ │ │ │ ├── OrderApproveService.kt │ │ │ │ ├── OrderConfirmService.kt │ │ │ │ ├── OrderFactory.kt │ │ │ │ ├── WithdrawOrderService.kt │ │ │ │ ├── WithdrawPaymentService.kt │ │ │ │ └── handler/ │ │ │ │ ├── ConfirmOrderFailHandler.kt │ │ │ │ └── WithDrawOrderHandler.kt │ │ │ ├── settlement/ │ │ │ │ ├── adaptor/ │ │ │ │ │ ├── EventSettlementAdaptor.kt │ │ │ │ │ └── TransactionSettlementAdaptor.kt │ │ │ │ ├── domain/ │ │ │ │ │ ├── EventSettlement.kt │ │ │ │ │ ├── EventSettlementStatus.kt │ │ │ │ │ ├── SettlementFeeVo.kt │ │ │ │ │ └── TransactionSettlement.kt │ │ │ │ ├── repository/ │ │ │ │ │ ├── EventSettlementRepository.kt │ │ │ │ │ └── TransactionSettlementRepository.kt │ │ │ │ └── service/ │ │ │ │ └── EventSettlementDomainService.kt │ │ │ ├── ticket_item/ │ │ │ │ ├── adaptor/ │ │ │ │ │ ├── OptionAdaptor.kt │ │ │ │ │ ├── OptionGroupAdaptor.kt │ │ │ │ │ └── TicketItemAdaptor.kt │ │ │ │ ├── domain/ │ │ │ │ │ ├── ItemOptionGroup.kt │ │ │ │ │ ├── Option.kt │ │ │ │ │ ├── OptionGroup.kt │ │ │ │ │ ├── OptionGroupStatus.kt │ │ │ │ │ ├── OptionGroupType.kt │ │ │ │ │ ├── TicketItem.kt │ │ │ │ │ ├── TicketItemStatus.kt │ │ │ │ │ ├── TicketPayType.kt │ │ │ │ │ └── TicketType.kt │ │ │ │ ├── exception/ │ │ │ │ │ ├── DuplicatedItemOptionGroupException.kt │ │ │ │ │ ├── EmptyAccountInfoException.kt │ │ │ │ │ ├── ForbiddenOptionChangeException.kt │ │ │ │ │ ├── ForbiddenOptionGroupDeleteException.kt │ │ │ │ │ ├── ForbiddenOptionPriceException.kt │ │ │ │ │ ├── ForbiddenTicketItemDeleteException.kt │ │ │ │ │ ├── InvalidOptionGroupException.kt │ │ │ │ │ ├── InvalidOptionPriceException.kt │ │ │ │ │ ├── InvalidPartnerException.kt │ │ │ │ │ ├── InvalidTicketItemException.kt │ │ │ │ │ ├── InvalidTicketPriceException.kt │ │ │ │ │ ├── InvalidTicketTypeException.kt │ │ │ │ │ ├── NotAppliedItemOptionGroupException.kt │ │ │ │ │ ├── NotCorrectOptionAnswerException.kt │ │ │ │ │ ├── OptionGroupNotFoundException.kt │ │ │ │ │ ├── OptionNotFoundException.kt │ │ │ │ │ ├── TicketItemErrorCode.kt │ │ │ │ │ ├── TicketItemNotFoundException.kt │ │ │ │ │ ├── TicketItemQuantityException.kt │ │ │ │ │ ├── TicketItemQuantityLackException.kt │ │ │ │ │ ├── TicketItemQuantityLargeException.kt │ │ │ │ │ └── TicketPurchaseLimitException.kt │ │ │ │ ├── repository/ │ │ │ │ │ ├── OptionGroupRepository.kt │ │ │ │ │ ├── OptionRepository.kt │ │ │ │ │ └── TicketItemRepository.kt │ │ │ │ └── service/ │ │ │ │ ├── ItemOptionGroupService.kt │ │ │ │ ├── TicketItemService.kt │ │ │ │ └── TicketOptionService.kt │ │ │ └── user/ │ │ │ ├── adaptor/ │ │ │ │ ├── RefreshTokenAdaptor.kt │ │ │ │ └── UserAdaptor.kt │ │ │ ├── domain/ │ │ │ │ ├── AccountRole.kt │ │ │ │ ├── AccountState.kt │ │ │ │ ├── OauthInfo.kt │ │ │ │ ├── OauthProvider.kt │ │ │ │ ├── Profile.kt │ │ │ │ ├── RefreshTokenEntity.kt │ │ │ │ └── User.kt │ │ │ ├── exception/ │ │ │ │ ├── AlreadyDeletedUserException.kt │ │ │ │ ├── AlreadySignUpUserException.kt │ │ │ │ ├── EmptyPhoneNumException.kt │ │ │ │ ├── ForbiddenUserException.kt │ │ │ │ ├── UserErrorCode.kt │ │ │ │ ├── UserNotFoundException.kt │ │ │ │ └── UserPhoneNumberInvalidException.kt │ │ │ ├── repository/ │ │ │ │ ├── RefreshTokenRepository.kt │ │ │ │ └── UserRepository.kt │ │ │ └── service/ │ │ │ └── UserDomainService.kt │ │ └── resources/ │ │ ├── application-domain-local.yml │ │ └── application-domain.yml │ └── test/ │ ├── java/ │ │ └── band/ │ │ └── gosrock/ │ │ ├── domain/ │ │ │ ├── CunCurrencyExecutorService.java │ │ │ ├── DisableDomainEvent.java │ │ │ ├── DisableRedissonLock.java │ │ │ ├── DomainIntegrateProfileResolver.java │ │ │ ├── DomainIntegrateSpringBootTest.java │ │ │ ├── DomainIntegrateTestConfig.java │ │ │ ├── common/ │ │ │ │ ├── aop/ │ │ │ │ │ └── redissonLock/ │ │ │ │ │ └── RedissonLockAopTest.java │ │ │ │ └── vo/ │ │ │ │ └── RefundInfoVoTest.java │ │ │ └── domains/ │ │ │ ├── cart/ │ │ │ │ └── domain/ │ │ │ │ ├── CartLineItemTest.java │ │ │ │ ├── CartOptionAnswerTest.java │ │ │ │ ├── CartTest.java │ │ │ │ └── CartValidatorTest.java │ │ │ ├── coupon/ │ │ │ │ └── domain/ │ │ │ │ ├── CouponCampaignTest.java │ │ │ │ ├── CouponStockInfoTest.java │ │ │ │ └── IssuedCouponTest.java │ │ │ ├── event/ │ │ │ │ └── EventTest.java │ │ │ ├── host/ │ │ │ │ ├── domain/ │ │ │ │ │ ├── HostProfileTest.java │ │ │ │ │ ├── HostRoleTest.java │ │ │ │ │ ├── HostTest.java │ │ │ │ │ └── HostUserTest.java │ │ │ │ └── service/ │ │ │ │ ├── HostServiceConcurrencyFailureTest.java │ │ │ │ └── HostServiceConcurrencyTest.java │ │ │ ├── issuedTicket/ │ │ │ │ ├── adaptor/ │ │ │ │ │ └── IssuedTicketAdaptorTest.java │ │ │ │ ├── domain/ │ │ │ │ │ ├── IssuedTicketItemInfoVoTest.java │ │ │ │ │ ├── IssuedTicketOptionAnswerTest.java │ │ │ │ │ ├── IssuedTicketTest.java │ │ │ │ │ ├── IssuedTicketUserInfoVoTest.java │ │ │ │ │ └── validator/ │ │ │ │ │ └── IssuedTicketValidatorTest.java │ │ │ │ └── service/ │ │ │ │ ├── IssuedTicketDomainServiceTest.java │ │ │ │ └── handlers/ │ │ │ │ ├── OrderEventHandlerTest.java │ │ │ │ └── WithdrawOrderEventHandlerTest.java │ │ │ └── order/ │ │ │ ├── domain/ │ │ │ │ ├── OrderLineItemTest.java │ │ │ │ ├── OrderOptionAnswerTest.java │ │ │ │ ├── OrderTest.java │ │ │ │ └── validator/ │ │ │ │ └── OrderValidatorTest.java │ │ │ └── service/ │ │ │ ├── OrderApproveServiceConcurrencyFailTest.java │ │ │ ├── OrderApproveServiceConcurrencyTest.java │ │ │ ├── OrderApproveServiceTest.java │ │ │ ├── WithdrawOrderServiceTest.java │ │ │ └── handler/ │ │ │ └── WithDrawOrderHandlerTest.java │ │ └── domains/ │ │ └── user/ │ │ └── domain/ │ │ ├── OauthInfoTest.java │ │ └── UserTest.java │ ├── kotlin/ │ │ └── band/ │ │ └── gosrock/ │ │ └── domain/ │ │ ├── common/ │ │ │ └── vo/ │ │ │ ├── DateTimePeriodTest.kt │ │ │ └── MoneyTest.kt │ │ └── domains/ │ │ ├── cart/ │ │ │ └── service/ │ │ │ └── DoneOrderEventHandlerTest.kt │ │ ├── event/ │ │ │ └── EventStatusTransitionTest.kt │ │ ├── host/ │ │ │ └── HostTransferMasterTest.kt │ │ ├── issuedTicket/ │ │ │ ├── IssuedTicketStatusTransitionTest.kt │ │ │ ├── adaptor/ │ │ │ │ └── IssuedTicketAdaptorTest.kt │ │ │ └── domain/ │ │ │ └── IssuedTicketItemInfoVoTest.kt │ │ ├── order/ │ │ │ ├── OrderPaymentCalculationTest.kt │ │ │ ├── OrderRefundTest.kt │ │ │ └── service/ │ │ │ └── handler/ │ │ │ └── ConfirmOrderFailHandlerTest.kt │ │ └── user/ │ │ └── domain/ │ │ └── ProfileTest.kt │ └── resources/ │ ├── application-test.yml │ ├── logback-test.xml │ └── mockito-extensions/ │ └── org.mockito.plugins.MockMaker ├── DuDoong-Infrastructure/ │ ├── build.gradle.kts │ └── src/ │ ├── main/ │ │ ├── kotlin/ │ │ │ └── band/ │ │ │ └── gosrock/ │ │ │ └── infrastructure/ │ │ │ ├── DuDoongInfraApplication.kt │ │ │ ├── config/ │ │ │ │ ├── alilmTalk/ │ │ │ │ │ ├── NcpHelper.kt │ │ │ │ │ └── dto/ │ │ │ │ │ ├── AlimTalkEventInfo.kt │ │ │ │ │ ├── AlimTalkOrderInfo.kt │ │ │ │ │ ├── AlimTalkUserInfo.kt │ │ │ │ │ └── MessageDto.kt │ │ │ │ ├── feign/ │ │ │ │ │ └── FeignCommonConfig.kt │ │ │ │ ├── mail/ │ │ │ │ │ └── dto/ │ │ │ │ │ ├── EmailEventInfo.kt │ │ │ │ │ ├── EmailIssuedTicketInfo.kt │ │ │ │ │ ├── EmailOrderInfo.kt │ │ │ │ │ └── EmailUserInfo.kt │ │ │ │ ├── pdf/ │ │ │ │ │ ├── B64ImgReplacedElementFactory.kt │ │ │ │ │ └── PdfRender.kt │ │ │ │ ├── redis/ │ │ │ │ │ ├── RedisCacheConfig.kt │ │ │ │ │ ├── RedisConfig.kt │ │ │ │ │ └── RedissonConfig.kt │ │ │ │ ├── s3/ │ │ │ │ │ ├── ImageFileExtension.kt │ │ │ │ │ ├── ImageUrlDto.kt │ │ │ │ │ ├── S3Config.kt │ │ │ │ │ ├── S3PrivateFileService.kt │ │ │ │ │ └── S3UploadPresignedUrlService.kt │ │ │ │ ├── ses/ │ │ │ │ │ ├── AwsSesConfig.kt │ │ │ │ │ ├── AwsSesUtils.kt │ │ │ │ │ ├── RawEmailAttachmentDto.kt │ │ │ │ │ └── SendRawEmailDto.kt │ │ │ │ └── slack/ │ │ │ │ ├── SlackAsyncErrorSender.kt │ │ │ │ ├── SlackErrorNotificationProvider.kt │ │ │ │ ├── SlackHelper.kt │ │ │ │ ├── SlackMessageProvider.kt │ │ │ │ ├── SlackServiceNotificationProvider.kt │ │ │ │ └── config/ │ │ │ │ └── SlackApiConfig.kt │ │ │ └── outer/ │ │ │ └── api/ │ │ │ ├── BaseFeignClientPackage.kt │ │ │ ├── alimTalk/ │ │ │ │ ├── client/ │ │ │ │ │ └── NcpClient.kt │ │ │ │ └── config/ │ │ │ │ ├── NcpConfig.kt │ │ │ │ └── NcpErrorDecoder.kt │ │ │ ├── oauth/ │ │ │ │ ├── client/ │ │ │ │ │ ├── KakaoInfoClient.kt │ │ │ │ │ └── KakaoOauthClient.kt │ │ │ │ ├── config/ │ │ │ │ │ ├── KakaoInfoConfig.kt │ │ │ │ │ ├── KakaoInfoErrorDecoder.kt │ │ │ │ │ ├── KakaoKauthConfig.kt │ │ │ │ │ └── KauthErrorDecoder.kt │ │ │ │ ├── dto/ │ │ │ │ │ ├── KakaoInformationResponse.kt │ │ │ │ │ ├── KakaoKauthErrorResponse.kt │ │ │ │ │ ├── KakaoTokenResponse.kt │ │ │ │ │ ├── OIDCPublicKeyDto.kt │ │ │ │ │ ├── OIDCPublicKeysResponse.kt │ │ │ │ │ └── UnlinkKaKaoTarget.kt │ │ │ │ └── exception/ │ │ │ │ └── KakaoKauthErrorCode.kt │ │ │ └── tossPayments/ │ │ │ ├── client/ │ │ │ │ ├── PaymentsCancelClient.kt │ │ │ │ ├── PaymentsConfirmClient.kt │ │ │ │ ├── PaymentsCreateClient.kt │ │ │ │ ├── SettlementClient.kt │ │ │ │ └── TransactionGetClient.kt │ │ │ ├── config/ │ │ │ │ ├── FeignTossConfig.kt │ │ │ │ ├── PaymentCancelErrorDecoder.kt │ │ │ │ ├── PaymentConfirmErrorDecoder.kt │ │ │ │ ├── PaymentCreateErrorDecoder.kt │ │ │ │ ├── PaymentsCancelConfig.kt │ │ │ │ ├── PaymentsConfirmConfig.kt │ │ │ │ ├── PaymentsCreateConfig.kt │ │ │ │ ├── TossErrorDecoder.kt │ │ │ │ ├── TossHeaderConfig.kt │ │ │ │ ├── TransactionGetConfig.kt │ │ │ │ └── TransactionGetErrorDecoder.kt │ │ │ ├── dto/ │ │ │ │ ├── request/ │ │ │ │ │ ├── CancelPaymentsRequest.kt │ │ │ │ │ ├── ConfirmPaymentsRequest.kt │ │ │ │ │ └── CreatePaymentsRequest.kt │ │ │ │ └── response/ │ │ │ │ ├── CardAcquireStatus.kt │ │ │ │ ├── CardCode.kt │ │ │ │ ├── EasyPayCode.kt │ │ │ │ ├── FeeCode.kt │ │ │ │ ├── PaymentCheckout.kt │ │ │ │ ├── PaymentEasyPay.kt │ │ │ │ ├── PaymentReceipt.kt │ │ │ │ ├── PaymentStatus.kt │ │ │ │ ├── PaymentsCancels.kt │ │ │ │ ├── PaymentsCard.kt │ │ │ │ ├── PaymentsCardPromotion.kt │ │ │ │ ├── PaymentsCashReceipt.kt │ │ │ │ ├── PaymentsFailure.kt │ │ │ │ ├── PaymentsResponse.kt │ │ │ │ ├── SettlementFeeDto.kt │ │ │ │ ├── SettlementResponse.kt │ │ │ │ └── TossPaymentMethod.kt │ │ │ └── exception/ │ │ │ ├── PaymentsCancelErrorCode.kt │ │ │ ├── PaymentsConfirmErrorCode.kt │ │ │ ├── PaymentsCreateErrorCode.kt │ │ │ ├── PaymentsEnumNotMatchException.kt │ │ │ ├── PaymentsUnHandleException.kt │ │ │ ├── TossPaymentsErrorDto.kt │ │ │ └── TransactionGetErrorCode.kt │ │ └── resources/ │ │ ├── application-infrastructure.yml │ │ └── templates/ │ │ ├── entranceIssuedTicket.html │ │ ├── eventSettlement.html │ │ ├── fragments/ │ │ │ ├── button.html │ │ │ ├── divider.html │ │ │ ├── footer.html │ │ │ ├── header.html │ │ │ ├── issuedTicketInfo.html │ │ │ ├── orderInfo.html │ │ │ ├── subTilte.html │ │ │ └── title.html │ │ ├── hostInvite.html │ │ ├── layouts/ │ │ │ └── mailFormat.html │ │ ├── orderApproveConfirm.html │ │ ├── orderApproveRequest.html │ │ ├── orderPaymentDone.html │ │ ├── orderWithdrawCancel.html │ │ ├── orderWithdrawRefund.html │ │ ├── settlement.html │ │ └── signUp.html │ └── test/ │ ├── java/ │ │ └── band/ │ │ └── gosrock/ │ │ └── infrastructure/ │ │ ├── InfraIntegrateProfileResolver.java │ │ ├── InfraIntegrateSpringBootTest.java │ │ ├── InfraIntegrateTestConfig.java │ │ ├── config/ │ │ │ ├── redis/ │ │ │ │ └── AutoConfigureTestFeign.java │ │ │ └── s3/ │ │ │ └── S3UploadPresignedUrlServiceTest.java │ │ └── outer/ │ │ └── api/ │ │ └── tossPayments/ │ │ ├── client/ │ │ │ ├── PaymentsCancelClientTest.java │ │ │ ├── TossPaymentsClientTest.java │ │ │ └── TossSettlementClientTest.java │ │ └── config/ │ │ └── PaymentConfirmErrorDecoderTest.java │ └── resources/ │ ├── application.yml │ ├── logback-test.xml │ └── payload/ │ └── settlement-response.json ├── LICENSE ├── README.md ├── build.gradle.kts ├── docker-compose.yml ├── e2e-tests/ │ ├── .gitignore │ ├── README.md │ ├── conftest.py │ ├── requirements.txt │ ├── test_01_auth.py │ ├── test_02_host.py │ ├── test_03_event.py │ ├── test_04_ticket_item.py │ ├── test_05_order_flow.py │ ├── test_06_issued_ticket.py │ ├── test_07_comment.py │ ├── test_08_refund.py │ ├── test_09_error_cases.py │ ├── test_10_event_lifecycle.py │ ├── test_11_multi_user.py │ ├── test_12_host_management.py │ ├── test_13_user_profile.py │ ├── test_14_ticket_stock.py │ ├── test_15_event_crud.py │ ├── test_16_event_open_conditions.py │ ├── test_17_event_modification_rules.py │ ├── test_18_event_status_matrix.py │ ├── test_19_host_invite.py │ ├── test_20_ticket_options.py │ ├── test_21_dudoong_ticket.py │ ├── test_22_order_detail_verification.py │ ├── test_23_refund_edge_cases.py │ ├── test_24_admin_features.py │ ├── test_25_coupon_flow.py │ ├── test_26_order_edge_cases.py │ ├── test_27_host_role_change.py │ ├── test_28_ticket_quantity_public.py │ ├── test_29_order_cancel.py │ ├── test_30_order_before_open.py │ ├── test_31_admin_auth_role.py │ ├── test_32_admin_token_separation.py │ ├── test_33_host_role_authorization.py │ ├── test_34_admin_usecase_authorization.py │ ├── test_35_user_nickname.py │ ├── test_36_host_master_transfer.py │ ├── test_37_order_cancel_reason.py │ └── test_38_refund_api.py ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lombok.config ├── scripts/ │ └── local-start.sh └── settings.gradle.kts
SYMBOL INDEX (483 symbols across 88 files)
FILE: DuDoong-Api/src/test/java/band/gosrock/DuDoongApiServerApplication.java
class DuDoongApiServerApplication (line 7) | @ApiIntegrateSpringBootTest
method contextLoads (line 9) | @Test
FILE: DuDoong-Api/src/test/java/band/gosrock/api/email/RegisterUserEventHandlerTest.java
class RegisterUserEventHandlerTest (line 17) | @ApiIntegrateSpringBootTest
method 유저등록시도메인이벤트가발생해야한다 (line 23) | @Test
FILE: DuDoong-Api/src/test/java/band/gosrock/api/supports/ApiIntegrateProfileResolver.java
class ApiIntegrateProfileResolver (line 9) | public class ApiIntegrateProfileResolver implements ActiveProfilesResolv...
method resolve (line 11) | @Override
FILE: DuDoong-Api/src/test/java/band/gosrock/api/supports/ApiIntegrateTestConfig.java
class ApiIntegrateTestConfig (line 12) | @Configuration
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/CunCurrencyExecutorService.java
class CunCurrencyExecutorService (line 12) | @Slf4j
method execute (line 17) | public static void execute(Executable executable, AtomicLong successCo...
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/DomainIntegrateProfileResolver.java
class DomainIntegrateProfileResolver (line 9) | public class DomainIntegrateProfileResolver implements ActiveProfilesRes...
method resolve (line 11) | @Override
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/DomainIntegrateTestConfig.java
class DomainIntegrateTestConfig (line 10) | @Configuration
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/common/aop/redissonLock/RedissonLockAopTest.java
class RedissonLockAopTest (line 15) | @ExtendWith(MockitoExtension.class)
method beforeEach (line 22) | @BeforeEach
method 커스텀오브젝트에서_클래스타입과_식별자로_키를_가져와야한다 (line 27) | @Test
method 기본오브젝트에서_식별자로_키를_가져와야한다 (line 41) | @Test
method 잘못된_인자를_설정하면_오류가_발생해야한다 (line 55) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/common/vo/RefundInfoVoTest.java
class RefundInfoVoTest (line 8) | class RefundInfoVoTest {
method 환불가능_시간_테스 (line 10) | @Test
method 환불불가_시간_테스트 (line 22) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/cart/domain/CartLineItemTest.java
class CartLineItemTest (line 15) | @ExtendWith(MockitoExtension.class)
method setup (line 30) | @BeforeEach
method 카트라인_총옵션가격_조회 (line 45) | @Test
method 카트라인_총가격_조회 (line 56) | @Test
method 카트라인_결제금액_있을때_결제필요_여부_조회 (line 67) | @Test
method 카트라인_결제금액_없을때_결제필요_여부_조회 (line 78) | @Test
method 카트라인_옵션아이디목록_조회_검증 (line 88) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/cart/domain/CartOptionAnswerTest.java
class CartOptionAnswerTest (line 16) | @ExtendWith(MockitoExtension.class)
method setUp (line 26) | @BeforeEach
method 주문옵션답변_옵션답변정보_변환_검증 (line 34) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/cart/domain/CartTest.java
class CartTest (line 17) | @ExtendWith(MockitoExtension.class)
method setUp (line 28) | @BeforeEach
method 카트_결제필요여부_로직검증 (line 36) | @Test
method 카트_총금액_조회_로직검증 (line 51) | @Test
method 카트_아이템총_수량조회_로직검증 (line 66) | @Test
method 카트_아이템아이디_중복제거_조회_로직검증 (line 79) | @Test
method 카트_정적팩터리를_이용한생성시에_올바르게생성했는지검증 (line 92) | @Test
method 카트_장바구니이름_업데이트_검증 (line 105) | @Test
method 카트_아이템아이디_조회_검증 (line 115) | @Test
method 카트_카트라인_한개조회시_없으면_에러발생 (line 126) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/cart/domain/CartValidatorTest.java
class CartValidatorTest (line 31) | @ExtendWith(MockitoExtension.class)
method setUp (line 51) | @BeforeEach
method 카트_티켓팅_가능시간검증_실패 (line 58) | @Test
method 카트_티켓팅_가능시간검증_성공 (line 70) | @Test
method 카트_티켓팅_재고검증_실패 (line 80) | @Test
method 카트_티켓팅_재고검증_성공 (line 91) | @Test
method 카트_티켓팅_이벤트_상태검증_성공 (line 100) | @Test
method 카트_티켓팅_이벤트_상태검증_실패 (line 109) | @Test
method 카트_설문지_전부응답검증_성공 (line 118) | @Test
method 카트_설문지_전부응답검증_적게대답_실패 (line 135) | @Test
method 카트_설문지_전부응답검증_많게대답_실패 (line 156) | @Test
method 카트_설문지_전부응답검증_이상하게대답_실패 (line 175) | @Test
method 카트_설문지_올바른응답검증_이상하게대답_실패 (line 195) | @Test
method 카트_설문지_올바른응답검증_올바르게대답_성공 (line 217) | @Test
method 카트_아이템_한종류가아니면_실패 (line 236) | @Test
method 카트_아이템_구매갯수제한_실패 (line 246) | @Test
method 카트_아이템_한종류면_성공 (line 257) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/coupon/domain/CouponCampaignTest.java
class CouponCampaignTest (line 15) | @ExtendWith(MockitoExtension.class)
method setUp (line 19) | @BeforeEach
method testValidatePercentageAmount (line 31) | @Test
method testDecreaseCouponStock (line 40) | @Test
method testValidateIssuePeriod (line 49) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/coupon/domain/CouponStockInfoTest.java
class CouponStockInfoTest (line 12) | @ExtendWith(MockitoExtension.class)
method setUp (line 18) | @BeforeEach
method 쿠폰_남은_재고_없음 (line 26) | @Test
method 쿠폰_남은_재고_있음 (line 36) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/coupon/domain/IssuedCouponTest.java
class IssuedCouponTest (line 15) | @ExtendWith(MockitoExtension.class)
method setUp (line 27) | @BeforeEach
method 사용가능한_쿠폰_상태_확인 (line 32) | @Test
method use_usageStatusFalse_setUsageStatusToTrue (line 40) | @Test
method 내쿠폰_확인 (line 49) | @Test
method validMine_validUserId_noExceptionThrown (line 58) | @Test
method testGetDiscountAmount_withAmountDiscountType (line 90) | @Test
method testGetDiscountAmount_withPercentageDiscountType (line 103) | @Test
method testCheckSupplyAmount_withLessThanDiscount (line 116) | @Test
method testCheckSupplyAmount_withLessThanMinimum (line 127) | @Test
method testCheckSupplyAmount_withValidSupply (line 138) | @Test
method recovery_usageStatusFalse (line 150) | @Test
method recovery_usageStatusTrue (line 156) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/event/EventTest.java
class EventTest (line 18) | @ExtendWith(MockitoExtension.class)
method setup (line 28) | @BeforeEach
method startAt_가져오기_테스트 (line 33) | @Test
method eventBasic_null_이면_endAt도_반드시_null (line 45) | @Test
method endAt_가져오기_테스트 (line 55) | @Test
method eventBasic_업데이트_테스트 (line 78) | @Test
method 이벤트_정산중으로_상태변경_테스트 (line 90) | @Test
method 이벤트_종료로_상태변경_테스트 (line 102) | @Test
method 이벤트_오픈으로_상태변경_테스트 (line 114) | @Test
method 오픈_시간_이전인_이벤트는_오픈할수_없음 (line 130) | @Test
method 이벤트_준비중으로_상태변경_테스트 (line 143) | @Test
method 이벤트_삭제_상태변경_테스트 (line 151) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/domain/HostProfileTest.java
class HostProfileTest (line 11) | @ExtendWith(MockitoExtension.class)
method setup (line 18) | @BeforeEach
method 호스트_프로필_업데이트_테스트 (line 23) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/domain/HostRoleTest.java
class HostRoleTest (line 11) | @ExtendWith(MockitoExtension.class)
method ENUM_값에_해당하지_않으면_NULL_반환해야한다 (line 14) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/domain/HostTest.java
class HostTest (line 18) | @ExtendWith(MockitoExtension.class)
method setup (line 29) | @BeforeEach
method 호스트에_유저_1명_추가기능_검증 (line 34) | @Test
method 호스트유저_추가후_유저ID로_가져오기_검증 (line 49) | @Test
method 호스트유저ID로_포함여부_검증 (line 64) | @Test
method 호스트유저로_포함여부_검증 (line 74) | @Test
method 호스트에_유저_여러명_추가기능_검증 (line 84) | @Test
method 호스트에_유저_초대기능_검증 (line 96) | @Test
method 이미있는_호스트유저는_초대불가능 (line 106) | @Test
method 호스트_프로필_변경_호출되었는지_검증 (line 118) | @Test
method 슬랙알림용_URL변경_검증 (line 134) | @Test
method 슬랙알림용_URL은_이미있는_값과_같으면_안된다 (line 144) | @Test
method 마스터호스트_역할변경_불가_검증 (line 154) | @Test
method 호스트_역할변경_검증 (line 164) | @Test
method 호스트유저_매니저인지_검증 (line 177) | @Test
method 초대_수락한_호스트유저인지_검증 (line 195) | @Test
method 이미_가입한_호스트_ID로_검증_테스트 (line 209) | @Test
method 이미_가입한_호스트_호스트유저로_검증_테스트 (line 221) | @Test
method 호스트유저_종속_검증_테스트 (line 234) | @Test
method 호스트유저_게스트권한_검증_테스트 (line 244) | @Test
method 호스트유저_매니저권한_검증_테스트 (line 259) | @Test
method 호스트유저_마스터_검증_테스트 (line 280) | @Test
method 호스트_파트너여부_검증_테스트 (line 295) | @Test
method 호스트_vo_변환_테스트 (line 307) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/domain/HostUserTest.java
class HostUserTest (line 16) | @ExtendWith(MockitoExtension.class)
method setup (line 22) | @BeforeEach
method 호스트유저_권한변경_테스트 (line 27) | @Test
method 호스트유저_초대수락으로_활성화_테스트 (line 37) | @Test
method 호스트유저_초대_중복수락은_불가능하다 (line 46) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/service/HostServiceConcurrencyFailureTest.java
class HostServiceConcurrencyFailureTest (line 25) | @DomainIntegrateSpringBootTest
method setup (line 39) | @BeforeEach
method 호스트유저_초대요청_동시성_실패 (line 47) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/service/HostServiceConcurrencyTest.java
class HostServiceConcurrencyTest (line 24) | @DomainIntegrateSpringBootTest
method setup (line 37) | @BeforeEach
method 호스트유저_초대요청_동시성테스트 (line 45) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/issuedTicket/adaptor/IssuedTicketAdaptorTest.java
class IssuedTicketAdaptorTest (line 7) | @ExtendWith(MockitoExtension.class)
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/issuedTicket/domain/IssuedTicketItemInfoVoTest.java
class IssuedTicketItemInfoVoTest (line 16) | @ExtendWith(MockitoExtension.class)
method setUp (line 31) | @BeforeEach
method 티켓_아이템_정보를_발급티켓_아이템_인포로_정상적으로_변환_테스트 (line 37) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/issuedTicket/domain/IssuedTicketOptionAnswerTest.java
class IssuedTicketOptionAnswerTest (line 16) | @ExtendWith(MockitoExtension.class)
method setUp (line 29) | @BeforeEach
method 주문옵션답변_발급티켓옵션답변_변환_테스트 (line 38) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/issuedTicket/domain/IssuedTicketTest.java
class IssuedTicketTest (line 19) | @ExtendWith(MockitoExtension.class)
method setUp (line 40) | @BeforeEach
method 발급티켓_옵션들_합_검증 (line 52) | @Test
method 발급티켓_취소_로직_검증 (line 69) | @Test
method 이미취소된_발급티켓_취소시_에러_검증 (line 78) | @Test
method 발급티켓_입장_로직_검증 (line 86) | @Test
method 취소티켓_입장요청시_에러_검증 (line 95) | @Test
method 이미입장된티켓_입장요청시_에러_검증 (line 103) | @Test
method 입장티켓_입장_취소_로직_검증 (line 112) | @Test
method 입장취소요청시_에러_검증 (line 123) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/issuedTicket/domain/IssuedTicketUserInfoVoTest.java
class IssuedTicketUserInfoVoTest (line 16) | @ExtendWith(MockitoExtension.class)
method setUp (line 27) | @BeforeEach
method 유저_정보를_발급티켓_유저_인포로_변환_테스트 (line 35) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/issuedTicket/domain/validator/IssuedTicketValidatorTest.java
class IssuedTicketValidatorTest (line 15) | @ExtendWith(MockitoExtension.class)
method setUp (line 22) | @BeforeEach
method 발급티켓_이벤트_검증_성공 (line 27) | @Test
method 발급티켓_이벤트_검증_실패 (line 35) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/issuedTicket/service/IssuedTicketDomainServiceTest.java
class IssuedTicketDomainServiceTest (line 33) | @DomainIntegrateSpringBootTest
method setUp (line 85) | @BeforeEach
method 티켓_취소_로직_정상_작동_테스트 (line 118) | @Test
method 주문중_티켓_취소_로직_정상_작동_테스트 (line 149) | @Test
method 발급_티켓_입장_처리_로직_정상_작동_테스트 (line 181) | @Test
method 티켓_발급_로직_정상_작동_테스트 (line 195) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/issuedTicket/service/handlers/OrderEventHandlerTest.java
class OrderEventHandlerTest (line 16) | @ExtendWith(MockitoExtension.class)
method 주문이_정상_처리되었으면_티켓발급_서비스를_실행하는지_테스트 (line 27) | @Test
method 주문이_여러번_정상_처리되었으면_그만큼_티켓발급_서비스를_실행하는지_테스트 (line 36) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/issuedTicket/service/handlers/WithdrawOrderEventHandlerTest.java
class WithdrawOrderEventHandlerTest (line 18) | @ExtendWith(MockitoExtension.class)
method setUp (line 35) | @BeforeEach
method 주문이_취소되었으면_티켓발급_취소_로직_실행_테스트 (line 41) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/OrderLineItemTest.java
class OrderLineItemTest (line 17) | @ExtendWith(MockitoExtension.class)
method setUp (line 34) | @BeforeEach
method 아이템_가격_조회_검증 (line 41) | @Test
method 옵션_총_가격_조회_검증 (line 52) | @Test
method 오더라인_총_가격_조회_검증 (line 66) | @Test
method 옵션에_가격이_붙으면_결제가_필요한_오더라인이다 (line 81) | @Test
method 아이템에_가격이_있으면_결제가_필요한_오더라인이다 (line 95) | @Test
method 가격이없는_오더라인이면_결제가_필요하지않다 (line 107) | @Test
method 주문라인_아이템아이디_조회_검증 (line 123) | @Test
method 주문라인_아이템그룹아이디_조회_검증 (line 134) | @Test
method 주문라인_아이템이름_조회_검증 (line 145) | @Test
method 주문라인_정적팩터리_메서드_검증 (line 156) | @Test
method 주문라인_옵션아이디조회_검증 (line 171) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/OrderOptionAnswerTest.java
class OrderOptionAnswerTest (line 17) | @ExtendWith(MockitoExtension.class)
method setUp (line 30) | @BeforeEach
method 주문옵션답변_정적팩터리_생성자_검증 (line 38) | @Test
method 주문옵션답변_옵션답변정보_변환_검증 (line 49) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/OrderTest.java
class OrderTest (line 14) | @ExtendWith(MockitoExtension.class)
method setUp (line 28) | @BeforeEach
method 할인쿠폰이없다면_총할인금액은_0원이다 (line 37) | @Test
method 총공급가액_계산_검증 (line 46) | @Test
method 쿠폰_없을때_총결제금액_계산_검증 (line 59) | @Test
method 쿠폰_있을때_총결제금액_계산_검증 (line 72) | @Test
method 쿠폰_으로_0원_결제가능 (line 87) | @Test
method 주문번호생성_검증 (line 103) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/validator/OrderValidatorTest.java
class OrderValidatorTest (line 48) | @ExtendWith(MockitoExtension.class)
method setUp (line 70) | @BeforeEach
method 주문방식_승인주문_검증_실패 (line 82) | @Test
method 주문방식_승인주문_검증_성공 (line 93) | @Test
method 주인_검증_실패 (line 102) | @Test
method 주인_검증_성공 (line 113) | @Test
method 주문확인_금액검증_성공 (line 123) | @Test
method 주문확인_금액검증_실패 (line 133) | @Test
method 주문방식_결제방식검증_성공 (line 146) | @Test
method 주문방식_결제방식검증_실패 (line 156) | @Test
method 주문환불_환불가능상태검증_실패 (line 167) | @Test
method 주문환불_환불가능상태검증_성공 (line 181) | @Test
method 주문_무료금액검증_성공 (line 191) | @Test
method 주문_무료금액검증_실패 (line 200) | @Test
method 주문취소_상태검증_실패 (line 209) | @Test
method 주문취소_상태검증_성공 (line 224) | @Test
method 주문환불_상태검증_실패 (line 235) | @Test
method 주문환불_상태검증_성공 (line 250) | @Test
method 주문확인_결제대기중검증_성공 (line 261) | @Test
method 주문확인_결제대기중검증_실패 (line 270) | @Test
method 주문확인_승인대기중검증_성공 (line 287) | @Test
method 주문확인_승인대기중검증_실패 (line 296) | @Test
method 주문과정중_상품옵션이_변하면_실패 (line 311) | @Test
method 주문과정중_상품옵션이_그대로면_성공 (line 332) | @Test
method 주문_티켓팅_가능시간검증_실패 (line 350) | @Test
method 주문_티켓팅_가능시간검증_성공 (line 362) | @Test
method 주문_티켓팅_재고검증_실패 (line 372) | @Test
method 주문_티켓팅_재고검증_성공 (line 383) | @Test
method 주문_티켓팅_이벤트_상태검증_성공 (line 392) | @Test
method 주문_티켓팅_이벤트_상태검증_실패 (line 401) | @Test
method 주문_아이템_한종류가아니면_실패 (line 410) | @Test
method 주문_아이템_한종류면_성공 (line 420) | @Test
method 주문_아이템_구매갯수제한_실패 (line 428) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceConcurrencyFailTest.java
class OrderApproveServiceConcurrencyFailTest (line 30) | @DomainIntegrateSpringBootTest
method setUp (line 44) | @BeforeEach
method 동시성_실패_주문승인 (line 57) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceConcurrencyTest.java
class OrderApproveServiceConcurrencyTest (line 30) | @DomainIntegrateSpringBootTest
method setUp (line 45) | @BeforeEach
method 동시성_주문승인 (line 58) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceTest.java
class OrderApproveServiceTest (line 19) | @ExtendWith(MockitoExtension.class)
method 주문승인_승인로직_한번만_호출 (line 28) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/WithdrawOrderServiceTest.java
class WithdrawOrderServiceTest (line 27) | @DomainIntegrateSpringBootTest
method setUp (line 41) | @BeforeEach
method 동시성_주문철회_취소케이스 (line 51) | @Test
method 동시성_주문철회_환불케이스 (line 62) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/handler/WithDrawOrderHandlerTest.java
class WithDrawOrderHandlerTest (line 19) | @ExtendWith(MockitoExtension.class)
method 결제된_주문이_아니면_토스로_철회요청을_보내지_않는다 (line 30) | @Test
method 결제된_주문이면_토스로_철회요청을_보낸다 (line 43) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domains/user/domain/OauthInfoTest.java
class OauthInfoTest (line 10) | class OauthInfoTest {
method 탈퇴시에_OauthInfo_oid가_탈퇴상태가되어야한다 (line 12) | @Test
FILE: DuDoong-Domain/src/test/java/band/gosrock/domains/user/domain/UserTest.java
class UserTest (line 9) | class UserTest {
method 유저프로필변경테스트 (line 11) | @Test
FILE: DuDoong-Infrastructure/src/test/java/band/gosrock/infrastructure/InfraIntegrateProfileResolver.java
class InfraIntegrateProfileResolver (line 9) | public class InfraIntegrateProfileResolver implements ActiveProfilesReso...
method resolve (line 11) | @Override
FILE: DuDoong-Infrastructure/src/test/java/band/gosrock/infrastructure/InfraIntegrateTestConfig.java
class InfraIntegrateTestConfig (line 9) | @Configuration
FILE: DuDoong-Infrastructure/src/test/java/band/gosrock/infrastructure/outer/api/tossPayments/client/PaymentsCancelClientTest.java
class PaymentsCancelClientTest (line 25) | @ContextConfiguration
method 주문취소_실패시_멱등성_테스트 (line 39) | @Test
FILE: DuDoong-Infrastructure/src/test/java/band/gosrock/infrastructure/outer/api/tossPayments/client/TossPaymentsClientTest.java
class TossPaymentsClientTest (line 11) | @ActiveProfiles({"common"})
FILE: DuDoong-Infrastructure/src/test/java/band/gosrock/infrastructure/outer/api/tossPayments/client/TossSettlementClientTest.java
class TossSettlementClientTest (line 28) | @ContextConfiguration
method 정산요청_올바르게_파싱되어야한다 (line 43) | @Test
FILE: DuDoong-Infrastructure/src/test/java/band/gosrock/infrastructure/outer/api/tossPayments/config/PaymentConfirmErrorDecoderTest.java
class PaymentConfirmErrorDecoderTest (line 8) | class PaymentConfirmErrorDecoderTest {
method 코드가주어졌을때_ErrorCode_로변환할수있어야한다 (line 10) | @Test
FILE: e2e-tests/conftest.py
class TestState (line 13) | class TestState:
function state (line 28) | def state():
function base_url (line 34) | def base_url():
function auth_token (line 40) | def auth_token(base_url, state):
function auth_headers (line 64) | def auth_headers(auth_token):
function assert_status (line 69) | def assert_status(response, expected_status):
function get_data (line 77) | def get_data(response):
FILE: e2e-tests/test_01_auth.py
function test_local_login (line 11) | def test_local_login(base_url, state):
function test_token_refresh (line 38) | def test_token_refresh(base_url, state):
function test_unauthenticated_access (line 57) | def test_unauthenticated_access(base_url):
FILE: e2e-tests/test_02_host.py
function test_create_host (line 11) | def test_create_host(base_url, auth_headers, state):
function test_read_host_profiles (line 34) | def test_read_host_profiles(base_url, auth_headers, state):
FILE: e2e-tests/test_03_event.py
function _future_date_str (line 12) | def _future_date_str(days_ahead: int = 30) -> str:
function test_create_event (line 18) | def test_create_event(base_url, auth_headers, state):
function test_update_event_basic (line 42) | def test_update_event_basic(base_url, auth_headers, state):
function test_update_event_detail (line 64) | def test_update_event_detail(base_url, auth_headers, state):
function test_search_events (line 81) | def test_search_events(base_url):
FILE: e2e-tests/test_04_ticket_item.py
function test_create_free_ticket_item (line 11) | def test_create_free_ticket_item(base_url, auth_headers, state):
function test_get_event_ticket_items (line 42) | def test_get_event_ticket_items(base_url, state):
function test_open_event (line 67) | def test_open_event(base_url, auth_headers, state):
function test_read_event (line 83) | def test_read_event(base_url, state):
FILE: e2e-tests/test_05_order_flow.py
function test_create_cart (line 11) | def test_create_cart(base_url, auth_headers, state):
function test_read_cart (line 41) | def test_read_cart(base_url, auth_headers, state):
function test_create_order (line 58) | def test_create_order(base_url, auth_headers, state):
function test_free_order (line 83) | def test_free_order(base_url, auth_headers, state):
function test_read_order (line 100) | def test_read_order(base_url, auth_headers, state):
FILE: e2e-tests/test_06_issued_ticket.py
function test_read_order_tickets (line 11) | def test_read_order_tickets(base_url, auth_headers, state):
function test_read_my_orders (line 28) | def test_read_my_orders(base_url, auth_headers, state):
FILE: e2e-tests/test_07_comment.py
function test_create_comment (line 11) | def test_create_comment(base_url, auth_headers, state):
function test_read_comments (line 36) | def test_read_comments(base_url, state):
function test_get_comment_counts (line 53) | def test_get_comment_counts(base_url, state):
FILE: e2e-tests/test_08_refund.py
function _create_new_order_for_refund (line 11) | def _create_new_order_for_refund(base_url, auth_headers, state) -> str:
function test_refund_order (line 57) | def test_refund_order(base_url, auth_headers, state):
FILE: e2e-tests/test_09_error_cases.py
function test_create_host_empty_name (line 11) | def test_create_host_empty_name(base_url, auth_headers):
function test_create_event_missing_fields (line 30) | def test_create_event_missing_fields(base_url, auth_headers):
function test_create_ticket_invalid_price (line 48) | def test_create_ticket_invalid_price(base_url, auth_headers, state):
function test_read_nonexistent_event (line 74) | def test_read_nonexistent_event(base_url):
function test_expired_token (line 87) | def test_expired_token(base_url):
function test_open_event_without_ticket (line 101) | def test_open_event_without_ticket(base_url, auth_headers, state):
FILE: e2e-tests/test_10_event_lifecycle.py
function _future_date_str (line 12) | def _future_date_str(days_ahead: int = 30) -> str:
function test_event_status_preparing (line 22) | def test_event_status_preparing(base_url, auth_headers, state):
function test_event_status_open (line 63) | def test_event_status_open(base_url, auth_headers):
function test_event_status_calculating (line 137) | def test_event_status_calculating(base_url, auth_headers):
function test_event_status_closed (line 161) | def test_event_status_closed(base_url, auth_headers):
FILE: e2e-tests/test_11_multi_user.py
function _login_user2 (line 21) | def _login_user2(base_url: str) -> str:
function test_create_second_user (line 39) | def test_create_second_user(base_url):
function test_user2_orders_user1_event (line 49) | def test_user2_orders_user1_event(base_url, state):
function test_user2_cannot_modify_user1_event (line 106) | def test_user2_cannot_modify_user1_event(base_url, state):
FILE: e2e-tests/test_12_host_management.py
function test_update_host_profile (line 11) | def test_update_host_profile(base_url, auth_headers, state):
function test_read_host_detail (line 33) | def test_read_host_detail(base_url, auth_headers, state):
FILE: e2e-tests/test_13_user_profile.py
function test_read_my_profile (line 11) | def test_read_my_profile(base_url, auth_headers):
function test_toggle_marketing (line 27) | def test_toggle_marketing(base_url, auth_headers):
FILE: e2e-tests/test_14_ticket_stock.py
function test_create_limited_ticket (line 17) | def test_create_limited_ticket(base_url, auth_headers, state):
function test_order_within_limit (line 47) | def test_order_within_limit(base_url, auth_headers):
function test_order_exceeds_limit (line 97) | def test_order_exceeds_limit(base_url, auth_headers):
FILE: e2e-tests/test_15_event_crud.py
function _future_date_str (line 12) | def _future_date_str(days_ahead: int = 30) -> str:
function test_create_event_for_crud (line 24) | def test_create_event_for_crud(base_url, auth_headers, state):
function test_update_event_basic_info (line 50) | def test_update_event_basic_info(base_url, auth_headers):
function test_update_event_place (line 82) | def test_update_event_place(base_url, auth_headers):
function test_delete_event (line 107) | def test_delete_event(base_url, auth_headers):
FILE: e2e-tests/test_16_event_open_conditions.py
function _future_date_str (line 12) | def _future_date_str(days_ahead: int = 30) -> str:
function _create_fresh_event (line 18) | def _create_fresh_event(base_url: str, auth_headers: dict, host_id: int,...
function test_open_without_basic (line 38) | def test_open_without_basic(base_url, auth_headers, state):
function test_open_without_detail (line 58) | def test_open_without_detail(base_url, auth_headers, state):
function test_open_without_ticket (line 94) | def test_open_without_ticket(base_url, auth_headers, state):
function test_open_with_all_conditions (line 141) | def test_open_with_all_conditions(base_url, auth_headers, state):
FILE: e2e-tests/test_17_event_modification_rules.py
function _future_date_str (line 12) | def _future_date_str(days_ahead: int = 30) -> str:
function _create_fresh_event (line 18) | def _create_fresh_event(base_url: str, auth_headers: dict, host_id: int,...
function _setup_open_event (line 34) | def _setup_open_event(base_url: str, auth_headers: dict, host_id: int, n...
function test_modify_preparing_event_basic (line 89) | def test_modify_preparing_event_basic(base_url, auth_headers, state):
function test_modify_preparing_event_detail (line 116) | def test_modify_preparing_event_detail(base_url, auth_headers, state):
function test_open_event_then_modify_basic (line 138) | def test_open_event_then_modify_basic(base_url, auth_headers, state):
function test_open_event_then_modify_detail (line 172) | def test_open_event_then_modify_detail(base_url, auth_headers, state):
function test_delete_preparing_event (line 205) | def test_delete_preparing_event(base_url, auth_headers, state):
function test_delete_open_event (line 224) | def test_delete_open_event(base_url, auth_headers, state):
FILE: e2e-tests/test_18_event_status_matrix.py
function _future_date_str (line 19) | def _future_date_str(days_ahead: int = 30) -> str:
function _create_fresh_event (line 24) | def _create_fresh_event(base_url, auth_headers, host_id, name):
function _create_open_event (line 32) | def _create_open_event(base_url, auth_headers, host_id, name="상태매트릭스테스트"):
function test_valid_transition_open_to_calculating (line 66) | def test_valid_transition_open_to_calculating(base_url, auth_headers, st...
function test_valid_transition_calculating_to_closed (line 84) | def test_valid_transition_calculating_to_closed(base_url, auth_headers, ...
function test_preparing_to_calculating (line 109) | def test_preparing_to_calculating(base_url, auth_headers, state):
function test_open_to_closed_directly (line 126) | def test_open_to_closed_directly(base_url, auth_headers, state):
FILE: e2e-tests/test_19_host_invite.py
function _login_user (line 21) | def _login_user(base_url: str, email: str, name: str, phone: str) -> dict:
function test_setup_users_for_invite (line 37) | def test_setup_users_for_invite(base_url):
function test_invite_user_to_host (line 50) | def test_invite_user_to_host(base_url, auth_headers, state):
function test_user2_joins_host (line 72) | def test_user2_joins_host(base_url, state):
function test_invite_and_reject (line 90) | def test_invite_and_reject(base_url, auth_headers, state):
function test_update_host_slack_url (line 119) | def test_update_host_slack_url(base_url, auth_headers, state):
function test_non_host_user_cannot_access_host_features (line 141) | def test_non_host_user_cannot_access_host_features(base_url, state):
function test_host_member_can_access_events (line 160) | def test_host_member_can_access_events(base_url, state):
FILE: e2e-tests/test_20_ticket_options.py
function test_create_option_group (line 20) | def test_create_option_group(base_url, auth_headers, state):
function test_get_event_options (line 48) | def test_get_event_options(base_url, auth_headers, state):
function test_setup_preparing_event_for_options (line 63) | def test_setup_preparing_event_for_options(base_url, auth_headers, state):
function test_apply_option_to_ticket (line 104) | def test_apply_option_to_ticket(base_url, auth_headers, state):
function test_get_ticket_item_options (line 122) | def test_get_ticket_item_options(base_url, auth_headers, state):
function test_unapply_option_from_ticket (line 136) | def test_unapply_option_from_ticket(base_url, auth_headers, state):
function test_create_ticket_for_delete_test (line 154) | def test_create_ticket_for_delete_test(base_url, auth_headers, state):
function test_delete_ticket_in_preparing (line 196) | def test_delete_ticket_in_preparing(base_url, auth_headers):
function test_delete_ticket_in_open_fails (line 212) | def test_delete_ticket_in_open_fails(base_url, auth_headers, state):
FILE: e2e-tests/test_21_dudoong_ticket.py
function _login_buyer (line 22) | def _login_buyer(base_url: str) -> dict:
function _create_order (line 37) | def _create_order(base_url: str, headers: dict, ticket_item_id: int) -> ...
function test_setup_dudoong_event (line 56) | def test_setup_dudoong_event(base_url, auth_headers, state):
function test_dudoong_order_creates_pending (line 129) | def test_dudoong_order_creates_pending(base_url):
function test_host_approves_order (line 157) | def test_host_approves_order(base_url, auth_headers):
function test_approved_order_has_issued_tickets (line 174) | def test_approved_order_has_issued_tickets(base_url):
function test_dudoong_order_refuse (line 189) | def test_dudoong_order_refuse(base_url, auth_headers):
function test_refused_order_status (line 209) | def test_refused_order_status(base_url):
FILE: e2e-tests/test_22_order_detail_verification.py
function test_cart_response_fields (line 18) | def test_cart_response_fields(base_url, auth_headers, state):
function test_order_creation_response_fields (line 53) | def test_order_creation_response_fields(base_url, auth_headers):
function test_free_payment_response (line 87) | def test_free_payment_response(base_url, auth_headers):
function test_issued_tickets_after_payment (line 105) | def test_issued_tickets_after_payment(base_url, auth_headers):
function test_stock_decremented_after_payment (line 129) | def test_stock_decremented_after_payment(base_url, state):
FILE: e2e-tests/test_23_refund_edge_cases.py
function _create_and_complete_order (line 20) | def _create_and_complete_order(base_url: str, headers: dict, ticket_item...
function test_refund_and_verify_stock_restore (line 43) | def test_refund_and_verify_stock_restore(base_url, auth_headers, state):
function test_refund_issued_ticket_cancelled (line 134) | def test_refund_issued_ticket_cancelled(base_url, auth_headers):
function test_double_refund_fails (line 161) | def test_double_refund_fails(base_url, auth_headers):
function test_refunded_order_status (line 178) | def test_refunded_order_status(base_url, auth_headers):
FILE: e2e-tests/test_24_admin_features.py
function test_admin_order_table (line 17) | def test_admin_order_table(base_url, auth_headers, state):
function test_admin_issued_ticket_table (line 34) | def test_admin_issued_ticket_table(base_url, auth_headers, state):
function test_entry_check_in (line 58) | def test_entry_check_in(base_url, auth_headers, state):
function test_double_entry_fails (line 75) | def test_double_entry_fails(base_url, auth_headers, state):
function test_admin_cancel_order (line 94) | def test_admin_cancel_order(base_url, auth_headers, state):
FILE: e2e-tests/test_25_coupon_flow.py
function test_create_coupon_campaign (line 21) | def test_create_coupon_campaign(base_url, auth_headers):
function test_issue_coupon (line 55) | def test_issue_coupon(base_url, auth_headers):
function test_get_my_coupons (line 77) | def test_get_my_coupons(base_url, auth_headers):
function test_order_with_coupon (line 90) | def test_order_with_coupon(base_url, auth_headers, state):
FILE: e2e-tests/test_26_order_edge_cases.py
function _login (line 21) | def _login(base_url: str, email: str, name: str) -> dict:
function test_setup_stock_exhaustion (line 37) | def test_setup_stock_exhaustion(base_url, auth_headers, state):
function test_order_after_stock_exhausted (line 125) | def test_order_after_stock_exhausted(base_url):
function test_other_user_cannot_view_order (line 179) | def test_other_user_cannot_view_order(base_url):
function test_duplicate_free_payment_fails (line 196) | def test_duplicate_free_payment_fails(base_url, auth_headers, state):
function test_unauthenticated_order_attempt (line 212) | def test_unauthenticated_order_attempt(base_url, state):
FILE: e2e-tests/test_27_host_role_change.py
function _login (line 21) | def _login(base_url: str, email: str, name: str) -> dict:
function test_setup_host_for_role_change (line 36) | def test_setup_host_for_role_change(base_url, auth_headers, state):
function test_change_role_manager_to_guest (line 65) | def test_change_role_manager_to_guest(base_url, auth_headers):
function test_demoted_user_restricted_access (line 95) | def test_demoted_user_restricted_access(base_url):
function test_multiple_role_users_host_detail (line 121) | def test_multiple_role_users_host_detail(base_url, auth_headers):
FILE: e2e-tests/test_28_ticket_quantity_public.py
function test_setup_event_for_quantity_test (line 18) | def test_setup_event_for_quantity_test(base_url, auth_headers, state):
function test_public_ticket_shows_quantity (line 80) | def test_public_ticket_shows_quantity(base_url, state):
function test_private_ticket_hides_quantity (line 117) | def test_private_ticket_hides_quantity(base_url):
function test_both_ticket_types_in_same_event (line 150) | def test_both_ticket_types_in_same_event(base_url):
FILE: e2e-tests/test_29_order_cancel.py
function _login (line 20) | def _login(base_url: str, email: str, name: str) -> dict:
function test_setup_cancel_scenario (line 35) | def test_setup_cancel_scenario(base_url, auth_headers, state):
function test_create_order_for_cancel (line 103) | def test_create_order_for_cancel(base_url):
function test_cancel_pending_order (line 128) | def test_cancel_pending_order(base_url, auth_headers):
function test_cancelled_order_status (line 147) | def test_cancelled_order_status(base_url):
function test_stock_restored_after_cancel (line 174) | def test_stock_restored_after_cancel(base_url):
FILE: e2e-tests/test_30_order_before_open.py
function _login (line 18) | def _login(base_url: str, email: str, name: str) -> dict:
function test_setup_preparing_event (line 33) | def test_setup_preparing_event(base_url, auth_headers, state):
function test_cart_before_open_fails (line 89) | def test_cart_before_open_fails(base_url):
function test_event_still_preparing (line 139) | def test_event_still_preparing(base_url):
FILE: e2e-tests/test_31_admin_auth_role.py
function admin_base_url (line 15) | def admin_base_url():
function normal_user_token (line 23) | def normal_user_token():
function normal_user_headers (line 47) | def normal_user_headers(normal_user_token):
function test_admin_dashboard_forbidden_for_user (line 52) | def test_admin_dashboard_forbidden_for_user(admin_base_url, normal_user_...
function test_admin_users_forbidden_for_user (line 65) | def test_admin_users_forbidden_for_user(admin_base_url, normal_user_head...
function test_admin_events_forbidden_for_user (line 78) | def test_admin_events_forbidden_for_user(admin_base_url, normal_user_hea...
function test_admin_unauthenticated_access (line 91) | def test_admin_unauthenticated_access(admin_base_url):
FILE: e2e-tests/test_32_admin_token_separation.py
function api_base_url (line 18) | def api_base_url():
function admin_base_url (line 24) | def admin_base_url(api_base_url):
function normal_user_token (line 30) | def normal_user_token(api_base_url):
class TestNormalUserTokenBlocked (line 49) | class TestNormalUserTokenBlocked:
method test_authorization_header_blocked (line 59) | def test_authorization_header_blocked(self, admin_base_url, normal_use...
method test_cookie_with_normal_token_blocked (line 73) | def test_cookie_with_normal_token_blocked(self, admin_base_url, normal...
class TestUnauthenticatedBlocked (line 87) | class TestUnauthenticatedBlocked:
method test_no_token_blocked (line 97) | def test_no_token_blocked(self, admin_base_url, endpoint):
class TestAdminMeEndpoint (line 110) | class TestAdminMeEndpoint:
method test_me_without_token_blocked (line 113) | def test_me_without_token_blocked(self, admin_base_url):
method test_me_with_normal_token_blocked (line 121) | def test_me_with_normal_token_blocked(self, admin_base_url, normal_use...
FILE: e2e-tests/test_33_host_role_authorization.py
function _future_date_str (line 17) | def _future_date_str(days_ahead=30):
function mysql_query (line 22) | def mysql_query(sql):
function mysql_exec (line 32) | def mysql_exec(sql):
function login_user (line 41) | def login_user(base_url, email, name):
function api_base (line 64) | def api_base():
function user_a (line 69) | def user_a(api_base):
function user_b (line 76) | def user_b(api_base):
function host_and_event (line 83) | def host_and_event(api_base, user_a):
class TestNonMemberBlocked (line 115) | class TestNonMemberBlocked:
method test_outsider_cannot_read_host_events (line 118) | def test_outsider_cannot_read_host_events(self, api_base, user_b, host...
method test_outsider_cannot_update_event (line 126) | def test_outsider_cannot_update_event(self, api_base, user_b, host_and...
class TestMasterAccess (line 139) | class TestMasterAccess:
method test_master_can_read_host_events (line 142) | def test_master_can_read_host_events(self, api_base, user_a, host_and_...
method test_master_can_read_event_checklist (line 148) | def test_master_can_read_event_checklist(self, api_base, user_a, host_...
class TestSuperAdminBypass (line 155) | class TestSuperAdminBypass:
method test_super_admin_can_access_any_host (line 158) | def test_super_admin_can_access_any_host(self, api_base, user_b, host_...
FILE: e2e-tests/test_34_admin_usecase_authorization.py
function mysql_query (line 16) | def mysql_query(sql):
function mysql_exec (line 26) | def mysql_exec(sql):
function api_base (line 36) | def api_base():
function admin_base (line 41) | def admin_base(api_base):
function test_user (line 46) | def test_user(api_base):
class TestUserBlocked (line 72) | class TestUserBlocked:
method test_user_cannot_access_dashboard (line 75) | def test_user_cannot_access_dashboard(self, admin_base, test_user):
class TestAdminAccess (line 81) | class TestAdminAccess:
method test_admin_can_read_dashboard (line 84) | def test_admin_can_read_dashboard(self, admin_base, test_user):
method test_admin_can_read_users (line 94) | def test_admin_can_read_users(self, admin_base, test_user):
method test_admin_cannot_change_user_role (line 104) | def test_admin_cannot_change_user_role(self, admin_base, test_user):
class TestSuperAdminFullAccess (line 119) | class TestSuperAdminFullAccess:
method test_super_admin_full_access (line 122) | def test_super_admin_full_access(self, admin_base, test_user):
FILE: e2e-tests/test_35_user_nickname.py
function test_change_my_name (line 12) | def test_change_my_name(base_url, auth_headers):
function test_change_name_blank_returns_400 (line 34) | def test_change_name_blank_returns_400(base_url, auth_headers):
function test_change_name_too_long_returns_400 (line 44) | def test_change_name_too_long_returns_400(base_url, auth_headers):
function test_change_name_too_short_returns_400 (line 54) | def test_change_name_too_short_returns_400(base_url, auth_headers):
FILE: e2e-tests/test_36_host_master_transfer.py
function transfer_state (line 12) | def transfer_state():
function _login (line 23) | def _login(base_url, email, name):
function test_setup_master_and_member (line 39) | def test_setup_master_and_member(base_url, transfer_state):
function test_create_host_for_transfer (line 62) | def test_create_host_for_transfer(base_url, transfer_state):
function test_invite_member (line 82) | def test_invite_member(base_url, transfer_state):
function test_member_join_host (line 96) | def test_member_join_host(base_url, transfer_state):
function test_transfer_master (line 108) | def test_transfer_master(base_url, transfer_state):
function test_verify_new_master (line 123) | def test_verify_new_master(base_url, transfer_state):
function test_old_master_cannot_transfer_again (line 148) | def test_old_master_cannot_transfer_again(base_url, transfer_state):
FILE: e2e-tests/test_37_order_cancel_reason.py
function _login (line 21) | def _login(base_url: str, email: str, name: str) -> dict:
function _create_order (line 36) | def _create_order(base_url: str, ticket_item_id: int, headers: dict) -> ...
function test_setup_refund_reason_scenario (line 55) | def test_setup_refund_reason_scenario(base_url, auth_headers, state):
function test_create_orders_for_refund (line 134) | def test_create_orders_for_refund(base_url):
function test_admin_complete_refund (line 143) | def test_admin_complete_refund():
function test_admin_cancel_with_reason (line 165) | def test_admin_cancel_with_reason(base_url):
FILE: e2e-tests/test_38_refund_api.py
function _create_and_refund_order (line 11) | def _create_and_refund_order(base_url, auth_headers, state) -> str:
class TestHostRefundApi (line 55) | class TestHostRefundApi:
method setup (line 59) | def setup(self, base_url, auth_headers, state):
method test_refund_list (line 64) | def test_refund_list(self):
method test_refund_list_with_status_filter (line 84) | def test_refund_list_with_status_filter(self):
method test_refund_detail (line 97) | def test_refund_detail(self):
method test_complete_refund (line 113) | def test_complete_refund(self):
class TestAdminRefundApi (line 133) | class TestAdminRefundApi:
method setup (line 137) | def setup(self, base_url, auth_headers, state):
method test_admin_refund_list (line 144) | def test_admin_refund_list(self):
method test_admin_refund_detail (line 159) | def test_admin_refund_detail(self):
method test_admin_complete_refund (line 176) | def test_admin_complete_refund(self):
Condensed preview — 1032 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,859K chars).
[
{
"path": ".claude/settings.local.json",
"chars": 80,
"preview": "{\n \"permissions\": {\n \"allow\": [\n \"mcp__mysql__mysql_query\"\n ]\n }\n}\n"
},
{
"path": ".github/CODEOWNERS",
"chars": 52,
"preview": "* @ImNM @sanbonai06 @cofls6581 @gengminy @kim-wonjin"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 70,
"preview": "## 개요\n- close #issueNumber\n\n## 작업사항\n- 내용을 적어주세요.\n\n## 변경로직\n- 내용을 적어주세요."
},
{
"path": ".github/workflows/BuildApiServer.yml",
"chars": 1444,
"preview": "name: Build Api Server\non:\n push:\n tags:\n - Api-v*.*.*\n\njobs:\n build:\n runs-on: ubuntu-latest\n strategy:"
},
{
"path": ".github/workflows/BuildBatchServer.yml",
"chars": 1511,
"preview": "name: Build Batch Server\non:\n push:\n tags:\n - Batch-v*.*.*\n\njobs:\n build:\n runs-on: ubuntu-latest\n strat"
},
{
"path": ".github/workflows/BuildSocketServer.yml",
"chars": 1461,
"preview": "name: Build Socket Server\non:\n push:\n tags:\n - Socket-v*.*.*\n\njobs:\n build:\n runs-on: ubuntu-latest\n str"
},
{
"path": ".github/workflows/ci.yml",
"chars": 752,
"preview": "name: ci\non:\n pull_request:\n branch: 'dev'\n\njobs:\n ktlintCheck:\n runs-on: ubuntu-latest\n\n steps:\n - uses"
},
{
"path": ".gitignore",
"chars": 509,
"preview": "DuDoong-Domain/HELP.md\n.gradle\nbuild/\n!gradle/wrapper/gradle-wrapper.jar\n!**/src/main/**/build/\n!**/src/test/**/build/\n\n"
},
{
"path": "CLAUDE.md",
"chars": 8025,
"preview": "# DuDoong Backend - Claude 컨텍스트\n\n## 🎯 현재 진행 중인 작업\n\n**Java → Kotlin 전체 마이그레이션**\n\n마스터 트래킹 이슈: https://github.com/Gosrock/D"
},
{
"path": "DuDoong-Admin/build.gradle.kts",
"chars": 578,
"preview": "tasks.bootJar { enabled = false }\ntasks.jar { enabled = true }\n\ndependencies {\n implementation(\"org.springframework.b"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminCommentController.kt",
"chars": 2034,
"preview": "package band.gosrock.admin.controller\n\nimport band.gosrock.admin.model.dto.response.AdminCommentResponse\nimport band.gos"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminDashboardController.kt",
"chars": 1369,
"preview": "package band.gosrock.admin.controller\n\nimport band.gosrock.admin.model.dto.response.DashboardResponse\nimport band.gosroc"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminEventController.kt",
"chars": 8344,
"preview": "package band.gosrock.admin.controller\n\nimport band.gosrock.admin.model.dto.request.AdminAdjustTicketStockRequest\nimport "
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminHostController.kt",
"chars": 6700,
"preview": "package band.gosrock.admin.controller\n\nimport band.gosrock.admin.model.dto.request.AdminAddHostMemberRequest\nimport band"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminOrderController.kt",
"chars": 4305,
"preview": "package band.gosrock.admin.controller\n\nimport band.gosrock.admin.model.dto.request.AdminCancelOrderRequest\nimport band.g"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminRefundController.kt",
"chars": 2485,
"preview": "package band.gosrock.admin.controller\n\nimport band.gosrock.admin.model.dto.response.AdminRefundResponse\nimport band.gosr"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/controller/AdminUserController.kt",
"chars": 4510,
"preview": "package band.gosrock.admin.controller\n\nimport band.gosrock.admin.model.dto.request.AdminChangeNameRequest\nimport band.go"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/exception/AdminErrorCode.kt",
"chars": 1041,
"preview": "package band.gosrock.admin.exception\n\nimport band.gosrock.common.annotation.ExplainError\nimport band.gosrock.common.cons"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/exception/AdminForbiddenException.kt",
"chars": 327,
"preview": "package band.gosrock.admin.exception\n\nimport band.gosrock.common.exception.DuDoongCodeException\n\nclass AdminForbiddenExc"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminAddHostMemberRequest.kt",
"chars": 189,
"preview": "package band.gosrock.admin.model.dto.request\n\nimport band.gosrock.domain.domains.host.domain.HostRole\n\ndata class AdminA"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminAdjustTicketStockRequest.kt",
"chars": 131,
"preview": "package band.gosrock.admin.model.dto.request\n\ndata class AdminAdjustTicketStockRequest(\n val delta: Long, // 양수 = 증가,"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminCancelOrderRequest.kt",
"chars": 116,
"preview": "package band.gosrock.admin.model.dto.request\n\ndata class AdminCancelOrderRequest(\n val reason: String? = null,\n)\n"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminChangeNameRequest.kt",
"chars": 304,
"preview": "package band.gosrock.admin.model.dto.request\n\nimport jakarta.validation.constraints.NotBlank\nimport jakarta.validation.c"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminRefundStatusRequest.kt",
"chars": 213,
"preview": "package band.gosrock.admin.model.dto.request\n\nimport jakarta.validation.constraints.NotNull\n\ndata class AdminRefundStatu"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminTransferMasterRequest.kt",
"chars": 250,
"preview": "package band.gosrock.admin.model.dto.request\n\nimport jakarta.validation.constraints.NotNull\n\n/** 어드민 호스트 마스터 강제 양도 요청 DT"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateEventRequest.kt",
"chars": 322,
"preview": "package band.gosrock.admin.model.dto.request\n\nimport java.time.LocalDateTime\n\ndata class AdminUpdateEventRequest(\n va"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateEventStatusRequest.kt",
"chars": 180,
"preview": "package band.gosrock.admin.model.dto.request\n\nimport band.gosrock.domain.domains.event.domain.EventStatus\n\ndata class Ad"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateHostMemberRoleRequest.kt",
"chars": 174,
"preview": "package band.gosrock.admin.model.dto.request\n\nimport band.gosrock.domain.domains.host.domain.HostRole\n\ndata class AdminU"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateHostPartnerRequest.kt",
"chars": 116,
"preview": "package band.gosrock.admin.model.dto.request\n\ndata class AdminUpdateHostPartnerRequest(\n val partner: Boolean,\n)\n"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateHostProfileRequest.kt",
"chars": 204,
"preview": "package band.gosrock.admin.model.dto.request\n\ndata class AdminUpdateHostProfileRequest(\n val name: String?,\n val i"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateTicketItemRequest.kt",
"chars": 289,
"preview": "package band.gosrock.admin.model.dto.request\n\nimport java.math.BigDecimal\n\ndata class AdminUpdateTicketItemRequest(\n "
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateUserRoleRequest.kt",
"chars": 174,
"preview": "package band.gosrock.admin.model.dto.request\n\nimport band.gosrock.domain.domains.user.domain.AccountRole\n\ndata class Adm"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/request/AdminUpdateUserStatusRequest.kt",
"chars": 180,
"preview": "package band.gosrock.admin.model.dto.request\n\nimport band.gosrock.domain.domains.user.domain.AccountState\n\ndata class Ad"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminCommentResponse.kt",
"chars": 964,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.comment.domain.Comment\nimport band.gos"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminEventResponse.kt",
"chars": 2572,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.event.domain.Event\nimport band.gosrock"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminHostDetailResponse.kt",
"chars": 1159,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.host.domain.Host\nimport java.time.Loca"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminHostMemberResponse.kt",
"chars": 735,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.host.domain.HostRole\nimport band.gosro"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminHostResponse.kt",
"chars": 995,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.host.domain.Host\nimport java.time.Loca"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminIssuedTicketResponse.kt",
"chars": 1102,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.issuedTicket.domain.IssuedTicket\nimpor"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminOrderResponse.kt",
"chars": 2274,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.order.domain.Order\nimport band.gosrock"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminRefundResponse.kt",
"chars": 1410,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.order.domain.Order\nimport band.gosrock"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminTicketItemResponse.kt",
"chars": 1772,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.ticket_item.domain.TicketItem\nimport b"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminUserDetailResponse.kt",
"chars": 1470,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.user.domain.AccountRole\nimport band.go"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/AdminUserResponse.kt",
"chars": 942,
"preview": "package band.gosrock.admin.model.dto.response\n\nimport band.gosrock.domain.domains.user.domain.AccountRole\nimport band.go"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/model/dto/response/DashboardResponse.kt",
"chars": 369,
"preview": "package band.gosrock.admin.model.dto.response\n\ndata class DashboardResponse(\n val totalUsers: Long,\n val todayNewU"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminAddHostMemberUseCase.kt",
"chars": 1386,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminAddHostMemberRequest\nimport band.go"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminAdjustTicketStockUseCase.kt",
"chars": 884,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminTicketItemResponse\nimport band.gos"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminAuthValidator.kt",
"chars": 1412,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.exception.AdminForbiddenException\nimport band.gosrock.doma"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminCancelOrderUseCase.kt",
"chars": 1367,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminOrderResponse\nimport band.gosrock."
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminChangeNameUseCase.kt",
"chars": 687,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminChangeNameRequest\nimport band.gosro"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminCompleteRefundUseCase.kt",
"chars": 1214,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminRefundResponse\nimport band.gosrock"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminDeleteCommentUseCase.kt",
"chars": 582,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.common.annotation.UseCase\nimport band.gosrock.domain.domains.com"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminDeleteEventUseCase.kt",
"chars": 845,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.common.annotation.UseCase\nimport band.gosrock.domain.domains.eve"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminExcelService.kt",
"chars": 7888,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminEventResponse\nimport band.gosrock."
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminExportIssuedTicketsUseCase.kt",
"chars": 1468,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.common.annotation.UseCase\nimport band.gosrock.domain.domains.iss"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetCommentsUseCase.kt",
"chars": 1341,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminCommentResponse\nimport band.gosroc"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetEventDetailUseCase.kt",
"chars": 1932,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminEventResponse\nimport band.gosrock."
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetEventsUseCase.kt",
"chars": 1640,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminEventResponse\nimport band.gosrock."
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetHostDetailUseCase.kt",
"chars": 686,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminHostDetailResponse\nimport band.gos"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetHostEventsUseCase.kt",
"chars": 1026,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminEventResponse\nimport band.gosrock."
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetHostMembersUseCase.kt",
"chars": 1089,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminHostMemberResponse\nimport band.gos"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetHostsUseCase.kt",
"chars": 803,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminHostResponse\nimport band.gosrock.c"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetIssuedTicketsUseCase.kt",
"chars": 1154,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminIssuedTicketResponse\nimport band.g"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetMeUseCase.kt",
"chars": 429,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminUserDetailResponse\nimport band.gos"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetOrderDetailUseCase.kt",
"chars": 1192,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminOrderResponse\nimport band.gosrock."
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetOrdersUseCase.kt",
"chars": 2568,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminOrderResponse\nimport band.gosrock."
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetRefundDetailUseCase.kt",
"chars": 1196,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminRefundResponse\nimport band.gosrock"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetRefundsUseCase.kt",
"chars": 1752,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminRefundResponse\nimport band.gosrock"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetTicketItemsUseCase.kt",
"chars": 734,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminTicketItemResponse\nimport band.gos"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetUserDetailUseCase.kt",
"chars": 699,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminUserDetailResponse\nimport band.gos"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminGetUsersUseCase.kt",
"chars": 1070,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminUserResponse\nimport band.gosrock.c"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminRemoveHostMemberUseCase.kt",
"chars": 842,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.common.annotation.UseCase\nimport band.gosrock.domain.domains.hos"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminTransferMasterUseCase.kt",
"chars": 998,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminTransferMasterRequest\nimport band.g"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateEventStatusUseCase.kt",
"chars": 1085,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminUpdateEventStatusRequest\nimport ban"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateEventUseCase.kt",
"chars": 1498,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminUpdateEventRequest\nimport band.gosr"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateHostMemberRoleUseCase.kt",
"chars": 1335,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminUpdateHostMemberRoleRequest\nimport "
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateHostPartnerUseCase.kt",
"chars": 983,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminUpdateHostPartnerRequest\nimport ban"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateHostProfileUseCase.kt",
"chars": 1457,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminUpdateHostProfileRequest\nimport ban"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateRefundStatusUseCase.kt",
"chars": 1584,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminRefundStatusRequest\nimport band.gos"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateTicketItemUseCase.kt",
"chars": 1327,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminUpdateTicketItemRequest\nimport band"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateUserRoleUseCase.kt",
"chars": 1000,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminUpdateUserRoleRequest\nimport band.g"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/AdminUpdateUserStatusUseCase.kt",
"chars": 1003,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminUpdateUserStatusRequest\nimport band"
},
{
"path": "DuDoong-Admin/src/main/kotlin/band/gosrock/admin/service/GetDashboardUseCase.kt",
"chars": 4384,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminEventResponse\nimport band.gosrock."
},
{
"path": "DuDoong-Admin/src/test/kotlin/band/gosrock/admin/service/AdminAuthValidatorTest.kt",
"chars": 4535,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.common.exception.DuDoongCodeException\nimport band.gosrock.domain"
},
{
"path": "DuDoong-Admin/src/test/kotlin/band/gosrock/admin/service/AdminChangeNameUseCaseTest.kt",
"chars": 3271,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminChangeNameRequest\nimport band.gosro"
},
{
"path": "DuDoong-Admin/src/test/kotlin/band/gosrock/admin/service/AdminCompleteRefundUseCaseTest.kt",
"chars": 2223,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.domain.domains.event.adaptor.EventAdaptor\nimport band.gosrock.do"
},
{
"path": "DuDoong-Admin/src/test/kotlin/band/gosrock/admin/service/AdminExcelServiceTest.kt",
"chars": 11447,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.response.AdminIssuedTicketResponse\nimport band.g"
},
{
"path": "DuDoong-Admin/src/test/kotlin/band/gosrock/admin/service/AdminUpdateRefundStatusUseCaseTest.kt",
"chars": 3515,
"preview": "package band.gosrock.admin.service\n\nimport band.gosrock.admin.model.dto.request.AdminRefundStatusRequest\nimport band.gos"
},
{
"path": "DuDoong-Api/Dockerfile",
"chars": 240,
"preview": "FROM eclipse-temurin:21-jre-alpine\n\nEXPOSE 8080\n\nCOPY ./build/libs/*.jar app.jar\nARG PROFILE=prod\nENV PROFILE=${PROFILE}"
},
{
"path": "DuDoong-Api/build.gradle.kts",
"chars": 515,
"preview": "dependencies {\n implementation(\"org.springframework.boot:spring-boot-starter-web\")\n implementation(\"org.springfram"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/DuDoongApiServerApplication.kt",
"chars": 1059,
"preview": "package band.gosrock\n\nimport org.slf4j.LoggerFactory\nimport org.springframework.boot.SpringApplication\nimport org.spring"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/admin/controller/AdminAuthController.kt",
"chars": 973,
"preview": "package band.gosrock.api.admin.controller\n\nimport band.gosrock.admin.model.dto.response.AdminUserDetailResponse\nimport b"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/dto/OrderAlimTalkDto.kt",
"chars": 404,
"preview": "package band.gosrock.api.alimTalk.dto\n\nimport band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkEventInfo\nimport "
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/handler/DoneOrderEventAlimTalkHandler.kt",
"chars": 2234,
"preview": "package band.gosrock.api.alimTalk.handler\n\nimport band.gosrock.api.alimTalk.service.SendDoneOrderAlimTalkService\nimport "
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/handler/RegisterUserEventAlimTalkHandler.kt",
"chars": 1374,
"preview": "package band.gosrock.api.alimTalk.handler\n\nimport band.gosrock.api.alimTalk.service.SendRegisterAlimTalkService\nimport b"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/handler/WithDrawOrderEventAlimTalkHandler.kt",
"chars": 2104,
"preview": "package band.gosrock.api.alimTalk.handler\n\nimport band.gosrock.api.alimTalk.service.SendWithdrawOrderAlimTalkService\nimp"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/service/SendDoneOrderAlimTalkService.kt",
"chars": 1004,
"preview": "package band.gosrock.api.alimTalk.service\n\nimport band.gosrock.api.alimTalk.dto.OrderAlimTalkDto\nimport band.gosrock.dom"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/service/SendRegisterAlimTalkService.kt",
"chars": 511,
"preview": "package band.gosrock.api.alimTalk.service\n\nimport band.gosrock.domain.common.alarm.UserKakaoTalkAlarm\nimport band.gosroc"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/service/SendWithdrawOrderAlimTalkService.kt",
"chars": 1010,
"preview": "package band.gosrock.api.alimTalk.service\n\nimport band.gosrock.api.alimTalk.dto.OrderAlimTalkDto\nimport band.gosrock.dom"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/alimTalk/service/helper/OrderAlimTalkInfoHelper.kt",
"chars": 1114,
"preview": "package band.gosrock.api.alimTalk.service.helper\n\nimport band.gosrock.api.alimTalk.dto.OrderAlimTalkDto\nimport band.gosr"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/controller/AuthController.kt",
"chars": 8698,
"preview": "package band.gosrock.api.auth.controller\n\nimport band.gosrock.api.auth.model.dto.request.RegisterRequest\nimport band.gos"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/KakaoUserInfoDto.kt",
"chars": 704,
"preview": "package band.gosrock.api.auth.model.dto\n\nimport band.gosrock.domain.domains.user.domain.OauthInfo\nimport band.gosrock.do"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/request/RegisterRequest.kt",
"chars": 576,
"preview": "package band.gosrock.api.auth.model.dto.request\n\nimport band.gosrock.domain.domains.user.domain.Profile\nimport jakarta.v"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/response/AvailableRegisterResponse.kt",
"chars": 253,
"preview": "package band.gosrock.api.auth.model.dto.response\n\nimport io.swagger.v3.oas.annotations.media.Schema\n\ndata class Availabl"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/response/OauthLoginLinkResponse.kt",
"chars": 108,
"preview": "package band.gosrock.api.auth.model.dto.response\n\ndata class OauthLoginLinkResponse(\n val link: String\n)\n"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/response/OauthTokenResponse.kt",
"chars": 581,
"preview": "package band.gosrock.api.auth.model.dto.response\n\nimport band.gosrock.infrastructure.outer.api.oauth.dto.KakaoTokenRespo"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/response/OauthUserInfoResponse.kt",
"chars": 625,
"preview": "package band.gosrock.api.auth.model.dto.response\n\nimport band.gosrock.api.auth.model.dto.KakaoUserInfoDto\n\ndata class Oa"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/model/dto/response/TokenAndUserResponse.kt",
"chars": 418,
"preview": "package band.gosrock.api.auth.model.dto.response\n\nimport band.gosrock.domain.common.dto.ProfileViewDto\nimport io.swagger"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/LocalDevLoginUseCase.kt",
"chars": 1013,
"preview": "package band.gosrock.api.auth.service\n\nimport band.gosrock.api.auth.model.dto.request.RegisterRequest\nimport band.gosroc"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/LoginUseCase.kt",
"chars": 1011,
"preview": "package band.gosrock.api.auth.service\n\nimport band.gosrock.api.auth.model.dto.response.TokenAndUserResponse\nimport band."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/LogoutUseCase.kt",
"chars": 337,
"preview": "package band.gosrock.api.auth.service\n\nimport band.gosrock.common.annotation.UseCase\nimport band.gosrock.domain.domains."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/OauthUserInfoUseCase.kt",
"chars": 507,
"preview": "package band.gosrock.api.auth.service\n\nimport band.gosrock.api.auth.model.dto.response.OauthUserInfoResponse\nimport band"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/RefreshUseCase.kt",
"chars": 1400,
"preview": "package band.gosrock.api.auth.service\n\nimport band.gosrock.api.auth.model.dto.response.TokenAndUserResponse\nimport band."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/RegisterUseCase.kt",
"chars": 2779,
"preview": "package band.gosrock.api.auth.service\n\nimport band.gosrock.api.auth.model.dto.request.RegisterRequest\nimport band.gosroc"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/WithDrawUseCase.kt",
"chars": 1108,
"preview": "package band.gosrock.api.auth.service\n\nimport band.gosrock.api.auth.service.helper.KakaoOauthHelper\nimport band.gosrock."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/helper/CookieHelper.kt",
"chars": 2286,
"preview": "package band.gosrock.api.auth.service.helper\n\nimport band.gosrock.api.auth.model.dto.response.TokenAndUserResponse\nimpor"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/helper/KakaoOauthHelper.kt",
"chars": 3431,
"preview": "package band.gosrock.api.auth.service.helper\n\nimport band.gosrock.api.auth.model.dto.KakaoUserInfoDto\nimport band.gosroc"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/helper/OauthOIDCHelper.kt",
"chars": 949,
"preview": "package band.gosrock.api.auth.service.helper\n\nimport band.gosrock.common.annotation.Helper\nimport band.gosrock.common.dt"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/auth/service/helper/TokenGenerateHelper.kt",
"chars": 1893,
"preview": "package band.gosrock.api.auth.service.helper\n\nimport band.gosrock.api.auth.model.dto.response.TokenAndUserResponse\nimpor"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/controller/CartController.kt",
"chars": 1771,
"preview": "package band.gosrock.api.cart.controller\n\nimport band.gosrock.api.cart.docs.CreateCartExceptionDocs\nimport band.gosrock."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/docs/CreateCartExceptionDocs.kt",
"chars": 1828,
"preview": "package band.gosrock.api.cart.docs\n\nimport band.gosrock.common.annotation.ExceptionDoc\nimport band.gosrock.common.annota"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/model/dto/request/AddCartLineDto.kt",
"chars": 445,
"preview": "package band.gosrock.api.cart.model.dto.request\n\nimport io.swagger.v3.oas.annotations.media.Schema\nimport jakarta.valida"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/model/dto/request/AddCartOptionAnswerDto.kt",
"chars": 440,
"preview": "package band.gosrock.api.cart.model.dto.request\n\nimport io.swagger.v3.oas.annotations.media.Schema\nimport jakarta.valida"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/model/dto/request/AddCartRequest.kt",
"chars": 333,
"preview": "package band.gosrock.api.cart.model.dto.request\n\nimport io.swagger.v3.oas.annotations.media.Schema\nimport jakarta.valida"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/model/dto/response/CartItemResponse.kt",
"chars": 1393,
"preview": "package band.gosrock.api.cart.model.dto.response\n\nimport band.gosrock.domain.common.vo.Money\nimport band.gosrock.domain."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/model/dto/response/CartResponse.kt",
"chars": 2219,
"preview": "package band.gosrock.api.cart.model.dto.response\n\nimport band.gosrock.domain.common.vo.AccountInfoVo\nimport band.gosrock"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/model/mapper/CartMapper.kt",
"chars": 4915,
"preview": "package band.gosrock.api.cart.model.mapper\n\nimport band.gosrock.api.cart.model.dto.request.AddCartLineDto\nimport band.go"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/service/CheckOptionUseCase.kt",
"chars": 120,
"preview": "package band.gosrock.api.cart.service\n\nimport band.gosrock.common.annotation.UseCase\n\n@UseCase\nclass CheckOptionUseCase\n"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/service/CreateCartUseCase.kt",
"chars": 805,
"preview": "package band.gosrock.api.cart.service\n\nimport band.gosrock.api.cart.model.dto.request.AddCartRequest\nimport band.gosrock"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/cart/service/ReadCartUseCase.kt",
"chars": 729,
"preview": "package band.gosrock.api.cart.service\n\nimport band.gosrock.api.cart.model.dto.response.CartResponse\nimport band.gosrock."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/controller/CommentController.kt",
"chars": 3884,
"preview": "package band.gosrock.api.comment.controller\n\nimport band.gosrock.api.comment.model.request.CreateCommentRequest\nimport b"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/mapper/CommentMapper.kt",
"chars": 2229,
"preview": "package band.gosrock.api.comment.mapper\n\nimport band.gosrock.api.comment.model.request.CreateCommentRequest\nimport band."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/model/request/CreateCommentRequest.kt",
"chars": 395,
"preview": "package band.gosrock.api.comment.model.request\n\nimport jakarta.validation.constraints.NotBlank\nimport jakarta.validation"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/model/response/CreateCommentResponse.kt",
"chars": 865,
"preview": "package band.gosrock.api.comment.model.response\n\nimport band.gosrock.common.annotation.DateFormat\nimport band.gosrock.do"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/model/response/RetrieveCommentCountResponse.kt",
"chars": 315,
"preview": "package band.gosrock.api.comment.model.response\n\ndata class RetrieveCommentCountResponse(\n val commentCounts: Long?,\n"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/model/response/RetrieveCommentDTO.kt",
"chars": 547,
"preview": "package band.gosrock.api.comment.model.response\n\nimport band.gosrock.domain.common.vo.CommentInfoVo\nimport band.gosrock."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/model/response/RetrieveCommentListResponse.kt",
"chars": 608,
"preview": "package band.gosrock.api.comment.model.response\n\nimport band.gosrock.domain.domains.comment.domain.Comment\nimport org.sp"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/model/response/RetrieveRandomCommentResponse.kt",
"chars": 499,
"preview": "package band.gosrock.api.comment.model.response\n\nimport band.gosrock.domain.common.vo.CommentInfoVo\nimport band.gosrock."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/service/CreateCommentUseCase.kt",
"chars": 1160,
"preview": "package band.gosrock.api.comment.service\n\nimport band.gosrock.api.comment.mapper.CommentMapper\nimport band.gosrock.api.c"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/service/DeleteCommentUseCase.kt",
"chars": 1006,
"preview": "package band.gosrock.api.comment.service\n\nimport band.gosrock.api.comment.mapper.CommentMapper\nimport band.gosrock.api.c"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/service/RetrieveCommentCountUseCase.kt",
"chars": 900,
"preview": "package band.gosrock.api.comment.service\n\nimport band.gosrock.api.comment.mapper.CommentMapper\nimport band.gosrock.api.c"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/service/RetrieveCommentUseCase.kt",
"chars": 892,
"preview": "package band.gosrock.api.comment.service\n\nimport band.gosrock.api.comment.mapper.CommentMapper\nimport band.gosrock.api.c"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/comment/service/RetrieveRandomCommentUseCase.kt",
"chars": 917,
"preview": "package band.gosrock.api.comment.service\n\nimport band.gosrock.api.comment.mapper.CommentMapper\nimport band.gosrock.api.c"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/UserUtils.kt",
"chars": 468,
"preview": "package band.gosrock.api.common\n\nimport band.gosrock.api.config.security.SecurityUtils\nimport band.gosrock.domain.domain"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostPartner/HostPartnerAllowed.kt",
"chars": 382,
"preview": "package band.gosrock.api.common.aop.hostPartner\n\nimport band.gosrock.api.common.aop.hostRole.FindHostFrom\nimport java.la"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostPartner/HostPartnerAop.kt",
"chars": 1958,
"preview": "package band.gosrock.api.common.aop.hostPartner\n\nimport org.aspectj.lang.ProceedingJoinPoint\nimport org.aspectj.lang.ann"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostPartner/HostPartnerCallTransaction.kt",
"chars": 238,
"preview": "package band.gosrock.api.common.aop.hostPartner\n\nimport org.aspectj.lang.ProceedingJoinPoint\n\ninternal interface HostPar"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostPartner/HostPartnerCallTransactionFactory.kt",
"chars": 611,
"preview": "package band.gosrock.api.common.aop.hostPartner\n\nimport band.gosrock.api.common.aop.hostRole.FindHostFrom\nimport org.spr"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostPartner/HostPartnerEventTransaction.kt",
"chars": 977,
"preview": "package band.gosrock.api.common.aop.hostPartner\n\nimport band.gosrock.domain.domains.event.adaptor.EventAdaptor\nimport ba"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostPartner/HostPartnerHostTransaction.kt",
"chars": 810,
"preview": "package band.gosrock.api.common.aop.hostPartner\n\nimport band.gosrock.domain.domains.host.adaptor.HostAdaptor\nimport org."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostRole/FindHostFrom.kt",
"chars": 145,
"preview": "package band.gosrock.api.common.aop.hostRole\n\nenum class FindHostFrom(val identifier: String) {\n HOST_ID(\"hostId\"),\n "
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostRole/HostCallTransactionFactory.kt",
"chars": 1396,
"preview": "package band.gosrock.api.common.aop.hostRole\n\nimport band.gosrock.domain.domains.event.adaptor.EventAdaptor\nimport band."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostRole/HostQualification.kt",
"chars": 613,
"preview": "package band.gosrock.api.common.aop.hostRole\n\nimport band.gosrock.domain.domains.host.domain.Host\n\n/** 각 권한에 맞춰서 host 도메"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostRole/HostRoleAop.kt",
"chars": 2084,
"preview": "package band.gosrock.api.common.aop.hostRole\n\nimport org.aspectj.lang.ProceedingJoinPoint\nimport org.aspectj.lang.annota"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostRole/HostRoleCallTransaction.kt",
"chars": 271,
"preview": "package band.gosrock.api.common.aop.hostRole\n\nimport org.aspectj.lang.ProceedingJoinPoint\n\ninternal interface HostRoleCa"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostRole/HostRoleEventTransaction.kt",
"chars": 1643,
"preview": "package band.gosrock.api.common.aop.hostRole\n\nimport band.gosrock.domain.domains.event.adaptor.EventAdaptor\nimport band."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostRole/HostRoleHostTransaction.kt",
"chars": 1470,
"preview": "package band.gosrock.api.common.aop.hostRole\n\nimport band.gosrock.domain.domains.host.adaptor.HostAdaptor\nimport band.go"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/aop/hostRole/HostRolesAllowed.kt",
"chars": 491,
"preview": "package band.gosrock.api.common.aop.hostRole\n\nimport java.lang.annotation.Retention\nimport java.lang.annotation.Retentio"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/customizer/EnumValuePropertyCustomizer.kt",
"chars": 1219,
"preview": "package band.gosrock.api.common.customizer\n\nimport com.fasterxml.jackson.databind.JavaType\nimport io.swagger.v3.core.con"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/page/PageResponse.kt",
"chars": 672,
"preview": "package band.gosrock.api.common.page\n\nimport org.springframework.data.domain.Page\n\ndata class PageResponse<T>(\n val c"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/slice/SliceParam.kt",
"chars": 1239,
"preview": "package band.gosrock.api.common.slice\n\nimport io.swagger.v3.oas.annotations.media.Schema\nimport org.springframework.data"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/common/slice/SliceResponse.kt",
"chars": 533,
"preview": "package band.gosrock.api.common.slice\n\nimport org.springframework.data.domain.Slice\n\ndata class SliceResponse<T>(\n va"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/ExampleHolder.kt",
"chars": 177,
"preview": "package band.gosrock.api.config\n\nimport io.swagger.v3.oas.models.examples.Example\n\ndata class ExampleHolder(\n val hol"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/HttpContentCacheFilter.kt",
"chars": 869,
"preview": "package band.gosrock.api.config\n\nimport org.springframework.stereotype.Component\nimport org.springframework.web.filter.O"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/MdcFilter.kt",
"chars": 2249,
"preview": "package band.gosrock.api.config\n\nimport jakarta.servlet.FilterChain\nimport jakarta.servlet.http.HttpServletRequest\nimpor"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/ServletFilterConfig.kt",
"chars": 1997,
"preview": "package band.gosrock.api.config\n\nimport org.springframework.boot.web.servlet.FilterRegistrationBean\nimport org.springfra"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/SwaggerConfig.kt",
"chars": 8165,
"preview": "package band.gosrock.api.config\n\nimport band.gosrock.common.annotation.ApiErrorCodeExample\nimport band.gosrock.common.an"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/WebMvcConfig.kt",
"chars": 780,
"preview": "package band.gosrock.api.config\n\nimport band.gosrock.api.config.security.CurrentUserIdResolver\nimport org.springframewor"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/rateLimit/IPRateLimiter.kt",
"chars": 1116,
"preview": "package band.gosrock.api.config.rateLimit\n\nimport io.github.bucket4j.Bandwidth\nimport io.github.bucket4j.Bucket\nimport i"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/rateLimit/ThrottlingInterceptor.kt",
"chars": 2812,
"preview": "package band.gosrock.api.config.rateLimit\n\nimport band.gosrock.api.config.security.SecurityUtils\nimport band.gosrock.api"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/rateLimit/ThrottlingWebConfigure.kt",
"chars": 498,
"preview": "package band.gosrock.api.config.rateLimit\n\nimport org.springframework.stereotype.Component\nimport org.springframework.we"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/rateLimit/UserRateLimiter.kt",
"chars": 1118,
"preview": "package band.gosrock.api.config.rateLimit\n\nimport io.github.bucket4j.Bandwidth\nimport io.github.bucket4j.Bucket\nimport i"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/response/GlobalExceptionHandler.kt",
"chars": 6184,
"preview": "package band.gosrock.api.config.response\n\nimport band.gosrock.api.config.security.SecurityUtils\nimport band.gosrock.api."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/response/SuccessResponseAdvice.kt",
"chars": 1704,
"preview": "package band.gosrock.api.config.response\n\nimport band.gosrock.common.dto.SuccessResponse\nimport org.springframework.core"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/security/AccessDeniedFilter.kt",
"chars": 2354,
"preview": "package band.gosrock.api.config.security\n\nimport band.gosrock.common.consts.DuDoongStatic.SwaggerPatterns\nimport band.go"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/security/AuthDetails.kt",
"chars": 774,
"preview": "package band.gosrock.api.config.security\n\nimport org.springframework.security.core.GrantedAuthority\nimport org.springfra"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/security/CorsConfig.kt",
"chars": 1034,
"preview": "package band.gosrock.api.config.security\n\nimport band.gosrock.common.helper.SpringEnvironmentHelper\nimport org.springfra"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/security/CurrentUserIdResolver.kt",
"chars": 989,
"preview": "package band.gosrock.api.config.security\n\nimport band.gosrock.common.annotation.CurrentUserId\nimport org.springframework"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/security/JwtExceptionFilter.kt",
"chars": 1548,
"preview": "package band.gosrock.api.config.security\n\nimport band.gosrock.common.dto.ErrorResponse\nimport band.gosrock.common.except"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/security/JwtTokenFilter.kt",
"chars": 2661,
"preview": "package band.gosrock.api.config.security\n\nimport band.gosrock.api.auth.service.helper.CookieHelper\nimport band.gosrock.c"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/security/SecurityConfig.kt",
"chars": 3949,
"preview": "package band.gosrock.api.config.security\n\nimport band.gosrock.common.dto.ErrorResponse\nimport band.gosrock.common.except"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/config/security/SecurityUtils.kt",
"chars": 1008,
"preview": "package band.gosrock.api.config.security\n\nimport band.gosrock.common.exception.SecurityContextNotFoundException\nimport o"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/controller/CouponController.kt",
"chars": 2492,
"preview": "package band.gosrock.api.coupon.controller\n\nimport band.gosrock.common.annotation.CurrentUserId\nimport band.gosrock.api."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/dto/reqeust/CreateCouponCampaignRequest.kt",
"chars": 1908,
"preview": "package band.gosrock.api.coupon.dto.reqeust\n\nimport band.gosrock.common.annotation.DateFormat\nimport band.gosrock.domain"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/dto/response/CreateCouponCampaignResponse.kt",
"chars": 400,
"preview": "package band.gosrock.api.coupon.dto.response\n\nimport io.swagger.v3.oas.annotations.media.Schema\n\ndata class CreateCoupon"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/dto/response/CreateUserCouponResponse.kt",
"chars": 736,
"preview": "package band.gosrock.api.coupon.dto.response\n\nimport band.gosrock.common.annotation.DateFormat\nimport band.gosrock.domai"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/dto/response/ReadIssuedCouponOrderResponse.kt",
"chars": 899,
"preview": "package band.gosrock.api.coupon.dto.response\n\nimport band.gosrock.domain.common.vo.IssuedCouponInfoVo\nimport band.gosroc"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/dto/response/ReadIssuedCouponResponse.kt",
"chars": 540,
"preview": "package band.gosrock.api.coupon.dto.response\n\nimport band.gosrock.domain.common.vo.IssuedCouponInfoVo\nimport io.swagger."
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/mapper/CouponCampaignMapper.kt",
"chars": 2198,
"preview": "package band.gosrock.api.coupon.mapper\n\nimport band.gosrock.api.coupon.dto.reqeust.CreateCouponCampaignRequest\nimport ba"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/mapper/IssuedCouponMapper.kt",
"chars": 1858,
"preview": "package band.gosrock.api.coupon.mapper\n\nimport band.gosrock.api.coupon.dto.response.CreateUserCouponResponse\nimport band"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/service/CreateCouponUseCase.kt",
"chars": 1471,
"preview": "package band.gosrock.api.coupon.service\n\nimport band.gosrock.api.coupon.dto.reqeust.CreateCouponCampaignRequest\nimport b"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/service/CreateUserCouponUseCase.kt",
"chars": 1357,
"preview": "package band.gosrock.api.coupon.service\n\nimport band.gosrock.api.coupon.dto.response.CreateUserCouponResponse\nimport ban"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/coupon/service/ReadIssuedCouponUseCase.kt",
"chars": 1911,
"preview": "package band.gosrock.api.coupon.service\n\nimport band.gosrock.api.coupon.dto.response.ReadIssuedCouponResponse\nimport ban"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/email/dto/IssuedTicketMailDTO.kt",
"chars": 392,
"preview": "package band.gosrock.api.email.dto\n\nimport band.gosrock.infrastructure.config.mail.dto.EmailEventInfo\nimport band.gosroc"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/email/dto/OrderMailDto.kt",
"chars": 364,
"preview": "package band.gosrock.api.email.dto\n\nimport band.gosrock.infrastructure.config.mail.dto.EmailEventInfo\nimport band.gosroc"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/email/handler/CreateOrderEventEmailHandler.kt",
"chars": 1308,
"preview": "package band.gosrock.api.email.handler\n\nimport band.gosrock.api.email.service.OrderApproveRequestEmailService\nimport ban"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/email/handler/DoneOrderEventEmailHandler.kt",
"chars": 1578,
"preview": "package band.gosrock.api.email.handler\n\nimport band.gosrock.api.email.service.OrderApproveConfirmEmailService\nimport ban"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/email/handler/EntranceIssuedTicketEventEmailHandler.kt",
"chars": 1395,
"preview": "package band.gosrock.api.email.handler\n\nimport band.gosrock.api.email.service.EntranceIssuedTicketEmailService\nimport ba"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/email/handler/RegisterUserEventEmailHandler.kt",
"chars": 1162,
"preview": "package band.gosrock.api.email.handler\n\nimport band.gosrock.api.email.service.SendRegisterEmailService\nimport band.gosro"
},
{
"path": "DuDoong-Api/src/main/kotlin/band/gosrock/api/email/handler/WithDrawOrderEventEmailHandler.kt",
"chars": 1501,
"preview": "package band.gosrock.api.email.handler\n\nimport band.gosrock.api.email.service.OrderMailInfoHelper\nimport band.gosrock.ap"
}
]
// ... and 832 more files (download for full content)
About this extraction
This page contains the full source code of the Gosrock/DuDoong-Backend GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1032 files (1.5 MB), approximately 437.5k tokens, and a symbol index with 483 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.