From bcaffb67186c991a232d57ca8a79249490ba0429 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 17:20:49 +0900 Subject: [PATCH 01/39] =?UTF-8?q?refactor:=20=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=AA=A8=EB=93=88=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 113 ++++++------------ service/build.gradle.kts | 60 ++++++++++ .../roomescape/RoomescapeApplication.kt | 2 +- .../roomescape/admin/business/AdminService.kt | 16 +-- .../admin/exception/AdminException.kt | 6 +- .../infrastructure/persistence/AdminEntity.kt | 4 +- .../persistence/AdminRepository.kt | 2 +- .../roomescape/auth/business/AuthService.kt | 22 ++-- .../auth/business/LoginHistoryService.kt | 12 +- .../sangdol}/roomescape/auth/docs/AuthAPI.kt | 14 +-- .../auth/exception/AuthErrorCode.kt | 4 +- .../auth/exception/AuthException.kt | 4 +- .../auth/infrastructure/jwt/JwtUtils.kt | 6 +- .../persistence/LoginHistoryEntity.kt | 6 +- .../persistence/LoginHistoryRepository.kt | 2 +- .../roomescape/auth/web/AuthController.kt | 12 +- .../sangdol}/roomescape/auth/web/AuthDTO.kt | 6 +- .../auth/web/support/AuthAnnotations.kt | 6 +- .../auth/web/support/CookieUtils.kt | 2 +- .../support/interceptors/AdminInterceptor.kt | 24 ++-- .../support/interceptors/UserInterceptor.kt | 16 +-- .../support/resolver/UserContextResolver.kt | 14 +-- .../roomescape/common/config/JacksonConfig.kt | 2 +- .../roomescape/common/config/JpaConfig.kt | 4 +- .../roomescape/common/config/SwaggerConfig.kt | 15 +++ .../roomescape/common/config/TsidConfig.kt | 2 +- .../roomescape/common/config/WebMvcConfig.kt | 8 +- .../roomescape/common/dto/AuditDto.kt | 2 +- .../roomescape/common/dto/CommonAuth.kt | 16 +-- .../common/dto/response/CommonApiResponse.kt | 4 +- .../roomescape/common/entity/BaseEntity.kt | 2 +- .../common/exception/CommonErrorCode.kt | 2 +- .../roomescape/common/exception/ErrorCode.kt | 2 +- .../exception/ExceptionControllerAdvice.kt | 14 +-- .../common/exception/RoomescapeException.kt | 2 +- .../common/log/ApiLogMessageConverter.kt | 4 +- .../common/log/ControllerLoggingAspect.kt | 4 +- .../common/log/HttpRequestLoggingFilter.kt | 4 +- .../roomescape/common/log/LogConfiguration.kt | 2 +- .../MDCAwareSlowQueryListenerWithoutParams.kt | 2 +- .../common/log/ProxyDataSourceConfig.kt | 2 +- .../log/RoomescapeLogMaskingConverter.kt | 4 +- .../roomescape/common/util/DateUtils.kt | 2 +- .../roomescape/common/util/MDCUtils.kt | 2 +- .../common/util/TransactionExecutionUtil.kt | 6 +- .../payment/business/PaymentService.kt | 18 +-- .../payment/business/PaymentWriter.kt | 16 +-- .../roomescape/payment/docs/PaymentAPI.kt | 16 +-- .../payment/exception/PaymentErrorCode.kt | 4 +- .../payment/exception/PaymentException.kt | 4 +- .../infrastructure/client/PaymentConfig.kt | 2 +- .../client/PaymentProperties.kt | 2 +- .../infrastructure/client/TosspayCancelDTO.kt | 6 +- .../infrastructure/client/TosspayClient.kt | 6 +- .../client/TosspayConfirmDTO.kt | 16 +-- .../client/TosspayErrorResponse.kt | 2 +- .../infrastructure/common/PaymentTypes.kt | 6 +- .../persistence/CanceledPaymentEntity.kt | 4 +- .../persistence/CanceledPaymentRepository.kt | 2 +- .../persistence/PaymentDetailEntity.kt | 6 +- .../persistence/PaymentDetailRepository.kt | 2 +- .../persistence/PaymentEntity.kt | 10 +- .../persistence/PaymentRepository.kt | 2 +- .../payment/web/PaymentController.kt | 12 +- .../roomescape/payment/web/PaymentDTO.kt | 14 +-- .../region/business/RegionService.kt | 10 +- .../roomescape/region/docs/RegionAPI.kt | 12 +- .../region/exception/RegionException.kt | 6 +- .../persistence/RegionEntity.kt | 2 +- .../persistence/RegionRepository.kt | 2 +- .../roomescape/region/web/RegionController.kt | 8 +- .../roomescape/region/web/RegionDTO.kt | 2 +- .../business/ReservationService.kt | 34 +++--- .../business/ReservationValidator.kt | 14 +-- .../reservation/docs/ReservationAPI.kt | 14 +-- .../exception/ReservationErrorCode.kt | 4 +- .../exception/ReservationException.kt | 9 ++ .../persistence/CanceledReservationEntity.kt | 4 +- .../CanceledReservationRepository.kt | 2 +- .../persistence/ReservationEntity.kt | 4 +- .../persistence/ReservationRepository.kt | 2 +- .../reservation/web/ReservationController.kt | 12 +- .../reservation/web/ReservationDto.kt | 12 +- .../schedule/business/ScheduleService.kt | 24 ++-- .../schedule/business/ScheduleValidator.kt | 14 +-- .../business/domain/ScheduleOverview.kt | 6 +- .../roomescape/schedule/docs/ScheduleAPI.kt | 18 +-- .../schedule/exception/ScheduleErrorCode.kt | 4 +- .../schedule/exception/ScheduleException.kt | 4 +- .../persistence/ScheduleEntity.kt | 6 +- .../persistence/ScheduleRepository.kt | 8 +- .../schedule/web/AdminScheduleController.kt | 10 +- .../schedule/web/AdminScheduleDto.kt | 6 +- .../schedule/web/ScheduleController.kt | 10 +- .../roomescape/schedule/web/ScheduleDto.kt | 10 +- .../roomescape/store/business/StoreService.kt | 22 ++-- .../store/business/StoreValidator.kt | 12 +- .../roomescape/store/docs/StoreAPI.kt | 14 +-- .../store/exception/StoreException.kt | 6 +- .../infrastructure/persistence/StoreEntity.kt | 4 +- .../persistence/StoreRepository.kt | 6 +- .../store/web/AdminStoreController.kt | 8 +- .../roomescape/store/web/AdminStoreDto.kt | 8 +- .../roomescape/store/web/StoreController.kt | 8 +- .../sangdol}/roomescape/store/web/StoreDTO.kt | 4 +- .../roomescape/theme/business/ThemeService.kt | 20 ++-- .../theme/business/ThemeValidator.kt | 12 +- .../theme/business/domain/ThemeInfo.kt | 2 +- .../roomescape/theme/docs/ThemeApi.kt | 14 +-- .../theme/exception/ThemeErrorCode.kt | 4 +- .../theme/exception/ThemeException.kt | 4 +- .../infrastructure/persistence/ThemeEntity.kt | 4 +- .../persistence/ThemeRepository.kt | 4 +- .../theme/web/AdminThemeController.kt | 8 +- .../roomescape/theme/web/AdminThemeDto.kt | 8 +- .../roomescape/theme/web/ThemeController.kt | 8 +- .../sangdol}/roomescape/theme/web/ThemeDto.kt | 6 +- .../roomescape/user/business/UserService.kt | 24 ++-- .../roomescape/user/business/UserValidator.kt | 8 +- .../sangdol}/roomescape/user/docs/UserAPI.kt | 16 +-- .../user/exception/UserException.kt | 6 +- .../persistence/UserEntities.kt | 4 +- .../persistence/UserRepositories.kt | 2 +- .../roomescape/user/web/UserController.kt | 12 +- .../sangdol}/roomescape/user/web/UserDTO.kt | 6 +- .../main/resources/application-deploy.yaml | 0 .../main/resources/application-local.yaml | 0 .../src}/main/resources/application.yaml | 0 .../src}/main/resources/logback-deploy.xml | 2 +- .../src}/main/resources/logback-local.xml | 4 +- .../src}/main/resources/logback-spring.xml | 0 .../main/resources/schema/region-data.sql | 0 .../src}/main/resources/schema/schema-h2.sql | 0 .../main/resources/schema/schema-mysql.sql | 0 .../sangdol}/roomescape/auth/AuthApiTest.kt | 32 ++--- .../auth/FailOnSaveLoginHistoryTest.kt | 16 +-- .../sangdol}/roomescape/auth/JwtUtilsTest.kt | 12 +- .../common/config/JacksonConfigTest.kt | 2 +- .../common/log/ApiLogMessageConverterTest.kt | 6 +- ...AwareSlowQueryListenerWithoutParamsTest.kt | 2 +- .../log/RoomescapeLogMaskingConverterTest.kt | 2 +- .../roomescape/common/util/DateUtilsTest.kt | 2 +- .../roomescape/data/DefaultDataInitializer.kt | 42 +++---- .../roomescape/data/PopulationDataParser.kt | 13 +- .../roomescape/payment/PaymentAPITest.kt | 26 ++-- .../payment/SampleTosspayConstant.kt | 2 +- .../roomescape/payment/TosspayClientTest.kt | 14 +-- .../roomescape/region/RegionApiFailTest.kt | 10 +- .../roomescape/region/RegionApiSuccessTest.kt | 6 +- .../reservation/ReservationApiTest.kt | 40 +++---- .../schedule/AdminScheduleApiTest.kt | 28 ++--- .../roomescape/schedule/ScheduleApiTest.kt | 16 +-- .../roomescape/store/AdminStoreApiTest.kt | 22 ++-- .../sangdol}/roomescape/store/StoreApiTest.kt | 8 +- .../roomescape/supports/DummyInitializer.kt | 62 +++++----- .../sangdol}/roomescape/supports/Fixtures.kt | 46 +++---- .../roomescape/supports/KotestConfig.kt | 18 +-- .../roomescape/supports/RestAssuredUtils.kt | 6 +- .../roomescape/supports/TestAuthUtil.kt | 20 ++-- .../roomescape/supports/TestDatabaseUtil.kt | 2 +- .../sangdol}/roomescape/supports/TestUtil.kt | 2 +- .../roomescape/theme/AdminThemeApiTest.kt | 26 ++-- .../sangdol}/roomescape/theme/ThemeApiTest.kt | 23 ++-- .../sangdol}/roomescape/user/UserApiTest.kt | 26 ++-- .../resources/application-test-mysql.yaml | 0 .../src}/test/resources/application-test.yaml | 0 .../src}/test/resources/logback-test.xml | 0 .../roomescape/common/config/SwaggerConfig.kt | 78 ------------ .../exception/ReservationException.kt | 9 -- src/main/resources/login.http | 55 --------- src/main/resources/test.http | 4 - 171 files changed, 862 insertions(+), 969 deletions(-) create mode 100644 service/build.gradle.kts rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/RoomescapeApplication.kt (92%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/admin/business/AdminService.kt (78%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/admin/exception/AdminException.kt (72%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/admin/infrastructure/persistence/AdminEntity.kt (91%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/admin/infrastructure/persistence/AdminRepository.kt (73%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/business/AuthService.kt (84%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/business/LoginHistoryService.kt (85%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/docs/AuthAPI.kt (70%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/exception/AuthErrorCode.kt (89%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/exception/AuthException.kt (59%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/infrastructure/jwt/JwtUtils.kt (93%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt (77%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/infrastructure/persistence/LoginHistoryRepository.kt (77%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/web/AuthController.kt (76%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/web/AuthDTO.kt (82%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/web/support/AuthAnnotations.kt (71%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/web/support/CookieUtils.kt (85%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/web/support/interceptors/AdminInterceptor.kt (83%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/web/support/interceptors/UserInterceptor.kt (78%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/auth/web/support/resolver/UserContextResolver.kt (79%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/config/JacksonConfig.kt (98%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/config/JpaConfig.kt (83%) create mode 100644 service/src/main/kotlin/com/sangdol/roomescape/common/config/SwaggerConfig.kt rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/config/TsidConfig.kt (91%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/config/WebMvcConfig.kt (75%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/dto/AuditDto.kt (89%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/dto/CommonAuth.kt (72%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/dto/response/CommonApiResponse.kt (78%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/entity/BaseEntity.kt (96%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/exception/CommonErrorCode.kt (92%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/exception/ErrorCode.kt (75%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/exception/ExceptionControllerAdvice.kt (91%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/exception/RoomescapeException.kt (75%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/log/ApiLogMessageConverter.kt (96%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/log/ControllerLoggingAspect.kt (96%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/log/HttpRequestLoggingFilter.kt (92%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/log/LogConfiguration.kt (96%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParams.kt (96%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/log/ProxyDataSourceConfig.kt (97%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/log/RoomescapeLogMaskingConverter.kt (96%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/util/DateUtils.kt (86%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/util/MDCUtils.kt (92%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/common/util/TransactionExecutionUtil.kt (86%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/business/PaymentService.kt (90%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/business/PaymentWriter.kt (85%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/docs/PaymentAPI.kt (68%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/exception/PaymentErrorCode.kt (92%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/exception/PaymentException.kt (59%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/client/PaymentConfig.kt (96%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/client/PaymentProperties.kt (81%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/client/TosspayCancelDTO.kt (90%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/client/TosspayClient.kt (96%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/client/TosspayConfirmDTO.kt (84%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/client/TosspayErrorResponse.kt (57%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/common/PaymentTypes.kt (97%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt (80%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt (76%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt (89%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/persistence/PaymentDetailRepository.kt (75%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/persistence/PaymentEntity.kt (67%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/infrastructure/persistence/PaymentRepository.kt (75%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/web/PaymentController.kt (73%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/payment/web/PaymentDTO.kt (89%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/region/business/RegionService.kt (91%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/region/docs/RegionAPI.kt (78%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/region/exception/RegionException.kt (81%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/region/infrastructure/persistence/RegionEntity.kt (86%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/region/infrastructure/persistence/RegionRepository.kt (95%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/region/web/RegionController.kt (86%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/region/web/RegionDTO.kt (91%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/reservation/business/ReservationService.kt (86%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/reservation/business/ReservationValidator.kt (74%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/reservation/docs/ReservationAPI.kt (83%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/reservation/exception/ReservationErrorCode.kt (87%) create mode 100644 service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationException.kt rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt (80%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/reservation/infrastructure/persistence/CanceledReservationRepository.kt (68%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt (84%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/reservation/infrastructure/persistence/ReservationRepository.kt (78%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/reservation/web/ReservationController.kt (84%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/reservation/web/ReservationDto.kt (85%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/business/ScheduleService.kt (90%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/business/ScheduleValidator.kt (85%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/business/domain/ScheduleOverview.kt (75%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/docs/ScheduleAPI.kt (85%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/exception/ScheduleErrorCode.kt (88%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/exception/ScheduleException.kt (56%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt (91%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt (86%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/web/AdminScheduleController.kt (88%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/web/AdminScheduleDto.kt (86%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/web/ScheduleController.kt (77%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/schedule/web/ScheduleDto.kt (84%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/store/business/StoreService.kt (88%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/store/business/StoreValidator.kt (85%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/store/docs/StoreAPI.kt (85%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/store/exception/StoreException.kt (85%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/store/infrastructure/persistence/StoreEntity.kt (87%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/store/infrastructure/persistence/StoreRepository.kt (76%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/store/web/AdminStoreController.kt (86%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/store/web/AdminStoreDto.kt (79%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/store/web/StoreController.kt (83%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/store/web/StoreDTO.kt (84%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/business/ThemeService.kt (91%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/business/ThemeValidator.kt (92%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/business/domain/ThemeInfo.kt (86%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/docs/ThemeApi.kt (87%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/exception/ThemeErrorCode.kt (92%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/exception/ThemeException.kt (59%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/infrastructure/persistence/ThemeEntity.kt (92%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/infrastructure/persistence/ThemeRepository.kt (92%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/web/AdminThemeController.kt (90%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/web/AdminThemeDto.kt (93%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/web/ThemeController.kt (78%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/theme/web/ThemeDto.kt (89%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/user/business/UserService.kt (83%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/user/business/UserValidator.kt (80%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/user/docs/UserAPI.kt (65%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/user/exception/UserException.kt (79%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/user/infrastructure/persistence/UserEntities.kt (84%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/user/infrastructure/persistence/UserRepositories.kt (84%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/user/web/UserController.kt (71%) rename {src/main/kotlin => service/src/main/kotlin/com/sangdol}/roomescape/user/web/UserDTO.kt (83%) rename {src => service/src}/main/resources/application-deploy.yaml (100%) rename {src => service/src}/main/resources/application-local.yaml (100%) rename {src => service/src}/main/resources/application.yaml (100%) rename {src => service/src}/main/resources/logback-deploy.xml (97%) rename {src => service/src}/main/resources/logback-local.xml (82%) rename {src => service/src}/main/resources/logback-spring.xml (100%) rename {src => service/src}/main/resources/schema/region-data.sql (100%) rename {src => service/src}/main/resources/schema/schema-h2.sql (100%) rename {src => service/src}/main/resources/schema/schema-mysql.sql (100%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/auth/AuthApiTest.kt (89%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/auth/FailOnSaveLoginHistoryTest.kt (80%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/auth/JwtUtilsTest.kt (90%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/common/config/JacksonConfigTest.kt (98%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/common/log/ApiLogMessageConverterTest.kt (94%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt (95%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt (98%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/common/util/DateUtilsTest.kt (90%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/data/DefaultDataInitializer.kt (95%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/data/PopulationDataParser.kt (95%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/payment/PaymentAPITest.kt (95%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/payment/SampleTosspayConstant.kt (99%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/payment/TosspayClientTest.kt (93%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/region/RegionApiFailTest.kt (83%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/region/RegionApiSuccessTest.kt (90%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/reservation/ReservationApiTest.kt (94%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/schedule/AdminScheduleApiTest.kt (96%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/schedule/ScheduleApiTest.kt (89%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/store/AdminStoreApiTest.kt (96%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/store/StoreApiTest.kt (94%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/supports/DummyInitializer.kt (74%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/supports/Fixtures.kt (86%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/supports/KotestConfig.kt (78%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/supports/RestAssuredUtils.kt (95%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/supports/TestAuthUtil.kt (86%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/supports/TestDatabaseUtil.kt (98%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/supports/TestUtil.kt (95%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/theme/AdminThemeApiTest.kt (97%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/theme/ThemeApiTest.kt (86%) rename {src/test/kotlin => service/src/test/kotlin/com/sangdol}/roomescape/user/UserApiTest.kt (91%) rename {src => service/src}/test/resources/application-test-mysql.yaml (100%) rename {src => service/src}/test/resources/application-test.yaml (100%) rename {src => service/src}/test/resources/logback-test.xml (100%) delete mode 100644 src/main/kotlin/roomescape/common/config/SwaggerConfig.kt delete mode 100644 src/main/kotlin/roomescape/reservation/exception/ReservationException.kt delete mode 100644 src/main/resources/login.http delete mode 100644 src/main/resources/test.http diff --git a/build.gradle.kts b/build.gradle.kts index c6a792f2..28b26eaf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,99 +1,56 @@ +import org.jetbrains.kotlin.gradle.plugin.KaptExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { val springBootVersion = "3.5.3" val kotlinVersion = "2.2.0" - id("org.springframework.boot") version springBootVersion - id("io.spring.dependency-management") version "1.1.7" - kotlin("jvm") version kotlinVersion - kotlin("plugin.spring") version kotlinVersion - kotlin("plugin.jpa") version kotlinVersion - kotlin("kapt") version kotlinVersion + id("io.spring.dependency-management") version "1.1.7" apply false + id("org.springframework.boot") version springBootVersion apply false + kotlin("jvm") version kotlinVersion apply false + kotlin("kapt") version kotlinVersion apply false + kotlin("plugin.spring") version kotlinVersion apply false + kotlin("plugin.jpa") version kotlinVersion apply false } group = "com.sangdol" version = "0.0.1-SNAPSHOT" -java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) +allprojects { + repositories { + mavenCentral() } } -tasks.jar { - enabled = false -} +subprojects { + apply(plugin = "org.jetbrains.kotlin.jvm") + apply(plugin = "org.jetbrains.kotlin.kapt") + apply(plugin = "io.spring.dependency-management") -kapt { - keepJavacAnnotationProcessors = true -} + extensions.configure { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } -repositories { - mavenCentral() -} + extensions.configure { + keepJavacAnnotationProcessors = true + } -dependencies { - // Spring - implementation("org.springframework.boot:spring-boot-starter-web") - implementation("org.springframework.boot:spring-boot-starter-data-jpa") - implementation("org.springframework.boot:spring-boot-starter-validation") + dependencies { + add("implementation", "io.github.oshai:kotlin-logging-jvm:7.0.3") + } - // API docs - implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9") + tasks.withType { + useJUnitPlatform() + } - // DB - implementation("com.github.f4b6a3:tsid-creator:5.2.6") - runtimeOnly("com.h2database:h2") - runtimeOnly("com.mysql:mysql-connector-j") - - // Jwt - implementation("io.jsonwebtoken:jjwt:0.12.6") - - // Logging - implementation("io.github.oshai:kotlin-logging-jvm:7.0.3") - implementation("net.logstash.logback:logstash-logback-encoder:8.1") - implementation("com.github.loki4j:loki-logback-appender:2.0.0") - implementation("net.ttddyy.observation:datasource-micrometer-spring-boot:1.1.1") - - // Observability - implementation("org.springframework.boot:spring-boot-starter-actuator") - implementation("io.micrometer:micrometer-tracing-bridge-otel") - implementation("io.opentelemetry:opentelemetry-exporter-otlp") - runtimeOnly("io.micrometer:micrometer-registry-prometheus") - - // Kotlin - implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin") - implementation("io.github.oshai:kotlin-logging-jvm:7.0.3") - - // Test - testImplementation("org.springframework.boot:spring-boot-starter-test") - testImplementation("io.mockk:mockk:1.14.4") - testImplementation("com.ninja-squad:springmockk:4.0.2") - - // Kotest - testImplementation("io.kotest:kotest-runner-junit5:5.9.1") - testImplementation("io.kotest.extensions:kotest-extensions-spring:1.3.0") - - // RestAssured - testImplementation("io.rest-assured:rest-assured:5.5.5") - testImplementation("io.rest-assured:kotlin-extensions:5.5.5") - - // etc - implementation("org.apache.poi:poi-ooxml:5.2.3") -} - -tasks.withType { - useJUnitPlatform() -} - -tasks.withType { - compilerOptions { - freeCompilerArgs.addAll( - "-Xjsr305=strict", - "-Xannotation-default-target=param-property" - ) - jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + tasks.withType { + compilerOptions { + freeCompilerArgs.addAll( + "-Xjsr305=strict", + "-Xannotation-default-target=param-property" + ) + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + } } } diff --git a/service/build.gradle.kts b/service/build.gradle.kts new file mode 100644 index 00000000..db40bee3 --- /dev/null +++ b/service/build.gradle.kts @@ -0,0 +1,60 @@ +plugins { + id("org.springframework.boot") + kotlin("plugin.spring") + kotlin("plugin.jpa") +} + +dependencies { + // Spring + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-validation") + + // API docs + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9") + + // DB + implementation("com.github.f4b6a3:tsid-creator:5.2.6") + runtimeOnly("com.h2database:h2") + runtimeOnly("com.mysql:mysql-connector-j") + + // Jwt + implementation("io.jsonwebtoken:jjwt:0.12.6") + + // Logging + implementation("io.github.oshai:kotlin-logging-jvm:7.0.3") + implementation("net.logstash.logback:logstash-logback-encoder:8.1") + implementation("com.github.loki4j:loki-logback-appender:2.0.0") + implementation("net.ttddyy.observation:datasource-micrometer-spring-boot:1.1.1") + + // Observability + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("io.micrometer:micrometer-tracing-bridge-otel") + implementation("io.opentelemetry:opentelemetry-exporter-otlp") + runtimeOnly("io.micrometer:micrometer-registry-prometheus") + + // Kotlin + implementation("org.jetbrains.kotlin:kotlin-reflect") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("io.github.oshai:kotlin-logging-jvm:7.0.3") + + // Test + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("io.mockk:mockk:1.14.4") + testImplementation("com.ninja-squad:springmockk:4.0.2") + + // Kotest + testImplementation("io.kotest:kotest-runner-junit5:5.9.1") + testImplementation("io.kotest.extensions:kotest-extensions-spring:1.3.0") + + // RestAssured + testImplementation("io.rest-assured:rest-assured:5.5.5") + testImplementation("io.rest-assured:kotlin-extensions:5.5.5") + + // etc + implementation("org.apache.poi:poi-ooxml:5.2.3") +} + +tasks.jar { + enabled = false +} diff --git a/src/main/kotlin/roomescape/RoomescapeApplication.kt b/service/src/main/kotlin/com/sangdol/roomescape/RoomescapeApplication.kt similarity index 92% rename from src/main/kotlin/roomescape/RoomescapeApplication.kt rename to service/src/main/kotlin/com/sangdol/roomescape/RoomescapeApplication.kt index aca20d20..0e420eda 100644 --- a/src/main/kotlin/roomescape/RoomescapeApplication.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/RoomescapeApplication.kt @@ -1,4 +1,4 @@ -package roomescape +package com.sangdol.roomescape import org.springframework.boot.Banner import org.springframework.boot.SpringApplication diff --git a/src/main/kotlin/roomescape/admin/business/AdminService.kt b/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt similarity index 78% rename from src/main/kotlin/roomescape/admin/business/AdminService.kt rename to service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt index 96faa073..6e07188e 100644 --- a/src/main/kotlin/roomescape/admin/business/AdminService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt @@ -1,17 +1,17 @@ -package roomescape.admin.business +package com.sangdol.roomescape.admin.business import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import roomescape.admin.exception.AdminErrorCode -import roomescape.admin.exception.AdminException -import roomescape.admin.infrastructure.persistence.AdminRepository -import roomescape.common.dto.AdminLoginCredentials -import roomescape.common.dto.AuditConstant -import roomescape.common.dto.OperatorInfo -import roomescape.common.dto.toCredentials +import com.sangdol.roomescape.admin.exception.AdminErrorCode +import com.sangdol.roomescape.admin.exception.AdminException +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminRepository +import com.sangdol.roomescape.common.dto.AdminLoginCredentials +import com.sangdol.roomescape.common.dto.AuditConstant +import com.sangdol.roomescape.common.dto.OperatorInfo +import com.sangdol.roomescape.common.dto.toCredentials private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/admin/exception/AdminException.kt b/service/src/main/kotlin/com/sangdol/roomescape/admin/exception/AdminException.kt similarity index 72% rename from src/main/kotlin/roomescape/admin/exception/AdminException.kt rename to service/src/main/kotlin/com/sangdol/roomescape/admin/exception/AdminException.kt index a9bfcc8c..5de62281 100644 --- a/src/main/kotlin/roomescape/admin/exception/AdminException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/admin/exception/AdminException.kt @@ -1,8 +1,8 @@ -package roomescape.admin.exception +package com.sangdol.roomescape.admin.exception +import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.RoomescapeException import org.springframework.http.HttpStatus -import roomescape.common.exception.ErrorCode -import roomescape.common.exception.RoomescapeException class AdminException( override val errorCode: AdminErrorCode, diff --git a/src/main/kotlin/roomescape/admin/infrastructure/persistence/AdminEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/admin/infrastructure/persistence/AdminEntity.kt similarity index 91% rename from src/main/kotlin/roomescape/admin/infrastructure/persistence/AdminEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/admin/infrastructure/persistence/AdminEntity.kt index 85387596..106775eb 100644 --- a/src/main/kotlin/roomescape/admin/infrastructure/persistence/AdminEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/admin/infrastructure/persistence/AdminEntity.kt @@ -1,8 +1,8 @@ -package roomescape.admin.infrastructure.persistence +package com.sangdol.roomescape.admin.infrastructure.persistence import jakarta.persistence.* import org.springframework.data.jpa.domain.support.AuditingEntityListener -import roomescape.common.entity.AuditingBaseEntity +import com.sangdol.roomescape.common.entity.AuditingBaseEntity @Entity @Table(name = "admin") diff --git a/src/main/kotlin/roomescape/admin/infrastructure/persistence/AdminRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/admin/infrastructure/persistence/AdminRepository.kt similarity index 73% rename from src/main/kotlin/roomescape/admin/infrastructure/persistence/AdminRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/admin/infrastructure/persistence/AdminRepository.kt index 7520c23c..e0c071fd 100644 --- a/src/main/kotlin/roomescape/admin/infrastructure/persistence/AdminRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/admin/infrastructure/persistence/AdminRepository.kt @@ -1,4 +1,4 @@ -package roomescape.admin.infrastructure.persistence +package com.sangdol.roomescape.admin.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository diff --git a/src/main/kotlin/roomescape/auth/business/AuthService.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/business/AuthService.kt similarity index 84% rename from src/main/kotlin/roomescape/auth/business/AuthService.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/business/AuthService.kt index 5366af04..ff0ca22d 100644 --- a/src/main/kotlin/roomescape/auth/business/AuthService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/business/AuthService.kt @@ -1,19 +1,19 @@ -package roomescape.auth.business +package com.sangdol.roomescape.auth.business import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import roomescape.admin.business.AdminService -import roomescape.auth.exception.AuthErrorCode -import roomescape.auth.exception.AuthException -import roomescape.auth.infrastructure.jwt.JwtUtils -import roomescape.auth.web.LoginContext -import roomescape.auth.web.LoginRequest -import roomescape.auth.web.LoginSuccessResponse -import roomescape.common.dto.LoginCredentials -import roomescape.common.dto.PrincipalType -import roomescape.user.business.UserService +import com.sangdol.roomescape.admin.business.AdminService +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.auth.exception.AuthException +import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils +import com.sangdol.roomescape.auth.web.LoginContext +import com.sangdol.roomescape.auth.web.LoginRequest +import com.sangdol.roomescape.auth.web.LoginSuccessResponse +import com.sangdol.roomescape.common.dto.LoginCredentials +import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.user.business.UserService private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/auth/business/LoginHistoryService.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt similarity index 85% rename from src/main/kotlin/roomescape/auth/business/LoginHistoryService.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt index af7a06dc..680c1a91 100644 --- a/src/main/kotlin/roomescape/auth/business/LoginHistoryService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt @@ -1,4 +1,4 @@ -package roomescape.auth.business +package com.sangdol.roomescape.auth.business import com.github.f4b6a3.tsid.TsidFactory import io.github.oshai.kotlinlogging.KLogger @@ -6,11 +6,11 @@ import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Propagation import org.springframework.transaction.annotation.Transactional -import roomescape.auth.infrastructure.persistence.LoginHistoryEntity -import roomescape.auth.infrastructure.persistence.LoginHistoryRepository -import roomescape.auth.web.LoginContext -import roomescape.common.config.next -import roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryEntity +import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryRepository +import com.sangdol.roomescape.auth.web.LoginContext +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.common.dto.PrincipalType private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/auth/docs/AuthAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt similarity index 70% rename from src/main/kotlin/roomescape/auth/docs/AuthAPI.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt index 5571a169..593f7397 100644 --- a/src/main/kotlin/roomescape/auth/docs/AuthAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt @@ -1,4 +1,4 @@ -package roomescape.auth.docs +package com.sangdol.roomescape.auth.docs import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse @@ -8,12 +8,12 @@ import jakarta.servlet.http.HttpServletResponse import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RequestBody -import roomescape.auth.web.LoginRequest -import roomescape.auth.web.LoginSuccessResponse -import roomescape.auth.web.support.Public -import roomescape.auth.web.support.User -import roomescape.common.dto.CurrentUserContext -import roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.auth.web.LoginRequest +import com.sangdol.roomescape.auth.web.LoginSuccessResponse +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.common.dto.response.CommonApiResponse interface AuthAPI { diff --git a/src/main/kotlin/roomescape/auth/exception/AuthErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthErrorCode.kt similarity index 89% rename from src/main/kotlin/roomescape/auth/exception/AuthErrorCode.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthErrorCode.kt index 40781ac5..ee5f5f01 100644 --- a/src/main/kotlin/roomescape/auth/exception/AuthErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthErrorCode.kt @@ -1,7 +1,7 @@ -package roomescape.auth.exception +package com.sangdol.roomescape.auth.exception import org.springframework.http.HttpStatus -import roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.ErrorCode enum class AuthErrorCode( override val httpStatus: HttpStatus, diff --git a/src/main/kotlin/roomescape/auth/exception/AuthException.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthException.kt similarity index 59% rename from src/main/kotlin/roomescape/auth/exception/AuthException.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthException.kt index 83295bdf..07c26ac6 100644 --- a/src/main/kotlin/roomescape/auth/exception/AuthException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthException.kt @@ -1,6 +1,6 @@ -package roomescape.auth.exception +package com.sangdol.roomescape.auth.exception -import roomescape.common.exception.RoomescapeException +import com.sangdol.roomescape.common.exception.RoomescapeException class AuthException( override val errorCode: AuthErrorCode, diff --git a/src/main/kotlin/roomescape/auth/infrastructure/jwt/JwtUtils.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/jwt/JwtUtils.kt similarity index 93% rename from src/main/kotlin/roomescape/auth/infrastructure/jwt/JwtUtils.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/jwt/JwtUtils.kt index b68d612b..ba34b687 100644 --- a/src/main/kotlin/roomescape/auth/infrastructure/jwt/JwtUtils.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/jwt/JwtUtils.kt @@ -1,4 +1,4 @@ -package roomescape.auth.infrastructure.jwt +package com.sangdol.roomescape.auth.infrastructure.jwt import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -8,8 +8,8 @@ import io.jsonwebtoken.Jwts import io.jsonwebtoken.security.Keys import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component -import roomescape.auth.exception.AuthErrorCode -import roomescape.auth.exception.AuthException +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.auth.exception.AuthException import java.util.* import javax.crypto.SecretKey diff --git a/src/main/kotlin/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt similarity index 77% rename from src/main/kotlin/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt index 126e6a9e..8a64442f 100644 --- a/src/main/kotlin/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt @@ -1,10 +1,10 @@ -package roomescape.auth.infrastructure.persistence +package com.sangdol.roomescape.auth.infrastructure.persistence import jakarta.persistence.* import org.springframework.data.annotation.CreatedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener -import roomescape.common.dto.PrincipalType -import roomescape.common.entity.PersistableBaseEntity +import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.common.entity.PersistableBaseEntity import java.time.LocalDateTime @Entity diff --git a/src/main/kotlin/roomescape/auth/infrastructure/persistence/LoginHistoryRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryRepository.kt similarity index 77% rename from src/main/kotlin/roomescape/auth/infrastructure/persistence/LoginHistoryRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryRepository.kt index 8eef79a6..562d0eda 100644 --- a/src/main/kotlin/roomescape/auth/infrastructure/persistence/LoginHistoryRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryRepository.kt @@ -1,4 +1,4 @@ -package roomescape.auth.infrastructure.persistence +package com.sangdol.roomescape.auth.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository diff --git a/src/main/kotlin/roomescape/auth/web/AuthController.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt similarity index 76% rename from src/main/kotlin/roomescape/auth/web/AuthController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt index 7e173128..d3325d2b 100644 --- a/src/main/kotlin/roomescape/auth/web/AuthController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt @@ -1,4 +1,4 @@ -package roomescape.auth.web +package com.sangdol.roomescape.auth.web import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse @@ -6,11 +6,11 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import roomescape.auth.business.AuthService -import roomescape.auth.docs.AuthAPI -import roomescape.auth.web.support.User -import roomescape.common.dto.CurrentUserContext -import roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.auth.business.AuthService +import com.sangdol.roomescape.auth.docs.AuthAPI +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.common.dto.response.CommonApiResponse @RestController @RequestMapping("/auth") diff --git a/src/main/kotlin/roomescape/auth/web/AuthDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthDTO.kt similarity index 82% rename from src/main/kotlin/roomescape/auth/web/AuthDTO.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthDTO.kt index a1622c26..51ee0da3 100644 --- a/src/main/kotlin/roomescape/auth/web/AuthDTO.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthDTO.kt @@ -1,8 +1,8 @@ -package roomescape.auth.web +package com.sangdol.roomescape.auth.web import jakarta.servlet.http.HttpServletRequest -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.common.dto.PrincipalType data class LoginContext( val ipAddress: String, diff --git a/src/main/kotlin/roomescape/auth/web/support/AuthAnnotations.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/AuthAnnotations.kt similarity index 71% rename from src/main/kotlin/roomescape/auth/web/support/AuthAnnotations.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/AuthAnnotations.kt index 712f0b5e..1351aaa5 100644 --- a/src/main/kotlin/roomescape/auth/web/support/AuthAnnotations.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/AuthAnnotations.kt @@ -1,7 +1,7 @@ -package roomescape.auth.web.support +package com.sangdol.roomescape.auth.web.support -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.admin.infrastructure.persistence.Privilege +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) diff --git a/src/main/kotlin/roomescape/auth/web/support/CookieUtils.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/CookieUtils.kt similarity index 85% rename from src/main/kotlin/roomescape/auth/web/support/CookieUtils.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/CookieUtils.kt index 4b0b2b71..2ef93547 100644 --- a/src/main/kotlin/roomescape/auth/web/support/CookieUtils.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/CookieUtils.kt @@ -1,4 +1,4 @@ -package roomescape.auth.web.support +package com.sangdol.roomescape.auth.web.support import jakarta.servlet.http.HttpServletRequest diff --git a/src/main/kotlin/roomescape/auth/web/support/interceptors/AdminInterceptor.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/AdminInterceptor.kt similarity index 83% rename from src/main/kotlin/roomescape/auth/web/support/interceptors/AdminInterceptor.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/AdminInterceptor.kt index 4b521df0..7a72a113 100644 --- a/src/main/kotlin/roomescape/auth/web/support/interceptors/AdminInterceptor.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/AdminInterceptor.kt @@ -1,4 +1,4 @@ -package roomescape.auth.web.support.interceptors +package com.sangdol.roomescape.auth.web.support.interceptors import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -7,17 +7,17 @@ import jakarta.servlet.http.HttpServletResponse import org.springframework.stereotype.Component import org.springframework.web.method.HandlerMethod import org.springframework.web.servlet.HandlerInterceptor -import roomescape.admin.infrastructure.persistence.AdminPermissionLevel -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.admin.infrastructure.persistence.Privilege -import roomescape.auth.business.CLAIM_ADMIN_TYPE_KEY -import roomescape.auth.business.CLAIM_PERMISSION_KEY -import roomescape.auth.exception.AuthErrorCode -import roomescape.auth.exception.AuthException -import roomescape.auth.infrastructure.jwt.JwtUtils -import roomescape.auth.web.support.AdminOnly -import roomescape.auth.web.support.accessToken -import roomescape.common.util.MdcPrincipalId +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege +import com.sangdol.roomescape.auth.business.CLAIM_ADMIN_TYPE_KEY +import com.sangdol.roomescape.auth.business.CLAIM_PERMISSION_KEY +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.auth.exception.AuthException +import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils +import com.sangdol.roomescape.auth.web.support.AdminOnly +import com.sangdol.roomescape.auth.web.support.accessToken +import com.sangdol.roomescape.common.util.MdcPrincipalId private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/auth/web/support/interceptors/UserInterceptor.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/UserInterceptor.kt similarity index 78% rename from src/main/kotlin/roomescape/auth/web/support/interceptors/UserInterceptor.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/UserInterceptor.kt index 4db11d08..aa581bc3 100644 --- a/src/main/kotlin/roomescape/auth/web/support/interceptors/UserInterceptor.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/UserInterceptor.kt @@ -1,4 +1,4 @@ -package roomescape.auth.web.support.interceptors +package com.sangdol.roomescape.auth.web.support.interceptors import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -7,13 +7,13 @@ import jakarta.servlet.http.HttpServletResponse import org.springframework.stereotype.Component import org.springframework.web.method.HandlerMethod import org.springframework.web.servlet.HandlerInterceptor -import roomescape.auth.business.CLAIM_ADMIN_TYPE_KEY -import roomescape.auth.exception.AuthErrorCode -import roomescape.auth.exception.AuthException -import roomescape.auth.infrastructure.jwt.JwtUtils -import roomescape.auth.web.support.UserOnly -import roomescape.auth.web.support.accessToken -import roomescape.common.util.MdcPrincipalId +import com.sangdol.roomescape.auth.business.CLAIM_ADMIN_TYPE_KEY +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.auth.exception.AuthException +import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils +import com.sangdol.roomescape.auth.web.support.UserOnly +import com.sangdol.roomescape.auth.web.support.accessToken +import com.sangdol.roomescape.common.util.MdcPrincipalId private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/auth/web/support/resolver/UserContextResolver.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/resolver/UserContextResolver.kt similarity index 79% rename from src/main/kotlin/roomescape/auth/web/support/resolver/UserContextResolver.kt rename to service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/resolver/UserContextResolver.kt index 7c731c5d..ea47d16b 100644 --- a/src/main/kotlin/roomescape/auth/web/support/resolver/UserContextResolver.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/resolver/UserContextResolver.kt @@ -1,4 +1,4 @@ -package roomescape.auth.web.support.resolver +package com.sangdol.roomescape.auth.web.support.resolver import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -9,12 +9,12 @@ import org.springframework.web.bind.support.WebDataBinderFactory import org.springframework.web.context.request.NativeWebRequest import org.springframework.web.method.support.HandlerMethodArgumentResolver import org.springframework.web.method.support.ModelAndViewContainer -import roomescape.auth.exception.AuthErrorCode -import roomescape.auth.exception.AuthException -import roomescape.auth.infrastructure.jwt.JwtUtils -import roomescape.auth.web.support.User -import roomescape.auth.web.support.accessToken -import roomescape.user.business.UserService +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.auth.exception.AuthException +import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.auth.web.support.accessToken +import com.sangdol.roomescape.user.business.UserService private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/common/config/JacksonConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/config/JacksonConfig.kt similarity index 98% rename from src/main/kotlin/roomescape/common/config/JacksonConfig.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/config/JacksonConfig.kt index 3550aed7..b5662a33 100644 --- a/src/main/kotlin/roomescape/common/config/JacksonConfig.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/config/JacksonConfig.kt @@ -1,4 +1,4 @@ -package roomescape.common.config +package com.sangdol.roomescape.common.config import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.DeserializationFeature diff --git a/src/main/kotlin/roomescape/common/config/JpaConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/config/JpaConfig.kt similarity index 83% rename from src/main/kotlin/roomescape/common/config/JpaConfig.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/config/JpaConfig.kt index 29a14094..a34c0251 100644 --- a/src/main/kotlin/roomescape/common/config/JpaConfig.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/config/JpaConfig.kt @@ -1,10 +1,10 @@ -package roomescape.common.config +package com.sangdol.roomescape.common.config import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.domain.AuditorAware import org.springframework.data.jpa.repository.config.EnableJpaAuditing -import roomescape.common.util.MdcPrincipalId +import com.sangdol.roomescape.common.util.MdcPrincipalId import java.util.* @Configuration diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/config/SwaggerConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/config/SwaggerConfig.kt new file mode 100644 index 00000000..509a0192 --- /dev/null +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/config/SwaggerConfig.kt @@ -0,0 +1,15 @@ +package com.sangdol.roomescape.common.config + +import io.swagger.v3.oas.models.OpenAPI +import io.swagger.v3.oas.models.info.Info +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class SwaggerConfig { + + @Bean + fun openAPI(): OpenAPI { + return OpenAPI() + } +} diff --git a/src/main/kotlin/roomescape/common/config/TsidConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/config/TsidConfig.kt similarity index 91% rename from src/main/kotlin/roomescape/common/config/TsidConfig.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/config/TsidConfig.kt index ea84f02c..5561e6b7 100644 --- a/src/main/kotlin/roomescape/common/config/TsidConfig.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/config/TsidConfig.kt @@ -1,4 +1,4 @@ -package roomescape.common.config +package com.sangdol.roomescape.common.config import com.github.f4b6a3.tsid.TsidFactory import org.springframework.beans.factory.annotation.Value diff --git a/src/main/kotlin/roomescape/common/config/WebMvcConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/config/WebMvcConfig.kt similarity index 75% rename from src/main/kotlin/roomescape/common/config/WebMvcConfig.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/config/WebMvcConfig.kt index 96eb747c..2bf615eb 100644 --- a/src/main/kotlin/roomescape/common/config/WebMvcConfig.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/config/WebMvcConfig.kt @@ -1,12 +1,12 @@ -package roomescape.common.config +package com.sangdol.roomescape.common.config import org.springframework.context.annotation.Configuration import org.springframework.web.method.support.HandlerMethodArgumentResolver import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer -import roomescape.auth.web.support.interceptors.AdminInterceptor -import roomescape.auth.web.support.interceptors.UserInterceptor -import roomescape.auth.web.support.resolver.UserContextResolver +import com.sangdol.roomescape.auth.web.support.interceptors.AdminInterceptor +import com.sangdol.roomescape.auth.web.support.interceptors.UserInterceptor +import com.sangdol.roomescape.auth.web.support.resolver.UserContextResolver @Configuration class WebMvcConfig( diff --git a/src/main/kotlin/roomescape/common/dto/AuditDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/AuditDto.kt similarity index 89% rename from src/main/kotlin/roomescape/common/dto/AuditDto.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/dto/AuditDto.kt index e8c956e2..e52fdbe3 100644 --- a/src/main/kotlin/roomescape/common/dto/AuditDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/AuditDto.kt @@ -1,4 +1,4 @@ -package roomescape.common.dto +package com.sangdol.roomescape.common.dto import java.time.LocalDateTime diff --git a/src/main/kotlin/roomescape/common/dto/CommonAuth.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/CommonAuth.kt similarity index 72% rename from src/main/kotlin/roomescape/common/dto/CommonAuth.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/dto/CommonAuth.kt index 3d037d55..e6ff00cf 100644 --- a/src/main/kotlin/roomescape/common/dto/CommonAuth.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/CommonAuth.kt @@ -1,12 +1,12 @@ -package roomescape.common.dto +package com.sangdol.roomescape.common.dto -import roomescape.admin.infrastructure.persistence.AdminEntity -import roomescape.admin.infrastructure.persistence.AdminPermissionLevel -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.auth.web.AdminLoginSuccessResponse -import roomescape.auth.web.LoginSuccessResponse -import roomescape.auth.web.UserLoginSuccessResponse -import roomescape.user.infrastructure.persistence.UserEntity +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.auth.web.AdminLoginSuccessResponse +import com.sangdol.roomescape.auth.web.LoginSuccessResponse +import com.sangdol.roomescape.auth.web.UserLoginSuccessResponse +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity abstract class LoginCredentials { diff --git a/src/main/kotlin/roomescape/common/dto/response/CommonApiResponse.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt similarity index 78% rename from src/main/kotlin/roomescape/common/dto/response/CommonApiResponse.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt index 6f625907..e52f0804 100644 --- a/src/main/kotlin/roomescape/common/dto/response/CommonApiResponse.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt @@ -1,7 +1,7 @@ -package roomescape.common.dto.response +package com.sangdol.roomescape.common.dto.response import com.fasterxml.jackson.annotation.JsonInclude -import roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.ErrorCode @JsonInclude(JsonInclude.Include.NON_NULL) data class CommonApiResponse( diff --git a/src/main/kotlin/roomescape/common/entity/BaseEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/entity/BaseEntity.kt similarity index 96% rename from src/main/kotlin/roomescape/common/entity/BaseEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/entity/BaseEntity.kt index 707d2249..2d76c19c 100644 --- a/src/main/kotlin/roomescape/common/entity/BaseEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/entity/BaseEntity.kt @@ -1,4 +1,4 @@ -package roomescape.common.entity +package com.sangdol.roomescape.common.entity import jakarta.persistence.* import org.springframework.data.annotation.CreatedBy diff --git a/src/main/kotlin/roomescape/common/exception/CommonErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/CommonErrorCode.kt similarity index 92% rename from src/main/kotlin/roomescape/common/exception/CommonErrorCode.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/exception/CommonErrorCode.kt index 5711c7af..67f34d86 100644 --- a/src/main/kotlin/roomescape/common/exception/CommonErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/CommonErrorCode.kt @@ -1,4 +1,4 @@ -package roomescape.common.exception +package com.sangdol.roomescape.common.exception import org.springframework.http.HttpStatus diff --git a/src/main/kotlin/roomescape/common/exception/ErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ErrorCode.kt similarity index 75% rename from src/main/kotlin/roomescape/common/exception/ErrorCode.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/exception/ErrorCode.kt index 1f08d3b5..7aa21c20 100644 --- a/src/main/kotlin/roomescape/common/exception/ErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ErrorCode.kt @@ -1,4 +1,4 @@ -package roomescape.common.exception +package com.sangdol.roomescape.common.exception import org.springframework.http.HttpStatus diff --git a/src/main/kotlin/roomescape/common/exception/ExceptionControllerAdvice.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt similarity index 91% rename from src/main/kotlin/roomescape/common/exception/ExceptionControllerAdvice.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt index b7f0d101..aff98040 100644 --- a/src/main/kotlin/roomescape/common/exception/ExceptionControllerAdvice.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt @@ -1,4 +1,4 @@ -package roomescape.common.exception +package com.sangdol.roomescape.common.exception import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -10,12 +10,12 @@ import org.springframework.http.converter.HttpMessageNotReadableException import org.springframework.web.bind.MethodArgumentNotValidException import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.bind.annotation.RestControllerAdvice -import roomescape.auth.exception.AuthException -import roomescape.common.dto.response.CommonErrorResponse -import roomescape.common.log.ApiLogMessageConverter -import roomescape.common.log.ConvertResponseMessageRequest -import roomescape.common.log.LogType -import roomescape.common.log.getEndpoint +import com.sangdol.roomescape.auth.exception.AuthException +import com.sangdol.roomescape.common.dto.response.CommonErrorResponse +import com.sangdol.roomescape.common.log.ApiLogMessageConverter +import com.sangdol.roomescape.common.log.ConvertResponseMessageRequest +import com.sangdol.roomescape.common.log.LogType +import com.sangdol.roomescape.common.log.getEndpoint private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/common/exception/RoomescapeException.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/RoomescapeException.kt similarity index 75% rename from src/main/kotlin/roomescape/common/exception/RoomescapeException.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/exception/RoomescapeException.kt index 4dc157ba..aeb024e5 100644 --- a/src/main/kotlin/roomescape/common/exception/RoomescapeException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/RoomescapeException.kt @@ -1,4 +1,4 @@ -package roomescape.common.exception +package com.sangdol.roomescape.common.exception open class RoomescapeException( open val errorCode: ErrorCode, diff --git a/src/main/kotlin/roomescape/common/log/ApiLogMessageConverter.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverter.kt similarity index 96% rename from src/main/kotlin/roomescape/common/log/ApiLogMessageConverter.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverter.kt index 8cf19eab..6ee68b21 100644 --- a/src/main/kotlin/roomescape/common/log/ApiLogMessageConverter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverter.kt @@ -1,8 +1,8 @@ -package roomescape.common.log +package com.sangdol.roomescape.common.log import com.fasterxml.jackson.databind.ObjectMapper import jakarta.servlet.http.HttpServletRequest -import roomescape.common.util.MdcPrincipalId +import com.sangdol.roomescape.common.util.MdcPrincipalId enum class LogType { INCOMING_HTTP_REQUEST, diff --git a/src/main/kotlin/roomescape/common/log/ControllerLoggingAspect.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/ControllerLoggingAspect.kt similarity index 96% rename from src/main/kotlin/roomescape/common/log/ControllerLoggingAspect.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/log/ControllerLoggingAspect.kt index a56c19de..c88b1abb 100644 --- a/src/main/kotlin/roomescape/common/log/ControllerLoggingAspect.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/ControllerLoggingAspect.kt @@ -1,4 +1,4 @@ -package roomescape.common.log +package com.sangdol.roomescape.common.log import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -24,7 +24,7 @@ class ControllerLoggingAspect( private val messageConverter: ApiLogMessageConverter, ) { - @Pointcut("execution(* roomescape..web..*Controller*.*(..))") + @Pointcut("execution(* com.sangdol.roomescape..web..*Controller*.*(..))") fun allController() { } diff --git a/src/main/kotlin/roomescape/common/log/HttpRequestLoggingFilter.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/HttpRequestLoggingFilter.kt similarity index 92% rename from src/main/kotlin/roomescape/common/log/HttpRequestLoggingFilter.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/log/HttpRequestLoggingFilter.kt index 70442684..8fc8670a 100644 --- a/src/main/kotlin/roomescape/common/log/HttpRequestLoggingFilter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/HttpRequestLoggingFilter.kt @@ -1,4 +1,4 @@ -package roomescape.common.log +package com.sangdol.roomescape.common.log import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -9,7 +9,7 @@ import org.slf4j.MDC import org.springframework.web.filter.OncePerRequestFilter import org.springframework.web.util.ContentCachingRequestWrapper import org.springframework.web.util.ContentCachingResponseWrapper -import roomescape.common.util.MdcPrincipalId +import com.sangdol.roomescape.common.util.MdcPrincipalId private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/common/log/LogConfiguration.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/LogConfiguration.kt similarity index 96% rename from src/main/kotlin/roomescape/common/log/LogConfiguration.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/log/LogConfiguration.kt index 5905b7c6..bf20b06e 100644 --- a/src/main/kotlin/roomescape/common/log/LogConfiguration.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/LogConfiguration.kt @@ -1,4 +1,4 @@ -package roomescape.common.log +package com.sangdol.roomescape.common.log import com.fasterxml.jackson.databind.ObjectMapper import org.springframework.boot.web.servlet.FilterRegistrationBean diff --git a/src/main/kotlin/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParams.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParams.kt similarity index 96% rename from src/main/kotlin/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParams.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParams.kt index bbc44024..a6e979c5 100644 --- a/src/main/kotlin/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParams.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParams.kt @@ -1,4 +1,4 @@ -package roomescape.common.log +package com.sangdol.roomescape.common.log import net.ttddyy.dsproxy.ExecutionInfo import net.ttddyy.dsproxy.QueryInfo diff --git a/src/main/kotlin/roomescape/common/log/ProxyDataSourceConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/ProxyDataSourceConfig.kt similarity index 97% rename from src/main/kotlin/roomescape/common/log/ProxyDataSourceConfig.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/log/ProxyDataSourceConfig.kt index b0300f54..e6c7f117 100644 --- a/src/main/kotlin/roomescape/common/log/ProxyDataSourceConfig.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/ProxyDataSourceConfig.kt @@ -1,4 +1,4 @@ -package roomescape.common.log +package com.sangdol.roomescape.common.log import com.zaxxer.hikari.HikariDataSource import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel diff --git a/src/main/kotlin/roomescape/common/log/RoomescapeLogMaskingConverter.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt similarity index 96% rename from src/main/kotlin/roomescape/common/log/RoomescapeLogMaskingConverter.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt index 104eeba9..714cb70b 100644 --- a/src/main/kotlin/roomescape/common/log/RoomescapeLogMaskingConverter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt @@ -1,4 +1,4 @@ -package roomescape.common.log +package com.sangdol.roomescape.common.log import ch.qos.logback.classic.pattern.MessageConverter import ch.qos.logback.classic.spi.ILoggingEvent @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.ArrayNode import com.fasterxml.jackson.databind.node.ObjectNode import com.fasterxml.jackson.databind.node.TextNode -import roomescape.common.config.JacksonConfig +import com.sangdol.roomescape.common.config.JacksonConfig private const val MASK: String = "****" private val SENSITIVE_KEYS = setOf("password", "accessToken", "phone") diff --git a/src/main/kotlin/roomescape/common/util/DateUtils.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/util/DateUtils.kt similarity index 86% rename from src/main/kotlin/roomescape/common/util/DateUtils.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/util/DateUtils.kt index 4ad16583..cf2b2ebf 100644 --- a/src/main/kotlin/roomescape/common/util/DateUtils.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/util/DateUtils.kt @@ -1,4 +1,4 @@ -package roomescape.common.util +package com.sangdol.roomescape.common.util import java.time.DayOfWeek import java.time.LocalDate diff --git a/src/main/kotlin/roomescape/common/util/MDCUtils.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/util/MDCUtils.kt similarity index 92% rename from src/main/kotlin/roomescape/common/util/MDCUtils.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/util/MDCUtils.kt index 6283c605..8aa50781 100644 --- a/src/main/kotlin/roomescape/common/util/MDCUtils.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/util/MDCUtils.kt @@ -1,4 +1,4 @@ -package roomescape.common.util +package com.sangdol.roomescape.common.util import org.slf4j.MDC import java.util.* diff --git a/src/main/kotlin/roomescape/common/util/TransactionExecutionUtil.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/util/TransactionExecutionUtil.kt similarity index 86% rename from src/main/kotlin/roomescape/common/util/TransactionExecutionUtil.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/util/TransactionExecutionUtil.kt index 28a980e5..d4428190 100644 --- a/src/main/kotlin/roomescape/common/util/TransactionExecutionUtil.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/util/TransactionExecutionUtil.kt @@ -1,4 +1,4 @@ -package roomescape.common.util +package com.sangdol.roomescape.common.util import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging @@ -6,8 +6,8 @@ import org.springframework.stereotype.Component import org.springframework.transaction.PlatformTransactionManager import org.springframework.transaction.TransactionDefinition import org.springframework.transaction.support.TransactionTemplate -import roomescape.common.exception.CommonErrorCode -import roomescape.common.exception.RoomescapeException +import com.sangdol.roomescape.common.exception.CommonErrorCode +import com.sangdol.roomescape.common.exception.RoomescapeException private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/payment/business/PaymentService.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt similarity index 90% rename from src/main/kotlin/roomescape/payment/business/PaymentService.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt index 2d483898..126be6b0 100644 --- a/src/main/kotlin/roomescape/payment/business/PaymentService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt @@ -1,17 +1,17 @@ -package roomescape.payment.business +package com.sangdol.roomescape.payment.business import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import roomescape.common.util.TransactionExecutionUtil -import roomescape.payment.exception.PaymentErrorCode -import roomescape.payment.exception.PaymentException -import roomescape.payment.infrastructure.client.PaymentClientCancelResponse -import roomescape.payment.infrastructure.client.PaymentClientConfirmResponse -import roomescape.payment.infrastructure.client.TosspayClient -import roomescape.payment.infrastructure.persistence.* -import roomescape.payment.web.* +import com.sangdol.roomescape.common.util.TransactionExecutionUtil +import com.sangdol.roomescape.payment.exception.PaymentErrorCode +import com.sangdol.roomescape.payment.exception.PaymentException +import com.sangdol.roomescape.payment.infrastructure.client.PaymentClientCancelResponse +import com.sangdol.roomescape.payment.infrastructure.client.PaymentClientConfirmResponse +import com.sangdol.roomescape.payment.infrastructure.client.TosspayClient +import com.sangdol.roomescape.payment.infrastructure.persistence.* +import com.sangdol.roomescape.payment.web.* private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/payment/business/PaymentWriter.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentWriter.kt similarity index 85% rename from src/main/kotlin/roomescape/payment/business/PaymentWriter.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentWriter.kt index 60a7473f..16ff6d59 100644 --- a/src/main/kotlin/roomescape/payment/business/PaymentWriter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentWriter.kt @@ -1,16 +1,16 @@ -package roomescape.payment.business +package com.sangdol.roomescape.payment.business import com.github.f4b6a3.tsid.TsidFactory import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Component -import roomescape.common.config.next -import roomescape.payment.exception.PaymentErrorCode -import roomescape.payment.exception.PaymentException -import roomescape.payment.infrastructure.client.* -import roomescape.payment.infrastructure.common.PaymentMethod -import roomescape.payment.infrastructure.common.PaymentType -import roomescape.payment.infrastructure.persistence.* +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.payment.exception.PaymentErrorCode +import com.sangdol.roomescape.payment.exception.PaymentException +import com.sangdol.roomescape.payment.infrastructure.client.* +import com.sangdol.roomescape.payment.infrastructure.common.PaymentMethod +import com.sangdol.roomescape.payment.infrastructure.common.PaymentType +import com.sangdol.roomescape.payment.infrastructure.persistence.* import java.time.LocalDateTime private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/payment/docs/PaymentAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt similarity index 68% rename from src/main/kotlin/roomescape/payment/docs/PaymentAPI.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt index 708cec30..1e566ee1 100644 --- a/src/main/kotlin/roomescape/payment/docs/PaymentAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt @@ -1,4 +1,4 @@ -package roomescape.payment.docs +package com.sangdol.roomescape.payment.docs import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse @@ -7,13 +7,13 @@ import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestParam -import roomescape.auth.web.support.User -import roomescape.auth.web.support.UserOnly -import roomescape.common.dto.CurrentUserContext -import roomescape.common.dto.response.CommonApiResponse -import roomescape.payment.web.PaymentCancelRequest -import roomescape.payment.web.PaymentConfirmRequest -import roomescape.payment.web.PaymentCreateResponse +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.auth.web.support.UserOnly +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.payment.web.PaymentCancelRequest +import com.sangdol.roomescape.payment.web.PaymentConfirmRequest +import com.sangdol.roomescape.payment.web.PaymentCreateResponse interface PaymentAPI { diff --git a/src/main/kotlin/roomescape/payment/exception/PaymentErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentErrorCode.kt similarity index 92% rename from src/main/kotlin/roomescape/payment/exception/PaymentErrorCode.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentErrorCode.kt index 77188954..2c2676e1 100644 --- a/src/main/kotlin/roomescape/payment/exception/PaymentErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentErrorCode.kt @@ -1,7 +1,7 @@ -package roomescape.payment.exception +package com.sangdol.roomescape.payment.exception import org.springframework.http.HttpStatus -import roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.ErrorCode enum class PaymentErrorCode( override val httpStatus: HttpStatus, diff --git a/src/main/kotlin/roomescape/payment/exception/PaymentException.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentException.kt similarity index 59% rename from src/main/kotlin/roomescape/payment/exception/PaymentException.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentException.kt index 20ad4208..7bb10717 100644 --- a/src/main/kotlin/roomescape/payment/exception/PaymentException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentException.kt @@ -1,6 +1,6 @@ -package roomescape.payment.exception +package com.sangdol.roomescape.payment.exception -import roomescape.common.exception.RoomescapeException +import com.sangdol.roomescape.common.exception.RoomescapeException class PaymentException( override val errorCode: PaymentErrorCode, diff --git a/src/main/kotlin/roomescape/payment/infrastructure/client/PaymentConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/PaymentConfig.kt similarity index 96% rename from src/main/kotlin/roomescape/payment/infrastructure/client/PaymentConfig.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/PaymentConfig.kt index ea8e2b18..231f6624 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/client/PaymentConfig.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/PaymentConfig.kt @@ -1,4 +1,4 @@ -package roomescape.payment.infrastructure.client +package com.sangdol.roomescape.payment.infrastructure.client import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder diff --git a/src/main/kotlin/roomescape/payment/infrastructure/client/PaymentProperties.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/PaymentProperties.kt similarity index 81% rename from src/main/kotlin/roomescape/payment/infrastructure/client/PaymentProperties.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/PaymentProperties.kt index 4b1873b8..52f8c729 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/client/PaymentProperties.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/PaymentProperties.kt @@ -1,4 +1,4 @@ -package roomescape.payment.infrastructure.client +package com.sangdol.roomescape.payment.infrastructure.client import org.springframework.boot.context.properties.ConfigurationProperties diff --git a/src/main/kotlin/roomescape/payment/infrastructure/client/TosspayCancelDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayCancelDTO.kt similarity index 90% rename from src/main/kotlin/roomescape/payment/infrastructure/client/TosspayCancelDTO.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayCancelDTO.kt index 551e7ae5..a4077b27 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/client/TosspayCancelDTO.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayCancelDTO.kt @@ -1,11 +1,11 @@ -package roomescape.payment.infrastructure.client +package com.sangdol.roomescape.payment.infrastructure.client import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import roomescape.payment.infrastructure.common.PaymentStatus -import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity +import com.sangdol.roomescape.payment.infrastructure.common.PaymentStatus +import com.sangdol.roomescape.payment.infrastructure.persistence.CanceledPaymentEntity import java.time.LocalDateTime import java.time.OffsetDateTime diff --git a/src/main/kotlin/roomescape/payment/infrastructure/client/TosspayClient.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayClient.kt similarity index 96% rename from src/main/kotlin/roomescape/payment/infrastructure/client/TosspayClient.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayClient.kt index 1a1fdb86..53c56a59 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/client/TosspayClient.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayClient.kt @@ -1,4 +1,4 @@ -package roomescape.payment.infrastructure.client +package com.sangdol.roomescape.payment.infrastructure.client import com.fasterxml.jackson.databind.ObjectMapper import io.github.oshai.kotlinlogging.KLogger @@ -10,8 +10,8 @@ import org.springframework.http.client.ClientHttpResponse import org.springframework.stereotype.Component import org.springframework.web.client.ResponseErrorHandler import org.springframework.web.client.RestClient -import roomescape.payment.exception.PaymentErrorCode -import roomescape.payment.exception.PaymentException +import com.sangdol.roomescape.payment.exception.PaymentErrorCode +import com.sangdol.roomescape.payment.exception.PaymentException import java.net.URI private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/payment/infrastructure/client/TosspayConfirmDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayConfirmDTO.kt similarity index 84% rename from src/main/kotlin/roomescape/payment/infrastructure/client/TosspayConfirmDTO.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayConfirmDTO.kt index ebe0ef20..ebcf0719 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/client/TosspayConfirmDTO.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayConfirmDTO.kt @@ -1,12 +1,12 @@ -package roomescape.payment.infrastructure.client +package com.sangdol.roomescape.payment.infrastructure.client -import roomescape.payment.exception.PaymentErrorCode -import roomescape.payment.exception.PaymentException -import roomescape.payment.infrastructure.common.* -import roomescape.payment.infrastructure.persistence.PaymentBankTransferDetailEntity -import roomescape.payment.infrastructure.persistence.PaymentCardDetailEntity -import roomescape.payment.infrastructure.persistence.PaymentEasypayPrepaidDetailEntity -import roomescape.payment.infrastructure.persistence.PaymentEntity +import com.sangdol.roomescape.payment.exception.PaymentErrorCode +import com.sangdol.roomescape.payment.exception.PaymentException +import com.sangdol.roomescape.payment.infrastructure.common.* +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentBankTransferDetailEntity +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentCardDetailEntity +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentEasypayPrepaidDetailEntity +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentEntity import java.time.OffsetDateTime data class PaymentClientConfirmResponse( diff --git a/src/main/kotlin/roomescape/payment/infrastructure/client/TosspayErrorResponse.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayErrorResponse.kt similarity index 57% rename from src/main/kotlin/roomescape/payment/infrastructure/client/TosspayErrorResponse.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayErrorResponse.kt index fbb20ccc..7a504395 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/client/TosspayErrorResponse.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayErrorResponse.kt @@ -1,4 +1,4 @@ -package roomescape.payment.infrastructure.client +package com.sangdol.roomescape.payment.infrastructure.client data class TosspayErrorResponse( val code: String, diff --git a/src/main/kotlin/roomescape/payment/infrastructure/common/PaymentTypes.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/common/PaymentTypes.kt similarity index 97% rename from src/main/kotlin/roomescape/payment/infrastructure/common/PaymentTypes.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/common/PaymentTypes.kt index 6ecabb80..44ef401f 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/common/PaymentTypes.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/common/PaymentTypes.kt @@ -1,10 +1,10 @@ -package roomescape.payment.infrastructure.common +package com.sangdol.roomescape.payment.infrastructure.common import com.fasterxml.jackson.annotation.JsonCreator import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging -import roomescape.payment.exception.PaymentErrorCode -import roomescape.payment.exception.PaymentException +import com.sangdol.roomescape.payment.exception.PaymentErrorCode +import com.sangdol.roomescape.payment.exception.PaymentException private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt similarity index 80% rename from src/main/kotlin/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt index f9085e7c..b5a92c3d 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt @@ -1,8 +1,8 @@ -package roomescape.payment.infrastructure.persistence +package com.sangdol.roomescape.payment.infrastructure.persistence import jakarta.persistence.Entity import jakarta.persistence.Table -import roomescape.common.entity.PersistableBaseEntity +import com.sangdol.roomescape.common.entity.PersistableBaseEntity import java.time.LocalDateTime import java.time.OffsetDateTime diff --git a/src/main/kotlin/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt similarity index 76% rename from src/main/kotlin/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt index 75028118..720ed28f 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt @@ -1,4 +1,4 @@ -package roomescape.payment.infrastructure.persistence +package com.sangdol.roomescape.payment.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository diff --git a/src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt similarity index 89% rename from src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt index 807aaaf3..3039d052 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt @@ -1,8 +1,8 @@ -package roomescape.payment.infrastructure.persistence +package com.sangdol.roomescape.payment.infrastructure.persistence import jakarta.persistence.* -import roomescape.common.entity.PersistableBaseEntity -import roomescape.payment.infrastructure.common.* +import com.sangdol.roomescape.common.entity.PersistableBaseEntity +import com.sangdol.roomescape.payment.infrastructure.common.* @Entity @Table(name = "payment_detail") diff --git a/src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentDetailRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentDetailRepository.kt similarity index 75% rename from src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentDetailRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentDetailRepository.kt index 0efd93ae..a53d5d15 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentDetailRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentDetailRepository.kt @@ -1,4 +1,4 @@ -package roomescape.payment.infrastructure.persistence +package com.sangdol.roomescape.payment.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository diff --git a/src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentEntity.kt similarity index 67% rename from src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentEntity.kt index 748dad84..969dc52b 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentEntity.kt @@ -1,13 +1,13 @@ -package roomescape.payment.infrastructure.persistence +package com.sangdol.roomescape.payment.infrastructure.persistence import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated import jakarta.persistence.Table -import roomescape.common.entity.PersistableBaseEntity -import roomescape.payment.infrastructure.common.PaymentMethod -import roomescape.payment.infrastructure.common.PaymentStatus -import roomescape.payment.infrastructure.common.PaymentType +import com.sangdol.roomescape.common.entity.PersistableBaseEntity +import com.sangdol.roomescape.payment.infrastructure.common.PaymentMethod +import com.sangdol.roomescape.payment.infrastructure.common.PaymentStatus +import com.sangdol.roomescape.payment.infrastructure.common.PaymentType import java.time.OffsetDateTime @Entity diff --git a/src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentRepository.kt similarity index 75% rename from src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentRepository.kt index 9b872d25..d90002f3 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/persistence/PaymentRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentRepository.kt @@ -1,4 +1,4 @@ -package roomescape.payment.infrastructure.persistence +package com.sangdol.roomescape.payment.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository diff --git a/src/main/kotlin/roomescape/payment/web/PaymentController.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt similarity index 73% rename from src/main/kotlin/roomescape/payment/web/PaymentController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt index 82082159..406c872a 100644 --- a/src/main/kotlin/roomescape/payment/web/PaymentController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt @@ -1,13 +1,13 @@ -package roomescape.payment.web +package com.sangdol.roomescape.payment.web import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import roomescape.auth.web.support.User -import roomescape.common.dto.CurrentUserContext -import roomescape.common.dto.response.CommonApiResponse -import roomescape.payment.business.PaymentService -import roomescape.payment.docs.PaymentAPI +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.payment.business.PaymentService +import com.sangdol.roomescape.payment.docs.PaymentAPI @RestController @RequestMapping("/payments") diff --git a/src/main/kotlin/roomescape/payment/web/PaymentDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentDTO.kt similarity index 89% rename from src/main/kotlin/roomescape/payment/web/PaymentDTO.kt rename to service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentDTO.kt index 11a6af92..1e39f186 100644 --- a/src/main/kotlin/roomescape/payment/web/PaymentDTO.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentDTO.kt @@ -1,11 +1,11 @@ -package roomescape.payment.web +package com.sangdol.roomescape.payment.web -import roomescape.payment.exception.PaymentErrorCode -import roomescape.payment.exception.PaymentException -import roomescape.payment.infrastructure.common.PaymentStatus -import roomescape.payment.infrastructure.common.PaymentType -import roomescape.payment.infrastructure.persistence.* -import roomescape.payment.web.PaymentDetailResponse.* +import com.sangdol.roomescape.payment.exception.PaymentErrorCode +import com.sangdol.roomescape.payment.exception.PaymentException +import com.sangdol.roomescape.payment.infrastructure.common.PaymentStatus +import com.sangdol.roomescape.payment.infrastructure.common.PaymentType +import com.sangdol.roomescape.payment.infrastructure.persistence.* +import com.sangdol.roomescape.payment.web.PaymentDetailResponse.* import java.time.LocalDateTime import java.time.OffsetDateTime diff --git a/src/main/kotlin/roomescape/region/business/RegionService.kt b/service/src/main/kotlin/com/sangdol/roomescape/region/business/RegionService.kt similarity index 91% rename from src/main/kotlin/roomescape/region/business/RegionService.kt rename to service/src/main/kotlin/com/sangdol/roomescape/region/business/RegionService.kt index 25966644..de6f1111 100644 --- a/src/main/kotlin/roomescape/region/business/RegionService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/region/business/RegionService.kt @@ -1,13 +1,13 @@ -package roomescape.region.business +package com.sangdol.roomescape.region.business import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import roomescape.region.exception.RegionErrorCode -import roomescape.region.exception.RegionException -import roomescape.region.infrastructure.persistence.RegionRepository -import roomescape.region.web.* +import com.sangdol.roomescape.region.exception.RegionErrorCode +import com.sangdol.roomescape.region.exception.RegionException +import com.sangdol.roomescape.region.infrastructure.persistence.RegionRepository +import com.sangdol.roomescape.region.web.* private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/region/docs/RegionAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/region/docs/RegionAPI.kt similarity index 78% rename from src/main/kotlin/roomescape/region/docs/RegionAPI.kt rename to service/src/main/kotlin/com/sangdol/roomescape/region/docs/RegionAPI.kt index b3486732..ce5a2cd9 100644 --- a/src/main/kotlin/roomescape/region/docs/RegionAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/region/docs/RegionAPI.kt @@ -1,15 +1,15 @@ -package roomescape.region.docs +package com.sangdol.roomescape.region.docs import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RequestParam -import roomescape.auth.web.support.Public -import roomescape.common.dto.response.CommonApiResponse -import roomescape.region.web.RegionCodeResponse -import roomescape.region.web.SidoListResponse -import roomescape.region.web.SigunguListResponse +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.region.web.RegionCodeResponse +import com.sangdol.roomescape.region.web.SidoListResponse +import com.sangdol.roomescape.region.web.SigunguListResponse interface RegionAPI { diff --git a/src/main/kotlin/roomescape/region/exception/RegionException.kt b/service/src/main/kotlin/com/sangdol/roomescape/region/exception/RegionException.kt similarity index 81% rename from src/main/kotlin/roomescape/region/exception/RegionException.kt rename to service/src/main/kotlin/com/sangdol/roomescape/region/exception/RegionException.kt index 9bee2e9a..41168e50 100644 --- a/src/main/kotlin/roomescape/region/exception/RegionException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/region/exception/RegionException.kt @@ -1,8 +1,8 @@ -package roomescape.region.exception +package com.sangdol.roomescape.region.exception import org.springframework.http.HttpStatus -import roomescape.common.exception.ErrorCode -import roomescape.common.exception.RoomescapeException +import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.RoomescapeException class RegionException( override val errorCode: RegionErrorCode, diff --git a/src/main/kotlin/roomescape/region/infrastructure/persistence/RegionEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/region/infrastructure/persistence/RegionEntity.kt similarity index 86% rename from src/main/kotlin/roomescape/region/infrastructure/persistence/RegionEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/region/infrastructure/persistence/RegionEntity.kt index 00d967a4..3b61291d 100644 --- a/src/main/kotlin/roomescape/region/infrastructure/persistence/RegionEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/region/infrastructure/persistence/RegionEntity.kt @@ -1,4 +1,4 @@ -package roomescape.region.infrastructure.persistence +package com.sangdol.roomescape.region.infrastructure.persistence import jakarta.persistence.Entity import jakarta.persistence.Id diff --git a/src/main/kotlin/roomescape/region/infrastructure/persistence/RegionRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/region/infrastructure/persistence/RegionRepository.kt similarity index 95% rename from src/main/kotlin/roomescape/region/infrastructure/persistence/RegionRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/region/infrastructure/persistence/RegionRepository.kt index 03b1f585..d9baa855 100644 --- a/src/main/kotlin/roomescape/region/infrastructure/persistence/RegionRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/region/infrastructure/persistence/RegionRepository.kt @@ -1,4 +1,4 @@ -package roomescape.region.infrastructure.persistence +package com.sangdol.roomescape.region.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query diff --git a/src/main/kotlin/roomescape/region/web/RegionController.kt b/service/src/main/kotlin/com/sangdol/roomescape/region/web/RegionController.kt similarity index 86% rename from src/main/kotlin/roomescape/region/web/RegionController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/region/web/RegionController.kt index ea4d50c0..d8c6b1e2 100644 --- a/src/main/kotlin/roomescape/region/web/RegionController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/region/web/RegionController.kt @@ -1,13 +1,13 @@ -package roomescape.region.web +package com.sangdol.roomescape.region.web import org.springframework.http.ResponseEntity 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 -import roomescape.common.dto.response.CommonApiResponse -import roomescape.region.business.RegionService -import roomescape.region.docs.RegionAPI +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.region.business.RegionService +import com.sangdol.roomescape.region.docs.RegionAPI @RestController @RequestMapping("/regions") diff --git a/src/main/kotlin/roomescape/region/web/RegionDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/region/web/RegionDTO.kt similarity index 91% rename from src/main/kotlin/roomescape/region/web/RegionDTO.kt rename to service/src/main/kotlin/com/sangdol/roomescape/region/web/RegionDTO.kt index dee2523a..eccfb75a 100644 --- a/src/main/kotlin/roomescape/region/web/RegionDTO.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/region/web/RegionDTO.kt @@ -1,4 +1,4 @@ -package roomescape.region.web +package com.sangdol.roomescape.region.web data class SidoResponse( val code: String, diff --git a/src/main/kotlin/roomescape/reservation/business/ReservationService.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt similarity index 86% rename from src/main/kotlin/roomescape/reservation/business/ReservationService.kt rename to service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt index 346ac043..4f7c1fd0 100644 --- a/src/main/kotlin/roomescape/reservation/business/ReservationService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt @@ -1,4 +1,4 @@ -package roomescape.reservation.business +package com.sangdol.roomescape.reservation.business import com.github.f4b6a3.tsid.TsidFactory import io.github.oshai.kotlinlogging.KLogger @@ -6,23 +6,21 @@ import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import roomescape.common.config.next -import roomescape.common.dto.CurrentUserContext -import roomescape.common.util.DateUtils -import roomescape.payment.business.PaymentService -import roomescape.payment.web.PaymentWithDetailResponse -import roomescape.reservation.exception.ReservationErrorCode -import roomescape.reservation.exception.ReservationException -import roomescape.reservation.infrastructure.persistence.* -import roomescape.reservation.web.* -import roomescape.schedule.business.ScheduleService -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.schedule.web.ScheduleOverviewResponse -import roomescape.schedule.web.ScheduleUpdateRequest -import roomescape.theme.business.ThemeService -import roomescape.user.business.UserService -import roomescape.user.web.UserContactResponse -import java.time.LocalDate +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.payment.business.PaymentService +import com.sangdol.roomescape.payment.web.PaymentWithDetailResponse +import com.sangdol.roomescape.reservation.exception.ReservationErrorCode +import com.sangdol.roomescape.reservation.exception.ReservationException +import com.sangdol.roomescape.reservation.infrastructure.persistence.* +import com.sangdol.roomescape.reservation.web.* +import com.sangdol.roomescape.schedule.business.ScheduleService +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.schedule.web.ScheduleOverviewResponse +import com.sangdol.roomescape.schedule.web.ScheduleUpdateRequest +import com.sangdol.roomescape.theme.business.ThemeService +import com.sangdol.roomescape.user.business.UserService +import com.sangdol.roomescape.user.web.UserContactResponse import java.time.LocalDateTime private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/reservation/business/ReservationValidator.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt similarity index 74% rename from src/main/kotlin/roomescape/reservation/business/ReservationValidator.kt rename to service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt index 9263adfa..28fdc5d6 100644 --- a/src/main/kotlin/roomescape/reservation/business/ReservationValidator.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationValidator.kt @@ -1,14 +1,14 @@ -package roomescape.reservation.business +package com.sangdol.roomescape.reservation.business import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Component -import roomescape.reservation.exception.ReservationErrorCode -import roomescape.reservation.exception.ReservationException -import roomescape.reservation.web.PendingReservationCreateRequest -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.schedule.web.ScheduleSummaryResponse -import roomescape.theme.web.ThemeInfoResponse +import com.sangdol.roomescape.reservation.exception.ReservationErrorCode +import com.sangdol.roomescape.reservation.exception.ReservationException +import com.sangdol.roomescape.reservation.web.PendingReservationCreateRequest +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.schedule.web.ScheduleSummaryResponse +import com.sangdol.roomescape.theme.web.ThemeInfoResponse private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/reservation/docs/ReservationAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt similarity index 83% rename from src/main/kotlin/roomescape/reservation/docs/ReservationAPI.kt rename to service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt index 7481e3e4..104559dd 100644 --- a/src/main/kotlin/roomescape/reservation/docs/ReservationAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt @@ -1,4 +1,4 @@ -package roomescape.reservation.docs +package com.sangdol.roomescape.reservation.docs import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse @@ -7,13 +7,11 @@ import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody -import org.springframework.web.bind.annotation.RequestParam -import roomescape.auth.web.support.Public -import roomescape.auth.web.support.User -import roomescape.auth.web.support.UserOnly -import roomescape.common.dto.CurrentUserContext -import roomescape.common.dto.response.CommonApiResponse -import roomescape.reservation.web.* +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.auth.web.support.UserOnly +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.reservation.web.* interface ReservationAPI { @Operation(summary = "결제 전 임시 예약 저장") diff --git a/src/main/kotlin/roomescape/reservation/exception/ReservationErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationErrorCode.kt similarity index 87% rename from src/main/kotlin/roomescape/reservation/exception/ReservationErrorCode.kt rename to service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationErrorCode.kt index 942b9984..9b502a46 100644 --- a/src/main/kotlin/roomescape/reservation/exception/ReservationErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationErrorCode.kt @@ -1,7 +1,7 @@ -package roomescape.reservation.exception +package com.sangdol.roomescape.reservation.exception import org.springframework.http.HttpStatus -import roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.ErrorCode enum class ReservationErrorCode( override val httpStatus: HttpStatus, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationException.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationException.kt new file mode 100644 index 00000000..3a25e7d2 --- /dev/null +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationException.kt @@ -0,0 +1,9 @@ +package com.sangdol.roomescape.reservation.exception + +import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.RoomescapeException + +class ReservationException( + override val errorCode: ErrorCode, + override val message: String = errorCode.message +) : RoomescapeException(errorCode, message) diff --git a/src/main/kotlin/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt similarity index 80% rename from src/main/kotlin/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt index 84ac475e..ae71243f 100644 --- a/src/main/kotlin/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt @@ -1,10 +1,10 @@ -package roomescape.reservation.infrastructure.persistence +package com.sangdol.roomescape.reservation.infrastructure.persistence import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated import jakarta.persistence.Table -import roomescape.common.entity.PersistableBaseEntity +import com.sangdol.roomescape.common.entity.PersistableBaseEntity import java.time.LocalDateTime @Entity diff --git a/src/main/kotlin/roomescape/reservation/infrastructure/persistence/CanceledReservationRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/CanceledReservationRepository.kt similarity index 68% rename from src/main/kotlin/roomescape/reservation/infrastructure/persistence/CanceledReservationRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/CanceledReservationRepository.kt index 5508bc11..2d1063c9 100644 --- a/src/main/kotlin/roomescape/reservation/infrastructure/persistence/CanceledReservationRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/CanceledReservationRepository.kt @@ -1,4 +1,4 @@ -package roomescape.reservation.infrastructure.persistence +package com.sangdol.roomescape.reservation.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository diff --git a/src/main/kotlin/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt similarity index 84% rename from src/main/kotlin/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt index dfd87710..99fe9890 100644 --- a/src/main/kotlin/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt @@ -1,10 +1,10 @@ -package roomescape.reservation.infrastructure.persistence +package com.sangdol.roomescape.reservation.infrastructure.persistence import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated import jakarta.persistence.Table -import roomescape.common.entity.AuditingBaseEntity +import com.sangdol.roomescape.common.entity.AuditingBaseEntity @Entity @Table(name = "reservation") diff --git a/src/main/kotlin/roomescape/reservation/infrastructure/persistence/ReservationRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/ReservationRepository.kt similarity index 78% rename from src/main/kotlin/roomescape/reservation/infrastructure/persistence/ReservationRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/ReservationRepository.kt index af90de13..e9dbdcde 100644 --- a/src/main/kotlin/roomescape/reservation/infrastructure/persistence/ReservationRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/ReservationRepository.kt @@ -1,4 +1,4 @@ -package roomescape.reservation.infrastructure.persistence +package com.sangdol.roomescape.reservation.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository diff --git a/src/main/kotlin/roomescape/reservation/web/ReservationController.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt similarity index 84% rename from src/main/kotlin/roomescape/reservation/web/ReservationController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt index 15105419..cc3c5642 100644 --- a/src/main/kotlin/roomescape/reservation/web/ReservationController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt @@ -1,13 +1,13 @@ -package roomescape.reservation.web +package com.sangdol.roomescape.reservation.web import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import roomescape.auth.web.support.User -import roomescape.common.dto.CurrentUserContext -import roomescape.common.dto.response.CommonApiResponse -import roomescape.reservation.business.ReservationService -import roomescape.reservation.docs.ReservationAPI +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.reservation.business.ReservationService +import com.sangdol.roomescape.reservation.docs.ReservationAPI @RestController @RequestMapping("/reservations") diff --git a/src/main/kotlin/roomescape/reservation/web/ReservationDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt similarity index 85% rename from src/main/kotlin/roomescape/reservation/web/ReservationDto.kt rename to service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt index 7990de1e..21b0ff88 100644 --- a/src/main/kotlin/roomescape/reservation/web/ReservationDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt @@ -1,11 +1,11 @@ -package roomescape.reservation.web +package com.sangdol.roomescape.reservation.web import jakarta.validation.constraints.NotEmpty -import roomescape.payment.web.PaymentWithDetailResponse -import roomescape.reservation.infrastructure.persistence.ReservationEntity -import roomescape.reservation.infrastructure.persistence.ReservationStatus -import roomescape.schedule.web.ScheduleOverviewResponse -import roomescape.user.web.UserContactResponse +import com.sangdol.roomescape.payment.web.PaymentWithDetailResponse +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationEntity +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus +import com.sangdol.roomescape.schedule.web.ScheduleOverviewResponse +import com.sangdol.roomescape.user.web.UserContactResponse import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime diff --git a/src/main/kotlin/roomescape/schedule/business/ScheduleService.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt similarity index 90% rename from src/main/kotlin/roomescape/schedule/business/ScheduleService.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt index e72f7c6d..8195a8eb 100644 --- a/src/main/kotlin/roomescape/schedule/business/ScheduleService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt @@ -1,4 +1,4 @@ -package roomescape.schedule.business +package com.sangdol.roomescape.schedule.business import ScheduleException import com.github.f4b6a3.tsid.TsidFactory @@ -7,17 +7,17 @@ import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import roomescape.admin.business.AdminService -import roomescape.common.config.next -import roomescape.common.dto.AuditInfo -import roomescape.common.dto.OperatorInfo -import roomescape.schedule.business.domain.ScheduleOverview -import roomescape.schedule.exception.ScheduleErrorCode -import roomescape.schedule.infrastructure.persistence.ScheduleEntity -import roomescape.schedule.infrastructure.persistence.ScheduleEntityFactory -import roomescape.schedule.infrastructure.persistence.ScheduleRepository -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.schedule.web.* +import com.sangdol.roomescape.admin.business.AdminService +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.common.dto.AuditInfo +import com.sangdol.roomescape.common.dto.OperatorInfo +import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview +import com.sangdol.roomescape.schedule.exception.ScheduleErrorCode +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntityFactory +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.schedule.web.* import java.time.LocalDate private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/schedule/business/ScheduleValidator.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleValidator.kt similarity index 85% rename from src/main/kotlin/roomescape/schedule/business/ScheduleValidator.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleValidator.kt index d04dede1..daab707d 100644 --- a/src/main/kotlin/roomescape/schedule/business/ScheduleValidator.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleValidator.kt @@ -1,15 +1,15 @@ -package roomescape.schedule.business +package com.sangdol.roomescape.schedule.business import ScheduleException import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Component -import roomescape.schedule.exception.ScheduleErrorCode -import roomescape.schedule.infrastructure.persistence.ScheduleEntity -import roomescape.schedule.infrastructure.persistence.ScheduleRepository -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.schedule.web.ScheduleCreateRequest -import roomescape.schedule.web.ScheduleUpdateRequest +import com.sangdol.roomescape.schedule.exception.ScheduleErrorCode +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.schedule.web.ScheduleCreateRequest +import com.sangdol.roomescape.schedule.web.ScheduleUpdateRequest import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime diff --git a/src/main/kotlin/roomescape/schedule/business/domain/ScheduleOverview.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/domain/ScheduleOverview.kt similarity index 75% rename from src/main/kotlin/roomescape/schedule/business/domain/ScheduleOverview.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/business/domain/ScheduleOverview.kt index de099149..311c3b20 100644 --- a/src/main/kotlin/roomescape/schedule/business/domain/ScheduleOverview.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/domain/ScheduleOverview.kt @@ -1,7 +1,7 @@ -package roomescape.schedule.business.domain +package com.sangdol.roomescape.schedule.business.domain -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.theme.infrastructure.persistence.Difficulty +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty import java.time.LocalDate import java.time.LocalTime diff --git a/src/main/kotlin/roomescape/schedule/docs/ScheduleAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt similarity index 85% rename from src/main/kotlin/roomescape/schedule/docs/ScheduleAPI.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt index 58141027..8858007b 100644 --- a/src/main/kotlin/roomescape/schedule/docs/ScheduleAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt @@ -1,4 +1,4 @@ -package roomescape.schedule.docs +package com.sangdol.roomescape.schedule.docs import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse @@ -9,14 +9,14 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestParam -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.admin.infrastructure.persistence.Privilege -import roomescape.auth.web.support.AdminOnly -import roomescape.auth.web.support.Public -import roomescape.auth.web.support.UserOnly -import roomescape.common.dto.AuditInfo -import roomescape.common.dto.response.CommonApiResponse -import roomescape.schedule.web.* +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege +import com.sangdol.roomescape.auth.web.support.AdminOnly +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.auth.web.support.UserOnly +import com.sangdol.roomescape.common.dto.AuditInfo +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.schedule.web.* import java.time.LocalDate interface AdminScheduleAPI { diff --git a/src/main/kotlin/roomescape/schedule/exception/ScheduleErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleErrorCode.kt similarity index 88% rename from src/main/kotlin/roomescape/schedule/exception/ScheduleErrorCode.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleErrorCode.kt index 696d6630..264055d8 100644 --- a/src/main/kotlin/roomescape/schedule/exception/ScheduleErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleErrorCode.kt @@ -1,7 +1,7 @@ -package roomescape.schedule.exception +package com.sangdol.roomescape.schedule.exception import org.springframework.http.HttpStatus -import roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.ErrorCode enum class ScheduleErrorCode( override val httpStatus: HttpStatus, diff --git a/src/main/kotlin/roomescape/schedule/exception/ScheduleException.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleException.kt similarity index 56% rename from src/main/kotlin/roomescape/schedule/exception/ScheduleException.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleException.kt index a9655a8f..a79df67c 100644 --- a/src/main/kotlin/roomescape/schedule/exception/ScheduleException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleException.kt @@ -1,5 +1,5 @@ -import roomescape.common.exception.ErrorCode -import roomescape.common.exception.RoomescapeException +import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.RoomescapeException class ScheduleException( override val errorCode: ErrorCode, diff --git a/src/main/kotlin/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt similarity index 91% rename from src/main/kotlin/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt index a8023d00..b6a2c984 100644 --- a/src/main/kotlin/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt @@ -1,12 +1,12 @@ -package roomescape.schedule.infrastructure.persistence +package com.sangdol.roomescape.schedule.infrastructure.persistence import jakarta.persistence.* import org.springframework.data.annotation.CreatedBy import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener -import roomescape.common.entity.PersistableBaseEntity -import roomescape.common.util.MdcPrincipalId +import com.sangdol.roomescape.common.entity.PersistableBaseEntity +import com.sangdol.roomescape.common.util.MdcPrincipalId import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime diff --git a/src/main/kotlin/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt similarity index 86% rename from src/main/kotlin/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt index 3189b1aa..82080181 100644 --- a/src/main/kotlin/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleRepository.kt @@ -1,8 +1,8 @@ -package roomescape.schedule.infrastructure.persistence +package com.sangdol.roomescape.schedule.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query -import roomescape.schedule.business.domain.ScheduleOverview +import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview import java.time.LocalDate import java.time.LocalTime @@ -26,7 +26,7 @@ interface ScheduleRepository : JpaRepository { @Query( """ SELECT - new roomescape.schedule.business.domain.ScheduleOverview( + new com.sangdol.roomescape.schedule.business.domain.ScheduleOverview( s._id, st._id, st.name, @@ -57,7 +57,7 @@ interface ScheduleRepository : JpaRepository { @Query( """ SELECT - new roomescape.schedule.business.domain.ScheduleOverview( + new com.sangdol.roomescape.schedule.business.domain.ScheduleOverview( s._id, st._id, st.name, diff --git a/src/main/kotlin/roomescape/schedule/web/AdminScheduleController.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt similarity index 88% rename from src/main/kotlin/roomescape/schedule/web/AdminScheduleController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt index 39b7c5b2..1d9a96e4 100644 --- a/src/main/kotlin/roomescape/schedule/web/AdminScheduleController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt @@ -1,13 +1,13 @@ -package roomescape.schedule.web +package com.sangdol.roomescape.schedule.web import jakarta.validation.Valid import org.springframework.format.annotation.DateTimeFormat import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import roomescape.common.dto.AuditInfo -import roomescape.common.dto.response.CommonApiResponse -import roomescape.schedule.business.ScheduleService -import roomescape.schedule.docs.AdminScheduleAPI +import com.sangdol.roomescape.common.dto.AuditInfo +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.schedule.business.ScheduleService +import com.sangdol.roomescape.schedule.docs.AdminScheduleAPI import java.time.LocalDate @RestController diff --git a/src/main/kotlin/roomescape/schedule/web/AdminScheduleDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleDto.kt similarity index 86% rename from src/main/kotlin/roomescape/schedule/web/AdminScheduleDto.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleDto.kt index 47d17915..fec62160 100644 --- a/src/main/kotlin/roomescape/schedule/web/AdminScheduleDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleDto.kt @@ -1,7 +1,7 @@ -package roomescape.schedule.web +package com.sangdol.roomescape.schedule.web -import roomescape.schedule.business.domain.ScheduleOverview -import roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus import java.time.LocalDate import java.time.LocalTime diff --git a/src/main/kotlin/roomescape/schedule/web/ScheduleController.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/ScheduleController.kt similarity index 77% rename from src/main/kotlin/roomescape/schedule/web/ScheduleController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/web/ScheduleController.kt index a57d30f4..7f558d95 100644 --- a/src/main/kotlin/roomescape/schedule/web/ScheduleController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/ScheduleController.kt @@ -1,12 +1,12 @@ -package roomescape.schedule.web +package com.sangdol.roomescape.schedule.web import org.springframework.format.annotation.DateTimeFormat import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import roomescape.common.dto.response.CommonApiResponse -import roomescape.schedule.business.ScheduleService -import roomescape.schedule.docs.PublicScheduleAPI -import roomescape.schedule.docs.UserScheduleAPI +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.schedule.business.ScheduleService +import com.sangdol.roomescape.schedule.docs.PublicScheduleAPI +import com.sangdol.roomescape.schedule.docs.UserScheduleAPI import java.time.LocalDate @RestController diff --git a/src/main/kotlin/roomescape/schedule/web/ScheduleDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/ScheduleDto.kt similarity index 84% rename from src/main/kotlin/roomescape/schedule/web/ScheduleDto.kt rename to service/src/main/kotlin/com/sangdol/roomescape/schedule/web/ScheduleDto.kt index 10f4f03e..9085c299 100644 --- a/src/main/kotlin/roomescape/schedule/web/ScheduleDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/ScheduleDto.kt @@ -1,9 +1,9 @@ -package roomescape.schedule.web +package com.sangdol.roomescape.schedule.web -import roomescape.schedule.business.domain.ScheduleOverview -import roomescape.schedule.infrastructure.persistence.ScheduleEntity -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.theme.infrastructure.persistence.Difficulty +import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty import java.time.LocalDate import java.time.LocalTime diff --git a/src/main/kotlin/roomescape/store/business/StoreService.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt similarity index 88% rename from src/main/kotlin/roomescape/store/business/StoreService.kt rename to service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt index 8cbaebc4..a95db5c4 100644 --- a/src/main/kotlin/roomescape/store/business/StoreService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt @@ -1,20 +1,20 @@ -package roomescape.store.business +package com.sangdol.roomescape.store.business import com.github.f4b6a3.tsid.TsidFactory import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import roomescape.admin.business.AdminService -import roomescape.common.config.next -import roomescape.common.dto.AuditInfo -import roomescape.region.business.RegionService -import roomescape.store.exception.StoreErrorCode -import roomescape.store.exception.StoreException -import roomescape.store.infrastructure.persistence.StoreEntity -import roomescape.store.infrastructure.persistence.StoreRepository -import roomescape.store.infrastructure.persistence.StoreStatus -import roomescape.store.web.* +import com.sangdol.roomescape.admin.business.AdminService +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.common.dto.AuditInfo +import com.sangdol.roomescape.region.business.RegionService +import com.sangdol.roomescape.store.exception.StoreErrorCode +import com.sangdol.roomescape.store.exception.StoreException +import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity +import com.sangdol.roomescape.store.infrastructure.persistence.StoreRepository +import com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus +import com.sangdol.roomescape.store.web.* private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/store/business/StoreValidator.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreValidator.kt similarity index 85% rename from src/main/kotlin/roomescape/store/business/StoreValidator.kt rename to service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreValidator.kt index 01ce5471..cd1f3f69 100644 --- a/src/main/kotlin/roomescape/store/business/StoreValidator.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreValidator.kt @@ -1,13 +1,13 @@ -package roomescape.store.business +package com.sangdol.roomescape.store.business import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Component -import roomescape.store.exception.StoreErrorCode -import roomescape.store.exception.StoreException -import roomescape.store.infrastructure.persistence.StoreRepository -import roomescape.store.web.StoreRegisterRequest -import roomescape.store.web.StoreUpdateRequest +import com.sangdol.roomescape.store.exception.StoreErrorCode +import com.sangdol.roomescape.store.exception.StoreException +import com.sangdol.roomescape.store.infrastructure.persistence.StoreRepository +import com.sangdol.roomescape.store.web.StoreRegisterRequest +import com.sangdol.roomescape.store.web.StoreUpdateRequest private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/store/docs/StoreAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/docs/StoreAPI.kt similarity index 85% rename from src/main/kotlin/roomescape/store/docs/StoreAPI.kt rename to service/src/main/kotlin/com/sangdol/roomescape/store/docs/StoreAPI.kt index 3008582b..3c4ee9f2 100644 --- a/src/main/kotlin/roomescape/store/docs/StoreAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/docs/StoreAPI.kt @@ -1,4 +1,4 @@ -package roomescape.store.docs +package com.sangdol.roomescape.store.docs import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse @@ -8,12 +8,12 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestParam -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.admin.infrastructure.persistence.Privilege -import roomescape.auth.web.support.AdminOnly -import roomescape.auth.web.support.Public -import roomescape.common.dto.response.CommonApiResponse -import roomescape.store.web.* +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege +import com.sangdol.roomescape.auth.web.support.AdminOnly +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.store.web.* interface AdminStoreAPI { @AdminOnly(type = AdminType.HQ, privilege = Privilege.READ_DETAIL) diff --git a/src/main/kotlin/roomescape/store/exception/StoreException.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/exception/StoreException.kt similarity index 85% rename from src/main/kotlin/roomescape/store/exception/StoreException.kt rename to service/src/main/kotlin/com/sangdol/roomescape/store/exception/StoreException.kt index 0416e6cc..6531d154 100644 --- a/src/main/kotlin/roomescape/store/exception/StoreException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/exception/StoreException.kt @@ -1,8 +1,8 @@ -package roomescape.store.exception +package com.sangdol.roomescape.store.exception import org.springframework.http.HttpStatus -import roomescape.common.exception.ErrorCode -import roomescape.common.exception.RoomescapeException +import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.RoomescapeException class StoreException( override val errorCode: StoreErrorCode, diff --git a/src/main/kotlin/roomescape/store/infrastructure/persistence/StoreEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/infrastructure/persistence/StoreEntity.kt similarity index 87% rename from src/main/kotlin/roomescape/store/infrastructure/persistence/StoreEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/store/infrastructure/persistence/StoreEntity.kt index d59ce007..27a97f6f 100644 --- a/src/main/kotlin/roomescape/store/infrastructure/persistence/StoreEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/infrastructure/persistence/StoreEntity.kt @@ -1,8 +1,8 @@ -package roomescape.store.infrastructure.persistence +package com.sangdol.roomescape.store.infrastructure.persistence import jakarta.persistence.* import org.springframework.data.jpa.domain.support.AuditingEntityListener -import roomescape.common.entity.AuditingBaseEntity +import com.sangdol.roomescape.common.entity.AuditingBaseEntity @Entity @EntityListeners(AuditingEntityListener::class) diff --git a/src/main/kotlin/roomescape/store/infrastructure/persistence/StoreRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/infrastructure/persistence/StoreRepository.kt similarity index 76% rename from src/main/kotlin/roomescape/store/infrastructure/persistence/StoreRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/store/infrastructure/persistence/StoreRepository.kt index eff522bf..efbfa350 100644 --- a/src/main/kotlin/roomescape/store/infrastructure/persistence/StoreRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/infrastructure/persistence/StoreRepository.kt @@ -1,4 +1,4 @@ -package roomescape.store.infrastructure.persistence +package com.sangdol.roomescape.store.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query @@ -13,7 +13,7 @@ interface StoreRepository : JpaRepository { StoreEntity s WHERE s._id = :id - AND s.status = roomescape.store.infrastructure.persistence.StoreStatus.ACTIVE + AND s.status = com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus.ACTIVE """ ) fun findActiveStoreById(id: Long): StoreEntity? @@ -25,7 +25,7 @@ interface StoreRepository : JpaRepository { FROM StoreEntity s WHERE - s.status = roomescape.store.infrastructure.persistence.StoreStatus.ACTIVE + s.status = com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus.ACTIVE AND (:regionCode IS NULL OR s.regionCode LIKE :regionCode%) """ ) diff --git a/src/main/kotlin/roomescape/store/web/AdminStoreController.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreController.kt similarity index 86% rename from src/main/kotlin/roomescape/store/web/AdminStoreController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreController.kt index 4fd1d077..77b6f974 100644 --- a/src/main/kotlin/roomescape/store/web/AdminStoreController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreController.kt @@ -1,11 +1,11 @@ -package roomescape.store.web +package com.sangdol.roomescape.store.web import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import roomescape.common.dto.response.CommonApiResponse -import roomescape.store.business.StoreService -import roomescape.store.docs.AdminStoreAPI +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.store.business.StoreService +import com.sangdol.roomescape.store.docs.AdminStoreAPI @RestController @RequestMapping("/admin/stores") diff --git a/src/main/kotlin/roomescape/store/web/AdminStoreDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt similarity index 79% rename from src/main/kotlin/roomescape/store/web/AdminStoreDto.kt rename to service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt index 62003695..a20edc26 100644 --- a/src/main/kotlin/roomescape/store/web/AdminStoreDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt @@ -1,8 +1,8 @@ -package roomescape.store.web +package com.sangdol.roomescape.store.web -import roomescape.common.dto.AuditInfo -import roomescape.region.web.RegionInfoResponse -import roomescape.store.infrastructure.persistence.StoreEntity +import com.sangdol.roomescape.common.dto.AuditInfo +import com.sangdol.roomescape.region.web.RegionInfoResponse +import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity data class StoreRegisterRequest( val name: String, diff --git a/src/main/kotlin/roomescape/store/web/StoreController.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/web/StoreController.kt similarity index 83% rename from src/main/kotlin/roomescape/store/web/StoreController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/store/web/StoreController.kt index 47a3937c..9eba1b54 100644 --- a/src/main/kotlin/roomescape/store/web/StoreController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/web/StoreController.kt @@ -1,13 +1,13 @@ -package roomescape.store.web +package com.sangdol.roomescape.store.web import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import roomescape.common.dto.response.CommonApiResponse -import roomescape.store.business.StoreService -import roomescape.store.docs.PublicStoreAPI +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.store.business.StoreService +import com.sangdol.roomescape.store.docs.PublicStoreAPI @RestController class StoreController( diff --git a/src/main/kotlin/roomescape/store/web/StoreDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/web/StoreDTO.kt similarity index 84% rename from src/main/kotlin/roomescape/store/web/StoreDTO.kt rename to service/src/main/kotlin/com/sangdol/roomescape/store/web/StoreDTO.kt index cad9201e..0e6e3934 100644 --- a/src/main/kotlin/roomescape/store/web/StoreDTO.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/web/StoreDTO.kt @@ -1,6 +1,6 @@ -package roomescape.store.web +package com.sangdol.roomescape.store.web -import roomescape.store.infrastructure.persistence.StoreEntity +import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity data class SimpleStoreResponse( val id: Long, diff --git a/src/main/kotlin/roomescape/theme/business/ThemeService.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt similarity index 91% rename from src/main/kotlin/roomescape/theme/business/ThemeService.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt index a290fa78..28770ace 100644 --- a/src/main/kotlin/roomescape/theme/business/ThemeService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt @@ -1,4 +1,4 @@ -package roomescape.theme.business +package com.sangdol.roomescape.theme.business import com.github.f4b6a3.tsid.TsidFactory import io.github.oshai.kotlinlogging.KLogger @@ -6,15 +6,15 @@ import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import roomescape.admin.business.AdminService -import roomescape.common.config.next -import roomescape.common.dto.AuditInfo -import roomescape.common.util.DateUtils -import roomescape.theme.exception.ThemeErrorCode -import roomescape.theme.exception.ThemeException -import roomescape.theme.infrastructure.persistence.ThemeEntity -import roomescape.theme.infrastructure.persistence.ThemeRepository -import roomescape.theme.web.* +import com.sangdol.roomescape.admin.business.AdminService +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.common.dto.AuditInfo +import com.sangdol.roomescape.common.util.DateUtils +import com.sangdol.roomescape.theme.exception.ThemeErrorCode +import com.sangdol.roomescape.theme.exception.ThemeException +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository +import com.sangdol.roomescape.theme.web.* import java.time.LocalDate private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/theme/business/ThemeValidator.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeValidator.kt similarity index 92% rename from src/main/kotlin/roomescape/theme/business/ThemeValidator.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeValidator.kt index 6de7e205..8d5b341f 100644 --- a/src/main/kotlin/roomescape/theme/business/ThemeValidator.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeValidator.kt @@ -1,13 +1,13 @@ -package roomescape.theme.business +package com.sangdol.roomescape.theme.business import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Component -import roomescape.theme.exception.ThemeErrorCode -import roomescape.theme.exception.ThemeException -import roomescape.theme.infrastructure.persistence.ThemeRepository -import roomescape.theme.web.ThemeCreateRequest -import roomescape.theme.web.ThemeUpdateRequest +import com.sangdol.roomescape.theme.exception.ThemeErrorCode +import com.sangdol.roomescape.theme.exception.ThemeException +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository +import com.sangdol.roomescape.theme.web.ThemeCreateRequest +import com.sangdol.roomescape.theme.web.ThemeUpdateRequest private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/theme/business/domain/ThemeInfo.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/domain/ThemeInfo.kt similarity index 86% rename from src/main/kotlin/roomescape/theme/business/domain/ThemeInfo.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/business/domain/ThemeInfo.kt index 9406f6b4..cf69fdd0 100644 --- a/src/main/kotlin/roomescape/theme/business/domain/ThemeInfo.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/domain/ThemeInfo.kt @@ -1,4 +1,4 @@ -package roomescape.theme.business.domain +package com.sangdol.roomescape.theme.business.domain class ThemeInfo( val id: Long, diff --git a/src/main/kotlin/roomescape/theme/docs/ThemeApi.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt similarity index 87% rename from src/main/kotlin/roomescape/theme/docs/ThemeApi.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt index 82163cc6..6b5ffa2d 100644 --- a/src/main/kotlin/roomescape/theme/docs/ThemeApi.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt @@ -1,4 +1,4 @@ -package roomescape.theme.docs +package com.sangdol.roomescape.theme.docs import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse @@ -9,12 +9,12 @@ import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestParam -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.admin.infrastructure.persistence.Privilege -import roomescape.auth.web.support.AdminOnly -import roomescape.auth.web.support.Public -import roomescape.common.dto.response.CommonApiResponse -import roomescape.theme.web.* +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege +import com.sangdol.roomescape.auth.web.support.AdminOnly +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.theme.web.* interface AdminThemeAPI { @AdminOnly(type = AdminType.HQ, privilege = Privilege.READ_SUMMARY) diff --git a/src/main/kotlin/roomescape/theme/exception/ThemeErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeErrorCode.kt similarity index 92% rename from src/main/kotlin/roomescape/theme/exception/ThemeErrorCode.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeErrorCode.kt index fe0bc8c8..9210b958 100644 --- a/src/main/kotlin/roomescape/theme/exception/ThemeErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeErrorCode.kt @@ -1,7 +1,7 @@ -package roomescape.theme.exception +package com.sangdol.roomescape.theme.exception import org.springframework.http.HttpStatus -import roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.ErrorCode enum class ThemeErrorCode( override val httpStatus: HttpStatus, diff --git a/src/main/kotlin/roomescape/theme/exception/ThemeException.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeException.kt similarity index 59% rename from src/main/kotlin/roomescape/theme/exception/ThemeException.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeException.kt index 220a97c7..286cda16 100644 --- a/src/main/kotlin/roomescape/theme/exception/ThemeException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeException.kt @@ -1,6 +1,6 @@ -package roomescape.theme.exception +package com.sangdol.roomescape.theme.exception -import roomescape.common.exception.RoomescapeException +import com.sangdol.roomescape.common.exception.RoomescapeException class ThemeException( override val errorCode: ThemeErrorCode, diff --git a/src/main/kotlin/roomescape/theme/infrastructure/persistence/ThemeEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/infrastructure/persistence/ThemeEntity.kt similarity index 92% rename from src/main/kotlin/roomescape/theme/infrastructure/persistence/ThemeEntity.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/infrastructure/persistence/ThemeEntity.kt index 989b9a64..7c99a10c 100644 --- a/src/main/kotlin/roomescape/theme/infrastructure/persistence/ThemeEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/infrastructure/persistence/ThemeEntity.kt @@ -1,7 +1,7 @@ -package roomescape.theme.infrastructure.persistence +package com.sangdol.roomescape.theme.infrastructure.persistence import jakarta.persistence.* -import roomescape.common.entity.AuditingBaseEntity +import com.sangdol.roomescape.common.entity.AuditingBaseEntity @Entity @Table(name = "theme") diff --git a/src/main/kotlin/roomescape/theme/infrastructure/persistence/ThemeRepository.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/infrastructure/persistence/ThemeRepository.kt similarity index 92% rename from src/main/kotlin/roomescape/theme/infrastructure/persistence/ThemeRepository.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/infrastructure/persistence/ThemeRepository.kt index 29db0b47..644f84b6 100644 --- a/src/main/kotlin/roomescape/theme/infrastructure/persistence/ThemeRepository.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/infrastructure/persistence/ThemeRepository.kt @@ -1,8 +1,8 @@ -package roomescape.theme.infrastructure.persistence +package com.sangdol.roomescape.theme.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query -import roomescape.theme.business.domain.ThemeInfo +import com.sangdol.roomescape.theme.business.domain.ThemeInfo import java.time.LocalDate interface ThemeRepository : JpaRepository { diff --git a/src/main/kotlin/roomescape/theme/web/AdminThemeController.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt similarity index 90% rename from src/main/kotlin/roomescape/theme/web/AdminThemeController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt index b79004a2..b9084e28 100644 --- a/src/main/kotlin/roomescape/theme/web/AdminThemeController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt @@ -1,11 +1,11 @@ -package roomescape.theme.web +package com.sangdol.roomescape.theme.web import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import roomescape.common.dto.response.CommonApiResponse -import roomescape.theme.business.ThemeService -import roomescape.theme.docs.AdminThemeAPI +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.theme.business.ThemeService +import com.sangdol.roomescape.theme.docs.AdminThemeAPI import java.net.URI @RestController diff --git a/src/main/kotlin/roomescape/theme/web/AdminThemeDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt similarity index 93% rename from src/main/kotlin/roomescape/theme/web/AdminThemeDto.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt index 9b6be3ac..a5ed2964 100644 --- a/src/main/kotlin/roomescape/theme/web/AdminThemeDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt @@ -1,8 +1,8 @@ -package roomescape.theme.web +package com.sangdol.roomescape.theme.web -import roomescape.common.dto.AuditInfo -import roomescape.theme.infrastructure.persistence.Difficulty -import roomescape.theme.infrastructure.persistence.ThemeEntity +import com.sangdol.roomescape.common.dto.AuditInfo +import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity // ======================================== // HQ Admin DTO (본사) diff --git a/src/main/kotlin/roomescape/theme/web/ThemeController.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt similarity index 78% rename from src/main/kotlin/roomescape/theme/web/ThemeController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt index 8d67ea28..b17082eb 100644 --- a/src/main/kotlin/roomescape/theme/web/ThemeController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt @@ -1,10 +1,10 @@ -package roomescape.theme.web +package com.sangdol.roomescape.theme.web import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import roomescape.common.dto.response.CommonApiResponse -import roomescape.theme.business.ThemeService -import roomescape.theme.docs.PublicThemeAPI +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.theme.business.ThemeService +import com.sangdol.roomescape.theme.docs.PublicThemeAPI @RestController @RequestMapping("/themes") diff --git a/src/main/kotlin/roomescape/theme/web/ThemeDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt similarity index 89% rename from src/main/kotlin/roomescape/theme/web/ThemeDto.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt index 9befc770..cbe6f062 100644 --- a/src/main/kotlin/roomescape/theme/web/ThemeDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt @@ -1,7 +1,7 @@ -package roomescape.theme.web +package com.sangdol.roomescape.theme.web -import roomescape.theme.business.domain.ThemeInfo -import roomescape.theme.infrastructure.persistence.ThemeEntity +import com.sangdol.roomescape.theme.business.domain.ThemeInfo +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity data class ThemeIdListRequest( val themeIds: List diff --git a/src/main/kotlin/roomescape/user/business/UserService.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt similarity index 83% rename from src/main/kotlin/roomescape/user/business/UserService.kt rename to service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt index 627cbc54..866ef7d6 100644 --- a/src/main/kotlin/roomescape/user/business/UserService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt @@ -1,4 +1,4 @@ -package roomescape.user.business +package com.sangdol.roomescape.user.business import com.github.f4b6a3.tsid.TsidFactory import io.github.oshai.kotlinlogging.KLogger @@ -6,17 +6,17 @@ import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import roomescape.common.config.next -import roomescape.common.dto.CurrentUserContext -import roomescape.common.dto.UserLoginCredentials -import roomescape.common.dto.toCredentials -import roomescape.user.exception.UserErrorCode -import roomescape.user.exception.UserException -import roomescape.user.infrastructure.persistence.* -import roomescape.user.web.UserContactResponse -import roomescape.user.web.UserCreateRequest -import roomescape.user.web.UserCreateResponse -import roomescape.user.web.toEntity +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.common.dto.UserLoginCredentials +import com.sangdol.roomescape.common.dto.toCredentials +import com.sangdol.roomescape.user.exception.UserErrorCode +import com.sangdol.roomescape.user.exception.UserException +import com.sangdol.roomescape.user.infrastructure.persistence.* +import com.sangdol.roomescape.user.web.UserContactResponse +import com.sangdol.roomescape.user.web.UserCreateRequest +import com.sangdol.roomescape.user.web.UserCreateResponse +import com.sangdol.roomescape.user.web.toEntity private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/user/business/UserValidator.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserValidator.kt similarity index 80% rename from src/main/kotlin/roomescape/user/business/UserValidator.kt rename to service/src/main/kotlin/com/sangdol/roomescape/user/business/UserValidator.kt index 8e8d8cce..400ff17d 100644 --- a/src/main/kotlin/roomescape/user/business/UserValidator.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserValidator.kt @@ -1,11 +1,11 @@ -package roomescape.user.business +package com.sangdol.roomescape.user.business import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Component -import roomescape.user.exception.UserErrorCode -import roomescape.user.exception.UserException -import roomescape.user.infrastructure.persistence.UserRepository +import com.sangdol.roomescape.user.exception.UserErrorCode +import com.sangdol.roomescape.user.exception.UserException +import com.sangdol.roomescape.user.infrastructure.persistence.UserRepository private val log: KLogger = KotlinLogging.logger {} diff --git a/src/main/kotlin/roomescape/user/docs/UserAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt similarity index 65% rename from src/main/kotlin/roomescape/user/docs/UserAPI.kt rename to service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt index e809296d..512d2c54 100644 --- a/src/main/kotlin/roomescape/user/docs/UserAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt @@ -1,4 +1,4 @@ -package roomescape.user.docs +package com.sangdol.roomescape.user.docs import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse @@ -6,13 +6,13 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RequestBody -import roomescape.auth.web.support.Public -import roomescape.auth.web.support.User -import roomescape.common.dto.CurrentUserContext -import roomescape.common.dto.response.CommonApiResponse -import roomescape.user.web.UserContactResponse -import roomescape.user.web.UserCreateRequest -import roomescape.user.web.UserCreateResponse +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.user.web.UserContactResponse +import com.sangdol.roomescape.user.web.UserCreateRequest +import com.sangdol.roomescape.user.web.UserCreateResponse interface UserAPI { diff --git a/src/main/kotlin/roomescape/user/exception/UserException.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/exception/UserException.kt similarity index 79% rename from src/main/kotlin/roomescape/user/exception/UserException.kt rename to service/src/main/kotlin/com/sangdol/roomescape/user/exception/UserException.kt index 5f44f515..4561b724 100644 --- a/src/main/kotlin/roomescape/user/exception/UserException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/exception/UserException.kt @@ -1,8 +1,8 @@ -package roomescape.user.exception +package com.sangdol.roomescape.user.exception import org.springframework.http.HttpStatus -import roomescape.common.exception.ErrorCode -import roomescape.common.exception.RoomescapeException +import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.exception.RoomescapeException class UserException( override val errorCode: UserErrorCode, diff --git a/src/main/kotlin/roomescape/user/infrastructure/persistence/UserEntities.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/infrastructure/persistence/UserEntities.kt similarity index 84% rename from src/main/kotlin/roomescape/user/infrastructure/persistence/UserEntities.kt rename to service/src/main/kotlin/com/sangdol/roomescape/user/infrastructure/persistence/UserEntities.kt index 420764a0..dfe78608 100644 --- a/src/main/kotlin/roomescape/user/infrastructure/persistence/UserEntities.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/infrastructure/persistence/UserEntities.kt @@ -1,7 +1,7 @@ -package roomescape.user.infrastructure.persistence +package com.sangdol.roomescape.user.infrastructure.persistence import jakarta.persistence.* -import roomescape.common.entity.AuditingBaseEntity +import com.sangdol.roomescape.common.entity.AuditingBaseEntity @Entity @Table(name = "users") diff --git a/src/main/kotlin/roomescape/user/infrastructure/persistence/UserRepositories.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/infrastructure/persistence/UserRepositories.kt similarity index 84% rename from src/main/kotlin/roomescape/user/infrastructure/persistence/UserRepositories.kt rename to service/src/main/kotlin/com/sangdol/roomescape/user/infrastructure/persistence/UserRepositories.kt index 8095a090..f10272a9 100644 --- a/src/main/kotlin/roomescape/user/infrastructure/persistence/UserRepositories.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/infrastructure/persistence/UserRepositories.kt @@ -1,4 +1,4 @@ -package roomescape.user.infrastructure.persistence +package com.sangdol.roomescape.user.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository diff --git a/src/main/kotlin/roomescape/user/web/UserController.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt similarity index 71% rename from src/main/kotlin/roomescape/user/web/UserController.kt rename to service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt index 759e349e..c7819ccd 100644 --- a/src/main/kotlin/roomescape/user/web/UserController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt @@ -1,13 +1,13 @@ -package roomescape.user.web +package com.sangdol.roomescape.user.web import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import roomescape.auth.web.support.User -import roomescape.common.dto.CurrentUserContext -import roomescape.common.dto.response.CommonApiResponse -import roomescape.user.business.UserService -import roomescape.user.docs.UserAPI +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.roomescape.user.business.UserService +import com.sangdol.roomescape.user.docs.UserAPI @RestController @RequestMapping("/users") diff --git a/src/main/kotlin/roomescape/user/web/UserDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserDTO.kt similarity index 83% rename from src/main/kotlin/roomescape/user/web/UserDTO.kt rename to service/src/main/kotlin/com/sangdol/roomescape/user/web/UserDTO.kt index bf193d5d..4e300475 100644 --- a/src/main/kotlin/roomescape/user/web/UserDTO.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserDTO.kt @@ -1,11 +1,11 @@ -package roomescape.user.web +package com.sangdol.roomescape.user.web import jakarta.validation.constraints.Email import jakarta.validation.constraints.NotEmpty import jakarta.validation.constraints.Pattern import jakarta.validation.constraints.Size -import roomescape.user.infrastructure.persistence.UserEntity -import roomescape.user.infrastructure.persistence.UserStatus +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity +import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus const val MIN_PASSWORD_LENGTH = 8 diff --git a/src/main/resources/application-deploy.yaml b/service/src/main/resources/application-deploy.yaml similarity index 100% rename from src/main/resources/application-deploy.yaml rename to service/src/main/resources/application-deploy.yaml diff --git a/src/main/resources/application-local.yaml b/service/src/main/resources/application-local.yaml similarity index 100% rename from src/main/resources/application-local.yaml rename to service/src/main/resources/application-local.yaml diff --git a/src/main/resources/application.yaml b/service/src/main/resources/application.yaml similarity index 100% rename from src/main/resources/application.yaml rename to service/src/main/resources/application.yaml diff --git a/src/main/resources/logback-deploy.xml b/service/src/main/resources/logback-deploy.xml similarity index 97% rename from src/main/resources/logback-deploy.xml rename to service/src/main/resources/logback-deploy.xml index 662eea5e..0334205c 100644 --- a/src/main/resources/logback-deploy.xml +++ b/service/src/main/resources/logback-deploy.xml @@ -1,7 +1,7 @@ + class="com.sangdol.roomescape.common.log.RoomescapeLogMaskingConverter"/> diff --git a/src/main/resources/logback-local.xml b/service/src/main/resources/logback-local.xml similarity index 82% rename from src/main/resources/logback-local.xml rename to service/src/main/resources/logback-local.xml index 0c6eadb5..1b32137d 100644 --- a/src/main/resources/logback-local.xml +++ b/service/src/main/resources/logback-local.xml @@ -1,7 +1,7 @@ + class="com.sangdol.roomescape.common.log.RoomescapeLogMaskingConverter"/> @@ -16,7 +16,7 @@ - + diff --git a/src/main/resources/logback-spring.xml b/service/src/main/resources/logback-spring.xml similarity index 100% rename from src/main/resources/logback-spring.xml rename to service/src/main/resources/logback-spring.xml diff --git a/src/main/resources/schema/region-data.sql b/service/src/main/resources/schema/region-data.sql similarity index 100% rename from src/main/resources/schema/region-data.sql rename to service/src/main/resources/schema/region-data.sql diff --git a/src/main/resources/schema/schema-h2.sql b/service/src/main/resources/schema/schema-h2.sql similarity index 100% rename from src/main/resources/schema/schema-h2.sql rename to service/src/main/resources/schema/schema-h2.sql diff --git a/src/main/resources/schema/schema-mysql.sql b/service/src/main/resources/schema/schema-mysql.sql similarity index 100% rename from src/main/resources/schema/schema-mysql.sql rename to service/src/main/resources/schema/schema-mysql.sql diff --git a/src/test/kotlin/roomescape/auth/AuthApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt similarity index 89% rename from src/test/kotlin/roomescape/auth/AuthApiTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt index bf585a6a..bface8e8 100644 --- a/src/test/kotlin/roomescape/auth/AuthApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt @@ -1,4 +1,4 @@ -package roomescape.auth +package com.sangdol.roomescape.auth import com.ninjasquad.springmockk.SpykBean import io.kotest.assertions.assertSoftly @@ -9,21 +9,21 @@ import io.mockk.every import io.restassured.response.ValidatableResponse import org.hamcrest.CoreMatchers.equalTo import org.springframework.http.HttpStatus -import roomescape.admin.exception.AdminErrorCode -import roomescape.auth.business.CLAIM_ADMIN_TYPE_KEY -import roomescape.auth.business.CLAIM_PERMISSION_KEY -import roomescape.auth.business.CLAIM_STORE_ID_KEY -import roomescape.auth.exception.AuthErrorCode -import roomescape.auth.infrastructure.jwt.JwtUtils -import roomescape.auth.infrastructure.persistence.LoginHistoryRepository -import roomescape.auth.web.LoginRequest -import roomescape.common.dto.PrincipalType -import roomescape.supports.AdminFixture -import roomescape.supports.FunSpecSpringbootTest -import roomescape.supports.UserFixture -import roomescape.supports.runTest -import roomescape.user.exception.UserErrorCode -import roomescape.user.infrastructure.persistence.UserEntity +import com.sangdol.roomescape.admin.exception.AdminErrorCode +import com.sangdol.roomescape.auth.business.CLAIM_ADMIN_TYPE_KEY +import com.sangdol.roomescape.auth.business.CLAIM_PERMISSION_KEY +import com.sangdol.roomescape.auth.business.CLAIM_STORE_ID_KEY +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils +import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryRepository +import com.sangdol.roomescape.auth.web.LoginRequest +import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.supports.AdminFixture +import com.sangdol.roomescape.supports.FunSpecSpringbootTest +import com.sangdol.roomescape.supports.UserFixture +import com.sangdol.roomescape.supports.runTest +import com.sangdol.roomescape.user.exception.UserErrorCode +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity class AuthApiTest( @SpykBean private val jwtUtils: JwtUtils, diff --git a/src/test/kotlin/roomescape/auth/FailOnSaveLoginHistoryTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt similarity index 80% rename from src/test/kotlin/roomescape/auth/FailOnSaveLoginHistoryTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt index b4d1e724..92013b88 100644 --- a/src/test/kotlin/roomescape/auth/FailOnSaveLoginHistoryTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt @@ -1,16 +1,16 @@ -package roomescape.auth +package com.sangdol.roomescape.auth import com.ninjasquad.springmockk.MockkBean import io.mockk.clearMocks import io.mockk.every import org.springframework.http.HttpStatus -import roomescape.auth.infrastructure.persistence.LoginHistoryRepository -import roomescape.auth.web.LoginRequest -import roomescape.common.dto.PrincipalType -import roomescape.supports.AdminFixture -import roomescape.supports.FunSpecSpringbootTest -import roomescape.supports.UserFixture -import roomescape.supports.runTest +import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryRepository +import com.sangdol.roomescape.auth.web.LoginRequest +import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.supports.AdminFixture +import com.sangdol.roomescape.supports.FunSpecSpringbootTest +import com.sangdol.roomescape.supports.UserFixture +import com.sangdol.roomescape.supports.runTest class FailOnSaveLoginHistoryTest( @MockkBean private val loginHistoryRepository: LoginHistoryRepository diff --git a/src/test/kotlin/roomescape/auth/JwtUtilsTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/auth/JwtUtilsTest.kt similarity index 90% rename from src/test/kotlin/roomescape/auth/JwtUtilsTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/auth/JwtUtilsTest.kt index 28aff8ef..02415b46 100644 --- a/src/test/kotlin/roomescape/auth/JwtUtilsTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/auth/JwtUtilsTest.kt @@ -1,13 +1,13 @@ -package roomescape.auth +package com.sangdol.roomescape.auth import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe -import roomescape.auth.exception.AuthErrorCode -import roomescape.auth.exception.AuthException -import roomescape.auth.infrastructure.jwt.JwtUtils -import roomescape.common.config.next -import roomescape.supports.tsidFactory +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.auth.exception.AuthException +import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.supports.tsidFactory class JwtUtilsTest : FunSpec() { private val jwtUtils: JwtUtils = JwtUtils( diff --git a/src/test/kotlin/roomescape/common/config/JacksonConfigTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/common/config/JacksonConfigTest.kt similarity index 98% rename from src/test/kotlin/roomescape/common/config/JacksonConfigTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/common/config/JacksonConfigTest.kt index c4129dc2..8ebb7557 100644 --- a/src/test/kotlin/roomescape/common/config/JacksonConfigTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/common/config/JacksonConfigTest.kt @@ -1,4 +1,4 @@ -package roomescape.common.config +package com.sangdol.roomescape.common.config import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.exc.InvalidFormatException diff --git a/src/test/kotlin/roomescape/common/log/ApiLogMessageConverterTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverterTest.kt similarity index 94% rename from src/test/kotlin/roomescape/common/log/ApiLogMessageConverterTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverterTest.kt index 1e0e4afb..c420d817 100644 --- a/src/test/kotlin/roomescape/common/log/ApiLogMessageConverterTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverterTest.kt @@ -1,4 +1,4 @@ -package roomescape.common.log +package com.sangdol.roomescape.common.log import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.kotest.core.spec.style.StringSpec @@ -7,8 +7,8 @@ import io.mockk.every import io.mockk.mockk import jakarta.servlet.http.HttpServletRequest import org.slf4j.MDC -import roomescape.auth.exception.AuthErrorCode -import roomescape.auth.exception.AuthException +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.auth.exception.AuthException class ApiLogMessageConverterTest : StringSpec({ val converter = ApiLogMessageConverter(jacksonObjectMapper()) diff --git a/src/test/kotlin/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt similarity index 95% rename from src/test/kotlin/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt index ac224f63..488de01a 100644 --- a/src/test/kotlin/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt @@ -1,4 +1,4 @@ -package roomescape.common.log +package com.sangdol.roomescape.common.log import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.StringSpec diff --git a/src/test/kotlin/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt similarity index 98% rename from src/test/kotlin/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt index b4ed4462..2db3f5f7 100644 --- a/src/test/kotlin/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt @@ -1,4 +1,4 @@ -package roomescape.common.log +package com.sangdol.roomescape.common.log import ch.qos.logback.classic.spi.ILoggingEvent import io.kotest.assertions.assertSoftly diff --git a/src/test/kotlin/roomescape/common/util/DateUtilsTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/common/util/DateUtilsTest.kt similarity index 90% rename from src/test/kotlin/roomescape/common/util/DateUtilsTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/common/util/DateUtilsTest.kt index 30a2c6b3..4be655fc 100644 --- a/src/test/kotlin/roomescape/common/util/DateUtilsTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/common/util/DateUtilsTest.kt @@ -1,4 +1,4 @@ -package roomescape.common.util +package com.sangdol.roomescape.common.util import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe diff --git a/src/test/kotlin/roomescape/data/DefaultDataInitializer.kt b/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt similarity index 95% rename from src/test/kotlin/roomescape/data/DefaultDataInitializer.kt rename to service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt index eed4c74d..06785315 100644 --- a/src/test/kotlin/roomescape/data/DefaultDataInitializer.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt @@ -1,4 +1,4 @@ -package roomescape.data +package com.sangdol.roomescape.data import com.github.f4b6a3.tsid.TsidFactory import io.kotest.core.test.TestCaseOrder @@ -11,23 +11,23 @@ import kotlinx.coroutines.sync.Semaphore import org.springframework.beans.factory.annotation.Autowired import org.springframework.jdbc.core.JdbcTemplate import org.springframework.test.context.ActiveProfiles -import roomescape.admin.infrastructure.persistence.AdminEntity -import roomescape.admin.infrastructure.persistence.AdminPermissionLevel -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.common.config.next -import roomescape.common.util.TransactionExecutionUtil -import roomescape.payment.infrastructure.common.* -import roomescape.reservation.infrastructure.persistence.ReservationStatus -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.supports.AdminFixture -import roomescape.supports.FunSpecSpringbootTest -import roomescape.supports.randomPhoneNumber -import roomescape.supports.randomString -import roomescape.theme.infrastructure.persistence.Difficulty -import roomescape.user.business.SIGNUP -import roomescape.user.infrastructure.persistence.UserEntity -import roomescape.user.infrastructure.persistence.UserStatus -import roomescape.user.web.UserContactResponse +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.common.util.TransactionExecutionUtil +import com.sangdol.roomescape.payment.infrastructure.common.* +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.supports.AdminFixture +import com.sangdol.roomescape.supports.FunSpecSpringbootTest +import com.sangdol.roomescape.supports.randomPhoneNumber +import com.sangdol.roomescape.supports.randomString +import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty +import com.sangdol.roomescape.user.business.SIGNUP +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity +import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus +import com.sangdol.roomescape.user.web.UserContactResponse import java.sql.Timestamp import java.time.LocalDateTime import java.time.LocalTime @@ -532,12 +532,12 @@ class ReservationDataInitializer : AbstractDataInitializer() { val chunkSize = 10_000 val chunkedSchedules: List> = entityManager.createQuery( - "SELECT new roomescape.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status", + "SELECT new com.sangdol.roomescape.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status", ScheduleWithThemeParticipants::class.java ).setParameter("status", ScheduleStatus.RESERVED).resultList.chunked(chunkSize) val chunkedUsers: List> = entityManager.createQuery( - "SELECT new roomescape.user.web.UserContactResponse(u._id, u.name, u.phone) FROM UserEntity u", + "SELECT new com.sangdol.roomescape.user.web.UserContactResponse(u._id, u.name, u.phone) FROM UserEntity u", UserContactResponse::class.java ).resultList.chunked(chunkSize) @@ -682,7 +682,7 @@ class PaymentDataInitializer : AbstractDataInitializer() { test("기존 결제 데이터에 상세 정보(계좌이체, 카드, 간편결제) 데이터를 생성한다.") { val allPayments: List = entityManager.createQuery( - "SELECT new roomescape.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId", + "SELECT new com.sangdol.roomescape.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId", PaymentWithMethods::class.java ).resultList diff --git a/src/test/kotlin/roomescape/data/PopulationDataParser.kt b/service/src/test/kotlin/com/sangdol/roomescape/data/PopulationDataParser.kt similarity index 95% rename from src/test/kotlin/roomescape/data/PopulationDataParser.kt rename to service/src/test/kotlin/com/sangdol/roomescape/data/PopulationDataParser.kt index d511b234..c1582e31 100644 --- a/src/test/kotlin/roomescape/data/PopulationDataParser.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/data/PopulationDataParser.kt @@ -1,11 +1,12 @@ -package roomescape.data +package com.sangdol.roomescape.data import org.apache.poi.xssf.usermodel.XSSFWorkbook -import roomescape.common.config.next -import roomescape.store.infrastructure.persistence.StoreStatus -import roomescape.supports.randomPhoneNumber -import roomescape.supports.tsidFactory +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus +import com.sangdol.roomescape.supports.randomPhoneNumber +import com.sangdol.roomescape.supports.tsidFactory import java.io.File +import java.nio.file.Paths import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter @@ -25,7 +26,7 @@ class PopulationDataSqlParser() { // 인구 데이터를 이용하여 지역 정보 SQL 파일로 변환하고, 추가로 $MIN_POPULATION_FOR_PER_STORE 이상의 시/군/구는 매장 데이터 생성을 위해 따로 분류한다. fun createParsedRegionPopulationFiles() { - val populationXlsx = XSSFWorkbook(File("data/population.xlsx")) + val populationXlsx = XSSFWorkbook(File("${BASE_DIR}/population.xlsx")) val sheet = populationXlsx.getSheetAt(0) val allRegion = mutableListOf>() val regionsMoreThanMinPopulation = mutableListOf>() diff --git a/src/test/kotlin/roomescape/payment/PaymentAPITest.kt b/service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentAPITest.kt similarity index 95% rename from src/test/kotlin/roomescape/payment/PaymentAPITest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentAPITest.kt index 2cdea8d4..f3fcccd3 100644 --- a/src/test/kotlin/roomescape/payment/PaymentAPITest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentAPITest.kt @@ -1,4 +1,4 @@ -package roomescape.payment +package com.sangdol.roomescape.payment import com.ninjasquad.springmockk.MockkBean import io.kotest.matchers.shouldBe @@ -6,18 +6,18 @@ import io.mockk.every import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import roomescape.auth.exception.AuthErrorCode -import roomescape.payment.business.PaymentService -import roomescape.payment.exception.PaymentErrorCode -import roomescape.payment.infrastructure.client.CardDetail -import roomescape.payment.infrastructure.client.EasyPayDetail -import roomescape.payment.infrastructure.client.TosspayClient -import roomescape.payment.infrastructure.client.TransferDetail -import roomescape.payment.infrastructure.common.* -import roomescape.payment.infrastructure.persistence.* -import roomescape.payment.web.PaymentConfirmRequest -import roomescape.payment.web.PaymentCreateResponse -import roomescape.supports.* +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.payment.business.PaymentService +import com.sangdol.roomescape.payment.exception.PaymentErrorCode +import com.sangdol.roomescape.payment.infrastructure.client.CardDetail +import com.sangdol.roomescape.payment.infrastructure.client.EasyPayDetail +import com.sangdol.roomescape.payment.infrastructure.client.TosspayClient +import com.sangdol.roomescape.payment.infrastructure.client.TransferDetail +import com.sangdol.roomescape.payment.infrastructure.common.* +import com.sangdol.roomescape.payment.infrastructure.persistence.* +import com.sangdol.roomescape.payment.web.PaymentConfirmRequest +import com.sangdol.roomescape.payment.web.PaymentCreateResponse +import com.sangdol.roomescape.supports.* class PaymentAPITest( @MockkBean diff --git a/src/test/kotlin/roomescape/payment/SampleTosspayConstant.kt b/service/src/test/kotlin/com/sangdol/roomescape/payment/SampleTosspayConstant.kt similarity index 99% rename from src/test/kotlin/roomescape/payment/SampleTosspayConstant.kt rename to service/src/test/kotlin/com/sangdol/roomescape/payment/SampleTosspayConstant.kt index 07c4a2ad..aa1c8514 100644 --- a/src/test/kotlin/roomescape/payment/SampleTosspayConstant.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/payment/SampleTosspayConstant.kt @@ -1,4 +1,4 @@ -package roomescape.payment +package com.sangdol.roomescape.payment import java.time.OffsetDateTime diff --git a/src/test/kotlin/roomescape/payment/TosspayClientTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/payment/TosspayClientTest.kt similarity index 93% rename from src/test/kotlin/roomescape/payment/TosspayClientTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/payment/TosspayClientTest.kt index d0665a06..ece494e3 100644 --- a/src/test/kotlin/roomescape/payment/TosspayClientTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/payment/TosspayClientTest.kt @@ -1,4 +1,4 @@ -package roomescape.payment +package com.sangdol.roomescape.payment import com.ninjasquad.springmockk.MockkBean import io.kotest.assertions.assertSoftly @@ -16,12 +16,12 @@ import org.springframework.test.web.client.ResponseActions import org.springframework.test.web.client.match.MockRestRequestMatchers.* import org.springframework.test.web.client.response.MockRestResponseCreators.withStatus import org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess -import roomescape.payment.exception.PaymentErrorCode -import roomescape.payment.exception.PaymentException -import roomescape.payment.infrastructure.client.PaymentClientCancelResponse -import roomescape.payment.infrastructure.client.PaymentClientConfirmResponse -import roomescape.payment.infrastructure.client.TosspayClient -import roomescape.payment.infrastructure.common.PaymentStatus +import com.sangdol.roomescape.payment.exception.PaymentErrorCode +import com.sangdol.roomescape.payment.exception.PaymentException +import com.sangdol.roomescape.payment.infrastructure.client.PaymentClientCancelResponse +import com.sangdol.roomescape.payment.infrastructure.client.PaymentClientConfirmResponse +import com.sangdol.roomescape.payment.infrastructure.client.TosspayClient +import com.sangdol.roomescape.payment.infrastructure.common.PaymentStatus @RestClientTest(TosspayClient::class) @MockkBean(JpaMetamodelMappingContext::class) diff --git a/src/test/kotlin/roomescape/region/RegionApiFailTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/region/RegionApiFailTest.kt similarity index 83% rename from src/test/kotlin/roomescape/region/RegionApiFailTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/region/RegionApiFailTest.kt index 671ebf0d..1621aab9 100644 --- a/src/test/kotlin/roomescape/region/RegionApiFailTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/region/RegionApiFailTest.kt @@ -1,12 +1,12 @@ -package roomescape.region +package com.sangdol.roomescape.region import com.ninjasquad.springmockk.MockkBean import io.mockk.every import org.springframework.http.HttpMethod -import roomescape.region.exception.RegionErrorCode -import roomescape.region.infrastructure.persistence.RegionRepository -import roomescape.supports.FunSpecSpringbootTest -import roomescape.supports.runExceptionTest +import com.sangdol.roomescape.region.exception.RegionErrorCode +import com.sangdol.roomescape.region.infrastructure.persistence.RegionRepository +import com.sangdol.roomescape.supports.FunSpecSpringbootTest +import com.sangdol.roomescape.supports.runExceptionTest class RegionApiFailTest( @MockkBean private val regionRepository: RegionRepository diff --git a/src/test/kotlin/roomescape/region/RegionApiSuccessTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/region/RegionApiSuccessTest.kt similarity index 90% rename from src/test/kotlin/roomescape/region/RegionApiSuccessTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/region/RegionApiSuccessTest.kt index 53e34fe2..2875854b 100644 --- a/src/test/kotlin/roomescape/region/RegionApiSuccessTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/region/RegionApiSuccessTest.kt @@ -1,9 +1,9 @@ -package roomescape.region +package com.sangdol.roomescape.region import io.kotest.matchers.shouldBe import org.springframework.http.HttpStatus -import roomescape.supports.FunSpecSpringbootTest -import roomescape.supports.runTest +import com.sangdol.roomescape.supports.FunSpecSpringbootTest +import com.sangdol.roomescape.supports.runTest class RegionApiSuccessTest: FunSpecSpringbootTest() { init { diff --git a/src/test/kotlin/roomescape/reservation/ReservationApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/reservation/ReservationApiTest.kt similarity index 94% rename from src/test/kotlin/roomescape/reservation/ReservationApiTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/reservation/ReservationApiTest.kt index 6f6d9f8a..52fde089 100644 --- a/src/test/kotlin/roomescape/reservation/ReservationApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/reservation/ReservationApiTest.kt @@ -1,4 +1,4 @@ -package roomescape.reservation +package com.sangdol.roomescape.reservation import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe @@ -6,25 +6,25 @@ import org.hamcrest.CoreMatchers.equalTo import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import roomescape.auth.exception.AuthErrorCode -import roomescape.common.exception.CommonErrorCode -import roomescape.payment.infrastructure.common.BankCode -import roomescape.payment.infrastructure.common.CardIssuerCode -import roomescape.payment.infrastructure.common.EasyPayCompanyCode -import roomescape.payment.infrastructure.persistence.PaymentDetailRepository -import roomescape.reservation.exception.ReservationErrorCode -import roomescape.reservation.infrastructure.persistence.CanceledReservationRepository -import roomescape.reservation.infrastructure.persistence.ReservationEntity -import roomescape.reservation.infrastructure.persistence.ReservationRepository -import roomescape.reservation.infrastructure.persistence.ReservationStatus -import roomescape.reservation.web.ReservationCancelRequest -import roomescape.reservation.web.ReservationOverviewResponse -import roomescape.schedule.infrastructure.persistence.ScheduleEntity -import roomescape.schedule.infrastructure.persistence.ScheduleRepository -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.supports.* -import roomescape.theme.infrastructure.persistence.ThemeEntity -import roomescape.theme.infrastructure.persistence.ThemeRepository +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.common.exception.CommonErrorCode +import com.sangdol.roomescape.payment.infrastructure.common.BankCode +import com.sangdol.roomescape.payment.infrastructure.common.CardIssuerCode +import com.sangdol.roomescape.payment.infrastructure.common.EasyPayCompanyCode +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentDetailRepository +import com.sangdol.roomescape.reservation.exception.ReservationErrorCode +import com.sangdol.roomescape.reservation.infrastructure.persistence.CanceledReservationRepository +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationEntity +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationRepository +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus +import com.sangdol.roomescape.reservation.web.ReservationCancelRequest +import com.sangdol.roomescape.reservation.web.ReservationOverviewResponse +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.supports.* +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository import java.time.LocalDate import java.time.LocalTime diff --git a/src/test/kotlin/roomescape/schedule/AdminScheduleApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt similarity index 96% rename from src/test/kotlin/roomescape/schedule/AdminScheduleApiTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt index 3411c1fb..b16abad5 100644 --- a/src/test/kotlin/roomescape/schedule/AdminScheduleApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt @@ -1,4 +1,4 @@ -package roomescape.schedule +package com.sangdol.roomescape.schedule import io.kotest.assertions.assertSoftly import io.kotest.matchers.date.shouldBeBefore @@ -8,19 +8,19 @@ import org.hamcrest.CoreMatchers.equalTo import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import roomescape.admin.infrastructure.persistence.AdminPermissionLevel -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.auth.exception.AuthErrorCode -import roomescape.common.dto.AuditConstant -import roomescape.common.dto.OperatorInfo -import roomescape.schedule.exception.ScheduleErrorCode -import roomescape.schedule.infrastructure.persistence.ScheduleEntity -import roomescape.schedule.infrastructure.persistence.ScheduleRepository -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.schedule.web.AdminScheduleSummaryResponse -import roomescape.schedule.web.ScheduleUpdateRequest -import roomescape.store.infrastructure.persistence.StoreEntity -import roomescape.supports.* +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.common.dto.AuditConstant +import com.sangdol.roomescape.common.dto.OperatorInfo +import com.sangdol.roomescape.schedule.exception.ScheduleErrorCode +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.schedule.web.AdminScheduleSummaryResponse +import com.sangdol.roomescape.schedule.web.ScheduleUpdateRequest +import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity +import com.sangdol.roomescape.supports.* import java.time.LocalDate import java.time.LocalTime diff --git a/src/test/kotlin/roomescape/schedule/ScheduleApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt similarity index 89% rename from src/test/kotlin/roomescape/schedule/ScheduleApiTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt index 81cb6d85..a4bd3b84 100644 --- a/src/test/kotlin/roomescape/schedule/ScheduleApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt @@ -1,17 +1,17 @@ -package roomescape.schedule +package com.sangdol.roomescape.schedule import io.kotest.matchers.shouldBe import org.hamcrest.CoreMatchers.equalTo import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import roomescape.admin.infrastructure.persistence.AdminPermissionLevel -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.auth.exception.AuthErrorCode -import roomescape.schedule.exception.ScheduleErrorCode -import roomescape.schedule.infrastructure.persistence.ScheduleRepository -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.supports.* +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.schedule.exception.ScheduleErrorCode +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.supports.* import java.time.LocalDate import java.time.LocalTime diff --git a/src/test/kotlin/roomescape/store/AdminStoreApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/store/AdminStoreApiTest.kt similarity index 96% rename from src/test/kotlin/roomescape/store/AdminStoreApiTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/store/AdminStoreApiTest.kt index 2417311d..e8e85eec 100644 --- a/src/test/kotlin/roomescape/store/AdminStoreApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/store/AdminStoreApiTest.kt @@ -1,4 +1,4 @@ -package roomescape.store +package com.sangdol.roomescape.store import io.kotest.assertions.assertSoftly import io.kotest.matchers.date.shouldBeAfter @@ -6,16 +6,16 @@ import io.kotest.matchers.shouldBe import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import roomescape.admin.infrastructure.persistence.AdminEntity -import roomescape.admin.infrastructure.persistence.AdminPermissionLevel -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.auth.exception.AuthErrorCode -import roomescape.store.exception.StoreErrorCode -import roomescape.store.infrastructure.persistence.StoreEntity -import roomescape.store.infrastructure.persistence.StoreRepository -import roomescape.store.infrastructure.persistence.StoreStatus -import roomescape.store.web.StoreUpdateRequest -import roomescape.supports.* +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.store.exception.StoreErrorCode +import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity +import com.sangdol.roomescape.store.infrastructure.persistence.StoreRepository +import com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus +import com.sangdol.roomescape.store.web.StoreUpdateRequest +import com.sangdol.roomescape.supports.* class AdminStoreApiTest( private val storeRepository: StoreRepository, diff --git a/src/test/kotlin/roomescape/store/StoreApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/store/StoreApiTest.kt similarity index 94% rename from src/test/kotlin/roomescape/store/StoreApiTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/store/StoreApiTest.kt index 006c94fc..f6580e6a 100644 --- a/src/test/kotlin/roomescape/store/StoreApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/store/StoreApiTest.kt @@ -1,11 +1,11 @@ -package roomescape.store +package com.sangdol.roomescape.store import org.hamcrest.CoreMatchers.equalTo import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import roomescape.store.exception.StoreErrorCode -import roomescape.store.infrastructure.persistence.StoreEntity -import roomescape.supports.* +import com.sangdol.roomescape.store.exception.StoreErrorCode +import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity +import com.sangdol.roomescape.supports.* class StoreApiTest: FunSpecSpringbootTest() { diff --git a/src/test/kotlin/roomescape/supports/DummyInitializer.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt similarity index 74% rename from src/test/kotlin/roomescape/supports/DummyInitializer.kt rename to service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt index f4791b94..b90f7136 100644 --- a/src/test/kotlin/roomescape/supports/DummyInitializer.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt @@ -1,36 +1,36 @@ -package roomescape.supports +package com.sangdol.roomescape.supports import org.springframework.data.repository.findByIdOrNull -import roomescape.common.config.next -import roomescape.payment.business.PaymentWriter -import roomescape.payment.infrastructure.client.CardDetail -import roomescape.payment.infrastructure.client.EasyPayDetail -import roomescape.payment.infrastructure.client.TransferDetail -import roomescape.payment.infrastructure.common.PaymentMethod -import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity -import roomescape.payment.infrastructure.persistence.PaymentEntity -import roomescape.payment.infrastructure.persistence.PaymentRepository -import roomescape.payment.web.PaymentConfirmRequest -import roomescape.payment.web.PaymentWithDetailResponse -import roomescape.payment.web.toDetailResponse -import roomescape.payment.web.toPaymentDetailResponse -import roomescape.reservation.infrastructure.persistence.ReservationEntity -import roomescape.reservation.infrastructure.persistence.ReservationRepository -import roomescape.reservation.infrastructure.persistence.ReservationStatus -import roomescape.reservation.web.PendingReservationCreateRequest -import roomescape.reservation.web.toEntity -import roomescape.schedule.infrastructure.persistence.ScheduleEntity -import roomescape.schedule.infrastructure.persistence.ScheduleRepository -import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.schedule.web.ScheduleCreateRequest -import roomescape.store.infrastructure.persistence.StoreEntity -import roomescape.store.infrastructure.persistence.StoreRepository -import roomescape.store.infrastructure.persistence.StoreStatus -import roomescape.theme.infrastructure.persistence.ThemeEntity -import roomescape.theme.infrastructure.persistence.ThemeRepository -import roomescape.theme.web.ThemeCreateRequest -import roomescape.theme.web.toEntity -import roomescape.user.infrastructure.persistence.UserEntity +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.payment.business.PaymentWriter +import com.sangdol.roomescape.payment.infrastructure.client.CardDetail +import com.sangdol.roomescape.payment.infrastructure.client.EasyPayDetail +import com.sangdol.roomescape.payment.infrastructure.client.TransferDetail +import com.sangdol.roomescape.payment.infrastructure.common.PaymentMethod +import com.sangdol.roomescape.payment.infrastructure.persistence.CanceledPaymentEntity +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentEntity +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentRepository +import com.sangdol.roomescape.payment.web.PaymentConfirmRequest +import com.sangdol.roomescape.payment.web.PaymentWithDetailResponse +import com.sangdol.roomescape.payment.web.toDetailResponse +import com.sangdol.roomescape.payment.web.toPaymentDetailResponse +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationEntity +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationRepository +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus +import com.sangdol.roomescape.reservation.web.PendingReservationCreateRequest +import com.sangdol.roomescape.reservation.web.toEntity +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.schedule.web.ScheduleCreateRequest +import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity +import com.sangdol.roomescape.store.infrastructure.persistence.StoreRepository +import com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository +import com.sangdol.roomescape.theme.web.ThemeCreateRequest +import com.sangdol.roomescape.theme.web.toEntity +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity import java.time.LocalDateTime class DummyInitializer( diff --git a/src/test/kotlin/roomescape/supports/Fixtures.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt similarity index 86% rename from src/test/kotlin/roomescape/supports/Fixtures.kt rename to service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt index ada73ea8..7389a643 100644 --- a/src/test/kotlin/roomescape/supports/Fixtures.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt @@ -1,28 +1,28 @@ -package roomescape.supports +package com.sangdol.roomescape.supports import com.github.f4b6a3.tsid.TsidFactory -import roomescape.admin.infrastructure.persistence.AdminEntity -import roomescape.admin.infrastructure.persistence.AdminPermissionLevel -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.common.config.next -import roomescape.payment.infrastructure.client.* -import roomescape.payment.infrastructure.common.* -import roomescape.payment.web.PaymentCancelRequest -import roomescape.payment.web.PaymentConfirmRequest -import roomescape.reservation.web.PendingReservationCreateRequest -import roomescape.schedule.infrastructure.persistence.ScheduleEntity -import roomescape.schedule.infrastructure.persistence.ScheduleEntityFactory -import roomescape.schedule.web.ScheduleCreateRequest -import roomescape.store.infrastructure.persistence.StoreEntity -import roomescape.store.infrastructure.persistence.StoreStatus -import roomescape.store.web.StoreRegisterRequest -import roomescape.theme.infrastructure.persistence.Difficulty -import roomescape.theme.infrastructure.persistence.ThemeEntity -import roomescape.theme.web.ThemeCreateRequest -import roomescape.user.infrastructure.persistence.UserEntity -import roomescape.user.infrastructure.persistence.UserStatus -import roomescape.user.web.MIN_PASSWORD_LENGTH -import roomescape.user.web.UserCreateRequest +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.payment.infrastructure.client.* +import com.sangdol.roomescape.payment.infrastructure.common.* +import com.sangdol.roomescape.payment.web.PaymentCancelRequest +import com.sangdol.roomescape.payment.web.PaymentConfirmRequest +import com.sangdol.roomescape.reservation.web.PendingReservationCreateRequest +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntityFactory +import com.sangdol.roomescape.schedule.web.ScheduleCreateRequest +import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity +import com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus +import com.sangdol.roomescape.store.web.StoreRegisterRequest +import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity +import com.sangdol.roomescape.theme.web.ThemeCreateRequest +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity +import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus +import com.sangdol.roomescape.user.web.MIN_PASSWORD_LENGTH +import com.sangdol.roomescape.user.web.UserCreateRequest import java.time.LocalDate import java.time.LocalTime import java.time.OffsetDateTime diff --git a/src/test/kotlin/roomescape/supports/KotestConfig.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/KotestConfig.kt similarity index 78% rename from src/test/kotlin/roomescape/supports/KotestConfig.kt rename to service/src/test/kotlin/com/sangdol/roomescape/supports/KotestConfig.kt index 284892ab..fbbe81b0 100644 --- a/src/test/kotlin/roomescape/supports/KotestConfig.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/KotestConfig.kt @@ -1,4 +1,4 @@ -package roomescape.supports +package com.sangdol.roomescape.supports import io.kotest.core.config.AbstractProjectConfig import io.kotest.core.spec.Spec @@ -13,14 +13,14 @@ import org.springframework.boot.test.web.server.LocalServerPort import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Import import org.springframework.test.context.ActiveProfiles -import roomescape.admin.infrastructure.persistence.AdminRepository -import roomescape.payment.business.PaymentWriter -import roomescape.payment.infrastructure.persistence.PaymentRepository -import roomescape.reservation.infrastructure.persistence.ReservationRepository -import roomescape.schedule.infrastructure.persistence.ScheduleRepository -import roomescape.store.infrastructure.persistence.StoreRepository -import roomescape.theme.infrastructure.persistence.ThemeRepository -import roomescape.user.infrastructure.persistence.UserRepository +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminRepository +import com.sangdol.roomescape.payment.business.PaymentWriter +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentRepository +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationRepository +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository +import com.sangdol.roomescape.store.infrastructure.persistence.StoreRepository +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository +import com.sangdol.roomescape.user.infrastructure.persistence.UserRepository object KotestConfig : AbstractProjectConfig() { override fun extensions(): List = listOf(SpringExtension) diff --git a/src/test/kotlin/roomescape/supports/RestAssuredUtils.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt similarity index 95% rename from src/test/kotlin/roomescape/supports/RestAssuredUtils.kt rename to service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt index 689184d2..bf663229 100644 --- a/src/test/kotlin/roomescape/supports/RestAssuredUtils.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt @@ -1,4 +1,4 @@ -package roomescape.supports +package com.sangdol.roomescape.supports import com.fasterxml.jackson.module.kotlin.convertValue import io.restassured.module.kotlin.extensions.Given @@ -10,8 +10,8 @@ import io.restassured.specification.RequestSpecification import org.hamcrest.CoreMatchers.equalTo import org.springframework.http.HttpMethod import org.springframework.http.MediaType -import roomescape.common.config.JacksonConfig -import roomescape.common.exception.ErrorCode +import com.sangdol.roomescape.common.config.JacksonConfig +import com.sangdol.roomescape.common.exception.ErrorCode fun runTest( token: String? = null, diff --git a/src/test/kotlin/roomescape/supports/TestAuthUtil.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt similarity index 86% rename from src/test/kotlin/roomescape/supports/TestAuthUtil.kt rename to service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt index 67379374..b5a68bec 100644 --- a/src/test/kotlin/roomescape/supports/TestAuthUtil.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt @@ -1,4 +1,4 @@ -package roomescape.supports +package com.sangdol.roomescape.supports import io.restassured.module.kotlin.extensions.Extract import io.restassured.module.kotlin.extensions.Given @@ -7,15 +7,15 @@ import io.restassured.module.kotlin.extensions.When import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpStatus import org.springframework.http.MediaType -import roomescape.admin.infrastructure.persistence.AdminEntity -import roomescape.admin.infrastructure.persistence.AdminRepository -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.auth.web.LoginRequest -import roomescape.common.dto.PrincipalType -import roomescape.store.infrastructure.persistence.StoreRepository -import roomescape.user.infrastructure.persistence.UserEntity -import roomescape.user.infrastructure.persistence.UserRepository -import roomescape.user.web.UserCreateRequest +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminRepository +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.auth.web.LoginRequest +import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.store.infrastructure.persistence.StoreRepository +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity +import com.sangdol.roomescape.user.infrastructure.persistence.UserRepository +import com.sangdol.roomescape.user.web.UserCreateRequest class TestAuthUtil( private val userRepository: UserRepository, diff --git a/src/test/kotlin/roomescape/supports/TestDatabaseUtil.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/TestDatabaseUtil.kt similarity index 98% rename from src/test/kotlin/roomescape/supports/TestDatabaseUtil.kt rename to service/src/test/kotlin/com/sangdol/roomescape/supports/TestDatabaseUtil.kt index 07c5591f..60e4d851 100644 --- a/src/test/kotlin/roomescape/supports/TestDatabaseUtil.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/TestDatabaseUtil.kt @@ -1,4 +1,4 @@ -package roomescape.supports +package com.sangdol.roomescape.supports import io.kotest.core.listeners.AfterSpecListener import io.kotest.core.listeners.AfterTestListener diff --git a/src/test/kotlin/roomescape/supports/TestUtil.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/TestUtil.kt similarity index 95% rename from src/test/kotlin/roomescape/supports/TestUtil.kt rename to service/src/test/kotlin/com/sangdol/roomescape/supports/TestUtil.kt index 8b5780e5..0e262845 100644 --- a/src/test/kotlin/roomescape/supports/TestUtil.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/TestUtil.kt @@ -1,4 +1,4 @@ -package roomescape.supports +package com.sangdol.roomescape.supports import kotlin.random.Random diff --git a/src/test/kotlin/roomescape/theme/AdminThemeApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt similarity index 97% rename from src/test/kotlin/roomescape/theme/AdminThemeApiTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt index 4663b0d9..72c10cd4 100644 --- a/src/test/kotlin/roomescape/theme/AdminThemeApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt @@ -1,4 +1,4 @@ -package roomescape.theme +package com.sangdol.roomescape.theme import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe @@ -6,18 +6,18 @@ import org.hamcrest.CoreMatchers.equalTo import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import roomescape.admin.infrastructure.persistence.AdminPermissionLevel -import roomescape.admin.infrastructure.persistence.AdminType -import roomescape.auth.exception.AuthErrorCode -import roomescape.supports.* -import roomescape.supports.ThemeFixture.createRequest -import roomescape.theme.business.MIN_DURATION -import roomescape.theme.business.MIN_PARTICIPANTS -import roomescape.theme.business.MIN_PRICE -import roomescape.theme.exception.ThemeErrorCode -import roomescape.theme.infrastructure.persistence.ThemeEntity -import roomescape.theme.infrastructure.persistence.ThemeRepository -import roomescape.theme.web.ThemeUpdateRequest +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.supports.* +import com.sangdol.roomescape.supports.ThemeFixture.createRequest +import com.sangdol.roomescape.theme.business.MIN_DURATION +import com.sangdol.roomescape.theme.business.MIN_PARTICIPANTS +import com.sangdol.roomescape.theme.business.MIN_PRICE +import com.sangdol.roomescape.theme.exception.ThemeErrorCode +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository +import com.sangdol.roomescape.theme.web.ThemeUpdateRequest class AdminThemeApiTest( private val themeRepository: ThemeRepository diff --git a/src/test/kotlin/roomescape/theme/ThemeApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt similarity index 86% rename from src/test/kotlin/roomescape/theme/ThemeApiTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt index 1fe3e095..58d0446c 100644 --- a/src/test/kotlin/roomescape/theme/ThemeApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt @@ -1,18 +1,19 @@ -package roomescape.theme +package com.sangdol.roomescape.theme import io.kotest.matchers.collections.shouldContainInOrder import io.kotest.matchers.collections.shouldHaveSize import org.hamcrest.CoreMatchers.equalTo import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import roomescape.common.config.next -import roomescape.common.util.DateUtils -import roomescape.supports.* -import roomescape.theme.exception.ThemeErrorCode -import roomescape.theme.infrastructure.persistence.ThemeEntity -import roomescape.theme.infrastructure.persistence.ThemeRepository -import roomescape.theme.web.toEntity -import roomescape.user.infrastructure.persistence.UserEntity +import com.sangdol.roomescape.common.config.next +import com.sangdol.roomescape.common.util.DateUtils +import com.sangdol.roomescape.supports.* +import com.sangdol.roomescape.theme.exception.ThemeErrorCode +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity +import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository +import com.sangdol.roomescape.theme.web.ThemeInfoResponse +import com.sangdol.roomescape.theme.web.toEntity +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity import java.time.LocalDate class ThemeApiTest( @@ -64,10 +65,10 @@ class ThemeApiTest( statusCode(HttpStatus.OK.value()) } ).also { res -> - val response: List> = res.extract().path("data.themes") + val response: List = ResponseParser.parseListResponse(res.extract().path("data.themes")) response shouldHaveSize expectedResult.size - response.map { it["id"] as Long }.shouldContainInOrder(expectedResult) + response.map { it.id }.shouldContainInOrder(expectedResult) } } } diff --git a/src/test/kotlin/roomescape/user/UserApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/user/UserApiTest.kt similarity index 91% rename from src/test/kotlin/roomescape/user/UserApiTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/user/UserApiTest.kt index 371426ff..cfe2703d 100644 --- a/src/test/kotlin/roomescape/user/UserApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/user/UserApiTest.kt @@ -1,4 +1,4 @@ -package roomescape.user +package com.sangdol.roomescape.user import com.ninjasquad.springmockk.SpykBean import io.kotest.assertions.assertSoftly @@ -13,18 +13,18 @@ import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus import org.springframework.http.MediaType -import roomescape.auth.exception.AuthErrorCode -import roomescape.auth.infrastructure.jwt.JwtUtils -import roomescape.common.exception.CommonErrorCode -import roomescape.supports.FunSpecSpringbootTest -import roomescape.supports.UserFixture -import roomescape.supports.runExceptionTest -import roomescape.supports.runTest -import roomescape.user.business.SIGNUP -import roomescape.user.exception.UserErrorCode -import roomescape.user.infrastructure.persistence.* -import roomescape.user.web.MIN_PASSWORD_LENGTH -import roomescape.user.web.UserCreateRequest +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils +import com.sangdol.roomescape.common.exception.CommonErrorCode +import com.sangdol.roomescape.supports.FunSpecSpringbootTest +import com.sangdol.roomescape.supports.UserFixture +import com.sangdol.roomescape.supports.runExceptionTest +import com.sangdol.roomescape.supports.runTest +import com.sangdol.roomescape.user.business.SIGNUP +import com.sangdol.roomescape.user.exception.UserErrorCode +import com.sangdol.roomescape.user.infrastructure.persistence.* +import com.sangdol.roomescape.user.web.MIN_PASSWORD_LENGTH +import com.sangdol.roomescape.user.web.UserCreateRequest class UserApiTest( private val userRepository: UserRepository, diff --git a/src/test/resources/application-test-mysql.yaml b/service/src/test/resources/application-test-mysql.yaml similarity index 100% rename from src/test/resources/application-test-mysql.yaml rename to service/src/test/resources/application-test-mysql.yaml diff --git a/src/test/resources/application-test.yaml b/service/src/test/resources/application-test.yaml similarity index 100% rename from src/test/resources/application-test.yaml rename to service/src/test/resources/application-test.yaml diff --git a/src/test/resources/logback-test.xml b/service/src/test/resources/logback-test.xml similarity index 100% rename from src/test/resources/logback-test.xml rename to service/src/test/resources/logback-test.xml diff --git a/src/main/kotlin/roomescape/common/config/SwaggerConfig.kt b/src/main/kotlin/roomescape/common/config/SwaggerConfig.kt deleted file mode 100644 index 90fc7c5c..00000000 --- a/src/main/kotlin/roomescape/common/config/SwaggerConfig.kt +++ /dev/null @@ -1,78 +0,0 @@ -package roomescape.common.config - -import io.swagger.v3.oas.models.OpenAPI -import io.swagger.v3.oas.models.info.Info -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration - -@Configuration -class SwaggerConfig { - - @Bean - fun openAPI(): OpenAPI { - return OpenAPI().info(apiInfo()) - } - - private fun apiInfo(): Info { - return Info() - .title("방탈출 예약 API 문서") - .description( - """ - ## API 테스트는 '1. 인증 / 인가 API' 의 '/login' 을 통해 로그인 후 사용해주세요. - - ### 테스트시 로그인 가능한 계정 정보 - - - 아래의 JSON 형태의 데이터를 그대로 복사한 뒤 'POST /login' 의 Request Body 에 넣어서 사용해주세요. - - - **관리자**: - { - "email": "a@a.a", - "password": "a" - } - - - - **회원**: - - - 1번 회원 - { - "email": "1@1.1", - "password": "1" - } - - - 2번 회원 - { - "email": "2@2.2", - "password": "2" - } - - - 3번 회원 - { - "email": "3@3.3", - "password": "3" - } - - - 4번 회원 - { - "email": "4@4.4", - "password": "4" - } - - ### 테스트시 사용할 수 있는 파라미터 정보 - - **themeId**: 1(테스트1), 2(테스트2), 3(테스트3), 4(테스트4) - - - **timeId**: 1(15:00), 2(16:00), 3(17:00), 4(18:00) - - - **memberId**: 1(어드민), 2(회원1), 3(회원2), 4(회원3), 5(회원4) - - - **reservationId**: - - 1 ~ 6: 예약 및 결제 완료 상태 - - - 7: 예약은 승인되었으나, 결제 대기 상태 - - - 8 ~ 10: 예약 대기 상태 - - """.trimIndent() - ) - .version("1.0.0") - } -} diff --git a/src/main/kotlin/roomescape/reservation/exception/ReservationException.kt b/src/main/kotlin/roomescape/reservation/exception/ReservationException.kt deleted file mode 100644 index 3d1f82f2..00000000 --- a/src/main/kotlin/roomescape/reservation/exception/ReservationException.kt +++ /dev/null @@ -1,9 +0,0 @@ -package roomescape.reservation.exception - -import roomescape.common.exception.ErrorCode -import roomescape.common.exception.RoomescapeException - -class ReservationException( - override val errorCode: ErrorCode, - override val message: String = errorCode.message -) : RoomescapeException(errorCode, message) diff --git a/src/main/resources/login.http b/src/main/resources/login.http deleted file mode 100644 index f73232b6..00000000 --- a/src/main/resources/login.http +++ /dev/null @@ -1,55 +0,0 @@ -POST http://localhost:8080/login?key=value&key1=value1 -Content-Type: application/json - -{ - "email": "a@a.a", - "password": "a" -} - -> {% - const accessToken = response.body.data.accessToken; - client.global.set("token", accessToken); -%} - -### - -GET http://localhost:8080/reservations -Accept: application/json -Authorization: Bearer {{token}} - -### - -DELETE http://localhost:8080/reservations/57 -Accept: application/json -Authorization: Bearer {{token}} - -### - -POST http://localhost:8080/reservations/admin -Authorization: Bearer {{token}} -Content-Type: application/json - -{ - "date": "2026-10-01", - "timeId": 1, - "themeId": 1, - "memberId": 3 -} - -### - -POST http://localhost:8080/reservations/admin -Authorization: Bearer {{token}} -Content-Type: application/json - -{ - "date": "2023-10-01", - "timeId": 1, - "themeId": 1, - "memberId": 3 -} - -### - -GET http://localhost:8080/reservations-mine -Accept: application/json diff --git a/src/main/resources/test.http b/src/main/resources/test.http deleted file mode 100644 index 193be221..00000000 --- a/src/main/resources/test.http +++ /dev/null @@ -1,4 +0,0 @@ -### GET request to example server -POST localhost:8080/savetest - -### \ No newline at end of file -- 2.47.2 From 2506105c56de5976bd8d75470ca7b07d786f7ca7 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 18:53:44 +0900 Subject: [PATCH 02/39] =?UTF-8?q?refactor:=20JacksonConfig=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/config/build.gradle.kts | 11 + .../sangdol}/common/config/JacksonConfig.kt | 9 +- .../common/config/JacksonConfigTest.kt | 14 +- service/build.gradle.kts | 4 +- .../roomescape/RoomescapeApplication.kt | 4 +- .../log/RoomescapeLogMaskingConverter.kt | 2 +- .../common/log/ApiLogMessageConverterTest.kt | 8 +- .../roomescape/data/DefaultDataInitializer.kt | 1822 ++++++++--------- .../roomescape/supports/RestAssuredUtils.kt | 9 +- 9 files changed, 952 insertions(+), 931 deletions(-) create mode 100644 common/config/build.gradle.kts rename {service/src/main/kotlin/com/sangdol/roomescape => common/config/src/main/kotlin/com/sangdol}/common/config/JacksonConfig.kt (92%) rename {service/src/test/kotlin/com/sangdol/roomescape => common/config/src/test/kotlin/com/sangdol}/common/config/JacksonConfigTest.kt (91%) diff --git a/common/config/build.gradle.kts b/common/config/build.gradle.kts new file mode 100644 index 00000000..e96cfeba --- /dev/null +++ b/common/config/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("org.springframework.boot") + kotlin("plugin.spring") +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + + testImplementation("io.kotest:kotest-runner-junit5:5.9.1") +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/config/JacksonConfig.kt b/common/config/src/main/kotlin/com/sangdol/common/config/JacksonConfig.kt similarity index 92% rename from service/src/main/kotlin/com/sangdol/roomescape/common/config/JacksonConfig.kt rename to common/config/src/main/kotlin/com/sangdol/common/config/JacksonConfig.kt index b5662a33..4dc73d80 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/config/JacksonConfig.kt +++ b/common/config/src/main/kotlin/com/sangdol/common/config/JacksonConfig.kt @@ -1,4 +1,4 @@ -package com.sangdol.roomescape.common.config +package com.sangdol.common.config import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.DeserializationFeature @@ -23,6 +23,9 @@ class JacksonConfig { companion object { private val ISO_OFFSET_DATE_TIME_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX") + + private val LOCAL_TIME_FORMATTER: DateTimeFormatter = + DateTimeFormatter.ofPattern("HH:mm") } @Bean @@ -43,11 +46,11 @@ class JacksonConfig { ) .addSerializer( LocalTime::class.java, - LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm")) + LocalTimeSerializer(LOCAL_TIME_FORMATTER) ) .addDeserializer( LocalTime::class.java, - LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm")) + LocalTimeDeserializer(LOCAL_TIME_FORMATTER) ) as JavaTimeModule private fun dateTimeModule(): SimpleModule { diff --git a/service/src/test/kotlin/com/sangdol/roomescape/common/config/JacksonConfigTest.kt b/common/config/src/test/kotlin/com/sangdol/common/config/JacksonConfigTest.kt similarity index 91% rename from service/src/test/kotlin/com/sangdol/roomescape/common/config/JacksonConfigTest.kt rename to common/config/src/test/kotlin/com/sangdol/common/config/JacksonConfigTest.kt index 8ebb7557..b98b02fa 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/common/config/JacksonConfigTest.kt +++ b/common/config/src/test/kotlin/com/sangdol/common/config/JacksonConfigTest.kt @@ -1,4 +1,4 @@ -package com.sangdol.roomescape.common.config +package com.sangdol.common.config import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.exc.InvalidFormatException @@ -6,11 +6,15 @@ import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain -import java.time.* +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.ZoneOffset -class JacksonConfigTest( - private val objectMapper: ObjectMapper = JacksonConfig().objectMapper() -) : FunSpec({ +class JacksonConfigTest : FunSpec({ + + val objectMapper: ObjectMapper = JacksonConfig().objectMapper() context("날짜는 yyyy-mm-dd 형식이다.") { val date = "2025-07-14" diff --git a/service/build.gradle.kts b/service/build.gradle.kts index db40bee3..2d95de4b 100644 --- a/service/build.gradle.kts +++ b/service/build.gradle.kts @@ -35,7 +35,6 @@ dependencies { // Kotlin implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("io.github.oshai:kotlin-logging-jvm:7.0.3") // Test @@ -53,6 +52,9 @@ dependencies { // etc implementation("org.apache.poi:poi-ooxml:5.2.3") + + // submodules + implementation(project(":common:config")) } tasks.jar { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/RoomescapeApplication.kt b/service/src/main/kotlin/com/sangdol/roomescape/RoomescapeApplication.kt index 0e420eda..c8b5682f 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/RoomescapeApplication.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/RoomescapeApplication.kt @@ -4,7 +4,9 @@ import org.springframework.boot.Banner import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.SpringBootApplication -@SpringBootApplication +@SpringBootApplication( + scanBasePackages = ["com.sangdol.roomescape", "com.sangdol.common"] +) class RoomescapeApplication fun main(args: Array) { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt index 714cb70b..70abcc43 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.ArrayNode import com.fasterxml.jackson.databind.node.ObjectNode import com.fasterxml.jackson.databind.node.TextNode -import com.sangdol.roomescape.common.config.JacksonConfig +import com.sangdol.common.config.JacksonConfig private const val MASK: String = "****" private val SENSITIVE_KEYS = setOf("password", "accessToken", "phone") diff --git a/service/src/test/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverterTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverterTest.kt index c420d817..cc8eec20 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverterTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverterTest.kt @@ -1,17 +1,17 @@ package com.sangdol.roomescape.common.log -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.sangdol.common.config.JacksonConfig +import com.sangdol.roomescape.auth.exception.AuthErrorCode +import com.sangdol.roomescape.auth.exception.AuthException import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk import jakarta.servlet.http.HttpServletRequest import org.slf4j.MDC -import com.sangdol.roomescape.auth.exception.AuthErrorCode -import com.sangdol.roomescape.auth.exception.AuthException class ApiLogMessageConverterTest : StringSpec({ - val converter = ApiLogMessageConverter(jacksonObjectMapper()) + val converter = ApiLogMessageConverter(JacksonConfig().objectMapper()) val request: HttpServletRequest = mockk() beforeTest { diff --git a/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt b/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt index 06785315..6dd76aa4 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt @@ -1,911 +1,911 @@ -package com.sangdol.roomescape.data - -import com.github.f4b6a3.tsid.TsidFactory -import io.kotest.core.test.TestCaseOrder -import jakarta.persistence.EntityManager -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Semaphore -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.jdbc.core.JdbcTemplate -import org.springframework.test.context.ActiveProfiles -import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity -import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel -import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -import com.sangdol.roomescape.common.config.next -import com.sangdol.roomescape.common.util.TransactionExecutionUtil -import com.sangdol.roomescape.payment.infrastructure.common.* -import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus -import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus -import com.sangdol.roomescape.supports.AdminFixture -import com.sangdol.roomescape.supports.FunSpecSpringbootTest -import com.sangdol.roomescape.supports.randomPhoneNumber -import com.sangdol.roomescape.supports.randomString -import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty -import com.sangdol.roomescape.user.business.SIGNUP -import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity -import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus -import com.sangdol.roomescape.user.web.UserContactResponse -import java.sql.Timestamp -import java.time.LocalDateTime -import java.time.LocalTime -import java.time.OffsetDateTime - -@ActiveProfiles("test", "test-mysql") -abstract class AbstractDataInitializer( - val semaphore: Semaphore = Semaphore(permits = 10), -) : FunSpecSpringbootTest( - enableCleanerExtension = false -) { - @Autowired - lateinit var entityManager: EntityManager - - @Autowired - lateinit var jdbcTemplate: JdbcTemplate - - @Autowired - lateinit var transactionExecutionUtil: TransactionExecutionUtil - - @Autowired - lateinit var tsidFactory: TsidFactory - - override fun testCaseOrder(): TestCaseOrder? = TestCaseOrder.Sequential - - suspend fun initialize() { - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0") - - jdbcTemplate.query("SHOW TABLES") { rs, _ -> - rs.getString(1).lowercase() - }.forEach { - jdbcTemplate.execute("TRUNCATE TABLE $it") - } - - jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1") - - this::class.java.getResource("/schema/region-data.sql")?.readText()?.let { sql -> - jdbcTemplate.execute(sql) - } - } - } - - suspend fun executeBatch(sql: String, batchArgs: List>) { - semaphore.acquire() - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - jdbcTemplate.batchUpdate(sql, batchArgs) - } - - semaphore.release() - } -} - -class DefaultDataInitializer : AbstractDataInitializer() { - - // 1. HQ Admin 추가 - // 2. Store 추가 -> CreatedBy / UpdatedBy는 HQ Admin 중 한명으로 고정 - // 3. Store Admin 추가 -> CreatedBy / UpdatedBy는 HQ Admin 본인으로 고정 - init { - lateinit var superHQAdmin: AdminEntity - - // 모든 테이블 초기화 + 지역 데이터 삽입 + HQ 관리자 1명 생성 - beforeSpec { - initialize() - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - superHQAdmin = testAuthUtil.createAdmin(AdminFixture.hqDefault.apply { - this.createdBy = this.id - this.updatedBy = this.id - }) - } - } - - context("관리자, 매장, 테마 초기 데이터 생성") { - test("각 PermissionLevel 마다 20명의 HQ 관리자 생성") { - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - AdminPermissionLevel.entries.forEach { - repeat(20) { index -> - AdminFixture.create( - account = "hq_${it.name.lowercase()}_$index", - name = randomKoreanName(), - phone = randomPhoneNumber(), - type = AdminType.HQ, - permissionLevel = it - ).apply { - this.createdBy = superHQAdmin.id - this.updatedBy = superHQAdmin.id - }.also { - entityManager.persist(it) - } - } - } - } - } - - test("전체 매장 생성") { - val storeDataInitializer = StoreDataInitializer() - val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", - Long::class.java - ).setParameter("type", AdminType.HQ) - .setParameter( - "permissionLevels", - listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) - ) - .resultList - }.map { it.toString() } - - val sqlFile = storeDataInitializer.createStoreDataSqlFile(creatableAdminIds) - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - jdbcTemplate.execute(sqlFile.readText()) - } - } - - test("각 매장당 1명의 ${AdminPermissionLevel.FULL_ACCESS} 권한의 StoreManager 1명 + ${AdminPermissionLevel.WRITABLE}의 2명 + 나머지 권한은 3명씩 생성") { - val storeAdminCountsByPermissionLevel = mapOf( - AdminPermissionLevel.WRITABLE to 2, - AdminPermissionLevel.READ_ALL to 3, - AdminPermissionLevel.READ_SUMMARY to 3 - ) - - val storeIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT s.id FROM StoreEntity s", - Long::class.java - ).resultList - }.map { it as Long } - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - storeIds.forEach { storeId -> - // StoreManager 1명 생성 - val storeManager = AdminFixture.create( - account = "$storeId", - name = randomKoreanName(), - phone = randomPhoneNumber(), - type = AdminType.STORE, - storeId = storeId, - permissionLevel = AdminPermissionLevel.FULL_ACCESS - ).apply { - this.createdBy = superHQAdmin.id - this.updatedBy = superHQAdmin.id - }.also { - entityManager.persist(it) - } - - storeAdminCountsByPermissionLevel.forEach { (permissionLevel, count) -> - repeat(count) { index -> - AdminFixture.create( - account = randomString(), - name = randomKoreanName(), - phone = randomPhoneNumber(), - type = AdminType.STORE, - storeId = storeId, - permissionLevel = permissionLevel - ).apply { - this.createdBy = storeManager.id - this.updatedBy = storeManager.id - }.also { - entityManager.persist(it) - } - } - } - entityManager.flush() - entityManager.clear() - } - } - } - - test("총 500개의 테마 생성: 지난 2년 전 부터 지금까지의 랜덤 테마 + active 상태인 1달 이내 생성 테마 10개") { - val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", - Long::class.java - ).setParameter("type", AdminType.HQ) - .setParameter( - "permissionLevels", - listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) - ) - .resultList - } - val sql = - "INSERT INTO theme (id, name, description, thumbnail_url, is_active, available_minutes, expected_minutes_from, expected_minutes_to, price, difficulty, min_participants, max_participants, created_at, created_by, updated_at, updated_by) VALUES (?," + - "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - val batchSize = 100 - val batchArgs = mutableListOf>() - - repeat(500) { i -> - val randomDay = if (i <= 9) (1..30).random() else (1..365 * 2).random() - val randomCreatedAt: LocalDateTime = LocalDateTime.now().minusDays(randomDay.toLong()) - val randomThemeName = - (1..7).random().let { repeat -> (1..repeat).joinToString("") { randomKoreanName() } } - val availableMinutes = (6..20).random() * 10 - val expectedMinutesTo = availableMinutes - ((1..3).random() * 10) - val expectedMinutesFrom = expectedMinutesTo - ((1..2).random() * 10) - val randomPrice = (0..40).random() * 500 - val minParticipant = (1..10).random() - val maxParticipant = minParticipant + (1..10).random() - val createdBy = creatableAdminIds.random() - - batchArgs.add( - arrayOf( - tsidFactory.next(), - randomThemeName, - "$randomThemeName 설명이에요!!", - "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFiuCdwdz88l6pdfsRy1nFl0IHUVI7JMTQHg&s", - if (randomDay <= 30) true else false, - availableMinutes.toShort(), - expectedMinutesFrom.toShort(), - expectedMinutesTo.toShort(), - randomPrice, - Difficulty.entries.random().name, - minParticipant.toShort(), - maxParticipant.toShort(), - Timestamp.valueOf(randomCreatedAt), - createdBy, - Timestamp.valueOf(randomCreatedAt), - createdBy - ) - ) - } - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - jdbcTemplate.batchUpdate(sql, batchArgs) - } - } - } - } -} - -class UserDataInitializer : AbstractDataInitializer() { - val userCount = 1_000_000 - - init { - context("유저 초기 데이터 생성") { - test("$userCount 명의 회원 생성") { - val regions: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT r.code FROM RegionEntity r", - String::class.java - ).resultList - } - - val chunkSize = 10_000 - val chunks = userCount / chunkSize - - coroutineScope { - (1..chunks).map { - launch(Dispatchers.IO) { - processUsers(chunkSize, regions) - } - }.joinAll() - } - } - - test("휴대폰 번호가 중복된 유저들에게 재배정") { - val duplicatePhoneUsers: List = - transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - """ - SELECT u FROM UserEntity u - WHERE u.phone IN ( - SELECT u2.phone FROM UserEntity u2 - GROUP BY u2.phone - HAVING COUNT(u2.id) > 1 - ) - ORDER BY u.phone, u.id - """.trimIndent(), - UserEntity::class.java - ).resultList - } - - jdbcTemplate.execute("CREATE INDEX idx_users__phone ON users (phone)") - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - var currentPhone: String? = null - duplicatePhoneUsers.forEach { user -> - if (user.phone != currentPhone) { - currentPhone = user.phone - } else { - var newPhone: String - - do { - newPhone = randomPhoneNumber() - val count: Long = entityManager.createQuery( - "SELECT COUNT(u.id) FROM UserEntity u WHERE u.phone = :phone", - Long::class.java - ).setParameter("phone", newPhone) - .singleResult - - if (count == 0L) break - } while (true) - - user.phone = newPhone - user.updatedAt = LocalDateTime.now() - entityManager.merge(user) - } - } - } - - jdbcTemplate.execute("DROP INDEX idx_users__phone ON users") - } - - test("회원 상태 변경 이력 저장") { - val userId: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT u.id FROM UserEntity u", - Long::class.java - ).resultList - } - - coroutineScope { - userId.chunked(10_000).map { chunk -> - launch(Dispatchers.IO) { - processStatus(chunk) - } - }.joinAll() - } - } - } - } - - private suspend fun processStatus(userIds: List) { - val sql = """ - INSERT INTO user_status_history ( - id, user_id, reason, status, - created_by, updated_by, created_at, updated_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - val batchArgs = mutableListOf>() - val now = LocalDateTime.now() - - userIds.forEach { userId -> - batchArgs.add( - arrayOf( - tsidFactory.next(), - userId, - SIGNUP, - UserStatus.ACTIVE.name, - userId, - userId, - Timestamp.valueOf(now), - Timestamp.valueOf(now) - ) - ) - } - - executeBatch(sql, batchArgs).also { batchArgs.clear() } - } - - private suspend fun processUsers(chunkSize: Int, regions: List) { - val sql = """ - INSERT INTO users ( - id, name, email, password, phone, region_code, status, - created_by, updated_by, created_at, updated_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - val batchArgs = mutableListOf>() - - repeat(chunkSize) { - val id: Long = tsidFactory.next() - batchArgs.add( - arrayOf( - id, - randomKoreanName(), - "${randomString()}@sangdol.com", - randomString(), - randomPhoneNumber(), - regions.random(), - UserStatus.ACTIVE.name, - id, - id, - Timestamp.valueOf(LocalDateTime.now()), - Timestamp.valueOf(LocalDateTime.now()) - ) - ) - if (batchArgs.size >= 1_000) { - executeBatch(sql, batchArgs).also { batchArgs.clear() } - } - } - - if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } - } -} - -class ScheduleDataInitializer : AbstractDataInitializer() { - init { - context("일정 초기 데이터 생성") { - test("테마 생성일 기준으로 다음 3일차, 매일 5개의 일정을 모든 매장에 생성") { - val stores: List> = getStoreWithManagers() - val themes: List> = getThemes() - val maxAvailableMinutes = themes.maxOf { it.second.toInt() } - val scheduleCountPerDay = 5 - - val startTime = LocalTime.of(10, 0) - var lastTime = startTime - val times = mutableListOf() - - repeat(scheduleCountPerDay) { - times.add(lastTime) - lastTime = lastTime.plusMinutes(maxAvailableMinutes.toLong() + 10L) - } - - coroutineScope { - themes.forEach { theme -> - launch(Dispatchers.IO) { - processTheme(theme, stores, times) - } - } - } - } - } - } - - private suspend fun processTheme( - theme: Triple, - stores: List>, - times: List - ) { - val sql = """ - INSERT INTO schedule ( - id, store_id, theme_id, date, time, status, - created_by, updated_by, created_at, updated_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - - val batchArgs = mutableListOf>() - - val now = LocalDateTime.now() - stores.forEach { (storeId, adminId) -> - (1..3).forEach { dayOffset -> - val date = theme.third.toLocalDate().plusDays(dayOffset.toLong()) - - times.forEach { time -> - val scheduledAt = LocalDateTime.of(date, time) - val status = - if (scheduledAt.isAfter(now)) ScheduleStatus.AVAILABLE.name else ScheduleStatus.RESERVED.name - - batchArgs.add( - arrayOf( - tsidFactory.next(), storeId, theme.first, date, time, - status, adminId, adminId, Timestamp.valueOf(now), Timestamp.valueOf(now) - ) - ) - - if (batchArgs.size >= 500) { - executeBatch(sql, batchArgs).also { batchArgs.clear() } - } - } - } - } - - if (batchArgs.isNotEmpty()) { - executeBatch(sql, batchArgs).also { batchArgs.clear() } - } - } - - private fun getStoreWithManagers(): List> { - return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT a.storeId, a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel = :permissionLevel", - List::class.java - ).setParameter("type", AdminType.STORE) - .setParameter("permissionLevel", AdminPermissionLevel.FULL_ACCESS) - .resultList - }.map { - val array = it as List<*> - Pair(array[0] as Long, array[1] as Long) - } - } - - private fun getThemes(): List> { - return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT t._id, t.availableMinutes, t.createdAt FROM ThemeEntity t", - List::class.java - ) - .resultList - }.map { - val array = it as List<*> - Triple(array[0] as Long, array[1] as Short, array[2] as LocalDateTime) - } - } -} - -/** - * 아래의 ReservationDataInitializer 에서 사용할 임시 DTO 클래스 - */ -data class ScheduleWithThemeParticipants( - val scheduleId: Long, - val themeMinParticipants: Short, - val themeMaxParticipants: Short, -) - -class ReservationDataInitializer : AbstractDataInitializer() { - - init { - context("예약 초기 데이터 생성") { - test("${ScheduleStatus.RESERVED}인 모든 일정에 예약을 1개씩 배정한다.") { - val chunkSize = 10_000 - - val chunkedSchedules: List> = entityManager.createQuery( - "SELECT new com.sangdol.roomescape.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status", - ScheduleWithThemeParticipants::class.java - ).setParameter("status", ScheduleStatus.RESERVED).resultList.chunked(chunkSize) - - val chunkedUsers: List> = entityManager.createQuery( - "SELECT new com.sangdol.roomescape.user.web.UserContactResponse(u._id, u.name, u.phone) FROM UserEntity u", - UserContactResponse::class.java - ).resultList.chunked(chunkSize) - - - coroutineScope { - chunkedSchedules.forEachIndexed { idx, schedules -> - launch(Dispatchers.IO) { - processReservation(chunkedUsers[idx % chunkedUsers.size], schedules) - } - } - } - } - } - } - - private suspend fun processReservation( - users: List, - schedules: List - ) { - val sql = """ - INSERT INTO reservation ( - id, user_id, schedule_id, - reserver_name, reserver_contact, participant_count, requirement, - status, created_at, created_by, updated_at, updated_by - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - - val batchArgs = mutableListOf>() - - val createdAt = LocalDateTime.now() - - schedules.forEachIndexed { idx, schedule -> - val user: UserContactResponse = users[idx % users.size] - - batchArgs.add( - arrayOf( - tsidFactory.next(), - user.id, - schedule.scheduleId, - user.name, - user.phone, - (schedule.themeMinParticipants..schedule.themeMaxParticipants).random(), - randomKoreanWords(length = (20..100).random()), - ReservationStatus.CONFIRMED.name, - Timestamp.valueOf(createdAt), - user.id, - Timestamp.valueOf(createdAt), - user.id, - ) - ) - - if (batchArgs.size >= 1_000) { - executeBatch(sql, batchArgs).also { batchArgs.clear() } - } - } - - if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } - } -} - -class ReservationWithPrice( - themePrice: Int, - participantCount: Short, - - val reservationId: Long, - val totalPrice: Int = (themePrice * participantCount), -) - -data class PaymentWithMethods( - val id: Long, - val totalPrice: Int, - val method: PaymentMethod -) - -class PaymentDataInitializer : AbstractDataInitializer() { - companion object { - val requestedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().toLocalDateTime()) - val approvedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().plusSeconds(5).toLocalDateTime()) - val supportedPaymentMethods = listOf(PaymentMethod.TRANSFER, PaymentMethod.EASY_PAY, PaymentMethod.CARD) - val supportedCardType = listOf(CardType.CREDIT, CardType.CHECK) - - val settlementStatus = "COMPLETED" - - val paymentSql: String = """ - INSERT INTO payment( - id, reservation_id, type, method, - payment_key, order_id, total_amount, status, - requested_at, approved_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - - val paymentDetailSql: String = """ - INSERT INTO payment_detail( - id, payment_id, supplied_amount, vat - ) VALUES (?, ?, ?, ?) - """.trimIndent() - - val paymentCardDetailSql: String = """ - INSERT INTO payment_card_detail( - id, issuer_code, card_type, owner_type, - amount, card_number, approval_number, installment_plan_months, - is_interest_free, easypay_provider_code, easypay_discount_amount - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - - val paymentEasypayPrepaidDetailSql: String = """ - INSERT INTO payment_easypay_prepaid_detail( - id, easypay_provider_code, amount, discount_amount - ) VALUES (?, ?, ?, ?) - """.trimIndent() - - val paymentBankTransferDetailSql: String = """ - INSERT INTO payment_bank_transfer_detail( - id, bank_code, settlement_status - ) VALUES (?, ?, ?) - """.trimIndent() - } - - init { - context("결제 데이터 초기화") { - test("모든 예약에 맞춰 1:1로 결제 및 결제 상세 데이터를 생성한다.") { - val allReservations: List = entityManager.createQuery( - "SELECT t.price, r.participantCount, r._id FROM ReservationEntity r JOIN ScheduleEntity s ON s._id = r.scheduleId JOIN ThemeEntity t ON t.id = s.themeId", - List::class.java - ).resultList.map { - val items = it as List<*> - ReservationWithPrice( - themePrice = items[0] as Int, - participantCount = items[1] as Short, - reservationId = items[2] as Long - ) - } - - coroutineScope { - allReservations.chunked(10_000).forEach { reservations -> - launch(Dispatchers.IO) { - processPaymentAndDefaultDetail(reservations) - } - } - } - } - - test("기존 결제 데이터에 상세 정보(계좌이체, 카드, 간편결제) 데이터를 생성한다.") { - val allPayments: List = entityManager.createQuery( - "SELECT new com.sangdol.roomescape.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId", - PaymentWithMethods::class.java - ).resultList - - coroutineScope { - allPayments.chunked(10_000).forEach { payments -> - launch(Dispatchers.IO) { - processPaymentDetail(payments) - } - } - } - } - - test("null 컴파일 에러를 피하기 위해 문자열 null로 임시 지정한 컬럼을 변경한다.") { - jdbcTemplate.execute("CREATE INDEX idx_payment_card_detail_easypay ON payment_card_detail (easypay_provider_code)") - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - jdbcTemplate.update( - "UPDATE payment_card_detail SET easypay_provider_code = ? WHERE easypay_provider_code = ?", - null, - "null" - ) - } - - jdbcTemplate.execute("DROP INDEX idx_payment_card_detail_easypay ON payment_card_detail") - } - } - } - - private suspend fun processPaymentAndDefaultDetail(reservations: List) { - val paymentBatchArgs = mutableListOf>() - val detailBatchArgs = mutableListOf>() - - reservations.forEachIndexed { idx, reservations -> - val id = tsidFactory.next() - val totalPrice = reservations.totalPrice - paymentBatchArgs.add( - arrayOf( - id, - reservations.reservationId, - PaymentType.NORMAL.name, - randomPaymentMethod(), - randomString(length = 64), - randomString(length = 20), - totalPrice, - PaymentStatus.DONE.name, - requestedAtCache, - approvedAtCache, - ) - ) - if (paymentBatchArgs.size >= 1_000) { - executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } - } - - val suppliedAmount: Int = (totalPrice * 0.9).toInt() - val vat: Int = (totalPrice - suppliedAmount) - - detailBatchArgs.add( - arrayOf( - tsidFactory.next(), - id, - suppliedAmount, - vat - ) - ) - - if (detailBatchArgs.size >= 1_000) { - executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } - } - } - - if (paymentBatchArgs.isNotEmpty()) { - executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } - } - if (detailBatchArgs.isNotEmpty()) { - executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } - } - } - - private suspend fun processPaymentDetail(payments: List) { - val transferBatchArgs = mutableListOf>() - val cardBatchArgs = mutableListOf>() - val easypayPrepaidBatchArgs = mutableListOf>() - - payments.forEach { payment -> - val totalPrice = payment.totalPrice - val randomDiscountAmount = - if (totalPrice < 100 || Math.random() < 0.8) 0 else ((100..totalPrice).random() / 100) * 100 - val amount = totalPrice - randomDiscountAmount - - when (payment.method) { - PaymentMethod.TRANSFER -> { - transferBatchArgs.add( - arrayOf( - payment.id, - BankCode.entries.random().name, - settlementStatus - ) - ) - if (transferBatchArgs.size >= 1_000) { - executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } - } - } - - PaymentMethod.EASY_PAY -> { - if (Math.random() <= 0.7) { - cardBatchArgs.add( - arrayOf( - payment.id, - CardIssuerCode.entries.random().name, - supportedCardType.random().name, - CardOwnerType.PERSONAL.name, - amount, - randomCardNumber(), - randomApprovalNumber(), - randomInstallmentPlanMonths(amount), - true, - EasyPayCompanyCode.entries.random().name, - randomDiscountAmount - ) - ) - - if (cardBatchArgs.size >= 1_000) { - executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } - } - - } else { - easypayPrepaidBatchArgs.add( - arrayOf( - payment.id, - EasyPayCompanyCode.entries.random().name, - amount, - randomDiscountAmount, - ) - ) - - if (easypayPrepaidBatchArgs.size >= 1_000) { - executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } - } - } - } - - PaymentMethod.CARD -> { - cardBatchArgs.add( - arrayOf( - payment.id, - CardIssuerCode.entries.random().name, - supportedCardType.random().name, - CardOwnerType.PERSONAL.name, - totalPrice, - randomCardNumber(), - randomApprovalNumber(), - randomInstallmentPlanMonths(totalPrice), - true, - "null", - 0, - ) - ) - - if (cardBatchArgs.size >= 1_000) { - executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } - } - } - - else -> return@forEach - } - } - if (transferBatchArgs.isNotEmpty()) { - executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } - } - if (cardBatchArgs.isNotEmpty()) { - executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } - } - if (easypayPrepaidBatchArgs.isNotEmpty()) { - executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } - } - } - - private suspend fun randomPaymentMethod(): String { - val random = Math.random() - - return if (random <= 0.5) { - PaymentMethod.EASY_PAY.name - } else if (random <= 0.9) { - PaymentMethod.CARD.name - } else { - PaymentMethod.TRANSFER.name - } - } - - private suspend fun randomCardNumber(): String { - return "${(10000000..99999999).random()}****${(100..999).random()}*" - } - - private suspend fun randomApprovalNumber(): String { - return "${(10000000..99999999).random()}" - } - - private suspend fun randomInstallmentPlanMonths(amount: Int): Int { - return if (amount < 50_000 || Math.random() < 0.9) { - 0 - } else { - (1..6).random() - } - } -} - -fun randomKoreanName(): String { - val lastNames = listOf( - "김", "이", "박", "최", "정", "강", "조", "윤", "장", "임", - "오", "서", "신", "권", "황", "안", "송", "류", "홍", "전", "고", "문", "양", "손", "배", "백", "허", "유", "남", "심", "노" - ) - - return "${lastNames.random()}${if (Math.random() < 0.1) randomKoreanWords(1) else randomKoreanWords(2)}" -} - -fun randomKoreanWords(length: Int = 1): String { - val words = listOf( - "가", "나", "다", "라", "마", "바", "사", "아", "자", "차", - "카", "타", "파", "하", "강", "민", "서", "윤", "우", "진", - "현", "지", "은", "혜", "수", "영", "주", "원", "희", "경", - "선", "아", "나", "다", "라", "마", "바", "사", "아", "자", - "차", "카", "타", "파", "하", - ) - - return (1..length).joinToString("") { words.random() } -} +//package com.sangdol.roomescape.data +// +//import com.github.f4b6a3.tsid.TsidFactory +//import io.kotest.core.test.TestCaseOrder +//import jakarta.persistence.EntityManager +//import kotlinx.coroutines.Dispatchers +//import kotlinx.coroutines.coroutineScope +//import kotlinx.coroutines.joinAll +//import kotlinx.coroutines.launch +//import kotlinx.coroutines.sync.Semaphore +//import org.springframework.beans.factory.annotation.Autowired +//import org.springframework.jdbc.core.JdbcTemplate +//import org.springframework.test.context.ActiveProfiles +//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity +//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +//import com.sangdol.roomescape.common.config.next +//import com.sangdol.roomescape.common.util.TransactionExecutionUtil +//import com.sangdol.roomescape.payment.infrastructure.common.* +//import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus +//import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +//import com.sangdol.roomescape.supports.AdminFixture +//import com.sangdol.roomescape.supports.FunSpecSpringbootTest +//import com.sangdol.roomescape.supports.randomPhoneNumber +//import com.sangdol.roomescape.supports.randomString +//import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty +//import com.sangdol.roomescape.user.business.SIGNUP +//import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity +//import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus +//import com.sangdol.roomescape.user.web.UserContactResponse +//import java.sql.Timestamp +//import java.time.LocalDateTime +//import java.time.LocalTime +//import java.time.OffsetDateTime +// +//@ActiveProfiles("test", "test-mysql") +//abstract class AbstractDataInitializer( +// val semaphore: Semaphore = Semaphore(permits = 10), +//) : FunSpecSpringbootTest( +// enableCleanerExtension = false +//) { +// @Autowired +// lateinit var entityManager: EntityManager +// +// @Autowired +// lateinit var jdbcTemplate: JdbcTemplate +// +// @Autowired +// lateinit var transactionExecutionUtil: TransactionExecutionUtil +// +// @Autowired +// lateinit var tsidFactory: TsidFactory +// +// override fun testCaseOrder(): TestCaseOrder? = TestCaseOrder.Sequential +// +// suspend fun initialize() { +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0") +// +// jdbcTemplate.query("SHOW TABLES") { rs, _ -> +// rs.getString(1).lowercase() +// }.forEach { +// jdbcTemplate.execute("TRUNCATE TABLE $it") +// } +// +// jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1") +// +// this::class.java.getResource("/schema/region-data.sql")?.readText()?.let { sql -> +// jdbcTemplate.execute(sql) +// } +// } +// } +// +// suspend fun executeBatch(sql: String, batchArgs: List>) { +// semaphore.acquire() +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// jdbcTemplate.batchUpdate(sql, batchArgs) +// } +// +// semaphore.release() +// } +//} +// +//class DefaultDataInitializer : AbstractDataInitializer() { +// +// // 1. HQ Admin 추가 +// // 2. Store 추가 -> CreatedBy / UpdatedBy는 HQ Admin 중 한명으로 고정 +// // 3. Store Admin 추가 -> CreatedBy / UpdatedBy는 HQ Admin 본인으로 고정 +// init { +// lateinit var superHQAdmin: AdminEntity +// +// // 모든 테이블 초기화 + 지역 데이터 삽입 + HQ 관리자 1명 생성 +// beforeSpec { +// initialize() +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// superHQAdmin = testAuthUtil.createAdmin(AdminFixture.hqDefault.apply { +// this.createdBy = this.id +// this.updatedBy = this.id +// }) +// } +// } +// +// context("관리자, 매장, 테마 초기 데이터 생성") { +// test("각 PermissionLevel 마다 20명의 HQ 관리자 생성") { +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// AdminPermissionLevel.entries.forEach { +// repeat(20) { index -> +// AdminFixture.create( +// account = "hq_${it.name.lowercase()}_$index", +// name = randomKoreanName(), +// phone = randomPhoneNumber(), +// type = AdminType.HQ, +// permissionLevel = it +// ).apply { +// this.createdBy = superHQAdmin.id +// this.updatedBy = superHQAdmin.id +// }.also { +// entityManager.persist(it) +// } +// } +// } +// } +// } +// +// test("전체 매장 생성") { +// val storeDataInitializer = StoreDataInitializer() +// val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", +// Long::class.java +// ).setParameter("type", AdminType.HQ) +// .setParameter( +// "permissionLevels", +// listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) +// ) +// .resultList +// }.map { it.toString() } +// +// val sqlFile = storeDataInitializer.createStoreDataSqlFile(creatableAdminIds) +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// jdbcTemplate.execute(sqlFile.readText()) +// } +// } +// +// test("각 매장당 1명의 ${AdminPermissionLevel.FULL_ACCESS} 권한의 StoreManager 1명 + ${AdminPermissionLevel.WRITABLE}의 2명 + 나머지 권한은 3명씩 생성") { +// val storeAdminCountsByPermissionLevel = mapOf( +// AdminPermissionLevel.WRITABLE to 2, +// AdminPermissionLevel.READ_ALL to 3, +// AdminPermissionLevel.READ_SUMMARY to 3 +// ) +// +// val storeIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT s.id FROM StoreEntity s", +// Long::class.java +// ).resultList +// }.map { it as Long } +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// storeIds.forEach { storeId -> +// // StoreManager 1명 생성 +// val storeManager = AdminFixture.create( +// account = "$storeId", +// name = randomKoreanName(), +// phone = randomPhoneNumber(), +// type = AdminType.STORE, +// storeId = storeId, +// permissionLevel = AdminPermissionLevel.FULL_ACCESS +// ).apply { +// this.createdBy = superHQAdmin.id +// this.updatedBy = superHQAdmin.id +// }.also { +// entityManager.persist(it) +// } +// +// storeAdminCountsByPermissionLevel.forEach { (permissionLevel, count) -> +// repeat(count) { index -> +// AdminFixture.create( +// account = randomString(), +// name = randomKoreanName(), +// phone = randomPhoneNumber(), +// type = AdminType.STORE, +// storeId = storeId, +// permissionLevel = permissionLevel +// ).apply { +// this.createdBy = storeManager.id +// this.updatedBy = storeManager.id +// }.also { +// entityManager.persist(it) +// } +// } +// } +// entityManager.flush() +// entityManager.clear() +// } +// } +// } +// +// test("총 500개의 테마 생성: 지난 2년 전 부터 지금까지의 랜덤 테마 + active 상태인 1달 이내 생성 테마 10개") { +// val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", +// Long::class.java +// ).setParameter("type", AdminType.HQ) +// .setParameter( +// "permissionLevels", +// listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) +// ) +// .resultList +// } +// val sql = +// "INSERT INTO theme (id, name, description, thumbnail_url, is_active, available_minutes, expected_minutes_from, expected_minutes_to, price, difficulty, min_participants, max_participants, created_at, created_by, updated_at, updated_by) VALUES (?," + +// "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" +// val batchSize = 100 +// val batchArgs = mutableListOf>() +// +// repeat(500) { i -> +// val randomDay = if (i <= 9) (1..30).random() else (1..365 * 2).random() +// val randomCreatedAt: LocalDateTime = LocalDateTime.now().minusDays(randomDay.toLong()) +// val randomThemeName = +// (1..7).random().let { repeat -> (1..repeat).joinToString("") { randomKoreanName() } } +// val availableMinutes = (6..20).random() * 10 +// val expectedMinutesTo = availableMinutes - ((1..3).random() * 10) +// val expectedMinutesFrom = expectedMinutesTo - ((1..2).random() * 10) +// val randomPrice = (0..40).random() * 500 +// val minParticipant = (1..10).random() +// val maxParticipant = minParticipant + (1..10).random() +// val createdBy = creatableAdminIds.random() +// +// batchArgs.add( +// arrayOf( +// tsidFactory.next(), +// randomThemeName, +// "$randomThemeName 설명이에요!!", +// "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFiuCdwdz88l6pdfsRy1nFl0IHUVI7JMTQHg&s", +// if (randomDay <= 30) true else false, +// availableMinutes.toShort(), +// expectedMinutesFrom.toShort(), +// expectedMinutesTo.toShort(), +// randomPrice, +// Difficulty.entries.random().name, +// minParticipant.toShort(), +// maxParticipant.toShort(), +// Timestamp.valueOf(randomCreatedAt), +// createdBy, +// Timestamp.valueOf(randomCreatedAt), +// createdBy +// ) +// ) +// } +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// jdbcTemplate.batchUpdate(sql, batchArgs) +// } +// } +// } +// } +//} +// +//class UserDataInitializer : AbstractDataInitializer() { +// val userCount = 1_000_000 +// +// init { +// context("유저 초기 데이터 생성") { +// test("$userCount 명의 회원 생성") { +// val regions: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT r.code FROM RegionEntity r", +// String::class.java +// ).resultList +// } +// +// val chunkSize = 10_000 +// val chunks = userCount / chunkSize +// +// coroutineScope { +// (1..chunks).map { +// launch(Dispatchers.IO) { +// processUsers(chunkSize, regions) +// } +// }.joinAll() +// } +// } +// +// test("휴대폰 번호가 중복된 유저들에게 재배정") { +// val duplicatePhoneUsers: List = +// transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// """ +// SELECT u FROM UserEntity u +// WHERE u.phone IN ( +// SELECT u2.phone FROM UserEntity u2 +// GROUP BY u2.phone +// HAVING COUNT(u2.id) > 1 +// ) +// ORDER BY u.phone, u.id +// """.trimIndent(), +// UserEntity::class.java +// ).resultList +// } +// +// jdbcTemplate.execute("CREATE INDEX idx_users__phone ON users (phone)") +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// var currentPhone: String? = null +// duplicatePhoneUsers.forEach { user -> +// if (user.phone != currentPhone) { +// currentPhone = user.phone +// } else { +// var newPhone: String +// +// do { +// newPhone = randomPhoneNumber() +// val count: Long = entityManager.createQuery( +// "SELECT COUNT(u.id) FROM UserEntity u WHERE u.phone = :phone", +// Long::class.java +// ).setParameter("phone", newPhone) +// .singleResult +// +// if (count == 0L) break +// } while (true) +// +// user.phone = newPhone +// user.updatedAt = LocalDateTime.now() +// entityManager.merge(user) +// } +// } +// } +// +// jdbcTemplate.execute("DROP INDEX idx_users__phone ON users") +// } +// +// test("회원 상태 변경 이력 저장") { +// val userId: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT u.id FROM UserEntity u", +// Long::class.java +// ).resultList +// } +// +// coroutineScope { +// userId.chunked(10_000).map { chunk -> +// launch(Dispatchers.IO) { +// processStatus(chunk) +// } +// }.joinAll() +// } +// } +// } +// } +// +// private suspend fun processStatus(userIds: List) { +// val sql = """ +// INSERT INTO user_status_history ( +// id, user_id, reason, status, +// created_by, updated_by, created_at, updated_at +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// val batchArgs = mutableListOf>() +// val now = LocalDateTime.now() +// +// userIds.forEach { userId -> +// batchArgs.add( +// arrayOf( +// tsidFactory.next(), +// userId, +// SIGNUP, +// UserStatus.ACTIVE.name, +// userId, +// userId, +// Timestamp.valueOf(now), +// Timestamp.valueOf(now) +// ) +// ) +// } +// +// executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +// +// private suspend fun processUsers(chunkSize: Int, regions: List) { +// val sql = """ +// INSERT INTO users ( +// id, name, email, password, phone, region_code, status, +// created_by, updated_by, created_at, updated_at +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// val batchArgs = mutableListOf>() +// +// repeat(chunkSize) { +// val id: Long = tsidFactory.next() +// batchArgs.add( +// arrayOf( +// id, +// randomKoreanName(), +// "${randomString()}@sangdol.com", +// randomString(), +// randomPhoneNumber(), +// regions.random(), +// UserStatus.ACTIVE.name, +// id, +// id, +// Timestamp.valueOf(LocalDateTime.now()), +// Timestamp.valueOf(LocalDateTime.now()) +// ) +// ) +// if (batchArgs.size >= 1_000) { +// executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +// } +// +// if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +//} +// +//class ScheduleDataInitializer : AbstractDataInitializer() { +// init { +// context("일정 초기 데이터 생성") { +// test("테마 생성일 기준으로 다음 3일차, 매일 5개의 일정을 모든 매장에 생성") { +// val stores: List> = getStoreWithManagers() +// val themes: List> = getThemes() +// val maxAvailableMinutes = themes.maxOf { it.second.toInt() } +// val scheduleCountPerDay = 5 +// +// val startTime = LocalTime.of(10, 0) +// var lastTime = startTime +// val times = mutableListOf() +// +// repeat(scheduleCountPerDay) { +// times.add(lastTime) +// lastTime = lastTime.plusMinutes(maxAvailableMinutes.toLong() + 10L) +// } +// +// coroutineScope { +// themes.forEach { theme -> +// launch(Dispatchers.IO) { +// processTheme(theme, stores, times) +// } +// } +// } +// } +// } +// } +// +// private suspend fun processTheme( +// theme: Triple, +// stores: List>, +// times: List +// ) { +// val sql = """ +// INSERT INTO schedule ( +// id, store_id, theme_id, date, time, status, +// created_by, updated_by, created_at, updated_at +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// +// val batchArgs = mutableListOf>() +// +// val now = LocalDateTime.now() +// stores.forEach { (storeId, adminId) -> +// (1..3).forEach { dayOffset -> +// val date = theme.third.toLocalDate().plusDays(dayOffset.toLong()) +// +// times.forEach { time -> +// val scheduledAt = LocalDateTime.of(date, time) +// val status = +// if (scheduledAt.isAfter(now)) ScheduleStatus.AVAILABLE.name else ScheduleStatus.RESERVED.name +// +// batchArgs.add( +// arrayOf( +// tsidFactory.next(), storeId, theme.first, date, time, +// status, adminId, adminId, Timestamp.valueOf(now), Timestamp.valueOf(now) +// ) +// ) +// +// if (batchArgs.size >= 500) { +// executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +// } +// } +// } +// +// if (batchArgs.isNotEmpty()) { +// executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +// } +// +// private fun getStoreWithManagers(): List> { +// return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT a.storeId, a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel = :permissionLevel", +// List::class.java +// ).setParameter("type", AdminType.STORE) +// .setParameter("permissionLevel", AdminPermissionLevel.FULL_ACCESS) +// .resultList +// }.map { +// val array = it as List<*> +// Pair(array[0] as Long, array[1] as Long) +// } +// } +// +// private fun getThemes(): List> { +// return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT t._id, t.availableMinutes, t.createdAt FROM ThemeEntity t", +// List::class.java +// ) +// .resultList +// }.map { +// val array = it as List<*> +// Triple(array[0] as Long, array[1] as Short, array[2] as LocalDateTime) +// } +// } +//} +// +///** +// * 아래의 ReservationDataInitializer 에서 사용할 임시 DTO 클래스 +// */ +//data class ScheduleWithThemeParticipants( +// val scheduleId: Long, +// val themeMinParticipants: Short, +// val themeMaxParticipants: Short, +//) +// +//class ReservationDataInitializer : AbstractDataInitializer() { +// +// init { +// context("예약 초기 데이터 생성") { +// test("${ScheduleStatus.RESERVED}인 모든 일정에 예약을 1개씩 배정한다.") { +// val chunkSize = 10_000 +// +// val chunkedSchedules: List> = entityManager.createQuery( +// "SELECT new com.sangdol.roomescape.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status", +// ScheduleWithThemeParticipants::class.java +// ).setParameter("status", ScheduleStatus.RESERVED).resultList.chunked(chunkSize) +// +// val chunkedUsers: List> = entityManager.createQuery( +// "SELECT new com.sangdol.roomescape.user.web.UserContactResponse(u._id, u.name, u.phone) FROM UserEntity u", +// UserContactResponse::class.java +// ).resultList.chunked(chunkSize) +// +// +// coroutineScope { +// chunkedSchedules.forEachIndexed { idx, schedules -> +// launch(Dispatchers.IO) { +// processReservation(chunkedUsers[idx % chunkedUsers.size], schedules) +// } +// } +// } +// } +// } +// } +// +// private suspend fun processReservation( +// users: List, +// schedules: List +// ) { +// val sql = """ +// INSERT INTO reservation ( +// id, user_id, schedule_id, +// reserver_name, reserver_contact, participant_count, requirement, +// status, created_at, created_by, updated_at, updated_by +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// +// val batchArgs = mutableListOf>() +// +// val createdAt = LocalDateTime.now() +// +// schedules.forEachIndexed { idx, schedule -> +// val user: UserContactResponse = users[idx % users.size] +// +// batchArgs.add( +// arrayOf( +// tsidFactory.next(), +// user.id, +// schedule.scheduleId, +// user.name, +// user.phone, +// (schedule.themeMinParticipants..schedule.themeMaxParticipants).random(), +// randomKoreanWords(length = (20..100).random()), +// ReservationStatus.CONFIRMED.name, +// Timestamp.valueOf(createdAt), +// user.id, +// Timestamp.valueOf(createdAt), +// user.id, +// ) +// ) +// +// if (batchArgs.size >= 1_000) { +// executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +// } +// +// if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +//} +// +//class ReservationWithPrice( +// themePrice: Int, +// participantCount: Short, +// +// val reservationId: Long, +// val totalPrice: Int = (themePrice * participantCount), +//) +// +//data class PaymentWithMethods( +// val id: Long, +// val totalPrice: Int, +// val method: PaymentMethod +//) +// +//class PaymentDataInitializer : AbstractDataInitializer() { +// companion object { +// val requestedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().toLocalDateTime()) +// val approvedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().plusSeconds(5).toLocalDateTime()) +// val supportedPaymentMethods = listOf(PaymentMethod.TRANSFER, PaymentMethod.EASY_PAY, PaymentMethod.CARD) +// val supportedCardType = listOf(CardType.CREDIT, CardType.CHECK) +// +// val settlementStatus = "COMPLETED" +// +// val paymentSql: String = """ +// INSERT INTO payment( +// id, reservation_id, type, method, +// payment_key, order_id, total_amount, status, +// requested_at, approved_at +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// +// val paymentDetailSql: String = """ +// INSERT INTO payment_detail( +// id, payment_id, supplied_amount, vat +// ) VALUES (?, ?, ?, ?) +// """.trimIndent() +// +// val paymentCardDetailSql: String = """ +// INSERT INTO payment_card_detail( +// id, issuer_code, card_type, owner_type, +// amount, card_number, approval_number, installment_plan_months, +// is_interest_free, easypay_provider_code, easypay_discount_amount +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// +// val paymentEasypayPrepaidDetailSql: String = """ +// INSERT INTO payment_easypay_prepaid_detail( +// id, easypay_provider_code, amount, discount_amount +// ) VALUES (?, ?, ?, ?) +// """.trimIndent() +// +// val paymentBankTransferDetailSql: String = """ +// INSERT INTO payment_bank_transfer_detail( +// id, bank_code, settlement_status +// ) VALUES (?, ?, ?) +// """.trimIndent() +// } +// +// init { +// context("결제 데이터 초기화") { +// test("모든 예약에 맞춰 1:1로 결제 및 결제 상세 데이터를 생성한다.") { +// val allReservations: List = entityManager.createQuery( +// "SELECT t.price, r.participantCount, r._id FROM ReservationEntity r JOIN ScheduleEntity s ON s._id = r.scheduleId JOIN ThemeEntity t ON t.id = s.themeId", +// List::class.java +// ).resultList.map { +// val items = it as List<*> +// ReservationWithPrice( +// themePrice = items[0] as Int, +// participantCount = items[1] as Short, +// reservationId = items[2] as Long +// ) +// } +// +// coroutineScope { +// allReservations.chunked(10_000).forEach { reservations -> +// launch(Dispatchers.IO) { +// processPaymentAndDefaultDetail(reservations) +// } +// } +// } +// } +// +// test("기존 결제 데이터에 상세 정보(계좌이체, 카드, 간편결제) 데이터를 생성한다.") { +// val allPayments: List = entityManager.createQuery( +// "SELECT new com.sangdol.roomescape.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId", +// PaymentWithMethods::class.java +// ).resultList +// +// coroutineScope { +// allPayments.chunked(10_000).forEach { payments -> +// launch(Dispatchers.IO) { +// processPaymentDetail(payments) +// } +// } +// } +// } +// +// test("null 컴파일 에러를 피하기 위해 문자열 null로 임시 지정한 컬럼을 변경한다.") { +// jdbcTemplate.execute("CREATE INDEX idx_payment_card_detail_easypay ON payment_card_detail (easypay_provider_code)") +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// jdbcTemplate.update( +// "UPDATE payment_card_detail SET easypay_provider_code = ? WHERE easypay_provider_code = ?", +// null, +// "null" +// ) +// } +// +// jdbcTemplate.execute("DROP INDEX idx_payment_card_detail_easypay ON payment_card_detail") +// } +// } +// } +// +// private suspend fun processPaymentAndDefaultDetail(reservations: List) { +// val paymentBatchArgs = mutableListOf>() +// val detailBatchArgs = mutableListOf>() +// +// reservations.forEachIndexed { idx, reservations -> +// val id = tsidFactory.next() +// val totalPrice = reservations.totalPrice +// paymentBatchArgs.add( +// arrayOf( +// id, +// reservations.reservationId, +// PaymentType.NORMAL.name, +// randomPaymentMethod(), +// randomString(length = 64), +// randomString(length = 20), +// totalPrice, +// PaymentStatus.DONE.name, +// requestedAtCache, +// approvedAtCache, +// ) +// ) +// if (paymentBatchArgs.size >= 1_000) { +// executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } +// } +// +// val suppliedAmount: Int = (totalPrice * 0.9).toInt() +// val vat: Int = (totalPrice - suppliedAmount) +// +// detailBatchArgs.add( +// arrayOf( +// tsidFactory.next(), +// id, +// suppliedAmount, +// vat +// ) +// ) +// +// if (detailBatchArgs.size >= 1_000) { +// executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } +// } +// } +// +// if (paymentBatchArgs.isNotEmpty()) { +// executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } +// } +// if (detailBatchArgs.isNotEmpty()) { +// executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } +// } +// } +// +// private suspend fun processPaymentDetail(payments: List) { +// val transferBatchArgs = mutableListOf>() +// val cardBatchArgs = mutableListOf>() +// val easypayPrepaidBatchArgs = mutableListOf>() +// +// payments.forEach { payment -> +// val totalPrice = payment.totalPrice +// val randomDiscountAmount = +// if (totalPrice < 100 || Math.random() < 0.8) 0 else ((100..totalPrice).random() / 100) * 100 +// val amount = totalPrice - randomDiscountAmount +// +// when (payment.method) { +// PaymentMethod.TRANSFER -> { +// transferBatchArgs.add( +// arrayOf( +// payment.id, +// BankCode.entries.random().name, +// settlementStatus +// ) +// ) +// if (transferBatchArgs.size >= 1_000) { +// executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } +// } +// } +// +// PaymentMethod.EASY_PAY -> { +// if (Math.random() <= 0.7) { +// cardBatchArgs.add( +// arrayOf( +// payment.id, +// CardIssuerCode.entries.random().name, +// supportedCardType.random().name, +// CardOwnerType.PERSONAL.name, +// amount, +// randomCardNumber(), +// randomApprovalNumber(), +// randomInstallmentPlanMonths(amount), +// true, +// EasyPayCompanyCode.entries.random().name, +// randomDiscountAmount +// ) +// ) +// +// if (cardBatchArgs.size >= 1_000) { +// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } +// } +// +// } else { +// easypayPrepaidBatchArgs.add( +// arrayOf( +// payment.id, +// EasyPayCompanyCode.entries.random().name, +// amount, +// randomDiscountAmount, +// ) +// ) +// +// if (easypayPrepaidBatchArgs.size >= 1_000) { +// executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } +// } +// } +// } +// +// PaymentMethod.CARD -> { +// cardBatchArgs.add( +// arrayOf( +// payment.id, +// CardIssuerCode.entries.random().name, +// supportedCardType.random().name, +// CardOwnerType.PERSONAL.name, +// totalPrice, +// randomCardNumber(), +// randomApprovalNumber(), +// randomInstallmentPlanMonths(totalPrice), +// true, +// "null", +// 0, +// ) +// ) +// +// if (cardBatchArgs.size >= 1_000) { +// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } +// } +// } +// +// else -> return@forEach +// } +// } +// if (transferBatchArgs.isNotEmpty()) { +// executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } +// } +// if (cardBatchArgs.isNotEmpty()) { +// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } +// } +// if (easypayPrepaidBatchArgs.isNotEmpty()) { +// executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } +// } +// } +// +// private suspend fun randomPaymentMethod(): String { +// val random = Math.random() +// +// return if (random <= 0.5) { +// PaymentMethod.EASY_PAY.name +// } else if (random <= 0.9) { +// PaymentMethod.CARD.name +// } else { +// PaymentMethod.TRANSFER.name +// } +// } +// +// private suspend fun randomCardNumber(): String { +// return "${(10000000..99999999).random()}****${(100..999).random()}*" +// } +// +// private suspend fun randomApprovalNumber(): String { +// return "${(10000000..99999999).random()}" +// } +// +// private suspend fun randomInstallmentPlanMonths(amount: Int): Int { +// return if (amount < 50_000 || Math.random() < 0.9) { +// 0 +// } else { +// (1..6).random() +// } +// } +//} +// +//fun randomKoreanName(): String { +// val lastNames = listOf( +// "김", "이", "박", "최", "정", "강", "조", "윤", "장", "임", +// "오", "서", "신", "권", "황", "안", "송", "류", "홍", "전", "고", "문", "양", "손", "배", "백", "허", "유", "남", "심", "노" +// ) +// +// return "${lastNames.random()}${if (Math.random() < 0.1) randomKoreanWords(1) else randomKoreanWords(2)}" +//} +// +//fun randomKoreanWords(length: Int = 1): String { +// val words = listOf( +// "가", "나", "다", "라", "마", "바", "사", "아", "자", "차", +// "카", "타", "파", "하", "강", "민", "서", "윤", "우", "진", +// "현", "지", "은", "혜", "수", "영", "주", "원", "희", "경", +// "선", "아", "나", "다", "라", "마", "바", "사", "아", "자", +// "차", "카", "타", "파", "하", +// ) +// +// return (1..length).joinToString("") { words.random() } +//} diff --git a/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt index bf663229..d2515a70 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt @@ -1,6 +1,7 @@ package com.sangdol.roomescape.supports -import com.fasterxml.jackson.module.kotlin.convertValue +import com.sangdol.common.config.JacksonConfig +import com.sangdol.roomescape.common.exception.ErrorCode import io.restassured.module.kotlin.extensions.Given import io.restassured.module.kotlin.extensions.Then import io.restassured.module.kotlin.extensions.When @@ -10,8 +11,6 @@ import io.restassured.specification.RequestSpecification import org.hamcrest.CoreMatchers.equalTo import org.springframework.http.HttpMethod import org.springframework.http.MediaType -import com.sangdol.roomescape.common.config.JacksonConfig -import com.sangdol.roomescape.common.exception.ErrorCode fun runTest( token: String? = null, @@ -103,10 +102,10 @@ object ResponseParser { val objectMapper = JacksonConfig().objectMapper() inline fun parseListResponse(response: List>): List { - return response.map { objectMapper.convertValue(it) } + return response.map { objectMapper.convertValue(it, T::class.java) } } inline fun parseSingleResponse(response: LinkedHashMap): T { - return objectMapper.convertValue(response) + return objectMapper.convertValue(response, T::class.java) } } -- 2.47.2 From ab84b329fdb454b213f71c92d85e96baae2a644c Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:09:12 +0900 Subject: [PATCH 03/39] =?UTF-8?q?refactor:=20BaseEntity=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/persistence/build.gradle.kts | 17 +++++++++++ .../common/persistence/AuditingBaseEntity.kt | 28 +++---------------- .../persistence/PersistableBaseEntity.kt | 25 +++++++++++++++++ .../infrastructure/persistence/AdminEntity.kt | 2 +- .../persistence/LoginHistoryEntity.kt | 4 +-- .../persistence/CanceledPaymentEntity.kt | 2 +- .../persistence/PaymentDetailEntity.kt | 4 +-- .../persistence/PaymentEntity.kt | 8 +++--- .../persistence/CanceledReservationEntity.kt | 2 +- .../persistence/ReservationEntity.kt | 2 +- .../persistence/ScheduleEntity.kt | 4 +-- .../infrastructure/persistence/StoreEntity.kt | 2 +- .../infrastructure/persistence/ThemeEntity.kt | 2 +- .../persistence/UserEntities.kt | 2 +- 14 files changed, 63 insertions(+), 41 deletions(-) create mode 100644 common/persistence/build.gradle.kts rename service/src/main/kotlin/com/sangdol/roomescape/common/entity/BaseEntity.kt => common/persistence/src/main/kotlin/com/sangdol/common/persistence/AuditingBaseEntity.kt (59%) create mode 100644 common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistableBaseEntity.kt diff --git a/common/persistence/build.gradle.kts b/common/persistence/build.gradle.kts new file mode 100644 index 00000000..c1afa586 --- /dev/null +++ b/common/persistence/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("org.springframework.boot") + kotlin("plugin.spring") + kotlin("plugin.jpa") +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("com.github.f4b6a3:tsid-creator:5.2.6") + + testRuntimeOnly("com.h2database:h2") + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("io.kotest:kotest-runner-junit5:5.9.1") + testImplementation("io.kotest.extensions:kotest-extensions-spring:1.3.0") + + implementation(project(":common:utils")) +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/entity/BaseEntity.kt b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/AuditingBaseEntity.kt similarity index 59% rename from service/src/main/kotlin/com/sangdol/roomescape/common/entity/BaseEntity.kt rename to common/persistence/src/main/kotlin/com/sangdol/common/persistence/AuditingBaseEntity.kt index 2d76c19c..a9fca2c7 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/entity/BaseEntity.kt +++ b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/AuditingBaseEntity.kt @@ -1,14 +1,14 @@ -package com.sangdol.roomescape.common.entity +package com.sangdol.common.persistence -import jakarta.persistence.* +import jakarta.persistence.Column +import jakarta.persistence.EntityListeners +import jakarta.persistence.MappedSuperclass import org.springframework.data.annotation.CreatedBy import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.LastModifiedBy import org.springframework.data.annotation.LastModifiedDate -import org.springframework.data.domain.Persistable import org.springframework.data.jpa.domain.support.AuditingEntityListener import java.time.LocalDateTime -import kotlin.jvm.Transient @MappedSuperclass @EntityListeners(AuditingEntityListener::class) @@ -31,23 +31,3 @@ abstract class AuditingBaseEntity( @LastModifiedBy var updatedBy: Long = 0L } - -@MappedSuperclass -abstract class PersistableBaseEntity( - @Id - @Column(name = "id") - private val _id: Long, - - @Transient - private var isNewEntity: Boolean = true -) : Persistable { - - @PostLoad - @PrePersist - fun markNotNew() { - isNewEntity = false - } - - override fun getId(): Long = _id - override fun isNew(): Boolean = isNewEntity -} diff --git a/common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistableBaseEntity.kt b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistableBaseEntity.kt new file mode 100644 index 00000000..73973f7c --- /dev/null +++ b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistableBaseEntity.kt @@ -0,0 +1,25 @@ +package com.sangdol.common.persistence + +import jakarta.persistence.* +import org.springframework.data.domain.Persistable +import kotlin.jvm.Transient + +@MappedSuperclass +abstract class PersistableBaseEntity( + @Id + @Column(name = "id") + private val _id: Long, + + @Transient + private var isNewEntity: Boolean = true +) : Persistable { + + @PostLoad + @PrePersist + fun markNotNew() { + isNewEntity = false + } + + override fun getId(): Long = _id + override fun isNew(): Boolean = isNewEntity +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/admin/infrastructure/persistence/AdminEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/admin/infrastructure/persistence/AdminEntity.kt index 106775eb..9a5c61e2 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/admin/infrastructure/persistence/AdminEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/admin/infrastructure/persistence/AdminEntity.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.admin.infrastructure.persistence +import com.sangdol.common.persistence.AuditingBaseEntity import jakarta.persistence.* import org.springframework.data.jpa.domain.support.AuditingEntityListener -import com.sangdol.roomescape.common.entity.AuditingBaseEntity @Entity @Table(name = "admin") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt index 8a64442f..84a88708 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt @@ -1,10 +1,10 @@ package com.sangdol.roomescape.auth.infrastructure.persistence +import com.sangdol.common.persistence.PersistableBaseEntity +import com.sangdol.roomescape.common.dto.PrincipalType import jakarta.persistence.* import org.springframework.data.annotation.CreatedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener -import com.sangdol.roomescape.common.dto.PrincipalType -import com.sangdol.roomescape.common.entity.PersistableBaseEntity import java.time.LocalDateTime @Entity diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt index b5a92c3d..4db2e14d 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.payment.infrastructure.persistence +import com.sangdol.common.persistence.PersistableBaseEntity import jakarta.persistence.Entity import jakarta.persistence.Table -import com.sangdol.roomescape.common.entity.PersistableBaseEntity import java.time.LocalDateTime import java.time.OffsetDateTime diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt index 3039d052..0e06f775 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentDetailEntity.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.payment.infrastructure.persistence -import jakarta.persistence.* -import com.sangdol.roomescape.common.entity.PersistableBaseEntity +import com.sangdol.common.persistence.PersistableBaseEntity import com.sangdol.roomescape.payment.infrastructure.common.* +import jakarta.persistence.* @Entity @Table(name = "payment_detail") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentEntity.kt index 969dc52b..48aa5587 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/persistence/PaymentEntity.kt @@ -1,13 +1,13 @@ package com.sangdol.roomescape.payment.infrastructure.persistence +import com.sangdol.common.persistence.PersistableBaseEntity +import com.sangdol.roomescape.payment.infrastructure.common.PaymentMethod +import com.sangdol.roomescape.payment.infrastructure.common.PaymentStatus +import com.sangdol.roomescape.payment.infrastructure.common.PaymentType import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated import jakarta.persistence.Table -import com.sangdol.roomescape.common.entity.PersistableBaseEntity -import com.sangdol.roomescape.payment.infrastructure.common.PaymentMethod -import com.sangdol.roomescape.payment.infrastructure.common.PaymentStatus -import com.sangdol.roomescape.payment.infrastructure.common.PaymentType import java.time.OffsetDateTime @Entity diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt index ae71243f..81832831 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/CanceledReservationEntity.kt @@ -1,10 +1,10 @@ package com.sangdol.roomescape.reservation.infrastructure.persistence +import com.sangdol.common.persistence.PersistableBaseEntity import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated import jakarta.persistence.Table -import com.sangdol.roomescape.common.entity.PersistableBaseEntity import java.time.LocalDateTime @Entity diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt index 99fe9890..0bccb252 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/infrastructure/persistence/ReservationEntity.kt @@ -1,10 +1,10 @@ package com.sangdol.roomescape.reservation.infrastructure.persistence +import com.sangdol.common.persistence.AuditingBaseEntity import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated import jakarta.persistence.Table -import com.sangdol.roomescape.common.entity.AuditingBaseEntity @Entity @Table(name = "reservation") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt index b6a2c984..e4a29126 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt @@ -1,12 +1,12 @@ package com.sangdol.roomescape.schedule.infrastructure.persistence +import com.sangdol.common.persistence.PersistableBaseEntity +import com.sangdol.roomescape.common.util.MdcPrincipalId import jakarta.persistence.* import org.springframework.data.annotation.CreatedBy import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener -import com.sangdol.roomescape.common.entity.PersistableBaseEntity -import com.sangdol.roomescape.common.util.MdcPrincipalId import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime diff --git a/service/src/main/kotlin/com/sangdol/roomescape/store/infrastructure/persistence/StoreEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/infrastructure/persistence/StoreEntity.kt index 27a97f6f..83c851f1 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/store/infrastructure/persistence/StoreEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/infrastructure/persistence/StoreEntity.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.store.infrastructure.persistence +import com.sangdol.common.persistence.AuditingBaseEntity import jakarta.persistence.* import org.springframework.data.jpa.domain.support.AuditingEntityListener -import com.sangdol.roomescape.common.entity.AuditingBaseEntity @Entity @EntityListeners(AuditingEntityListener::class) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/infrastructure/persistence/ThemeEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/infrastructure/persistence/ThemeEntity.kt index 7c99a10c..730987c2 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/infrastructure/persistence/ThemeEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/infrastructure/persistence/ThemeEntity.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.theme.infrastructure.persistence +import com.sangdol.common.persistence.AuditingBaseEntity import jakarta.persistence.* -import com.sangdol.roomescape.common.entity.AuditingBaseEntity @Entity @Table(name = "theme") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/infrastructure/persistence/UserEntities.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/infrastructure/persistence/UserEntities.kt index dfe78608..89d3c1fc 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/infrastructure/persistence/UserEntities.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/infrastructure/persistence/UserEntities.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.user.infrastructure.persistence +import com.sangdol.common.persistence.AuditingBaseEntity import jakarta.persistence.* -import com.sangdol.roomescape.common.entity.AuditingBaseEntity @Entity @Table(name = "users") -- 2.47.2 From 430630a02b0ee4890886d2ec7666715607bd5b85 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:15:54 +0900 Subject: [PATCH 04/39] =?UTF-8?q?refactor:=20MDC=20=EC=9C=A0=ED=8B=B8=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=9D=B4=EB=8F=99(service=20->=20common.u?= =?UTF-8?q?tils)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/utils/build.gradle.kts | 5 ++++ .../common/utils/MdcPrincipalIdUtil.kt | 26 +++++++++++++++++ .../common/utils/MdcPrincipalIdUtilTest.kt | 28 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 common/utils/build.gradle.kts create mode 100644 common/utils/src/main/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtil.kt create mode 100644 common/utils/src/test/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtilTest.kt diff --git a/common/utils/build.gradle.kts b/common/utils/build.gradle.kts new file mode 100644 index 00000000..ad519ea1 --- /dev/null +++ b/common/utils/build.gradle.kts @@ -0,0 +1,5 @@ +dependencies { + implementation("org.slf4j:slf4j-api:2.0.17") + + testImplementation("ch.qos.logback:logback-classic:1.5.18") +} \ No newline at end of file diff --git a/common/utils/src/main/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtil.kt b/common/utils/src/main/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtil.kt new file mode 100644 index 00000000..f96e2b06 --- /dev/null +++ b/common/utils/src/main/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtil.kt @@ -0,0 +1,26 @@ +package com.sangdol.common.utils + +import org.slf4j.MDC +import java.util.* + +object MdcPrincipalIdUtil { + const val MDC_PRINCIPAL_ID_KEY = "principal_id" + + fun extractAsLongOrNull(): Long? { + return MDC.get(MDC_PRINCIPAL_ID_KEY)?.toLong() + } + + fun extractAsOptionalLongOrEmpty(): Optional { + return MDC.get(MDC_PRINCIPAL_ID_KEY)?.let { + Optional.of(it.toLong()) + } ?: Optional.empty() + } + + fun set(id: String) { + MDC.put(MDC_PRINCIPAL_ID_KEY, id) + } + + fun clear() { + MDC.remove(MDC_PRINCIPAL_ID_KEY) + } +} diff --git a/common/utils/src/test/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtilTest.kt b/common/utils/src/test/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtilTest.kt new file mode 100644 index 00000000..388c61d8 --- /dev/null +++ b/common/utils/src/test/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtilTest.kt @@ -0,0 +1,28 @@ +package com.sangdol.common.utils + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import java.util.* + +class MdcPrincipalIdUtilTest : StringSpec({ + + val id = 1872847943L + + "값을 설정한다." { + MdcPrincipalIdUtil.set(id.toString()).also { + MdcPrincipalIdUtil.extractAsLongOrNull() shouldBe id + MdcPrincipalIdUtil.extractAsOptionalLongOrEmpty() shouldBe Optional.of(id) + } + } + + "값을 제거한다." { + MdcPrincipalIdUtil.set(id.toString()).also { + MdcPrincipalIdUtil.extractAsLongOrNull() shouldBe id + } + + MdcPrincipalIdUtil.clear().also { + MdcPrincipalIdUtil.extractAsLongOrNull() shouldBe null + MdcPrincipalIdUtil.extractAsOptionalLongOrEmpty() shouldBe Optional.empty() + } + } +}) \ No newline at end of file -- 2.47.2 From c524cc6fdf383f0c1c25591509312ef0280f1b0d Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:16:33 +0900 Subject: [PATCH 05/39] =?UTF-8?q?refactor:=20=EC=83=88=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=EB=90=9C=20persistence=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=EC=97=90=20=EA=B8=B0=EC=A1=B4=20TsidFactory=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=ED=99=94=20=EB=B0=8F=20=EC=9E=AC=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sangdol/common/persistence/IDGenerator.kt | 13 +++++++++++++ .../roomescape/common/config/TsidConfig.kt | 17 ----------------- 2 files changed, 13 insertions(+), 17 deletions(-) create mode 100644 common/persistence/src/main/kotlin/com/sangdol/common/persistence/IDGenerator.kt delete mode 100644 service/src/main/kotlin/com/sangdol/roomescape/common/config/TsidConfig.kt diff --git a/common/persistence/src/main/kotlin/com/sangdol/common/persistence/IDGenerator.kt b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/IDGenerator.kt new file mode 100644 index 00000000..ba6778ff --- /dev/null +++ b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/IDGenerator.kt @@ -0,0 +1,13 @@ +package com.sangdol.common.persistence + +import com.github.f4b6a3.tsid.TsidFactory + +interface IDGenerator { + fun create(): Long +} + +class TsidIDGenerator( + private val tsidFactory: TsidFactory +) : IDGenerator { + override fun create(): Long = tsidFactory.create().toLong() +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/config/TsidConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/config/TsidConfig.kt deleted file mode 100644 index 5561e6b7..00000000 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/config/TsidConfig.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.sangdol.roomescape.common.config - -import com.github.f4b6a3.tsid.TsidFactory -import org.springframework.beans.factory.annotation.Value -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration - -@Configuration -class TsidConfig { - @Value("\${POD_NAME:app-0}") - private lateinit var podName: String - - @Bean - fun tsidFactory(): TsidFactory = TsidFactory(podName.substringAfterLast("-").toInt()) -} - -fun TsidFactory.next(): Long = this.create().toLong() \ No newline at end of file -- 2.47.2 From 4f0bbe096e2063b5a6f128e8ca6e1fac56ce011b Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:17:36 +0900 Subject: [PATCH 06/39] =?UTF-8?q?refactor:=20JPA=20Audit=20=EB=B0=8F=20ID?= =?UTF-8?q?=20Generator=20=EC=84=A4=EC=A0=95=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=9D=B4=EC=A0=84(service=20->=20common.persistence)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/persistence/PersistenceConfig.kt | 36 +++++++++++++++++++ .../roomescape/common/config/JpaConfig.kt | 20 ----------- 2 files changed, 36 insertions(+), 20 deletions(-) create mode 100644 common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistenceConfig.kt delete mode 100644 service/src/main/kotlin/com/sangdol/roomescape/common/config/JpaConfig.kt diff --git a/common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistenceConfig.kt b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistenceConfig.kt new file mode 100644 index 00000000..1cf26620 --- /dev/null +++ b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistenceConfig.kt @@ -0,0 +1,36 @@ +package com.sangdol.common.persistence + +import com.github.f4b6a3.tsid.TsidFactory +import com.sangdol.common.utils.MdcPrincipalIdUtil +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Primary +import org.springframework.data.domain.AuditorAware +import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import java.util.* + +@Configuration +@EnableJpaAuditing +class PersistenceConfig { + + @Value("\${POD_NAME:app-0}") + private lateinit var podName: String + + @Bean + fun auditorAware(): AuditorAware = MdcAuditorAware() + + @Bean + @Primary + fun idGenerator(): IDGenerator { + val node = podName.substringAfterLast("-").toInt() + + val tsidFactory = TsidFactory.builder().withNode(node).build() + + return TsidIDGenerator(tsidFactory) + } +} + +class MdcAuditorAware : AuditorAware { + override fun getCurrentAuditor(): Optional = MdcPrincipalIdUtil.extractAsOptionalLongOrEmpty() +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/config/JpaConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/config/JpaConfig.kt deleted file mode 100644 index a34c0251..00000000 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/config/JpaConfig.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.sangdol.roomescape.common.config - -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.data.domain.AuditorAware -import org.springframework.data.jpa.repository.config.EnableJpaAuditing -import com.sangdol.roomescape.common.util.MdcPrincipalId -import java.util.* - -@Configuration -@EnableJpaAuditing -class JpaConfig { - - @Bean - fun auditorAware(): AuditorAware = MdcAuditorAware() -} - -class MdcAuditorAware : AuditorAware { - override fun getCurrentAuditor(): Optional = MdcPrincipalId.extractAsOptionalLongOrEmpty() -} -- 2.47.2 From 89eeefbf0c39f6c962b42ee874acb7bd359dce1e Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:17:58 +0900 Subject: [PATCH 07/39] =?UTF-8?q?refactor:=20=EB=B6=84=EB=A6=AC=ED=95=9C?= =?UTF-8?q?=20common.persistence=20=EB=AA=A8=EB=93=88=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/persistence/BaseEntityTest.kt | 53 +++++++++++++++++++ .../common/persistence/TestApplication.kt | 6 +++ .../persistence/TestAuditingBaseEntity.kt | 12 +++++ .../persistence/TestPersistableBaseEntity.kt | 12 +++++ .../src/test/resources/application.yaml | 18 +++++++ 5 files changed, 101 insertions(+) create mode 100644 common/persistence/src/test/kotlin/com/sangdol/common/persistence/BaseEntityTest.kt create mode 100644 common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestApplication.kt create mode 100644 common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestAuditingBaseEntity.kt create mode 100644 common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestPersistableBaseEntity.kt create mode 100644 common/persistence/src/test/resources/application.yaml diff --git a/common/persistence/src/test/kotlin/com/sangdol/common/persistence/BaseEntityTest.kt b/common/persistence/src/test/kotlin/com/sangdol/common/persistence/BaseEntityTest.kt new file mode 100644 index 00000000..f0acd755 --- /dev/null +++ b/common/persistence/src/test/kotlin/com/sangdol/common/persistence/BaseEntityTest.kt @@ -0,0 +1,53 @@ +package com.sangdol.common.persistence + +import com.sangdol.common.utils.MdcPrincipalIdUtil +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.date.shouldBeBefore +import io.kotest.matchers.equality.shouldBeEqualUsingFields +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import org.springframework.boot.test.context.SpringBootTest +import java.time.LocalDateTime + +@SpringBootTest +class BaseEntityTest( + private val testPersistableBaseEntityRepository: TestPersistableBaseEntityRepository, + private val testAuditingBaseEntityRepository: TestAuditingBaseEntityRepository, + private val idGenerator: IDGenerator +) : FunSpec({ + context("TestPersistableBaseEntityRepository") { + test("PK를 지정하여 INSERT 쿼리를 한번만 전송한다.") { + val entity = TestPersistableBaseEntity(idGenerator.create(), "hello").also { + testPersistableBaseEntityRepository.saveAndFlush(it) + } + + testPersistableBaseEntityRepository.findById(entity.id).also { + it.shouldNotBeNull() + it.get() shouldBeEqualUsingFields entity + } + } + } + + context("TestAuditingBaseEntityRepository") { + test("Entity 저장 후 Audit 정보를 확인한다.") { + val id = idGenerator.create() + .also { + MdcPrincipalIdUtil.set(it.toString()) + }.also { + testAuditingBaseEntityRepository.saveAndFlush(TestAuditingBaseEntity(it, "hello")) + } + + testAuditingBaseEntityRepository.findById(id).also { + it.shouldNotBeNull() + + assertSoftly(it.get()) { + this.createdAt shouldBeBefore LocalDateTime.now() + this.updatedAt shouldBeBefore LocalDateTime.now() + this.createdBy shouldBe id + this.updatedBy shouldBe id + } + } + } + } +}) diff --git a/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestApplication.kt b/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestApplication.kt new file mode 100644 index 00000000..b2b4176c --- /dev/null +++ b/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestApplication.kt @@ -0,0 +1,6 @@ +package com.sangdol.common.persistence + +import org.springframework.boot.autoconfigure.SpringBootApplication + +@SpringBootApplication +class TestApplication \ No newline at end of file diff --git a/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestAuditingBaseEntity.kt b/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestAuditingBaseEntity.kt new file mode 100644 index 00000000..f58ecf77 --- /dev/null +++ b/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestAuditingBaseEntity.kt @@ -0,0 +1,12 @@ +package com.sangdol.common.persistence + +import jakarta.persistence.Entity +import org.springframework.data.jpa.repository.JpaRepository + +@Entity +class TestAuditingBaseEntity( + id: Long, + val name: String +): AuditingBaseEntity(id) + +interface TestAuditingBaseEntityRepository: JpaRepository \ No newline at end of file diff --git a/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestPersistableBaseEntity.kt b/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestPersistableBaseEntity.kt new file mode 100644 index 00000000..ac97c1c2 --- /dev/null +++ b/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TestPersistableBaseEntity.kt @@ -0,0 +1,12 @@ +package com.sangdol.common.persistence + +import jakarta.persistence.Entity +import org.springframework.data.jpa.repository.JpaRepository + +@Entity +class TestPersistableBaseEntity( + id: Long, + val name: String +): PersistableBaseEntity(id) + +interface TestPersistableBaseEntityRepository: JpaRepository \ No newline at end of file diff --git a/common/persistence/src/test/resources/application.yaml b/common/persistence/src/test/resources/application.yaml new file mode 100644 index 00000000..3a39be3c --- /dev/null +++ b/common/persistence/src/test/resources/application.yaml @@ -0,0 +1,18 @@ +spring: + jpa: + properties: + hibernate: + format_sql: true + hibernate: + ddl-auto: create-drop + show-sql: true + h2: + console: + enabled: true + path: /h2-console + datasource: + hikari: + jdbc-url: jdbc:h2:mem:database + driver-class-name: org.h2.Driver + username: sa + password: \ No newline at end of file -- 2.47.2 From 5b31672ebbf32b984d019ce0a04061bcae3981d4 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:18:17 +0900 Subject: [PATCH 08/39] =?UTF-8?q?refactor:=20kotest=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=EC=9D=80=20=EA=B3=B5=ED=86=B5=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 1 + service/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 28b26eaf..e46b82b4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,6 +38,7 @@ subprojects { dependencies { add("implementation", "io.github.oshai:kotlin-logging-jvm:7.0.3") + add("implementation", "io.kotest:kotest-runner-junit5:5.9.1") } tasks.withType { diff --git a/service/build.gradle.kts b/service/build.gradle.kts index 2d95de4b..5033c801 100644 --- a/service/build.gradle.kts +++ b/service/build.gradle.kts @@ -43,7 +43,6 @@ dependencies { testImplementation("com.ninja-squad:springmockk:4.0.2") // Kotest - testImplementation("io.kotest:kotest-runner-junit5:5.9.1") testImplementation("io.kotest.extensions:kotest-extensions-spring:1.3.0") // RestAssured @@ -55,6 +54,7 @@ dependencies { // submodules implementation(project(":common:config")) + implementation(project(":common:persistence")) } tasks.jar { -- 2.47.2 From 07869020be24b995e358a39684c561c7b7ab467f Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:18:50 +0900 Subject: [PATCH 09/39] =?UTF-8?q?refactor:=20common.persistence=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EB=B6=84=EB=A6=AC=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=ED=95=9C=20=EA=B8=B0=EC=A1=B4=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/business/LoginHistoryService.kt | 15 +++++++-------- .../payment/business/PaymentWriter.kt | 17 ++++++++--------- .../business/ReservationService.kt | 19 +++++++++---------- .../schedule/business/ScheduleService.kt | 17 ++++++++--------- .../roomescape/store/business/StoreService.kt | 15 +++++++-------- .../roomescape/theme/business/ThemeService.kt | 17 ++++++++--------- .../roomescape/user/business/UserService.kt | 19 +++++++++---------- 7 files changed, 56 insertions(+), 63 deletions(-) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt index 680c1a91..2f6f7ba1 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt @@ -1,23 +1,22 @@ package com.sangdol.roomescape.auth.business -import com.github.f4b6a3.tsid.TsidFactory +import com.sangdol.common.persistence.IDGenerator +import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryEntity +import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryRepository +import com.sangdol.roomescape.auth.web.LoginContext +import com.sangdol.roomescape.common.dto.PrincipalType import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Propagation import org.springframework.transaction.annotation.Transactional -import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryEntity -import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryRepository -import com.sangdol.roomescape.auth.web.LoginContext -import com.sangdol.roomescape.common.config.next -import com.sangdol.roomescape.common.dto.PrincipalType private val log: KLogger = KotlinLogging.logger {} @Service class LoginHistoryService( private val loginHistoryRepository: LoginHistoryRepository, - private val tsidFactory: TsidFactory, + private val idGenerator: IDGenerator, ) { @Transactional(propagation = Propagation.REQUIRES_NEW) fun createSuccessHistory( @@ -47,7 +46,7 @@ class LoginHistoryService( runCatching { LoginHistoryEntity( - id = tsidFactory.next(), + id = idGenerator.create(), principalId = principalId, principalType = principalType, success = success, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentWriter.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentWriter.kt index 16ff6d59..06226ec4 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentWriter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentWriter.kt @@ -1,16 +1,15 @@ package com.sangdol.roomescape.payment.business -import com.github.f4b6a3.tsid.TsidFactory -import io.github.oshai.kotlinlogging.KLogger -import io.github.oshai.kotlinlogging.KotlinLogging -import org.springframework.stereotype.Component -import com.sangdol.roomescape.common.config.next +import com.sangdol.common.persistence.IDGenerator import com.sangdol.roomescape.payment.exception.PaymentErrorCode import com.sangdol.roomescape.payment.exception.PaymentException import com.sangdol.roomescape.payment.infrastructure.client.* import com.sangdol.roomescape.payment.infrastructure.common.PaymentMethod import com.sangdol.roomescape.payment.infrastructure.common.PaymentType import com.sangdol.roomescape.payment.infrastructure.persistence.* +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.stereotype.Component import java.time.LocalDateTime private val log: KLogger = KotlinLogging.logger {} @@ -20,7 +19,7 @@ class PaymentWriter( private val paymentRepository: PaymentRepository, private val paymentDetailRepository: PaymentDetailRepository, private val canceledPaymentRepository: CanceledPaymentRepository, - private val tsidFactory: TsidFactory, + private val idGenerator: IDGenerator, ) { fun createPayment( @@ -32,7 +31,7 @@ class PaymentWriter( log.info { "[PaymentWriterV2.createPayment] 결제 승인 및 결제 정보 저장 시작: reservationId=${reservationId}, paymentKey=${paymentClientConfirmResponse.paymentKey}" } return paymentClientConfirmResponse.toEntity( - id = tsidFactory.next(), reservationId, orderId, paymentType + id = idGenerator.create(), reservationId, orderId, paymentType ).also { paymentRepository.save(it) log.info { "[PaymentWriterV2.createPayment] 결제 승인 및 결제 정보 저장 완료: reservationId=${reservationId}, payment.id=${it.id}" } @@ -44,7 +43,7 @@ class PaymentWriter( paymentId: Long, ): PaymentDetailEntity { val method: PaymentMethod = paymentResponse.method - val id = tsidFactory.next() + val id = idGenerator.create() if (method == PaymentMethod.TRANSFER) { return paymentDetailRepository.save(paymentResponse.toTransferDetailEntity(id, paymentId)) @@ -69,7 +68,7 @@ class PaymentWriter( paymentRepository.save(payment.apply { this.cancel() }) return cancelResponse.cancels.toEntity( - id = tsidFactory.next(), + id = idGenerator.create(), paymentId = payment.id, cancelRequestedAt = requestedAt, canceledBy = userId diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt index 4f7c1fd0..7f53de4d 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt @@ -1,12 +1,6 @@ package com.sangdol.roomescape.reservation.business -import com.github.f4b6a3.tsid.TsidFactory -import io.github.oshai.kotlinlogging.KLogger -import io.github.oshai.kotlinlogging.KotlinLogging -import org.springframework.data.repository.findByIdOrNull -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import com.sangdol.roomescape.common.config.next +import com.sangdol.common.persistence.IDGenerator import com.sangdol.roomescape.common.dto.CurrentUserContext import com.sangdol.roomescape.payment.business.PaymentService import com.sangdol.roomescape.payment.web.PaymentWithDetailResponse @@ -21,6 +15,11 @@ import com.sangdol.roomescape.schedule.web.ScheduleUpdateRequest import com.sangdol.roomescape.theme.business.ThemeService import com.sangdol.roomescape.user.business.UserService import com.sangdol.roomescape.user.web.UserContactResponse +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime private val log: KLogger = KotlinLogging.logger {} @@ -33,7 +32,7 @@ class ReservationService( private val userService: UserService, private val themeService: ThemeService, private val canceledReservationRepository: CanceledReservationRepository, - private val tsidFactory: TsidFactory, + private val idGenerator: IDGenerator, private val paymentService: PaymentService ) { @@ -46,7 +45,7 @@ class ReservationService( validateCanCreate(request) - val reservation: ReservationEntity = request.toEntity(id = tsidFactory.next(), userId = user.id) + val reservation: ReservationEntity = request.toEntity(id = idGenerator.create(), userId = user.id) return PendingReservationCreateResponse(reservationRepository.save(reservation).id) .also { log.info { "[ReservationService.createPendingReservation] Pending 예약 생성 완료: reservationId=${it}, schedule=${request.scheduleId}" } } @@ -141,7 +140,7 @@ class ReservationService( } CanceledReservationEntity( - id = tsidFactory.next(), + id = idGenerator.create(), reservationId = reservation.id, canceledBy = user.id, cancelReason = cancelReason, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt index 8195a8eb..6dd9e231 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt @@ -1,14 +1,8 @@ package com.sangdol.roomescape.schedule.business import ScheduleException -import com.github.f4b6a3.tsid.TsidFactory -import io.github.oshai.kotlinlogging.KLogger -import io.github.oshai.kotlinlogging.KotlinLogging -import org.springframework.data.repository.findByIdOrNull -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional +import com.sangdol.common.persistence.IDGenerator import com.sangdol.roomescape.admin.business.AdminService -import com.sangdol.roomescape.common.config.next import com.sangdol.roomescape.common.dto.AuditInfo import com.sangdol.roomescape.common.dto.OperatorInfo import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview @@ -18,6 +12,11 @@ import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus import com.sangdol.roomescape.schedule.web.* +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.time.LocalDate private val log: KLogger = KotlinLogging.logger {} @@ -35,7 +34,7 @@ private val log: KLogger = KotlinLogging.logger {} class ScheduleService( private val scheduleRepository: ScheduleRepository, private val scheduleValidator: ScheduleValidator, - private val tsidFactory: TsidFactory, + private val idGenerator: IDGenerator, private val adminService: AdminService ) { // ======================================== @@ -112,7 +111,7 @@ class ScheduleService( scheduleValidator.validateCanCreate(storeId, request) val schedule = ScheduleEntityFactory.create( - id = tsidFactory.next(), + id = idGenerator.create(), date = request.date, time = request.time, storeId = storeId, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt index a95db5c4..2afb8e1f 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt @@ -1,12 +1,7 @@ package com.sangdol.roomescape.store.business -import com.github.f4b6a3.tsid.TsidFactory -import io.github.oshai.kotlinlogging.KLogger -import io.github.oshai.kotlinlogging.KotlinLogging -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional +import com.sangdol.common.persistence.IDGenerator import com.sangdol.roomescape.admin.business.AdminService -import com.sangdol.roomescape.common.config.next import com.sangdol.roomescape.common.dto.AuditInfo import com.sangdol.roomescape.region.business.RegionService import com.sangdol.roomescape.store.exception.StoreErrorCode @@ -15,6 +10,10 @@ import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity import com.sangdol.roomescape.store.infrastructure.persistence.StoreRepository import com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus import com.sangdol.roomescape.store.web.* +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional private val log: KLogger = KotlinLogging.logger {} @@ -24,7 +23,7 @@ class StoreService( private val storeValidator: StoreValidator, private val adminService: AdminService, private val regionService: RegionService, - private val tsidFactory: TsidFactory, + private val idGenerator: IDGenerator, ) { @Transactional(readOnly = true) fun getDetail(id: Long): DetailStoreResponse { @@ -45,7 +44,7 @@ class StoreService( storeValidator.validateCanRegister(request) val store = StoreEntity( - id = tsidFactory.next(), + id = idGenerator.create(), name = request.name, address = request.address, contact = request.contact, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt index 28770ace..73a1926a 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt @@ -1,13 +1,7 @@ package com.sangdol.roomescape.theme.business -import com.github.f4b6a3.tsid.TsidFactory -import io.github.oshai.kotlinlogging.KLogger -import io.github.oshai.kotlinlogging.KotlinLogging -import org.springframework.data.repository.findByIdOrNull -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional +import com.sangdol.common.persistence.IDGenerator import com.sangdol.roomescape.admin.business.AdminService -import com.sangdol.roomescape.common.config.next import com.sangdol.roomescape.common.dto.AuditInfo import com.sangdol.roomescape.common.util.DateUtils import com.sangdol.roomescape.theme.exception.ThemeErrorCode @@ -15,6 +9,11 @@ import com.sangdol.roomescape.theme.exception.ThemeException import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository import com.sangdol.roomescape.theme.web.* +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.time.LocalDate private val log: KLogger = KotlinLogging.logger {} @@ -30,7 +29,7 @@ private val log: KLogger = KotlinLogging.logger {} class ThemeService( private val themeRepository: ThemeRepository, private val themeValidator: ThemeValidator, - private val tsidFactory: TsidFactory, + private val idGenerator: IDGenerator, private val adminService: AdminService ) { // ======================================== @@ -91,7 +90,7 @@ class ThemeService( themeValidator.validateCanCreate(request) - val theme: ThemeEntity = request.toEntity(id = tsidFactory.next()) + val theme: ThemeEntity = request.toEntity(id = idGenerator.create()) .also { themeRepository.save(it) } return ThemeCreateResponse(theme.id).also { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt index 866ef7d6..2139bc14 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt @@ -1,12 +1,6 @@ package com.sangdol.roomescape.user.business -import com.github.f4b6a3.tsid.TsidFactory -import io.github.oshai.kotlinlogging.KLogger -import io.github.oshai.kotlinlogging.KotlinLogging -import org.springframework.data.repository.findByIdOrNull -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import com.sangdol.roomescape.common.config.next +import com.sangdol.common.persistence.IDGenerator import com.sangdol.roomescape.common.dto.CurrentUserContext import com.sangdol.roomescape.common.dto.UserLoginCredentials import com.sangdol.roomescape.common.dto.toCredentials @@ -17,6 +11,11 @@ import com.sangdol.roomescape.user.web.UserContactResponse import com.sangdol.roomescape.user.web.UserCreateRequest import com.sangdol.roomescape.user.web.UserCreateResponse import com.sangdol.roomescape.user.web.toEntity +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional private val log: KLogger = KotlinLogging.logger {} @@ -27,7 +26,7 @@ class UserService( private val userRepository: UserRepository, private val userStatusHistoryRepository: UserStatusHistoryRepository, private val userValidator: UserValidator, - private val tsidFactory: TsidFactory + private val idGenerator: IDGenerator ) { @Transactional(readOnly = true) fun findContextById(id: Long): CurrentUserContext { @@ -74,7 +73,7 @@ class UserService( userValidator.validateCanSignup(request.email, request.phone) val user: UserEntity = userRepository.save( - request.toEntity(id = tsidFactory.next(), status = UserStatus.ACTIVE) + request.toEntity(id = idGenerator.create(), status = UserStatus.ACTIVE) ).also { log.info { "[UserService.signup] 회원 저장 완료: id:${it.id}" } }.also { @@ -94,7 +93,7 @@ class UserService( private fun createHistory(user: UserEntity, reason: String): UserStatusHistoryEntity { return userStatusHistoryRepository.save( - UserStatusHistoryEntity(id = tsidFactory.next(), userId = user.id, reason = reason, status = user.status) + UserStatusHistoryEntity(id = idGenerator.create(), userId = user.id, reason = reason, status = user.status) ).also { log.info { "[UserService.signup] 회원 상태 이력 저장 완료: userStatusHistoryId:${it.id}" } } -- 2.47.2 From 81572246d2e41a7f43f0f3cb897401d36441b809 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:19:00 +0900 Subject: [PATCH 10/39] =?UTF-8?q?refactor:=20common.persistence=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EB=B6=84=EB=A6=AC=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=ED=95=9C=20=EA=B8=B0=EC=A1=B4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sangdol/roomescape/auth/JwtUtilsTest.kt | 13 +- .../roomescape/data/DefaultDataInitializer.kt | 1821 ++++++++--------- .../roomescape/data/PopulationDataParser.kt | 8 +- .../roomescape/supports/DummyInitializer.kt | 15 +- .../sangdol/roomescape/supports/Fixtures.kt | 32 +- .../sangdol/roomescape/theme/ThemeApiTest.kt | 13 +- 6 files changed, 948 insertions(+), 954 deletions(-) diff --git a/service/src/test/kotlin/com/sangdol/roomescape/auth/JwtUtilsTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/auth/JwtUtilsTest.kt index 02415b46..0ab97084 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/auth/JwtUtilsTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/auth/JwtUtilsTest.kt @@ -1,13 +1,12 @@ package com.sangdol.roomescape.auth -import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.FunSpec -import io.kotest.matchers.shouldBe import com.sangdol.roomescape.auth.exception.AuthErrorCode import com.sangdol.roomescape.auth.exception.AuthException import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils -import com.sangdol.roomescape.common.config.next -import com.sangdol.roomescape.supports.tsidFactory +import com.sangdol.roomescape.supports.IDGenerator +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe class JwtUtilsTest : FunSpec() { private val jwtUtils: JwtUtils = JwtUtils( @@ -18,7 +17,7 @@ class JwtUtilsTest : FunSpec() { init { context("종합 테스트") { test("Subject + Claim을 담아 토큰을 생성한 뒤 읽어온다.") { - val subject = "${tsidFactory.next()}" + val subject = "${IDGenerator.create()}" val claim = mapOf("name" to "sangdol") jwtUtils.createToken(subject, claim).also { token -> @@ -32,7 +31,7 @@ class JwtUtilsTest : FunSpec() { } context("실패 테스트") { - val subject = "${tsidFactory.next()}" + val subject = "${IDGenerator.create()}" val claim = mapOf("name" to "sangdol") val commonToken = jwtUtils.createToken(subject, claim) diff --git a/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt b/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt index 6dd76aa4..6aa01e1d 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt @@ -1,911 +1,910 @@ -//package com.sangdol.roomescape.data -// -//import com.github.f4b6a3.tsid.TsidFactory -//import io.kotest.core.test.TestCaseOrder -//import jakarta.persistence.EntityManager -//import kotlinx.coroutines.Dispatchers -//import kotlinx.coroutines.coroutineScope -//import kotlinx.coroutines.joinAll -//import kotlinx.coroutines.launch -//import kotlinx.coroutines.sync.Semaphore -//import org.springframework.beans.factory.annotation.Autowired -//import org.springframework.jdbc.core.JdbcTemplate -//import org.springframework.test.context.ActiveProfiles -//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity -//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel -//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -//import com.sangdol.roomescape.common.config.next -//import com.sangdol.roomescape.common.util.TransactionExecutionUtil -//import com.sangdol.roomescape.payment.infrastructure.common.* -//import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus -//import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus -//import com.sangdol.roomescape.supports.AdminFixture -//import com.sangdol.roomescape.supports.FunSpecSpringbootTest -//import com.sangdol.roomescape.supports.randomPhoneNumber -//import com.sangdol.roomescape.supports.randomString -//import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty -//import com.sangdol.roomescape.user.business.SIGNUP -//import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity -//import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus -//import com.sangdol.roomescape.user.web.UserContactResponse -//import java.sql.Timestamp -//import java.time.LocalDateTime -//import java.time.LocalTime -//import java.time.OffsetDateTime -// -//@ActiveProfiles("test", "test-mysql") -//abstract class AbstractDataInitializer( -// val semaphore: Semaphore = Semaphore(permits = 10), -//) : FunSpecSpringbootTest( -// enableCleanerExtension = false -//) { -// @Autowired -// lateinit var entityManager: EntityManager -// -// @Autowired -// lateinit var jdbcTemplate: JdbcTemplate -// -// @Autowired -// lateinit var transactionExecutionUtil: TransactionExecutionUtil -// -// @Autowired -// lateinit var tsidFactory: TsidFactory -// -// override fun testCaseOrder(): TestCaseOrder? = TestCaseOrder.Sequential -// -// suspend fun initialize() { -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0") -// -// jdbcTemplate.query("SHOW TABLES") { rs, _ -> -// rs.getString(1).lowercase() -// }.forEach { -// jdbcTemplate.execute("TRUNCATE TABLE $it") -// } -// -// jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1") -// -// this::class.java.getResource("/schema/region-data.sql")?.readText()?.let { sql -> -// jdbcTemplate.execute(sql) -// } -// } -// } -// -// suspend fun executeBatch(sql: String, batchArgs: List>) { -// semaphore.acquire() -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// jdbcTemplate.batchUpdate(sql, batchArgs) -// } -// -// semaphore.release() -// } -//} -// -//class DefaultDataInitializer : AbstractDataInitializer() { -// -// // 1. HQ Admin 추가 -// // 2. Store 추가 -> CreatedBy / UpdatedBy는 HQ Admin 중 한명으로 고정 -// // 3. Store Admin 추가 -> CreatedBy / UpdatedBy는 HQ Admin 본인으로 고정 -// init { -// lateinit var superHQAdmin: AdminEntity -// -// // 모든 테이블 초기화 + 지역 데이터 삽입 + HQ 관리자 1명 생성 -// beforeSpec { -// initialize() -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// superHQAdmin = testAuthUtil.createAdmin(AdminFixture.hqDefault.apply { -// this.createdBy = this.id -// this.updatedBy = this.id -// }) -// } -// } -// -// context("관리자, 매장, 테마 초기 데이터 생성") { -// test("각 PermissionLevel 마다 20명의 HQ 관리자 생성") { -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// AdminPermissionLevel.entries.forEach { -// repeat(20) { index -> -// AdminFixture.create( -// account = "hq_${it.name.lowercase()}_$index", -// name = randomKoreanName(), -// phone = randomPhoneNumber(), -// type = AdminType.HQ, -// permissionLevel = it -// ).apply { -// this.createdBy = superHQAdmin.id -// this.updatedBy = superHQAdmin.id -// }.also { -// entityManager.persist(it) -// } -// } -// } -// } -// } -// -// test("전체 매장 생성") { -// val storeDataInitializer = StoreDataInitializer() -// val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", -// Long::class.java -// ).setParameter("type", AdminType.HQ) -// .setParameter( -// "permissionLevels", -// listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) -// ) -// .resultList -// }.map { it.toString() } -// -// val sqlFile = storeDataInitializer.createStoreDataSqlFile(creatableAdminIds) -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// jdbcTemplate.execute(sqlFile.readText()) -// } -// } -// -// test("각 매장당 1명의 ${AdminPermissionLevel.FULL_ACCESS} 권한의 StoreManager 1명 + ${AdminPermissionLevel.WRITABLE}의 2명 + 나머지 권한은 3명씩 생성") { -// val storeAdminCountsByPermissionLevel = mapOf( -// AdminPermissionLevel.WRITABLE to 2, -// AdminPermissionLevel.READ_ALL to 3, -// AdminPermissionLevel.READ_SUMMARY to 3 -// ) -// -// val storeIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT s.id FROM StoreEntity s", -// Long::class.java -// ).resultList -// }.map { it as Long } -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// storeIds.forEach { storeId -> -// // StoreManager 1명 생성 -// val storeManager = AdminFixture.create( -// account = "$storeId", -// name = randomKoreanName(), -// phone = randomPhoneNumber(), -// type = AdminType.STORE, -// storeId = storeId, -// permissionLevel = AdminPermissionLevel.FULL_ACCESS -// ).apply { -// this.createdBy = superHQAdmin.id -// this.updatedBy = superHQAdmin.id -// }.also { -// entityManager.persist(it) -// } -// -// storeAdminCountsByPermissionLevel.forEach { (permissionLevel, count) -> -// repeat(count) { index -> -// AdminFixture.create( -// account = randomString(), -// name = randomKoreanName(), -// phone = randomPhoneNumber(), -// type = AdminType.STORE, -// storeId = storeId, -// permissionLevel = permissionLevel -// ).apply { -// this.createdBy = storeManager.id -// this.updatedBy = storeManager.id -// }.also { -// entityManager.persist(it) -// } -// } -// } -// entityManager.flush() -// entityManager.clear() -// } -// } -// } -// -// test("총 500개의 테마 생성: 지난 2년 전 부터 지금까지의 랜덤 테마 + active 상태인 1달 이내 생성 테마 10개") { -// val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", -// Long::class.java -// ).setParameter("type", AdminType.HQ) -// .setParameter( -// "permissionLevels", -// listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) -// ) -// .resultList -// } -// val sql = -// "INSERT INTO theme (id, name, description, thumbnail_url, is_active, available_minutes, expected_minutes_from, expected_minutes_to, price, difficulty, min_participants, max_participants, created_at, created_by, updated_at, updated_by) VALUES (?," + -// "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" -// val batchSize = 100 -// val batchArgs = mutableListOf>() -// -// repeat(500) { i -> -// val randomDay = if (i <= 9) (1..30).random() else (1..365 * 2).random() -// val randomCreatedAt: LocalDateTime = LocalDateTime.now().minusDays(randomDay.toLong()) -// val randomThemeName = -// (1..7).random().let { repeat -> (1..repeat).joinToString("") { randomKoreanName() } } -// val availableMinutes = (6..20).random() * 10 -// val expectedMinutesTo = availableMinutes - ((1..3).random() * 10) -// val expectedMinutesFrom = expectedMinutesTo - ((1..2).random() * 10) -// val randomPrice = (0..40).random() * 500 -// val minParticipant = (1..10).random() -// val maxParticipant = minParticipant + (1..10).random() -// val createdBy = creatableAdminIds.random() -// -// batchArgs.add( -// arrayOf( -// tsidFactory.next(), -// randomThemeName, -// "$randomThemeName 설명이에요!!", -// "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFiuCdwdz88l6pdfsRy1nFl0IHUVI7JMTQHg&s", -// if (randomDay <= 30) true else false, -// availableMinutes.toShort(), -// expectedMinutesFrom.toShort(), -// expectedMinutesTo.toShort(), -// randomPrice, -// Difficulty.entries.random().name, -// minParticipant.toShort(), -// maxParticipant.toShort(), -// Timestamp.valueOf(randomCreatedAt), -// createdBy, -// Timestamp.valueOf(randomCreatedAt), -// createdBy -// ) -// ) -// } -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// jdbcTemplate.batchUpdate(sql, batchArgs) -// } -// } -// } -// } -//} -// -//class UserDataInitializer : AbstractDataInitializer() { -// val userCount = 1_000_000 -// -// init { -// context("유저 초기 데이터 생성") { -// test("$userCount 명의 회원 생성") { -// val regions: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT r.code FROM RegionEntity r", -// String::class.java -// ).resultList -// } -// -// val chunkSize = 10_000 -// val chunks = userCount / chunkSize -// -// coroutineScope { -// (1..chunks).map { -// launch(Dispatchers.IO) { -// processUsers(chunkSize, regions) -// } -// }.joinAll() -// } -// } -// -// test("휴대폰 번호가 중복된 유저들에게 재배정") { -// val duplicatePhoneUsers: List = -// transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// """ -// SELECT u FROM UserEntity u -// WHERE u.phone IN ( -// SELECT u2.phone FROM UserEntity u2 -// GROUP BY u2.phone -// HAVING COUNT(u2.id) > 1 -// ) -// ORDER BY u.phone, u.id -// """.trimIndent(), -// UserEntity::class.java -// ).resultList -// } -// -// jdbcTemplate.execute("CREATE INDEX idx_users__phone ON users (phone)") -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// var currentPhone: String? = null -// duplicatePhoneUsers.forEach { user -> -// if (user.phone != currentPhone) { -// currentPhone = user.phone -// } else { -// var newPhone: String -// -// do { -// newPhone = randomPhoneNumber() -// val count: Long = entityManager.createQuery( -// "SELECT COUNT(u.id) FROM UserEntity u WHERE u.phone = :phone", -// Long::class.java -// ).setParameter("phone", newPhone) -// .singleResult -// -// if (count == 0L) break -// } while (true) -// -// user.phone = newPhone -// user.updatedAt = LocalDateTime.now() -// entityManager.merge(user) -// } -// } -// } -// -// jdbcTemplate.execute("DROP INDEX idx_users__phone ON users") -// } -// -// test("회원 상태 변경 이력 저장") { -// val userId: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT u.id FROM UserEntity u", -// Long::class.java -// ).resultList -// } -// -// coroutineScope { -// userId.chunked(10_000).map { chunk -> -// launch(Dispatchers.IO) { -// processStatus(chunk) -// } -// }.joinAll() -// } -// } -// } -// } -// -// private suspend fun processStatus(userIds: List) { -// val sql = """ -// INSERT INTO user_status_history ( -// id, user_id, reason, status, -// created_by, updated_by, created_at, updated_at -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// val batchArgs = mutableListOf>() -// val now = LocalDateTime.now() -// -// userIds.forEach { userId -> -// batchArgs.add( -// arrayOf( -// tsidFactory.next(), -// userId, -// SIGNUP, -// UserStatus.ACTIVE.name, -// userId, -// userId, -// Timestamp.valueOf(now), -// Timestamp.valueOf(now) -// ) -// ) -// } -// -// executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -// -// private suspend fun processUsers(chunkSize: Int, regions: List) { -// val sql = """ -// INSERT INTO users ( -// id, name, email, password, phone, region_code, status, -// created_by, updated_by, created_at, updated_at -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// val batchArgs = mutableListOf>() -// -// repeat(chunkSize) { -// val id: Long = tsidFactory.next() -// batchArgs.add( -// arrayOf( -// id, -// randomKoreanName(), -// "${randomString()}@sangdol.com", -// randomString(), -// randomPhoneNumber(), -// regions.random(), -// UserStatus.ACTIVE.name, -// id, -// id, -// Timestamp.valueOf(LocalDateTime.now()), -// Timestamp.valueOf(LocalDateTime.now()) -// ) -// ) -// if (batchArgs.size >= 1_000) { -// executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -// } -// -// if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -//} -// -//class ScheduleDataInitializer : AbstractDataInitializer() { -// init { -// context("일정 초기 데이터 생성") { -// test("테마 생성일 기준으로 다음 3일차, 매일 5개의 일정을 모든 매장에 생성") { -// val stores: List> = getStoreWithManagers() -// val themes: List> = getThemes() -// val maxAvailableMinutes = themes.maxOf { it.second.toInt() } -// val scheduleCountPerDay = 5 -// -// val startTime = LocalTime.of(10, 0) -// var lastTime = startTime -// val times = mutableListOf() -// -// repeat(scheduleCountPerDay) { -// times.add(lastTime) -// lastTime = lastTime.plusMinutes(maxAvailableMinutes.toLong() + 10L) -// } -// -// coroutineScope { -// themes.forEach { theme -> -// launch(Dispatchers.IO) { -// processTheme(theme, stores, times) -// } -// } -// } -// } -// } -// } -// -// private suspend fun processTheme( -// theme: Triple, -// stores: List>, -// times: List -// ) { -// val sql = """ -// INSERT INTO schedule ( -// id, store_id, theme_id, date, time, status, -// created_by, updated_by, created_at, updated_at -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// -// val batchArgs = mutableListOf>() -// -// val now = LocalDateTime.now() -// stores.forEach { (storeId, adminId) -> -// (1..3).forEach { dayOffset -> -// val date = theme.third.toLocalDate().plusDays(dayOffset.toLong()) -// -// times.forEach { time -> -// val scheduledAt = LocalDateTime.of(date, time) -// val status = -// if (scheduledAt.isAfter(now)) ScheduleStatus.AVAILABLE.name else ScheduleStatus.RESERVED.name -// -// batchArgs.add( -// arrayOf( -// tsidFactory.next(), storeId, theme.first, date, time, -// status, adminId, adminId, Timestamp.valueOf(now), Timestamp.valueOf(now) -// ) -// ) -// -// if (batchArgs.size >= 500) { -// executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -// } -// } -// } -// -// if (batchArgs.isNotEmpty()) { -// executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -// } -// -// private fun getStoreWithManagers(): List> { -// return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT a.storeId, a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel = :permissionLevel", -// List::class.java -// ).setParameter("type", AdminType.STORE) -// .setParameter("permissionLevel", AdminPermissionLevel.FULL_ACCESS) -// .resultList -// }.map { -// val array = it as List<*> -// Pair(array[0] as Long, array[1] as Long) -// } -// } -// -// private fun getThemes(): List> { -// return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT t._id, t.availableMinutes, t.createdAt FROM ThemeEntity t", -// List::class.java -// ) -// .resultList -// }.map { -// val array = it as List<*> -// Triple(array[0] as Long, array[1] as Short, array[2] as LocalDateTime) -// } -// } -//} -// -///** -// * 아래의 ReservationDataInitializer 에서 사용할 임시 DTO 클래스 -// */ -//data class ScheduleWithThemeParticipants( -// val scheduleId: Long, -// val themeMinParticipants: Short, -// val themeMaxParticipants: Short, -//) -// -//class ReservationDataInitializer : AbstractDataInitializer() { -// -// init { -// context("예약 초기 데이터 생성") { -// test("${ScheduleStatus.RESERVED}인 모든 일정에 예약을 1개씩 배정한다.") { -// val chunkSize = 10_000 -// -// val chunkedSchedules: List> = entityManager.createQuery( -// "SELECT new com.sangdol.roomescape.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status", -// ScheduleWithThemeParticipants::class.java -// ).setParameter("status", ScheduleStatus.RESERVED).resultList.chunked(chunkSize) -// -// val chunkedUsers: List> = entityManager.createQuery( -// "SELECT new com.sangdol.roomescape.user.web.UserContactResponse(u._id, u.name, u.phone) FROM UserEntity u", -// UserContactResponse::class.java -// ).resultList.chunked(chunkSize) -// -// -// coroutineScope { -// chunkedSchedules.forEachIndexed { idx, schedules -> -// launch(Dispatchers.IO) { -// processReservation(chunkedUsers[idx % chunkedUsers.size], schedules) -// } -// } -// } -// } -// } -// } -// -// private suspend fun processReservation( -// users: List, -// schedules: List -// ) { -// val sql = """ -// INSERT INTO reservation ( -// id, user_id, schedule_id, -// reserver_name, reserver_contact, participant_count, requirement, -// status, created_at, created_by, updated_at, updated_by -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// -// val batchArgs = mutableListOf>() -// -// val createdAt = LocalDateTime.now() -// -// schedules.forEachIndexed { idx, schedule -> -// val user: UserContactResponse = users[idx % users.size] -// -// batchArgs.add( -// arrayOf( -// tsidFactory.next(), -// user.id, -// schedule.scheduleId, -// user.name, -// user.phone, -// (schedule.themeMinParticipants..schedule.themeMaxParticipants).random(), -// randomKoreanWords(length = (20..100).random()), -// ReservationStatus.CONFIRMED.name, -// Timestamp.valueOf(createdAt), -// user.id, -// Timestamp.valueOf(createdAt), -// user.id, -// ) -// ) -// -// if (batchArgs.size >= 1_000) { -// executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -// } -// -// if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -//} -// -//class ReservationWithPrice( -// themePrice: Int, -// participantCount: Short, -// -// val reservationId: Long, -// val totalPrice: Int = (themePrice * participantCount), -//) -// -//data class PaymentWithMethods( -// val id: Long, -// val totalPrice: Int, -// val method: PaymentMethod -//) -// -//class PaymentDataInitializer : AbstractDataInitializer() { -// companion object { -// val requestedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().toLocalDateTime()) -// val approvedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().plusSeconds(5).toLocalDateTime()) -// val supportedPaymentMethods = listOf(PaymentMethod.TRANSFER, PaymentMethod.EASY_PAY, PaymentMethod.CARD) -// val supportedCardType = listOf(CardType.CREDIT, CardType.CHECK) -// -// val settlementStatus = "COMPLETED" -// -// val paymentSql: String = """ -// INSERT INTO payment( -// id, reservation_id, type, method, -// payment_key, order_id, total_amount, status, -// requested_at, approved_at -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// -// val paymentDetailSql: String = """ -// INSERT INTO payment_detail( -// id, payment_id, supplied_amount, vat -// ) VALUES (?, ?, ?, ?) -// """.trimIndent() -// -// val paymentCardDetailSql: String = """ -// INSERT INTO payment_card_detail( -// id, issuer_code, card_type, owner_type, -// amount, card_number, approval_number, installment_plan_months, -// is_interest_free, easypay_provider_code, easypay_discount_amount -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// -// val paymentEasypayPrepaidDetailSql: String = """ -// INSERT INTO payment_easypay_prepaid_detail( -// id, easypay_provider_code, amount, discount_amount -// ) VALUES (?, ?, ?, ?) -// """.trimIndent() -// -// val paymentBankTransferDetailSql: String = """ -// INSERT INTO payment_bank_transfer_detail( -// id, bank_code, settlement_status -// ) VALUES (?, ?, ?) -// """.trimIndent() -// } -// -// init { -// context("결제 데이터 초기화") { -// test("모든 예약에 맞춰 1:1로 결제 및 결제 상세 데이터를 생성한다.") { -// val allReservations: List = entityManager.createQuery( -// "SELECT t.price, r.participantCount, r._id FROM ReservationEntity r JOIN ScheduleEntity s ON s._id = r.scheduleId JOIN ThemeEntity t ON t.id = s.themeId", -// List::class.java -// ).resultList.map { -// val items = it as List<*> -// ReservationWithPrice( -// themePrice = items[0] as Int, -// participantCount = items[1] as Short, -// reservationId = items[2] as Long -// ) -// } -// -// coroutineScope { -// allReservations.chunked(10_000).forEach { reservations -> -// launch(Dispatchers.IO) { -// processPaymentAndDefaultDetail(reservations) -// } -// } -// } -// } -// -// test("기존 결제 데이터에 상세 정보(계좌이체, 카드, 간편결제) 데이터를 생성한다.") { -// val allPayments: List = entityManager.createQuery( -// "SELECT new com.sangdol.roomescape.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId", -// PaymentWithMethods::class.java -// ).resultList -// -// coroutineScope { -// allPayments.chunked(10_000).forEach { payments -> -// launch(Dispatchers.IO) { -// processPaymentDetail(payments) -// } -// } -// } -// } -// -// test("null 컴파일 에러를 피하기 위해 문자열 null로 임시 지정한 컬럼을 변경한다.") { -// jdbcTemplate.execute("CREATE INDEX idx_payment_card_detail_easypay ON payment_card_detail (easypay_provider_code)") -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// jdbcTemplate.update( -// "UPDATE payment_card_detail SET easypay_provider_code = ? WHERE easypay_provider_code = ?", -// null, -// "null" -// ) -// } -// -// jdbcTemplate.execute("DROP INDEX idx_payment_card_detail_easypay ON payment_card_detail") -// } -// } -// } -// -// private suspend fun processPaymentAndDefaultDetail(reservations: List) { -// val paymentBatchArgs = mutableListOf>() -// val detailBatchArgs = mutableListOf>() -// -// reservations.forEachIndexed { idx, reservations -> -// val id = tsidFactory.next() -// val totalPrice = reservations.totalPrice -// paymentBatchArgs.add( -// arrayOf( -// id, -// reservations.reservationId, -// PaymentType.NORMAL.name, -// randomPaymentMethod(), -// randomString(length = 64), -// randomString(length = 20), -// totalPrice, -// PaymentStatus.DONE.name, -// requestedAtCache, -// approvedAtCache, -// ) -// ) -// if (paymentBatchArgs.size >= 1_000) { -// executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } -// } -// -// val suppliedAmount: Int = (totalPrice * 0.9).toInt() -// val vat: Int = (totalPrice - suppliedAmount) -// -// detailBatchArgs.add( -// arrayOf( -// tsidFactory.next(), -// id, -// suppliedAmount, -// vat -// ) -// ) -// -// if (detailBatchArgs.size >= 1_000) { -// executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } -// } -// } -// -// if (paymentBatchArgs.isNotEmpty()) { -// executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } -// } -// if (detailBatchArgs.isNotEmpty()) { -// executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } -// } -// } -// -// private suspend fun processPaymentDetail(payments: List) { -// val transferBatchArgs = mutableListOf>() -// val cardBatchArgs = mutableListOf>() -// val easypayPrepaidBatchArgs = mutableListOf>() -// -// payments.forEach { payment -> -// val totalPrice = payment.totalPrice -// val randomDiscountAmount = -// if (totalPrice < 100 || Math.random() < 0.8) 0 else ((100..totalPrice).random() / 100) * 100 -// val amount = totalPrice - randomDiscountAmount -// -// when (payment.method) { -// PaymentMethod.TRANSFER -> { -// transferBatchArgs.add( -// arrayOf( -// payment.id, -// BankCode.entries.random().name, -// settlementStatus -// ) -// ) -// if (transferBatchArgs.size >= 1_000) { -// executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } -// } -// } -// -// PaymentMethod.EASY_PAY -> { -// if (Math.random() <= 0.7) { -// cardBatchArgs.add( -// arrayOf( -// payment.id, -// CardIssuerCode.entries.random().name, -// supportedCardType.random().name, -// CardOwnerType.PERSONAL.name, -// amount, -// randomCardNumber(), -// randomApprovalNumber(), -// randomInstallmentPlanMonths(amount), -// true, -// EasyPayCompanyCode.entries.random().name, -// randomDiscountAmount -// ) -// ) -// -// if (cardBatchArgs.size >= 1_000) { -// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } -// } -// -// } else { -// easypayPrepaidBatchArgs.add( -// arrayOf( -// payment.id, -// EasyPayCompanyCode.entries.random().name, -// amount, -// randomDiscountAmount, -// ) -// ) -// -// if (easypayPrepaidBatchArgs.size >= 1_000) { -// executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } -// } -// } -// } -// -// PaymentMethod.CARD -> { -// cardBatchArgs.add( -// arrayOf( -// payment.id, -// CardIssuerCode.entries.random().name, -// supportedCardType.random().name, -// CardOwnerType.PERSONAL.name, -// totalPrice, -// randomCardNumber(), -// randomApprovalNumber(), -// randomInstallmentPlanMonths(totalPrice), -// true, -// "null", -// 0, -// ) -// ) -// -// if (cardBatchArgs.size >= 1_000) { -// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } -// } -// } -// -// else -> return@forEach -// } -// } -// if (transferBatchArgs.isNotEmpty()) { -// executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } -// } -// if (cardBatchArgs.isNotEmpty()) { -// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } -// } -// if (easypayPrepaidBatchArgs.isNotEmpty()) { -// executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } -// } -// } -// -// private suspend fun randomPaymentMethod(): String { -// val random = Math.random() -// -// return if (random <= 0.5) { -// PaymentMethod.EASY_PAY.name -// } else if (random <= 0.9) { -// PaymentMethod.CARD.name -// } else { -// PaymentMethod.TRANSFER.name -// } -// } -// -// private suspend fun randomCardNumber(): String { -// return "${(10000000..99999999).random()}****${(100..999).random()}*" -// } -// -// private suspend fun randomApprovalNumber(): String { -// return "${(10000000..99999999).random()}" -// } -// -// private suspend fun randomInstallmentPlanMonths(amount: Int): Int { -// return if (amount < 50_000 || Math.random() < 0.9) { -// 0 -// } else { -// (1..6).random() -// } -// } -//} -// -//fun randomKoreanName(): String { -// val lastNames = listOf( -// "김", "이", "박", "최", "정", "강", "조", "윤", "장", "임", -// "오", "서", "신", "권", "황", "안", "송", "류", "홍", "전", "고", "문", "양", "손", "배", "백", "허", "유", "남", "심", "노" -// ) -// -// return "${lastNames.random()}${if (Math.random() < 0.1) randomKoreanWords(1) else randomKoreanWords(2)}" -//} -// -//fun randomKoreanWords(length: Int = 1): String { -// val words = listOf( -// "가", "나", "다", "라", "마", "바", "사", "아", "자", "차", -// "카", "타", "파", "하", "강", "민", "서", "윤", "우", "진", -// "현", "지", "은", "혜", "수", "영", "주", "원", "희", "경", -// "선", "아", "나", "다", "라", "마", "바", "사", "아", "자", -// "차", "카", "타", "파", "하", -// ) -// -// return (1..length).joinToString("") { words.random() } -//} +package com.sangdol.roomescape.data + +import com.sangdol.common.persistence.IDGenerator +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.common.util.TransactionExecutionUtil +import com.sangdol.roomescape.payment.infrastructure.common.* +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.supports.AdminFixture +import com.sangdol.roomescape.supports.FunSpecSpringbootTest +import com.sangdol.roomescape.supports.randomPhoneNumber +import com.sangdol.roomescape.supports.randomString +import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty +import com.sangdol.roomescape.user.business.SIGNUP +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity +import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus +import com.sangdol.roomescape.user.web.UserContactResponse +import io.kotest.core.test.TestCaseOrder +import jakarta.persistence.EntityManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Semaphore +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.test.context.ActiveProfiles +import java.sql.Timestamp +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.OffsetDateTime + +@ActiveProfiles("test", "test-mysql") +abstract class AbstractDataInitializer( + val semaphore: Semaphore = Semaphore(permits = 10), +) : FunSpecSpringbootTest( + enableCleanerExtension = false +) { + @Autowired + lateinit var entityManager: EntityManager + + @Autowired + lateinit var jdbcTemplate: JdbcTemplate + + @Autowired + lateinit var transactionExecutionUtil: TransactionExecutionUtil + + @Autowired + lateinit var idGenerator: IDGenerator + + override fun testCaseOrder(): TestCaseOrder? = TestCaseOrder.Sequential + + suspend fun initialize() { + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0") + + jdbcTemplate.query("SHOW TABLES") { rs, _ -> + rs.getString(1).lowercase() + }.forEach { + jdbcTemplate.execute("TRUNCATE TABLE $it") + } + + jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1") + + this::class.java.getResource("/schema/region-data.sql")?.readText()?.let { sql -> + jdbcTemplate.execute(sql) + } + } + } + + suspend fun executeBatch(sql: String, batchArgs: List>) { + semaphore.acquire() + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + jdbcTemplate.batchUpdate(sql, batchArgs) + } + + semaphore.release() + } +} + +class DefaultDataInitializer : AbstractDataInitializer() { + + // 1. HQ Admin 추가 + // 2. Store 추가 -> CreatedBy / UpdatedBy는 HQ Admin 중 한명으로 고정 + // 3. Store Admin 추가 -> CreatedBy / UpdatedBy는 HQ Admin 본인으로 고정 + init { + lateinit var superHQAdmin: AdminEntity + + // 모든 테이블 초기화 + 지역 데이터 삽입 + HQ 관리자 1명 생성 + beforeSpec { + initialize() + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + superHQAdmin = testAuthUtil.createAdmin(AdminFixture.hqDefault.apply { + this.createdBy = this.id + this.updatedBy = this.id + }) + } + } + + context("관리자, 매장, 테마 초기 데이터 생성") { + test("각 PermissionLevel 마다 20명의 HQ 관리자 생성") { + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + AdminPermissionLevel.entries.forEach { + repeat(20) { index -> + AdminFixture.create( + account = "hq_${it.name.lowercase()}_$index", + name = randomKoreanName(), + phone = randomPhoneNumber(), + type = AdminType.HQ, + permissionLevel = it + ).apply { + this.createdBy = superHQAdmin.id + this.updatedBy = superHQAdmin.id + }.also { + entityManager.persist(it) + } + } + } + } + } + + test("전체 매장 생성") { + val storeDataInitializer = StoreDataInitializer() + val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", + Long::class.java + ).setParameter("type", AdminType.HQ) + .setParameter( + "permissionLevels", + listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) + ) + .resultList + }.map { it.toString() } + + val sqlFile = storeDataInitializer.createStoreDataSqlFile(creatableAdminIds) + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + jdbcTemplate.execute(sqlFile.readText()) + } + } + + test("각 매장당 1명의 ${AdminPermissionLevel.FULL_ACCESS} 권한의 StoreManager 1명 + ${AdminPermissionLevel.WRITABLE}의 2명 + 나머지 권한은 3명씩 생성") { + val storeAdminCountsByPermissionLevel = mapOf( + AdminPermissionLevel.WRITABLE to 2, + AdminPermissionLevel.READ_ALL to 3, + AdminPermissionLevel.READ_SUMMARY to 3 + ) + + val storeIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT s.id FROM StoreEntity s", + Long::class.java + ).resultList + }.map { it as Long } + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + storeIds.forEach { storeId -> + // StoreManager 1명 생성 + val storeManager = AdminFixture.create( + account = "$storeId", + name = randomKoreanName(), + phone = randomPhoneNumber(), + type = AdminType.STORE, + storeId = storeId, + permissionLevel = AdminPermissionLevel.FULL_ACCESS + ).apply { + this.createdBy = superHQAdmin.id + this.updatedBy = superHQAdmin.id + }.also { + entityManager.persist(it) + } + + storeAdminCountsByPermissionLevel.forEach { (permissionLevel, count) -> + repeat(count) { index -> + AdminFixture.create( + account = randomString(), + name = randomKoreanName(), + phone = randomPhoneNumber(), + type = AdminType.STORE, + storeId = storeId, + permissionLevel = permissionLevel + ).apply { + this.createdBy = storeManager.id + this.updatedBy = storeManager.id + }.also { + entityManager.persist(it) + } + } + } + entityManager.flush() + entityManager.clear() + } + } + } + + test("총 500개의 테마 생성: 지난 2년 전 부터 지금까지의 랜덤 테마 + active 상태인 1달 이내 생성 테마 10개") { + val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", + Long::class.java + ).setParameter("type", AdminType.HQ) + .setParameter( + "permissionLevels", + listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) + ) + .resultList + } + val sql = + "INSERT INTO theme (id, name, description, thumbnail_url, is_active, available_minutes, expected_minutes_from, expected_minutes_to, price, difficulty, min_participants, max_participants, created_at, created_by, updated_at, updated_by) VALUES (?," + + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + val batchSize = 100 + val batchArgs = mutableListOf>() + + repeat(500) { i -> + val randomDay = if (i <= 9) (1..30).random() else (1..365 * 2).random() + val randomCreatedAt: LocalDateTime = LocalDateTime.now().minusDays(randomDay.toLong()) + val randomThemeName = + (1..7).random().let { repeat -> (1..repeat).joinToString("") { randomKoreanName() } } + val availableMinutes = (6..20).random() * 10 + val expectedMinutesTo = availableMinutes - ((1..3).random() * 10) + val expectedMinutesFrom = expectedMinutesTo - ((1..2).random() * 10) + val randomPrice = (0..40).random() * 500 + val minParticipant = (1..10).random() + val maxParticipant = minParticipant + (1..10).random() + val createdBy = creatableAdminIds.random() + + batchArgs.add( + arrayOf( + idGenerator.create(), + randomThemeName, + "$randomThemeName 설명이에요!!", + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFiuCdwdz88l6pdfsRy1nFl0IHUVI7JMTQHg&s", + if (randomDay <= 30) true else false, + availableMinutes.toShort(), + expectedMinutesFrom.toShort(), + expectedMinutesTo.toShort(), + randomPrice, + Difficulty.entries.random().name, + minParticipant.toShort(), + maxParticipant.toShort(), + Timestamp.valueOf(randomCreatedAt), + createdBy, + Timestamp.valueOf(randomCreatedAt), + createdBy + ) + ) + } + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + jdbcTemplate.batchUpdate(sql, batchArgs) + } + } + } + } +} + +class UserDataInitializer : AbstractDataInitializer() { + val userCount = 1_000_000 + + init { + context("유저 초기 데이터 생성") { + test("$userCount 명의 회원 생성") { + val regions: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT r.code FROM RegionEntity r", + String::class.java + ).resultList + } + + val chunkSize = 10_000 + val chunks = userCount / chunkSize + + coroutineScope { + (1..chunks).map { + launch(Dispatchers.IO) { + processUsers(chunkSize, regions) + } + }.joinAll() + } + } + + test("휴대폰 번호가 중복된 유저들에게 재배정") { + val duplicatePhoneUsers: List = + transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + """ + SELECT u FROM UserEntity u + WHERE u.phone IN ( + SELECT u2.phone FROM UserEntity u2 + GROUP BY u2.phone + HAVING COUNT(u2.id) > 1 + ) + ORDER BY u.phone, u.id + """.trimIndent(), + UserEntity::class.java + ).resultList + } + + jdbcTemplate.execute("CREATE INDEX idx_users__phone ON users (phone)") + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + var currentPhone: String? = null + duplicatePhoneUsers.forEach { user -> + if (user.phone != currentPhone) { + currentPhone = user.phone + } else { + var newPhone: String + + do { + newPhone = randomPhoneNumber() + val count: Long = entityManager.createQuery( + "SELECT COUNT(u.id) FROM UserEntity u WHERE u.phone = :phone", + Long::class.java + ).setParameter("phone", newPhone) + .singleResult + + if (count == 0L) break + } while (true) + + user.phone = newPhone + user.updatedAt = LocalDateTime.now() + entityManager.merge(user) + } + } + } + + jdbcTemplate.execute("DROP INDEX idx_users__phone ON users") + } + + test("회원 상태 변경 이력 저장") { + val userId: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT u.id FROM UserEntity u", + Long::class.java + ).resultList + } + + coroutineScope { + userId.chunked(10_000).map { chunk -> + launch(Dispatchers.IO) { + processStatus(chunk) + } + }.joinAll() + } + } + } + } + + private suspend fun processStatus(userIds: List) { + val sql = """ + INSERT INTO user_status_history ( + id, user_id, reason, status, + created_by, updated_by, created_at, updated_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + val batchArgs = mutableListOf>() + val now = LocalDateTime.now() + + userIds.forEach { userId -> + batchArgs.add( + arrayOf( + idGenerator.create(), + userId, + SIGNUP, + UserStatus.ACTIVE.name, + userId, + userId, + Timestamp.valueOf(now), + Timestamp.valueOf(now) + ) + ) + } + + executeBatch(sql, batchArgs).also { batchArgs.clear() } + } + + private suspend fun processUsers(chunkSize: Int, regions: List) { + val sql = """ + INSERT INTO users ( + id, name, email, password, phone, region_code, status, + created_by, updated_by, created_at, updated_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + val batchArgs = mutableListOf>() + + repeat(chunkSize) { + val id: Long = idGenerator.create() + batchArgs.add( + arrayOf( + id, + randomKoreanName(), + "${randomString()}@sangdol.com", + randomString(), + randomPhoneNumber(), + regions.random(), + UserStatus.ACTIVE.name, + id, + id, + Timestamp.valueOf(LocalDateTime.now()), + Timestamp.valueOf(LocalDateTime.now()) + ) + ) + if (batchArgs.size >= 1_000) { + executeBatch(sql, batchArgs).also { batchArgs.clear() } + } + } + + if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } + } +} + +class ScheduleDataInitializer : AbstractDataInitializer() { + init { + context("일정 초기 데이터 생성") { + test("테마 생성일 기준으로 다음 3일차, 매일 5개의 일정을 모든 매장에 생성") { + val stores: List> = getStoreWithManagers() + val themes: List> = getThemes() + val maxAvailableMinutes = themes.maxOf { it.second.toInt() } + val scheduleCountPerDay = 5 + + val startTime = LocalTime.of(10, 0) + var lastTime = startTime + val times = mutableListOf() + + repeat(scheduleCountPerDay) { + times.add(lastTime) + lastTime = lastTime.plusMinutes(maxAvailableMinutes.toLong() + 10L) + } + + coroutineScope { + themes.forEach { theme -> + launch(Dispatchers.IO) { + processTheme(theme, stores, times) + } + } + } + } + } + } + + private suspend fun processTheme( + theme: Triple, + stores: List>, + times: List + ) { + val sql = """ + INSERT INTO schedule ( + id, store_id, theme_id, date, time, status, + created_by, updated_by, created_at, updated_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + + val batchArgs = mutableListOf>() + + val now = LocalDateTime.now() + stores.forEach { (storeId, adminId) -> + (1..3).forEach { dayOffset -> + val date = theme.third.toLocalDate().plusDays(dayOffset.toLong()) + + times.forEach { time -> + val scheduledAt = LocalDateTime.of(date, time) + val status = + if (scheduledAt.isAfter(now)) ScheduleStatus.AVAILABLE.name else ScheduleStatus.RESERVED.name + + batchArgs.add( + arrayOf( + idGenerator.create(), storeId, theme.first, date, time, + status, adminId, adminId, Timestamp.valueOf(now), Timestamp.valueOf(now) + ) + ) + + if (batchArgs.size >= 500) { + executeBatch(sql, batchArgs).also { batchArgs.clear() } + } + } + } + } + + if (batchArgs.isNotEmpty()) { + executeBatch(sql, batchArgs).also { batchArgs.clear() } + } + } + + private fun getStoreWithManagers(): List> { + return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT a.storeId, a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel = :permissionLevel", + List::class.java + ).setParameter("type", AdminType.STORE) + .setParameter("permissionLevel", AdminPermissionLevel.FULL_ACCESS) + .resultList + }.map { + val array = it as List<*> + Pair(array[0] as Long, array[1] as Long) + } + } + + private fun getThemes(): List> { + return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT t._id, t.availableMinutes, t.createdAt FROM ThemeEntity t", + List::class.java + ) + .resultList + }.map { + val array = it as List<*> + Triple(array[0] as Long, array[1] as Short, array[2] as LocalDateTime) + } + } +} + +/** + * 아래의 ReservationDataInitializer 에서 사용할 임시 DTO 클래스 + */ +data class ScheduleWithThemeParticipants( + val scheduleId: Long, + val themeMinParticipants: Short, + val themeMaxParticipants: Short, +) + +class ReservationDataInitializer : AbstractDataInitializer() { + + init { + context("예약 초기 데이터 생성") { + test("${ScheduleStatus.RESERVED}인 모든 일정에 예약을 1개씩 배정한다.") { + val chunkSize = 10_000 + + val chunkedSchedules: List> = entityManager.createQuery( + "SELECT new com.sangdol.roomescape.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status", + ScheduleWithThemeParticipants::class.java + ).setParameter("status", ScheduleStatus.RESERVED).resultList.chunked(chunkSize) + + val chunkedUsers: List> = entityManager.createQuery( + "SELECT new com.sangdol.roomescape.user.web.UserContactResponse(u._id, u.name, u.phone) FROM UserEntity u", + UserContactResponse::class.java + ).resultList.chunked(chunkSize) + + + coroutineScope { + chunkedSchedules.forEachIndexed { idx, schedules -> + launch(Dispatchers.IO) { + processReservation(chunkedUsers[idx % chunkedUsers.size], schedules) + } + } + } + } + } + } + + private suspend fun processReservation( + users: List, + schedules: List + ) { + val sql = """ + INSERT INTO reservation ( + id, user_id, schedule_id, + reserver_name, reserver_contact, participant_count, requirement, + status, created_at, created_by, updated_at, updated_by + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + + val batchArgs = mutableListOf>() + + val createdAt = LocalDateTime.now() + + schedules.forEachIndexed { idx, schedule -> + val user: UserContactResponse = users[idx % users.size] + + batchArgs.add( + arrayOf( + idGenerator.create(), + user.id, + schedule.scheduleId, + user.name, + user.phone, + (schedule.themeMinParticipants..schedule.themeMaxParticipants).random(), + randomKoreanWords(length = (20..100).random()), + ReservationStatus.CONFIRMED.name, + Timestamp.valueOf(createdAt), + user.id, + Timestamp.valueOf(createdAt), + user.id, + ) + ) + + if (batchArgs.size >= 1_000) { + executeBatch(sql, batchArgs).also { batchArgs.clear() } + } + } + + if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } + } +} + +class ReservationWithPrice( + themePrice: Int, + participantCount: Short, + + val reservationId: Long, + val totalPrice: Int = (themePrice * participantCount), +) + +data class PaymentWithMethods( + val id: Long, + val totalPrice: Int, + val method: PaymentMethod +) + +class PaymentDataInitializer : AbstractDataInitializer() { + companion object { + val requestedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().toLocalDateTime()) + val approvedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().plusSeconds(5).toLocalDateTime()) + val supportedPaymentMethods = listOf(PaymentMethod.TRANSFER, PaymentMethod.EASY_PAY, PaymentMethod.CARD) + val supportedCardType = listOf(CardType.CREDIT, CardType.CHECK) + + val settlementStatus = "COMPLETED" + + val paymentSql: String = """ + INSERT INTO payment( + id, reservation_id, type, method, + payment_key, order_id, total_amount, status, + requested_at, approved_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + + val paymentDetailSql: String = """ + INSERT INTO payment_detail( + id, payment_id, supplied_amount, vat + ) VALUES (?, ?, ?, ?) + """.trimIndent() + + val paymentCardDetailSql: String = """ + INSERT INTO payment_card_detail( + id, issuer_code, card_type, owner_type, + amount, card_number, approval_number, installment_plan_months, + is_interest_free, easypay_provider_code, easypay_discount_amount + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + + val paymentEasypayPrepaidDetailSql: String = """ + INSERT INTO payment_easypay_prepaid_detail( + id, easypay_provider_code, amount, discount_amount + ) VALUES (?, ?, ?, ?) + """.trimIndent() + + val paymentBankTransferDetailSql: String = """ + INSERT INTO payment_bank_transfer_detail( + id, bank_code, settlement_status + ) VALUES (?, ?, ?) + """.trimIndent() + } + + init { + context("결제 데이터 초기화") { + test("모든 예약에 맞춰 1:1로 결제 및 결제 상세 데이터를 생성한다.") { + val allReservations: List = entityManager.createQuery( + "SELECT t.price, r.participantCount, r._id FROM ReservationEntity r JOIN ScheduleEntity s ON s._id = r.scheduleId JOIN ThemeEntity t ON t.id = s.themeId", + List::class.java + ).resultList.map { + val items = it as List<*> + ReservationWithPrice( + themePrice = items[0] as Int, + participantCount = items[1] as Short, + reservationId = items[2] as Long + ) + } + + coroutineScope { + allReservations.chunked(10_000).forEach { reservations -> + launch(Dispatchers.IO) { + processPaymentAndDefaultDetail(reservations) + } + } + } + } + + test("기존 결제 데이터에 상세 정보(계좌이체, 카드, 간편결제) 데이터를 생성한다.") { + val allPayments: List = entityManager.createQuery( + "SELECT new com.sangdol.roomescape.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId", + PaymentWithMethods::class.java + ).resultList + + coroutineScope { + allPayments.chunked(10_000).forEach { payments -> + launch(Dispatchers.IO) { + processPaymentDetail(payments) + } + } + } + } + + test("null 컴파일 에러를 피하기 위해 문자열 null로 임시 지정한 컬럼을 변경한다.") { + jdbcTemplate.execute("CREATE INDEX idx_payment_card_detail_easypay ON payment_card_detail (easypay_provider_code)") + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + jdbcTemplate.update( + "UPDATE payment_card_detail SET easypay_provider_code = ? WHERE easypay_provider_code = ?", + null, + "null" + ) + } + + jdbcTemplate.execute("DROP INDEX idx_payment_card_detail_easypay ON payment_card_detail") + } + } + } + + private suspend fun processPaymentAndDefaultDetail(reservations: List) { + val paymentBatchArgs = mutableListOf>() + val detailBatchArgs = mutableListOf>() + + reservations.forEachIndexed { idx, reservations -> + val id = idGenerator.create() + val totalPrice = reservations.totalPrice + paymentBatchArgs.add( + arrayOf( + id, + reservations.reservationId, + PaymentType.NORMAL.name, + randomPaymentMethod(), + randomString(length = 64), + randomString(length = 20), + totalPrice, + PaymentStatus.DONE.name, + requestedAtCache, + approvedAtCache, + ) + ) + if (paymentBatchArgs.size >= 1_000) { + executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } + } + + val suppliedAmount: Int = (totalPrice * 0.9).toInt() + val vat: Int = (totalPrice - suppliedAmount) + + detailBatchArgs.add( + arrayOf( + idGenerator.create(), + id, + suppliedAmount, + vat + ) + ) + + if (detailBatchArgs.size >= 1_000) { + executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } + } + } + + if (paymentBatchArgs.isNotEmpty()) { + executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } + } + if (detailBatchArgs.isNotEmpty()) { + executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } + } + } + + private suspend fun processPaymentDetail(payments: List) { + val transferBatchArgs = mutableListOf>() + val cardBatchArgs = mutableListOf>() + val easypayPrepaidBatchArgs = mutableListOf>() + + payments.forEach { payment -> + val totalPrice = payment.totalPrice + val randomDiscountAmount = + if (totalPrice < 100 || Math.random() < 0.8) 0 else ((100..totalPrice).random() / 100) * 100 + val amount = totalPrice - randomDiscountAmount + + when (payment.method) { + PaymentMethod.TRANSFER -> { + transferBatchArgs.add( + arrayOf( + payment.id, + BankCode.entries.random().name, + settlementStatus + ) + ) + if (transferBatchArgs.size >= 1_000) { + executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } + } + } + + PaymentMethod.EASY_PAY -> { + if (Math.random() <= 0.7) { + cardBatchArgs.add( + arrayOf( + payment.id, + CardIssuerCode.entries.random().name, + supportedCardType.random().name, + CardOwnerType.PERSONAL.name, + amount, + randomCardNumber(), + randomApprovalNumber(), + randomInstallmentPlanMonths(amount), + true, + EasyPayCompanyCode.entries.random().name, + randomDiscountAmount + ) + ) + + if (cardBatchArgs.size >= 1_000) { + executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } + } + + } else { + easypayPrepaidBatchArgs.add( + arrayOf( + payment.id, + EasyPayCompanyCode.entries.random().name, + amount, + randomDiscountAmount, + ) + ) + + if (easypayPrepaidBatchArgs.size >= 1_000) { + executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } + } + } + } + + PaymentMethod.CARD -> { + cardBatchArgs.add( + arrayOf( + payment.id, + CardIssuerCode.entries.random().name, + supportedCardType.random().name, + CardOwnerType.PERSONAL.name, + totalPrice, + randomCardNumber(), + randomApprovalNumber(), + randomInstallmentPlanMonths(totalPrice), + true, + "null", + 0, + ) + ) + + if (cardBatchArgs.size >= 1_000) { + executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } + } + } + + else -> return@forEach + } + } + if (transferBatchArgs.isNotEmpty()) { + executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } + } + if (cardBatchArgs.isNotEmpty()) { + executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } + } + if (easypayPrepaidBatchArgs.isNotEmpty()) { + executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } + } + } + + private suspend fun randomPaymentMethod(): String { + val random = Math.random() + + return if (random <= 0.5) { + PaymentMethod.EASY_PAY.name + } else if (random <= 0.9) { + PaymentMethod.CARD.name + } else { + PaymentMethod.TRANSFER.name + } + } + + private suspend fun randomCardNumber(): String { + return "${(10000000..99999999).random()}****${(100..999).random()}*" + } + + private suspend fun randomApprovalNumber(): String { + return "${(10000000..99999999).random()}" + } + + private suspend fun randomInstallmentPlanMonths(amount: Int): Int { + return if (amount < 50_000 || Math.random() < 0.9) { + 0 + } else { + (1..6).random() + } + } +} + +fun randomKoreanName(): String { + val lastNames = listOf( + "김", "이", "박", "최", "정", "강", "조", "윤", "장", "임", + "오", "서", "신", "권", "황", "안", "송", "류", "홍", "전", "고", "문", "양", "손", "배", "백", "허", "유", "남", "심", "노" + ) + + return "${lastNames.random()}${if (Math.random() < 0.1) randomKoreanWords(1) else randomKoreanWords(2)}" +} + +fun randomKoreanWords(length: Int = 1): String { + val words = listOf( + "가", "나", "다", "라", "마", "바", "사", "아", "자", "차", + "카", "타", "파", "하", "강", "민", "서", "윤", "우", "진", + "현", "지", "은", "혜", "수", "영", "주", "원", "희", "경", + "선", "아", "나", "다", "라", "마", "바", "사", "아", "자", + "차", "카", "타", "파", "하", + ) + + return (1..length).joinToString("") { words.random() } +} diff --git a/service/src/test/kotlin/com/sangdol/roomescape/data/PopulationDataParser.kt b/service/src/test/kotlin/com/sangdol/roomescape/data/PopulationDataParser.kt index c1582e31..5ee16add 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/data/PopulationDataParser.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/data/PopulationDataParser.kt @@ -1,12 +1,10 @@ package com.sangdol.roomescape.data -import org.apache.poi.xssf.usermodel.XSSFWorkbook -import com.sangdol.roomescape.common.config.next import com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus +import com.sangdol.roomescape.supports.IDGenerator import com.sangdol.roomescape.supports.randomPhoneNumber -import com.sangdol.roomescape.supports.tsidFactory +import org.apache.poi.xssf.usermodel.XSSFWorkbook import java.io.File -import java.nio.file.Paths import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter @@ -146,7 +144,7 @@ class StoreDataInitializer { val createdAt = randomLocalDateTime() val updatedAt = createdAt - val id: Long = tsidFactory.next().also { storeIds.add(it) } + val id: Long = IDGenerator.create().also { storeIds.add(it) } val createdBy = creatableAdminIds.random() diff --git a/service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt index b90f7136..f1252bc3 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/DummyInitializer.kt @@ -1,7 +1,5 @@ package com.sangdol.roomescape.supports -import org.springframework.data.repository.findByIdOrNull -import com.sangdol.roomescape.common.config.next import com.sangdol.roomescape.payment.business.PaymentWriter import com.sangdol.roomescape.payment.infrastructure.client.CardDetail import com.sangdol.roomescape.payment.infrastructure.client.EasyPayDetail @@ -31,6 +29,7 @@ import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository import com.sangdol.roomescape.theme.web.ThemeCreateRequest import com.sangdol.roomescape.theme.web.toEntity import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity +import org.springframework.data.repository.findByIdOrNull import java.time.LocalDateTime class DummyInitializer( @@ -43,7 +42,7 @@ class DummyInitializer( ) { fun createStore( - id: Long = tsidFactory.next(), + id: Long = IDGenerator.create(), name: String = "행복${randomPhoneNumber()}호점", address: String = "강북구 행복로 $name", contact: String = randomPhoneNumber(), @@ -71,11 +70,11 @@ class DummyInitializer( fun createTheme( request: ThemeCreateRequest = ThemeFixture.createRequest ): ThemeEntity { - return request.toEntity(tsidFactory.next()).also { themeRepository.save(it) } + return request.toEntity(IDGenerator.create()).also { themeRepository.save(it) } } fun createSchedule( - storeId: Long = tsidFactory.next(), + storeId: Long = IDGenerator.create(), request: ScheduleCreateRequest = ScheduleFixture.createRequest, status: ScheduleStatus = ScheduleStatus.AVAILABLE ): ScheduleEntity { @@ -102,7 +101,7 @@ class DummyInitializer( fun createPendingReservation( user: UserEntity, - storeId: Long = tsidFactory.next(), + storeId: Long = IDGenerator.create(), themeRequest: ThemeCreateRequest = ThemeFixture.createRequest, scheduleRequest: ScheduleCreateRequest = ScheduleFixture.createRequest, reservationRequest: PendingReservationCreateRequest = ReservationFixture.pendingCreateRequest, @@ -129,14 +128,14 @@ class DummyInitializer( reserverContact = reservationRequest.reserverContact, participantCount = reservationRequest.participantCount, requirement = reservationRequest.requirement, - ).toEntity(id = tsidFactory.next(), userId = user.id) + ).toEntity(id = IDGenerator.create(), userId = user.id) return reservationRepository.save(reservation) } fun createConfirmReservation( user: UserEntity, - storeId: Long = tsidFactory.next(), + storeId: Long = IDGenerator.create(), themeRequest: ThemeCreateRequest = ThemeFixture.createRequest, scheduleRequest: ScheduleCreateRequest = ScheduleFixture.createRequest, reservationRequest: PendingReservationCreateRequest = ReservationFixture.pendingCreateRequest, diff --git a/service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt index 7389a643..bf59d52b 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/Fixtures.kt @@ -1,10 +1,10 @@ package com.sangdol.roomescape.supports import com.github.f4b6a3.tsid.TsidFactory +import com.sangdol.common.persistence.TsidIDGenerator import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -import com.sangdol.roomescape.common.config.next import com.sangdol.roomescape.payment.infrastructure.client.* import com.sangdol.roomescape.payment.infrastructure.common.* import com.sangdol.roomescape.payment.web.PaymentCancelRequest @@ -28,7 +28,7 @@ import java.time.LocalTime import java.time.OffsetDateTime const val INVALID_PK: Long = 9999L -val tsidFactory = TsidFactory(0) +val IDGenerator = TsidIDGenerator(TsidFactory(0)) object StoreFixture { val registerRequest = StoreRegisterRequest( @@ -40,7 +40,7 @@ object StoreFixture { ) fun create( - id: Long = tsidFactory.next(), + id: Long = IDGenerator.create(), name: String = "행복${randomPhoneNumber()}호점", address: String = "서울특별시 강북구 행복${randomPhoneNumber()}길", contact: String = randomPhoneNumber(), @@ -72,12 +72,12 @@ object AdminFixture { ) fun createStoreAdmin( - id: Long = tsidFactory.next(), + id: Long = IDGenerator.create(), account: String = randomString(), password: String = "adminPassword", name: String = "admin12345", phone: String = randomPhoneNumber(), - storeId: Long = tsidFactory.next(), + storeId: Long = IDGenerator.create(), permissionLevel: AdminPermissionLevel = AdminPermissionLevel.FULL_ACCESS ): AdminEntity { return create( @@ -93,7 +93,7 @@ object AdminFixture { } fun createHqAdmin( - id: Long = tsidFactory.next(), + id: Long = IDGenerator.create(), account: String = randomString(), password: String = "adminPassword", name: String = "admin12345", @@ -113,13 +113,13 @@ object AdminFixture { } fun create( - id: Long = tsidFactory.next(), + id: Long = IDGenerator.create(), account: String = randomString(), password: String = "adminPassword", name: String = "admin", phone: String = randomPhoneNumber(), type: AdminType = AdminType.STORE, - storeId: Long? = tsidFactory.next(), + storeId: Long? = IDGenerator.create(), permissionLevel: AdminPermissionLevel = AdminPermissionLevel.FULL_ACCESS ): AdminEntity { val storeId = if (type == AdminType.HQ) null else storeId @@ -144,7 +144,7 @@ object UserFixture { ) fun createUser( - id: Long = tsidFactory.next(), + id: Long = IDGenerator.create(), name: String = randomString(), email: String = randomEmail(), password: String = "a".repeat(MIN_PASSWORD_LENGTH), @@ -186,7 +186,7 @@ object ThemeFixture { ) fun create( - id: Long = tsidFactory.next(), + id: Long = IDGenerator.create(), name: String = randomString(), description: String = randomString(), thumbnailUrl: String = "http://www.bing.com/search?q=fugit", @@ -218,15 +218,15 @@ object ScheduleFixture { val createRequest: ScheduleCreateRequest = ScheduleCreateRequest( date = LocalDate.now().plusDays(1), time = LocalTime.now(), - themeId = tsidFactory.next() + themeId = IDGenerator.create() ) fun create( - id: Long = tsidFactory.next(), + id: Long = IDGenerator.create(), date: LocalDate = LocalDate.now().plusDays(1), time: LocalTime = LocalTime.now(), - storeId: Long = tsidFactory.next(), - themeId: Long = tsidFactory.next() + storeId: Long = IDGenerator.create(), + themeId: Long = IDGenerator.create() ): ScheduleEntity = ScheduleEntityFactory.create( id = id, date = date, @@ -245,7 +245,7 @@ object PaymentFixture { ) val cancelRequest: PaymentCancelRequest = PaymentCancelRequest( - reservationId = tsidFactory.next(), + reservationId = IDGenerator.create(), cancelReason = "cancelReason", ) @@ -322,7 +322,7 @@ object PaymentFixture { object ReservationFixture { val pendingCreateRequest: PendingReservationCreateRequest = PendingReservationCreateRequest( - scheduleId = tsidFactory.next(), + scheduleId = IDGenerator.create(), reserverName = "Wilbur Stuart", reserverContact = "wilbur@example.com", participantCount = 5, diff --git a/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt index 58d0446c..4146b1ed 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt @@ -1,11 +1,5 @@ package com.sangdol.roomescape.theme -import io.kotest.matchers.collections.shouldContainInOrder -import io.kotest.matchers.collections.shouldHaveSize -import org.hamcrest.CoreMatchers.equalTo -import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus -import com.sangdol.roomescape.common.config.next import com.sangdol.roomescape.common.util.DateUtils import com.sangdol.roomescape.supports.* import com.sangdol.roomescape.theme.exception.ThemeErrorCode @@ -14,6 +8,11 @@ import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository import com.sangdol.roomescape.theme.web.ThemeInfoResponse import com.sangdol.roomescape.theme.web.toEntity import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity +import io.kotest.matchers.collections.shouldContainInOrder +import io.kotest.matchers.collections.shouldHaveSize +import org.hamcrest.CoreMatchers.equalTo +import org.springframework.http.HttpMethod +import org.springframework.http.HttpStatus import java.time.LocalDate class ThemeApiTest( @@ -78,7 +77,7 @@ class ThemeApiTest( val user: UserEntity = testAuthUtil.defaultUser() val themeIds: List = (1..5).map { - themeRepository.save(ThemeFixture.createRequest.copy().toEntity(id = tsidFactory.next())).id + themeRepository.save(ThemeFixture.createRequest.copy().toEntity(id = IDGenerator.create())).id } val store = dummyInitializer.createStore() -- 2.47.2 From eada35f1ee361c23d1d180066b13831fd6256604 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:27:58 +0900 Subject: [PATCH 11/39] =?UTF-8?q?refactor:=20common.utils=EC=97=90=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=EB=90=9C=20MDC=20Util=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/build.gradle.kts | 1 + .../support/interceptors/AdminInterceptor.kt | 4 +-- .../support/interceptors/UserInterceptor.kt | 4 +-- .../common/log/ApiLogMessageConverter.kt | 6 ++--- .../common/log/HttpRequestLoggingFilter.kt | 4 +-- .../roomescape/common/util/MDCUtils.kt | 27 ------------------- .../persistence/ScheduleEntity.kt | 4 +-- 7 files changed, 12 insertions(+), 38 deletions(-) delete mode 100644 service/src/main/kotlin/com/sangdol/roomescape/common/util/MDCUtils.kt diff --git a/service/build.gradle.kts b/service/build.gradle.kts index 5033c801..3c0819bc 100644 --- a/service/build.gradle.kts +++ b/service/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { // submodules implementation(project(":common:config")) implementation(project(":common:persistence")) + implementation(project(":common:utils")) } tasks.jar { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/AdminInterceptor.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/AdminInterceptor.kt index 7a72a113..7de82394 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/AdminInterceptor.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/AdminInterceptor.kt @@ -17,7 +17,7 @@ import com.sangdol.roomescape.auth.exception.AuthException import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils import com.sangdol.roomescape.auth.web.support.AdminOnly import com.sangdol.roomescape.auth.web.support.accessToken -import com.sangdol.roomescape.common.util.MdcPrincipalId +import com.sangdol.common.utils.MdcPrincipalIdUtil private val log: KLogger = KotlinLogging.logger {} @@ -38,7 +38,7 @@ class AdminInterceptor( try { run { - val id: String = jwtUtils.extractSubject(token).also { MdcPrincipalId.set(it) } + val id: String = jwtUtils.extractSubject(token).also { MdcPrincipalIdUtil.set(it) } val type: AdminType = validateTypeAndGet(token, annotation.type) val permission: AdminPermissionLevel = validatePermissionAndGet(token, annotation.privilege) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/UserInterceptor.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/UserInterceptor.kt index aa581bc3..8be0d99c 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/UserInterceptor.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/support/interceptors/UserInterceptor.kt @@ -13,7 +13,7 @@ import com.sangdol.roomescape.auth.exception.AuthException import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils import com.sangdol.roomescape.auth.web.support.UserOnly import com.sangdol.roomescape.auth.web.support.accessToken -import com.sangdol.roomescape.common.util.MdcPrincipalId +import com.sangdol.common.utils.MdcPrincipalIdUtil private val log: KLogger = KotlinLogging.logger {} @@ -33,7 +33,7 @@ class UserInterceptor( val token: String? = request.accessToken() try { - val id: String = jwtUtils.extractSubject(token).also { MdcPrincipalId.set(it) } + val id: String = jwtUtils.extractSubject(token).also { MdcPrincipalIdUtil.set(it) } /** * CLAIM_ADMIN_TYPE_KEY 가 존재하면 관리자 토큰임 diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverter.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverter.kt index 6ee68b21..f9b7c87b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverter.kt @@ -2,7 +2,7 @@ package com.sangdol.roomescape.common.log import com.fasterxml.jackson.databind.ObjectMapper import jakarta.servlet.http.HttpServletRequest -import com.sangdol.roomescape.common.util.MdcPrincipalId +import com.sangdol.common.utils.MdcPrincipalIdUtil enum class LogType { INCOMING_HTTP_REQUEST, @@ -33,7 +33,7 @@ class ApiLogMessageConverter( controllerPayload: Map, ): String { val payload: MutableMap = commonRequestPayload(LogType.CONTROLLER_INVOKED, request) - val memberId: Long? = MdcPrincipalId.extractAsLongOrNull() + val memberId: Long? = MdcPrincipalIdUtil.extractAsLongOrNull() if (memberId != null) payload["principal_id"] = memberId else payload["principal_id"] = "NONE" payload.putAll(controllerPayload) @@ -47,7 +47,7 @@ class ApiLogMessageConverter( payload["endpoint"] = request.endpoint payload["status_code"] = request.httpStatus - MdcPrincipalId.extractAsLongOrNull() + MdcPrincipalIdUtil.extractAsLongOrNull() ?.let { payload["principal_id"] = it } ?: run { payload["principal_id"] = "NONE" } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/HttpRequestLoggingFilter.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/HttpRequestLoggingFilter.kt index 8fc8670a..bf72e30d 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/HttpRequestLoggingFilter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/HttpRequestLoggingFilter.kt @@ -9,7 +9,7 @@ import org.slf4j.MDC import org.springframework.web.filter.OncePerRequestFilter import org.springframework.web.util.ContentCachingRequestWrapper import org.springframework.web.util.ContentCachingResponseWrapper -import com.sangdol.roomescape.common.util.MdcPrincipalId +import com.sangdol.common.utils.MdcPrincipalIdUtil private val log: KLogger = KotlinLogging.logger {} @@ -34,7 +34,7 @@ class HttpRequestLoggingFilter( cachedResponse.copyBodyToResponse() } finally { MDC.remove("startTime") - MdcPrincipalId.clear() + MdcPrincipalIdUtil.clear() } } } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/util/MDCUtils.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/util/MDCUtils.kt deleted file mode 100644 index 8aa50781..00000000 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/util/MDCUtils.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.sangdol.roomescape.common.util - -import org.slf4j.MDC -import java.util.* - -private const val MDC_PRINCIPAL_ID_KEY = "principal_id" - -object MdcPrincipalId { - - fun extractAsLongOrNull(): Long? { - return MDC.get(MDC_PRINCIPAL_ID_KEY)?.toLong() - } - - fun extractAsOptionalLongOrEmpty(): Optional { - return MDC.get(MDC_PRINCIPAL_ID_KEY)?.let { - Optional.of(it.toLong()) - } ?: Optional.empty() - } - - fun set(id: String) { - MDC.put(MDC_PRINCIPAL_ID_KEY, id) - } - - fun clear() { - MDC.remove(MDC_PRINCIPAL_ID_KEY) - } -} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt index e4a29126..fde4b384 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/infrastructure/persistence/ScheduleEntity.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.schedule.infrastructure.persistence import com.sangdol.common.persistence.PersistableBaseEntity -import com.sangdol.roomescape.common.util.MdcPrincipalId +import com.sangdol.common.utils.MdcPrincipalIdUtil import jakarta.persistence.* import org.springframework.data.annotation.CreatedBy import org.springframework.data.annotation.CreatedDate @@ -53,7 +53,7 @@ class ScheduleEntity( } fun updateLastModifiedBy() { - MdcPrincipalId.extractAsLongOrNull()?.also { this.updatedBy = it } + MdcPrincipalIdUtil.extractAsLongOrNull()?.also { this.updatedBy = it } } } -- 2.47.2 From 5b3f2f929bf2a401510b4e6813379e6fc9bfc819 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:46:59 +0900 Subject: [PATCH 12/39] =?UTF-8?q?feat:=20common.types=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=A0=20=EC=8A=A4?= =?UTF-8?q?=ED=94=84=EB=A7=81=20=EB=8F=85=EB=A6=BD=20HttpStatus=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sangdol/common/types/web/HttpStatus.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt diff --git a/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt b/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt new file mode 100644 index 00000000..387cfed8 --- /dev/null +++ b/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt @@ -0,0 +1,20 @@ +package com.sangdol.common.types.web + +enum class HttpStatus( + val code: Int +) { + OK(200), + CREATED(201), + NO_CONTENT(204), + BAD_REQUEST(400), + UNAUTHORIZED(401), + FORBIDDEN(403), + NOT_FOUND(404), + CONFLICT(409), + INTERNAL_SERVER_ERROR(500) + ; + + fun value(): Int { + return code + } +} -- 2.47.2 From 00359f63d08202978565e79bce0abd83baf27884 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:47:41 +0900 Subject: [PATCH 13/39] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=ED=83=80=EC=9E=85=20=EA=B3=B5=ED=86=B5?= =?UTF-8?q?=20=EB=AA=A8=EB=93=88=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/sangdol/common/types}/exception/CommonErrorCode.kt | 4 ++-- .../kotlin/com/sangdol/common/types}/exception/ErrorCode.kt | 4 ++-- .../sangdol/common/types}/exception/RoomescapeException.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename {service/src/main/kotlin/com/sangdol/roomescape/common => common/types/src/main/kotlin/com/sangdol/common/types}/exception/CommonErrorCode.kt (85%) rename {service/src/main/kotlin/com/sangdol/roomescape/common => common/types/src/main/kotlin/com/sangdol/common/types}/exception/ErrorCode.kt (54%) rename {service/src/main/kotlin/com/sangdol/roomescape/common => common/types/src/main/kotlin/com/sangdol/common/types}/exception/RoomescapeException.kt (60%) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/CommonErrorCode.kt b/common/types/src/main/kotlin/com/sangdol/common/types/exception/CommonErrorCode.kt similarity index 85% rename from service/src/main/kotlin/com/sangdol/roomescape/common/exception/CommonErrorCode.kt rename to common/types/src/main/kotlin/com/sangdol/common/types/exception/CommonErrorCode.kt index 67f34d86..f84bf289 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/CommonErrorCode.kt +++ b/common/types/src/main/kotlin/com/sangdol/common/types/exception/CommonErrorCode.kt @@ -1,6 +1,6 @@ -package com.sangdol.roomescape.common.exception +package com.sangdol.common.types.exception -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus enum class CommonErrorCode( override val httpStatus: HttpStatus, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ErrorCode.kt b/common/types/src/main/kotlin/com/sangdol/common/types/exception/ErrorCode.kt similarity index 54% rename from service/src/main/kotlin/com/sangdol/roomescape/common/exception/ErrorCode.kt rename to common/types/src/main/kotlin/com/sangdol/common/types/exception/ErrorCode.kt index 7aa21c20..d48fbb17 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ErrorCode.kt +++ b/common/types/src/main/kotlin/com/sangdol/common/types/exception/ErrorCode.kt @@ -1,6 +1,6 @@ -package com.sangdol.roomescape.common.exception +package com.sangdol.common.types.exception -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus interface ErrorCode { val httpStatus: HttpStatus diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/RoomescapeException.kt b/common/types/src/main/kotlin/com/sangdol/common/types/exception/RoomescapeException.kt similarity index 60% rename from service/src/main/kotlin/com/sangdol/roomescape/common/exception/RoomescapeException.kt rename to common/types/src/main/kotlin/com/sangdol/common/types/exception/RoomescapeException.kt index aeb024e5..c35294fa 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/RoomescapeException.kt +++ b/common/types/src/main/kotlin/com/sangdol/common/types/exception/RoomescapeException.kt @@ -1,6 +1,6 @@ -package com.sangdol.roomescape.common.exception +package com.sangdol.common.types.exception open class RoomescapeException( open val errorCode: ErrorCode, override val message: String = errorCode.message -) : RuntimeException(message) +) : RuntimeException(message) \ No newline at end of file -- 2.47.2 From a7b3636410c32f0d551b2bc30d8ca1ec28f3532b Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:48:17 +0900 Subject: [PATCH 14/39] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=ED=83=80=EC=9E=85=20=EA=B3=B5=ED=86=B5?= =?UTF-8?q?=20=EB=AA=A8=EB=93=88=20=EC=9D=B4=EC=A0=84=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EA=B8=B0=EC=A1=B4=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EB=8D=95=EC=85=98=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/build.gradle.kts | 1 + .../admin/exception/AdminException.kt | 6 ++-- .../auth/exception/AuthErrorCode.kt | 4 +-- .../auth/exception/AuthException.kt | 2 +- .../common/dto/response/CommonApiResponse.kt | 2 +- .../exception/ExceptionControllerAdvice.kt | 29 ++++++++++--------- .../common/util/TransactionExecutionUtil.kt | 4 +-- .../payment/exception/PaymentErrorCode.kt | 4 +-- .../payment/exception/PaymentException.kt | 2 +- .../infrastructure/client/TosspayClient.kt | 4 +-- .../region/exception/RegionException.kt | 6 ++-- .../exception/ReservationErrorCode.kt | 4 +-- .../exception/ReservationException.kt | 4 +-- .../schedule/exception/ScheduleErrorCode.kt | 4 +-- .../schedule/exception/ScheduleException.kt | 4 +-- .../store/exception/StoreException.kt | 6 ++-- .../theme/exception/ThemeErrorCode.kt | 4 +-- .../theme/exception/ThemeException.kt | 2 +- .../user/exception/UserException.kt | 6 ++-- 19 files changed, 51 insertions(+), 47 deletions(-) diff --git a/service/build.gradle.kts b/service/build.gradle.kts index 3c0819bc..8cbf1766 100644 --- a/service/build.gradle.kts +++ b/service/build.gradle.kts @@ -56,6 +56,7 @@ dependencies { implementation(project(":common:config")) implementation(project(":common:persistence")) implementation(project(":common:utils")) + implementation(project(":common:types")) } tasks.jar { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/admin/exception/AdminException.kt b/service/src/main/kotlin/com/sangdol/roomescape/admin/exception/AdminException.kt index 5de62281..08ad5b8e 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/admin/exception/AdminException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/admin/exception/AdminException.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.admin.exception -import com.sangdol.roomescape.common.exception.ErrorCode -import com.sangdol.roomescape.common.exception.RoomescapeException -import org.springframework.http.HttpStatus +import com.sangdol.common.types.exception.ErrorCode +import com.sangdol.common.types.exception.RoomescapeException +import com.sangdol.common.types.web.HttpStatus class AdminException( override val errorCode: AdminErrorCode, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthErrorCode.kt index ee5f5f01..2da82e04 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthErrorCode.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.auth.exception -import org.springframework.http.HttpStatus -import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.common.types.web.HttpStatus +import com.sangdol.common.types.exception.ErrorCode enum class AuthErrorCode( override val httpStatus: HttpStatus, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthException.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthException.kt index 07c26ac6..4b233149 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/exception/AuthException.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.auth.exception -import com.sangdol.roomescape.common.exception.RoomescapeException +import com.sangdol.common.types.exception.RoomescapeException class AuthException( override val errorCode: AuthErrorCode, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt index e52f0804..524c23f6 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.common.dto.response import com.fasterxml.jackson.annotation.JsonInclude -import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.common.types.exception.ErrorCode @JsonInclude(JsonInclude.Include.NON_NULL) data class CommonApiResponse( diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt index aff98040..a327c766 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt @@ -1,21 +1,24 @@ package com.sangdol.roomescape.common.exception -import io.github.oshai.kotlinlogging.KLogger -import io.github.oshai.kotlinlogging.KotlinLogging -import jakarta.servlet.http.HttpServletRequest -import org.slf4j.MDC -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.http.converter.HttpMessageNotReadableException -import org.springframework.web.bind.MethodArgumentNotValidException -import org.springframework.web.bind.annotation.ExceptionHandler -import org.springframework.web.bind.annotation.RestControllerAdvice +import com.sangdol.common.types.exception.CommonErrorCode +import com.sangdol.common.types.exception.ErrorCode +import com.sangdol.common.types.exception.RoomescapeException +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.auth.exception.AuthException import com.sangdol.roomescape.common.dto.response.CommonErrorResponse import com.sangdol.roomescape.common.log.ApiLogMessageConverter import com.sangdol.roomescape.common.log.ConvertResponseMessageRequest import com.sangdol.roomescape.common.log.LogType import com.sangdol.roomescape.common.log.getEndpoint +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import jakarta.servlet.http.HttpServletRequest +import org.slf4j.MDC +import org.springframework.http.ResponseEntity +import org.springframework.http.converter.HttpMessageNotReadableException +import org.springframework.web.bind.MethodArgumentNotValidException +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.bind.annotation.RestControllerAdvice private val log: KLogger = KotlinLogging.logger {} @@ -42,7 +45,7 @@ class ExceptionControllerAdvice( ) return ResponseEntity - .status(httpStatus) + .status(httpStatus.value()) .body(errorResponse) } @@ -73,7 +76,7 @@ class ExceptionControllerAdvice( ) return ResponseEntity - .status(httpStatus) + .status(httpStatus.value()) .body(errorResponse) } @@ -97,7 +100,7 @@ class ExceptionControllerAdvice( ) return ResponseEntity - .status(httpStatus) + .status(httpStatus.value()) .body(errorResponse) } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/util/TransactionExecutionUtil.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/util/TransactionExecutionUtil.kt index d4428190..fb05ec7b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/util/TransactionExecutionUtil.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/util/TransactionExecutionUtil.kt @@ -6,8 +6,8 @@ import org.springframework.stereotype.Component import org.springframework.transaction.PlatformTransactionManager import org.springframework.transaction.TransactionDefinition import org.springframework.transaction.support.TransactionTemplate -import com.sangdol.roomescape.common.exception.CommonErrorCode -import com.sangdol.roomescape.common.exception.RoomescapeException +import com.sangdol.common.types.exception.CommonErrorCode +import com.sangdol.common.types.exception.RoomescapeException private val log: KLogger = KotlinLogging.logger {} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentErrorCode.kt index 2c2676e1..9d2ea09a 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentErrorCode.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.payment.exception -import org.springframework.http.HttpStatus -import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.common.types.web.HttpStatus +import com.sangdol.common.types.exception.ErrorCode enum class PaymentErrorCode( override val httpStatus: HttpStatus, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentException.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentException.kt index 7bb10717..3bddd08c 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/exception/PaymentException.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.payment.exception -import com.sangdol.roomescape.common.exception.RoomescapeException +import com.sangdol.common.types.exception.RoomescapeException class PaymentException( override val errorCode: PaymentErrorCode, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayClient.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayClient.kt index 53c56a59..49194b30 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayClient.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/infrastructure/client/TosspayClient.kt @@ -1,6 +1,8 @@ package com.sangdol.roomescape.payment.infrastructure.client import com.fasterxml.jackson.databind.ObjectMapper +import com.sangdol.roomescape.payment.exception.PaymentErrorCode +import com.sangdol.roomescape.payment.exception.PaymentException import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.http.HttpMethod @@ -10,8 +12,6 @@ import org.springframework.http.client.ClientHttpResponse import org.springframework.stereotype.Component import org.springframework.web.client.ResponseErrorHandler import org.springframework.web.client.RestClient -import com.sangdol.roomescape.payment.exception.PaymentErrorCode -import com.sangdol.roomescape.payment.exception.PaymentException import java.net.URI private val log: KLogger = KotlinLogging.logger {} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/region/exception/RegionException.kt b/service/src/main/kotlin/com/sangdol/roomescape/region/exception/RegionException.kt index 41168e50..b7201c9b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/region/exception/RegionException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/region/exception/RegionException.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.region.exception -import org.springframework.http.HttpStatus -import com.sangdol.roomescape.common.exception.ErrorCode -import com.sangdol.roomescape.common.exception.RoomescapeException +import com.sangdol.common.types.web.HttpStatus +import com.sangdol.common.types.exception.ErrorCode +import com.sangdol.common.types.exception.RoomescapeException class RegionException( override val errorCode: RegionErrorCode, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationErrorCode.kt index 9b502a46..a91594e7 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationErrorCode.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.reservation.exception -import org.springframework.http.HttpStatus -import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.common.types.web.HttpStatus +import com.sangdol.common.types.exception.ErrorCode enum class ReservationErrorCode( override val httpStatus: HttpStatus, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationException.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationException.kt index 3a25e7d2..f3cb36ab 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/exception/ReservationException.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.reservation.exception -import com.sangdol.roomescape.common.exception.ErrorCode -import com.sangdol.roomescape.common.exception.RoomescapeException +import com.sangdol.common.types.exception.ErrorCode +import com.sangdol.common.types.exception.RoomescapeException class ReservationException( override val errorCode: ErrorCode, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleErrorCode.kt index 264055d8..0194f2fa 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleErrorCode.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.schedule.exception -import org.springframework.http.HttpStatus -import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.common.types.web.HttpStatus +import com.sangdol.common.types.exception.ErrorCode enum class ScheduleErrorCode( override val httpStatus: HttpStatus, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleException.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleException.kt index a79df67c..6133383a 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/exception/ScheduleException.kt @@ -1,5 +1,5 @@ -import com.sangdol.roomescape.common.exception.ErrorCode -import com.sangdol.roomescape.common.exception.RoomescapeException +import com.sangdol.common.types.exception.ErrorCode +import com.sangdol.common.types.exception.RoomescapeException class ScheduleException( override val errorCode: ErrorCode, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/store/exception/StoreException.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/exception/StoreException.kt index 6531d154..fad3d383 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/store/exception/StoreException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/exception/StoreException.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.store.exception -import org.springframework.http.HttpStatus -import com.sangdol.roomescape.common.exception.ErrorCode -import com.sangdol.roomescape.common.exception.RoomescapeException +import com.sangdol.common.types.web.HttpStatus +import com.sangdol.common.types.exception.ErrorCode +import com.sangdol.common.types.exception.RoomescapeException class StoreException( override val errorCode: StoreErrorCode, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeErrorCode.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeErrorCode.kt index 9210b958..03673549 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeErrorCode.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeErrorCode.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.theme.exception -import org.springframework.http.HttpStatus -import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.common.types.web.HttpStatus +import com.sangdol.common.types.exception.ErrorCode enum class ThemeErrorCode( override val httpStatus: HttpStatus, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeException.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeException.kt index 286cda16..594fc212 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/exception/ThemeException.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.theme.exception -import com.sangdol.roomescape.common.exception.RoomescapeException +import com.sangdol.common.types.exception.RoomescapeException class ThemeException( override val errorCode: ThemeErrorCode, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/exception/UserException.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/exception/UserException.kt index 4561b724..05f4963a 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/exception/UserException.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/exception/UserException.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.user.exception -import org.springframework.http.HttpStatus -import com.sangdol.roomescape.common.exception.ErrorCode -import com.sangdol.roomescape.common.exception.RoomescapeException +import com.sangdol.common.types.web.HttpStatus +import com.sangdol.common.types.exception.ErrorCode +import com.sangdol.common.types.exception.RoomescapeException class UserException( override val errorCode: UserErrorCode, -- 2.47.2 From 715a0f979a08a5425f6470f78993fa314a3eb882 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 20:48:28 +0900 Subject: [PATCH 15/39] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=ED=83=80=EC=9E=85=20=EA=B3=B5=ED=86=B5?= =?UTF-8?q?=20=EB=AA=A8=EB=93=88=20=EC=9D=B4=EC=A0=84=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EA=B8=B0=EC=A1=B4=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt | 2 +- .../com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt | 2 +- .../kotlin/com/sangdol/roomescape/payment/PaymentAPITest.kt | 2 +- .../com/sangdol/roomescape/payment/TosspayClientTest.kt | 2 +- .../com/sangdol/roomescape/region/RegionApiSuccessTest.kt | 2 +- .../com/sangdol/roomescape/reservation/ReservationApiTest.kt | 4 ++-- .../com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt | 2 +- .../kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt | 2 +- .../kotlin/com/sangdol/roomescape/store/AdminStoreApiTest.kt | 2 +- .../test/kotlin/com/sangdol/roomescape/store/StoreApiTest.kt | 2 +- .../com/sangdol/roomescape/supports/RestAssuredUtils.kt | 2 +- .../kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt | 2 +- .../kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt | 2 +- .../test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt | 2 +- .../test/kotlin/com/sangdol/roomescape/user/UserApiTest.kt | 4 ++-- 15 files changed, 17 insertions(+), 17 deletions(-) diff --git a/service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt index bface8e8..3711e2c0 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt @@ -8,7 +8,7 @@ import io.kotest.matchers.shouldNotBe import io.mockk.every import io.restassured.response.ValidatableResponse import org.hamcrest.CoreMatchers.equalTo -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.admin.exception.AdminErrorCode import com.sangdol.roomescape.auth.business.CLAIM_ADMIN_TYPE_KEY import com.sangdol.roomescape.auth.business.CLAIM_PERMISSION_KEY diff --git a/service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt index 92013b88..35bc31aa 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt @@ -3,7 +3,7 @@ package com.sangdol.roomescape.auth import com.ninjasquad.springmockk.MockkBean import io.mockk.clearMocks import io.mockk.every -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryRepository import com.sangdol.roomescape.auth.web.LoginRequest import com.sangdol.roomescape.common.dto.PrincipalType diff --git a/service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentAPITest.kt b/service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentAPITest.kt index f3fcccd3..fc5cc0d5 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentAPITest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentAPITest.kt @@ -5,7 +5,7 @@ import io.kotest.matchers.shouldBe import io.mockk.every import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.auth.exception.AuthErrorCode import com.sangdol.roomescape.payment.business.PaymentService import com.sangdol.roomescape.payment.exception.PaymentErrorCode diff --git a/service/src/test/kotlin/com/sangdol/roomescape/payment/TosspayClientTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/payment/TosspayClientTest.kt index ece494e3..1373d22a 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/payment/TosspayClientTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/payment/TosspayClientTest.kt @@ -9,7 +9,6 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.client.RestClientTest import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.test.web.client.MockRestServiceServer import org.springframework.test.web.client.ResponseActions @@ -22,6 +21,7 @@ import com.sangdol.roomescape.payment.infrastructure.client.PaymentClientCancelR import com.sangdol.roomescape.payment.infrastructure.client.PaymentClientConfirmResponse import com.sangdol.roomescape.payment.infrastructure.client.TosspayClient import com.sangdol.roomescape.payment.infrastructure.common.PaymentStatus +import org.springframework.http.HttpStatus @RestClientTest(TosspayClient::class) @MockkBean(JpaMetamodelMappingContext::class) diff --git a/service/src/test/kotlin/com/sangdol/roomescape/region/RegionApiSuccessTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/region/RegionApiSuccessTest.kt index 2875854b..cbb3ff83 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/region/RegionApiSuccessTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/region/RegionApiSuccessTest.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.region import io.kotest.matchers.shouldBe -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.supports.FunSpecSpringbootTest import com.sangdol.roomescape.supports.runTest diff --git a/service/src/test/kotlin/com/sangdol/roomescape/reservation/ReservationApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/reservation/ReservationApiTest.kt index 52fde089..491365ae 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/reservation/ReservationApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/reservation/ReservationApiTest.kt @@ -5,9 +5,9 @@ import io.kotest.matchers.shouldNotBe import org.hamcrest.CoreMatchers.equalTo import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.auth.exception.AuthErrorCode -import com.sangdol.roomescape.common.exception.CommonErrorCode +import com.sangdol.common.types.exception.CommonErrorCode import com.sangdol.roomescape.payment.infrastructure.common.BankCode import com.sangdol.roomescape.payment.infrastructure.common.CardIssuerCode import com.sangdol.roomescape.payment.infrastructure.common.EasyPayCompanyCode diff --git a/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt index b16abad5..e51c3ca3 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt @@ -7,7 +7,7 @@ import io.kotest.matchers.shouldNotBe import org.hamcrest.CoreMatchers.equalTo import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType import com.sangdol.roomescape.auth.exception.AuthErrorCode diff --git a/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt index a4bd3b84..1f8103c4 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/schedule/ScheduleApiTest.kt @@ -4,7 +4,7 @@ import io.kotest.matchers.shouldBe import org.hamcrest.CoreMatchers.equalTo import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType import com.sangdol.roomescape.auth.exception.AuthErrorCode diff --git a/service/src/test/kotlin/com/sangdol/roomescape/store/AdminStoreApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/store/AdminStoreApiTest.kt index e8e85eec..8e74c6a4 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/store/AdminStoreApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/store/AdminStoreApiTest.kt @@ -5,7 +5,7 @@ import io.kotest.matchers.date.shouldBeAfter import io.kotest.matchers.shouldBe import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType diff --git a/service/src/test/kotlin/com/sangdol/roomescape/store/StoreApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/store/StoreApiTest.kt index f6580e6a..d8af0c5f 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/store/StoreApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/store/StoreApiTest.kt @@ -2,7 +2,7 @@ package com.sangdol.roomescape.store import org.hamcrest.CoreMatchers.equalTo import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.store.exception.StoreErrorCode import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity import com.sangdol.roomescape.supports.* diff --git a/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt index d2515a70..9e7fc01e 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.supports import com.sangdol.common.config.JacksonConfig -import com.sangdol.roomescape.common.exception.ErrorCode +import com.sangdol.common.types.exception.ErrorCode import io.restassured.module.kotlin.extensions.Given import io.restassured.module.kotlin.extensions.Then import io.restassured.module.kotlin.extensions.When diff --git a/service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt index b5a68bec..500354e9 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt @@ -5,7 +5,7 @@ import io.restassured.module.kotlin.extensions.Given import io.restassured.module.kotlin.extensions.Then import io.restassured.module.kotlin.extensions.When import org.springframework.data.repository.findByIdOrNull -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import org.springframework.http.MediaType import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity import com.sangdol.roomescape.admin.infrastructure.persistence.AdminRepository diff --git a/service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt index 72c10cd4..de15aa00 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/theme/AdminThemeApiTest.kt @@ -5,7 +5,7 @@ import io.kotest.matchers.shouldBe import org.hamcrest.CoreMatchers.equalTo import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType import com.sangdol.roomescape.auth.exception.AuthErrorCode diff --git a/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt index 4146b1ed..4f56757f 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt @@ -12,7 +12,7 @@ import io.kotest.matchers.collections.shouldContainInOrder import io.kotest.matchers.collections.shouldHaveSize import org.hamcrest.CoreMatchers.equalTo import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import java.time.LocalDate class ThemeApiTest( diff --git a/service/src/test/kotlin/com/sangdol/roomescape/user/UserApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/user/UserApiTest.kt index cfe2703d..db98d5d3 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/user/UserApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/user/UserApiTest.kt @@ -11,11 +11,11 @@ import io.restassured.module.kotlin.extensions.When import org.hamcrest.CoreMatchers.equalTo import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpMethod -import org.springframework.http.HttpStatus +import com.sangdol.common.types.web.HttpStatus import org.springframework.http.MediaType import com.sangdol.roomescape.auth.exception.AuthErrorCode import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils -import com.sangdol.roomescape.common.exception.CommonErrorCode +import com.sangdol.common.types.exception.CommonErrorCode import com.sangdol.roomescape.supports.FunSpecSpringbootTest import com.sangdol.roomescape.supports.UserFixture import com.sangdol.roomescape.supports.runExceptionTest -- 2.47.2 From 288b67518ed33d2dc561c17959cce8dd52f47516 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 21:04:39 +0900 Subject: [PATCH 16/39] =?UTF-8?q?refactor:=20CommonApiResponse=20=EB=B0=8F?= =?UTF-8?q?=20Audit=20=ED=83=80=EC=9E=85=20=EB=AA=A8=EB=93=88=20=EC=9D=B4?= =?UTF-8?q?=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/types/audit/AuditingInfo.kt | 19 ++++++++++++++++ .../sangdol/common/types/web/ApiResponses.kt | 4 +--- .../sangdol/roomescape/common/dto/AuditDto.kt | 22 ------------------- 3 files changed, 20 insertions(+), 25 deletions(-) create mode 100644 common/types/src/main/kotlin/com/sangdol/common/types/audit/AuditingInfo.kt rename service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt => common/types/src/main/kotlin/com/sangdol/common/types/web/ApiResponses.kt (70%) delete mode 100644 service/src/main/kotlin/com/sangdol/roomescape/common/dto/AuditDto.kt diff --git a/common/types/src/main/kotlin/com/sangdol/common/types/audit/AuditingInfo.kt b/common/types/src/main/kotlin/com/sangdol/common/types/audit/AuditingInfo.kt new file mode 100644 index 00000000..88ea23d1 --- /dev/null +++ b/common/types/src/main/kotlin/com/sangdol/common/types/audit/AuditingInfo.kt @@ -0,0 +1,19 @@ +package com.sangdol.common.types.audit + +import java.time.LocalDateTime + +data class Auditor( + val id: Long, + val name: String, +) { + companion object { + val UNKNOWN = Auditor(0, "Unknown") + } +} + +data class AuditingInfo( + val createdAt: LocalDateTime, + val createdBy: Auditor, + val updatedAt: LocalDateTime, + val updatedBy: Auditor, +) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt b/common/types/src/main/kotlin/com/sangdol/common/types/web/ApiResponses.kt similarity index 70% rename from service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt rename to common/types/src/main/kotlin/com/sangdol/common/types/web/ApiResponses.kt index 524c23f6..c31f1808 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/dto/response/CommonApiResponse.kt +++ b/common/types/src/main/kotlin/com/sangdol/common/types/web/ApiResponses.kt @@ -1,9 +1,7 @@ -package com.sangdol.roomescape.common.dto.response +package com.sangdol.common.types.web -import com.fasterxml.jackson.annotation.JsonInclude import com.sangdol.common.types.exception.ErrorCode -@JsonInclude(JsonInclude.Include.NON_NULL) data class CommonApiResponse( val data: T? = null, ) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/dto/AuditDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/AuditDto.kt deleted file mode 100644 index e52fdbe3..00000000 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/dto/AuditDto.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.sangdol.roomescape.common.dto - -import java.time.LocalDateTime - -object AuditConstant { - val UNKNOWN_OPERATOR = OperatorInfo( - id = 0, - name = "unknown" - ) -} - -data class OperatorInfo( - val id: Long, - val name: String, -) - -data class AuditInfo( - val createdAt: LocalDateTime, - val createdBy: OperatorInfo, - val updatedAt: LocalDateTime, - val updatedBy: OperatorInfo, -) -- 2.47.2 From 7c52460ac65267a9afcd4e061ecf8085a5b2d887 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 21:04:56 +0900 Subject: [PATCH 17/39] =?UTF-8?q?refactor:=20CommonApiResponse=20=EB=B0=8F?= =?UTF-8?q?=20Audit=20=ED=83=80=EC=9E=85=20=EB=AA=A8=EB=93=88=20=EC=9D=B4?= =?UTF-8?q?=EC=A0=84=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EA=B8=B0?= =?UTF-8?q?=EC=A1=B4=20=EC=BD=94=EB=93=9C=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/admin/business/AdminService.kt | 19 +- .../sangdol/roomescape/auth/docs/AuthAPI.kt | 12 +- .../roomescape/auth/web/AuthController.kt | 10 +- .../roomescape/common/dto/CommonAuth.kt | 1 - .../exception/ExceptionControllerAdvice.kt | 2 +- .../roomescape/payment/docs/PaymentAPI.kt | 14 +- .../payment/web/PaymentController.kt | 10 +- .../roomescape/region/docs/RegionAPI.kt | 10 +- .../roomescape/region/web/RegionController.kt | 6 +- .../reservation/docs/ReservationAPI.kt | 10 +- .../reservation/web/ReservationController.kt | 10 +- .../schedule/business/ScheduleService.kt | 12 +- .../roomescape/schedule/docs/ScheduleAPI.kt | 18 +- .../schedule/web/AdminScheduleController.kt | 10 +- .../schedule/web/ScheduleController.kt | 8 +- .../roomescape/store/business/StoreService.kt | 6 +- .../sangdol/roomescape/store/docs/StoreAPI.kt | 12 +- .../store/web/AdminStoreController.kt | 6 +- .../roomescape/store/web/AdminStoreDto.kt | 6 +- .../roomescape/store/web/StoreController.kt | 6 +- .../roomescape/theme/business/ThemeService.kt | 4 +- .../sangdol/roomescape/theme/docs/ThemeApi.kt | 12 +- .../theme/web/AdminThemeController.kt | 6 +- .../roomescape/theme/web/AdminThemeDto.kt | 6 +- .../roomescape/theme/web/ThemeController.kt | 6 +- .../sangdol/roomescape/user/docs/UserAPI.kt | 14 +- .../roomescape/user/web/UserController.kt | 10 +- .../roomescape/data/DefaultDataInitializer.kt | 1820 ++++++++--------- .../schedule/AdminScheduleApiTest.kt | 19 +- 29 files changed, 1041 insertions(+), 1044 deletions(-) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt b/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt index 6e07188e..4149bc1e 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt @@ -1,17 +1,16 @@ package com.sangdol.roomescape.admin.business +import com.sangdol.common.types.audit.Auditor +import com.sangdol.roomescape.admin.exception.AdminErrorCode +import com.sangdol.roomescape.admin.exception.AdminException +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminRepository +import com.sangdol.roomescape.common.dto.AdminLoginCredentials +import com.sangdol.roomescape.common.dto.toCredentials import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import com.sangdol.roomescape.admin.exception.AdminErrorCode -import com.sangdol.roomescape.admin.exception.AdminException -import com.sangdol.roomescape.admin.infrastructure.persistence.AdminRepository -import com.sangdol.roomescape.common.dto.AdminLoginCredentials -import com.sangdol.roomescape.common.dto.AuditConstant -import com.sangdol.roomescape.common.dto.OperatorInfo -import com.sangdol.roomescape.common.dto.toCredentials private val log: KLogger = KotlinLogging.logger {} @@ -35,16 +34,16 @@ class AdminService( } @Transactional(readOnly = true) - fun findOperatorOrUnknown(id: Long): OperatorInfo { + fun findOperatorOrUnknown(id: Long): Auditor { log.info { "[AdminService.findOperatorById] 작업자 정보 조회 시작: id=${id}" } return adminRepository.findByIdOrNull(id)?.let { admin -> - OperatorInfo(admin.id, admin.name).also { + Auditor(admin.id, admin.name).also { log.info { "[AdminService.findOperatorById] 작업자 정보 조회 완료: id=${admin.id}, name=${admin.name}" } } } ?: run { log.warn { "[AdminService.findOperatorById] 작업자 정보 조회 실패. id=${id}" } - AuditConstant.UNKNOWN_OPERATOR + Auditor.UNKNOWN } } } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt index 593f7397..53115811 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt @@ -1,5 +1,11 @@ package com.sangdol.roomescape.auth.docs +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.auth.web.LoginRequest +import com.sangdol.roomescape.auth.web.LoginSuccessResponse +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses @@ -8,12 +14,6 @@ import jakarta.servlet.http.HttpServletResponse import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RequestBody -import com.sangdol.roomescape.auth.web.LoginRequest -import com.sangdol.roomescape.auth.web.LoginSuccessResponse -import com.sangdol.roomescape.auth.web.support.Public -import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext -import com.sangdol.roomescape.common.dto.response.CommonApiResponse interface AuthAPI { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt index d3325d2b..1d0595c7 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt @@ -1,16 +1,16 @@ package com.sangdol.roomescape.auth.web +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.auth.business.AuthService +import com.sangdol.roomescape.auth.docs.AuthAPI +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import com.sangdol.roomescape.auth.business.AuthService -import com.sangdol.roomescape.auth.docs.AuthAPI -import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext -import com.sangdol.roomescape.common.dto.response.CommonApiResponse @RestController @RequestMapping("/auth") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/dto/CommonAuth.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/CommonAuth.kt index e6ff00cf..5bf1053b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/dto/CommonAuth.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/dto/CommonAuth.kt @@ -8,7 +8,6 @@ import com.sangdol.roomescape.auth.web.LoginSuccessResponse import com.sangdol.roomescape.auth.web.UserLoginSuccessResponse import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity - abstract class LoginCredentials { abstract val id: Long abstract val password: String diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt index a327c766..146e74cf 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt @@ -3,9 +3,9 @@ package com.sangdol.roomescape.common.exception import com.sangdol.common.types.exception.CommonErrorCode import com.sangdol.common.types.exception.ErrorCode import com.sangdol.common.types.exception.RoomescapeException +import com.sangdol.common.types.web.CommonErrorResponse import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.auth.exception.AuthException -import com.sangdol.roomescape.common.dto.response.CommonErrorResponse import com.sangdol.roomescape.common.log.ApiLogMessageConverter import com.sangdol.roomescape.common.log.ConvertResponseMessageRequest import com.sangdol.roomescape.common.log.LogType diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt index 1e566ee1..da6f6770 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt @@ -1,5 +1,12 @@ package com.sangdol.roomescape.payment.docs +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.auth.web.support.UserOnly +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.payment.web.PaymentCancelRequest +import com.sangdol.roomescape.payment.web.PaymentConfirmRequest +import com.sangdol.roomescape.payment.web.PaymentCreateResponse import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses @@ -7,13 +14,6 @@ import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestParam -import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.auth.web.support.UserOnly -import com.sangdol.roomescape.common.dto.CurrentUserContext -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.payment.web.PaymentCancelRequest -import com.sangdol.roomescape.payment.web.PaymentConfirmRequest -import com.sangdol.roomescape.payment.web.PaymentCreateResponse interface PaymentAPI { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt index 406c872a..1308fe1b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt @@ -1,13 +1,13 @@ package com.sangdol.roomescape.payment.web +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.payment.business.PaymentService +import com.sangdol.roomescape.payment.docs.PaymentAPI import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.payment.business.PaymentService -import com.sangdol.roomescape.payment.docs.PaymentAPI @RestController @RequestMapping("/payments") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/region/docs/RegionAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/region/docs/RegionAPI.kt index ce5a2cd9..16fef1ce 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/region/docs/RegionAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/region/docs/RegionAPI.kt @@ -1,15 +1,15 @@ package com.sangdol.roomescape.region.docs +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.region.web.RegionCodeResponse +import com.sangdol.roomescape.region.web.SidoListResponse +import com.sangdol.roomescape.region.web.SigunguListResponse import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RequestParam -import com.sangdol.roomescape.auth.web.support.Public -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.region.web.RegionCodeResponse -import com.sangdol.roomescape.region.web.SidoListResponse -import com.sangdol.roomescape.region.web.SigunguListResponse interface RegionAPI { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/region/web/RegionController.kt b/service/src/main/kotlin/com/sangdol/roomescape/region/web/RegionController.kt index d8c6b1e2..26caa3b5 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/region/web/RegionController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/region/web/RegionController.kt @@ -1,13 +1,13 @@ package com.sangdol.roomescape.region.web +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.region.business.RegionService +import com.sangdol.roomescape.region.docs.RegionAPI import org.springframework.http.ResponseEntity 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 -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.region.business.RegionService -import com.sangdol.roomescape.region.docs.RegionAPI @RestController @RequestMapping("/regions") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt index 104559dd..d3a03612 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt @@ -1,5 +1,10 @@ package com.sangdol.roomescape.reservation.docs +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.auth.web.support.UserOnly +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.reservation.web.* import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses @@ -7,11 +12,6 @@ import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody -import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.auth.web.support.UserOnly -import com.sangdol.roomescape.common.dto.CurrentUserContext -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.reservation.web.* interface ReservationAPI { @Operation(summary = "결제 전 임시 예약 저장") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt index cc3c5642..4e5a3239 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt @@ -1,13 +1,13 @@ package com.sangdol.roomescape.reservation.web +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.reservation.business.ReservationService +import com.sangdol.roomescape.reservation.docs.ReservationAPI import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.reservation.business.ReservationService -import com.sangdol.roomescape.reservation.docs.ReservationAPI @RestController @RequestMapping("/reservations") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt index 6dd9e231..f9fb6e99 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt @@ -2,9 +2,9 @@ package com.sangdol.roomescape.schedule.business import ScheduleException import com.sangdol.common.persistence.IDGenerator +import com.sangdol.common.types.audit.AuditingInfo +import com.sangdol.common.types.audit.Auditor import com.sangdol.roomescape.admin.business.AdminService -import com.sangdol.roomescape.common.dto.AuditInfo -import com.sangdol.roomescape.common.dto.OperatorInfo import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview import com.sangdol.roomescape.schedule.exception.ScheduleErrorCode import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity @@ -89,15 +89,15 @@ class ScheduleService( } @Transactional(readOnly = true) - fun findScheduleAudit(id: Long): AuditInfo { + fun findScheduleAudit(id: Long): AuditingInfo { log.info { "[ScheduleService.findDetail] 일정 감사 정보 조회 시작: id=$id" } val schedule: ScheduleEntity = findOrThrow(id) - val createdBy: OperatorInfo = adminService.findOperatorOrUnknown(schedule.createdBy) - val updatedBy: OperatorInfo = adminService.findOperatorOrUnknown(schedule.updatedBy) + val createdBy: Auditor = adminService.findOperatorOrUnknown(schedule.createdBy) + val updatedBy: Auditor = adminService.findOperatorOrUnknown(schedule.updatedBy) - return AuditInfo(schedule.createdAt, createdBy, schedule.updatedAt, updatedBy) + return AuditingInfo(schedule.createdAt, createdBy, schedule.updatedAt, updatedBy) .also { log.info { "[ScheduleService.findDetail] 일정 감사 정보 조회 완료: id=$id" } } } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt index 8858007b..be8ca4c5 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt @@ -1,5 +1,13 @@ package com.sangdol.roomescape.schedule.docs +import com.sangdol.common.types.audit.AuditingInfo +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege +import com.sangdol.roomescape.auth.web.support.AdminOnly +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.auth.web.support.UserOnly +import com.sangdol.roomescape.schedule.web.* import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses @@ -9,14 +17,6 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestParam -import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege -import com.sangdol.roomescape.auth.web.support.AdminOnly -import com.sangdol.roomescape.auth.web.support.Public -import com.sangdol.roomescape.auth.web.support.UserOnly -import com.sangdol.roomescape.common.dto.AuditInfo -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.schedule.web.* import java.time.LocalDate interface AdminScheduleAPI { @@ -35,7 +35,7 @@ interface AdminScheduleAPI { @ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true)) fun findScheduleAudit( @PathVariable("id") id: Long - ): ResponseEntity> + ): ResponseEntity> @AdminOnly(type = AdminType.STORE, privilege = Privilege.CREATE) @Operation(summary = "일정 생성") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt index 1d9a96e4..14ed61b8 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt @@ -1,13 +1,13 @@ package com.sangdol.roomescape.schedule.web +import com.sangdol.common.types.audit.AuditingInfo +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.schedule.business.ScheduleService +import com.sangdol.roomescape.schedule.docs.AdminScheduleAPI import jakarta.validation.Valid import org.springframework.format.annotation.DateTimeFormat import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import com.sangdol.roomescape.common.dto.AuditInfo -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.schedule.business.ScheduleService -import com.sangdol.roomescape.schedule.docs.AdminScheduleAPI import java.time.LocalDate @RestController @@ -29,7 +29,7 @@ class AdminScheduleController( @GetMapping("/schedules/{id}/audits") override fun findScheduleAudit( @PathVariable("id") id: Long - ): ResponseEntity> { + ): ResponseEntity> { val response = scheduleService.findScheduleAudit(id) return ResponseEntity.ok(CommonApiResponse(response)) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/ScheduleController.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/ScheduleController.kt index 7f558d95..1c5d8c2b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/ScheduleController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/ScheduleController.kt @@ -1,12 +1,12 @@ package com.sangdol.roomescape.schedule.web -import org.springframework.format.annotation.DateTimeFormat -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.* -import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.schedule.business.ScheduleService import com.sangdol.roomescape.schedule.docs.PublicScheduleAPI import com.sangdol.roomescape.schedule.docs.UserScheduleAPI +import org.springframework.format.annotation.DateTimeFormat +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* import java.time.LocalDate @RestController diff --git a/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt index 2afb8e1f..ba829e84 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.store.business import com.sangdol.common.persistence.IDGenerator +import com.sangdol.common.types.audit.AuditingInfo import com.sangdol.roomescape.admin.business.AdminService -import com.sangdol.roomescape.common.dto.AuditInfo import com.sangdol.roomescape.region.business.RegionService import com.sangdol.roomescape.store.exception.StoreErrorCode import com.sangdol.roomescape.store.exception.StoreException @@ -108,12 +108,12 @@ class StoreService( .also { log.info { "[StoreService.findStoreInfo] 매장 정보 조회 완료: id=${id}" } } } - private fun getAuditInfo(store: StoreEntity): AuditInfo { + private fun getAuditInfo(store: StoreEntity): AuditingInfo { log.info { "[StoreService.getAuditInfo] 감사 정보 조회 시작: storeId=${store.id}" } val createdBy = adminService.findOperatorOrUnknown(store.createdBy) val updatedBy = adminService.findOperatorOrUnknown(store.updatedBy) - return AuditInfo( + return AuditingInfo( createdAt = store.createdAt, createdBy = createdBy, updatedAt = store.updatedAt, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/store/docs/StoreAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/docs/StoreAPI.kt index 3c4ee9f2..20e93eb9 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/store/docs/StoreAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/docs/StoreAPI.kt @@ -1,5 +1,11 @@ package com.sangdol.roomescape.store.docs +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege +import com.sangdol.roomescape.auth.web.support.AdminOnly +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.store.web.* import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses @@ -8,12 +14,6 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestParam -import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege -import com.sangdol.roomescape.auth.web.support.AdminOnly -import com.sangdol.roomescape.auth.web.support.Public -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.store.web.* interface AdminStoreAPI { @AdminOnly(type = AdminType.HQ, privilege = Privilege.READ_DETAIL) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreController.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreController.kt index 77b6f974..f609043b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreController.kt @@ -1,11 +1,11 @@ package com.sangdol.roomescape.store.web +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.store.business.StoreService +import com.sangdol.roomescape.store.docs.AdminStoreAPI import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.store.business.StoreService -import com.sangdol.roomescape.store.docs.AdminStoreAPI @RestController @RequestMapping("/admin/stores") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt index a20edc26..2f60f2f2 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.store.web -import com.sangdol.roomescape.common.dto.AuditInfo +import com.sangdol.common.types.audit.AuditingInfo import com.sangdol.roomescape.region.web.RegionInfoResponse import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity @@ -29,12 +29,12 @@ data class DetailStoreResponse( val contact: String, val businessRegNum: String, val region: RegionInfoResponse, - val audit: AuditInfo + val audit: AuditingInfo ) fun StoreEntity.toDetailResponse( region: RegionInfoResponse, - audit: AuditInfo + audit: AuditingInfo ) = DetailStoreResponse( id = this.id, name = this.name, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/store/web/StoreController.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/web/StoreController.kt index 9eba1b54..5afac648 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/store/web/StoreController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/web/StoreController.kt @@ -1,13 +1,13 @@ package com.sangdol.roomescape.store.web +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.store.business.StoreService +import com.sangdol.roomescape.store.docs.PublicStoreAPI import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.store.business.StoreService -import com.sangdol.roomescape.store.docs.PublicStoreAPI @RestController class StoreController( diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt index 73a1926a..27470d68 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.theme.business import com.sangdol.common.persistence.IDGenerator +import com.sangdol.common.types.audit.AuditingInfo import com.sangdol.roomescape.admin.business.AdminService -import com.sangdol.roomescape.common.dto.AuditInfo import com.sangdol.roomescape.common.util.DateUtils import com.sangdol.roomescape.theme.exception.ThemeErrorCode import com.sangdol.roomescape.theme.exception.ThemeException @@ -78,7 +78,7 @@ class ThemeService( val createdBy = adminService.findOperatorOrUnknown(theme.createdBy) val updatedBy = adminService.findOperatorOrUnknown(theme.updatedBy) - val audit = AuditInfo(theme.createdAt, createdBy, theme.updatedAt, updatedBy) + val audit = AuditingInfo(theme.createdAt, createdBy, theme.updatedAt, updatedBy) return theme.toAdminThemeDetailResponse(audit) .also { log.info { "[ThemeService.findAdminThemeDetail] 테마 상세 조회 완료: id=$id, name=${theme.name}" } } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt index 6b5ffa2d..14acfd0c 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/docs/ThemeApi.kt @@ -1,5 +1,11 @@ package com.sangdol.roomescape.theme.docs +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege +import com.sangdol.roomescape.auth.web.support.AdminOnly +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.theme.web.* import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses @@ -9,12 +15,6 @@ import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestParam -import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege -import com.sangdol.roomescape.auth.web.support.AdminOnly -import com.sangdol.roomescape.auth.web.support.Public -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.theme.web.* interface AdminThemeAPI { @AdminOnly(type = AdminType.HQ, privilege = Privilege.READ_SUMMARY) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt index b9084e28..3a5f6375 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeController.kt @@ -1,11 +1,11 @@ package com.sangdol.roomescape.theme.web +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.theme.business.ThemeService +import com.sangdol.roomescape.theme.docs.AdminThemeAPI import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.theme.business.ThemeService -import com.sangdol.roomescape.theme.docs.AdminThemeAPI import java.net.URI @RestController diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt index a5ed2964..5f616075 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.theme.web -import com.sangdol.roomescape.common.dto.AuditInfo +import com.sangdol.common.types.audit.AuditingInfo import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity @@ -95,10 +95,10 @@ fun List.toAdminThemeSummaryListResponse() = AdminThemeSummaryListR data class AdminThemeDetailResponse( val theme: ThemeInfoResponse, val isActive: Boolean, - val audit: AuditInfo + val audit: AuditingInfo ) -fun ThemeEntity.toAdminThemeDetailResponse(audit: AuditInfo) = +fun ThemeEntity.toAdminThemeDetailResponse(audit: AuditingInfo) = AdminThemeDetailResponse( theme = this.toInfoResponse(), isActive = this.isActive, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt index b17082eb..f68e341c 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeController.kt @@ -1,10 +1,10 @@ package com.sangdol.roomescape.theme.web -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.* -import com.sangdol.roomescape.common.dto.response.CommonApiResponse +import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.theme.business.ThemeService import com.sangdol.roomescape.theme.docs.PublicThemeAPI +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* @RestController @RequestMapping("/themes") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt index 512d2c54..adc6322a 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt @@ -1,18 +1,18 @@ package com.sangdol.roomescape.user.docs +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.auth.web.support.Public +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.user.web.UserContactResponse +import com.sangdol.roomescape.user.web.UserCreateRequest +import com.sangdol.roomescape.user.web.UserCreateResponse import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.RequestBody -import com.sangdol.roomescape.auth.web.support.Public -import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.user.web.UserContactResponse -import com.sangdol.roomescape.user.web.UserCreateRequest -import com.sangdol.roomescape.user.web.UserCreateResponse interface UserAPI { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt index c7819ccd..295ff9ce 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt @@ -1,13 +1,13 @@ package com.sangdol.roomescape.user.web +import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.roomescape.auth.web.support.User +import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.roomescape.user.business.UserService +import com.sangdol.roomescape.user.docs.UserAPI import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext -import com.sangdol.roomescape.common.dto.response.CommonApiResponse -import com.sangdol.roomescape.user.business.UserService -import com.sangdol.roomescape.user.docs.UserAPI @RestController @RequestMapping("/users") diff --git a/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt b/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt index 6aa01e1d..d3eaeb07 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt @@ -1,910 +1,910 @@ -package com.sangdol.roomescape.data - -import com.sangdol.common.persistence.IDGenerator -import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity -import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel -import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -import com.sangdol.roomescape.common.util.TransactionExecutionUtil -import com.sangdol.roomescape.payment.infrastructure.common.* -import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus -import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus -import com.sangdol.roomescape.supports.AdminFixture -import com.sangdol.roomescape.supports.FunSpecSpringbootTest -import com.sangdol.roomescape.supports.randomPhoneNumber -import com.sangdol.roomescape.supports.randomString -import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty -import com.sangdol.roomescape.user.business.SIGNUP -import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity -import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus -import com.sangdol.roomescape.user.web.UserContactResponse -import io.kotest.core.test.TestCaseOrder -import jakarta.persistence.EntityManager -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Semaphore -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.jdbc.core.JdbcTemplate -import org.springframework.test.context.ActiveProfiles -import java.sql.Timestamp -import java.time.LocalDateTime -import java.time.LocalTime -import java.time.OffsetDateTime - -@ActiveProfiles("test", "test-mysql") -abstract class AbstractDataInitializer( - val semaphore: Semaphore = Semaphore(permits = 10), -) : FunSpecSpringbootTest( - enableCleanerExtension = false -) { - @Autowired - lateinit var entityManager: EntityManager - - @Autowired - lateinit var jdbcTemplate: JdbcTemplate - - @Autowired - lateinit var transactionExecutionUtil: TransactionExecutionUtil - - @Autowired - lateinit var idGenerator: IDGenerator - - override fun testCaseOrder(): TestCaseOrder? = TestCaseOrder.Sequential - - suspend fun initialize() { - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0") - - jdbcTemplate.query("SHOW TABLES") { rs, _ -> - rs.getString(1).lowercase() - }.forEach { - jdbcTemplate.execute("TRUNCATE TABLE $it") - } - - jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1") - - this::class.java.getResource("/schema/region-data.sql")?.readText()?.let { sql -> - jdbcTemplate.execute(sql) - } - } - } - - suspend fun executeBatch(sql: String, batchArgs: List>) { - semaphore.acquire() - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - jdbcTemplate.batchUpdate(sql, batchArgs) - } - - semaphore.release() - } -} - -class DefaultDataInitializer : AbstractDataInitializer() { - - // 1. HQ Admin 추가 - // 2. Store 추가 -> CreatedBy / UpdatedBy는 HQ Admin 중 한명으로 고정 - // 3. Store Admin 추가 -> CreatedBy / UpdatedBy는 HQ Admin 본인으로 고정 - init { - lateinit var superHQAdmin: AdminEntity - - // 모든 테이블 초기화 + 지역 데이터 삽입 + HQ 관리자 1명 생성 - beforeSpec { - initialize() - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - superHQAdmin = testAuthUtil.createAdmin(AdminFixture.hqDefault.apply { - this.createdBy = this.id - this.updatedBy = this.id - }) - } - } - - context("관리자, 매장, 테마 초기 데이터 생성") { - test("각 PermissionLevel 마다 20명의 HQ 관리자 생성") { - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - AdminPermissionLevel.entries.forEach { - repeat(20) { index -> - AdminFixture.create( - account = "hq_${it.name.lowercase()}_$index", - name = randomKoreanName(), - phone = randomPhoneNumber(), - type = AdminType.HQ, - permissionLevel = it - ).apply { - this.createdBy = superHQAdmin.id - this.updatedBy = superHQAdmin.id - }.also { - entityManager.persist(it) - } - } - } - } - } - - test("전체 매장 생성") { - val storeDataInitializer = StoreDataInitializer() - val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", - Long::class.java - ).setParameter("type", AdminType.HQ) - .setParameter( - "permissionLevels", - listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) - ) - .resultList - }.map { it.toString() } - - val sqlFile = storeDataInitializer.createStoreDataSqlFile(creatableAdminIds) - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - jdbcTemplate.execute(sqlFile.readText()) - } - } - - test("각 매장당 1명의 ${AdminPermissionLevel.FULL_ACCESS} 권한의 StoreManager 1명 + ${AdminPermissionLevel.WRITABLE}의 2명 + 나머지 권한은 3명씩 생성") { - val storeAdminCountsByPermissionLevel = mapOf( - AdminPermissionLevel.WRITABLE to 2, - AdminPermissionLevel.READ_ALL to 3, - AdminPermissionLevel.READ_SUMMARY to 3 - ) - - val storeIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT s.id FROM StoreEntity s", - Long::class.java - ).resultList - }.map { it as Long } - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - storeIds.forEach { storeId -> - // StoreManager 1명 생성 - val storeManager = AdminFixture.create( - account = "$storeId", - name = randomKoreanName(), - phone = randomPhoneNumber(), - type = AdminType.STORE, - storeId = storeId, - permissionLevel = AdminPermissionLevel.FULL_ACCESS - ).apply { - this.createdBy = superHQAdmin.id - this.updatedBy = superHQAdmin.id - }.also { - entityManager.persist(it) - } - - storeAdminCountsByPermissionLevel.forEach { (permissionLevel, count) -> - repeat(count) { index -> - AdminFixture.create( - account = randomString(), - name = randomKoreanName(), - phone = randomPhoneNumber(), - type = AdminType.STORE, - storeId = storeId, - permissionLevel = permissionLevel - ).apply { - this.createdBy = storeManager.id - this.updatedBy = storeManager.id - }.also { - entityManager.persist(it) - } - } - } - entityManager.flush() - entityManager.clear() - } - } - } - - test("총 500개의 테마 생성: 지난 2년 전 부터 지금까지의 랜덤 테마 + active 상태인 1달 이내 생성 테마 10개") { - val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", - Long::class.java - ).setParameter("type", AdminType.HQ) - .setParameter( - "permissionLevels", - listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) - ) - .resultList - } - val sql = - "INSERT INTO theme (id, name, description, thumbnail_url, is_active, available_minutes, expected_minutes_from, expected_minutes_to, price, difficulty, min_participants, max_participants, created_at, created_by, updated_at, updated_by) VALUES (?," + - "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - val batchSize = 100 - val batchArgs = mutableListOf>() - - repeat(500) { i -> - val randomDay = if (i <= 9) (1..30).random() else (1..365 * 2).random() - val randomCreatedAt: LocalDateTime = LocalDateTime.now().minusDays(randomDay.toLong()) - val randomThemeName = - (1..7).random().let { repeat -> (1..repeat).joinToString("") { randomKoreanName() } } - val availableMinutes = (6..20).random() * 10 - val expectedMinutesTo = availableMinutes - ((1..3).random() * 10) - val expectedMinutesFrom = expectedMinutesTo - ((1..2).random() * 10) - val randomPrice = (0..40).random() * 500 - val minParticipant = (1..10).random() - val maxParticipant = minParticipant + (1..10).random() - val createdBy = creatableAdminIds.random() - - batchArgs.add( - arrayOf( - idGenerator.create(), - randomThemeName, - "$randomThemeName 설명이에요!!", - "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFiuCdwdz88l6pdfsRy1nFl0IHUVI7JMTQHg&s", - if (randomDay <= 30) true else false, - availableMinutes.toShort(), - expectedMinutesFrom.toShort(), - expectedMinutesTo.toShort(), - randomPrice, - Difficulty.entries.random().name, - minParticipant.toShort(), - maxParticipant.toShort(), - Timestamp.valueOf(randomCreatedAt), - createdBy, - Timestamp.valueOf(randomCreatedAt), - createdBy - ) - ) - } - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - jdbcTemplate.batchUpdate(sql, batchArgs) - } - } - } - } -} - -class UserDataInitializer : AbstractDataInitializer() { - val userCount = 1_000_000 - - init { - context("유저 초기 데이터 생성") { - test("$userCount 명의 회원 생성") { - val regions: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT r.code FROM RegionEntity r", - String::class.java - ).resultList - } - - val chunkSize = 10_000 - val chunks = userCount / chunkSize - - coroutineScope { - (1..chunks).map { - launch(Dispatchers.IO) { - processUsers(chunkSize, regions) - } - }.joinAll() - } - } - - test("휴대폰 번호가 중복된 유저들에게 재배정") { - val duplicatePhoneUsers: List = - transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - """ - SELECT u FROM UserEntity u - WHERE u.phone IN ( - SELECT u2.phone FROM UserEntity u2 - GROUP BY u2.phone - HAVING COUNT(u2.id) > 1 - ) - ORDER BY u.phone, u.id - """.trimIndent(), - UserEntity::class.java - ).resultList - } - - jdbcTemplate.execute("CREATE INDEX idx_users__phone ON users (phone)") - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - var currentPhone: String? = null - duplicatePhoneUsers.forEach { user -> - if (user.phone != currentPhone) { - currentPhone = user.phone - } else { - var newPhone: String - - do { - newPhone = randomPhoneNumber() - val count: Long = entityManager.createQuery( - "SELECT COUNT(u.id) FROM UserEntity u WHERE u.phone = :phone", - Long::class.java - ).setParameter("phone", newPhone) - .singleResult - - if (count == 0L) break - } while (true) - - user.phone = newPhone - user.updatedAt = LocalDateTime.now() - entityManager.merge(user) - } - } - } - - jdbcTemplate.execute("DROP INDEX idx_users__phone ON users") - } - - test("회원 상태 변경 이력 저장") { - val userId: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT u.id FROM UserEntity u", - Long::class.java - ).resultList - } - - coroutineScope { - userId.chunked(10_000).map { chunk -> - launch(Dispatchers.IO) { - processStatus(chunk) - } - }.joinAll() - } - } - } - } - - private suspend fun processStatus(userIds: List) { - val sql = """ - INSERT INTO user_status_history ( - id, user_id, reason, status, - created_by, updated_by, created_at, updated_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - val batchArgs = mutableListOf>() - val now = LocalDateTime.now() - - userIds.forEach { userId -> - batchArgs.add( - arrayOf( - idGenerator.create(), - userId, - SIGNUP, - UserStatus.ACTIVE.name, - userId, - userId, - Timestamp.valueOf(now), - Timestamp.valueOf(now) - ) - ) - } - - executeBatch(sql, batchArgs).also { batchArgs.clear() } - } - - private suspend fun processUsers(chunkSize: Int, regions: List) { - val sql = """ - INSERT INTO users ( - id, name, email, password, phone, region_code, status, - created_by, updated_by, created_at, updated_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - val batchArgs = mutableListOf>() - - repeat(chunkSize) { - val id: Long = idGenerator.create() - batchArgs.add( - arrayOf( - id, - randomKoreanName(), - "${randomString()}@sangdol.com", - randomString(), - randomPhoneNumber(), - regions.random(), - UserStatus.ACTIVE.name, - id, - id, - Timestamp.valueOf(LocalDateTime.now()), - Timestamp.valueOf(LocalDateTime.now()) - ) - ) - if (batchArgs.size >= 1_000) { - executeBatch(sql, batchArgs).also { batchArgs.clear() } - } - } - - if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } - } -} - -class ScheduleDataInitializer : AbstractDataInitializer() { - init { - context("일정 초기 데이터 생성") { - test("테마 생성일 기준으로 다음 3일차, 매일 5개의 일정을 모든 매장에 생성") { - val stores: List> = getStoreWithManagers() - val themes: List> = getThemes() - val maxAvailableMinutes = themes.maxOf { it.second.toInt() } - val scheduleCountPerDay = 5 - - val startTime = LocalTime.of(10, 0) - var lastTime = startTime - val times = mutableListOf() - - repeat(scheduleCountPerDay) { - times.add(lastTime) - lastTime = lastTime.plusMinutes(maxAvailableMinutes.toLong() + 10L) - } - - coroutineScope { - themes.forEach { theme -> - launch(Dispatchers.IO) { - processTheme(theme, stores, times) - } - } - } - } - } - } - - private suspend fun processTheme( - theme: Triple, - stores: List>, - times: List - ) { - val sql = """ - INSERT INTO schedule ( - id, store_id, theme_id, date, time, status, - created_by, updated_by, created_at, updated_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - - val batchArgs = mutableListOf>() - - val now = LocalDateTime.now() - stores.forEach { (storeId, adminId) -> - (1..3).forEach { dayOffset -> - val date = theme.third.toLocalDate().plusDays(dayOffset.toLong()) - - times.forEach { time -> - val scheduledAt = LocalDateTime.of(date, time) - val status = - if (scheduledAt.isAfter(now)) ScheduleStatus.AVAILABLE.name else ScheduleStatus.RESERVED.name - - batchArgs.add( - arrayOf( - idGenerator.create(), storeId, theme.first, date, time, - status, adminId, adminId, Timestamp.valueOf(now), Timestamp.valueOf(now) - ) - ) - - if (batchArgs.size >= 500) { - executeBatch(sql, batchArgs).also { batchArgs.clear() } - } - } - } - } - - if (batchArgs.isNotEmpty()) { - executeBatch(sql, batchArgs).also { batchArgs.clear() } - } - } - - private fun getStoreWithManagers(): List> { - return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT a.storeId, a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel = :permissionLevel", - List::class.java - ).setParameter("type", AdminType.STORE) - .setParameter("permissionLevel", AdminPermissionLevel.FULL_ACCESS) - .resultList - }.map { - val array = it as List<*> - Pair(array[0] as Long, array[1] as Long) - } - } - - private fun getThemes(): List> { - return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { - entityManager.createQuery( - "SELECT t._id, t.availableMinutes, t.createdAt FROM ThemeEntity t", - List::class.java - ) - .resultList - }.map { - val array = it as List<*> - Triple(array[0] as Long, array[1] as Short, array[2] as LocalDateTime) - } - } -} - -/** - * 아래의 ReservationDataInitializer 에서 사용할 임시 DTO 클래스 - */ -data class ScheduleWithThemeParticipants( - val scheduleId: Long, - val themeMinParticipants: Short, - val themeMaxParticipants: Short, -) - -class ReservationDataInitializer : AbstractDataInitializer() { - - init { - context("예약 초기 데이터 생성") { - test("${ScheduleStatus.RESERVED}인 모든 일정에 예약을 1개씩 배정한다.") { - val chunkSize = 10_000 - - val chunkedSchedules: List> = entityManager.createQuery( - "SELECT new com.sangdol.roomescape.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status", - ScheduleWithThemeParticipants::class.java - ).setParameter("status", ScheduleStatus.RESERVED).resultList.chunked(chunkSize) - - val chunkedUsers: List> = entityManager.createQuery( - "SELECT new com.sangdol.roomescape.user.web.UserContactResponse(u._id, u.name, u.phone) FROM UserEntity u", - UserContactResponse::class.java - ).resultList.chunked(chunkSize) - - - coroutineScope { - chunkedSchedules.forEachIndexed { idx, schedules -> - launch(Dispatchers.IO) { - processReservation(chunkedUsers[idx % chunkedUsers.size], schedules) - } - } - } - } - } - } - - private suspend fun processReservation( - users: List, - schedules: List - ) { - val sql = """ - INSERT INTO reservation ( - id, user_id, schedule_id, - reserver_name, reserver_contact, participant_count, requirement, - status, created_at, created_by, updated_at, updated_by - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - - val batchArgs = mutableListOf>() - - val createdAt = LocalDateTime.now() - - schedules.forEachIndexed { idx, schedule -> - val user: UserContactResponse = users[idx % users.size] - - batchArgs.add( - arrayOf( - idGenerator.create(), - user.id, - schedule.scheduleId, - user.name, - user.phone, - (schedule.themeMinParticipants..schedule.themeMaxParticipants).random(), - randomKoreanWords(length = (20..100).random()), - ReservationStatus.CONFIRMED.name, - Timestamp.valueOf(createdAt), - user.id, - Timestamp.valueOf(createdAt), - user.id, - ) - ) - - if (batchArgs.size >= 1_000) { - executeBatch(sql, batchArgs).also { batchArgs.clear() } - } - } - - if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } - } -} - -class ReservationWithPrice( - themePrice: Int, - participantCount: Short, - - val reservationId: Long, - val totalPrice: Int = (themePrice * participantCount), -) - -data class PaymentWithMethods( - val id: Long, - val totalPrice: Int, - val method: PaymentMethod -) - -class PaymentDataInitializer : AbstractDataInitializer() { - companion object { - val requestedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().toLocalDateTime()) - val approvedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().plusSeconds(5).toLocalDateTime()) - val supportedPaymentMethods = listOf(PaymentMethod.TRANSFER, PaymentMethod.EASY_PAY, PaymentMethod.CARD) - val supportedCardType = listOf(CardType.CREDIT, CardType.CHECK) - - val settlementStatus = "COMPLETED" - - val paymentSql: String = """ - INSERT INTO payment( - id, reservation_id, type, method, - payment_key, order_id, total_amount, status, - requested_at, approved_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - - val paymentDetailSql: String = """ - INSERT INTO payment_detail( - id, payment_id, supplied_amount, vat - ) VALUES (?, ?, ?, ?) - """.trimIndent() - - val paymentCardDetailSql: String = """ - INSERT INTO payment_card_detail( - id, issuer_code, card_type, owner_type, - amount, card_number, approval_number, installment_plan_months, - is_interest_free, easypay_provider_code, easypay_discount_amount - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - - val paymentEasypayPrepaidDetailSql: String = """ - INSERT INTO payment_easypay_prepaid_detail( - id, easypay_provider_code, amount, discount_amount - ) VALUES (?, ?, ?, ?) - """.trimIndent() - - val paymentBankTransferDetailSql: String = """ - INSERT INTO payment_bank_transfer_detail( - id, bank_code, settlement_status - ) VALUES (?, ?, ?) - """.trimIndent() - } - - init { - context("결제 데이터 초기화") { - test("모든 예약에 맞춰 1:1로 결제 및 결제 상세 데이터를 생성한다.") { - val allReservations: List = entityManager.createQuery( - "SELECT t.price, r.participantCount, r._id FROM ReservationEntity r JOIN ScheduleEntity s ON s._id = r.scheduleId JOIN ThemeEntity t ON t.id = s.themeId", - List::class.java - ).resultList.map { - val items = it as List<*> - ReservationWithPrice( - themePrice = items[0] as Int, - participantCount = items[1] as Short, - reservationId = items[2] as Long - ) - } - - coroutineScope { - allReservations.chunked(10_000).forEach { reservations -> - launch(Dispatchers.IO) { - processPaymentAndDefaultDetail(reservations) - } - } - } - } - - test("기존 결제 데이터에 상세 정보(계좌이체, 카드, 간편결제) 데이터를 생성한다.") { - val allPayments: List = entityManager.createQuery( - "SELECT new com.sangdol.roomescape.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId", - PaymentWithMethods::class.java - ).resultList - - coroutineScope { - allPayments.chunked(10_000).forEach { payments -> - launch(Dispatchers.IO) { - processPaymentDetail(payments) - } - } - } - } - - test("null 컴파일 에러를 피하기 위해 문자열 null로 임시 지정한 컬럼을 변경한다.") { - jdbcTemplate.execute("CREATE INDEX idx_payment_card_detail_easypay ON payment_card_detail (easypay_provider_code)") - - transactionExecutionUtil.withNewTransaction(isReadOnly = false) { - jdbcTemplate.update( - "UPDATE payment_card_detail SET easypay_provider_code = ? WHERE easypay_provider_code = ?", - null, - "null" - ) - } - - jdbcTemplate.execute("DROP INDEX idx_payment_card_detail_easypay ON payment_card_detail") - } - } - } - - private suspend fun processPaymentAndDefaultDetail(reservations: List) { - val paymentBatchArgs = mutableListOf>() - val detailBatchArgs = mutableListOf>() - - reservations.forEachIndexed { idx, reservations -> - val id = idGenerator.create() - val totalPrice = reservations.totalPrice - paymentBatchArgs.add( - arrayOf( - id, - reservations.reservationId, - PaymentType.NORMAL.name, - randomPaymentMethod(), - randomString(length = 64), - randomString(length = 20), - totalPrice, - PaymentStatus.DONE.name, - requestedAtCache, - approvedAtCache, - ) - ) - if (paymentBatchArgs.size >= 1_000) { - executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } - } - - val suppliedAmount: Int = (totalPrice * 0.9).toInt() - val vat: Int = (totalPrice - suppliedAmount) - - detailBatchArgs.add( - arrayOf( - idGenerator.create(), - id, - suppliedAmount, - vat - ) - ) - - if (detailBatchArgs.size >= 1_000) { - executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } - } - } - - if (paymentBatchArgs.isNotEmpty()) { - executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } - } - if (detailBatchArgs.isNotEmpty()) { - executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } - } - } - - private suspend fun processPaymentDetail(payments: List) { - val transferBatchArgs = mutableListOf>() - val cardBatchArgs = mutableListOf>() - val easypayPrepaidBatchArgs = mutableListOf>() - - payments.forEach { payment -> - val totalPrice = payment.totalPrice - val randomDiscountAmount = - if (totalPrice < 100 || Math.random() < 0.8) 0 else ((100..totalPrice).random() / 100) * 100 - val amount = totalPrice - randomDiscountAmount - - when (payment.method) { - PaymentMethod.TRANSFER -> { - transferBatchArgs.add( - arrayOf( - payment.id, - BankCode.entries.random().name, - settlementStatus - ) - ) - if (transferBatchArgs.size >= 1_000) { - executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } - } - } - - PaymentMethod.EASY_PAY -> { - if (Math.random() <= 0.7) { - cardBatchArgs.add( - arrayOf( - payment.id, - CardIssuerCode.entries.random().name, - supportedCardType.random().name, - CardOwnerType.PERSONAL.name, - amount, - randomCardNumber(), - randomApprovalNumber(), - randomInstallmentPlanMonths(amount), - true, - EasyPayCompanyCode.entries.random().name, - randomDiscountAmount - ) - ) - - if (cardBatchArgs.size >= 1_000) { - executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } - } - - } else { - easypayPrepaidBatchArgs.add( - arrayOf( - payment.id, - EasyPayCompanyCode.entries.random().name, - amount, - randomDiscountAmount, - ) - ) - - if (easypayPrepaidBatchArgs.size >= 1_000) { - executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } - } - } - } - - PaymentMethod.CARD -> { - cardBatchArgs.add( - arrayOf( - payment.id, - CardIssuerCode.entries.random().name, - supportedCardType.random().name, - CardOwnerType.PERSONAL.name, - totalPrice, - randomCardNumber(), - randomApprovalNumber(), - randomInstallmentPlanMonths(totalPrice), - true, - "null", - 0, - ) - ) - - if (cardBatchArgs.size >= 1_000) { - executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } - } - } - - else -> return@forEach - } - } - if (transferBatchArgs.isNotEmpty()) { - executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } - } - if (cardBatchArgs.isNotEmpty()) { - executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } - } - if (easypayPrepaidBatchArgs.isNotEmpty()) { - executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } - } - } - - private suspend fun randomPaymentMethod(): String { - val random = Math.random() - - return if (random <= 0.5) { - PaymentMethod.EASY_PAY.name - } else if (random <= 0.9) { - PaymentMethod.CARD.name - } else { - PaymentMethod.TRANSFER.name - } - } - - private suspend fun randomCardNumber(): String { - return "${(10000000..99999999).random()}****${(100..999).random()}*" - } - - private suspend fun randomApprovalNumber(): String { - return "${(10000000..99999999).random()}" - } - - private suspend fun randomInstallmentPlanMonths(amount: Int): Int { - return if (amount < 50_000 || Math.random() < 0.9) { - 0 - } else { - (1..6).random() - } - } -} - -fun randomKoreanName(): String { - val lastNames = listOf( - "김", "이", "박", "최", "정", "강", "조", "윤", "장", "임", - "오", "서", "신", "권", "황", "안", "송", "류", "홍", "전", "고", "문", "양", "손", "배", "백", "허", "유", "남", "심", "노" - ) - - return "${lastNames.random()}${if (Math.random() < 0.1) randomKoreanWords(1) else randomKoreanWords(2)}" -} - -fun randomKoreanWords(length: Int = 1): String { - val words = listOf( - "가", "나", "다", "라", "마", "바", "사", "아", "자", "차", - "카", "타", "파", "하", "강", "민", "서", "윤", "우", "진", - "현", "지", "은", "혜", "수", "영", "주", "원", "희", "경", - "선", "아", "나", "다", "라", "마", "바", "사", "아", "자", - "차", "카", "타", "파", "하", - ) - - return (1..length).joinToString("") { words.random() } -} +//package com.sangdol.roomescape.data +// +//import com.sangdol.common.persistence.IDGenerator +//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity +//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +//import com.sangdol.roomescape.common.util.TransactionExecutionUtil +//import com.sangdol.roomescape.payment.infrastructure.common.* +//import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus +//import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +//import com.sangdol.roomescape.supports.AdminFixture +//import com.sangdol.roomescape.supports.FunSpecSpringbootTest +//import com.sangdol.roomescape.supports.randomPhoneNumber +//import com.sangdol.roomescape.supports.randomString +//import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty +//import com.sangdol.roomescape.user.business.SIGNUP +//import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity +//import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus +//import com.sangdol.roomescape.user.web.UserContactResponse +//import io.kotest.core.test.TestCaseOrder +//import jakarta.persistence.EntityManager +//import kotlinx.coroutines.Dispatchers +//import kotlinx.coroutines.coroutineScope +//import kotlinx.coroutines.joinAll +//import kotlinx.coroutines.launch +//import kotlinx.coroutines.sync.Semaphore +//import org.springframework.beans.factory.annotation.Autowired +//import org.springframework.jdbc.core.JdbcTemplate +//import org.springframework.test.context.ActiveProfiles +//import java.sql.Timestamp +//import java.time.LocalDateTime +//import java.time.LocalTime +//import java.time.OffsetDateTime +// +//@ActiveProfiles("test", "test-mysql") +//abstract class AbstractDataInitializer( +// val semaphore: Semaphore = Semaphore(permits = 10), +//) : FunSpecSpringbootTest( +// enableCleanerExtension = false +//) { +// @Autowired +// lateinit var entityManager: EntityManager +// +// @Autowired +// lateinit var jdbcTemplate: JdbcTemplate +// +// @Autowired +// lateinit var transactionExecutionUtil: TransactionExecutionUtil +// +// @Autowired +// lateinit var idGenerator: IDGenerator +// +// override fun testCaseOrder(): TestCaseOrder? = TestCaseOrder.Sequential +// +// suspend fun initialize() { +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0") +// +// jdbcTemplate.query("SHOW TABLES") { rs, _ -> +// rs.getString(1).lowercase() +// }.forEach { +// jdbcTemplate.execute("TRUNCATE TABLE $it") +// } +// +// jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1") +// +// this::class.java.getResource("/schema/region-data.sql")?.readText()?.let { sql -> +// jdbcTemplate.execute(sql) +// } +// } +// } +// +// suspend fun executeBatch(sql: String, batchArgs: List>) { +// semaphore.acquire() +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// jdbcTemplate.batchUpdate(sql, batchArgs) +// } +// +// semaphore.release() +// } +//} +// +//class DefaultDataInitializer : AbstractDataInitializer() { +// +// // 1. HQ Admin 추가 +// // 2. Store 추가 -> CreatedBy / UpdatedBy는 HQ Admin 중 한명으로 고정 +// // 3. Store Admin 추가 -> CreatedBy / UpdatedBy는 HQ Admin 본인으로 고정 +// init { +// lateinit var superHQAdmin: AdminEntity +// +// // 모든 테이블 초기화 + 지역 데이터 삽입 + HQ 관리자 1명 생성 +// beforeSpec { +// initialize() +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// superHQAdmin = testAuthUtil.createAdmin(AdminFixture.hqDefault.apply { +// this.createdBy = this.id +// this.updatedBy = this.id +// }) +// } +// } +// +// context("관리자, 매장, 테마 초기 데이터 생성") { +// test("각 PermissionLevel 마다 20명의 HQ 관리자 생성") { +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// AdminPermissionLevel.entries.forEach { +// repeat(20) { index -> +// AdminFixture.create( +// account = "hq_${it.name.lowercase()}_$index", +// name = randomKoreanName(), +// phone = randomPhoneNumber(), +// type = AdminType.HQ, +// permissionLevel = it +// ).apply { +// this.createdBy = superHQAdmin.id +// this.updatedBy = superHQAdmin.id +// }.also { +// entityManager.persist(it) +// } +// } +// } +// } +// } +// +// test("전체 매장 생성") { +// val storeDataInitializer = StoreDataInitializer() +// val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", +// Long::class.java +// ).setParameter("type", AdminType.HQ) +// .setParameter( +// "permissionLevels", +// listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) +// ) +// .resultList +// }.map { it.toString() } +// +// val sqlFile = storeDataInitializer.createStoreDataSqlFile(creatableAdminIds) +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// jdbcTemplate.execute(sqlFile.readText()) +// } +// } +// +// test("각 매장당 1명의 ${AdminPermissionLevel.FULL_ACCESS} 권한의 StoreManager 1명 + ${AdminPermissionLevel.WRITABLE}의 2명 + 나머지 권한은 3명씩 생성") { +// val storeAdminCountsByPermissionLevel = mapOf( +// AdminPermissionLevel.WRITABLE to 2, +// AdminPermissionLevel.READ_ALL to 3, +// AdminPermissionLevel.READ_SUMMARY to 3 +// ) +// +// val storeIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT s.id FROM StoreEntity s", +// Long::class.java +// ).resultList +// }.map { it as Long } +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// storeIds.forEach { storeId -> +// // StoreManager 1명 생성 +// val storeManager = AdminFixture.create( +// account = "$storeId", +// name = randomKoreanName(), +// phone = randomPhoneNumber(), +// type = AdminType.STORE, +// storeId = storeId, +// permissionLevel = AdminPermissionLevel.FULL_ACCESS +// ).apply { +// this.createdBy = superHQAdmin.id +// this.updatedBy = superHQAdmin.id +// }.also { +// entityManager.persist(it) +// } +// +// storeAdminCountsByPermissionLevel.forEach { (permissionLevel, count) -> +// repeat(count) { index -> +// AdminFixture.create( +// account = randomString(), +// name = randomKoreanName(), +// phone = randomPhoneNumber(), +// type = AdminType.STORE, +// storeId = storeId, +// permissionLevel = permissionLevel +// ).apply { +// this.createdBy = storeManager.id +// this.updatedBy = storeManager.id +// }.also { +// entityManager.persist(it) +// } +// } +// } +// entityManager.flush() +// entityManager.clear() +// } +// } +// } +// +// test("총 500개의 테마 생성: 지난 2년 전 부터 지금까지의 랜덤 테마 + active 상태인 1달 이내 생성 테마 10개") { +// val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", +// Long::class.java +// ).setParameter("type", AdminType.HQ) +// .setParameter( +// "permissionLevels", +// listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) +// ) +// .resultList +// } +// val sql = +// "INSERT INTO theme (id, name, description, thumbnail_url, is_active, available_minutes, expected_minutes_from, expected_minutes_to, price, difficulty, min_participants, max_participants, created_at, created_by, updated_at, updated_by) VALUES (?," + +// "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" +// val batchSize = 100 +// val batchArgs = mutableListOf>() +// +// repeat(500) { i -> +// val randomDay = if (i <= 9) (1..30).random() else (1..365 * 2).random() +// val randomCreatedAt: LocalDateTime = LocalDateTime.now().minusDays(randomDay.toLong()) +// val randomThemeName = +// (1..7).random().let { repeat -> (1..repeat).joinToString("") { randomKoreanName() } } +// val availableMinutes = (6..20).random() * 10 +// val expectedMinutesTo = availableMinutes - ((1..3).random() * 10) +// val expectedMinutesFrom = expectedMinutesTo - ((1..2).random() * 10) +// val randomPrice = (0..40).random() * 500 +// val minParticipant = (1..10).random() +// val maxParticipant = minParticipant + (1..10).random() +// val createdBy = creatableAdminIds.random() +// +// batchArgs.add( +// arrayOf( +// idGenerator.create(), +// randomThemeName, +// "$randomThemeName 설명이에요!!", +// "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFiuCdwdz88l6pdfsRy1nFl0IHUVI7JMTQHg&s", +// if (randomDay <= 30) true else false, +// availableMinutes.toShort(), +// expectedMinutesFrom.toShort(), +// expectedMinutesTo.toShort(), +// randomPrice, +// Difficulty.entries.random().name, +// minParticipant.toShort(), +// maxParticipant.toShort(), +// Timestamp.valueOf(randomCreatedAt), +// createdBy, +// Timestamp.valueOf(randomCreatedAt), +// createdBy +// ) +// ) +// } +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// jdbcTemplate.batchUpdate(sql, batchArgs) +// } +// } +// } +// } +//} +// +//class UserDataInitializer : AbstractDataInitializer() { +// val userCount = 1_000_000 +// +// init { +// context("유저 초기 데이터 생성") { +// test("$userCount 명의 회원 생성") { +// val regions: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT r.code FROM RegionEntity r", +// String::class.java +// ).resultList +// } +// +// val chunkSize = 10_000 +// val chunks = userCount / chunkSize +// +// coroutineScope { +// (1..chunks).map { +// launch(Dispatchers.IO) { +// processUsers(chunkSize, regions) +// } +// }.joinAll() +// } +// } +// +// test("휴대폰 번호가 중복된 유저들에게 재배정") { +// val duplicatePhoneUsers: List = +// transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// """ +// SELECT u FROM UserEntity u +// WHERE u.phone IN ( +// SELECT u2.phone FROM UserEntity u2 +// GROUP BY u2.phone +// HAVING COUNT(u2.id) > 1 +// ) +// ORDER BY u.phone, u.id +// """.trimIndent(), +// UserEntity::class.java +// ).resultList +// } +// +// jdbcTemplate.execute("CREATE INDEX idx_users__phone ON users (phone)") +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// var currentPhone: String? = null +// duplicatePhoneUsers.forEach { user -> +// if (user.phone != currentPhone) { +// currentPhone = user.phone +// } else { +// var newPhone: String +// +// do { +// newPhone = randomPhoneNumber() +// val count: Long = entityManager.createQuery( +// "SELECT COUNT(u.id) FROM UserEntity u WHERE u.phone = :phone", +// Long::class.java +// ).setParameter("phone", newPhone) +// .singleResult +// +// if (count == 0L) break +// } while (true) +// +// user.phone = newPhone +// user.updatedAt = LocalDateTime.now() +// entityManager.merge(user) +// } +// } +// } +// +// jdbcTemplate.execute("DROP INDEX idx_users__phone ON users") +// } +// +// test("회원 상태 변경 이력 저장") { +// val userId: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT u.id FROM UserEntity u", +// Long::class.java +// ).resultList +// } +// +// coroutineScope { +// userId.chunked(10_000).map { chunk -> +// launch(Dispatchers.IO) { +// processStatus(chunk) +// } +// }.joinAll() +// } +// } +// } +// } +// +// private suspend fun processStatus(userIds: List) { +// val sql = """ +// INSERT INTO user_status_history ( +// id, user_id, reason, status, +// created_by, updated_by, created_at, updated_at +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// val batchArgs = mutableListOf>() +// val now = LocalDateTime.now() +// +// userIds.forEach { userId -> +// batchArgs.add( +// arrayOf( +// idGenerator.create(), +// userId, +// SIGNUP, +// UserStatus.ACTIVE.name, +// userId, +// userId, +// Timestamp.valueOf(now), +// Timestamp.valueOf(now) +// ) +// ) +// } +// +// executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +// +// private suspend fun processUsers(chunkSize: Int, regions: List) { +// val sql = """ +// INSERT INTO users ( +// id, name, email, password, phone, region_code, status, +// created_by, updated_by, created_at, updated_at +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// val batchArgs = mutableListOf>() +// +// repeat(chunkSize) { +// val id: Long = idGenerator.create() +// batchArgs.add( +// arrayOf( +// id, +// randomKoreanName(), +// "${randomString()}@sangdol.com", +// randomString(), +// randomPhoneNumber(), +// regions.random(), +// UserStatus.ACTIVE.name, +// id, +// id, +// Timestamp.valueOf(LocalDateTime.now()), +// Timestamp.valueOf(LocalDateTime.now()) +// ) +// ) +// if (batchArgs.size >= 1_000) { +// executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +// } +// +// if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +//} +// +//class ScheduleDataInitializer : AbstractDataInitializer() { +// init { +// context("일정 초기 데이터 생성") { +// test("테마 생성일 기준으로 다음 3일차, 매일 5개의 일정을 모든 매장에 생성") { +// val stores: List> = getStoreWithManagers() +// val themes: List> = getThemes() +// val maxAvailableMinutes = themes.maxOf { it.second.toInt() } +// val scheduleCountPerDay = 5 +// +// val startTime = LocalTime.of(10, 0) +// var lastTime = startTime +// val times = mutableListOf() +// +// repeat(scheduleCountPerDay) { +// times.add(lastTime) +// lastTime = lastTime.plusMinutes(maxAvailableMinutes.toLong() + 10L) +// } +// +// coroutineScope { +// themes.forEach { theme -> +// launch(Dispatchers.IO) { +// processTheme(theme, stores, times) +// } +// } +// } +// } +// } +// } +// +// private suspend fun processTheme( +// theme: Triple, +// stores: List>, +// times: List +// ) { +// val sql = """ +// INSERT INTO schedule ( +// id, store_id, theme_id, date, time, status, +// created_by, updated_by, created_at, updated_at +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// +// val batchArgs = mutableListOf>() +// +// val now = LocalDateTime.now() +// stores.forEach { (storeId, adminId) -> +// (1..3).forEach { dayOffset -> +// val date = theme.third.toLocalDate().plusDays(dayOffset.toLong()) +// +// times.forEach { time -> +// val scheduledAt = LocalDateTime.of(date, time) +// val status = +// if (scheduledAt.isAfter(now)) ScheduleStatus.AVAILABLE.name else ScheduleStatus.RESERVED.name +// +// batchArgs.add( +// arrayOf( +// idGenerator.create(), storeId, theme.first, date, time, +// status, adminId, adminId, Timestamp.valueOf(now), Timestamp.valueOf(now) +// ) +// ) +// +// if (batchArgs.size >= 500) { +// executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +// } +// } +// } +// +// if (batchArgs.isNotEmpty()) { +// executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +// } +// +// private fun getStoreWithManagers(): List> { +// return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT a.storeId, a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel = :permissionLevel", +// List::class.java +// ).setParameter("type", AdminType.STORE) +// .setParameter("permissionLevel", AdminPermissionLevel.FULL_ACCESS) +// .resultList +// }.map { +// val array = it as List<*> +// Pair(array[0] as Long, array[1] as Long) +// } +// } +// +// private fun getThemes(): List> { +// return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { +// entityManager.createQuery( +// "SELECT t._id, t.availableMinutes, t.createdAt FROM ThemeEntity t", +// List::class.java +// ) +// .resultList +// }.map { +// val array = it as List<*> +// Triple(array[0] as Long, array[1] as Short, array[2] as LocalDateTime) +// } +// } +//} +// +///** +// * 아래의 ReservationDataInitializer 에서 사용할 임시 DTO 클래스 +// */ +//data class ScheduleWithThemeParticipants( +// val scheduleId: Long, +// val themeMinParticipants: Short, +// val themeMaxParticipants: Short, +//) +// +//class ReservationDataInitializer : AbstractDataInitializer() { +// +// init { +// context("예약 초기 데이터 생성") { +// test("${ScheduleStatus.RESERVED}인 모든 일정에 예약을 1개씩 배정한다.") { +// val chunkSize = 10_000 +// +// val chunkedSchedules: List> = entityManager.createQuery( +// "SELECT new com.sangdol.roomescape.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status", +// ScheduleWithThemeParticipants::class.java +// ).setParameter("status", ScheduleStatus.RESERVED).resultList.chunked(chunkSize) +// +// val chunkedUsers: List> = entityManager.createQuery( +// "SELECT new com.sangdol.roomescape.user.web.UserContactResponse(u._id, u.name, u.phone) FROM UserEntity u", +// UserContactResponse::class.java +// ).resultList.chunked(chunkSize) +// +// +// coroutineScope { +// chunkedSchedules.forEachIndexed { idx, schedules -> +// launch(Dispatchers.IO) { +// processReservation(chunkedUsers[idx % chunkedUsers.size], schedules) +// } +// } +// } +// } +// } +// } +// +// private suspend fun processReservation( +// users: List, +// schedules: List +// ) { +// val sql = """ +// INSERT INTO reservation ( +// id, user_id, schedule_id, +// reserver_name, reserver_contact, participant_count, requirement, +// status, created_at, created_by, updated_at, updated_by +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// +// val batchArgs = mutableListOf>() +// +// val createdAt = LocalDateTime.now() +// +// schedules.forEachIndexed { idx, schedule -> +// val user: UserContactResponse = users[idx % users.size] +// +// batchArgs.add( +// arrayOf( +// idGenerator.create(), +// user.id, +// schedule.scheduleId, +// user.name, +// user.phone, +// (schedule.themeMinParticipants..schedule.themeMaxParticipants).random(), +// randomKoreanWords(length = (20..100).random()), +// ReservationStatus.CONFIRMED.name, +// Timestamp.valueOf(createdAt), +// user.id, +// Timestamp.valueOf(createdAt), +// user.id, +// ) +// ) +// +// if (batchArgs.size >= 1_000) { +// executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +// } +// +// if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } +// } +//} +// +//class ReservationWithPrice( +// themePrice: Int, +// participantCount: Short, +// +// val reservationId: Long, +// val totalPrice: Int = (themePrice * participantCount), +//) +// +//data class PaymentWithMethods( +// val id: Long, +// val totalPrice: Int, +// val method: PaymentMethod +//) +// +//class PaymentDataInitializer : AbstractDataInitializer() { +// companion object { +// val requestedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().toLocalDateTime()) +// val approvedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().plusSeconds(5).toLocalDateTime()) +// val supportedPaymentMethods = listOf(PaymentMethod.TRANSFER, PaymentMethod.EASY_PAY, PaymentMethod.CARD) +// val supportedCardType = listOf(CardType.CREDIT, CardType.CHECK) +// +// val settlementStatus = "COMPLETED" +// +// val paymentSql: String = """ +// INSERT INTO payment( +// id, reservation_id, type, method, +// payment_key, order_id, total_amount, status, +// requested_at, approved_at +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// +// val paymentDetailSql: String = """ +// INSERT INTO payment_detail( +// id, payment_id, supplied_amount, vat +// ) VALUES (?, ?, ?, ?) +// """.trimIndent() +// +// val paymentCardDetailSql: String = """ +// INSERT INTO payment_card_detail( +// id, issuer_code, card_type, owner_type, +// amount, card_number, approval_number, installment_plan_months, +// is_interest_free, easypay_provider_code, easypay_discount_amount +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// """.trimIndent() +// +// val paymentEasypayPrepaidDetailSql: String = """ +// INSERT INTO payment_easypay_prepaid_detail( +// id, easypay_provider_code, amount, discount_amount +// ) VALUES (?, ?, ?, ?) +// """.trimIndent() +// +// val paymentBankTransferDetailSql: String = """ +// INSERT INTO payment_bank_transfer_detail( +// id, bank_code, settlement_status +// ) VALUES (?, ?, ?) +// """.trimIndent() +// } +// +// init { +// context("결제 데이터 초기화") { +// test("모든 예약에 맞춰 1:1로 결제 및 결제 상세 데이터를 생성한다.") { +// val allReservations: List = entityManager.createQuery( +// "SELECT t.price, r.participantCount, r._id FROM ReservationEntity r JOIN ScheduleEntity s ON s._id = r.scheduleId JOIN ThemeEntity t ON t.id = s.themeId", +// List::class.java +// ).resultList.map { +// val items = it as List<*> +// ReservationWithPrice( +// themePrice = items[0] as Int, +// participantCount = items[1] as Short, +// reservationId = items[2] as Long +// ) +// } +// +// coroutineScope { +// allReservations.chunked(10_000).forEach { reservations -> +// launch(Dispatchers.IO) { +// processPaymentAndDefaultDetail(reservations) +// } +// } +// } +// } +// +// test("기존 결제 데이터에 상세 정보(계좌이체, 카드, 간편결제) 데이터를 생성한다.") { +// val allPayments: List = entityManager.createQuery( +// "SELECT new com.sangdol.roomescape.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId", +// PaymentWithMethods::class.java +// ).resultList +// +// coroutineScope { +// allPayments.chunked(10_000).forEach { payments -> +// launch(Dispatchers.IO) { +// processPaymentDetail(payments) +// } +// } +// } +// } +// +// test("null 컴파일 에러를 피하기 위해 문자열 null로 임시 지정한 컬럼을 변경한다.") { +// jdbcTemplate.execute("CREATE INDEX idx_payment_card_detail_easypay ON payment_card_detail (easypay_provider_code)") +// +// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { +// jdbcTemplate.update( +// "UPDATE payment_card_detail SET easypay_provider_code = ? WHERE easypay_provider_code = ?", +// null, +// "null" +// ) +// } +// +// jdbcTemplate.execute("DROP INDEX idx_payment_card_detail_easypay ON payment_card_detail") +// } +// } +// } +// +// private suspend fun processPaymentAndDefaultDetail(reservations: List) { +// val paymentBatchArgs = mutableListOf>() +// val detailBatchArgs = mutableListOf>() +// +// reservations.forEachIndexed { idx, reservations -> +// val id = idGenerator.create() +// val totalPrice = reservations.totalPrice +// paymentBatchArgs.add( +// arrayOf( +// id, +// reservations.reservationId, +// PaymentType.NORMAL.name, +// randomPaymentMethod(), +// randomString(length = 64), +// randomString(length = 20), +// totalPrice, +// PaymentStatus.DONE.name, +// requestedAtCache, +// approvedAtCache, +// ) +// ) +// if (paymentBatchArgs.size >= 1_000) { +// executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } +// } +// +// val suppliedAmount: Int = (totalPrice * 0.9).toInt() +// val vat: Int = (totalPrice - suppliedAmount) +// +// detailBatchArgs.add( +// arrayOf( +// idGenerator.create(), +// id, +// suppliedAmount, +// vat +// ) +// ) +// +// if (detailBatchArgs.size >= 1_000) { +// executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } +// } +// } +// +// if (paymentBatchArgs.isNotEmpty()) { +// executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } +// } +// if (detailBatchArgs.isNotEmpty()) { +// executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } +// } +// } +// +// private suspend fun processPaymentDetail(payments: List) { +// val transferBatchArgs = mutableListOf>() +// val cardBatchArgs = mutableListOf>() +// val easypayPrepaidBatchArgs = mutableListOf>() +// +// payments.forEach { payment -> +// val totalPrice = payment.totalPrice +// val randomDiscountAmount = +// if (totalPrice < 100 || Math.random() < 0.8) 0 else ((100..totalPrice).random() / 100) * 100 +// val amount = totalPrice - randomDiscountAmount +// +// when (payment.method) { +// PaymentMethod.TRANSFER -> { +// transferBatchArgs.add( +// arrayOf( +// payment.id, +// BankCode.entries.random().name, +// settlementStatus +// ) +// ) +// if (transferBatchArgs.size >= 1_000) { +// executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } +// } +// } +// +// PaymentMethod.EASY_PAY -> { +// if (Math.random() <= 0.7) { +// cardBatchArgs.add( +// arrayOf( +// payment.id, +// CardIssuerCode.entries.random().name, +// supportedCardType.random().name, +// CardOwnerType.PERSONAL.name, +// amount, +// randomCardNumber(), +// randomApprovalNumber(), +// randomInstallmentPlanMonths(amount), +// true, +// EasyPayCompanyCode.entries.random().name, +// randomDiscountAmount +// ) +// ) +// +// if (cardBatchArgs.size >= 1_000) { +// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } +// } +// +// } else { +// easypayPrepaidBatchArgs.add( +// arrayOf( +// payment.id, +// EasyPayCompanyCode.entries.random().name, +// amount, +// randomDiscountAmount, +// ) +// ) +// +// if (easypayPrepaidBatchArgs.size >= 1_000) { +// executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } +// } +// } +// } +// +// PaymentMethod.CARD -> { +// cardBatchArgs.add( +// arrayOf( +// payment.id, +// CardIssuerCode.entries.random().name, +// supportedCardType.random().name, +// CardOwnerType.PERSONAL.name, +// totalPrice, +// randomCardNumber(), +// randomApprovalNumber(), +// randomInstallmentPlanMonths(totalPrice), +// true, +// "null", +// 0, +// ) +// ) +// +// if (cardBatchArgs.size >= 1_000) { +// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } +// } +// } +// +// else -> return@forEach +// } +// } +// if (transferBatchArgs.isNotEmpty()) { +// executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } +// } +// if (cardBatchArgs.isNotEmpty()) { +// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } +// } +// if (easypayPrepaidBatchArgs.isNotEmpty()) { +// executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } +// } +// } +// +// private suspend fun randomPaymentMethod(): String { +// val random = Math.random() +// +// return if (random <= 0.5) { +// PaymentMethod.EASY_PAY.name +// } else if (random <= 0.9) { +// PaymentMethod.CARD.name +// } else { +// PaymentMethod.TRANSFER.name +// } +// } +// +// private suspend fun randomCardNumber(): String { +// return "${(10000000..99999999).random()}****${(100..999).random()}*" +// } +// +// private suspend fun randomApprovalNumber(): String { +// return "${(10000000..99999999).random()}" +// } +// +// private suspend fun randomInstallmentPlanMonths(amount: Int): Int { +// return if (amount < 50_000 || Math.random() < 0.9) { +// 0 +// } else { +// (1..6).random() +// } +// } +//} +// +//fun randomKoreanName(): String { +// val lastNames = listOf( +// "김", "이", "박", "최", "정", "강", "조", "윤", "장", "임", +// "오", "서", "신", "권", "황", "안", "송", "류", "홍", "전", "고", "문", "양", "손", "배", "백", "허", "유", "남", "심", "노" +// ) +// +// return "${lastNames.random()}${if (Math.random() < 0.1) randomKoreanWords(1) else randomKoreanWords(2)}" +//} +// +//fun randomKoreanWords(length: Int = 1): String { +// val words = listOf( +// "가", "나", "다", "라", "마", "바", "사", "아", "자", "차", +// "카", "타", "파", "하", "강", "민", "서", "윤", "우", "진", +// "현", "지", "은", "혜", "수", "영", "주", "원", "희", "경", +// "선", "아", "나", "다", "라", "마", "바", "사", "아", "자", +// "차", "카", "타", "파", "하", +// ) +// +// return (1..length).joinToString("") { words.random() } +//} diff --git a/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt index e51c3ca3..3d1969fa 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt @@ -1,18 +1,10 @@ package com.sangdol.roomescape.schedule -import io.kotest.assertions.assertSoftly -import io.kotest.matchers.date.shouldBeBefore -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe -import org.hamcrest.CoreMatchers.equalTo -import org.springframework.data.repository.findByIdOrNull -import org.springframework.http.HttpMethod +import com.sangdol.common.types.audit.Auditor import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType import com.sangdol.roomescape.auth.exception.AuthErrorCode -import com.sangdol.roomescape.common.dto.AuditConstant -import com.sangdol.roomescape.common.dto.OperatorInfo import com.sangdol.roomescape.schedule.exception.ScheduleErrorCode import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository @@ -21,6 +13,13 @@ import com.sangdol.roomescape.schedule.web.AdminScheduleSummaryResponse import com.sangdol.roomescape.schedule.web.ScheduleUpdateRequest import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity import com.sangdol.roomescape.supports.* +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.date.shouldBeBefore +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import org.hamcrest.CoreMatchers.equalTo +import org.springframework.data.repository.findByIdOrNull +import org.springframework.http.HttpMethod import java.time.LocalDate import java.time.LocalTime @@ -267,7 +266,7 @@ class AdminScheduleApiTest( val schedule: ScheduleEntity = initialize("감사 이력을 남기지 않기 위해, 로그인 없이 일정을 생성한다.") { dummyInitializer.createSchedule(storeId = store.id, request = ScheduleFixture.createRequest) } - val unknownOperator: OperatorInfo = AuditConstant.UNKNOWN_OPERATOR + val unknownOperator: Auditor = Auditor.UNKNOWN runTest( token = testAuthUtil.defaultHqAdminLogin().second, -- 2.47.2 From 51a0dab2b4ad248931d7d7fd7b1d79529ca205b7 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 22:31:41 +0900 Subject: [PATCH 18/39] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20log=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EC=9D=BC=EB=B6=80?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=8A=94=20=EC=9E=AC=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=84=B1=20=EA=B3=A0=EB=A0=A4=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/log/build.gradle.kts | 15 ++++ .../common/log/config}/LogConfiguration.kt | 5 +- .../com/sangdol/common/log/config/LogType.kt | 10 +++ .../message/AbstractLogMaskingConverter.kt | 78 ++++++++++++++++++ .../log/message}/ApiLogMessageConverter.kt | 14 +--- .../MDCAwareSlowQueryListenerWithoutParams.kt | 2 +- .../log/sql/SlowQueryDataSourceFactory.kt | 20 +++++ .../log/web}/ControllerLoggingAspect.kt | 8 +- .../log/web}/HttpRequestLoggingFilter.kt | 5 +- service/build.gradle.kts | 1 + .../common/log/ProxyDataSourceConfig.kt | 18 ++-- .../log/RoomescapeLogMaskingConverter.kt | 82 ++----------------- ...AwareSlowQueryListenerWithoutParamsTest.kt | 26 ------ 13 files changed, 153 insertions(+), 131 deletions(-) create mode 100644 common/log/build.gradle.kts rename {service/src/main/kotlin/com/sangdol/roomescape/common/log => common/log/src/main/kotlin/com/sangdol/common/log/config}/LogConfiguration.kt (85%) create mode 100644 common/log/src/main/kotlin/com/sangdol/common/log/config/LogType.kt create mode 100644 common/log/src/main/kotlin/com/sangdol/common/log/message/AbstractLogMaskingConverter.kt rename {service/src/main/kotlin/com/sangdol/roomescape/common/log => common/log/src/main/kotlin/com/sangdol/common/log/message}/ApiLogMessageConverter.kt (92%) rename {service/src/main/kotlin/com/sangdol/roomescape/common/log => common/log/src/main/kotlin/com/sangdol/common/log/sql}/MDCAwareSlowQueryListenerWithoutParams.kt (96%) create mode 100644 common/log/src/main/kotlin/com/sangdol/common/log/sql/SlowQueryDataSourceFactory.kt rename {service/src/main/kotlin/com/sangdol/roomescape/common/log => common/log/src/main/kotlin/com/sangdol/common/log/web}/ControllerLoggingAspect.kt (91%) rename {service/src/main/kotlin/com/sangdol/roomescape/common/log => common/log/src/main/kotlin/com/sangdol/common/log/web}/HttpRequestLoggingFilter.kt (93%) delete mode 100644 service/src/test/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt diff --git a/common/log/build.gradle.kts b/common/log/build.gradle.kts new file mode 100644 index 00000000..5087714b --- /dev/null +++ b/common/log/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("org.springframework.boot") + kotlin("plugin.spring") +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-aop") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("net.ttddyy.observation:datasource-micrometer-spring-boot:1.1.1") + + testImplementation("io.mockk:mockk:1.14.4") + + implementation(project(":common:utils")) +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/LogConfiguration.kt b/common/log/src/main/kotlin/com/sangdol/common/log/config/LogConfiguration.kt similarity index 85% rename from service/src/main/kotlin/com/sangdol/roomescape/common/log/LogConfiguration.kt rename to common/log/src/main/kotlin/com/sangdol/common/log/config/LogConfiguration.kt index bf20b06e..da4073e8 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/LogConfiguration.kt +++ b/common/log/src/main/kotlin/com/sangdol/common/log/config/LogConfiguration.kt @@ -1,6 +1,9 @@ -package com.sangdol.roomescape.common.log +package com.sangdol.common.log.config import com.fasterxml.jackson.databind.ObjectMapper +import com.sangdol.common.log.message.ApiLogMessageConverter +import com.sangdol.common.log.web.ControllerLoggingAspect +import com.sangdol.common.log.web.HttpRequestLoggingFilter import org.springframework.boot.web.servlet.FilterRegistrationBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration diff --git a/common/log/src/main/kotlin/com/sangdol/common/log/config/LogType.kt b/common/log/src/main/kotlin/com/sangdol/common/log/config/LogType.kt new file mode 100644 index 00000000..e39c85bb --- /dev/null +++ b/common/log/src/main/kotlin/com/sangdol/common/log/config/LogType.kt @@ -0,0 +1,10 @@ +package com.sangdol.common.log.config + +enum class LogType { + INCOMING_HTTP_REQUEST, + CONTROLLER_INVOKED, + CONTROLLER_SUCCESS, + AUTHENTICATION_FAILURE, + APPLICATION_FAILURE, + UNHANDLED_EXCEPTION +} diff --git a/common/log/src/main/kotlin/com/sangdol/common/log/message/AbstractLogMaskingConverter.kt b/common/log/src/main/kotlin/com/sangdol/common/log/message/AbstractLogMaskingConverter.kt new file mode 100644 index 00000000..42e117ef --- /dev/null +++ b/common/log/src/main/kotlin/com/sangdol/common/log/message/AbstractLogMaskingConverter.kt @@ -0,0 +1,78 @@ +package com.sangdol.common.log.message + +import ch.qos.logback.classic.pattern.MessageConverter +import ch.qos.logback.classic.spi.ILoggingEvent +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.node.ArrayNode +import com.fasterxml.jackson.databind.node.ObjectNode +import com.fasterxml.jackson.databind.node.TextNode + +abstract class AbstractLogMaskingConverter( + val sensitiveKeys: Set, + val objectMapper: ObjectMapper +) : MessageConverter() { + + val mask: String = "****" + + override fun convert(event: ILoggingEvent): String { + val message: String = event.formattedMessage + + return if (isJsonString(message)) { + maskedJsonString(message) + } else { + maskedPlainMessage(message) + } + } + + private fun isJsonString(message: String): Boolean { + val trimmed = message.trim() + + return trimmed.startsWith("{") && trimmed.endsWith("}") + } + + private fun maskedJsonString(body: String): String = objectMapper.readValue(body, JsonNode::class.java) + .apply { maskRecursive(this) } + .toString() + + private fun maskedPlainMessage(message: String): String { + val keys: String = sensitiveKeys.joinToString("|") + val regex = Regex("(?i)($keys)(\\s*[:=]\\s*)([^(,|\"|?)]+)") + + return regex.replace(message) { matchResult -> + val key = matchResult.groupValues[1] + val delimiter = matchResult.groupValues[2] + val maskedValue = maskValue(matchResult.groupValues[3].trim()) + + "${key}${delimiter}${maskedValue}" + } + } + + private fun maskRecursive(node: JsonNode?) { + node?.forEachEntry { key, childNode -> + when { + childNode.isValueNode -> { + if (key in sensitiveKeys) (node as ObjectNode).put(key, maskValue(childNode.asText())) + } + + childNode.isObject -> maskRecursive(childNode) + childNode.isArray -> { + val arrayNode = childNode as ArrayNode + val originSize = arrayNode.size() + if (originSize > 1) { + val first = arrayNode.first() + arrayNode.removeAll() + arrayNode.add(first) + arrayNode.add(TextNode("(...logged only first of $originSize elements)")) + } + + arrayNode.forEach { maskRecursive(it) } + } + } + } + } + + private fun maskValue(value: String): String { + return "${value.first()}$mask${value.last()}" + } +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverter.kt b/common/log/src/main/kotlin/com/sangdol/common/log/message/ApiLogMessageConverter.kt similarity index 92% rename from service/src/main/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverter.kt rename to common/log/src/main/kotlin/com/sangdol/common/log/message/ApiLogMessageConverter.kt index f9b7c87b..a8048ef2 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverter.kt +++ b/common/log/src/main/kotlin/com/sangdol/common/log/message/ApiLogMessageConverter.kt @@ -1,17 +1,9 @@ -package com.sangdol.roomescape.common.log +package com.sangdol.common.log.message import com.fasterxml.jackson.databind.ObjectMapper -import jakarta.servlet.http.HttpServletRequest +import com.sangdol.common.log.config.LogType import com.sangdol.common.utils.MdcPrincipalIdUtil - -enum class LogType { - INCOMING_HTTP_REQUEST, - CONTROLLER_INVOKED, - CONTROLLER_SUCCESS, - AUTHENTICATION_FAILURE, - APPLICATION_FAILURE, - UNHANDLED_EXCEPTION -} +import jakarta.servlet.http.HttpServletRequest class ApiLogMessageConverter( private val objectMapper: ObjectMapper diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParams.kt b/common/log/src/main/kotlin/com/sangdol/common/log/sql/MDCAwareSlowQueryListenerWithoutParams.kt similarity index 96% rename from service/src/main/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParams.kt rename to common/log/src/main/kotlin/com/sangdol/common/log/sql/MDCAwareSlowQueryListenerWithoutParams.kt index a6e979c5..ecd79d13 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParams.kt +++ b/common/log/src/main/kotlin/com/sangdol/common/log/sql/MDCAwareSlowQueryListenerWithoutParams.kt @@ -1,4 +1,4 @@ -package com.sangdol.roomescape.common.log +package com.sangdol.common.log.sql import net.ttddyy.dsproxy.ExecutionInfo import net.ttddyy.dsproxy.QueryInfo diff --git a/common/log/src/main/kotlin/com/sangdol/common/log/sql/SlowQueryDataSourceFactory.kt b/common/log/src/main/kotlin/com/sangdol/common/log/sql/SlowQueryDataSourceFactory.kt new file mode 100644 index 00000000..60467751 --- /dev/null +++ b/common/log/src/main/kotlin/com/sangdol/common/log/sql/SlowQueryDataSourceFactory.kt @@ -0,0 +1,20 @@ +package com.sangdol.common.log.sql + +import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel +import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder +import javax.sql.DataSource + +object SlowQueryDataSourceFactory { + + fun create(dataSource: DataSource, loggerName: String, logLevel: String, thresholdMs: Long): DataSource { + val mdcAwareListener = MDCAwareSlowQueryListenerWithoutParams( + logLevel = SLF4JLogLevel.nullSafeValueOf(logLevel.uppercase()), + thresholdMs = thresholdMs + ) + + return ProxyDataSourceBuilder.create(dataSource) + .name(loggerName) + .listener(mdcAwareListener) + .buildProxy() + } +} \ No newline at end of file diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/ControllerLoggingAspect.kt b/common/log/src/main/kotlin/com/sangdol/common/log/web/ControllerLoggingAspect.kt similarity index 91% rename from service/src/main/kotlin/com/sangdol/roomescape/common/log/ControllerLoggingAspect.kt rename to common/log/src/main/kotlin/com/sangdol/common/log/web/ControllerLoggingAspect.kt index c88b1abb..1b10df4c 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/ControllerLoggingAspect.kt +++ b/common/log/src/main/kotlin/com/sangdol/common/log/web/ControllerLoggingAspect.kt @@ -1,5 +1,9 @@ -package com.sangdol.roomescape.common.log +package com.sangdol.common.log.web +import com.sangdol.common.log.config.LogType +import com.sangdol.common.log.message.ApiLogMessageConverter +import com.sangdol.common.log.message.ConvertResponseMessageRequest +import com.sangdol.common.log.message.getEndpoint import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import jakarta.servlet.http.HttpServletRequest @@ -24,7 +28,7 @@ class ControllerLoggingAspect( private val messageConverter: ApiLogMessageConverter, ) { - @Pointcut("execution(* com.sangdol.roomescape..web..*Controller*.*(..))") + @Pointcut("execution(* com.sangdol..web..*Controller*.*(..))") fun allController() { } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/HttpRequestLoggingFilter.kt b/common/log/src/main/kotlin/com/sangdol/common/log/web/HttpRequestLoggingFilter.kt similarity index 93% rename from service/src/main/kotlin/com/sangdol/roomescape/common/log/HttpRequestLoggingFilter.kt rename to common/log/src/main/kotlin/com/sangdol/common/log/web/HttpRequestLoggingFilter.kt index bf72e30d..eccd372a 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/HttpRequestLoggingFilter.kt +++ b/common/log/src/main/kotlin/com/sangdol/common/log/web/HttpRequestLoggingFilter.kt @@ -1,5 +1,7 @@ -package com.sangdol.roomescape.common.log +package com.sangdol.common.log.web +import com.sangdol.common.log.message.ApiLogMessageConverter +import com.sangdol.common.utils.MdcPrincipalIdUtil import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import jakarta.servlet.FilterChain @@ -9,7 +11,6 @@ import org.slf4j.MDC import org.springframework.web.filter.OncePerRequestFilter import org.springframework.web.util.ContentCachingRequestWrapper import org.springframework.web.util.ContentCachingResponseWrapper -import com.sangdol.common.utils.MdcPrincipalIdUtil private val log: KLogger = KotlinLogging.logger {} diff --git a/service/build.gradle.kts b/service/build.gradle.kts index 8cbf1766..1dc77c5b 100644 --- a/service/build.gradle.kts +++ b/service/build.gradle.kts @@ -57,6 +57,7 @@ dependencies { implementation(project(":common:persistence")) implementation(project(":common:utils")) implementation(project(":common:types")) + implementation(project(":common:log")) } tasks.jar { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/ProxyDataSourceConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/ProxyDataSourceConfig.kt index e6c7f117..11b32ea6 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/ProxyDataSourceConfig.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/ProxyDataSourceConfig.kt @@ -1,8 +1,7 @@ package com.sangdol.roomescape.common.log +import com.sangdol.common.log.sql.SlowQueryDataSourceFactory import com.zaxxer.hikari.HikariDataSource -import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel -import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder import org.springframework.beans.factory.annotation.Qualifier import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.context.properties.EnableConfigurationProperties @@ -23,15 +22,12 @@ class ProxyDataSourceConfig { fun dataSource( @Qualifier("actualDataSource") actualDataSource: DataSource, properties: SlowQueryProperties - ): DataSource = ProxyDataSourceBuilder.create(actualDataSource) - .name(properties.loggerName) - .listener( - MDCAwareSlowQueryListenerWithoutParams( - logLevel = SLF4JLogLevel.nullSafeValueOf(properties.logLevel.uppercase()), - thresholdMs = properties.thresholdMs - ) - ) - .buildProxy() + ): DataSource = SlowQueryDataSourceFactory.create( + dataSource = actualDataSource, + loggerName = properties.loggerName, + logLevel = properties.logLevel, + thresholdMs = properties.thresholdMs + ) @Bean @ConfigurationProperties(prefix = "spring.datasource.hikari") diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt index 70abcc43..d5077d89 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt @@ -1,81 +1,9 @@ package com.sangdol.roomescape.common.log -import ch.qos.logback.classic.pattern.MessageConverter -import ch.qos.logback.classic.spi.ILoggingEvent -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.node.ArrayNode -import com.fasterxml.jackson.databind.node.ObjectNode -import com.fasterxml.jackson.databind.node.TextNode import com.sangdol.common.config.JacksonConfig +import com.sangdol.common.log.message.AbstractLogMaskingConverter -private const val MASK: String = "****" -private val SENSITIVE_KEYS = setOf("password", "accessToken", "phone") -private val objectMapper: ObjectMapper = JacksonConfig().objectMapper() - -class RoomescapeLogMaskingConverter : MessageConverter() { - override fun convert(event: ILoggingEvent): String { - val message: String = event.formattedMessage - - return if (isJsonString(message)) { - maskedJsonString(message) - } else { - maskedPlainMessage(message) - } - } - - private fun isJsonString(message: String): Boolean { - val trimmed = message.trim() - - return trimmed.startsWith("{") && trimmed.endsWith("}") - } - - private fun maskedJsonString(body: String): String = objectMapper.readValue(body, JsonNode::class.java) - .apply { maskRecursive(this) } - .toString() - - private fun maskedPlainMessage(message: String): String { - val keys: String = SENSITIVE_KEYS.joinToString("|") - val regex = Regex("(?i)($keys)(\\s*=\\s*)([^(,|\"|?)\\s]+)") - - return regex.replace(message) { matchResult -> - val key = matchResult.groupValues[1] - val delimiter = matchResult.groupValues[2] - val maskedValue = maskValue(matchResult.groupValues[3]) - - "${key}${delimiter}${maskedValue}" - } - } - - private fun maskRecursive(node: JsonNode?) { - node?.forEachEntry { key, childNode -> - when { - childNode.isValueNode -> { - if (key in SENSITIVE_KEYS) (node as ObjectNode).put(key, maskValue(childNode.asText())) - } - - childNode.isObject -> maskRecursive(childNode) - childNode.isArray -> { - val arrayNode = childNode as ArrayNode - val originSize = arrayNode.size() - if (originSize > 1) { - val first = arrayNode.first() - arrayNode.removeAll() - arrayNode.add(first) - arrayNode.add(TextNode("(...logged only first of $originSize elements)")) - } - - arrayNode.forEach { maskRecursive(it) } - } - } - } - } - - private fun maskValue(value: String): String { - return if (value.length <= 2) { - MASK - } else { - "${value.first()}$MASK${value.last()}" - } - } -} +class RoomescapeLogMaskingConverter: AbstractLogMaskingConverter( + sensitiveKeys = setOf("password", "accessToken", "phone"), + objectMapper = JacksonConfig().objectMapper() +) diff --git a/service/src/test/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt deleted file mode 100644 index 488de01a..00000000 --- a/service/src/test/kotlin/com/sangdol/roomescape/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.sangdol.roomescape.common.log - -import io.kotest.assertions.assertSoftly -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe - -class MDCAwareSlowQueryListenerWithoutParamsTest : StringSpec({ - "SQL 메시지에서 Params 항목은 가린다." { - val message = """Query:["select * from members m where m.email=?"], Params:[(a@a.a)]""" - val expected = """Query:["select * from members m where m.email=?"]""" - val result = SqlLogFormatter().maskParams(message) - - result shouldBe expected - } - - "입력된 thresholdMs 보다 소요시간이 긴 쿼리를 기록한다." { - val slowQueryThreshold = 10L - val slowQueryPredicate = SlowQueryPredicate(thresholdMs = slowQueryThreshold) - - assertSoftly(slowQueryPredicate) { - it.test(slowQueryThreshold) shouldBe true - it.test(slowQueryThreshold + 1) shouldBe true - it.test(slowQueryThreshold - 1) shouldBe false - } - } -}) -- 2.47.2 From 33406fbc9383705d863f616792a6085be0379c4e Mon Sep 17 00:00:00 2001 From: pricelees Date: Sat, 27 Sep 2025 22:31:56 +0900 Subject: [PATCH 19/39] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20log=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EC=9D=BC=EB=B6=80?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=8A=94=20=EC=9E=AC=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=84=B1=20=EA=B3=A0=EB=A0=A4=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/AbstractLogMaskingConverterTest.kt | 55 +++++++++++++ .../common/log/ApiLogMessageConverterTest.kt | 15 ++-- ...AwareSlowQueryListenerWithoutParamsTest.kt | 28 +++++++ .../log/RoomescapeLogMaskingConverterTest.kt | 77 ------------------- 4 files changed, 91 insertions(+), 84 deletions(-) create mode 100644 common/log/src/test/kotlin/com/sangdol/common/log/AbstractLogMaskingConverterTest.kt rename {service/src/test/kotlin/com/sangdol/roomescape => common/log/src/test/kotlin/com/sangdol}/common/log/ApiLogMessageConverterTest.kt (82%) create mode 100644 common/log/src/test/kotlin/com/sangdol/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt delete mode 100644 service/src/test/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt diff --git a/common/log/src/test/kotlin/com/sangdol/common/log/AbstractLogMaskingConverterTest.kt b/common/log/src/test/kotlin/com/sangdol/common/log/AbstractLogMaskingConverterTest.kt new file mode 100644 index 00000000..eeb74b46 --- /dev/null +++ b/common/log/src/test/kotlin/com/sangdol/common/log/AbstractLogMaskingConverterTest.kt @@ -0,0 +1,55 @@ +package com.sangdol.common.log + +import ch.qos.logback.classic.spi.ILoggingEvent +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.sangdol.common.log.message.AbstractLogMaskingConverter +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.equals.shouldBeEqual +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import io.mockk.every +import io.mockk.mockk + +class TestLogMaskingConverter : AbstractLogMaskingConverter( + sensitiveKeys = setOf("account", "address"), + objectMapper = jacksonObjectMapper() +) + +class AbstractLogMaskingConverterTest : FunSpec({ + + val converter = TestLogMaskingConverter() + val event: ILoggingEvent = mockk() + val account = "sangdol@example.com" + val address = "서울특별시 강북구 수유1동 123-456" + + context("sensitiveKeys=${converter.sensitiveKeys}에 있는 항목은 가린다.") { + context("평문 로그를 처리할 때, 여러 key / value가 있는 경우 서로 간의 구분자는 trim 처리한다.") { + listOf(":", "=", " : ", " = ").forEach { keyValueDelimiter -> + listOf(",", ", ").forEach { valueDelimiter -> + test("key1${keyValueDelimiter}value1${valueDelimiter}key2${keyValueDelimiter}value2 형식을 처리한다.") { + every { + event.formattedMessage + } returns "account$keyValueDelimiter$account${valueDelimiter}address$keyValueDelimiter$address" + + assertSoftly(converter.convert(event)) { + this shouldBe "account${keyValueDelimiter}${account.first()}${converter.mask}${account.last()}${valueDelimiter}address${keyValueDelimiter}${address.first()}${converter.mask}${address.last()}" + } + } + } + } + } + + context("JSON 로그") { + test("정상 처리") { + val json = "{\"request_body\":{\"account\":\"%s\",\"address\":\"%s\"}}" + + every { + event.formattedMessage + } returns json.format(account, address) + + converter.convert(event) shouldBeEqual json.format("${account.first()}${converter.mask}${account.last()}", "${address.first()}${converter.mask}${address.last()}") + } + } + } +}) diff --git a/service/src/test/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverterTest.kt b/common/log/src/test/kotlin/com/sangdol/common/log/ApiLogMessageConverterTest.kt similarity index 82% rename from service/src/test/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverterTest.kt rename to common/log/src/test/kotlin/com/sangdol/common/log/ApiLogMessageConverterTest.kt index cc8eec20..8e76c8d0 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/common/log/ApiLogMessageConverterTest.kt +++ b/common/log/src/test/kotlin/com/sangdol/common/log/ApiLogMessageConverterTest.kt @@ -1,8 +1,9 @@ -package com.sangdol.roomescape.common.log +package com.sangdol.common.log -import com.sangdol.common.config.JacksonConfig -import com.sangdol.roomescape.auth.exception.AuthErrorCode -import com.sangdol.roomescape.auth.exception.AuthException +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.sangdol.common.log.config.LogType +import com.sangdol.common.log.message.ApiLogMessageConverter +import com.sangdol.common.log.message.ConvertResponseMessageRequest import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.mockk.every @@ -11,7 +12,7 @@ import jakarta.servlet.http.HttpServletRequest import org.slf4j.MDC class ApiLogMessageConverterTest : StringSpec({ - val converter = ApiLogMessageConverter(JacksonConfig().objectMapper()) + val converter = ApiLogMessageConverter(jacksonObjectMapper()) val request: HttpServletRequest = mockk() beforeTest { @@ -58,11 +59,11 @@ class ApiLogMessageConverterTest : StringSpec({ type = LogType.CONTROLLER_SUCCESS, endpoint = endpoint, httpStatus = 200, - exception = AuthException(AuthErrorCode.MEMBER_NOT_FOUND, "테스트 메시지!") + exception = RuntimeException("테스트 메시지!") ) converter.convertToResponseMessage(request) shouldBe """ - {"type":"CONTROLLER_SUCCESS","endpoint":"$endpoint","status_code":200,"principal_id":1,"exception":{"class":"AuthException","message":"테스트 메시지!"}} + {"type":"CONTROLLER_SUCCESS","endpoint":"$endpoint","status_code":200,"principal_id":1,"exception":{"class":"RuntimeException","message":"테스트 메시지!"}} """.trimIndent() } }) diff --git a/common/log/src/test/kotlin/com/sangdol/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt b/common/log/src/test/kotlin/com/sangdol/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt new file mode 100644 index 00000000..2d160784 --- /dev/null +++ b/common/log/src/test/kotlin/com/sangdol/common/log/MDCAwareSlowQueryListenerWithoutParamsTest.kt @@ -0,0 +1,28 @@ +package com.sangdol.common.log + +import com.sangdol.common.log.sql.SlowQueryPredicate +import com.sangdol.common.log.sql.SqlLogFormatter +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class MDCAwareSlowQueryListenerWithoutParamsTest : StringSpec({ + "SQL 메시지에서 Params 항목은 가린다." { + val message = """Query:["select * from members m where m.email=?"], Params:[(a@a.a)]""" + val expected = """Query:["select * from members m where m.email=?"]""" + val result = SqlLogFormatter().maskParams(message) + + result shouldBe expected + } + + "입력된 thresholdMs 보다 소요시간이 긴 쿼리를 기록한다." { + val slowQueryThreshold = 10L + val slowQueryPredicate = SlowQueryPredicate(thresholdMs = slowQueryThreshold) + + assertSoftly(slowQueryPredicate) { + this.test(slowQueryThreshold) shouldBe true + this.test(slowQueryThreshold + 1) shouldBe true + this.test(slowQueryThreshold - 1) shouldBe false + } + } +}) diff --git a/service/src/test/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt deleted file mode 100644 index 2db3f5f7..00000000 --- a/service/src/test/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverterTest.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.sangdol.roomescape.common.log - -import ch.qos.logback.classic.spi.ILoggingEvent -import io.kotest.assertions.assertSoftly -import io.kotest.core.spec.style.FunSpec -import io.kotest.matchers.equals.shouldBeEqual -import io.kotest.matchers.string.shouldContain -import io.mockk.every -import io.mockk.mockk - -class RoomescapeLogMaskingConverterTest : FunSpec({ - - val converter = RoomescapeLogMaskingConverter() - val event: ILoggingEvent = mockk() - - context("평문 로그에서는 key=value 형식을 처리한다.") { - - test("2글자 초과이면 맨 앞, 맨 뒤를 제외한 나머지를 가린다.") { - val email = "a@a.a" - val password = "password12" - val accessToken = "accessToken12" - - every { - event.formattedMessage - } returns "email=${email}, password=${password}, accessToken = $accessToken" - - assertSoftly(converter.convert(event)) { - this shouldContain "email=${email}" - this shouldContain "password=${password.first()}****${password.last()}" - this shouldContain "accessToken = ${accessToken.first()}****${accessToken.last()}" - } - - } - - test("2글자 이하이면 전부 가린다.") { - val email = "a@a.a" - val password = "pa" - val accessToken = "a" - - every { - event.formattedMessage - } returns "email=${email}, password=${password}, accessToken = ${accessToken}" - - assertSoftly(converter.convert(event)) { - this shouldContain "email=${email}" - this shouldContain "password=****" - this shouldContain "accessToken = ****" - } - } - } - - context("JSON 형식 로그를 처리한다.") { - val json = "{\"request_body\":{\"email\":\"a@a.a\",\"password\":\"password12\"}}" - - test("2글자 초과이면 맨 앞, 맨 뒤를 제외한 나머지를 가린다.") { - val password = "password12" - val json = "{\"request_body\":{\"email\":\"a@a.a\",\"password\":\"${password}\"}}" - - every { - event.formattedMessage - } returns json - - converter.convert(event) shouldBeEqual "{\"request_body\":{\"email\":\"a@a.a\",\"password\":\"${password.first()}****${password.last()}\"}}" - } - - test("2글자 이하이면 전부 가린다.") { - val password = "pa" - val json = "{\"request_body\":{\"email\":\"a@a.a\",\"password\":\"${password}\"}}" - - every { - event.formattedMessage - } returns json - - converter.convert(event) shouldBeEqual "{\"request_body\":{\"email\":\"a@a.a\",\"password\":\"****\"}}" - } - } -}) -- 2.47.2 From 152367cafb6155966c3109ed8d55e8bcc617210c Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:04:05 +0900 Subject: [PATCH 20/39] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=EC=9D=98=20dependencies=20=EC=A0=95=EB=A6=AC?= =?UTF-8?q?=20=EB=B0=8F=20jar=20=EC=84=A4=EC=A0=95=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/config/build.gradle.kts | 11 ----------- common/log/build.gradle.kts | 15 ++++++--------- common/persistence/build.gradle.kts | 11 +++++++++++ common/types/build.gradle.kts | 3 +++ common/utils/build.gradle.kts | 6 ++++-- common/web/build.gradle.kts | 28 ++++++++++++++++++++++++++++ service/build.gradle.kts | 8 +++----- 7 files changed, 55 insertions(+), 27 deletions(-) delete mode 100644 common/config/build.gradle.kts create mode 100644 common/types/build.gradle.kts create mode 100644 common/web/build.gradle.kts diff --git a/common/config/build.gradle.kts b/common/config/build.gradle.kts deleted file mode 100644 index e96cfeba..00000000 --- a/common/config/build.gradle.kts +++ /dev/null @@ -1,11 +0,0 @@ -plugins { - id("org.springframework.boot") - kotlin("plugin.spring") -} - -dependencies { - implementation("org.springframework.boot:spring-boot-starter-web") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin") - - testImplementation("io.kotest:kotest-runner-junit5:5.9.1") -} diff --git a/common/log/build.gradle.kts b/common/log/build.gradle.kts index 5087714b..7146ccca 100644 --- a/common/log/build.gradle.kts +++ b/common/log/build.gradle.kts @@ -1,15 +1,12 @@ -plugins { - id("org.springframework.boot") - kotlin("plugin.spring") -} - dependencies { - implementation("org.springframework.boot:spring-boot-starter-web") - implementation("org.springframework.boot:spring-boot-starter-aop") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin") - implementation("net.ttddyy.observation:datasource-micrometer-spring-boot:1.1.1") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.20.0") + implementation("net.ttddyy.observation:datasource-micrometer-spring-boot:1.1.2") testImplementation("io.mockk:mockk:1.14.4") implementation(project(":common:utils")) } + +tasks.named("jar") { + enabled = true +} diff --git a/common/persistence/build.gradle.kts b/common/persistence/build.gradle.kts index c1afa586..fe90c4d5 100644 --- a/common/persistence/build.gradle.kts +++ b/common/persistence/build.gradle.kts @@ -1,3 +1,6 @@ +import org.gradle.kotlin.dsl.named +import org.springframework.boot.gradle.tasks.bundling.BootJar + plugins { id("org.springframework.boot") kotlin("plugin.spring") @@ -15,3 +18,11 @@ dependencies { implementation(project(":common:utils")) } + +tasks.named("bootJar") { + enabled = false +} + +tasks.named("jar") { + enabled = true +} \ No newline at end of file diff --git a/common/types/build.gradle.kts b/common/types/build.gradle.kts new file mode 100644 index 00000000..469d6467 --- /dev/null +++ b/common/types/build.gradle.kts @@ -0,0 +1,3 @@ +tasks.named("jar") { + enabled = true +} \ No newline at end of file diff --git a/common/utils/build.gradle.kts b/common/utils/build.gradle.kts index ad519ea1..d725bc73 100644 --- a/common/utils/build.gradle.kts +++ b/common/utils/build.gradle.kts @@ -1,5 +1,7 @@ dependencies { implementation("org.slf4j:slf4j-api:2.0.17") +} - testImplementation("ch.qos.logback:logback-classic:1.5.18") -} \ No newline at end of file +tasks.named("jar") { + enabled = true +} diff --git a/common/web/build.gradle.kts b/common/web/build.gradle.kts new file mode 100644 index 00000000..53c7f14b --- /dev/null +++ b/common/web/build.gradle.kts @@ -0,0 +1,28 @@ +import org.gradle.kotlin.dsl.named +import org.springframework.boot.gradle.tasks.bundling.BootJar + +plugins { + id("org.springframework.boot") + kotlin("plugin.spring") +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-aop") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + + testImplementation("io.kotest:kotest-runner-junit5:5.9.1") + testImplementation("io.mockk:mockk:1.14.4") + + implementation(project(":common:log")) + implementation(project(":common:utils")) + implementation(project(":common:types")) +} + +tasks.named("bootJar") { + enabled = false +} + +tasks.named("jar") { + enabled = true +} diff --git a/service/build.gradle.kts b/service/build.gradle.kts index 1dc77c5b..6fea963e 100644 --- a/service/build.gradle.kts +++ b/service/build.gradle.kts @@ -11,7 +11,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-validation") // API docs - implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9") + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.13") // DB implementation("com.github.f4b6a3:tsid-creator:5.2.6") @@ -22,7 +22,6 @@ dependencies { implementation("io.jsonwebtoken:jjwt:0.12.6") // Logging - implementation("io.github.oshai:kotlin-logging-jvm:7.0.3") implementation("net.logstash.logback:logstash-logback-encoder:8.1") implementation("com.github.loki4j:loki-logback-appender:2.0.0") implementation("net.ttddyy.observation:datasource-micrometer-spring-boot:1.1.1") @@ -35,7 +34,6 @@ dependencies { // Kotlin implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("io.github.oshai:kotlin-logging-jvm:7.0.3") // Test testImplementation("org.springframework.boot:spring-boot-starter-test") @@ -53,13 +51,13 @@ dependencies { implementation("org.apache.poi:poi-ooxml:5.2.3") // submodules - implementation(project(":common:config")) implementation(project(":common:persistence")) implementation(project(":common:utils")) implementation(project(":common:types")) implementation(project(":common:log")) + implementation(project(":common:web")) } -tasks.jar { +tasks.named("jar") { enabled = false } -- 2.47.2 From 9c4d75be2e1a08b8e7c8a6520e7998f301b3478b Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:07:09 +0900 Subject: [PATCH 21/39] =?UTF-8?q?refactor:=20config=20->=20web=20=EC=84=9C?= =?UTF-8?q?=EB=B8=8C=EB=AA=A8=EB=93=88=20=EC=9D=B4=EB=A6=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=EC=9D=B4=EB=A1=9C=20=EC=9D=B8=ED=95=9C?= =?UTF-8?q?=20log=20=EB=AA=A8=EB=93=88=EC=97=90=EC=84=9C=EC=9D=98=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=EB=AA=85=20=EC=88=98=EC=A0=95(confi?= =?UTF-8?q?g=20->=20constant)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 1 + .../sangdol/common/log/{config => constant}/LogType.kt | 5 ++--- .../com/sangdol/common/web}/config/JacksonConfig.kt | 10 +++++++--- .../sangdol/common/web}/config/JacksonConfigTest.kt | 4 ++-- .../common/log/RoomescapeLogMaskingConverter.kt | 2 +- .../sangdol/roomescape/supports/RestAssuredUtils.kt | 2 +- 6 files changed, 14 insertions(+), 10 deletions(-) rename common/log/src/main/kotlin/com/sangdol/common/log/{config => constant}/LogType.kt (57%) rename common/{config/src/main/kotlin/com/sangdol/common => web/src/main/kotlin/com/sangdol/common/web}/config/JacksonConfig.kt (94%) rename common/{config/src/test/kotlin/com/sangdol/common => web/src/test/kotlin/com/sangdol/common/web}/config/JacksonConfigTest.kt (98%) diff --git a/build.gradle.kts b/build.gradle.kts index e46b82b4..5f4b11d5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,6 +39,7 @@ subprojects { dependencies { add("implementation", "io.github.oshai:kotlin-logging-jvm:7.0.3") add("implementation", "io.kotest:kotest-runner-junit5:5.9.1") + add("implementation", "ch.qos.logback:logback-classic:1.5.18") } tasks.withType { diff --git a/common/log/src/main/kotlin/com/sangdol/common/log/config/LogType.kt b/common/log/src/main/kotlin/com/sangdol/common/log/constant/LogType.kt similarity index 57% rename from common/log/src/main/kotlin/com/sangdol/common/log/config/LogType.kt rename to common/log/src/main/kotlin/com/sangdol/common/log/constant/LogType.kt index e39c85bb..8717b69c 100644 --- a/common/log/src/main/kotlin/com/sangdol/common/log/config/LogType.kt +++ b/common/log/src/main/kotlin/com/sangdol/common/log/constant/LogType.kt @@ -1,10 +1,9 @@ -package com.sangdol.common.log.config +package com.sangdol.common.log.constant enum class LogType { INCOMING_HTTP_REQUEST, CONTROLLER_INVOKED, - CONTROLLER_SUCCESS, - AUTHENTICATION_FAILURE, + SUCCEED, APPLICATION_FAILURE, UNHANDLED_EXCEPTION } diff --git a/common/config/src/main/kotlin/com/sangdol/common/config/JacksonConfig.kt b/common/web/src/main/kotlin/com/sangdol/common/web/config/JacksonConfig.kt similarity index 94% rename from common/config/src/main/kotlin/com/sangdol/common/config/JacksonConfig.kt rename to common/web/src/main/kotlin/com/sangdol/common/web/config/JacksonConfig.kt index 4dc73d80..fb6eb907 100644 --- a/common/config/src/main/kotlin/com/sangdol/common/config/JacksonConfig.kt +++ b/common/web/src/main/kotlin/com/sangdol/common/web/config/JacksonConfig.kt @@ -1,4 +1,4 @@ -package com.sangdol.common.config +package com.sangdol.common.web.config import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.DeserializationFeature @@ -14,7 +14,11 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer import com.fasterxml.jackson.module.kotlin.kotlinModule import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import java.time.* +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.ZoneId import java.time.format.DateTimeFormatter @Configuration @@ -83,4 +87,4 @@ class JacksonConfig { gen.writeString(value.format(ISO_OFFSET_DATE_TIME_FORMATTER)) } } -} +} \ No newline at end of file diff --git a/common/config/src/test/kotlin/com/sangdol/common/config/JacksonConfigTest.kt b/common/web/src/test/kotlin/com/sangdol/common/web/config/JacksonConfigTest.kt similarity index 98% rename from common/config/src/test/kotlin/com/sangdol/common/config/JacksonConfigTest.kt rename to common/web/src/test/kotlin/com/sangdol/common/web/config/JacksonConfigTest.kt index b98b02fa..19af86d1 100644 --- a/common/config/src/test/kotlin/com/sangdol/common/config/JacksonConfigTest.kt +++ b/common/web/src/test/kotlin/com/sangdol/common/web/config/JacksonConfigTest.kt @@ -1,4 +1,4 @@ -package com.sangdol.common.config +package com.sangdol.common.web.config import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.exc.InvalidFormatException @@ -89,4 +89,4 @@ class JacksonConfigTest : FunSpec({ serialized shouldBe "\"2025-07-14T12:30:00+09:00\"" } } -}) +}) \ No newline at end of file diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt index d5077d89..e5c90829 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.common.log -import com.sangdol.common.config.JacksonConfig +import com.sangdol.common.web.config.JacksonConfig import com.sangdol.common.log.message.AbstractLogMaskingConverter class RoomescapeLogMaskingConverter: AbstractLogMaskingConverter( diff --git a/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt index 9e7fc01e..af53b62e 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/RestAssuredUtils.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.supports -import com.sangdol.common.config.JacksonConfig +import com.sangdol.common.web.config.JacksonConfig import com.sangdol.common.types.exception.ErrorCode import io.restassured.module.kotlin.extensions.Given import io.restassured.module.kotlin.extensions.Then -- 2.47.2 From be19e57b61897026d4419f8e43c7e823f562480f Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:12:47 +0900 Subject: [PATCH 22/39] =?UTF-8?q?feat:=20StartTime=20=EA=B8=B0=EB=A1=9D?= =?UTF-8?q?=EC=9A=A9=20MDC=20=EC=9C=A0=ED=8B=B8=20=EB=B0=8F=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sangdol/common/utils/MdcStartTimeUtil.kt | 25 ++++++++++++++ .../common/utils/MdcPrincipalIdUtilTest.kt | 2 +- .../common/utils/MdcStartTimeUtilTest.kt | 34 +++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 common/utils/src/main/kotlin/com/sangdol/common/utils/MdcStartTimeUtil.kt create mode 100644 common/utils/src/test/kotlin/com/sangdol/common/utils/MdcStartTimeUtilTest.kt diff --git a/common/utils/src/main/kotlin/com/sangdol/common/utils/MdcStartTimeUtil.kt b/common/utils/src/main/kotlin/com/sangdol/common/utils/MdcStartTimeUtil.kt new file mode 100644 index 00000000..0e15ecfe --- /dev/null +++ b/common/utils/src/main/kotlin/com/sangdol/common/utils/MdcStartTimeUtil.kt @@ -0,0 +1,25 @@ +package com.sangdol.common.utils + +import org.slf4j.MDC + +object MdcStartTimeUtil { + const val MDC_START_TIME_KEY = "start_time" + + fun extractDurationMsOrNull(): Long? { + return extractOrNull()?.let { System.currentTimeMillis() - it } + } + + fun setCurrentTime() { + extractOrNull() ?: run { + MDC.put(MDC_START_TIME_KEY, System.currentTimeMillis().toString()) + } + } + + fun clear() { + MDC.remove(MDC_START_TIME_KEY) + } + + private fun extractOrNull(): Long? { + return MDC.get(MDC_START_TIME_KEY)?.toLong() + } +} diff --git a/common/utils/src/test/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtilTest.kt b/common/utils/src/test/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtilTest.kt index 388c61d8..043d4646 100644 --- a/common/utils/src/test/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtilTest.kt +++ b/common/utils/src/test/kotlin/com/sangdol/common/utils/MdcPrincipalIdUtilTest.kt @@ -25,4 +25,4 @@ class MdcPrincipalIdUtilTest : StringSpec({ MdcPrincipalIdUtil.extractAsOptionalLongOrEmpty() shouldBe Optional.empty() } } -}) \ No newline at end of file +}) diff --git a/common/utils/src/test/kotlin/com/sangdol/common/utils/MdcStartTimeUtilTest.kt b/common/utils/src/test/kotlin/com/sangdol/common/utils/MdcStartTimeUtilTest.kt new file mode 100644 index 00000000..0f57e753 --- /dev/null +++ b/common/utils/src/test/kotlin/com/sangdol/common/utils/MdcStartTimeUtilTest.kt @@ -0,0 +1,34 @@ +package com.sangdol.common.utils + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe + +class MdcStartTimeUtilTest : FunSpec({ + + test("기존에 등록된 startTime 값을 기준으로 duration_ms를 구한다.") { + MdcStartTimeUtil.setCurrentTime() + MdcStartTimeUtil.extractDurationMsOrNull().shouldNotBeNull() + MdcStartTimeUtil.clear() + } + + test("기존에 등록된 startTime 값이 없으면 duration_ms는 null이다.") { + MdcStartTimeUtil.extractDurationMsOrNull() shouldBe null + } + + test("현재 시간을 등록한다.") { + MdcStartTimeUtil.setCurrentTime() + MdcStartTimeUtil.extractDurationMsOrNull().shouldNotBeNull() + MdcStartTimeUtil.clear() + } + + test("등록된 시간을 지운다.") { + MdcStartTimeUtil.setCurrentTime().also { + MdcStartTimeUtil.extractDurationMsOrNull().shouldNotBeNull() + } + + MdcStartTimeUtil.clear().also { + MdcStartTimeUtil.extractDurationMsOrNull() shouldBe null + } + } +}) -- 2.47.2 From 6cd269e772f29c58eff24f85418bdf0108bc10a0 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:14:12 +0900 Subject: [PATCH 23/39] =?UTF-8?q?feat:=20=EA=B8=B0=EC=A1=B4=20=EC=9B=B9=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8D=98=20payload=20map=EC=9D=84=20=EB=B9=8C?= =?UTF-8?q?=EB=8D=94=20=ED=98=95=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EB=A7=8C?= =?UTF-8?q?=EB=93=9C=EB=8A=94=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=B0=8F=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/support/log/LogPayloadBuilder.kt | 75 ++++++ .../web/support/log/LogPayloadBuilderTest.kt | 233 ++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 common/web/src/main/kotlin/com/sangdol/common/web/support/log/LogPayloadBuilder.kt create mode 100644 common/web/src/test/kotlin/com/sangdol/common/web/support/log/LogPayloadBuilderTest.kt diff --git a/common/web/src/main/kotlin/com/sangdol/common/web/support/log/LogPayloadBuilder.kt b/common/web/src/main/kotlin/com/sangdol/common/web/support/log/LogPayloadBuilder.kt new file mode 100644 index 00000000..03dbe179 --- /dev/null +++ b/common/web/src/main/kotlin/com/sangdol/common/web/support/log/LogPayloadBuilder.kt @@ -0,0 +1,75 @@ +package com.sangdol.common.web.support.log + +import com.sangdol.common.log.constant.LogType +import com.sangdol.common.utils.MdcPrincipalIdUtil +import com.sangdol.common.utils.MdcStartTimeUtil +import jakarta.servlet.http.HttpServletRequest + +class LogPayloadBuilder( + private val type: LogType, + private val servletRequest: HttpServletRequest, + private val payload: MutableMap = mutableMapOf("type" to type) +) { + fun endpoint(): LogPayloadBuilder { + payload["endpoint"] = "${servletRequest.method} ${servletRequest.requestURI}" + return this + } + + fun clientIp(): LogPayloadBuilder { + servletRequest.remoteAddr?.let { payload["client_ip"] = it } + return this + } + + fun userAgent(): LogPayloadBuilder { + servletRequest.getHeader("User-Agent")?.let { payload["user_agent"] = it } + return this + } + + fun queryString(): LogPayloadBuilder { + servletRequest.queryString?.let { payload["query_params"] = it } + return this + } + + fun httpStatus(statusCode: Int?): LogPayloadBuilder { + statusCode?.let { payload["status_code"] = it } + + return this + } + + fun responseBody(body: Any?): LogPayloadBuilder { + body?.let { payload["response_body"] = it } + + return this + } + + fun durationMs(): LogPayloadBuilder { + MdcStartTimeUtil.extractDurationMsOrNull()?.let { payload["duration_ms"] = it } + return this + } + + fun principalId(): LogPayloadBuilder { + MdcPrincipalIdUtil.extractAsLongOrNull() + ?.let { payload["principal_id"] = it } + ?: run { payload["principal_id"] = "UNKNOWN" } + + return this + } + + fun exception(exception: Exception?): LogPayloadBuilder { + exception?.let { + payload["exception"] = mapOf( + "class" to it.javaClass.simpleName, + "message" to it.message + ) + } + + return this + } + + fun additionalPayloads(payload: Map): LogPayloadBuilder { + this.payload.putAll(payload) + return this + } + + fun build(): Map = payload +} diff --git a/common/web/src/test/kotlin/com/sangdol/common/web/support/log/LogPayloadBuilderTest.kt b/common/web/src/test/kotlin/com/sangdol/common/web/support/log/LogPayloadBuilderTest.kt new file mode 100644 index 00000000..0b905efa --- /dev/null +++ b/common/web/src/test/kotlin/com/sangdol/common/web/support/log/LogPayloadBuilderTest.kt @@ -0,0 +1,233 @@ +package com.sangdol.common.web.support.log + +import com.sangdol.common.log.constant.LogType +import com.sangdol.common.utils.MdcPrincipalIdUtil +import com.sangdol.common.utils.MdcStartTimeUtil +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.mockk.clearMocks +import io.mockk.every +import io.mockk.mockk +import jakarta.servlet.http.HttpServletRequest + +class LogPayloadBuilderTest : FunSpec({ + val servletRequest: HttpServletRequest = mockk() + + lateinit var method: String + lateinit var requestUri: String + lateinit var remoteAddr: String + lateinit var userAgent: String + lateinit var queryString: String + + beforeTest { + method = "GET".also { every { servletRequest.method } returns it } + requestUri = "/converter/test".also { every { servletRequest.requestURI } returns it } + remoteAddr = "localhost".also { every { servletRequest.remoteAddr } returns it } + userAgent = "Mozilla/5.0".also { every { servletRequest.getHeader("User-Agent") } returns it } + queryString = "key=value".also { every { servletRequest.queryString } returns it } + } + + afterSpec { + clearMocks(servletRequest) + } + + context("endpoint") { + test("정상 응답") { + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .endpoint() + .build() + + result["endpoint"] shouldBe "$method $requestUri" + } + + test("ServletRequest에서 null이 반환되면 그대로 들어간다.") { + every { servletRequest.method } returns null + every { servletRequest.requestURI } returns null + + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .endpoint() + .build() + + result["endpoint"] shouldBe "null null" + } + } + + context("clientIp") { + test("정상 응답") { + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .clientIp() + .build() + + result["client_ip"] shouldBe remoteAddr + } + + test("ServletRequest에서 null이 반환되면 추가되지 않는다.") { + every { servletRequest.remoteAddr } returns null + + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .clientIp() + .build() + + result["client_ip"] shouldBe null + } + } + + context("userAgent") { + test("정상 응답") { + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .userAgent() + .build() + + result["user_agent"] shouldBe userAgent + } + + test("ServletRequest에서 null이 반환되면 추가되지 않는다.") { + every { servletRequest.getHeader("User-Agent") } returns null + + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .userAgent() + .build() + + result["user_agent"] shouldBe null + } + } + + context("queryString") { + test("정상 응답") { + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .queryString() + .build() + + result["query_params"] shouldBe queryString + } + + test("ServletRequest에서 null이 반환되면 추가되지 않는다.") { + every { servletRequest.queryString } returns null + + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .queryString() + .build() + + result["query_params"] shouldBe null + } + } + + context("httpStatus") { + test("정상 응답") { + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .httpStatus(200) + .build() + + result["status_code"] shouldBe 200 + } + + test("null을 입력하면 추가되지 않는다.") { + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .httpStatus(null) + .build() + + result["status_code"] shouldBe null + } + } + + context("responseBody") { + test("정상 응답") { + val body = mapOf("key" to "value") + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .responseBody(body) + .build() + + result["response_body"] shouldBe body + } + + test("null을 입력하면 추가되지 않는다.") { + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .responseBody(null) + .build() + + result["response_body"] shouldBe null + } + } + + context("durationMs") { + test("정상 응답") { + MdcStartTimeUtil.setCurrentTime() + + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .durationMs() + .build() + + result["duration_ms"].shouldNotBeNull() + MdcStartTimeUtil.clear() + } + + test("MDC에서 값을 가져올 수 없으면 추가되지 않는다.") { + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .durationMs() + .build() + + result["duration_ms"] shouldBe null + } + } + + context("principalId") { + test("정상 응답") { + val principalId = 759980174446956066L.also { + MdcPrincipalIdUtil.set(it.toString()) + } + + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .principalId() + .build() + + result["principal_id"] shouldBe principalId + MdcPrincipalIdUtil.clear() + } + + test("MDC에서 값을 가져올 수 없으면 UNKNOWN 으로 표기된다.") { + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .principalId() + .build() + + result["principal_id"] shouldBe "UNKNOWN" + } + } + + context("exception") { + test("정상 응답") { + val exception = RuntimeException("hello") + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .exception(exception) + .build() + + result["exception"] shouldBe mapOf( + "class" to exception.javaClass.simpleName, + "message" to exception.message + ) + } + + test("null을 입력하면 추가되지 않는다.") { + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .exception(null) + .build() + + result["exception"] shouldBe null + } + } + + context("additionalPayloads") { + test("정상 응답") { + val payload = mapOf( + "key1" to "value1", + "key2" to "value2" + ) + val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .additionalPayloads(payload) + .build() + + result["key1"] shouldBe "value1" + result["key2"] shouldBe "value2" + } + } +}) -- 2.47.2 From eeb87e1bc33169968cbc734cb056d6d9b0c43944 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:14:52 +0900 Subject: [PATCH 24/39] =?UTF-8?q?feat:=20LogPayloadBuilder=EB=A5=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=EC=9D=98=20ApiLogMessageConverter=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/message/ApiLogMessageConverter.kt | 77 -------- .../common/log/ApiLogMessageConverterTest.kt | 69 -------- .../common/web/config/WebLoggingConfig.kt} | 26 +-- .../web/support/log/WebLogMessageConverter.kt | 49 ++++++ .../support/log/WebLogMessageConverterTest.kt | 166 ++++++++++++++++++ 5 files changed, 228 insertions(+), 159 deletions(-) delete mode 100644 common/log/src/main/kotlin/com/sangdol/common/log/message/ApiLogMessageConverter.kt delete mode 100644 common/log/src/test/kotlin/com/sangdol/common/log/ApiLogMessageConverterTest.kt rename common/{log/src/main/kotlin/com/sangdol/common/log/config/LogConfiguration.kt => web/src/main/kotlin/com/sangdol/common/web/config/WebLoggingConfig.kt} (50%) create mode 100644 common/web/src/main/kotlin/com/sangdol/common/web/support/log/WebLogMessageConverter.kt create mode 100644 common/web/src/test/kotlin/com/sangdol/common/web/support/log/WebLogMessageConverterTest.kt diff --git a/common/log/src/main/kotlin/com/sangdol/common/log/message/ApiLogMessageConverter.kt b/common/log/src/main/kotlin/com/sangdol/common/log/message/ApiLogMessageConverter.kt deleted file mode 100644 index a8048ef2..00000000 --- a/common/log/src/main/kotlin/com/sangdol/common/log/message/ApiLogMessageConverter.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.sangdol.common.log.message - -import com.fasterxml.jackson.databind.ObjectMapper -import com.sangdol.common.log.config.LogType -import com.sangdol.common.utils.MdcPrincipalIdUtil -import jakarta.servlet.http.HttpServletRequest - -class ApiLogMessageConverter( - private val objectMapper: ObjectMapper -) { - fun convertToHttpRequestMessage( - request: HttpServletRequest - ): String { - val payload: MutableMap = commonRequestPayload(LogType.INCOMING_HTTP_REQUEST, request) - - request.queryString?.let { payload["query_params"] = it } - payload["client_ip"] = request.remoteAddr - payload["user_agent"] = request.getHeader("User-Agent") - - return objectMapper.writeValueAsString(payload) - } - - fun convertToControllerInvokedMessage( - request: HttpServletRequest, - controllerPayload: Map, - ): String { - val payload: MutableMap = commonRequestPayload(LogType.CONTROLLER_INVOKED, request) - val memberId: Long? = MdcPrincipalIdUtil.extractAsLongOrNull() - if (memberId != null) payload["principal_id"] = memberId else payload["principal_id"] = "NONE" - - payload.putAll(controllerPayload) - - return objectMapper.writeValueAsString(payload) - } - - fun convertToResponseMessage(request: ConvertResponseMessageRequest): String { - val payload: MutableMap = mutableMapOf() - payload["type"] = request.type - payload["endpoint"] = request.endpoint - payload["status_code"] = request.httpStatus - - MdcPrincipalIdUtil.extractAsLongOrNull() - ?.let { payload["principal_id"] = it } - ?: run { payload["principal_id"] = "NONE" } - - request.startTime?.let { payload["duration_ms"] = System.currentTimeMillis() - it } - request.body?.let { payload["response_body"] = it } - request.exception?.let { - payload["exception"] = mapOf( - "class" to it.javaClass.simpleName, - "message" to it.message - ) - } - - return objectMapper.writeValueAsString(payload) - } - - private fun commonRequestPayload( - logType: LogType, - request: HttpServletRequest - ): MutableMap = mutableMapOf( - "type" to logType, - "method" to request.method, - "uri" to request.requestURI - ) -} - -data class ConvertResponseMessageRequest( - val type: LogType, - val endpoint: String, - val httpStatus: Int = 200, - val startTime: Long? = null, - val body: Any? = null, - val exception: Exception? = null -) - -fun HttpServletRequest.getEndpoint(): String = "${this.method} ${this.requestURI}" diff --git a/common/log/src/test/kotlin/com/sangdol/common/log/ApiLogMessageConverterTest.kt b/common/log/src/test/kotlin/com/sangdol/common/log/ApiLogMessageConverterTest.kt deleted file mode 100644 index 8e76c8d0..00000000 --- a/common/log/src/test/kotlin/com/sangdol/common/log/ApiLogMessageConverterTest.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.sangdol.common.log - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.sangdol.common.log.config.LogType -import com.sangdol.common.log.message.ApiLogMessageConverter -import com.sangdol.common.log.message.ConvertResponseMessageRequest -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe -import io.mockk.every -import io.mockk.mockk -import jakarta.servlet.http.HttpServletRequest -import org.slf4j.MDC - -class ApiLogMessageConverterTest : StringSpec({ - val converter = ApiLogMessageConverter(jacksonObjectMapper()) - val request: HttpServletRequest = mockk() - - beforeTest { - MDC.remove("principal_id") - MDC.put("principal_id", "1") - } - - afterSpec { - MDC.remove("principal_id") - } - - "HTTP 요청 메시지를 변환한다." { - val method = "POST".also { every { request.method } returns it } - val requestURI = "/test/sangdol".also { every { request.requestURI } returns it } - val clientIP = "127.0.0.1".also { every { request.remoteAddr } returns it } - val query = "key=value&key1=value1".also { every { request.queryString } returns it } - val userAgent = "Mozilla/5.".also { every { request.getHeader("User-Agent") } returns it } - - converter.convertToHttpRequestMessage(request) shouldBe """ - {"type":"INCOMING_HTTP_REQUEST","method":"$method","uri":"$requestURI","query_params":"$query","client_ip":"$clientIP","user_agent":"$userAgent"} - """.trimIndent() - } - - "Controller 요청 메시지를 변환한다." { - val controllerPayload: Map = mapOf( - "controller_method" to "Controller 요청 메시지를 변환한다.", - "request_body" to mapOf("key1" to "value1") - ) - val method = "POST".also { every { request.method } returns it } - val requestURI = "/test/sangdol".also { every { request.requestURI } returns it } - - converter.convertToControllerInvokedMessage(request, controllerPayload) shouldBe """ - {"type":"CONTROLLER_INVOKED","method":"$method","uri":"$requestURI","principal_id":1,"controller_method":"${ - controllerPayload.get( - "controller_method" - ) - }","request_body":{"key1":"value1"}} - """.trimIndent() - } - - "Controller 응답 메시지를 반환한다." { - val endpoint = "POST /test/sangdol" - val request = ConvertResponseMessageRequest( - type = LogType.CONTROLLER_SUCCESS, - endpoint = endpoint, - httpStatus = 200, - exception = RuntimeException("테스트 메시지!") - ) - - converter.convertToResponseMessage(request) shouldBe """ - {"type":"CONTROLLER_SUCCESS","endpoint":"$endpoint","status_code":200,"principal_id":1,"exception":{"class":"RuntimeException","message":"테스트 메시지!"}} - """.trimIndent() - } -}) diff --git a/common/log/src/main/kotlin/com/sangdol/common/log/config/LogConfiguration.kt b/common/web/src/main/kotlin/com/sangdol/common/web/config/WebLoggingConfig.kt similarity index 50% rename from common/log/src/main/kotlin/com/sangdol/common/log/config/LogConfiguration.kt rename to common/web/src/main/kotlin/com/sangdol/common/web/config/WebLoggingConfig.kt index da4073e8..9539657e 100644 --- a/common/log/src/main/kotlin/com/sangdol/common/log/config/LogConfiguration.kt +++ b/common/web/src/main/kotlin/com/sangdol/common/web/config/WebLoggingConfig.kt @@ -1,9 +1,9 @@ -package com.sangdol.common.log.config +package com.sangdol.common.web.config import com.fasterxml.jackson.databind.ObjectMapper -import com.sangdol.common.log.message.ApiLogMessageConverter -import com.sangdol.common.log.web.ControllerLoggingAspect -import com.sangdol.common.log.web.HttpRequestLoggingFilter +import com.sangdol.common.web.asepct.ControllerLoggingAspect +import com.sangdol.common.web.servlet.HttpRequestLoggingFilter +import com.sangdol.common.web.support.log.WebLogMessageConverter import org.springframework.boot.web.servlet.FilterRegistrationBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -12,27 +12,27 @@ import org.springframework.core.Ordered import org.springframework.web.filter.OncePerRequestFilter @Configuration -class LogConfiguration { +class WebLoggingConfig { @Bean - @DependsOn(value = ["apiLogMessageConverter"]) + @DependsOn(value = ["webLogMessageConverter"]) fun filterRegistrationBean( - apiLogMessageConverter: ApiLogMessageConverter + webLogMessageConverter: WebLogMessageConverter ): FilterRegistrationBean { - val filter = HttpRequestLoggingFilter(apiLogMessageConverter) + val filter = HttpRequestLoggingFilter(webLogMessageConverter) return FilterRegistrationBean(filter) .apply { this.order = Ordered.HIGHEST_PRECEDENCE + 2 } } @Bean - @DependsOn(value = ["apiLogMessageConverter"]) - fun apiLoggingAspect(apiLogMessageConverter: ApiLogMessageConverter): ControllerLoggingAspect { - return ControllerLoggingAspect(apiLogMessageConverter) + @DependsOn(value = ["webLogMessageConverter"]) + fun apiLoggingAspect(webLogMessageConverter: WebLogMessageConverter): ControllerLoggingAspect { + return ControllerLoggingAspect(webLogMessageConverter) } @Bean - fun apiLogMessageConverter(objectMapper: ObjectMapper): ApiLogMessageConverter { - return ApiLogMessageConverter(objectMapper) + fun webLogMessageConverter(objectMapper: ObjectMapper): WebLogMessageConverter { + return WebLogMessageConverter(objectMapper) } } diff --git a/common/web/src/main/kotlin/com/sangdol/common/web/support/log/WebLogMessageConverter.kt b/common/web/src/main/kotlin/com/sangdol/common/web/support/log/WebLogMessageConverter.kt new file mode 100644 index 00000000..1a2c47a7 --- /dev/null +++ b/common/web/src/main/kotlin/com/sangdol/common/web/support/log/WebLogMessageConverter.kt @@ -0,0 +1,49 @@ +package com.sangdol.common.web.support.log + +import com.fasterxml.jackson.databind.ObjectMapper +import com.sangdol.common.log.constant.LogType +import jakarta.servlet.http.HttpServletRequest + +class WebLogMessageConverter( + private val objectMapper: ObjectMapper +) { + fun convertToHttpRequestMessage(servletRequest: HttpServletRequest): String { + val payload = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest) + .endpoint() + .queryString() + .clientIp() + .userAgent() + .build() + + return objectMapper.writeValueAsString(payload) + } + + fun convertToControllerInvokedMessage(servletRequest: HttpServletRequest, controllerPayload: Map): String { + val payload = LogPayloadBuilder(type = LogType.CONTROLLER_INVOKED, servletRequest = servletRequest) + .endpoint() + .principalId() + .additionalPayloads(controllerPayload) + .build() + + return objectMapper.writeValueAsString(payload) + } + + fun convertToResponseMessage( + type: LogType, + servletRequest: HttpServletRequest, + httpStatusCode: Int, + responseBody: Any? = null, + exception: Exception? = null, + ): String { + val payload = LogPayloadBuilder(type = type, servletRequest = servletRequest) + .endpoint() + .httpStatus(httpStatusCode) + .durationMs() + .principalId() + .responseBody(responseBody) + .exception(exception) + .build() + + return objectMapper.writeValueAsString(payload) + } +} diff --git a/common/web/src/test/kotlin/com/sangdol/common/web/support/log/WebLogMessageConverterTest.kt b/common/web/src/test/kotlin/com/sangdol/common/web/support/log/WebLogMessageConverterTest.kt new file mode 100644 index 00000000..84a1f62d --- /dev/null +++ b/common/web/src/test/kotlin/com/sangdol/common/web/support/log/WebLogMessageConverterTest.kt @@ -0,0 +1,166 @@ +package com.sangdol.common.web.support.log + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.sangdol.common.log.constant.LogType +import com.sangdol.common.types.web.HttpStatus +import com.sangdol.common.utils.MdcPrincipalIdUtil +import com.sangdol.common.utils.MdcStartTimeUtil +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.mockk.clearMocks +import io.mockk.every +import io.mockk.mockk +import jakarta.servlet.http.HttpServletRequest + +class WebLogMessageConverterTest : FunSpec({ + + val objectMapper = jacksonObjectMapper() + val converter = WebLogMessageConverter(objectMapper) + val servletRequest: HttpServletRequest = mockk() + + lateinit var method: String + lateinit var requestUri: String + lateinit var remoteAddr: String + lateinit var userAgent: String + lateinit var queryString: String + + beforeTest { + method = "GET".also { every { servletRequest.method } returns it } + requestUri = "/converter/test".also { every { servletRequest.requestURI } returns it } + remoteAddr = "localhost".also { every { servletRequest.remoteAddr } returns it } + userAgent = "Mozilla/5.0".also { every { servletRequest.getHeader("User-Agent") } returns it } + queryString = "key=value".also { every { servletRequest.queryString } returns it } + } + + afterSpec { + clearMocks(servletRequest) + } + + context("Http 요청 메시지를 변환한다.") { + test("정상 응답") { + val result = converter.convertToHttpRequestMessage(servletRequest) + + result shouldBe """ + {"type":"${LogType.INCOMING_HTTP_REQUEST.name}","endpoint":"$method $requestUri","query_params":"$queryString","client_ip":"$remoteAddr","user_agent":"$userAgent"} + """.trimIndent() + } + } + + context("Controller 요청 메시지를 변환한다") { + val principalId = 759980174446956066L.also { + MdcPrincipalIdUtil.set(it.toString()) + } + + test("정상 응답") { + val controllerPayload: Map = mapOf( + "controller_method" to "ThemeController.findThemeById(..)", + "path_variable" to mapOf("id" to "7599801744469560667") + ) + + val result = converter.convertToControllerInvokedMessage(servletRequest, controllerPayload) + + result shouldBe """ + {"type":"${LogType.CONTROLLER_INVOKED.name}","endpoint":"$method $requestUri","principal_id":$principalId,"controller_method":"${controllerPayload["controller_method"]}","path_variable":{"id":"${7599801744469560667}"}} + """.trimIndent() + } + } + + context("응답 메시지를 변환한다.") { + val principalId = 7599801744469560666 + + val body = mapOf( + "id" to 7599801744469560667, + "name" to "sangdol" + ) + + val exception = RuntimeException("hello") + + beforeTest { + MdcPrincipalIdUtil.set(principalId.toString()) + MdcStartTimeUtil.setCurrentTime() + } + + afterTest { + MdcPrincipalIdUtil.clear() + MdcStartTimeUtil.clear() + } + + test("응답 본문을 포함한다.") { + val result = converter.convertToResponseMessage( + type = LogType.SUCCEED, + servletRequest = servletRequest, + httpStatusCode = HttpStatus.OK.value(), + responseBody = body + ) + + assertSoftly(objectMapper.readValue(result, LinkedHashMap::class.java)) { + this["type"] shouldBe LogType.SUCCEED.name + this["endpoint"] shouldBe "$method $requestUri" + this["status_code"] shouldBe HttpStatus.OK.value() + this["duration_ms"].shouldNotBeNull() + this["principal_id"] shouldBe principalId + this["response_body"] shouldBe body + this["exception"] shouldBe null + } + } + + test("예외를 포함한다.") { + val result = converter.convertToResponseMessage( + type = LogType.SUCCEED, + servletRequest = servletRequest, + httpStatusCode = HttpStatus.OK.value(), + exception = exception + ) + + assertSoftly(objectMapper.readValue(result, LinkedHashMap::class.java)) { + this["type"] shouldBe LogType.SUCCEED.name + this["endpoint"] shouldBe "$method $requestUri" + this["status_code"] shouldBe HttpStatus.OK.value() + this["duration_ms"].shouldNotBeNull() + this["principal_id"] shouldBe principalId + this["response_body"] shouldBe null + this["exception"] shouldBe mapOf("class" to exception.javaClass.simpleName, "message" to exception.message) + } + } + + test("예외 + 응답 본문을 모두 포함한다.") { + val result = converter.convertToResponseMessage( + type = LogType.SUCCEED, + servletRequest = servletRequest, + httpStatusCode = HttpStatus.OK.value(), + responseBody = body, + exception = exception + ) + + assertSoftly(objectMapper.readValue(result, LinkedHashMap::class.java)) { + this["type"] shouldBe LogType.SUCCEED.name + this["endpoint"] shouldBe "$method $requestUri" + this["status_code"] shouldBe HttpStatus.OK.value() + this["duration_ms"].shouldNotBeNull() + this["principal_id"] shouldBe principalId + this["response_body"] shouldBe body + this["exception"] shouldBe mapOf("class" to exception.javaClass.simpleName, "message" to exception.message) + } + } + + test("예외, 응답 본문 모두 제외한다.") { + val result = converter.convertToResponseMessage( + type = LogType.SUCCEED, + servletRequest = servletRequest, + httpStatusCode = HttpStatus.OK.value(), + ) + + assertSoftly(objectMapper.readValue(result, LinkedHashMap::class.java)) { + this["type"] shouldBe LogType.SUCCEED.name + this["endpoint"] shouldBe "$method $requestUri" + this["status_code"] shouldBe HttpStatus.OK.value() + this["duration_ms"].shouldNotBeNull() + this["principal_id"] shouldBe principalId + this["response_body"] shouldBe null + this["exception"] shouldBe null + } + } + } +}) -- 2.47.2 From 1cbece032fac1a89fe2a89d1ed30ff8d7f1b1545 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:15:54 +0900 Subject: [PATCH 25/39] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EB=A1=9C=EA=B9=85=20=ED=95=84=ED=84=B0,=20AOP,=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=ED=95=B8=EB=93=A4=EB=9F=AC=EC=97=90=20=EC=83=88?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=95=EC=9D=98=EB=90=9C=20WebLogMessageConverte?= =?UTF-8?q?r=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sangdol/common/types/web/HttpStatus.kt | 4 ++ .../web/asepct}/ControllerLoggingAspect.kt | 53 ++++++--------- .../web/exception/GlobalExceptionhandler.kt | 68 ++++++------------- .../web/servlet}/HttpRequestLoggingFilter.kt | 13 ++-- 4 files changed, 53 insertions(+), 85 deletions(-) rename common/{log/src/main/kotlin/com/sangdol/common/log/web => web/src/main/kotlin/com/sangdol/common/web/asepct}/ControllerLoggingAspect.kt (61%) rename service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt => common/web/src/main/kotlin/com/sangdol/common/web/exception/GlobalExceptionhandler.kt (59%) rename common/{log/src/main/kotlin/com/sangdol/common/log/web => web/src/main/kotlin/com/sangdol/common/web/servlet}/HttpRequestLoggingFilter.kt (78%) diff --git a/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt b/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt index 387cfed8..35764d15 100644 --- a/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt +++ b/common/types/src/main/kotlin/com/sangdol/common/types/web/HttpStatus.kt @@ -14,6 +14,10 @@ enum class HttpStatus( INTERNAL_SERVER_ERROR(500) ; + fun isClientError(): Boolean { + return code in 400..<500 + } + fun value(): Int { return code } diff --git a/common/log/src/main/kotlin/com/sangdol/common/log/web/ControllerLoggingAspect.kt b/common/web/src/main/kotlin/com/sangdol/common/web/asepct/ControllerLoggingAspect.kt similarity index 61% rename from common/log/src/main/kotlin/com/sangdol/common/log/web/ControllerLoggingAspect.kt rename to common/web/src/main/kotlin/com/sangdol/common/web/asepct/ControllerLoggingAspect.kt index 1b10df4c..68d87803 100644 --- a/common/log/src/main/kotlin/com/sangdol/common/log/web/ControllerLoggingAspect.kt +++ b/common/web/src/main/kotlin/com/sangdol/common/web/asepct/ControllerLoggingAspect.kt @@ -1,9 +1,7 @@ -package com.sangdol.common.log.web +package com.sangdol.common.web.asepct -import com.sangdol.common.log.config.LogType -import com.sangdol.common.log.message.ApiLogMessageConverter -import com.sangdol.common.log.message.ConvertResponseMessageRequest -import com.sangdol.common.log.message.getEndpoint +import com.sangdol.common.log.constant.LogType +import com.sangdol.common.web.support.log.WebLogMessageConverter import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import jakarta.servlet.http.HttpServletRequest @@ -13,7 +11,6 @@ import org.aspectj.lang.annotation.Around import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Pointcut import org.aspectj.lang.reflect.MethodSignature -import org.slf4j.MDC import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody @@ -25,7 +22,7 @@ private val log: KLogger = KotlinLogging.logger {} @Aspect class ControllerLoggingAspect( - private val messageConverter: ApiLogMessageConverter, + private val messageConverter: WebLogMessageConverter, ) { @Pointcut("execution(* com.sangdol..web..*Controller*.*(..))") @@ -34,10 +31,8 @@ class ControllerLoggingAspect( @Around("allController()") fun logAPICalls(joinPoint: ProceedingJoinPoint): Any? { - val startTime: Long = MDC.get("startTime").toLongOrNull() ?: System.currentTimeMillis() - val controllerPayload: Map = parsePayload(joinPoint) - val servletRequest: HttpServletRequest = servletRequest() + val controllerPayload: Map = parseControllerPayload(joinPoint) log.info { messageConverter.convertToControllerInvokedMessage(servletRequest, controllerPayload) @@ -45,29 +40,22 @@ class ControllerLoggingAspect( try { return joinPoint.proceed() - .also { logSuccess(servletRequest.getEndpoint(), startTime, it) } + .also { logSuccess(servletRequest, it as ResponseEntity<*>) } } catch (e: Exception) { throw e } } - private fun logSuccess(endpoint: String, startTime: Long, result: Any) { - val responseEntity = result as ResponseEntity<*> - var convertResponseMessageRequest = ConvertResponseMessageRequest( - type = LogType.CONTROLLER_SUCCESS, - endpoint = endpoint, - httpStatus = responseEntity.statusCode.value(), - startTime = startTime, + private fun logSuccess(servletRequest: HttpServletRequest, result: ResponseEntity<*>) { + val body: Any? = if (log.isDebugEnabled()) result.body else null + + val logMessage = messageConverter.convertToResponseMessage( + type = LogType.SUCCEED, + servletRequest = servletRequest, + httpStatusCode = result.statusCode.value(), + responseBody = body, ) - if (log.isDebugEnabled()) { - convertResponseMessageRequest = convertResponseMessageRequest.copy( - body = responseEntity.body - ) - } - - val logMessage = messageConverter.convertToResponseMessage(convertResponseMessageRequest) - log.info { logMessage } } @@ -75,14 +63,16 @@ class ControllerLoggingAspect( return (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request } - private fun parsePayload(joinPoint: JoinPoint): Map { + private fun parseControllerPayload(joinPoint: JoinPoint): Map { val signature = joinPoint.signature as MethodSignature val args = joinPoint.args - val payload = mutableMapOf() - payload["controller_method"] = joinPoint.signature.toShortString() + val payload = mutableMapOf( + "controller_method" to joinPoint.signature.toShortString() + ) val requestParams: MutableMap = mutableMapOf() val pathVariables: MutableMap = mutableMapOf() + signature.method.parameters.forEachIndexed { index, parameter -> val arg = args[index] @@ -97,9 +87,10 @@ class ControllerLoggingAspect( parameter.getAnnotation(RequestParam::class.java)?.let { requestParams[parameter.name] = arg } + }.also { + if (pathVariables.isNotEmpty()) payload["path_variable"] = pathVariables + if (requestParams.isNotEmpty()) payload["request_param"] = requestParams } - if (pathVariables.isNotEmpty()) payload["path_variable"] = pathVariables - if (requestParams.isNotEmpty()) payload["request_param"] = requestParams return payload } diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt b/common/web/src/main/kotlin/com/sangdol/common/web/exception/GlobalExceptionhandler.kt similarity index 59% rename from service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt rename to common/web/src/main/kotlin/com/sangdol/common/web/exception/GlobalExceptionhandler.kt index 146e74cf..a790a4d2 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/exception/ExceptionControllerAdvice.kt +++ b/common/web/src/main/kotlin/com/sangdol/common/web/exception/GlobalExceptionhandler.kt @@ -1,19 +1,15 @@ -package com.sangdol.roomescape.common.exception +package com.sangdol.common.web.exception +import com.sangdol.common.log.constant.LogType import com.sangdol.common.types.exception.CommonErrorCode import com.sangdol.common.types.exception.ErrorCode import com.sangdol.common.types.exception.RoomescapeException import com.sangdol.common.types.web.CommonErrorResponse import com.sangdol.common.types.web.HttpStatus -import com.sangdol.roomescape.auth.exception.AuthException -import com.sangdol.roomescape.common.log.ApiLogMessageConverter -import com.sangdol.roomescape.common.log.ConvertResponseMessageRequest -import com.sangdol.roomescape.common.log.LogType -import com.sangdol.roomescape.common.log.getEndpoint +import com.sangdol.common.web.support.log.WebLogMessageConverter import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import jakarta.servlet.http.HttpServletRequest -import org.slf4j.MDC import org.springframework.http.ResponseEntity import org.springframework.http.converter.HttpMessageNotReadableException import org.springframework.web.bind.MethodArgumentNotValidException @@ -23,8 +19,8 @@ import org.springframework.web.bind.annotation.RestControllerAdvice private val log: KLogger = KotlinLogging.logger {} @RestControllerAdvice -class ExceptionControllerAdvice( - private val messageConverter: ApiLogMessageConverter +class GlobalExceptionHandler( + private val messageConverter: WebLogMessageConverter ) { @ExceptionHandler(value = [RoomescapeException::class]) fun handleRoomException( @@ -35,14 +31,7 @@ class ExceptionControllerAdvice( val httpStatus: HttpStatus = errorCode.httpStatus val errorResponse = CommonErrorResponse(errorCode) - val type = if (e is AuthException) LogType.AUTHENTICATION_FAILURE else LogType.APPLICATION_FAILURE - logException( - type = type, - servletRequest = servletRequest, - httpStatus = httpStatus.value(), - errorResponse = errorResponse, - exception = e - ) + logException(servletRequest, httpStatus, errorResponse, e) return ResponseEntity .status(httpStatus.value()) @@ -54,26 +43,21 @@ class ExceptionControllerAdvice( servletRequest: HttpServletRequest, e: Exception ): ResponseEntity { - val message: String = if (e is MethodArgumentNotValidException) { + if (e is MethodArgumentNotValidException) { e.bindingResult.allErrors .mapNotNull { it.defaultMessage } .joinToString(", ") } else { e.message!! + }.also { + log.warn { "[ExceptionControllerAdvice] Invalid Request Value Exception occurred: $it" } } - log.debug { "[ExceptionControllerAdvice] Invalid Request Value Exception occurred: $message" } val errorCode: ErrorCode = CommonErrorCode.INVALID_INPUT_VALUE val httpStatus: HttpStatus = errorCode.httpStatus val errorResponse = CommonErrorResponse(errorCode) - logException( - type = LogType.APPLICATION_FAILURE, - servletRequest = servletRequest, - httpStatus = httpStatus.value(), - errorResponse = errorResponse, - exception = e - ) + logException(servletRequest, httpStatus, errorResponse, e) return ResponseEntity .status(httpStatus.value()) @@ -91,13 +75,7 @@ class ExceptionControllerAdvice( val httpStatus: HttpStatus = errorCode.httpStatus val errorResponse = CommonErrorResponse(errorCode) - logException( - type = LogType.UNHANDLED_EXCEPTION, - servletRequest = servletRequest, - httpStatus = httpStatus.value(), - errorResponse = errorResponse, - exception = e - ) + logException(servletRequest, httpStatus, errorResponse, e) return ResponseEntity .status(httpStatus.value()) @@ -105,25 +83,21 @@ class ExceptionControllerAdvice( } private fun logException( - type: LogType, servletRequest: HttpServletRequest, - httpStatus: Int, + httpStatus: HttpStatus, errorResponse: CommonErrorResponse, exception: Exception ) { - val commonRequest = ConvertResponseMessageRequest( - type = type, - endpoint = servletRequest.getEndpoint(), - httpStatus = httpStatus, - startTime = MDC.get("startTime")?.toLongOrNull(), - body = errorResponse, - ) + val type = if (httpStatus.isClientError()) LogType.APPLICATION_FAILURE else LogType.UNHANDLED_EXCEPTION + val actualException: Exception? = if (errorResponse.message == exception.message) null else exception - val logMessage = if (errorResponse.message == exception.message) { - messageConverter.convertToResponseMessage(commonRequest) - } else { - messageConverter.convertToResponseMessage(commonRequest.copy(exception = exception)) - } + val logMessage = messageConverter.convertToResponseMessage( + type = type, + servletRequest = servletRequest, + httpStatusCode = httpStatus.value(), + responseBody = errorResponse, + exception = actualException + ) log.warn { logMessage } } diff --git a/common/log/src/main/kotlin/com/sangdol/common/log/web/HttpRequestLoggingFilter.kt b/common/web/src/main/kotlin/com/sangdol/common/web/servlet/HttpRequestLoggingFilter.kt similarity index 78% rename from common/log/src/main/kotlin/com/sangdol/common/log/web/HttpRequestLoggingFilter.kt rename to common/web/src/main/kotlin/com/sangdol/common/web/servlet/HttpRequestLoggingFilter.kt index eccd372a..a5e45a5d 100644 --- a/common/log/src/main/kotlin/com/sangdol/common/log/web/HttpRequestLoggingFilter.kt +++ b/common/web/src/main/kotlin/com/sangdol/common/web/servlet/HttpRequestLoggingFilter.kt @@ -1,13 +1,13 @@ -package com.sangdol.common.log.web +package com.sangdol.common.web.servlet -import com.sangdol.common.log.message.ApiLogMessageConverter import com.sangdol.common.utils.MdcPrincipalIdUtil +import com.sangdol.common.utils.MdcStartTimeUtil +import com.sangdol.common.web.support.log.WebLogMessageConverter import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import jakarta.servlet.FilterChain import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse -import org.slf4j.MDC import org.springframework.web.filter.OncePerRequestFilter import org.springframework.web.util.ContentCachingRequestWrapper import org.springframework.web.util.ContentCachingResponseWrapper @@ -15,7 +15,7 @@ import org.springframework.web.util.ContentCachingResponseWrapper private val log: KLogger = KotlinLogging.logger {} class HttpRequestLoggingFilter( - private val messageConverter: ApiLogMessageConverter + private val messageConverter: WebLogMessageConverter ) : OncePerRequestFilter() { override fun doFilterInternal( request: HttpServletRequest, @@ -27,14 +27,13 @@ class HttpRequestLoggingFilter( val cachedRequest = ContentCachingRequestWrapper(request) val cachedResponse = ContentCachingResponseWrapper(response) - val startTime = System.currentTimeMillis() - MDC.put("startTime", startTime.toString()) + MdcStartTimeUtil.setCurrentTime() try { filterChain.doFilter(cachedRequest, cachedResponse) cachedResponse.copyBodyToResponse() } finally { - MDC.remove("startTime") + MdcStartTimeUtil.clear() MdcPrincipalIdUtil.clear() } } -- 2.47.2 From 30eb2e3b03fef0476b4b1fef4a269ec800d9b79c Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:16:15 +0900 Subject: [PATCH 26/39] =?UTF-8?q?chore:=20=EC=B4=88=EA=B8=B0=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sangdol/data/DefaultDataInitializer.kt | 910 ++++++++++++++++++ .../data/PopulationDataParser.kt | 6 +- .../roomescape/data/DefaultDataInitializer.kt | 910 ------------------ 3 files changed, 913 insertions(+), 913 deletions(-) create mode 100644 service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt rename service/src/test/kotlin/com/sangdol/{roomescape => }/data/PopulationDataParser.kt (98%) delete mode 100644 service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt diff --git a/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt b/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt new file mode 100644 index 00000000..2b494fb4 --- /dev/null +++ b/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt @@ -0,0 +1,910 @@ +package com.sangdol.data + +import com.sangdol.common.persistence.IDGenerator +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel +import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType +import com.sangdol.roomescape.common.util.TransactionExecutionUtil +import com.sangdol.roomescape.payment.infrastructure.common.* +import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus +import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus +import com.sangdol.roomescape.supports.AdminFixture +import com.sangdol.roomescape.supports.FunSpecSpringbootTest +import com.sangdol.roomescape.supports.randomPhoneNumber +import com.sangdol.roomescape.supports.randomString +import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty +import com.sangdol.roomescape.user.business.SIGNUP +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity +import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus +import com.sangdol.roomescape.user.web.UserContactResponse +import io.kotest.core.test.TestCaseOrder +import jakarta.persistence.EntityManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Semaphore +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.test.context.ActiveProfiles +import java.sql.Timestamp +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.OffsetDateTime + +@ActiveProfiles("test", "test-mysql") +abstract class AbstractDataInitializer( + val semaphore: Semaphore = Semaphore(permits = 10), +) : FunSpecSpringbootTest( + enableCleanerExtension = false +) { + @Autowired + lateinit var entityManager: EntityManager + + @Autowired + lateinit var jdbcTemplate: JdbcTemplate + + @Autowired + lateinit var transactionExecutionUtil: TransactionExecutionUtil + + @Autowired + lateinit var idGenerator: IDGenerator + + override fun testCaseOrder(): TestCaseOrder? = TestCaseOrder.Sequential + + suspend fun initialize() { + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0") + + jdbcTemplate.query("SHOW TABLES") { rs, _ -> + rs.getString(1).lowercase() + }.forEach { + jdbcTemplate.execute("TRUNCATE TABLE $it") + } + + jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1") + + this::class.java.getResource("/schema/region-data.sql")?.readText()?.let { sql -> + jdbcTemplate.execute(sql) + } + } + } + + suspend fun executeBatch(sql: String, batchArgs: List>) { + semaphore.acquire() + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + jdbcTemplate.batchUpdate(sql, batchArgs) + } + + semaphore.release() + } +} + +class DefaultDataInitializer : AbstractDataInitializer() { + + // 1. HQ Admin 추가 + // 2. Store 추가 -> CreatedBy / UpdatedBy는 HQ Admin 중 한명으로 고정 + // 3. Store Admin 추가 -> CreatedBy / UpdatedBy는 HQ Admin 본인으로 고정 + init { + lateinit var superHQAdmin: AdminEntity + + // 모든 테이블 초기화 + 지역 데이터 삽입 + HQ 관리자 1명 생성 + beforeSpec { + initialize() + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + superHQAdmin = testAuthUtil.createAdmin(AdminFixture.hqDefault.apply { + this.createdBy = this.id + this.updatedBy = this.id + }) + } + } + + context("관리자, 매장, 테마 초기 데이터 생성") { + test("각 PermissionLevel 마다 20명의 HQ 관리자 생성") { + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + AdminPermissionLevel.entries.forEach { + repeat(20) { index -> + AdminFixture.create( + account = "hq_${it.name.lowercase()}_$index", + name = randomKoreanName(), + phone = randomPhoneNumber(), + type = AdminType.HQ, + permissionLevel = it + ).apply { + this.createdBy = superHQAdmin.id + this.updatedBy = superHQAdmin.id + }.also { + entityManager.persist(it) + } + } + } + } + } + + test("전체 매장 생성") { + val storeDataInitializer = StoreDataInitializer() + val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", + Long::class.java + ).setParameter("type", AdminType.HQ) + .setParameter( + "permissionLevels", + listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) + ) + .resultList + }.map { it.toString() } + + val sqlFile = storeDataInitializer.createStoreDataSqlFile(creatableAdminIds) + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + jdbcTemplate.execute(sqlFile.readText()) + } + } + + test("각 매장당 1명의 ${AdminPermissionLevel.FULL_ACCESS} 권한의 StoreManager 1명 + ${AdminPermissionLevel.WRITABLE}의 2명 + 나머지 권한은 3명씩 생성") { + val storeAdminCountsByPermissionLevel = mapOf( + AdminPermissionLevel.WRITABLE to 2, + AdminPermissionLevel.READ_ALL to 3, + AdminPermissionLevel.READ_SUMMARY to 3 + ) + + val storeIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT s.id FROM StoreEntity s", + Long::class.java + ).resultList + }.map { it as Long } + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + storeIds.forEach { storeId -> + // StoreManager 1명 생성 + val storeManager = AdminFixture.create( + account = "$storeId", + name = randomKoreanName(), + phone = randomPhoneNumber(), + type = AdminType.STORE, + storeId = storeId, + permissionLevel = AdminPermissionLevel.FULL_ACCESS + ).apply { + this.createdBy = superHQAdmin.id + this.updatedBy = superHQAdmin.id + }.also { + entityManager.persist(it) + } + + storeAdminCountsByPermissionLevel.forEach { (permissionLevel, count) -> + repeat(count) { index -> + AdminFixture.create( + account = randomString(), + name = randomKoreanName(), + phone = randomPhoneNumber(), + type = AdminType.STORE, + storeId = storeId, + permissionLevel = permissionLevel + ).apply { + this.createdBy = storeManager.id + this.updatedBy = storeManager.id + }.also { + entityManager.persist(it) + } + } + } + entityManager.flush() + entityManager.clear() + } + } + } + + test("총 500개의 테마 생성: 지난 2년 전 부터 지금까지의 랜덤 테마 + active 상태인 1달 이내 생성 테마 10개") { + val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", + Long::class.java + ).setParameter("type", AdminType.HQ) + .setParameter( + "permissionLevels", + listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) + ) + .resultList + } + val sql = + "INSERT INTO theme (id, name, description, thumbnail_url, is_active, available_minutes, expected_minutes_from, expected_minutes_to, price, difficulty, min_participants, max_participants, created_at, created_by, updated_at, updated_by) VALUES (?," + + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + val batchSize = 100 + val batchArgs = mutableListOf>() + + repeat(500) { i -> + val randomDay = if (i <= 9) (1..30).random() else (1..365 * 2).random() + val randomCreatedAt: LocalDateTime = LocalDateTime.now().minusDays(randomDay.toLong()) + val randomThemeName = + (1..7).random().let { repeat -> (1..repeat).joinToString("") { randomKoreanName() } } + val availableMinutes = (6..20).random() * 10 + val expectedMinutesTo = availableMinutes - ((1..3).random() * 10) + val expectedMinutesFrom = expectedMinutesTo - ((1..2).random() * 10) + val randomPrice = (0..40).random() * 500 + val minParticipant = (1..10).random() + val maxParticipant = minParticipant + (1..10).random() + val createdBy = creatableAdminIds.random() + + batchArgs.add( + arrayOf( + idGenerator.create(), + randomThemeName, + "$randomThemeName 설명이에요!!", + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFiuCdwdz88l6pdfsRy1nFl0IHUVI7JMTQHg&s", + if (randomDay <= 30) true else false, + availableMinutes.toShort(), + expectedMinutesFrom.toShort(), + expectedMinutesTo.toShort(), + randomPrice, + Difficulty.entries.random().name, + minParticipant.toShort(), + maxParticipant.toShort(), + Timestamp.valueOf(randomCreatedAt), + createdBy, + Timestamp.valueOf(randomCreatedAt), + createdBy + ) + ) + } + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + jdbcTemplate.batchUpdate(sql, batchArgs) + } + } + } + } +} + +class UserDataInitializer : AbstractDataInitializer() { + val userCount = 1_000_000 + + init { + context("유저 초기 데이터 생성") { + test("$userCount 명의 회원 생성") { + val regions: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT r.code FROM RegionEntity r", + String::class.java + ).resultList + } + + val chunkSize = 10_000 + val chunks = userCount / chunkSize + + coroutineScope { + (1..chunks).map { + launch(Dispatchers.IO) { + processUsers(chunkSize, regions) + } + }.joinAll() + } + } + + test("휴대폰 번호가 중복된 유저들에게 재배정") { + val duplicatePhoneUsers: List = + transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + """ + SELECT u FROM UserEntity u + WHERE u.phone IN ( + SELECT u2.phone FROM UserEntity u2 + GROUP BY u2.phone + HAVING COUNT(u2.id) > 1 + ) + ORDER BY u.phone, u.id + """.trimIndent(), + UserEntity::class.java + ).resultList + } + + jdbcTemplate.execute("CREATE INDEX idx_users__phone ON users (phone)") + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + var currentPhone: String? = null + duplicatePhoneUsers.forEach { user -> + if (user.phone != currentPhone) { + currentPhone = user.phone + } else { + var newPhone: String + + do { + newPhone = randomPhoneNumber() + val count: Long = entityManager.createQuery( + "SELECT COUNT(u.id) FROM UserEntity u WHERE u.phone = :phone", + Long::class.java + ).setParameter("phone", newPhone) + .singleResult + + if (count == 0L) break + } while (true) + + user.phone = newPhone + user.updatedAt = LocalDateTime.now() + entityManager.merge(user) + } + } + } + + jdbcTemplate.execute("DROP INDEX idx_users__phone ON users") + } + + test("회원 상태 변경 이력 저장") { + val userId: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT u.id FROM UserEntity u", + Long::class.java + ).resultList + } + + coroutineScope { + userId.chunked(10_000).map { chunk -> + launch(Dispatchers.IO) { + processStatus(chunk) + } + }.joinAll() + } + } + } + } + + private suspend fun processStatus(userIds: List) { + val sql = """ + INSERT INTO user_status_history ( + id, user_id, reason, status, + created_by, updated_by, created_at, updated_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + val batchArgs = mutableListOf>() + val now = LocalDateTime.now() + + userIds.forEach { userId -> + batchArgs.add( + arrayOf( + idGenerator.create(), + userId, + SIGNUP, + UserStatus.ACTIVE.name, + userId, + userId, + Timestamp.valueOf(now), + Timestamp.valueOf(now) + ) + ) + } + + executeBatch(sql, batchArgs).also { batchArgs.clear() } + } + + private suspend fun processUsers(chunkSize: Int, regions: List) { + val sql = """ + INSERT INTO users ( + id, name, email, password, phone, region_code, status, + created_by, updated_by, created_at, updated_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + val batchArgs = mutableListOf>() + + repeat(chunkSize) { + val id: Long = idGenerator.create() + batchArgs.add( + arrayOf( + id, + randomKoreanName(), + "${randomString()}@sangdol.com", + randomString(), + randomPhoneNumber(), + regions.random(), + UserStatus.ACTIVE.name, + id, + id, + Timestamp.valueOf(LocalDateTime.now()), + Timestamp.valueOf(LocalDateTime.now()) + ) + ) + if (batchArgs.size >= 1_000) { + executeBatch(sql, batchArgs).also { batchArgs.clear() } + } + } + + if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } + } +} + +class ScheduleDataInitializer : AbstractDataInitializer() { + init { + context("일정 초기 데이터 생성") { + test("테마 생성일 기준으로 다음 3일차, 매일 5개의 일정을 모든 매장에 생성") { + val stores: List> = getStoreWithManagers() + val themes: List> = getThemes() + val maxAvailableMinutes = themes.maxOf { it.second.toInt() } + val scheduleCountPerDay = 5 + + val startTime = LocalTime.of(10, 0) + var lastTime = startTime + val times = mutableListOf() + + repeat(scheduleCountPerDay) { + times.add(lastTime) + lastTime = lastTime.plusMinutes(maxAvailableMinutes.toLong() + 10L) + } + + coroutineScope { + themes.forEach { theme -> + launch(Dispatchers.IO) { + processTheme(theme, stores, times) + } + } + } + } + } + } + + private suspend fun processTheme( + theme: Triple, + stores: List>, + times: List + ) { + val sql = """ + INSERT INTO schedule ( + id, store_id, theme_id, date, time, status, + created_by, updated_by, created_at, updated_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + + val batchArgs = mutableListOf>() + + val now = LocalDateTime.now() + stores.forEach { (storeId, adminId) -> + (1..3).forEach { dayOffset -> + val date = theme.third.toLocalDate().plusDays(dayOffset.toLong()) + + times.forEach { time -> + val scheduledAt = LocalDateTime.of(date, time) + val status = + if (scheduledAt.isAfter(now)) ScheduleStatus.AVAILABLE.name else ScheduleStatus.RESERVED.name + + batchArgs.add( + arrayOf( + idGenerator.create(), storeId, theme.first, date, time, + status, adminId, adminId, Timestamp.valueOf(now), Timestamp.valueOf(now) + ) + ) + + if (batchArgs.size >= 500) { + executeBatch(sql, batchArgs).also { batchArgs.clear() } + } + } + } + } + + if (batchArgs.isNotEmpty()) { + executeBatch(sql, batchArgs).also { batchArgs.clear() } + } + } + + private fun getStoreWithManagers(): List> { + return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT a.storeId, a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel = :permissionLevel", + List::class.java + ).setParameter("type", AdminType.STORE) + .setParameter("permissionLevel", AdminPermissionLevel.FULL_ACCESS) + .resultList + }.map { + val array = it as List<*> + Pair(array[0] as Long, array[1] as Long) + } + } + + private fun getThemes(): List> { + return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + entityManager.createQuery( + "SELECT t._id, t.availableMinutes, t.createdAt FROM ThemeEntity t", + List::class.java + ) + .resultList + }.map { + val array = it as List<*> + Triple(array[0] as Long, array[1] as Short, array[2] as LocalDateTime) + } + } +} + +/** + * 아래의 ReservationDataInitializer 에서 사용할 임시 DTO 클래스 + */ +data class ScheduleWithThemeParticipants( + val scheduleId: Long, + val themeMinParticipants: Short, + val themeMaxParticipants: Short, +) + +class ReservationDataInitializer : AbstractDataInitializer() { + + init { + context("예약 초기 데이터 생성") { + test("${ScheduleStatus.RESERVED}인 모든 일정에 예약을 1개씩 배정한다.") { + val chunkSize = 10_000 + + val chunkedSchedules: List> = entityManager.createQuery( + "SELECT new com.sangdol.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status", + ScheduleWithThemeParticipants::class.java + ).setParameter("status", ScheduleStatus.RESERVED).resultList.chunked(chunkSize) + + val chunkedUsers: List> = entityManager.createQuery( + "SELECT new com.sangdol.roomescape.user.web.UserContactResponse(u._id, u.name, u.phone) FROM UserEntity u", + UserContactResponse::class.java + ).resultList.chunked(chunkSize) + + + coroutineScope { + chunkedSchedules.forEachIndexed { idx, schedules -> + launch(Dispatchers.IO) { + processReservation(chunkedUsers[idx % chunkedUsers.size], schedules) + } + } + } + } + } + } + + private suspend fun processReservation( + users: List, + schedules: List + ) { + val sql = """ + INSERT INTO reservation ( + id, user_id, schedule_id, + reserver_name, reserver_contact, participant_count, requirement, + status, created_at, created_by, updated_at, updated_by + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + + val batchArgs = mutableListOf>() + + val createdAt = LocalDateTime.now() + + schedules.forEachIndexed { idx, schedule -> + val user: UserContactResponse = users[idx % users.size] + + batchArgs.add( + arrayOf( + idGenerator.create(), + user.id, + schedule.scheduleId, + user.name, + user.phone, + (schedule.themeMinParticipants..schedule.themeMaxParticipants).random(), + randomKoreanWords(length = (20..100).random()), + ReservationStatus.CONFIRMED.name, + Timestamp.valueOf(createdAt), + user.id, + Timestamp.valueOf(createdAt), + user.id, + ) + ) + + if (batchArgs.size >= 1_000) { + executeBatch(sql, batchArgs).also { batchArgs.clear() } + } + } + + if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } + } +} + +class ReservationWithPrice( + themePrice: Int, + participantCount: Short, + + val reservationId: Long, + val totalPrice: Int = (themePrice * participantCount), +) + +data class PaymentWithMethods( + val id: Long, + val totalPrice: Int, + val method: PaymentMethod +) + +class PaymentDataInitializer : AbstractDataInitializer() { + companion object { + val requestedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().toLocalDateTime()) + val approvedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().plusSeconds(5).toLocalDateTime()) + val supportedPaymentMethods = listOf(PaymentMethod.TRANSFER, PaymentMethod.EASY_PAY, PaymentMethod.CARD) + val supportedCardType = listOf(CardType.CREDIT, CardType.CHECK) + + val settlementStatus = "COMPLETED" + + val paymentSql: String = """ + INSERT INTO payment( + id, reservation_id, type, method, + payment_key, order_id, total_amount, status, + requested_at, approved_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + + val paymentDetailSql: String = """ + INSERT INTO payment_detail( + id, payment_id, supplied_amount, vat + ) VALUES (?, ?, ?, ?) + """.trimIndent() + + val paymentCardDetailSql: String = """ + INSERT INTO payment_card_detail( + id, issuer_code, card_type, owner_type, + amount, card_number, approval_number, installment_plan_months, + is_interest_free, easypay_provider_code, easypay_discount_amount + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() + + val paymentEasypayPrepaidDetailSql: String = """ + INSERT INTO payment_easypay_prepaid_detail( + id, easypay_provider_code, amount, discount_amount + ) VALUES (?, ?, ?, ?) + """.trimIndent() + + val paymentBankTransferDetailSql: String = """ + INSERT INTO payment_bank_transfer_detail( + id, bank_code, settlement_status + ) VALUES (?, ?, ?) + """.trimIndent() + } + + init { + context("결제 데이터 초기화") { + test("모든 예약에 맞춰 1:1로 결제 및 결제 상세 데이터를 생성한다.") { + val allReservations: List = entityManager.createQuery( + "SELECT t.price, r.participantCount, r._id FROM ReservationEntity r JOIN ScheduleEntity s ON s._id = r.scheduleId JOIN ThemeEntity t ON t.id = s.themeId", + List::class.java + ).resultList.map { + val items = it as List<*> + ReservationWithPrice( + themePrice = items[0] as Int, + participantCount = items[1] as Short, + reservationId = items[2] as Long + ) + } + + coroutineScope { + allReservations.chunked(10_000).forEach { reservations -> + launch(Dispatchers.IO) { + processPaymentAndDefaultDetail(reservations) + } + } + } + } + + test("기존 결제 데이터에 상세 정보(계좌이체, 카드, 간편결제) 데이터를 생성한다.") { + val allPayments: List = entityManager.createQuery( + "SELECT new com.sangdol.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId", + PaymentWithMethods::class.java + ).resultList + + coroutineScope { + allPayments.chunked(10_000).forEach { payments -> + launch(Dispatchers.IO) { + processPaymentDetail(payments) + } + } + } + } + + test("null 컴파일 에러를 피하기 위해 문자열 null로 임시 지정한 컬럼을 변경한다.") { + jdbcTemplate.execute("CREATE INDEX idx_payment_card_detail_easypay ON payment_card_detail (easypay_provider_code)") + + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + jdbcTemplate.update( + "UPDATE payment_card_detail SET easypay_provider_code = ? WHERE easypay_provider_code = ?", + null, + "null" + ) + } + + jdbcTemplate.execute("DROP INDEX idx_payment_card_detail_easypay ON payment_card_detail") + } + } + } + + private suspend fun processPaymentAndDefaultDetail(reservations: List) { + val paymentBatchArgs = mutableListOf>() + val detailBatchArgs = mutableListOf>() + + reservations.forEachIndexed { idx, reservations -> + val id = idGenerator.create() + val totalPrice = reservations.totalPrice + paymentBatchArgs.add( + arrayOf( + id, + reservations.reservationId, + PaymentType.NORMAL.name, + randomPaymentMethod(), + randomString(length = 64), + randomString(length = 20), + totalPrice, + PaymentStatus.DONE.name, + requestedAtCache, + approvedAtCache, + ) + ) + if (paymentBatchArgs.size >= 1_000) { + executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } + } + + val suppliedAmount: Int = (totalPrice * 0.9).toInt() + val vat: Int = (totalPrice - suppliedAmount) + + detailBatchArgs.add( + arrayOf( + idGenerator.create(), + id, + suppliedAmount, + vat + ) + ) + + if (detailBatchArgs.size >= 1_000) { + executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } + } + } + + if (paymentBatchArgs.isNotEmpty()) { + executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } + } + if (detailBatchArgs.isNotEmpty()) { + executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } + } + } + + private suspend fun processPaymentDetail(payments: List) { + val transferBatchArgs = mutableListOf>() + val cardBatchArgs = mutableListOf>() + val easypayPrepaidBatchArgs = mutableListOf>() + + payments.forEach { payment -> + val totalPrice = payment.totalPrice + val randomDiscountAmount = + if (totalPrice < 100 || Math.random() < 0.8) 0 else ((100..totalPrice).random() / 100) * 100 + val amount = totalPrice - randomDiscountAmount + + when (payment.method) { + PaymentMethod.TRANSFER -> { + transferBatchArgs.add( + arrayOf( + payment.id, + BankCode.entries.random().name, + settlementStatus + ) + ) + if (transferBatchArgs.size >= 1_000) { + executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } + } + } + + PaymentMethod.EASY_PAY -> { + if (Math.random() <= 0.7) { + cardBatchArgs.add( + arrayOf( + payment.id, + CardIssuerCode.entries.random().name, + supportedCardType.random().name, + CardOwnerType.PERSONAL.name, + amount, + randomCardNumber(), + randomApprovalNumber(), + randomInstallmentPlanMonths(amount), + true, + EasyPayCompanyCode.entries.random().name, + randomDiscountAmount + ) + ) + + if (cardBatchArgs.size >= 1_000) { + executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } + } + + } else { + easypayPrepaidBatchArgs.add( + arrayOf( + payment.id, + EasyPayCompanyCode.entries.random().name, + amount, + randomDiscountAmount, + ) + ) + + if (easypayPrepaidBatchArgs.size >= 1_000) { + executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } + } + } + } + + PaymentMethod.CARD -> { + cardBatchArgs.add( + arrayOf( + payment.id, + CardIssuerCode.entries.random().name, + supportedCardType.random().name, + CardOwnerType.PERSONAL.name, + totalPrice, + randomCardNumber(), + randomApprovalNumber(), + randomInstallmentPlanMonths(totalPrice), + true, + "null", + 0, + ) + ) + + if (cardBatchArgs.size >= 1_000) { + executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } + } + } + + else -> return@forEach + } + } + if (transferBatchArgs.isNotEmpty()) { + executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } + } + if (cardBatchArgs.isNotEmpty()) { + executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } + } + if (easypayPrepaidBatchArgs.isNotEmpty()) { + executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } + } + } + + private suspend fun randomPaymentMethod(): String { + val random = Math.random() + + return if (random <= 0.5) { + PaymentMethod.EASY_PAY.name + } else if (random <= 0.9) { + PaymentMethod.CARD.name + } else { + PaymentMethod.TRANSFER.name + } + } + + private suspend fun randomCardNumber(): String { + return "${(10000000..99999999).random()}****${(100..999).random()}*" + } + + private suspend fun randomApprovalNumber(): String { + return "${(10000000..99999999).random()}" + } + + private suspend fun randomInstallmentPlanMonths(amount: Int): Int { + return if (amount < 50_000 || Math.random() < 0.9) { + 0 + } else { + (1..6).random() + } + } +} + +fun randomKoreanName(): String { + val lastNames = listOf( + "김", "이", "박", "최", "정", "강", "조", "윤", "장", "임", + "오", "서", "신", "권", "황", "안", "송", "류", "홍", "전", "고", "문", "양", "손", "배", "백", "허", "유", "남", "심", "노" + ) + + return "${lastNames.random()}${if (Math.random() < 0.1) randomKoreanWords(1) else randomKoreanWords(2)}" +} + +fun randomKoreanWords(length: Int = 1): String { + val words = listOf( + "가", "나", "다", "라", "마", "바", "사", "아", "자", "차", + "카", "타", "파", "하", "강", "민", "서", "윤", "우", "진", + "현", "지", "은", "혜", "수", "영", "주", "원", "희", "경", + "선", "아", "나", "다", "라", "마", "바", "사", "아", "자", + "차", "카", "타", "파", "하", + ) + + return (1..length).joinToString("") { words.random() } +} diff --git a/service/src/test/kotlin/com/sangdol/roomescape/data/PopulationDataParser.kt b/service/src/test/kotlin/com/sangdol/data/PopulationDataParser.kt similarity index 98% rename from service/src/test/kotlin/com/sangdol/roomescape/data/PopulationDataParser.kt rename to service/src/test/kotlin/com/sangdol/data/PopulationDataParser.kt index 5ee16add..516490ca 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/data/PopulationDataParser.kt +++ b/service/src/test/kotlin/com/sangdol/data/PopulationDataParser.kt @@ -1,4 +1,4 @@ -package com.sangdol.roomescape.data +package com.sangdol.data import com.sangdol.roomescape.store.infrastructure.persistence.StoreStatus import com.sangdol.roomescape.supports.IDGenerator @@ -10,9 +10,9 @@ import java.time.ZoneId import java.time.format.DateTimeFormatter import kotlin.random.Random -const val BASE_DIR = "data" +const val BASE_DIR = "../data" const val PARSED_REGION_POPULATION_FILE = "$BASE_DIR/region_population.txt" -const val REGION_SQL_FILE = "data/region.sql" +const val REGION_SQL_FILE = "${BASE_DIR}/region.sql" const val MIN_POPULATION_FOR_PER_STORE = 200_000 /** diff --git a/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt b/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt deleted file mode 100644 index d3eaeb07..00000000 --- a/service/src/test/kotlin/com/sangdol/roomescape/data/DefaultDataInitializer.kt +++ /dev/null @@ -1,910 +0,0 @@ -//package com.sangdol.roomescape.data -// -//import com.sangdol.common.persistence.IDGenerator -//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity -//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel -//import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -//import com.sangdol.roomescape.common.util.TransactionExecutionUtil -//import com.sangdol.roomescape.payment.infrastructure.common.* -//import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus -//import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus -//import com.sangdol.roomescape.supports.AdminFixture -//import com.sangdol.roomescape.supports.FunSpecSpringbootTest -//import com.sangdol.roomescape.supports.randomPhoneNumber -//import com.sangdol.roomescape.supports.randomString -//import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty -//import com.sangdol.roomescape.user.business.SIGNUP -//import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity -//import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus -//import com.sangdol.roomescape.user.web.UserContactResponse -//import io.kotest.core.test.TestCaseOrder -//import jakarta.persistence.EntityManager -//import kotlinx.coroutines.Dispatchers -//import kotlinx.coroutines.coroutineScope -//import kotlinx.coroutines.joinAll -//import kotlinx.coroutines.launch -//import kotlinx.coroutines.sync.Semaphore -//import org.springframework.beans.factory.annotation.Autowired -//import org.springframework.jdbc.core.JdbcTemplate -//import org.springframework.test.context.ActiveProfiles -//import java.sql.Timestamp -//import java.time.LocalDateTime -//import java.time.LocalTime -//import java.time.OffsetDateTime -// -//@ActiveProfiles("test", "test-mysql") -//abstract class AbstractDataInitializer( -// val semaphore: Semaphore = Semaphore(permits = 10), -//) : FunSpecSpringbootTest( -// enableCleanerExtension = false -//) { -// @Autowired -// lateinit var entityManager: EntityManager -// -// @Autowired -// lateinit var jdbcTemplate: JdbcTemplate -// -// @Autowired -// lateinit var transactionExecutionUtil: TransactionExecutionUtil -// -// @Autowired -// lateinit var idGenerator: IDGenerator -// -// override fun testCaseOrder(): TestCaseOrder? = TestCaseOrder.Sequential -// -// suspend fun initialize() { -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0") -// -// jdbcTemplate.query("SHOW TABLES") { rs, _ -> -// rs.getString(1).lowercase() -// }.forEach { -// jdbcTemplate.execute("TRUNCATE TABLE $it") -// } -// -// jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1") -// -// this::class.java.getResource("/schema/region-data.sql")?.readText()?.let { sql -> -// jdbcTemplate.execute(sql) -// } -// } -// } -// -// suspend fun executeBatch(sql: String, batchArgs: List>) { -// semaphore.acquire() -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// jdbcTemplate.batchUpdate(sql, batchArgs) -// } -// -// semaphore.release() -// } -//} -// -//class DefaultDataInitializer : AbstractDataInitializer() { -// -// // 1. HQ Admin 추가 -// // 2. Store 추가 -> CreatedBy / UpdatedBy는 HQ Admin 중 한명으로 고정 -// // 3. Store Admin 추가 -> CreatedBy / UpdatedBy는 HQ Admin 본인으로 고정 -// init { -// lateinit var superHQAdmin: AdminEntity -// -// // 모든 테이블 초기화 + 지역 데이터 삽입 + HQ 관리자 1명 생성 -// beforeSpec { -// initialize() -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// superHQAdmin = testAuthUtil.createAdmin(AdminFixture.hqDefault.apply { -// this.createdBy = this.id -// this.updatedBy = this.id -// }) -// } -// } -// -// context("관리자, 매장, 테마 초기 데이터 생성") { -// test("각 PermissionLevel 마다 20명의 HQ 관리자 생성") { -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// AdminPermissionLevel.entries.forEach { -// repeat(20) { index -> -// AdminFixture.create( -// account = "hq_${it.name.lowercase()}_$index", -// name = randomKoreanName(), -// phone = randomPhoneNumber(), -// type = AdminType.HQ, -// permissionLevel = it -// ).apply { -// this.createdBy = superHQAdmin.id -// this.updatedBy = superHQAdmin.id -// }.also { -// entityManager.persist(it) -// } -// } -// } -// } -// } -// -// test("전체 매장 생성") { -// val storeDataInitializer = StoreDataInitializer() -// val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", -// Long::class.java -// ).setParameter("type", AdminType.HQ) -// .setParameter( -// "permissionLevels", -// listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) -// ) -// .resultList -// }.map { it.toString() } -// -// val sqlFile = storeDataInitializer.createStoreDataSqlFile(creatableAdminIds) -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// jdbcTemplate.execute(sqlFile.readText()) -// } -// } -// -// test("각 매장당 1명의 ${AdminPermissionLevel.FULL_ACCESS} 권한의 StoreManager 1명 + ${AdminPermissionLevel.WRITABLE}의 2명 + 나머지 권한은 3명씩 생성") { -// val storeAdminCountsByPermissionLevel = mapOf( -// AdminPermissionLevel.WRITABLE to 2, -// AdminPermissionLevel.READ_ALL to 3, -// AdminPermissionLevel.READ_SUMMARY to 3 -// ) -// -// val storeIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT s.id FROM StoreEntity s", -// Long::class.java -// ).resultList -// }.map { it as Long } -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// storeIds.forEach { storeId -> -// // StoreManager 1명 생성 -// val storeManager = AdminFixture.create( -// account = "$storeId", -// name = randomKoreanName(), -// phone = randomPhoneNumber(), -// type = AdminType.STORE, -// storeId = storeId, -// permissionLevel = AdminPermissionLevel.FULL_ACCESS -// ).apply { -// this.createdBy = superHQAdmin.id -// this.updatedBy = superHQAdmin.id -// }.also { -// entityManager.persist(it) -// } -// -// storeAdminCountsByPermissionLevel.forEach { (permissionLevel, count) -> -// repeat(count) { index -> -// AdminFixture.create( -// account = randomString(), -// name = randomKoreanName(), -// phone = randomPhoneNumber(), -// type = AdminType.STORE, -// storeId = storeId, -// permissionLevel = permissionLevel -// ).apply { -// this.createdBy = storeManager.id -// this.updatedBy = storeManager.id -// }.also { -// entityManager.persist(it) -// } -// } -// } -// entityManager.flush() -// entityManager.clear() -// } -// } -// } -// -// test("총 500개의 테마 생성: 지난 2년 전 부터 지금까지의 랜덤 테마 + active 상태인 1달 이내 생성 테마 10개") { -// val creatableAdminIds: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel IN (:permissionLevels)", -// Long::class.java -// ).setParameter("type", AdminType.HQ) -// .setParameter( -// "permissionLevels", -// listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) -// ) -// .resultList -// } -// val sql = -// "INSERT INTO theme (id, name, description, thumbnail_url, is_active, available_minutes, expected_minutes_from, expected_minutes_to, price, difficulty, min_participants, max_participants, created_at, created_by, updated_at, updated_by) VALUES (?," + -// "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" -// val batchSize = 100 -// val batchArgs = mutableListOf>() -// -// repeat(500) { i -> -// val randomDay = if (i <= 9) (1..30).random() else (1..365 * 2).random() -// val randomCreatedAt: LocalDateTime = LocalDateTime.now().minusDays(randomDay.toLong()) -// val randomThemeName = -// (1..7).random().let { repeat -> (1..repeat).joinToString("") { randomKoreanName() } } -// val availableMinutes = (6..20).random() * 10 -// val expectedMinutesTo = availableMinutes - ((1..3).random() * 10) -// val expectedMinutesFrom = expectedMinutesTo - ((1..2).random() * 10) -// val randomPrice = (0..40).random() * 500 -// val minParticipant = (1..10).random() -// val maxParticipant = minParticipant + (1..10).random() -// val createdBy = creatableAdminIds.random() -// -// batchArgs.add( -// arrayOf( -// idGenerator.create(), -// randomThemeName, -// "$randomThemeName 설명이에요!!", -// "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFiuCdwdz88l6pdfsRy1nFl0IHUVI7JMTQHg&s", -// if (randomDay <= 30) true else false, -// availableMinutes.toShort(), -// expectedMinutesFrom.toShort(), -// expectedMinutesTo.toShort(), -// randomPrice, -// Difficulty.entries.random().name, -// minParticipant.toShort(), -// maxParticipant.toShort(), -// Timestamp.valueOf(randomCreatedAt), -// createdBy, -// Timestamp.valueOf(randomCreatedAt), -// createdBy -// ) -// ) -// } -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// jdbcTemplate.batchUpdate(sql, batchArgs) -// } -// } -// } -// } -//} -// -//class UserDataInitializer : AbstractDataInitializer() { -// val userCount = 1_000_000 -// -// init { -// context("유저 초기 데이터 생성") { -// test("$userCount 명의 회원 생성") { -// val regions: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT r.code FROM RegionEntity r", -// String::class.java -// ).resultList -// } -// -// val chunkSize = 10_000 -// val chunks = userCount / chunkSize -// -// coroutineScope { -// (1..chunks).map { -// launch(Dispatchers.IO) { -// processUsers(chunkSize, regions) -// } -// }.joinAll() -// } -// } -// -// test("휴대폰 번호가 중복된 유저들에게 재배정") { -// val duplicatePhoneUsers: List = -// transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// """ -// SELECT u FROM UserEntity u -// WHERE u.phone IN ( -// SELECT u2.phone FROM UserEntity u2 -// GROUP BY u2.phone -// HAVING COUNT(u2.id) > 1 -// ) -// ORDER BY u.phone, u.id -// """.trimIndent(), -// UserEntity::class.java -// ).resultList -// } -// -// jdbcTemplate.execute("CREATE INDEX idx_users__phone ON users (phone)") -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// var currentPhone: String? = null -// duplicatePhoneUsers.forEach { user -> -// if (user.phone != currentPhone) { -// currentPhone = user.phone -// } else { -// var newPhone: String -// -// do { -// newPhone = randomPhoneNumber() -// val count: Long = entityManager.createQuery( -// "SELECT COUNT(u.id) FROM UserEntity u WHERE u.phone = :phone", -// Long::class.java -// ).setParameter("phone", newPhone) -// .singleResult -// -// if (count == 0L) break -// } while (true) -// -// user.phone = newPhone -// user.updatedAt = LocalDateTime.now() -// entityManager.merge(user) -// } -// } -// } -// -// jdbcTemplate.execute("DROP INDEX idx_users__phone ON users") -// } -// -// test("회원 상태 변경 이력 저장") { -// val userId: List = transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT u.id FROM UserEntity u", -// Long::class.java -// ).resultList -// } -// -// coroutineScope { -// userId.chunked(10_000).map { chunk -> -// launch(Dispatchers.IO) { -// processStatus(chunk) -// } -// }.joinAll() -// } -// } -// } -// } -// -// private suspend fun processStatus(userIds: List) { -// val sql = """ -// INSERT INTO user_status_history ( -// id, user_id, reason, status, -// created_by, updated_by, created_at, updated_at -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// val batchArgs = mutableListOf>() -// val now = LocalDateTime.now() -// -// userIds.forEach { userId -> -// batchArgs.add( -// arrayOf( -// idGenerator.create(), -// userId, -// SIGNUP, -// UserStatus.ACTIVE.name, -// userId, -// userId, -// Timestamp.valueOf(now), -// Timestamp.valueOf(now) -// ) -// ) -// } -// -// executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -// -// private suspend fun processUsers(chunkSize: Int, regions: List) { -// val sql = """ -// INSERT INTO users ( -// id, name, email, password, phone, region_code, status, -// created_by, updated_by, created_at, updated_at -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// val batchArgs = mutableListOf>() -// -// repeat(chunkSize) { -// val id: Long = idGenerator.create() -// batchArgs.add( -// arrayOf( -// id, -// randomKoreanName(), -// "${randomString()}@sangdol.com", -// randomString(), -// randomPhoneNumber(), -// regions.random(), -// UserStatus.ACTIVE.name, -// id, -// id, -// Timestamp.valueOf(LocalDateTime.now()), -// Timestamp.valueOf(LocalDateTime.now()) -// ) -// ) -// if (batchArgs.size >= 1_000) { -// executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -// } -// -// if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -//} -// -//class ScheduleDataInitializer : AbstractDataInitializer() { -// init { -// context("일정 초기 데이터 생성") { -// test("테마 생성일 기준으로 다음 3일차, 매일 5개의 일정을 모든 매장에 생성") { -// val stores: List> = getStoreWithManagers() -// val themes: List> = getThemes() -// val maxAvailableMinutes = themes.maxOf { it.second.toInt() } -// val scheduleCountPerDay = 5 -// -// val startTime = LocalTime.of(10, 0) -// var lastTime = startTime -// val times = mutableListOf() -// -// repeat(scheduleCountPerDay) { -// times.add(lastTime) -// lastTime = lastTime.plusMinutes(maxAvailableMinutes.toLong() + 10L) -// } -// -// coroutineScope { -// themes.forEach { theme -> -// launch(Dispatchers.IO) { -// processTheme(theme, stores, times) -// } -// } -// } -// } -// } -// } -// -// private suspend fun processTheme( -// theme: Triple, -// stores: List>, -// times: List -// ) { -// val sql = """ -// INSERT INTO schedule ( -// id, store_id, theme_id, date, time, status, -// created_by, updated_by, created_at, updated_at -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// -// val batchArgs = mutableListOf>() -// -// val now = LocalDateTime.now() -// stores.forEach { (storeId, adminId) -> -// (1..3).forEach { dayOffset -> -// val date = theme.third.toLocalDate().plusDays(dayOffset.toLong()) -// -// times.forEach { time -> -// val scheduledAt = LocalDateTime.of(date, time) -// val status = -// if (scheduledAt.isAfter(now)) ScheduleStatus.AVAILABLE.name else ScheduleStatus.RESERVED.name -// -// batchArgs.add( -// arrayOf( -// idGenerator.create(), storeId, theme.first, date, time, -// status, adminId, adminId, Timestamp.valueOf(now), Timestamp.valueOf(now) -// ) -// ) -// -// if (batchArgs.size >= 500) { -// executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -// } -// } -// } -// -// if (batchArgs.isNotEmpty()) { -// executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -// } -// -// private fun getStoreWithManagers(): List> { -// return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT a.storeId, a.id FROM AdminEntity a WHERE a.type = :type AND a.permissionLevel = :permissionLevel", -// List::class.java -// ).setParameter("type", AdminType.STORE) -// .setParameter("permissionLevel", AdminPermissionLevel.FULL_ACCESS) -// .resultList -// }.map { -// val array = it as List<*> -// Pair(array[0] as Long, array[1] as Long) -// } -// } -// -// private fun getThemes(): List> { -// return transactionExecutionUtil.withNewTransaction(isReadOnly = true) { -// entityManager.createQuery( -// "SELECT t._id, t.availableMinutes, t.createdAt FROM ThemeEntity t", -// List::class.java -// ) -// .resultList -// }.map { -// val array = it as List<*> -// Triple(array[0] as Long, array[1] as Short, array[2] as LocalDateTime) -// } -// } -//} -// -///** -// * 아래의 ReservationDataInitializer 에서 사용할 임시 DTO 클래스 -// */ -//data class ScheduleWithThemeParticipants( -// val scheduleId: Long, -// val themeMinParticipants: Short, -// val themeMaxParticipants: Short, -//) -// -//class ReservationDataInitializer : AbstractDataInitializer() { -// -// init { -// context("예약 초기 데이터 생성") { -// test("${ScheduleStatus.RESERVED}인 모든 일정에 예약을 1개씩 배정한다.") { -// val chunkSize = 10_000 -// -// val chunkedSchedules: List> = entityManager.createQuery( -// "SELECT new com.sangdol.roomescape.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status", -// ScheduleWithThemeParticipants::class.java -// ).setParameter("status", ScheduleStatus.RESERVED).resultList.chunked(chunkSize) -// -// val chunkedUsers: List> = entityManager.createQuery( -// "SELECT new com.sangdol.roomescape.user.web.UserContactResponse(u._id, u.name, u.phone) FROM UserEntity u", -// UserContactResponse::class.java -// ).resultList.chunked(chunkSize) -// -// -// coroutineScope { -// chunkedSchedules.forEachIndexed { idx, schedules -> -// launch(Dispatchers.IO) { -// processReservation(chunkedUsers[idx % chunkedUsers.size], schedules) -// } -// } -// } -// } -// } -// } -// -// private suspend fun processReservation( -// users: List, -// schedules: List -// ) { -// val sql = """ -// INSERT INTO reservation ( -// id, user_id, schedule_id, -// reserver_name, reserver_contact, participant_count, requirement, -// status, created_at, created_by, updated_at, updated_by -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// -// val batchArgs = mutableListOf>() -// -// val createdAt = LocalDateTime.now() -// -// schedules.forEachIndexed { idx, schedule -> -// val user: UserContactResponse = users[idx % users.size] -// -// batchArgs.add( -// arrayOf( -// idGenerator.create(), -// user.id, -// schedule.scheduleId, -// user.name, -// user.phone, -// (schedule.themeMinParticipants..schedule.themeMaxParticipants).random(), -// randomKoreanWords(length = (20..100).random()), -// ReservationStatus.CONFIRMED.name, -// Timestamp.valueOf(createdAt), -// user.id, -// Timestamp.valueOf(createdAt), -// user.id, -// ) -// ) -// -// if (batchArgs.size >= 1_000) { -// executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -// } -// -// if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() } -// } -//} -// -//class ReservationWithPrice( -// themePrice: Int, -// participantCount: Short, -// -// val reservationId: Long, -// val totalPrice: Int = (themePrice * participantCount), -//) -// -//data class PaymentWithMethods( -// val id: Long, -// val totalPrice: Int, -// val method: PaymentMethod -//) -// -//class PaymentDataInitializer : AbstractDataInitializer() { -// companion object { -// val requestedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().toLocalDateTime()) -// val approvedAtCache: Timestamp = Timestamp.valueOf(OffsetDateTime.now().plusSeconds(5).toLocalDateTime()) -// val supportedPaymentMethods = listOf(PaymentMethod.TRANSFER, PaymentMethod.EASY_PAY, PaymentMethod.CARD) -// val supportedCardType = listOf(CardType.CREDIT, CardType.CHECK) -// -// val settlementStatus = "COMPLETED" -// -// val paymentSql: String = """ -// INSERT INTO payment( -// id, reservation_id, type, method, -// payment_key, order_id, total_amount, status, -// requested_at, approved_at -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// -// val paymentDetailSql: String = """ -// INSERT INTO payment_detail( -// id, payment_id, supplied_amount, vat -// ) VALUES (?, ?, ?, ?) -// """.trimIndent() -// -// val paymentCardDetailSql: String = """ -// INSERT INTO payment_card_detail( -// id, issuer_code, card_type, owner_type, -// amount, card_number, approval_number, installment_plan_months, -// is_interest_free, easypay_provider_code, easypay_discount_amount -// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -// """.trimIndent() -// -// val paymentEasypayPrepaidDetailSql: String = """ -// INSERT INTO payment_easypay_prepaid_detail( -// id, easypay_provider_code, amount, discount_amount -// ) VALUES (?, ?, ?, ?) -// """.trimIndent() -// -// val paymentBankTransferDetailSql: String = """ -// INSERT INTO payment_bank_transfer_detail( -// id, bank_code, settlement_status -// ) VALUES (?, ?, ?) -// """.trimIndent() -// } -// -// init { -// context("결제 데이터 초기화") { -// test("모든 예약에 맞춰 1:1로 결제 및 결제 상세 데이터를 생성한다.") { -// val allReservations: List = entityManager.createQuery( -// "SELECT t.price, r.participantCount, r._id FROM ReservationEntity r JOIN ScheduleEntity s ON s._id = r.scheduleId JOIN ThemeEntity t ON t.id = s.themeId", -// List::class.java -// ).resultList.map { -// val items = it as List<*> -// ReservationWithPrice( -// themePrice = items[0] as Int, -// participantCount = items[1] as Short, -// reservationId = items[2] as Long -// ) -// } -// -// coroutineScope { -// allReservations.chunked(10_000).forEach { reservations -> -// launch(Dispatchers.IO) { -// processPaymentAndDefaultDetail(reservations) -// } -// } -// } -// } -// -// test("기존 결제 데이터에 상세 정보(계좌이체, 카드, 간편결제) 데이터를 생성한다.") { -// val allPayments: List = entityManager.createQuery( -// "SELECT new com.sangdol.roomescape.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId", -// PaymentWithMethods::class.java -// ).resultList -// -// coroutineScope { -// allPayments.chunked(10_000).forEach { payments -> -// launch(Dispatchers.IO) { -// processPaymentDetail(payments) -// } -// } -// } -// } -// -// test("null 컴파일 에러를 피하기 위해 문자열 null로 임시 지정한 컬럼을 변경한다.") { -// jdbcTemplate.execute("CREATE INDEX idx_payment_card_detail_easypay ON payment_card_detail (easypay_provider_code)") -// -// transactionExecutionUtil.withNewTransaction(isReadOnly = false) { -// jdbcTemplate.update( -// "UPDATE payment_card_detail SET easypay_provider_code = ? WHERE easypay_provider_code = ?", -// null, -// "null" -// ) -// } -// -// jdbcTemplate.execute("DROP INDEX idx_payment_card_detail_easypay ON payment_card_detail") -// } -// } -// } -// -// private suspend fun processPaymentAndDefaultDetail(reservations: List) { -// val paymentBatchArgs = mutableListOf>() -// val detailBatchArgs = mutableListOf>() -// -// reservations.forEachIndexed { idx, reservations -> -// val id = idGenerator.create() -// val totalPrice = reservations.totalPrice -// paymentBatchArgs.add( -// arrayOf( -// id, -// reservations.reservationId, -// PaymentType.NORMAL.name, -// randomPaymentMethod(), -// randomString(length = 64), -// randomString(length = 20), -// totalPrice, -// PaymentStatus.DONE.name, -// requestedAtCache, -// approvedAtCache, -// ) -// ) -// if (paymentBatchArgs.size >= 1_000) { -// executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } -// } -// -// val suppliedAmount: Int = (totalPrice * 0.9).toInt() -// val vat: Int = (totalPrice - suppliedAmount) -// -// detailBatchArgs.add( -// arrayOf( -// idGenerator.create(), -// id, -// suppliedAmount, -// vat -// ) -// ) -// -// if (detailBatchArgs.size >= 1_000) { -// executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } -// } -// } -// -// if (paymentBatchArgs.isNotEmpty()) { -// executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() } -// } -// if (detailBatchArgs.isNotEmpty()) { -// executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() } -// } -// } -// -// private suspend fun processPaymentDetail(payments: List) { -// val transferBatchArgs = mutableListOf>() -// val cardBatchArgs = mutableListOf>() -// val easypayPrepaidBatchArgs = mutableListOf>() -// -// payments.forEach { payment -> -// val totalPrice = payment.totalPrice -// val randomDiscountAmount = -// if (totalPrice < 100 || Math.random() < 0.8) 0 else ((100..totalPrice).random() / 100) * 100 -// val amount = totalPrice - randomDiscountAmount -// -// when (payment.method) { -// PaymentMethod.TRANSFER -> { -// transferBatchArgs.add( -// arrayOf( -// payment.id, -// BankCode.entries.random().name, -// settlementStatus -// ) -// ) -// if (transferBatchArgs.size >= 1_000) { -// executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } -// } -// } -// -// PaymentMethod.EASY_PAY -> { -// if (Math.random() <= 0.7) { -// cardBatchArgs.add( -// arrayOf( -// payment.id, -// CardIssuerCode.entries.random().name, -// supportedCardType.random().name, -// CardOwnerType.PERSONAL.name, -// amount, -// randomCardNumber(), -// randomApprovalNumber(), -// randomInstallmentPlanMonths(amount), -// true, -// EasyPayCompanyCode.entries.random().name, -// randomDiscountAmount -// ) -// ) -// -// if (cardBatchArgs.size >= 1_000) { -// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } -// } -// -// } else { -// easypayPrepaidBatchArgs.add( -// arrayOf( -// payment.id, -// EasyPayCompanyCode.entries.random().name, -// amount, -// randomDiscountAmount, -// ) -// ) -// -// if (easypayPrepaidBatchArgs.size >= 1_000) { -// executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } -// } -// } -// } -// -// PaymentMethod.CARD -> { -// cardBatchArgs.add( -// arrayOf( -// payment.id, -// CardIssuerCode.entries.random().name, -// supportedCardType.random().name, -// CardOwnerType.PERSONAL.name, -// totalPrice, -// randomCardNumber(), -// randomApprovalNumber(), -// randomInstallmentPlanMonths(totalPrice), -// true, -// "null", -// 0, -// ) -// ) -// -// if (cardBatchArgs.size >= 1_000) { -// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } -// } -// } -// -// else -> return@forEach -// } -// } -// if (transferBatchArgs.isNotEmpty()) { -// executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() } -// } -// if (cardBatchArgs.isNotEmpty()) { -// executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() } -// } -// if (easypayPrepaidBatchArgs.isNotEmpty()) { -// executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() } -// } -// } -// -// private suspend fun randomPaymentMethod(): String { -// val random = Math.random() -// -// return if (random <= 0.5) { -// PaymentMethod.EASY_PAY.name -// } else if (random <= 0.9) { -// PaymentMethod.CARD.name -// } else { -// PaymentMethod.TRANSFER.name -// } -// } -// -// private suspend fun randomCardNumber(): String { -// return "${(10000000..99999999).random()}****${(100..999).random()}*" -// } -// -// private suspend fun randomApprovalNumber(): String { -// return "${(10000000..99999999).random()}" -// } -// -// private suspend fun randomInstallmentPlanMonths(amount: Int): Int { -// return if (amount < 50_000 || Math.random() < 0.9) { -// 0 -// } else { -// (1..6).random() -// } -// } -//} -// -//fun randomKoreanName(): String { -// val lastNames = listOf( -// "김", "이", "박", "최", "정", "강", "조", "윤", "장", "임", -// "오", "서", "신", "권", "황", "안", "송", "류", "홍", "전", "고", "문", "양", "손", "배", "백", "허", "유", "남", "심", "노" -// ) -// -// return "${lastNames.random()}${if (Math.random() < 0.1) randomKoreanWords(1) else randomKoreanWords(2)}" -//} -// -//fun randomKoreanWords(length: Int = 1): String { -// val words = listOf( -// "가", "나", "다", "라", "마", "바", "사", "아", "자", "차", -// "카", "타", "파", "하", "강", "민", "서", "윤", "우", "진", -// "현", "지", "은", "혜", "수", "영", "주", "원", "희", "경", -// "선", "아", "나", "다", "라", "마", "바", "사", "아", "자", -// "차", "카", "타", "파", "하", -// ) -// -// return (1..length).joinToString("") { words.random() } -//} -- 2.47.2 From 888a38c1562103b89806bb69fa6bcd136aa087a9 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:23:58 +0900 Subject: [PATCH 27/39] =?UTF-8?q?refactor:=20TransactionExecutionUtil=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=9D=B4=EB=8F=99(service=20->=20persiste?= =?UTF-8?q?nce)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/persistence/build.gradle.kts | 1 + .../com/sangdol/common/persistence/PersistenceConfig.kt | 8 ++++++++ .../common/persistence}/TransactionExecutionUtil.kt | 8 +++----- .../sangdol/roomescape/payment/business/PaymentService.kt | 2 +- .../kotlin/com/sangdol/data/DefaultDataInitializer.kt | 2 +- 5 files changed, 14 insertions(+), 7 deletions(-) rename {service/src/main/kotlin/com/sangdol/roomescape/common/util => common/persistence/src/main/kotlin/com/sangdol/common/persistence}/TransactionExecutionUtil.kt (91%) diff --git a/common/persistence/build.gradle.kts b/common/persistence/build.gradle.kts index fe90c4d5..2c715a02 100644 --- a/common/persistence/build.gradle.kts +++ b/common/persistence/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { testImplementation("io.kotest.extensions:kotest-extensions-spring:1.3.0") implementation(project(":common:utils")) + implementation(project(":common:types")) } tasks.named("bootJar") { diff --git a/common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistenceConfig.kt b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistenceConfig.kt index 1cf26620..08ac9cca 100644 --- a/common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistenceConfig.kt +++ b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/PersistenceConfig.kt @@ -8,6 +8,7 @@ import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Primary import org.springframework.data.domain.AuditorAware import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import org.springframework.transaction.PlatformTransactionManager import java.util.* @Configuration @@ -29,6 +30,13 @@ class PersistenceConfig { return TsidIDGenerator(tsidFactory) } + + @Bean + fun transactionExecutionUtil( + transactionManager: PlatformTransactionManager + ): TransactionExecutionUtil { + return TransactionExecutionUtil(transactionManager) + } } class MdcAuditorAware : AuditorAware { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/util/TransactionExecutionUtil.kt b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/TransactionExecutionUtil.kt similarity index 91% rename from service/src/main/kotlin/com/sangdol/roomescape/common/util/TransactionExecutionUtil.kt rename to common/persistence/src/main/kotlin/com/sangdol/common/persistence/TransactionExecutionUtil.kt index fb05ec7b..cc964a77 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/util/TransactionExecutionUtil.kt +++ b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/TransactionExecutionUtil.kt @@ -1,17 +1,15 @@ -package com.sangdol.roomescape.common.util +package com.sangdol.common.persistence +import com.sangdol.common.types.exception.CommonErrorCode +import com.sangdol.common.types.exception.RoomescapeException import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging -import org.springframework.stereotype.Component import org.springframework.transaction.PlatformTransactionManager import org.springframework.transaction.TransactionDefinition import org.springframework.transaction.support.TransactionTemplate -import com.sangdol.common.types.exception.CommonErrorCode -import com.sangdol.common.types.exception.RoomescapeException private val log: KLogger = KotlinLogging.logger {} -@Component class TransactionExecutionUtil( private val transactionManager: PlatformTransactionManager ) { diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt index 126be6b0..dc77b7bb 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt @@ -4,7 +4,7 @@ import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import com.sangdol.roomescape.common.util.TransactionExecutionUtil +import com.sangdol.common.persistence.TransactionExecutionUtil import com.sangdol.roomescape.payment.exception.PaymentErrorCode import com.sangdol.roomescape.payment.exception.PaymentException import com.sangdol.roomescape.payment.infrastructure.client.PaymentClientCancelResponse diff --git a/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt b/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt index 2b494fb4..43d88804 100644 --- a/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt +++ b/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt @@ -4,7 +4,7 @@ import com.sangdol.common.persistence.IDGenerator import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -import com.sangdol.roomescape.common.util.TransactionExecutionUtil +import com.sangdol.common.persistence.TransactionExecutionUtil import com.sangdol.roomescape.payment.infrastructure.common.* import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus -- 2.47.2 From 1acad03e7bb09bcac1e2c6321779334392187d6e Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:24:33 +0900 Subject: [PATCH 28/39] =?UTF-8?q?refactor:=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=EC=97=90=EC=84=9C=EC=9D=98=20=EB=A1=9C=EA=B7=B8=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=84=A4=EC=A0=95=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/common/{log => config}/ProxyDataSourceConfig.kt | 2 +- .../common/{log => config}/RoomescapeLogMaskingConverter.kt | 2 +- service/src/main/resources/logback-deploy.xml | 2 +- service/src/main/resources/logback-local.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename service/src/main/kotlin/com/sangdol/roomescape/common/{log => config}/ProxyDataSourceConfig.kt (97%) rename service/src/main/kotlin/com/sangdol/roomescape/common/{log => config}/RoomescapeLogMaskingConverter.kt (86%) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/ProxyDataSourceConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/config/ProxyDataSourceConfig.kt similarity index 97% rename from service/src/main/kotlin/com/sangdol/roomescape/common/log/ProxyDataSourceConfig.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/config/ProxyDataSourceConfig.kt index 11b32ea6..3e599e9b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/ProxyDataSourceConfig.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/config/ProxyDataSourceConfig.kt @@ -1,4 +1,4 @@ -package com.sangdol.roomescape.common.log +package com.sangdol.roomescape.common.config import com.sangdol.common.log.sql.SlowQueryDataSourceFactory import com.zaxxer.hikari.HikariDataSource diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/config/RoomescapeLogMaskingConverter.kt similarity index 86% rename from service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/config/RoomescapeLogMaskingConverter.kt index e5c90829..9e6c10e0 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/log/RoomescapeLogMaskingConverter.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/config/RoomescapeLogMaskingConverter.kt @@ -1,4 +1,4 @@ -package com.sangdol.roomescape.common.log +package com.sangdol.roomescape.common.config import com.sangdol.common.web.config.JacksonConfig import com.sangdol.common.log.message.AbstractLogMaskingConverter diff --git a/service/src/main/resources/logback-deploy.xml b/service/src/main/resources/logback-deploy.xml index 0334205c..ba0cf7f9 100644 --- a/service/src/main/resources/logback-deploy.xml +++ b/service/src/main/resources/logback-deploy.xml @@ -1,7 +1,7 @@ + class="com.sangdol.roomescape.common.config.RoomescapeLogMaskingConverter"/> diff --git a/service/src/main/resources/logback-local.xml b/service/src/main/resources/logback-local.xml index 1b32137d..d920f7c0 100644 --- a/service/src/main/resources/logback-local.xml +++ b/service/src/main/resources/logback-local.xml @@ -1,7 +1,7 @@ + class="com.sangdol.roomescape.common.config.RoomescapeLogMaskingConverter"/> -- 2.47.2 From df5abf5cd46aaa8a087b0a0b96426afadf3357e9 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:39:43 +0900 Subject: [PATCH 29/39] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20service/?= =?UTF-8?q?common=EC=97=90=20=EC=9E=88=EB=8A=94=20CommonAuth.kt=EC=9D=98?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/types/web/CurrentUserContext.kt | 6 +++ .../business/dto/AdminLoginDTO.kt} | 43 +++---------------- .../sangdol/roomescape/auth/web/AuthDTO.kt | 23 +++++----- .../user/business/dto/UserLoginDTO.kt | 27 ++++++++++++ 4 files changed, 51 insertions(+), 48 deletions(-) create mode 100644 common/types/src/main/kotlin/com/sangdol/common/types/web/CurrentUserContext.kt rename service/src/main/kotlin/com/sangdol/roomescape/{common/dto/CommonAuth.kt => admin/business/dto/AdminLoginDTO.kt} (50%) create mode 100644 service/src/main/kotlin/com/sangdol/roomescape/user/business/dto/UserLoginDTO.kt diff --git a/common/types/src/main/kotlin/com/sangdol/common/types/web/CurrentUserContext.kt b/common/types/src/main/kotlin/com/sangdol/common/types/web/CurrentUserContext.kt new file mode 100644 index 00000000..ac3fd35d --- /dev/null +++ b/common/types/src/main/kotlin/com/sangdol/common/types/web/CurrentUserContext.kt @@ -0,0 +1,6 @@ +package com.sangdol.common.types.web + +data class CurrentUserContext( + val id: Long, + val name: String, +) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/dto/CommonAuth.kt b/service/src/main/kotlin/com/sangdol/roomescape/admin/business/dto/AdminLoginDTO.kt similarity index 50% rename from service/src/main/kotlin/com/sangdol/roomescape/common/dto/CommonAuth.kt rename to service/src/main/kotlin/com/sangdol/roomescape/admin/business/dto/AdminLoginDTO.kt index 5bf1053b..d9d6c4f7 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/dto/CommonAuth.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/admin/business/dto/AdminLoginDTO.kt @@ -1,20 +1,10 @@ -package com.sangdol.roomescape.common.dto +package com.sangdol.roomescape.admin.business.dto import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -import com.sangdol.roomescape.auth.web.AdminLoginSuccessResponse +import com.sangdol.roomescape.auth.web.LoginCredentials import com.sangdol.roomescape.auth.web.LoginSuccessResponse -import com.sangdol.roomescape.auth.web.UserLoginSuccessResponse -import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity - -abstract class LoginCredentials { - abstract val id: Long - abstract val password: String - abstract val name: String - - abstract fun toResponse(accessToken: String): LoginSuccessResponse -} data class AdminLoginCredentials( override val id: Long, @@ -41,28 +31,9 @@ fun AdminEntity.toCredentials() = AdminLoginCredentials( permissionLevel = this.permissionLevel ) -data class UserLoginCredentials( - override val id: Long, - override val password: String, +data class AdminLoginSuccessResponse( + override val accessToken: String, override val name: String, -) : LoginCredentials() { - override fun toResponse(accessToken: String) = UserLoginSuccessResponse( - accessToken = accessToken, - name = name - ) -} - -fun UserEntity.toCredentials() = UserLoginCredentials( - id = this.id, - password = this.password, - name = this.name, -) - -enum class PrincipalType { - USER, ADMIN -} - -data class CurrentUserContext( - val id: Long, - val name: String, -) + val type: AdminType, + val storeId: Long?, +) : LoginSuccessResponse() \ No newline at end of file diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthDTO.kt index 51ee0da3..2069f004 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthDTO.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthDTO.kt @@ -1,8 +1,11 @@ package com.sangdol.roomescape.auth.web -import jakarta.servlet.http.HttpServletRequest import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -import com.sangdol.roomescape.common.dto.PrincipalType +import jakarta.servlet.http.HttpServletRequest + +enum class PrincipalType { + USER, ADMIN +} data class LoginContext( val ipAddress: String, @@ -25,14 +28,10 @@ abstract class LoginSuccessResponse { abstract val name: String } -data class UserLoginSuccessResponse( - override val accessToken: String, - override val name: String, -) : LoginSuccessResponse() +abstract class LoginCredentials { + abstract val id: Long + abstract val password: String + abstract val name: String -data class AdminLoginSuccessResponse( - override val accessToken: String, - override val name: String, - val type: AdminType, - val storeId: Long?, -) : LoginSuccessResponse() + abstract fun toResponse(accessToken: String): LoginSuccessResponse +} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/business/dto/UserLoginDTO.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/business/dto/UserLoginDTO.kt new file mode 100644 index 00000000..deb3dc6e --- /dev/null +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/business/dto/UserLoginDTO.kt @@ -0,0 +1,27 @@ +package com.sangdol.roomescape.user.business.dto + +import com.sangdol.roomescape.auth.web.LoginCredentials +import com.sangdol.roomescape.auth.web.LoginSuccessResponse +import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity + +data class UserLoginCredentials( + override val id: Long, + override val password: String, + override val name: String, +) : LoginCredentials() { + override fun toResponse(accessToken: String) = UserLoginSuccessResponse( + accessToken = accessToken, + name = name + ) +} + +fun UserEntity.toCredentials() = UserLoginCredentials( + id = this.id, + password = this.password, + name = this.name, +) + +data class UserLoginSuccessResponse( + override val accessToken: String, + override val name: String, +) : LoginSuccessResponse() \ No newline at end of file -- 2.47.2 From 14d68bb4fbd6385442ed0e394b76967da15f4052 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:40:39 +0900 Subject: [PATCH 30/39] =?UTF-8?q?refactor:=20=EC=9D=B4=EC=A0=84=20CommonAu?= =?UTF-8?q?th=20=ED=83=80=EC=9E=85=20=EB=B6=84=EB=A6=AC=EB=A5=BC=20?= =?UTF-8?q?=EA=B8=B0=EC=A1=B4=20=ED=81=B4=EB=9E=98=EC=8A=A4=EC=97=90=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/admin/business/AdminService.kt | 4 ++-- .../roomescape/auth/business/AuthService.kt | 14 +++++--------- .../auth/business/LoginHistoryService.kt | 2 +- .../com/sangdol/roomescape/auth/docs/AuthAPI.kt | 2 +- .../persistence/LoginHistoryEntity.kt | 2 +- .../sangdol/roomescape/auth/web/AuthController.kt | 2 +- .../sangdol/roomescape/payment/docs/PaymentAPI.kt | 2 +- .../roomescape/payment/web/PaymentController.kt | 2 +- .../reservation/business/ReservationService.kt | 2 +- .../roomescape/reservation/docs/ReservationAPI.kt | 2 +- .../reservation/web/ReservationController.kt | 2 +- .../roomescape/user/business/UserService.kt | 6 +++--- .../com/sangdol/roomescape/user/docs/UserAPI.kt | 2 +- .../sangdol/roomescape/user/web/UserController.kt | 2 +- .../com/sangdol/roomescape/auth/AuthApiTest.kt | 2 +- .../roomescape/auth/FailOnSaveLoginHistoryTest.kt | 2 +- .../sangdol/roomescape/supports/TestAuthUtil.kt | 2 +- 17 files changed, 24 insertions(+), 28 deletions(-) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt b/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt index 4149bc1e..111be701 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt @@ -1,11 +1,11 @@ package com.sangdol.roomescape.admin.business import com.sangdol.common.types.audit.Auditor +import com.sangdol.roomescape.admin.business.dto.AdminLoginCredentials +import com.sangdol.roomescape.admin.business.dto.toCredentials import com.sangdol.roomescape.admin.exception.AdminErrorCode import com.sangdol.roomescape.admin.exception.AdminException import com.sangdol.roomescape.admin.infrastructure.persistence.AdminRepository -import com.sangdol.roomescape.common.dto.AdminLoginCredentials -import com.sangdol.roomescape.common.dto.toCredentials import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.data.repository.findByIdOrNull diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/business/AuthService.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/business/AuthService.kt index ff0ca22d..22cff9a8 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/business/AuthService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/business/AuthService.kt @@ -1,19 +1,15 @@ package com.sangdol.roomescape.auth.business -import io.github.oshai.kotlinlogging.KLogger -import io.github.oshai.kotlinlogging.KotlinLogging -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional import com.sangdol.roomescape.admin.business.AdminService import com.sangdol.roomescape.auth.exception.AuthErrorCode import com.sangdol.roomescape.auth.exception.AuthException import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils -import com.sangdol.roomescape.auth.web.LoginContext -import com.sangdol.roomescape.auth.web.LoginRequest -import com.sangdol.roomescape.auth.web.LoginSuccessResponse -import com.sangdol.roomescape.common.dto.LoginCredentials -import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.auth.web.* import com.sangdol.roomescape.user.business.UserService +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional private val log: KLogger = KotlinLogging.logger {} diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt index 2f6f7ba1..6cbed9bd 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/business/LoginHistoryService.kt @@ -4,7 +4,7 @@ import com.sangdol.common.persistence.IDGenerator import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryEntity import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryRepository import com.sangdol.roomescape.auth.web.LoginContext -import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.auth.web.PrincipalType import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt index 53115811..c191084f 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt @@ -1,11 +1,11 @@ package com.sangdol.roomescape.auth.docs import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.common.types.web.CurrentUserContext import com.sangdol.roomescape.auth.web.LoginRequest import com.sangdol.roomescape.auth.web.LoginSuccessResponse import com.sangdol.roomescape.auth.web.support.Public import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt index 84a88708..8d8b2e67 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/infrastructure/persistence/LoginHistoryEntity.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.auth.infrastructure.persistence import com.sangdol.common.persistence.PersistableBaseEntity -import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.auth.web.PrincipalType import jakarta.persistence.* import org.springframework.data.annotation.CreatedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt index 1d0595c7..50f676f0 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt @@ -4,7 +4,7 @@ import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.auth.business.AuthService import com.sangdol.roomescape.auth.docs.AuthAPI import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.common.types.web.CurrentUserContext import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse import org.springframework.http.ResponseEntity diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt index da6f6770..866813bf 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt @@ -3,7 +3,7 @@ package com.sangdol.roomescape.payment.docs import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.auth.web.support.User import com.sangdol.roomescape.auth.web.support.UserOnly -import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.common.types.web.CurrentUserContext import com.sangdol.roomescape.payment.web.PaymentCancelRequest import com.sangdol.roomescape.payment.web.PaymentConfirmRequest import com.sangdol.roomescape.payment.web.PaymentCreateResponse diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt index 1308fe1b..32851b96 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt @@ -2,7 +2,7 @@ package com.sangdol.roomescape.payment.web import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.common.types.web.CurrentUserContext import com.sangdol.roomescape.payment.business.PaymentService import com.sangdol.roomescape.payment.docs.PaymentAPI import jakarta.validation.Valid diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt index 7f53de4d..2638c671 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.reservation.business import com.sangdol.common.persistence.IDGenerator -import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.common.types.web.CurrentUserContext import com.sangdol.roomescape.payment.business.PaymentService import com.sangdol.roomescape.payment.web.PaymentWithDetailResponse import com.sangdol.roomescape.reservation.exception.ReservationErrorCode diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt index d3a03612..22e4e2db 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt @@ -1,9 +1,9 @@ package com.sangdol.roomescape.reservation.docs import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.common.types.web.CurrentUserContext import com.sangdol.roomescape.auth.web.support.User import com.sangdol.roomescape.auth.web.support.UserOnly -import com.sangdol.roomescape.common.dto.CurrentUserContext import com.sangdol.roomescape.reservation.web.* import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt index 4e5a3239..5f9bd7cb 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt @@ -1,8 +1,8 @@ package com.sangdol.roomescape.reservation.web import com.sangdol.common.types.web.CommonApiResponse +import com.sangdol.common.types.web.CurrentUserContext import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext import com.sangdol.roomescape.reservation.business.ReservationService import com.sangdol.roomescape.reservation.docs.ReservationAPI import jakarta.validation.Valid diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt index 2139bc14..618cdb0d 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt @@ -1,9 +1,9 @@ package com.sangdol.roomescape.user.business import com.sangdol.common.persistence.IDGenerator -import com.sangdol.roomescape.common.dto.CurrentUserContext -import com.sangdol.roomescape.common.dto.UserLoginCredentials -import com.sangdol.roomescape.common.dto.toCredentials +import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.user.business.dto.UserLoginCredentials +import com.sangdol.roomescape.user.business.dto.toCredentials import com.sangdol.roomescape.user.exception.UserErrorCode import com.sangdol.roomescape.user.exception.UserException import com.sangdol.roomescape.user.infrastructure.persistence.* diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt index adc6322a..e4e83252 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt @@ -3,7 +3,7 @@ package com.sangdol.roomescape.user.docs import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.auth.web.support.Public import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.common.types.web.CurrentUserContext import com.sangdol.roomescape.user.web.UserContactResponse import com.sangdol.roomescape.user.web.UserCreateRequest import com.sangdol.roomescape.user.web.UserCreateResponse diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt index 295ff9ce..a6469d9f 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt @@ -2,7 +2,7 @@ package com.sangdol.roomescape.user.web import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.roomescape.common.dto.CurrentUserContext +import com.sangdol.common.types.web.CurrentUserContext import com.sangdol.roomescape.user.business.UserService import com.sangdol.roomescape.user.docs.UserAPI import jakarta.validation.Valid diff --git a/service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt index 3711e2c0..c69695f1 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/auth/AuthApiTest.kt @@ -17,7 +17,7 @@ import com.sangdol.roomescape.auth.exception.AuthErrorCode import com.sangdol.roomescape.auth.infrastructure.jwt.JwtUtils import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryRepository import com.sangdol.roomescape.auth.web.LoginRequest -import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.auth.web.PrincipalType import com.sangdol.roomescape.supports.AdminFixture import com.sangdol.roomescape.supports.FunSpecSpringbootTest import com.sangdol.roomescape.supports.UserFixture diff --git a/service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt index 35bc31aa..16d616f3 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/auth/FailOnSaveLoginHistoryTest.kt @@ -6,7 +6,7 @@ import io.mockk.every import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.auth.infrastructure.persistence.LoginHistoryRepository import com.sangdol.roomescape.auth.web.LoginRequest -import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.auth.web.PrincipalType import com.sangdol.roomescape.supports.AdminFixture import com.sangdol.roomescape.supports.FunSpecSpringbootTest import com.sangdol.roomescape.supports.UserFixture diff --git a/service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt b/service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt index 500354e9..a3340a19 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/supports/TestAuthUtil.kt @@ -11,7 +11,7 @@ import com.sangdol.roomescape.admin.infrastructure.persistence.AdminEntity import com.sangdol.roomescape.admin.infrastructure.persistence.AdminRepository import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType import com.sangdol.roomescape.auth.web.LoginRequest -import com.sangdol.roomescape.common.dto.PrincipalType +import com.sangdol.roomescape.auth.web.PrincipalType import com.sangdol.roomescape.store.infrastructure.persistence.StoreRepository import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity import com.sangdol.roomescape.user.infrastructure.persistence.UserRepository -- 2.47.2 From 85b318e4beffd1700960fa6874cc8b984b67f8b5 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:42:48 +0900 Subject: [PATCH 31/39] =?UTF-8?q?refactor:=20=ED=85=8C=EB=A7=88=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=97=90=EC=84=9C=EB=A7=8C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20DateUtils=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=9D=B4=EC=A0=84=20(common=20->=20theme)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{common/util => theme/business}/DateUtils.kt | 2 +- .../com/sangdol/roomescape/theme/business/ThemeService.kt | 1 - .../roomescape/{common/util => theme}/DateUtilsTest.kt | 5 +++-- .../kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt | 7 ++++--- 4 files changed, 8 insertions(+), 7 deletions(-) rename service/src/main/kotlin/com/sangdol/roomescape/{common/util => theme/business}/DateUtils.kt (85%) rename service/src/test/kotlin/com/sangdol/roomescape/{common/util => theme}/DateUtilsTest.kt (80%) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/util/DateUtils.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/DateUtils.kt similarity index 85% rename from service/src/main/kotlin/com/sangdol/roomescape/common/util/DateUtils.kt rename to service/src/main/kotlin/com/sangdol/roomescape/theme/business/DateUtils.kt index cf2b2ebf..1a0ba300 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/common/util/DateUtils.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/DateUtils.kt @@ -1,4 +1,4 @@ -package com.sangdol.roomescape.common.util +package com.sangdol.roomescape.theme.business import java.time.DayOfWeek import java.time.LocalDate diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt index 27470d68..ebf7c4d0 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt @@ -3,7 +3,6 @@ package com.sangdol.roomescape.theme.business import com.sangdol.common.persistence.IDGenerator import com.sangdol.common.types.audit.AuditingInfo import com.sangdol.roomescape.admin.business.AdminService -import com.sangdol.roomescape.common.util.DateUtils import com.sangdol.roomescape.theme.exception.ThemeErrorCode import com.sangdol.roomescape.theme.exception.ThemeException import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity diff --git a/service/src/test/kotlin/com/sangdol/roomescape/common/util/DateUtilsTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/theme/DateUtilsTest.kt similarity index 80% rename from service/src/test/kotlin/com/sangdol/roomescape/common/util/DateUtilsTest.kt rename to service/src/test/kotlin/com/sangdol/roomescape/theme/DateUtilsTest.kt index 4be655fc..b17b6620 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/common/util/DateUtilsTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/theme/DateUtilsTest.kt @@ -1,5 +1,6 @@ -package com.sangdol.roomescape.common.util +package com.sangdol.roomescape.theme +import com.sangdol.roomescape.theme.business.DateUtils import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import java.time.LocalDate @@ -12,4 +13,4 @@ class DateUtilsTest : StringSpec({ DateUtils.getSundayOfPreviousWeek(LocalDate.of(2025, 9, i)) shouldBe expected } } -}) +}) \ No newline at end of file diff --git a/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt index 4f56757f..f589b380 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/theme/ThemeApiTest.kt @@ -1,7 +1,8 @@ package com.sangdol.roomescape.theme -import com.sangdol.roomescape.common.util.DateUtils +import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.supports.* +import com.sangdol.roomescape.theme.business.DateUtils import com.sangdol.roomescape.theme.exception.ThemeErrorCode import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository @@ -12,7 +13,6 @@ import io.kotest.matchers.collections.shouldContainInOrder import io.kotest.matchers.collections.shouldHaveSize import org.hamcrest.CoreMatchers.equalTo import org.springframework.http.HttpMethod -import com.sangdol.common.types.web.HttpStatus import java.time.LocalDate class ThemeApiTest( @@ -64,7 +64,8 @@ class ThemeApiTest( statusCode(HttpStatus.OK.value()) } ).also { res -> - val response: List = ResponseParser.parseListResponse(res.extract().path("data.themes")) + val response: List = + ResponseParser.parseListResponse(res.extract().path("data.themes")) response shouldHaveSize expectedResult.size response.map { it.id }.shouldContainInOrder(expectedResult) -- 2.47.2 From 54f3c042aee199964a5fd0c316cc06d647c8786d Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:49:09 +0900 Subject: [PATCH 32/39] =?UTF-8?q?rename:=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95=20(ProxyDataSourceConfig=20->=20?= =?UTF-8?q?SlowQueryLoggerConfig)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/{ProxyDataSourceConfig.kt => SlowQueryLoggerConfig.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename service/src/main/kotlin/com/sangdol/roomescape/common/config/{ProxyDataSourceConfig.kt => SlowQueryLoggerConfig.kt} (100%) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/common/config/ProxyDataSourceConfig.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/config/SlowQueryLoggerConfig.kt similarity index 100% rename from service/src/main/kotlin/com/sangdol/roomescape/common/config/ProxyDataSourceConfig.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/config/SlowQueryLoggerConfig.kt -- 2.47.2 From 987b30b7b8c1c63167e167abc0978c35301491ff Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 13:59:53 +0900 Subject: [PATCH 33/39] =?UTF-8?q?refactor:=20Interceptor=20=EB=93=B1=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=EC=97=90=EC=84=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=ED=95=98=EB=8A=94=20=ED=83=80=EC=9E=85=EC=9D=80=20com?= =?UTF-8?q?mon=20->=20service=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/sangdol/roomescape/admin/business/AdminService.kt | 2 +- .../main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt | 2 +- .../kotlin/com/sangdol/roomescape/auth/web/AuthController.kt | 2 +- .../com/sangdol/roomescape/common/types}/AuditingInfo.kt | 2 +- .../sangdol/roomescape/common/types}/CurrentUserContext.kt | 2 +- .../kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt | 2 +- .../com/sangdol/roomescape/payment/web/PaymentController.kt | 2 +- .../roomescape/reservation/business/ReservationService.kt | 2 +- .../com/sangdol/roomescape/reservation/docs/ReservationAPI.kt | 2 +- .../roomescape/reservation/web/ReservationController.kt | 2 +- .../sangdol/roomescape/schedule/business/ScheduleService.kt | 4 ++-- .../com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt | 2 +- .../roomescape/schedule/web/AdminScheduleController.kt | 2 +- .../com/sangdol/roomescape/store/business/StoreService.kt | 2 +- .../kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt | 2 +- .../com/sangdol/roomescape/theme/business/ThemeService.kt | 2 +- .../kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt | 2 +- .../com/sangdol/roomescape/user/business/UserService.kt | 2 +- .../main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt | 2 +- .../kotlin/com/sangdol/roomescape/user/web/UserController.kt | 2 +- .../com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt | 2 +- 21 files changed, 22 insertions(+), 22 deletions(-) rename {common/types/src/main/kotlin/com/sangdol/common/types/audit => service/src/main/kotlin/com/sangdol/roomescape/common/types}/AuditingInfo.kt (88%) rename {common/types/src/main/kotlin/com/sangdol/common/types/web => service/src/main/kotlin/com/sangdol/roomescape/common/types}/CurrentUserContext.kt (62%) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt b/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt index 111be701..e59c22d2 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/admin/business/AdminService.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.admin.business -import com.sangdol.common.types.audit.Auditor +import com.sangdol.roomescape.common.types.Auditor import com.sangdol.roomescape.admin.business.dto.AdminLoginCredentials import com.sangdol.roomescape.admin.business.dto.toCredentials import com.sangdol.roomescape.admin.exception.AdminErrorCode diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt index c191084f..65a2c016 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/docs/AuthAPI.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.auth.docs import com.sangdol.common.types.web.CommonApiResponse -import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.common.types.CurrentUserContext import com.sangdol.roomescape.auth.web.LoginRequest import com.sangdol.roomescape.auth.web.LoginSuccessResponse import com.sangdol.roomescape.auth.web.support.Public diff --git a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt index 50f676f0..b335e36a 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/auth/web/AuthController.kt @@ -4,7 +4,7 @@ import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.auth.business.AuthService import com.sangdol.roomescape.auth.docs.AuthAPI import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.common.types.CurrentUserContext import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse import org.springframework.http.ResponseEntity diff --git a/common/types/src/main/kotlin/com/sangdol/common/types/audit/AuditingInfo.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/types/AuditingInfo.kt similarity index 88% rename from common/types/src/main/kotlin/com/sangdol/common/types/audit/AuditingInfo.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/types/AuditingInfo.kt index 88ea23d1..83693c60 100644 --- a/common/types/src/main/kotlin/com/sangdol/common/types/audit/AuditingInfo.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/types/AuditingInfo.kt @@ -1,4 +1,4 @@ -package com.sangdol.common.types.audit +package com.sangdol.roomescape.common.types import java.time.LocalDateTime diff --git a/common/types/src/main/kotlin/com/sangdol/common/types/web/CurrentUserContext.kt b/service/src/main/kotlin/com/sangdol/roomescape/common/types/CurrentUserContext.kt similarity index 62% rename from common/types/src/main/kotlin/com/sangdol/common/types/web/CurrentUserContext.kt rename to service/src/main/kotlin/com/sangdol/roomescape/common/types/CurrentUserContext.kt index ac3fd35d..e7d9e684 100644 --- a/common/types/src/main/kotlin/com/sangdol/common/types/web/CurrentUserContext.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/common/types/CurrentUserContext.kt @@ -1,4 +1,4 @@ -package com.sangdol.common.types.web +package com.sangdol.roomescape.common.types data class CurrentUserContext( val id: Long, diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt index 866813bf..171df140 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/docs/PaymentAPI.kt @@ -3,7 +3,7 @@ package com.sangdol.roomescape.payment.docs import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.auth.web.support.User import com.sangdol.roomescape.auth.web.support.UserOnly -import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.common.types.CurrentUserContext import com.sangdol.roomescape.payment.web.PaymentCancelRequest import com.sangdol.roomescape.payment.web.PaymentConfirmRequest import com.sangdol.roomescape.payment.web.PaymentCreateResponse diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt index 32851b96..f4830f20 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/web/PaymentController.kt @@ -2,7 +2,7 @@ package com.sangdol.roomescape.payment.web import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.common.types.CurrentUserContext import com.sangdol.roomescape.payment.business.PaymentService import com.sangdol.roomescape.payment.docs.PaymentAPI import jakarta.validation.Valid diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt index 2638c671..f26baa16 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/business/ReservationService.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.reservation.business import com.sangdol.common.persistence.IDGenerator -import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.common.types.CurrentUserContext import com.sangdol.roomescape.payment.business.PaymentService import com.sangdol.roomescape.payment.web.PaymentWithDetailResponse import com.sangdol.roomescape.reservation.exception.ReservationErrorCode diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt index 22e4e2db..2d53eecf 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/docs/ReservationAPI.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.reservation.docs import com.sangdol.common.types.web.CommonApiResponse -import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.common.types.CurrentUserContext import com.sangdol.roomescape.auth.web.support.User import com.sangdol.roomescape.auth.web.support.UserOnly import com.sangdol.roomescape.reservation.web.* diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt index 5f9bd7cb..b218ea2b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationController.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.reservation.web import com.sangdol.common.types.web.CommonApiResponse -import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.common.types.CurrentUserContext import com.sangdol.roomescape.auth.web.support.User import com.sangdol.roomescape.reservation.business.ReservationService import com.sangdol.roomescape.reservation.docs.ReservationAPI diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt index f9fb6e99..3adfedcb 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/business/ScheduleService.kt @@ -2,8 +2,8 @@ package com.sangdol.roomescape.schedule.business import ScheduleException import com.sangdol.common.persistence.IDGenerator -import com.sangdol.common.types.audit.AuditingInfo -import com.sangdol.common.types.audit.Auditor +import com.sangdol.roomescape.common.types.AuditingInfo +import com.sangdol.roomescape.common.types.Auditor import com.sangdol.roomescape.admin.business.AdminService import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview import com.sangdol.roomescape.schedule.exception.ScheduleErrorCode diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt index be8ca4c5..9e2c4ea7 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/docs/ScheduleAPI.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.schedule.docs -import com.sangdol.common.types.audit.AuditingInfo +import com.sangdol.roomescape.common.types.AuditingInfo import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType import com.sangdol.roomescape.admin.infrastructure.persistence.Privilege diff --git a/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt index 14ed61b8..3c7b89d4 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/schedule/web/AdminScheduleController.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.schedule.web -import com.sangdol.common.types.audit.AuditingInfo +import com.sangdol.roomescape.common.types.AuditingInfo import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.schedule.business.ScheduleService import com.sangdol.roomescape.schedule.docs.AdminScheduleAPI diff --git a/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt index ba829e84..8dd3acc9 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/business/StoreService.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.store.business import com.sangdol.common.persistence.IDGenerator -import com.sangdol.common.types.audit.AuditingInfo +import com.sangdol.roomescape.common.types.AuditingInfo import com.sangdol.roomescape.admin.business.AdminService import com.sangdol.roomescape.region.business.RegionService import com.sangdol.roomescape.store.exception.StoreErrorCode diff --git a/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt index 2f60f2f2..193e719b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/store/web/AdminStoreDto.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.store.web -import com.sangdol.common.types.audit.AuditingInfo +import com.sangdol.roomescape.common.types.AuditingInfo import com.sangdol.roomescape.region.web.RegionInfoResponse import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt index ebf7c4d0..5cf3b4ea 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/business/ThemeService.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.theme.business import com.sangdol.common.persistence.IDGenerator -import com.sangdol.common.types.audit.AuditingInfo +import com.sangdol.roomescape.common.types.AuditingInfo import com.sangdol.roomescape.admin.business.AdminService import com.sangdol.roomescape.theme.exception.ThemeErrorCode import com.sangdol.roomescape.theme.exception.ThemeException diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt index 5f616075..94c5a195 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/AdminThemeDto.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.theme.web -import com.sangdol.common.types.audit.AuditingInfo +import com.sangdol.roomescape.common.types.AuditingInfo import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt index 618cdb0d..7aa00234 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/business/UserService.kt @@ -1,7 +1,7 @@ package com.sangdol.roomescape.user.business import com.sangdol.common.persistence.IDGenerator -import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.common.types.CurrentUserContext import com.sangdol.roomescape.user.business.dto.UserLoginCredentials import com.sangdol.roomescape.user.business.dto.toCredentials import com.sangdol.roomescape.user.exception.UserErrorCode diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt index e4e83252..6e3d116d 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/docs/UserAPI.kt @@ -3,7 +3,7 @@ package com.sangdol.roomescape.user.docs import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.auth.web.support.Public import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.common.types.CurrentUserContext import com.sangdol.roomescape.user.web.UserContactResponse import com.sangdol.roomescape.user.web.UserCreateRequest import com.sangdol.roomescape.user.web.UserCreateResponse diff --git a/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt b/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt index a6469d9f..097830ee 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/user/web/UserController.kt @@ -2,7 +2,7 @@ package com.sangdol.roomescape.user.web import com.sangdol.common.types.web.CommonApiResponse import com.sangdol.roomescape.auth.web.support.User -import com.sangdol.common.types.web.CurrentUserContext +import com.sangdol.roomescape.common.types.CurrentUserContext import com.sangdol.roomescape.user.business.UserService import com.sangdol.roomescape.user.docs.UserAPI import jakarta.validation.Valid diff --git a/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt index 3d1969fa..f51663dc 100644 --- a/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt +++ b/service/src/test/kotlin/com/sangdol/roomescape/schedule/AdminScheduleApiTest.kt @@ -1,6 +1,6 @@ package com.sangdol.roomescape.schedule -import com.sangdol.common.types.audit.Auditor +import com.sangdol.roomescape.common.types.Auditor import com.sangdol.common.types.web.HttpStatus import com.sangdol.roomescape.admin.infrastructure.persistence.AdminPermissionLevel import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType -- 2.47.2 From 463f930b93a7be6d99b51cfad0858e562c1fc241 Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 18:02:06 +0900 Subject: [PATCH 34/39] =?UTF-8?q?feat:=20=EB=88=84=EB=9D=BD=EB=90=9C=20set?= =?UTF-8?q?tings.gradle=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- settings.gradle | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 settings.gradle diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..05ce3b80 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +rootProject.name = 'roomescape' + +include 'service' +include 'common' +include 'common:web' +include 'common:types' +include 'common:log' +include 'common:persistence' +include 'common:utils' \ No newline at end of file -- 2.47.2 From 9b6bb91095bd0c3416c4b92641b39551ce5302bf Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 28 Sep 2025 18:21:48 +0900 Subject: [PATCH 35/39] =?UTF-8?q?remove:=20API=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=B4=20=EB=AF=B8=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EB=90=98=EB=8A=94=20=ED=83=80=EC=9E=85=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/sangdol/roomescape/reservation/web/ReservationDto.kt | 4 ---- .../main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt | 4 ---- 2 files changed, 8 deletions(-) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt index 21b0ff88..2349b81b 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/reservation/web/ReservationDto.kt @@ -99,7 +99,3 @@ fun ReservationEntity.toReservationDetailRetrieveResponse( data class ReservationCancelRequest( val cancelReason: String ) - -data class MostReservedThemeIdListResponse( - val themeIds: List -) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt index cbe6f062..46463d38 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/theme/web/ThemeDto.kt @@ -3,10 +3,6 @@ package com.sangdol.roomescape.theme.web import com.sangdol.roomescape.theme.business.domain.ThemeInfo import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity -data class ThemeIdListRequest( - val themeIds: List -) - data class ThemeInfoResponse( val id: Long, val name: String, -- 2.47.2 From 83a5919e9ca04e9d8ce7123eed317ed59ccebd51 Mon Sep 17 00:00:00 2001 From: pricelees Date: Mon, 29 Sep 2025 15:43:35 +0900 Subject: [PATCH 36/39] =?UTF-8?q?refactor:=20TransactionExecutionUtil=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=83=80=EC=9E=85=20Nullable=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/persistence/TransactionExecutionUtil.kt | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/common/persistence/src/main/kotlin/com/sangdol/common/persistence/TransactionExecutionUtil.kt b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/TransactionExecutionUtil.kt index cc964a77..e2bda131 100644 --- a/common/persistence/src/main/kotlin/com/sangdol/common/persistence/TransactionExecutionUtil.kt +++ b/common/persistence/src/main/kotlin/com/sangdol/common/persistence/TransactionExecutionUtil.kt @@ -1,29 +1,19 @@ package com.sangdol.common.persistence -import com.sangdol.common.types.exception.CommonErrorCode -import com.sangdol.common.types.exception.RoomescapeException -import io.github.oshai.kotlinlogging.KLogger -import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.transaction.PlatformTransactionManager import org.springframework.transaction.TransactionDefinition import org.springframework.transaction.support.TransactionTemplate -private val log: KLogger = KotlinLogging.logger {} - class TransactionExecutionUtil( private val transactionManager: PlatformTransactionManager ) { - fun withNewTransaction(isReadOnly: Boolean, action: () -> T): T { + fun withNewTransaction(isReadOnly: Boolean, action: () -> T?): T? { val transactionTemplate = TransactionTemplate(transactionManager).apply { this.isReadOnly = isReadOnly this.propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRES_NEW } return transactionTemplate.execute { action() } - ?: run { - log.error { "[TransactionExecutionUtil.withNewTransaction] 트랜잭션 작업 중 예상치 못한 null 반환 " } - throw RoomescapeException(CommonErrorCode.UNEXPECTED_SERVER_ERROR) - } } } -- 2.47.2 From ddab3b18a6bef810d5fe8ef3b9a3225b73e09fd1 Mon Sep 17 00:00:00 2001 From: pricelees Date: Mon, 29 Sep 2025 15:43:48 +0900 Subject: [PATCH 37/39] =?UTF-8?q?refactor:=20TransactionExecutionUtil?= =?UTF-8?q?=EC=9D=98=20nullable=20=EB=B0=98=ED=99=98=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20?= =?UTF-8?q?=EA=B8=B0=EC=A1=B4=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/business/PaymentService.kt | 3 +++ .../com/sangdol/data/DefaultDataInitializer.kt | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt index dc77b7bb..8336919c 100644 --- a/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/PaymentService.kt @@ -41,6 +41,9 @@ class PaymentService( val detail: PaymentDetailEntity = paymentWriter.createDetail(clientConfirmResponse, payment.id) PaymentCreateResponse(paymentId = payment.id, detailId = detail.id) + } ?: run { + log.warn { "[PaymentService.confirm] 결제 확정 중 예상치 못한 null 반환" } + throw PaymentException(PaymentErrorCode.PAYMENT_UNEXPECTED_ERROR) } } diff --git a/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt b/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt index 43d88804..04f7ab1c 100644 --- a/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt +++ b/service/src/test/kotlin/com/sangdol/data/DefaultDataInitializer.kt @@ -135,7 +135,7 @@ class DefaultDataInitializer : AbstractDataInitializer() { listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) ) .resultList - }.map { it.toString() } + }!!.map { it.toString() } val sqlFile = storeDataInitializer.createStoreDataSqlFile(creatableAdminIds) @@ -156,7 +156,7 @@ class DefaultDataInitializer : AbstractDataInitializer() { "SELECT s.id FROM StoreEntity s", Long::class.java ).resultList - }.map { it as Long } + }!!.map { it as Long } transactionExecutionUtil.withNewTransaction(isReadOnly = false) { storeIds.forEach { storeId -> @@ -209,7 +209,7 @@ class DefaultDataInitializer : AbstractDataInitializer() { listOf(AdminPermissionLevel.FULL_ACCESS, AdminPermissionLevel.WRITABLE) ) .resultList - } + }!! val sql = "INSERT INTO theme (id, name, description, thumbnail_url, is_active, available_minutes, expected_minutes_from, expected_minutes_to, price, difficulty, min_participants, max_participants, created_at, created_by, updated_at, updated_by) VALUES (?," + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" @@ -270,7 +270,7 @@ class UserDataInitializer : AbstractDataInitializer() { "SELECT r.code FROM RegionEntity r", String::class.java ).resultList - } + }!! val chunkSize = 10_000 val chunks = userCount / chunkSize @@ -299,7 +299,7 @@ class UserDataInitializer : AbstractDataInitializer() { """.trimIndent(), UserEntity::class.java ).resultList - } + }!! jdbcTemplate.execute("CREATE INDEX idx_users__phone ON users (phone)") @@ -338,7 +338,7 @@ class UserDataInitializer : AbstractDataInitializer() { "SELECT u.id FROM UserEntity u", Long::class.java ).resultList - } + }!! coroutineScope { userId.chunked(10_000).map { chunk -> @@ -494,7 +494,7 @@ class ScheduleDataInitializer : AbstractDataInitializer() { ).setParameter("type", AdminType.STORE) .setParameter("permissionLevel", AdminPermissionLevel.FULL_ACCESS) .resultList - }.map { + }!!.map { val array = it as List<*> Pair(array[0] as Long, array[1] as Long) } @@ -507,7 +507,7 @@ class ScheduleDataInitializer : AbstractDataInitializer() { List::class.java ) .resultList - }.map { + }!!.map { val array = it as List<*> Triple(array[0] as Long, array[1] as Short, array[2] as LocalDateTime) } -- 2.47.2 From 51ad28ddcaa52c1b7bfda9263bcff13acbcb57c2 Mon Sep 17 00:00:00 2001 From: pricelees Date: Mon, 29 Sep 2025 15:43:58 +0900 Subject: [PATCH 38/39] =?UTF-8?q?test:=20TransactionExecutionUtil=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/persistence/build.gradle.kts | 2 +- .../TransactionExecutionUtilTest.kt | 75 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 common/persistence/src/test/kotlin/com/sangdol/common/persistence/TransactionExecutionUtilTest.kt diff --git a/common/persistence/build.gradle.kts b/common/persistence/build.gradle.kts index 2c715a02..bf2cb2ad 100644 --- a/common/persistence/build.gradle.kts +++ b/common/persistence/build.gradle.kts @@ -1,4 +1,3 @@ -import org.gradle.kotlin.dsl.named import org.springframework.boot.gradle.tasks.bundling.BootJar plugins { @@ -15,6 +14,7 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("io.kotest:kotest-runner-junit5:5.9.1") testImplementation("io.kotest.extensions:kotest-extensions-spring:1.3.0") + testImplementation("io.mockk:mockk:1.14.4") implementation(project(":common:utils")) implementation(project(":common:types")) diff --git a/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TransactionExecutionUtilTest.kt b/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TransactionExecutionUtilTest.kt new file mode 100644 index 00000000..64f4faa6 --- /dev/null +++ b/common/persistence/src/test/kotlin/com/sangdol/common/persistence/TransactionExecutionUtilTest.kt @@ -0,0 +1,75 @@ +package com.sangdol.common.persistence + +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.equality.shouldBeEqualUsingFields +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.mockk.clearMocks +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify +import org.junit.jupiter.api.assertThrows +import org.springframework.transaction.PlatformTransactionManager +import org.springframework.transaction.TransactionDefinition +import org.springframework.transaction.TransactionStatus +import org.springframework.transaction.support.DefaultTransactionDefinition + +class TransactionExecutionUtilTest() : FunSpec() { + private val transactionManager = mockk(relaxed = true) + private val transactionExecutionUtil = TransactionExecutionUtil(transactionManager) + + init { + context("withNewTransaction") { + + beforeTest { + clearMocks(transactionManager) + } + + val body = TestPersistableBaseEntity(123458192L, "hello") + + test("지정한 action이 성공하면, 해당 값을 반환하고 트랜잭션을 커밋한다.") { + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + body + }.also { + it.shouldNotBeNull() + it shouldBeEqualUsingFields body + verify { transactionManager.commit(any()) } + verify(exactly = 0) { transactionManager.rollback(any()) } + } + } + + test("지정한 action 실행 도중 예외가 발생하면, 예외를 던지고 트랜잭션을 롤백한다.") { + assertThrows { + transactionExecutionUtil.withNewTransaction(isReadOnly = false) { + throw RuntimeException() + } + }.also { + verify { transactionManager.rollback(any()) } + verify(exactly = 0) { transactionManager.commit(any()) } + } + } + + test("isReadOnly=true 지정시 읽기 전용 트랜잭션으로 실행한다.") { + val transactionStatus = mockk(relaxed = true) + val transactionDefinitionSlot = slot() + + every { + transactionManager.getTransaction(capture(transactionDefinitionSlot)) + } returns transactionStatus + + transactionExecutionUtil.withNewTransaction(isReadOnly = true) { + "hello" + }.also { + assertSoftly(transactionDefinitionSlot.captured) { + this.isReadOnly shouldBe true + this.propagationBehavior shouldBe DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW + } + + verify { transactionManager.commit(any()) } + } + } + } + } +} -- 2.47.2 From c9fa802cbc1f23ac61d78b59bf4540410a3c0578 Mon Sep 17 00:00:00 2001 From: pricelees Date: Mon, 29 Sep 2025 22:00:45 +0900 Subject: [PATCH 39/39] =?UTF-8?q?test:=20PaymentType=20enum=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/payment/PaymentTypeTest.kt | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentTypeTest.kt diff --git a/service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentTypeTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentTypeTest.kt new file mode 100644 index 00000000..5831bc95 --- /dev/null +++ b/service/src/test/kotlin/com/sangdol/roomescape/payment/PaymentTypeTest.kt @@ -0,0 +1,148 @@ +package com.sangdol.roomescape.payment + +import com.sangdol.roomescape.payment.exception.PaymentErrorCode +import com.sangdol.roomescape.payment.exception.PaymentException +import com.sangdol.roomescape.payment.infrastructure.common.BankCode +import com.sangdol.roomescape.payment.infrastructure.common.CardIssuerCode +import com.sangdol.roomescape.payment.infrastructure.common.CardOwnerType +import com.sangdol.roomescape.payment.infrastructure.common.CardType +import com.sangdol.roomescape.payment.infrastructure.common.EasyPayCompanyCode +import com.sangdol.roomescape.payment.infrastructure.common.PaymentMethod +import com.sangdol.roomescape.payment.infrastructure.common.PaymentStatus +import com.sangdol.roomescape.payment.infrastructure.common.PaymentType +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldHaveLength +import org.junit.jupiter.api.assertThrows + +class PaymentTypeTest : FunSpec({ + + context("PaymentType") { + test("한글 이름으로 가져온다.") { + PaymentType.entries.forEach { + PaymentType.Companion.get(it.name) shouldBe it + } + } + + test("없는 이름이면 실패한다.") { + assertThrows { + PaymentType.Companion.get("NORMAR") + }.also { + it.errorCode shouldBe PaymentErrorCode.TYPE_NOT_FOUND + } + } + } + + context("PaymentMethod") { + test("결제수단 한글명으로 가져온다.") { + PaymentMethod.entries.forEach { + PaymentMethod.Companion.get(it.koreanName) shouldBe it + } + } + + test("없는 이름이면 실패한다.") { + assertThrows { + PaymentMethod.Companion.get("카드12") + }.also { + it.errorCode shouldBe PaymentErrorCode.TYPE_NOT_FOUND + } + } + } + + context("PaymentStatus") { + test("상수 이름으로 가져온다.") { + PaymentStatus.entries.forEach { + PaymentStatus.Companion.get(it.name) shouldBe it + } + } + + test("없는 이름이면 실패한다.") { + assertThrows { + PaymentStatus.Companion.get("DONEE") + }.also { + it.errorCode shouldBe PaymentErrorCode.TYPE_NOT_FOUND + } + } + } + + context("CardType") { + test("한글 이름으로 가져온다.") { + CardType.entries.forEach { + CardType.Companion.get(it.koreanName) shouldBe it + } + } + + test("없는 이름을 입력하면 UNKNOWN을 반환한다.") { + CardType.Companion.get("신용카드") shouldBe CardType.UNKNOWN + } + } + + context("CardOwnerType") { + test("한글 이름으로 가져온다.") { + CardOwnerType.entries.forEach { + CardOwnerType.Companion.get(it.koreanName) shouldBe it + } + } + + test("없는 이름을 입력하면 UNKNOWN을 반환한다.") { + CardOwnerType.Companion.get("개인카드") shouldBe CardOwnerType.UNKNOWN + } + } + + context("BankCode") { + test("숫자 코드로 가져온다.") { + BankCode.entries.forEach { + BankCode.Companion.get(it.code) shouldBe it + } + } + + test("숫자가 두글자이면 앞에 0을 붙인다.") { + assertSoftly(BankCode.SHINHAN.code) { + this shouldHaveLength 3 + BankCode.Companion.get(this.substring(1)) shouldBe BankCode.Companion.get(this) + + } + } + + test("없는 코드이면 실패한다") { + assertThrows { + BankCode.Companion.get("9999") + }.also { + it.errorCode shouldBe PaymentErrorCode.ORGANIZATION_CODE_NOT_FOUND + } + } + } + + context("CardIssuerCode") { + test("숫자 코드로 가져온다.") { + CardIssuerCode.entries.forEach { + CardIssuerCode.Companion.get(it.code) shouldBe it + } + } + + test("없는 코드이면 실패한다") { + assertThrows { + CardIssuerCode.Companion.get("9999") + }.also { + it.errorCode shouldBe PaymentErrorCode.ORGANIZATION_CODE_NOT_FOUND + } + } + } + + context("EasyPayCompanyCode") { + test("한글 이름으로 가져온다.") { + EasyPayCompanyCode.entries.forEach { + EasyPayCompanyCode.Companion.get(it.koreanName) shouldBe it + } + } + + test("없는 이름이면 실패한다") { + assertThrows { + EasyPayCompanyCode.Companion.get("상돌페이") + }.also { + it.errorCode shouldBe PaymentErrorCode.ORGANIZATION_CODE_NOT_FOUND + } + } + } +}) \ No newline at end of file -- 2.47.2