├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── todo.md │ ├── bug.md │ └── refactor.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── build.yml │ └── CI.yml ├── simtong-domain ├── src │ ├── test │ │ └── kotlin │ │ │ └── team │ │ │ └── comit │ │ │ └── simtong │ │ │ └── .gitkeep │ └── main │ │ └── kotlin │ │ └── team │ │ └── comit │ │ └── simtong │ │ ├── domain │ │ ├── file │ │ │ ├── service │ │ │ │ └── .gitkeep │ │ │ ├── spi │ │ │ │ └── QueryEmployeeCertificatePort.kt │ │ │ ├── model │ │ │ │ └── EmployeeCertificate.kt │ │ │ └── exception │ │ │ │ └── FileExceptions.kt │ │ ├── menu │ │ │ ├── service │ │ │ │ └── .gitkeep │ │ │ ├── spi │ │ │ │ ├── MenuSecurityPort.kt │ │ │ │ ├── MenuQueryUserPort.kt │ │ │ │ └── QueryMenuPort.kt │ │ │ ├── model │ │ │ │ └── Menu.kt │ │ │ └── exception │ │ │ │ └── MenuExceptions.kt │ │ ├── spot │ │ │ ├── service │ │ │ │ └── .gitkeep │ │ │ ├── spi │ │ │ │ ├── .gitkeep │ │ │ │ └── QuerySpotPort.kt │ │ │ ├── exception │ │ │ │ └── SpotExceptions.kt │ │ │ └── model │ │ │ │ └── Spot.kt │ │ ├── team │ │ │ ├── service │ │ │ │ └── .gitkeep │ │ │ ├── spi │ │ │ │ └── QueryTeamPort.kt │ │ │ ├── model │ │ │ │ └── Team.kt │ │ │ └── exception │ │ │ │ └── TeamExceptions.kt │ │ ├── user │ │ │ ├── service │ │ │ │ └── .gitkeep │ │ │ ├── model │ │ │ │ ├── DeviceToken.kt │ │ │ │ ├── Authority.kt │ │ │ │ └── User.kt │ │ │ ├── spi │ │ │ │ ├── UserQueryTeamPort.kt │ │ │ │ ├── UserQueryAuthCodeLimitPort.kt │ │ │ │ ├── UserSecurityPort.kt │ │ │ │ ├── UserQueryEmployeeCertificatePort.kt │ │ │ │ ├── UserQuerySpotPort.kt │ │ │ │ └── QueryUserPort.kt │ │ │ └── exception │ │ │ │ └── UserExceptions.kt │ │ ├── holiday │ │ │ ├── service │ │ │ │ └── .gitkeep │ │ │ ├── spi │ │ │ │ ├── HolidaySecurityPort.kt │ │ │ │ ├── HolidayQueryUserPort.kt │ │ │ │ ├── vo │ │ │ │ │ └── EmployeeHoliday.kt │ │ │ │ ├── QueryHolidayPeriodPort.kt │ │ │ │ └── QueryHolidayPort.kt │ │ │ └── model │ │ │ │ ├── HolidayType.kt │ │ │ │ ├── HolidayStatus.kt │ │ │ │ ├── HolidayQueryType.kt │ │ │ │ ├── HolidayPeriod.kt │ │ │ │ └── Holiday.kt │ │ ├── schedule │ │ │ ├── service │ │ │ │ └── .gitkeep │ │ │ ├── model │ │ │ │ ├── Scope.kt │ │ │ │ └── Schedule.kt │ │ │ ├── spi │ │ │ │ ├── ScheduleSecurityPort.kt │ │ │ │ ├── ScheduleQueryUserPort.kt │ │ │ │ ├── ScheduleQuerySpotPort.kt │ │ │ │ └── QuerySchedulePort.kt │ │ │ ├── vo │ │ │ │ └── SpotSchedule.kt │ │ │ └── exception │ │ │ │ └── ScheduleExceptions.kt │ │ ├── auth │ │ │ ├── spi │ │ │ │ ├── QueryAuthCodePort.kt │ │ │ │ └── QueryAuthCodeLimitPort.kt │ │ │ └── model │ │ │ │ ├── RefreshToken.kt │ │ │ │ └── AuthCode.kt │ │ └── notification │ │ │ ├── model │ │ │ ├── NotificationReceiver.kt │ │ │ ├── NotificationType.kt │ │ │ └── Notification.kt │ │ │ └── exception │ │ │ └── NotificationExceptions.kt │ │ └── global │ │ ├── annotation │ │ ├── Aggregate.kt │ │ ├── DomainService.kt │ │ └── Default.kt │ │ ├── exception │ │ ├── BusinessException.kt │ │ └── DomainExceptions.kt │ │ ├── DomainProperties.kt │ │ └── DomainPropertiesPrefix.kt └── build.gradle.kts ├── simtong-application ├── src │ ├── main │ │ └── kotlin │ │ │ └── team │ │ │ └── comit │ │ │ └── simtong │ │ │ ├── domain │ │ │ ├── auth │ │ │ │ ├── exception │ │ │ │ │ └── .gitkeep │ │ │ │ ├── spi │ │ │ │ │ ├── RefreshTokenPort.kt │ │ │ │ │ ├── AuthCodePort.kt │ │ │ │ │ ├── JwtPort.kt │ │ │ │ │ ├── SendEmailPort.kt │ │ │ │ │ ├── CommandAuthCodePort.kt │ │ │ │ │ ├── CommandAuthCodeLimitPort.kt │ │ │ │ │ ├── QueryRefreshTokenPort.kt │ │ │ │ │ ├── AuthCodeLimitPort.kt │ │ │ │ │ └── SecurityPort.kt │ │ │ │ ├── dto │ │ │ │ │ └── TokenResponse.kt │ │ │ │ └── usecase │ │ │ │ │ ├── ReissueTokenUseCase.kt │ │ │ │ │ └── CheckAuthCodeUseCase.kt │ │ │ ├── file │ │ │ │ ├── exception │ │ │ │ │ └── .gitkeep │ │ │ │ ├── spi │ │ │ │ │ ├── CheckFilePort.kt │ │ │ │ │ ├── UploadFilePort.kt │ │ │ │ │ ├── EmployeeCertificatePort.kt │ │ │ │ │ ├── CommandEmployeeCertificatePort.kt │ │ │ │ │ └── ParseEmployeeCertificateFilePort.kt │ │ │ │ └── usecase │ │ │ │ │ ├── UploadImageUseCase.kt │ │ │ │ │ ├── CheckEmployeeUseCase.kt │ │ │ │ │ └── RegisterEmployeeCertificateUseCase.kt │ │ │ ├── holiday │ │ │ │ ├── exception │ │ │ │ │ └── .gitkeep │ │ │ │ ├── spi │ │ │ │ │ ├── HolidayPort.kt │ │ │ │ │ ├── HolidayPeriodPort.kt │ │ │ │ │ ├── CommandHolidayPeriodPort.kt │ │ │ │ │ └── CommandHolidayPort.kt │ │ │ │ ├── dto │ │ │ │ │ ├── QueryMonthHolidayPeriodResponse.kt │ │ │ │ │ ├── QueryIndividualRequest.kt │ │ │ │ │ ├── AppointHolidayPeriodRequest.kt │ │ │ │ │ ├── QueryIndividualHolidaysResponse.kt │ │ │ │ │ └── QueryEmployeeHolidayResponse.kt │ │ │ │ └── usecase │ │ │ │ │ ├── QueryRemainAnnualUseCase.kt │ │ │ │ │ └── CheckHolidayPeriodUseCase.kt │ │ │ ├── menu │ │ │ │ ├── exception │ │ │ │ │ └── .gitkeep │ │ │ │ ├── spi │ │ │ │ │ ├── MenuPort.kt │ │ │ │ │ ├── CommandMenuPort.kt │ │ │ │ │ └── ParseMenuFilePort.kt │ │ │ │ ├── dto │ │ │ │ │ ├── MenuResponse.kt │ │ │ │ │ └── SaveMenuRequest.kt │ │ │ │ └── usecase │ │ │ │ │ ├── QueryPublicMenuUseCase.kt │ │ │ │ │ ├── SaveMenuUseCase.kt │ │ │ │ │ └── QueryMenuByMonthUseCase.kt │ │ │ ├── spot │ │ │ │ ├── exception │ │ │ │ │ └── .gitkeep │ │ │ │ ├── spi │ │ │ │ │ └── SpotPort.kt │ │ │ │ ├── dto │ │ │ │ │ └── SpotResponse.kt │ │ │ │ └── usecase │ │ │ │ │ └── ShowSpotListUseCase.kt │ │ │ ├── team │ │ │ │ ├── exception │ │ │ │ │ └── .gitkeep │ │ │ │ ├── spi │ │ │ │ │ └── TeamPort.kt │ │ │ │ ├── dto │ │ │ │ │ └── QueryTeamsResponse.kt │ │ │ │ └── usecase │ │ │ │ │ └── QueryTeamsUseCase.kt │ │ │ ├── user │ │ │ │ ├── exception │ │ │ │ │ └── .gitkeep │ │ │ │ ├── dto │ │ │ │ │ ├── ChangeEmailRequest.kt │ │ │ │ │ ├── ChangeNicknameRequest.kt │ │ │ │ │ ├── ChangeProfileImageRequest.kt │ │ │ │ │ ├── AdminSignInRequest.kt │ │ │ │ │ ├── ChangePasswordRequest.kt │ │ │ │ │ ├── CheckMatchedAccountRequest.kt │ │ │ │ │ ├── ResetPasswordRequest.kt │ │ │ │ │ ├── UserSignInRequest.kt │ │ │ │ │ ├── FindEmployeeNumberRequest.kt │ │ │ │ │ ├── QueryUserInfoResponse.kt │ │ │ │ │ ├── SignUpRequest.kt │ │ │ │ │ └── QueryAdminInfoResponse.kt │ │ │ │ ├── spi │ │ │ │ │ ├── CommandUserPort.kt │ │ │ │ │ ├── CommandDeviceTokenPort.kt │ │ │ │ │ ├── UserCommandAuthCodeLimitPort.kt │ │ │ │ │ ├── UserJwtPort.kt │ │ │ │ │ └── UserPort.kt │ │ │ │ └── usecase │ │ │ │ │ ├── CheckEmailDuplicationUseCase.kt │ │ │ │ │ ├── CheckNicknameDuplicationUseCase.kt │ │ │ │ │ ├── CheckMatchedAccountUseCase.kt │ │ │ │ │ ├── FindEmployeeNumberUseCase.kt │ │ │ │ │ ├── ComparePasswordUseCase.kt │ │ │ │ │ ├── ChangeNicknameUseCase.kt │ │ │ │ │ ├── ChangePasswordUseCase.kt │ │ │ │ │ ├── ChangeSpotUseCase.kt │ │ │ │ │ └── QueryUserInfoUseCase.kt │ │ │ ├── notification │ │ │ │ ├── exception │ │ │ │ │ └── .gitkeep │ │ │ │ └── spi │ │ │ │ │ ├── NotificationPort.kt │ │ │ │ │ ├── NotificationQueryUserPort.kt │ │ │ │ │ ├── SendPushMessagePort.kt │ │ │ │ │ └── CommandNotificationPort.kt │ │ │ └── schedule │ │ │ │ ├── exception │ │ │ │ └── .gitkeep │ │ │ │ ├── spi │ │ │ │ ├── SchedulePort.kt │ │ │ │ └── CommandSchedulePort.kt │ │ │ │ ├── dto │ │ │ │ ├── AddSpotScheduleRequest.kt │ │ │ │ ├── ChangeSpotScheduleRequest.kt │ │ │ │ ├── AddIndividualScheduleRequest.kt │ │ │ │ ├── ChangeIndividualScheduleRequest.kt │ │ │ │ ├── QueryIndividualSpotScheduleResponse.kt │ │ │ │ └── QueryEntireSpotScheduleResponse.kt │ │ │ │ └── usecase │ │ │ │ └── QueryEntireSpotScheduleUseCase.kt │ │ │ └── global │ │ │ └── annotation │ │ │ ├── UseCase.kt │ │ │ └── ReadOnlyUseCase.kt │ └── test │ │ └── kotlin │ │ └── team │ │ └── comit │ │ └── simtong │ │ └── global │ │ ├── annotation │ │ └── SimtongTest.kt │ │ └── DomainPropertiesInitialization.kt └── build.gradle.kts ├── simtong-presentation ├── src │ ├── main │ │ └── kotlin │ │ │ └── team │ │ │ └── comit │ │ │ └── simtong │ │ │ ├── domain │ │ │ ├── file │ │ │ │ ├── dto │ │ │ │ │ ├── request │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── response │ │ │ │ │ │ ├── UploadImageWebResponse.kt │ │ │ │ │ │ └── UploadImageListWebResponse.kt │ │ │ │ ├── converter │ │ │ │ │ ├── FileExtensions.kt │ │ │ │ │ ├── ExcelFileConverter.kt │ │ │ │ │ └── ImageFileConverter.kt │ │ │ │ └── exception │ │ │ │ │ └── WebFileExceptions.kt │ │ │ ├── admin │ │ │ │ └── dto │ │ │ │ │ ├── response │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── request │ │ │ │ │ └── SignInWebRequest.kt │ │ │ ├── email │ │ │ │ ├── dto │ │ │ │ │ ├── response │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── request │ │ │ │ │ │ ├── SendAuthCodeWebRequest.kt │ │ │ │ │ │ └── CheckAuthCodeWebRequest.kt │ │ │ │ └── WebEmailAdapter.kt │ │ │ ├── user │ │ │ │ ├── dto │ │ │ │ │ ├── response │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── request │ │ │ │ │ │ ├── ChangeSpotWebRequest.kt │ │ │ │ │ │ ├── ChangeProfileImageWebRequest.kt │ │ │ │ │ │ ├── ChangeEmailWebRequest.kt │ │ │ │ │ │ ├── ChangeNicknameWebRequest.kt │ │ │ │ │ │ ├── SignInWebRequest.kt │ │ │ │ │ │ └── SignUpWebRequest.kt │ │ │ │ └── value │ │ │ │ │ ├── EmployeeNumber.kt │ │ │ │ │ ├── NickName.kt │ │ │ │ │ └── Password.kt │ │ │ ├── HealthCheckWebAdapter.kt │ │ │ ├── holiday │ │ │ │ ├── dto │ │ │ │ │ ├── response │ │ │ │ │ │ └── QueryRemainAnnualWebResponse.kt │ │ │ │ │ └── request │ │ │ │ │ │ ├── AppointAnnualWebRequest.kt │ │ │ │ │ │ ├── AppointHolidayWebRequest.kt │ │ │ │ │ │ ├── CancelHolidayWebRequest.kt │ │ │ │ │ │ ├── ShareHolidayWebRequest.kt │ │ │ │ │ │ ├── ChangeEmployeeHolidayWebRequest.kt │ │ │ │ │ │ └── AppointHolidayPeriodWebRequest.kt │ │ │ │ └── value │ │ │ │ │ ├── WebHolidayStatus.kt │ │ │ │ │ └── WebHolidayQueryType.kt │ │ │ ├── common │ │ │ │ └── dto │ │ │ │ │ ├── response │ │ │ │ │ └── FindEmployeeNumberWebResponse.kt │ │ │ │ │ └── request │ │ │ │ │ ├── ChangePasswordWebRequest.kt │ │ │ │ │ ├── FindEmployeeNumberWebRequest.kt │ │ │ │ │ └── ResetPasswordWebRequest.kt │ │ │ ├── notification │ │ │ │ └── dto │ │ │ │ │ ├── WebNotificationType.kt │ │ │ │ │ └── SendNotificationWebRequest.kt │ │ │ ├── menu │ │ │ │ └── dto │ │ │ │ │ └── request │ │ │ │ │ └── SaveMenuWebRequest.kt │ │ │ └── schedule │ │ │ │ └── dto │ │ │ │ └── request │ │ │ │ ├── ChangeSpotScheduleWebRequest.kt │ │ │ │ ├── AddSpotScheduleWebRequest.kt │ │ │ │ ├── AddIndividualScheduleWebRequest.kt │ │ │ │ └── ChangeIndividualScheduleWebRequest.kt │ │ │ └── global │ │ │ ├── exception │ │ │ └── WebException.kt │ │ │ └── config │ │ │ └── WebMvcConfig.kt │ └── test │ │ └── kotlin │ │ └── team │ │ └── comit │ │ └── simtong │ │ └── SimtongApplicationTests.kt └── build.gradle.kts ├── simtong-infrastructure └── src │ └── main │ ├── kotlin │ └── team │ │ └── comit │ │ └── simtong │ │ ├── persistence │ │ ├── auth │ │ │ ├── repository │ │ │ │ ├── extension │ │ │ │ │ └── .gitkeep │ │ │ │ ├── AuthCodeRepository.kt │ │ │ │ ├── RefreshTokenRepository.kt │ │ │ │ └── AuthCodeLimitRepository.kt │ │ │ ├── mapper │ │ │ │ ├── AuthCodeMapper.kt │ │ │ │ ├── RefreshTokenMapper.kt │ │ │ │ └── AuthCodeLimitMapper.kt │ │ │ ├── entity │ │ │ │ ├── AuthCodeEntity.kt │ │ │ │ ├── AuthCodeLimitEntity.kt │ │ │ │ └── RefreshTokenEntity.kt │ │ │ ├── RefreshTokenPersistenceAdapter.kt │ │ │ └── AuthCodePersistenceAdapter.kt │ │ ├── GenericMapper.kt │ │ ├── schedule │ │ │ ├── ScheduleJpaRepository.kt │ │ │ └── vo │ │ │ │ └── SpotScheduleVo.kt │ │ ├── BaseTimeEntity.kt │ │ ├── spot │ │ │ ├── mapper │ │ │ │ └── SpotMapper.kt │ │ │ ├── SpotJpaRepository.kt │ │ │ ├── entity │ │ │ │ └── SpotJpaEntity.kt │ │ │ └── SpotPersistenceAdapter.kt │ │ ├── menu │ │ │ └── repository │ │ │ │ └── MenuJpaRepository.kt │ │ ├── holiday │ │ │ ├── repository │ │ │ │ ├── HolidayJpaRepository.kt │ │ │ │ └── HolidayPeriodJpaRepository.kt │ │ │ └── vo │ │ │ │ └── EmployeeHolidayVO.kt │ │ ├── notification │ │ │ ├── repository │ │ │ │ ├── NotificationJpaRepository.kt │ │ │ │ └── NotificationReceiverJpaRepository.kt │ │ │ ├── mapper │ │ │ │ └── NotificationMapper.kt │ │ │ └── entity │ │ │ │ └── NotificationJpaEntity.kt │ │ ├── file │ │ │ ├── mapper │ │ │ │ └── EmployeeCertificateMapper.kt │ │ │ ├── EmployeeCertificateJpaRepository.kt │ │ │ └── entity │ │ │ │ └── EmployeeCertificateJpaEntity.kt │ │ ├── BaseUUIDEntity.kt │ │ ├── team │ │ │ ├── TeamJpaRepository.kt │ │ │ ├── TeamPersistenceAdapter.kt │ │ │ ├── entity │ │ │ │ └── TeamJpaEntity.kt │ │ │ └── mapper │ │ │ │ └── TeamMapper.kt │ │ ├── BaseEntity.kt │ │ └── user │ │ │ ├── repository │ │ │ ├── DeviceTokenJpaRepository.kt │ │ │ └── UserJpaRepository.kt │ │ │ ├── entity │ │ │ └── DeviceTokenJpaEntity.kt │ │ │ └── mapper │ │ │ └── DeviceTokenMapper.kt │ │ ├── SimtongApplication.kt │ │ ├── global │ │ ├── config │ │ │ ├── RedisConfig.kt │ │ │ ├── ConfigurationPropertiesConfig.kt │ │ │ ├── QuerydslConfig.kt │ │ │ └── DomainPropertiesConfig.kt │ │ ├── security │ │ │ ├── token │ │ │ │ └── JwtComponent.kt │ │ │ ├── SecurityProperties.kt │ │ │ ├── SecurityAdapter.kt │ │ │ ├── principle │ │ │ │ ├── AuthDetailsService.kt │ │ │ │ └── AuthDetails.kt │ │ │ └── exception │ │ │ │ └── SecurityExceptions.kt │ │ ├── exception │ │ │ └── GlobalExceptions.kt │ │ ├── error │ │ │ ├── GlobalErrorHandler.kt │ │ │ └── dto │ │ │ │ └── CustomFieldError.kt │ │ ├── aop │ │ │ └── GlobalComponentScan.kt │ │ ├── extension │ │ │ └── QuerydslExtensionUtils.kt │ │ └── filter │ │ │ ├── FilterConfig.kt │ │ │ └── JwtFilter.kt │ │ └── thirdparty │ │ ├── email │ │ ├── template │ │ │ └── MailTemplate.kt │ │ └── AwsSESProperties.kt │ │ ├── storage │ │ └── AwsS3Properties.kt │ │ └── notification │ │ └── FcmConfig.kt │ └── resources │ └── application.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle.kts ├── codecov.yml ├── Dockerfile └── .gitignore /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @softpeanut @khcho0125 -------------------------------------------------------------------------------- /simtong-domain/src/test/kotlin/team/comit/simtong/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/file/service/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/menu/service/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/spot/service/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/spot/spi/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/team/service/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/service/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/service/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/schedule/service/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/exception/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/file/exception/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/exception/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/menu/exception/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/spot/exception/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/team/exception/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/exception/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/file/dto/request/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/notification/exception/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/schedule/exception/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/admin/dto/response/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/email/dto/response/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/user/dto/response/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simtong-domain/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | 3 | implementation(Dependencies.BYTEBUDDY) 4 | 5 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/repository/extension/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Team-ComIT/SimTong-Backend/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "simtong" 2 | 3 | include("simtong-application") 4 | include("simtong-domain") 5 | include("simtong-infrastructure") 6 | include("simtong-presentation") -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | comment: 5 | layout: "reach,diff,flags,files,footer" 6 | behavior: default 7 | require_changes: false 8 | branches: 9 | - main 10 | 11 | coverage: 12 | status: 13 | patch: off -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /simtong-presentation/src/test/kotlin/team/comit/simtong/SimtongApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong 2 | 3 | import org.junit.jupiter.api.Test 4 | 5 | class SimtongApplicationTests { 6 | 7 | @Test 8 | fun contextLoads() { 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/todo.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Todo 3 | about: "할 일이 있으신가요? \U0001F64B‍️" 4 | title: '' 5 | labels: feat 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe 11 | - 12 | - 13 | 14 | ## Additional 15 | - 16 | - 17 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM eclipse-temurin:17.0.3_7-jre-focal 2 | 3 | EXPOSE 8080 4 | ENV TZ=Asia/Seoul 5 | 6 | HEALTHCHECK --interval=5s --timeout=3s --retries=4 CMD curl -f http://localhost:8080 || exit 1 7 | 8 | COPY ./simtong-infrastructure/build/libs/*.jar app.jar 9 | ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"] -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/menu/spi/MenuPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.spi 2 | 3 | /** 4 | * 5 | * Menu Domain에 관한 요청을 하는 MenuPort 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/09/21 9 | * @version 1.0.0 10 | **/ 11 | interface MenuPort : QueryMenuPort, CommandMenuPort { 12 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/spi/RefreshTokenPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | /** 4 | * 5 | * RefreshToken Domain에 관한 요청을 하는 RefreshTokenPort 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/09/18 9 | * @version 1.0.0 10 | **/ 11 | interface RefreshTokenPort : QueryRefreshTokenPort -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/spi/HolidayPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.spi 2 | 3 | /** 4 | * 5 | * Holiday Domain에 관한 요청하는 HolidayPort 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/12/03 9 | * @version 1.0.0 10 | **/ 11 | interface HolidayPort : QueryHolidayPort, CommandHolidayPort -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## 작업 내용 설명 2 | - [ ] 3 | - [ ] 4 | 5 | ## 주요 변경 사항 6 | - 7 | - 8 | 9 | ## 체크리스트 10 | - [ ] 어플리케이션 구동(혹은 테스트)시 오류는 없나요? 11 | - [ ] 생성된 코드에 Javadoc 주석을 추가 하였나요? 12 | - [ ] 생성된 코드에 대한 테스트 코드가 작성 되었나요? 13 | 14 | ## 관련 이슈 15 | - resolved # -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/spi/AuthCodePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | /** 4 | * 5 | * AuthCode Domain에 관한 요청을 하는 AuthCodePort 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/09/25 9 | * @version 1.0.0 10 | **/ 11 | interface AuthCodePort : QueryAuthCodePort, CommandAuthCodePort -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/ChangeEmailRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | /** 4 | * 5 | * 이메일 변경 정보를 전달하는 ChangeEmailRequest 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/10/03 9 | * @version 1.0.0 10 | **/ 11 | data class ChangeEmailRequest( 12 | val email: String 13 | ) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 3 | about: "버그가 생겼어요 \U0001F47E" 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe 11 | - 12 | - 13 | 14 | ## How 15 | 1. 16 | 2. 17 | 18 | ## Additional 19 | - 20 | - 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/refactor.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Refactor 3 | about: 개선이 필요해요 ♻️ 4 | title: '' 5 | labels: refactor 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## AS-IS 11 | - 12 | - 13 | 14 | ## TO-BE 15 | - 16 | - 17 | 18 | ## Additional 19 | - 20 | - 21 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/spi/JwtPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | import team.comit.simtong.domain.user.spi.UserJwtPort 4 | 5 | /** 6 | * 7 | * Jwt 토큰을 요청하는 JwtPort 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/09/18 11 | * @version 1.0.0 12 | **/ 13 | interface JwtPort : UserJwtPort -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/schedule/spi/SchedulePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.spi 2 | 3 | /** 4 | * 5 | * Schedule Domain에 관한 요청을 하는 SchedulePort 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/11/21 9 | * @version 1.0.0 10 | **/ 11 | interface SchedulePort : CommandSchedulePort, QuerySchedulePort -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/file/spi/CheckFilePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.spi 2 | 3 | /** 4 | * 5 | * 파일 확인을 요청하는 CheckFilePort 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/10/11 9 | * @version 1.0.0 10 | **/ 11 | interface CheckFilePort { 12 | 13 | fun existsPath(path: String): Boolean 14 | 15 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/ChangeNicknameRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | /** 4 | * 5 | * 닉네임 변경 요청 정보를 전달하는 ChangeNicknameRequest 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/10/03 9 | * @version 1.0.0 10 | **/ 11 | data class ChangeNicknameRequest( 12 | val nickname: String 13 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/spi/HolidayPeriodPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.spi 2 | 3 | /** 4 | * 5 | * 휴무일 작성 기간에 관한 요청하는 HolidayPeriodPort 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/12/21 9 | * @version 1.0.0 10 | **/ 11 | interface HolidayPeriodPort : QueryHolidayPeriodPort, CommandHolidayPeriodPort -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/notification/spi/NotificationPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.notification.spi 2 | 3 | /** 4 | * 5 | * Notification Domain에 관해 요청하는 NotificationPort 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/12/30 9 | * @version 1.1.0 10 | **/ 11 | interface NotificationPort : CommandNotificationPort { 12 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/spi/SendEmailPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | /** 4 | * 5 | * Mail 전송을 요청하는 SendEmailPort 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/09/08 9 | * @version 1.0.0 10 | **/ 11 | interface SendEmailPort { 12 | 13 | fun sendAuthCode(code: String, email: String) 14 | 15 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/HealthCheckWebAdapter.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain 2 | 3 | import org.springframework.web.bind.annotation.GetMapping 4 | import org.springframework.web.bind.annotation.RestController 5 | 6 | @RestController 7 | class HealthCheckWebAdapter { 8 | 9 | @GetMapping 10 | fun healthCheck() = "OK" 11 | 12 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/team/spi/TeamPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.team.spi 2 | 3 | import team.comit.simtong.domain.user.spi.UserQueryTeamPort 4 | 5 | /** 6 | * 7 | * Team에 관한 요청을 하는 TeamPort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/09/18 11 | * @version 1.0.0 12 | **/ 13 | interface TeamPort : QueryTeamPort, UserQueryTeamPort -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/ChangeProfileImageRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | /** 4 | * 5 | * 프로필 사진 변경 요청 정보를 전달하는 ChangeProfileImageRequest 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/10/03 9 | * @version 1.0.0 10 | **/ 11 | data class ChangeProfileImageRequest( 12 | val profileImagePath: String 13 | ) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/SimtongApplication.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.runApplication 5 | 6 | @SpringBootApplication 7 | class SimtongApplication 8 | 9 | fun main(args: Array) { 10 | runApplication(*args) 11 | } 12 | -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/file/dto/response/UploadImageWebResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.dto.response 2 | 3 | /** 4 | * 5 | * 단일 이미지 업로드 요청 결과를 전송하는 UploadImageWebResponse 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/09/21 9 | * @version 1.0.0 10 | **/ 11 | data class UploadImageWebResponse( 12 | val filePath: String 13 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/AdminSignInRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | /** 4 | * 5 | * 관리자의 로그인을 정보를 전달하는 AdminSignInRequest 6 | * 7 | * @author kimbeomjin 8 | * @date 2023/01/01 9 | * @version 1.1.0 10 | **/ 11 | data class AdminSignInRequest( 12 | val employeeNumber: Int, 13 | 14 | val password: String 15 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/ChangePasswordRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | /** 4 | * 5 | * 사용자의 비밀번호 변경 요청을 하는 ChangePasswordRequest 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/10/14 9 | * @version 1.0.0 10 | **/ 11 | data class ChangePasswordRequest( 12 | val password: String, 13 | val newPassword: String 14 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/menu/spi/MenuSecurityPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.spi 2 | 3 | import java.util.* 4 | 5 | /** 6 | * 7 | * Menu Domain에 관한 보안 처리를 요청하는 MenuSecurityPort 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/09/26 11 | * @version 1.0.0 12 | **/ 13 | interface MenuSecurityPort { 14 | 15 | fun getCurrentUserId(): UUID 16 | 17 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/global/annotation/Aggregate.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.annotation 2 | 3 | /** 4 | * 5 | * 모델의 일관성을 관리하는 기준이 되는 Aggregate를 나타내는 어노테이션 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/08/27 9 | * @version 1.0.0 10 | **/ 11 | @Retention(AnnotationRetention.RUNTIME) 12 | @Target(AnnotationTarget.CLASS) 13 | annotation class Aggregate() 14 | -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/holiday/dto/response/QueryRemainAnnualWebResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto.response 2 | 3 | /** 4 | * 5 | * 남은 연차 개수를 전송하는 QueryRemainAnnualWebResponse 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/12/20 9 | * @version 1.0.0 10 | **/ 11 | data class QueryRemainAnnualWebResponse( 12 | val result: Long 13 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/model/DeviceToken.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.model 2 | 3 | import java.util.UUID 4 | 5 | /** 6 | * 7 | * User Aggregate의 디바이스 토큰 관리를 담당하는 DeviceToken 8 | * 9 | * @author kimbeomjin 10 | * @date 2023/01/01 11 | * @version 1.1.0 12 | **/ 13 | data class DeviceToken( 14 | val userId: UUID, 15 | val token: String 16 | ) 17 | -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/config/RedisConfig.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.config 2 | 3 | import org.springframework.data.redis.repository.configuration.EnableRedisRepositories 4 | 5 | /** 6 | * 7 | * Redis를 설정하는 RedisConfig 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/08/22 11 | * @version 1.0.0 12 | **/ 13 | @EnableRedisRepositories 14 | class RedisConfig -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/common/dto/response/FindEmployeeNumberWebResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.common.dto.response 2 | 3 | /** 4 | * 5 | * 사원 번호 찾기 결과를 전송하는 FindEmployeeNumberWebResponse 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/09/11 9 | * @version 1.0.0 10 | **/ 11 | data class FindEmployeeNumberWebResponse( 12 | val employeeNumber: Int 13 | ) -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/file/dto/response/UploadImageListWebResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.dto.response 2 | 3 | /** 4 | * 5 | * 다중 이미지 업로드 요청 결과를 전송하는 UploadImageListWebResponse 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/09/21 9 | * @version 1.0.0 10 | **/ 11 | data class UploadImageListWebResponse( 12 | val filePathList: List 13 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/spot/spi/QuerySpotPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.spot.spi 2 | 3 | import team.comit.simtong.domain.spot.model.Spot 4 | 5 | /** 6 | * 7 | * 지점에 관한 Query를 요청하는 QuerySpotPort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/10/18 11 | * @version 1.0.0 12 | **/ 13 | interface QuerySpotPort { 14 | 15 | fun queryAllSpot(): List 16 | 17 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/global/annotation/DomainService.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.annotation 2 | 3 | /** 4 | * 5 | * 구현 기술에 의존하지 않고 도메인과 밀접한 관련이 있는 DomainService를 나타내는 어노테이션 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/08/27 9 | * @version 1.0.0 10 | **/ 11 | @Retention(AnnotationRetention.RUNTIME) 12 | @Target(AnnotationTarget.CLASS) 13 | annotation class DomainService() 14 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/CheckMatchedAccountRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | /** 4 | * 5 | * 해당 사원번호와 이메일을 가진 계정 여부 확인을 요청하는 CheckMatchedAccountRequest 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/10/15 9 | * @version 1.0.0 10 | **/ 11 | data class CheckMatchedAccountRequest( 12 | val employeeNumber: Int, 13 | val email: String 14 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/spi/HolidaySecurityPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.spi 2 | 3 | import java.util.UUID 4 | 5 | /** 6 | * 7 | * Holiday Domain에서 보안에 관해 요청하는 HolidaySecurityPort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/03 11 | * @version 1.0.0 12 | **/ 13 | interface HolidaySecurityPort { 14 | 15 | fun getCurrentUserId(): UUID 16 | 17 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/schedule/model/Scope.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.model 2 | 3 | /** 4 | * 5 | * 일정의 범위를 구분하는 Scope 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/11/21 9 | * @version 1.0.0 10 | **/ 11 | enum class Scope { 12 | 13 | /** 14 | * 개인 일정 15 | */ 16 | INDIVIDUAL, 17 | 18 | /** 19 | * 전체 일정 20 | */ 21 | ENTIRE 22 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/holiday/dto/request/AppointAnnualWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto.request 2 | 3 | import java.time.LocalDate 4 | 5 | /** 6 | * 7 | * 연차 지정을 요청하는 AppointAnnualWebRequest 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/19 11 | * @version 1.0.0 12 | **/ 13 | data class AppointAnnualWebRequest( 14 | val date: LocalDate 15 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/schedule/spi/ScheduleSecurityPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.spi 2 | 3 | import java.util.UUID 4 | 5 | /** 6 | * 7 | * Schedule Domain에서 보안에 관해 요청하는 ScheduleSecurityPort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/11/21 11 | * @version 1.0.0 12 | **/ 13 | interface ScheduleSecurityPort { 14 | 15 | fun getCurrentUserId(): UUID 16 | 17 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/holiday/dto/request/AppointHolidayWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto.request 2 | 3 | import java.time.LocalDate 4 | 5 | /** 6 | * 7 | * 휴무일 지정을 요청하는 AppointHolidayWebRequest 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/03 11 | * @version 1.0.0 12 | **/ 13 | data class AppointHolidayWebRequest( 14 | val date: LocalDate 15 | ) -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/holiday/dto/request/CancelHolidayWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto.request 2 | 3 | import java.time.LocalDate 4 | 5 | /** 6 | * 7 | * 근무일 지정을 요청하는 CancelHolidayWebRequest 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/20 11 | * @version 1.0.0 12 | **/ 13 | data class CancelHolidayWebRequest( 14 | val date: LocalDate 15 | ) 16 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/menu/spi/CommandMenuPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.spi 2 | 3 | import team.comit.simtong.domain.menu.model.Menu 4 | 5 | /** 6 | * 7 | * Menu Domain에 관한 명령을 요청하는 CommandMenuPort 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/10 11 | * @version 1.2.3 12 | **/ 13 | interface CommandMenuPort { 14 | 15 | fun saveAll(menus: List) 16 | 17 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/ResetPasswordRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | /** 4 | * 5 | * 비밀번호 초기화을 하기 위한 ResetPasswordRequest 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/09/27 9 | * @version 1.0.0 10 | **/ 11 | data class ResetPasswordRequest( 12 | val email: String, 13 | 14 | val employeeNumber: Int, 15 | 16 | val newPassword: String 17 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/UserSignInRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | /** 4 | * 5 | * 일반 사용자의 로그인을 정보를 전달하는 UserSignInRequest 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/09/08 9 | * @version 1.0.0 10 | **/ 11 | data class UserSignInRequest( 12 | val employeeNumber: Int, 13 | 14 | val password: String, 15 | 16 | val deviceToken: String 17 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/model/HolidayType.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.model 2 | 3 | /** 4 | * 5 | * 휴무일 유형를 관리하는 HolidayType 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/12/02 9 | * @version 1.0.0 10 | **/ 11 | enum class HolidayType { 12 | 13 | /** 14 | * 휴무일 15 | */ 16 | HOLIDAY, 17 | 18 | /** 19 | * 연차 20 | */ 21 | ANNUAL 22 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/thirdparty/email/template/MailTemplate.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.thirdparty.email.template 2 | 3 | /** 4 | * 5 | * AWS SES Template를 관리하는 EmailTemplate 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/09/08 9 | * @version 1.2.3 10 | **/ 11 | enum class MailTemplate( 12 | val templateName: String 13 | ) { 14 | 15 | AUTHCODE("SIMTONG_SIGNUP_TEMPLATE") 16 | 17 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/global/exception/BusinessException.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.exception 2 | 3 | /** 4 | * 5 | * 원하는 상황에 예외를 발생시켜 알맞게 처리하기 위해 RuntimeException을 상속받은 BusinessException 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/12/16 9 | * @version 1.0.0 10 | **/ 11 | abstract class BusinessException( 12 | open val status: Int, 13 | override val message: String 14 | ) : RuntimeException() -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/file/spi/UploadFilePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.spi 2 | 3 | import java.io.File 4 | 5 | /** 6 | * 7 | * 파일을 업로드 요청하는 UploadFilePort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/09/07 11 | * @version 1.0.0 12 | **/ 13 | interface UploadFilePort { 14 | 15 | fun upload(file: File): String 16 | 17 | fun upload(files: List): List 18 | 19 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/GenericMapper.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence 2 | 3 | /** 4 | * 5 | * Mapper의 기본 템플릿을 지원하는 GenericMapper 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/09/04 9 | * @version 1.2.3 10 | **/ 11 | interface GenericMapper { 12 | 13 | fun toEntity(model: D): E 14 | 15 | fun toDomain(entity: E?): D? 16 | 17 | fun toDomainNotNull(entity: E): D 18 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/dto/TokenResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.dto 2 | 3 | import java.util.Date 4 | 5 | /** 6 | * 7 | * Jwt 토큰을 전송하는 TokenResponse 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/09/05 11 | * @version 1.0.0 12 | **/ 13 | data class TokenResponse( 14 | val accessToken: String, 15 | 16 | val accessTokenExp: Date, 17 | 18 | val refreshToken: String 19 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/spi/CommandAuthCodePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | import team.comit.simtong.domain.auth.model.AuthCode 4 | 5 | /** 6 | * 7 | * AuthCode에 관한 명령을 하는 CommandAuthCodePort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/09/24 11 | * @version 1.0.0 12 | **/ 13 | interface CommandAuthCodePort { 14 | 15 | fun save(authCode: AuthCode): AuthCode 16 | 17 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/spi/CommandUserPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import team.comit.simtong.domain.user.model.User 4 | 5 | /** 6 | * 7 | * User Domain에 관한 명령을 하는 CommandUserPort 8 | * 9 | * @author Chokyunghyeon 10 | * @author kimbeomjin 11 | * @date 2022/09/04 12 | * @version 1.0.0 13 | **/ 14 | interface CommandUserPort { 15 | 16 | fun save(user: User): User 17 | 18 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/global/annotation/Default.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.annotation 2 | 3 | /** 4 | * 5 | * MapStruct에 사용되는 Default 생성자를 지정하는 어노테이션 6 | * Default 어노테이션이 붙어있으면 MapStruct가 생성자를 가장 우선순위로 사용한다. 7 | * 8 | * @author Chokyunghyeon 9 | * @date 2022/09/30 10 | * @version 1.0.0 11 | **/ 12 | @Retention(AnnotationRetention.RUNTIME) 13 | @Target(AnnotationTarget.CONSTRUCTOR) 14 | annotation class Default() -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/model/HolidayStatus.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.model 2 | 3 | /** 4 | * 5 | * 휴무일 상태를 관리하는 HolidayStatus 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/12/20 9 | * @version 1.0.0 10 | **/ 11 | enum class HolidayStatus { 12 | 13 | /** 14 | * 휴무표 작성 완료 15 | */ 16 | WRITTEN, 17 | 18 | /** 19 | *휴무표 확정 완료 20 | */ 21 | COMPLETED 22 | 23 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/team/spi/QueryTeamPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.team.spi 2 | 3 | import team.comit.simtong.domain.team.model.Team 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * 팀에 관한 Query를 요청하는 QueryTeamPort 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/12/20 12 | * @version 1.0.0 13 | **/ 14 | interface QueryTeamPort { 15 | 16 | fun queryTeamsBySpotId(spotId: UUID): List 17 | 18 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/spi/UserQueryTeamPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import team.comit.simtong.domain.team.model.Team 4 | 5 | /** 6 | * 7 | * User Domain에서 Team Domain에 관한 Query를 요청하는 DomainQueryTeamPort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/09/18 11 | * @version 1.0.0 12 | **/ 13 | interface UserQueryTeamPort { 14 | 15 | fun queryTeamByName(name: String): Team? 16 | 17 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/global/exception/WebException.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.exception 2 | 3 | /** 4 | * 5 | * 표현 계층에서 상황에 따라 예외를 처리하도록 Runtime Exception을 상속받은 WebException 6 | * 7 | * @author Chokyunghyeon 8 | * @author kimbeomjin 9 | * @date 2022/12/09 10 | * @version 1.0.0 11 | **/ 12 | abstract class WebException( 13 | open val status: Int, 14 | override val message: String 15 | ) : RuntimeException() -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/FindEmployeeNumberRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | import java.util.* 4 | 5 | /** 6 | * 7 | * 사원 번호 찾기 정보를 전달하는 FindEmployeeNumberRequest 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/09/11 11 | * @version 1.0.0 12 | **/ 13 | data class FindEmployeeNumberRequest( 14 | val name: String, 15 | 16 | val spotId: UUID, 17 | 18 | val email: String 19 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/auth/spi/QueryAuthCodePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | import team.comit.simtong.domain.auth.model.AuthCode 4 | 5 | /** 6 | * 7 | * AuthCode Domain에 관한 Query를 요청하는 QueryAuthCodePort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/09/25 11 | * @version 1.0.0 12 | **/ 13 | interface QueryAuthCodePort { 14 | 15 | fun queryAuthCodeByEmail(email: String): AuthCode? 16 | 17 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/menu/spi/MenuQueryUserPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.spi 2 | 3 | import team.comit.simtong.domain.user.model.User 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * Menu Domain에서 User Domain에 관한 Query를 요청하는 MenuQueryUserPort 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/09/26 12 | * @version 1.0.0 13 | **/ 14 | interface MenuQueryUserPort { 15 | 16 | fun queryUserById(id: UUID): User? 17 | 18 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/user/dto/request/ChangeSpotWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto.request 2 | 3 | import java.util.UUID 4 | import javax.validation.constraints.NotNull 5 | 6 | /** 7 | * 8 | * 지점 변경을 요청하는 ChangeSpotWebRequest 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/10/15 12 | * @version 1.0.0 13 | **/ 14 | data class ChangeSpotWebRequest( 15 | @field:NotNull 16 | val spotId: UUID 17 | ) 18 | -------------------------------------------------------------------------------- /simtong-application/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("plugin.allopen") version PluginVersions.ALLOPEN_VERSION 3 | } 4 | 5 | dependencies { 6 | // impl project 7 | implementation(project(":simtong-domain")) 8 | 9 | // spring transaction 10 | implementation(Dependencies.SPRING_TRANSACTION) 11 | } 12 | 13 | allOpen { 14 | annotation("team.comit.simtong.global.annotation.UseCase") 15 | annotation("team.comit.simtong.global.annotation.ReadOnlyUseCase") 16 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/dto/QueryMonthHolidayPeriodResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto 2 | 3 | import java.time.LocalDate 4 | 5 | /** 6 | * 7 | * 해당 달의 휴무표 작성 기간을 조회한 결과를 전달하는 QueryMonthHolidayPeriodResponse 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/22 11 | * @version 1.0.0 12 | **/ 13 | data class QueryMonthHolidayPeriodResponse( 14 | val startAt: LocalDate, 15 | val endAt: LocalDate 16 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/spi/CommandDeviceTokenPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import team.comit.simtong.domain.user.model.DeviceToken 4 | 5 | /** 6 | * 7 | * DeviceToken Domain에 관한 명령을 하는 CommandDeviceTokenPort 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/31 11 | * @version 1.1.0 12 | **/ 13 | interface CommandDeviceTokenPort { 14 | 15 | fun save(deviceToken: DeviceToken): DeviceToken 16 | 17 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/dto/QueryIndividualRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto 2 | 3 | import java.time.LocalDate 4 | 5 | /** 6 | * 7 | * 개인 휴무일 조회 요청 정보를 전달하는 QueryIndividualRequest 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/21 11 | * @version 1.0.0 12 | **/ 13 | data class QueryIndividualRequest( 14 | val startAt: LocalDate, 15 | 16 | val endAt: LocalDate, 17 | 18 | val status: String 19 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/menu/dto/MenuResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.dto 2 | 3 | import java.time.LocalDate 4 | 5 | /** 6 | * 7 | * 메뉴를 반환하는 MenuResponse 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/09/26 11 | * @version 1.0.0 12 | **/ 13 | data class MenuResponse( 14 | val menu: List 15 | ) { 16 | data class MenuElement( 17 | val date: LocalDate, 18 | val meal: String 19 | ) 20 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/spot/spi/SpotPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.spot.spi 2 | 3 | import team.comit.simtong.domain.schedule.spi.ScheduleQuerySpotPort 4 | import team.comit.simtong.domain.user.spi.UserQuerySpotPort 5 | 6 | /** 7 | * 8 | * Spot Domain에 관한 요청을 하는 SpotPort 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/09/18 12 | * @version 1.0.0 13 | **/ 14 | interface SpotPort : UserQuerySpotPort, QuerySpotPort, ScheduleQuerySpotPort -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/file/spi/QueryEmployeeCertificatePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.spi 2 | 3 | /** 4 | * 5 | * EmployeeCertificate Domain에 관한 Query를 요청하는 QueryEmployeeCertificatePort 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/12/07 9 | * @version 1.0.0 10 | **/ 11 | interface QueryEmployeeCertificatePort { 12 | 13 | fun existsEmployeeCertificateByNameAndEmployeeNumber(name: String, employeeNumber: Int): Boolean 14 | 15 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/schedule/spi/ScheduleQueryUserPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.spi 2 | 3 | import team.comit.simtong.domain.user.model.User 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * Schedule Domain에서 User에 관한 Query를 요청하는 ScheduleUserPort 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/11/21 12 | * @version 1.0.0 13 | **/ 14 | interface ScheduleQueryUserPort { 15 | 16 | fun queryUserById(id: UUID): User? 17 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/holiday/value/WebHolidayStatus.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.value 2 | 3 | /** 4 | * 5 | * 휴무일 작성 상태에 대한 요청을 의미하는 WebHolidayStatus 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/12/21 9 | * @version 1.2.3 10 | **/ 11 | enum class WebHolidayStatus { 12 | 13 | /** 14 | * 휴무일 작성 완료 15 | */ 16 | WRITTEN, 17 | 18 | /** 19 | * 휴무일 확정 완료 20 | */ 21 | COMPLETED 22 | 23 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/user/dto/request/ChangeProfileImageWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto.request 2 | 3 | import javax.validation.constraints.NotBlank 4 | 5 | /** 6 | * 7 | * 프로필 사진 변경을 요청하는 ChangeProfileImageWebRequest 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/10/03 11 | * @version 1.0.0 12 | **/ 13 | data class ChangeProfileImageWebRequest( 14 | @field:NotBlank 15 | val profileImagePath: String 16 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/menu/dto/SaveMenuRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.dto 2 | 3 | import java.io.File 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * 메뉴 저장 요청 정보를 전달하는 SaveMenuRequest 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/12/17 12 | * @version 1.0.0 13 | **/ 14 | data class SaveMenuRequest( 15 | val file: File, 16 | 17 | val year: Int, 18 | 19 | val month: Int, 20 | 21 | val spotId: UUID 22 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/global/annotation/UseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.annotation 2 | 3 | import org.springframework.transaction.annotation.Transactional 4 | 5 | /** 6 | * 7 | * 추가, 수정, 삭제 기능을 담당하는 사용자 UseCase를 나타내는 어노테이션 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/08/27 11 | * @version 1.0.0 12 | **/ 13 | @Retention(AnnotationRetention.RUNTIME) 14 | @Target(AnnotationTarget.CLASS) 15 | @Transactional 16 | annotation class UseCase() 17 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/spi/HolidayQueryUserPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.spi 2 | 3 | import team.comit.simtong.domain.user.model.User 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * Holiday Domain에서 User에 관한 Query를 요청하는 HolidayQueryUserPort 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/12/03 12 | * @version 1.0.0 13 | **/ 14 | interface HolidayQueryUserPort { 15 | 16 | fun queryUserById(id: UUID): User? 17 | 18 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/spi/CommandAuthCodeLimitPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | import team.comit.simtong.domain.auth.model.AuthCodeLimit 4 | 5 | /** 6 | * 7 | * AuthCodeLimit Domain에 관한 명령을 하는 CommandAuthCodeLimitPort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/09/24 11 | * @version 1.0.0 12 | **/ 13 | interface CommandAuthCodeLimitPort { 14 | 15 | fun save(authCodeLimit: AuthCodeLimit): AuthCodeLimit 16 | 17 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/spi/QueryRefreshTokenPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | import team.comit.simtong.domain.auth.model.RefreshToken 4 | 5 | /** 6 | * 7 | * RefreshToken Domain에 관한 Query를 요청하는 QueryRefreshTokenPort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/09/18 11 | * @version 1.0.0 12 | **/ 13 | interface QueryRefreshTokenPort { 14 | 15 | fun queryRefreshTokenByToken(token: String): RefreshToken? 16 | 17 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/spi/CommandHolidayPeriodPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.spi 2 | 3 | import team.comit.simtong.domain.holiday.model.HolidayPeriod 4 | 5 | /** 6 | * 7 | * 휴무표 작성 기간에 관한 명령을 하는 CommandHolidayPeriodPort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/22 11 | * @version 1.0.0 12 | **/ 13 | interface CommandHolidayPeriodPort { 14 | 15 | fun save(holidayPeriod: HolidayPeriod) : HolidayPeriod 16 | 17 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/team/model/Team.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.team.model 2 | 3 | import team.comit.simtong.global.annotation.Aggregate 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * TeamAggregate Root를 담당하는 Team 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/09/18 12 | * @version 1.0.0 13 | **/ 14 | @Aggregate 15 | data class Team( 16 | val id: UUID = UUID(0, 0), 17 | 18 | val name: String, 19 | 20 | val spotId: UUID 21 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/team/dto/QueryTeamsResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.team.dto 2 | 3 | import java.util.UUID 4 | 5 | /** 6 | * 7 | * 팀 리스트를 전송하는 QueryTeamsResponse 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/20 11 | * @version 1.0.0 12 | **/ 13 | data class QueryTeamsResponse( 14 | val teamList: List 15 | ) { 16 | data class TeamElement( 17 | val id: UUID, 18 | val name: String 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/QueryUserInfoResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | /** 4 | * 5 | * 유저의 정보를 전송하는 QueryUserInfoResponse 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/09/27 9 | * @version 1.0.0 10 | **/ 11 | data class QueryUserInfoResponse ( 12 | val name: String, 13 | 14 | val email: String, 15 | 16 | val nickname: String, 17 | 18 | val spot: String, 19 | 20 | val profileImagePath: String 21 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/auth/spi/QueryAuthCodeLimitPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | import team.comit.simtong.domain.auth.model.AuthCodeLimit 4 | 5 | /** 6 | * 7 | * AuthCodeLimit Domain에 관한 Query를 요청하는 QueryAuthCodeLimitPort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/09/24 11 | * @version 1.0.0 12 | **/ 13 | interface QueryAuthCodeLimitPort { 14 | 15 | fun queryAuthCodeLimitByEmail(email: String): AuthCodeLimit? 16 | 17 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/schedule/spi/ScheduleQuerySpotPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.spi 2 | 3 | import team.comit.simtong.domain.spot.model.Spot 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * Schedule Domain에서 Spot에 관한 Query를 요청하는 ScheduleQuerySpotPort 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/11/26 12 | * @version 1.0.0 13 | **/ 14 | interface ScheduleQuerySpotPort { 15 | 16 | fun querySpotById(id: UUID) : Spot? 17 | 18 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/user/value/EmployeeNumber.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.value 2 | 3 | /** 4 | * 5 | * 사원번호를 의미하는 EmployeeNumber 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2023/01/04 9 | * @version 1.2.3 10 | **/ 11 | @JvmInline 12 | value class EmployeeNumber( 13 | val value: Int 14 | ) { 15 | companion object { 16 | const val MIN_VALUE: Long = 1200000000 17 | const val MAX_VALUE: Long = 1299999999 18 | } 19 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/spi/UserCommandAuthCodeLimitPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import team.comit.simtong.domain.auth.model.AuthCodeLimit 4 | 5 | /** 6 | * 7 | * User Domain에서 AuthCodeLimit Domain에 관한 명령을 하는 UserCommandAuthCodeLimitPort 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/06 11 | * @version 1.0.0 12 | **/ 13 | interface UserCommandAuthCodeLimitPort { 14 | 15 | fun delete(authCodeLimit: AuthCodeLimit) 16 | 17 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/file/spi/EmployeeCertificatePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.spi 2 | 3 | import team.comit.simtong.domain.user.spi.UserQueryEmployeeCertificatePort 4 | 5 | /** 6 | * 7 | * 직원 명부 Domain에 관한 요청을 하는 EmployeeCertificatePort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/06 11 | * @version 1.0.0 12 | **/ 13 | interface EmployeeCertificatePort : CommandEmployeeCertificatePort, QueryEmployeeCertificatePort, UserQueryEmployeeCertificatePort -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/schedule/spi/CommandSchedulePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.spi 2 | 3 | import team.comit.simtong.domain.schedule.model.Schedule 4 | 5 | /** 6 | * 7 | * Schedule Domain에 관한 명령을 하는 CommandSchedulePort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/11/21 11 | * @version 1.0.0 12 | **/ 13 | interface CommandSchedulePort { 14 | 15 | fun save(schedule: Schedule): Schedule 16 | 17 | fun delete(schedule: Schedule) 18 | 19 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/global/annotation/ReadOnlyUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.annotation 2 | 3 | import org.springframework.transaction.annotation.Transactional 4 | 5 | /** 6 | * 7 | * 조회 기능을 담당하는 사용자 UseCase를 나타내는 어노테이션 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/08/27 11 | * @version 1.0.0 12 | **/ 13 | @Retention(AnnotationRetention.RUNTIME) 14 | @Target(AnnotationTarget.CLASS) 15 | @Transactional(readOnly = true) 16 | annotation class ReadOnlyUseCase() 17 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/file/model/EmployeeCertificate.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.model 2 | 3 | /** 4 | * 5 | * EmployeeCertificate Aggregate Root를 담당하는 EmployeeCertificate 6 | * 직원을 증명하기 위해 사용되는 모델 7 | * 8 | * @author Chokyunghyeon 9 | * @date 2022/12/06 10 | * @version 1.0.0 11 | **/ 12 | data class EmployeeCertificate( 13 | val employeeNumber: Int, 14 | 15 | val name: String, 16 | 17 | val spotName: String, 18 | 19 | val teamName: String 20 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/menu/model/Menu.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.model 2 | 3 | import team.comit.simtong.global.annotation.Aggregate 4 | import java.time.LocalDate 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * MenuAggregate Root를 담당하는 Menu 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/09/20 13 | * @version 1.0.0 14 | **/ 15 | @Aggregate 16 | data class Menu( 17 | val date: LocalDate, 18 | 19 | val meal: String, 20 | 21 | val spotId: UUID 22 | ) -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/holiday/dto/request/ShareHolidayWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto.request 2 | 3 | import javax.validation.constraints.NotNull 4 | 5 | /** 6 | * 7 | * 휴무표 공유를 요청하는 ShareHolidayWebRequest 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/21 11 | * @version 1.0.0 12 | **/ 13 | data class ShareHolidayWebRequest( 14 | 15 | @field:NotNull 16 | val year: Int, 17 | 18 | @field:NotNull 19 | val month: Int 20 | ) 21 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/spot/dto/SpotResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.spot.dto 2 | 3 | import java.util.UUID 4 | 5 | /** 6 | * 7 | * 지점 리스트를 전송하는 SpotResponse 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/10/18 11 | * @version 1.0.0 12 | **/ 13 | data class SpotResponse( 14 | val spotList: List 15 | ) { 16 | data class SpotElement ( 17 | val id: UUID, 18 | val name: String, 19 | val location: String 20 | ) 21 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/model/HolidayQueryType.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.model 2 | 3 | /** 4 | * 5 | * 연차 및 휴무일을 필터링하기 위한 HolidayQueryType 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/12/22 9 | * @version 1.0.0 10 | **/ 11 | enum class HolidayQueryType { 12 | 13 | /** 14 | * 휴무일과 연차 15 | */ 16 | ALL, 17 | 18 | /** 19 | * 휴무일 20 | */ 21 | HOLIDAY, 22 | 23 | /** 24 | * 연차 25 | */ 26 | ANNUAL 27 | 28 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/spi/UserQueryAuthCodeLimitPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import team.comit.simtong.domain.auth.model.AuthCodeLimit 4 | 5 | /** 6 | * 7 | * User Domain에서 AuthCodeLimit Domain에 관한 Query를 요청하는 UserQueryAuthCodeLimitPort 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/09/18 11 | * @version 1.0.0 12 | **/ 13 | interface UserQueryAuthCodeLimitPort { 14 | 15 | fun queryAuthCodeLimitByEmail(email: String): AuthCodeLimit? 16 | 17 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/user/dto/request/ChangeEmailWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto.request 2 | 3 | import javax.validation.constraints.Email 4 | import javax.validation.constraints.NotBlank 5 | 6 | /** 7 | * 8 | * 이메일 변경을 요청하는 ChangeEmailWebRequest 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/10/03 12 | * @version 1.0.0 13 | **/ 14 | data class ChangeEmailWebRequest( 15 | @field:NotBlank 16 | @field:Email 17 | val email: String 18 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/menu/spi/ParseMenuFilePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.spi 2 | 3 | import team.comit.simtong.domain.menu.model.Menu 4 | import java.io.File 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * Menu 파일 파싱을 요청하는 ParseMenuFilePort 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/12/10 13 | * @version 1.0.0 14 | **/ 15 | interface ParseMenuFilePort { 16 | 17 | fun importMenu(file: File, year: Int, month: Int, spotId: UUID): List 18 | 19 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/security/token/JwtComponent.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.security.token 2 | 3 | /** 4 | * 5 | * Jwt의 상수를 관리하는 JwtComponent 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/09/01 9 | * @version 1.0.0 10 | **/ 11 | object JwtComponent { 12 | 13 | const val PREFIX = "Bearer " 14 | const val HEADER = "Authorization" 15 | const val AUTHORITY = "authority" 16 | const val ACCESS = "access" 17 | const val REFRESH = "refresh" 18 | 19 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/email/dto/request/SendAuthCodeWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.email.dto.request 2 | 3 | import javax.validation.constraints.Email 4 | import javax.validation.constraints.NotBlank 5 | 6 | /** 7 | * 8 | * 이메일 인증 코드 전송을 요청하는 SendEmailWebRequest 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/09/24 12 | * @version 1.0.0 13 | **/ 14 | data class SendAuthCodeWebRequest( 15 | @field:NotBlank 16 | @field:Email 17 | val email: String 18 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/file/spi/CommandEmployeeCertificatePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.spi 2 | 3 | import team.comit.simtong.domain.file.model.EmployeeCertificate 4 | 5 | /** 6 | * 7 | * Employee Certificate Domain에 관한 명령을 요청하는 CommandEmployeeCertificatePort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/06 11 | * @version 1.2.3 12 | **/ 13 | interface CommandEmployeeCertificatePort { 14 | 15 | fun saveAll(employeeCertificates: List) 16 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/dto/AppointHolidayPeriodRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto 2 | 3 | import java.time.LocalDate 4 | 5 | /** 6 | * 7 | * 휴무표 작성 기간 설정 요청 정보를 전달하는 AppointHolidayPeriodRequest 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/22 11 | * @version 1.0.0 12 | **/ 13 | data class AppointHolidayPeriodRequest( 14 | val year: Int, 15 | 16 | val month: Int, 17 | 18 | val startAt: LocalDate, 19 | 20 | val endAt: LocalDate 21 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/notification/model/NotificationReceiver.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.notification.model 2 | 3 | import java.time.LocalDateTime 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * 알림 Aggregate의 알림을 받은 수신자를 담당하는 NotificationReceiver 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/12/29 12 | * @version 1.1.0 13 | **/ 14 | data class NotificationReceiver( 15 | val userId: UUID, 16 | val notificationId: UUID, 17 | val checkedAt: LocalDateTime? = null 18 | ) 19 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/notification/model/NotificationType.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.notification.model 2 | 3 | /** 4 | * 5 | * 알림의 종류를 구분하는 NotificationType 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/12/29 9 | * @version 1.1.0 10 | **/ 11 | enum class NotificationType { 12 | 13 | /** 14 | * 메뉴 관련 알림 15 | */ 16 | MEAL, 17 | 18 | /** 19 | * 휴무일 관련 알림 20 | */ 21 | HOLIDAY, 22 | 23 | /** 24 | * 일정 관련 알림 25 | */ 26 | SCHEDULE 27 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/holiday/value/WebHolidayQueryType.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.value 2 | 3 | /** 4 | * 5 | * 연차 및 휴무일을 필터링하기 위한 WebHolidayQueryType 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/12/22 9 | * @version 1.2.3 10 | **/ 11 | enum class WebHolidayQueryType { 12 | 13 | /** 14 | * 휴무일과 연차 15 | */ 16 | ALL, 17 | 18 | /** 19 | * 휴무일 20 | */ 21 | HOLIDAY, 22 | 23 | /** 24 | * 연차 25 | */ 26 | ANNUAL 27 | 28 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/user/dto/request/ChangeNicknameWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto.request 2 | 3 | import team.comit.simtong.domain.user.value.NickName 4 | import javax.validation.constraints.Pattern 5 | 6 | /** 7 | * 8 | * 닉네임 변경을 요청하는 ChangeNicknameWebRequest 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/10/03 12 | * @version 1.2.3 13 | **/ 14 | data class ChangeNicknameWebRequest( 15 | @Pattern(regexp = NickName.PATTERN) 16 | val nickname: NickName 17 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/schedule/dto/AddSpotScheduleRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.dto 2 | 3 | import java.time.LocalDate 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * 지점 일정 추가 요청 정보를 전달하는 AddSpotScheduleRequest 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/11/21 12 | * @version 1.0.0 13 | **/ 14 | data class AddSpotScheduleRequest( 15 | val spotId: UUID, 16 | 17 | val title: String, 18 | 19 | val startAt: LocalDate, 20 | 21 | val endAt: LocalDate 22 | ) -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/holiday/dto/request/ChangeEmployeeHolidayWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto.request 2 | 3 | import java.time.LocalDate 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * 직원의 휴무일 변경을 요청하는 ChangeEmployeeHolidayWebRequest 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/12/23 12 | * @version 1.0.0 13 | **/ 14 | data class ChangeEmployeeHolidayWebRequest( 15 | val beforeDate: LocalDate, 16 | val userId: UUID, 17 | val afterDate: LocalDate 18 | ) 19 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/spi/UserJwtPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import team.comit.simtong.domain.auth.dto.TokenResponse 4 | import team.comit.simtong.domain.user.model.Authority 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * User Domain에서 Jwt에 관한 요청하는 UserJwtPort 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/09/18 13 | * @version 1.0.0 14 | **/ 15 | interface UserJwtPort { 16 | 17 | fun receiveToken(userId: UUID, authority: Authority): TokenResponse 18 | 19 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/notification/dto/WebNotificationType.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.notification.dto 2 | 3 | /** 4 | * 5 | * 알림의 종류를 구분하는 WebNotificationType 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/12/30 9 | * @version 1.1.0 10 | **/ 11 | enum class WebNotificationType { 12 | 13 | /** 14 | * 메뉴 관련 알림 15 | */ 16 | MEAL, 17 | 18 | /** 19 | * 휴무일 관련 알림 20 | */ 21 | HOLIDAY, 22 | 23 | /** 24 | * 일정 관련 알림 25 | */ 26 | SCHEDULE 27 | 28 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/model/HolidayPeriod.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.model 2 | 3 | import java.time.LocalDate 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * 휴무일 작성 기간의 Root Aggregate를 담당하는 HolidayPeriod 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/12/20 12 | * @version 1.0.0 13 | **/ 14 | data class HolidayPeriod( 15 | val year: Int, 16 | 17 | val month: Int, 18 | 19 | val spotId: UUID, 20 | 21 | val startAt: LocalDate, 22 | 23 | val endAt: LocalDate 24 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/file/spi/ParseEmployeeCertificateFilePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.spi 2 | 3 | import team.comit.simtong.domain.file.model.EmployeeCertificate 4 | import java.io.File 5 | 6 | /** 7 | * 8 | * EmployeeCertificate 파일 파싱을 요청하는 ParseEmployeeCertificateFilePort 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/12/06 12 | * @version 1.0.0 13 | **/ 14 | interface ParseEmployeeCertificateFilePort { 15 | 16 | fun importEmployeeCertificate(file: File): List 17 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/schedule/dto/ChangeSpotScheduleRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.dto 2 | 3 | import java.time.LocalDate 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * 지점 일정 변경 요청 정보를 전달하는 ChangeSpotScheduleRequest 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/11/22 12 | * @version 1.0.0 13 | **/ 14 | data class ChangeSpotScheduleRequest( 15 | val scheduleId: UUID, 16 | 17 | val title: String, 18 | 19 | val startAt: LocalDate, 20 | 21 | val endAt: LocalDate 22 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/spi/UserSecurityPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import java.util.UUID 4 | 5 | /** 6 | * 7 | * User Domain에 관한 보안 처리를 요청하는 UserSecurityPort 8 | * 9 | * @author Chokyunghyeon 10 | * @author kimbeomjin 11 | * @date 2022/09/04 12 | * @version 1.0.0 13 | **/ 14 | interface UserSecurityPort { 15 | 16 | fun compare(target: String, encryptedPassword: String): Boolean 17 | 18 | fun encode(password: String): String 19 | 20 | fun getCurrentUserId(): UUID 21 | 22 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/schedule/ScheduleJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.schedule 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import team.comit.simtong.persistence.schedule.entity.ScheduleJpaEntity 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * 일정의 Spring Repository를 담당하는 ScheduleJpaRepository 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/11/21 13 | * @version 1.0.0 14 | **/ 15 | interface ScheduleJpaRepository : CrudRepository { 16 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/notification/spi/NotificationQueryUserPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.notification.spi 2 | 3 | import java.util.UUID 4 | 5 | /** 6 | * 7 | * Notification Domain에서 User Domain에 관한 Query를 요청하는 NotificationQueryUserPort 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/30 11 | * @version 1.1.0 12 | **/ 13 | interface NotificationQueryUserPort { 14 | 15 | fun queryDeviceTokenByUserId(userId: UUID): String? 16 | 17 | fun queryDeviceTokensByUserIds(userIds: List): List 18 | 19 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/schedule/dto/AddIndividualScheduleRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.dto 2 | 3 | import java.time.LocalDate 4 | import java.time.LocalTime 5 | 6 | /** 7 | * 8 | * 개인 일정 추가 요청의 정보를 전달하는 AddIndividualScheduleRequest 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/11/26 12 | * @version 1.0.0 13 | **/ 14 | data class AddIndividualScheduleRequest( 15 | val title: String, 16 | 17 | val startAt: LocalDate, 18 | 19 | val endAt: LocalDate, 20 | 21 | val alarm: LocalTime? 22 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/model/Authority.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.model 2 | 3 | /** 4 | * 5 | * 유저(직원, 관리자)의 권한을 관리하는 Authority 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/08/21 9 | * @version 1.0.0 10 | **/ 11 | enum class Authority( 12 | val role: String 13 | ) { 14 | 15 | /** 16 | * 일반 계정 17 | */ 18 | ROLE_COMMON("COMMON"), 19 | 20 | /** 21 | * 관리자 계정 22 | */ 23 | ROLE_ADMIN("ADMIN"), 24 | 25 | /** 26 | * 최고 관리자 계정 27 | */ 28 | ROLE_SUPER("SUPER") 29 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/BaseTimeEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence 2 | 3 | import java.time.LocalDateTime 4 | import javax.persistence.Column 5 | import javax.persistence.MappedSuperclass 6 | 7 | /** 8 | * 9 | * createdAt 컬럼이 필요한 경우 상속받는 BaseTimeEntity 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/08/22 13 | * @version 1.0.0 14 | **/ 15 | @MappedSuperclass 16 | abstract class BaseTimeEntity( 17 | @Column(nullable = false, updatable = false) 18 | val createdAt: LocalDateTime = LocalDateTime.now(), 19 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/spi/CommandHolidayPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.spi 2 | 3 | import team.comit.simtong.domain.holiday.model.Holiday 4 | 5 | /** 6 | * 7 | * Holiday Domain에 관한 명령을 하는 CommandHolidayPort 8 | * 9 | * @author Chokyunghyeon 10 | * @author kimbeomjin 11 | * @date 2022/12/03 12 | * @version 1.2.3 13 | **/ 14 | interface CommandHolidayPort { 15 | 16 | fun save(holiday: Holiday): Holiday 17 | 18 | fun saveAll(holidays: List) 19 | 20 | fun delete(holiday: Holiday) 21 | 22 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/thirdparty/email/AwsSESProperties.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.thirdparty.email 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties 4 | import org.springframework.boot.context.properties.ConstructorBinding 5 | 6 | /** 7 | * 8 | * SES와 관련된 환경변수를 가져오는 AwsSESProperties 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/09/08 12 | * @version 1.0.0 13 | **/ 14 | @ConfigurationProperties("cloud.aws.ses") 15 | @ConstructorBinding 16 | class AwsSESProperties( 17 | val source: String 18 | ) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/thirdparty/storage/AwsS3Properties.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.thirdparty.storage 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties 4 | import org.springframework.boot.context.properties.ConstructorBinding 5 | 6 | /** 7 | * 8 | * S3와 관련한 환경변수를 가져오기 위한 AwsS3Properties 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/09/07 12 | * @version 1.0.0 13 | **/ 14 | @ConfigurationProperties("cloud.aws.s3") 15 | @ConstructorBinding 16 | class AwsS3Properties( 17 | val bucket: String 18 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/spi/AuthCodeLimitPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | import team.comit.simtong.domain.user.spi.UserCommandAuthCodeLimitPort 4 | import team.comit.simtong.domain.user.spi.UserQueryAuthCodeLimitPort 5 | 6 | /** 7 | * 8 | * AuthCodeLimit Domain에 관한 요청을 하는 AuthCodeLimitPort 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/09/18 12 | * @version 1.0.0 13 | **/ 14 | interface AuthCodeLimitPort : UserQueryAuthCodeLimitPort, QueryAuthCodeLimitPort, CommandAuthCodeLimitPort, UserCommandAuthCodeLimitPort -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/schedule/vo/SpotSchedule.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.vo 2 | 3 | import java.time.LocalDate 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * Schedule과 Schedule의 Spot 정보를 가지는 SpotSchedule 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/11/26 12 | * @version 1.0.0 13 | **/ 14 | open class SpotSchedule( 15 | val id: UUID, 16 | 17 | val title: String, 18 | 19 | val startAt: LocalDate, 20 | 21 | val endAt: LocalDate, 22 | 23 | val spotId: UUID, 24 | 25 | val spotName: String 26 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/SignUpRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | /** 4 | * 5 | * 회원 가입 정보를 전달하는 SignUpRequest 6 | * 7 | * @author Chokyunghyeon 8 | * @author kimbeomjin 9 | * @date 2022/09/04 10 | * @version 1.2.1 11 | **/ 12 | data class SignUpRequest( 13 | val name: String, 14 | 15 | val email: String, 16 | 17 | val password: String, 18 | 19 | val nickname: String, 20 | 21 | val employeeNumber: Int, 22 | 23 | val profileImagePath: String?, 24 | 25 | val deviceToken: String 26 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/spi/UserQueryEmployeeCertificatePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import team.comit.simtong.domain.file.model.EmployeeCertificate 4 | 5 | /** 6 | * 7 | * User Domain에서 Employee Certificate에 관한 Query를 요청하는 UserQueryEmployeeCertificatePort 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/07 11 | * @version 1.0.0 12 | **/ 13 | interface UserQueryEmployeeCertificatePort { 14 | 15 | fun queryEmployeeCertificateByNameAndEmployeeNumber(name: String, employeeNumber: Int) : EmployeeCertificate? 16 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/config/ConfigurationPropertiesConfig.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.config 2 | 3 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan 4 | import org.springframework.context.annotation.Configuration 5 | 6 | /** 7 | * 8 | * ConfigurationProperties 어노테이션을 활성화하기 위한 ConfigurationPropertiesConfig 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/08/31 12 | * @version 1.0.0 13 | **/ 14 | @ConfigurationPropertiesScan("team.comit.simtong") 15 | @Configuration 16 | class ConfigurationPropertiesConfig -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/menu/dto/request/SaveMenuWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.dto.request 2 | 3 | import org.springframework.web.multipart.MultipartFile 4 | import javax.validation.constraints.NotNull 5 | 6 | /** 7 | * 8 | * 메뉴를 저장 요청하는 SaveMenuWebRequest 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/12/17 12 | * @version 1.0.0 13 | **/ 14 | data class SaveMenuWebRequest( 15 | val file: MultipartFile, 16 | 17 | @field:NotNull 18 | val year: Int, 19 | 20 | @field:NotNull 21 | val month: Int 22 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/global/DomainProperties.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global 2 | 3 | import team.comit.simtong.global.exception.DomainExceptions 4 | import java.util.Properties 5 | 6 | /** 7 | * 8 | * Domain Aggregate의 설정 값을 받는 DomainProperties 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/10/29 12 | * @version 1.0.0 13 | **/ 14 | object DomainProperties : Properties() { 15 | 16 | override fun getProperty(key: String): String { 17 | return super.getProperty(key) ?: throw DomainExceptions.NotInitializationProperties() 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /simtong-application/src/test/kotlin/team/comit/simtong/global/annotation/SimtongTest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.annotation 2 | 3 | import org.junit.jupiter.api.extension.ExtendWith 4 | import org.springframework.context.annotation.Import 5 | import org.springframework.test.context.junit.jupiter.SpringExtension 6 | import team.comit.simtong.global.DomainPropertiesInitialization 7 | 8 | 9 | @Target(AnnotationTarget.CLASS) 10 | @Retention(AnnotationRetention.RUNTIME) 11 | @ExtendWith(SpringExtension::class) 12 | @Import(DomainPropertiesInitialization::class) 13 | annotation class SimtongTest 14 | -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/spot/mapper/SpotMapper.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.spot.mapper 2 | 3 | import org.mapstruct.Mapper 4 | import team.comit.simtong.domain.spot.model.Spot 5 | import team.comit.simtong.persistence.GenericMapper 6 | import team.comit.simtong.persistence.spot.entity.SpotJpaEntity 7 | 8 | /** 9 | * 10 | * SpotEntity와 DomainSpot을 변환하는 SpotMapper 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/09/18 14 | * @version 1.0.0 15 | **/ 16 | @Mapper 17 | abstract class SpotMapper : GenericMapper { 18 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/global/exception/DomainExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.exception 2 | 3 | /** 4 | * 5 | * Domain에서 발생하는 Exception을 관리하는 DomainExceptions 6 | * 7 | * @author kimbeomjin 8 | * @date 2022/12/16 9 | * @version 1.0.0 10 | **/ 11 | sealed class DomainExceptions { 12 | 13 | // 500 14 | class NotInitializationProperties(message: String = NOT_INITIALIZATION_PROPERTIES) : BusinessException(500, message) 15 | 16 | companion object { 17 | private const val NOT_INITIALIZATION_PROPERTIES = "Domain Properties 초기화 실패" 18 | } 19 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/repository/AuthCodeRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth.repository 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.auth.entity.AuthCodeEntity 6 | 7 | /** 8 | * 9 | * Spring Repository의 기능을 이용하는 AuthCodeRepository 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/08/22 13 | * @version 1.0.0 14 | **/ 15 | @Repository 16 | interface AuthCodeRepository : CrudRepository { 17 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/menu/repository/MenuJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.menu.repository 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.menu.entity.MenuJpaEntity 6 | 7 | /** 8 | * 9 | * Spring Repository의 기능을 이용하는 MenuJpaRepository 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/09/20 13 | * @version 1.0.0 14 | **/ 15 | @Repository 16 | interface MenuJpaRepository : CrudRepository { 17 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/spi/UserQuerySpotPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import team.comit.simtong.domain.spot.model.Spot 4 | import java.util.* 5 | 6 | /** 7 | * 8 | * User Domain에서 Spot Domain에 관한 Query를 요청하는 UserQuerySpotPort 9 | * 10 | * @author kimbeomjin 11 | * @author Chokyunghyeon 12 | * @date 2022/09/18 13 | * @version 1.0.0 14 | **/ 15 | interface UserQuerySpotPort { 16 | 17 | fun querySpotByName(name: String): Spot? 18 | 19 | fun querySpotById(id: UUID): Spot? 20 | 21 | fun existsSpotById(id: UUID): Boolean 22 | 23 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/auth/model/RefreshToken.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.model 2 | 3 | import team.comit.simtong.domain.user.model.Authority 4 | import team.comit.simtong.global.annotation.Aggregate 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * RefreshToken Aggregate Root를 담당하는 RefreshToken 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/09/18 13 | * @version 1.0.0 14 | **/ 15 | @Aggregate 16 | data class RefreshToken( 17 | val token: String, 18 | 19 | val userId: UUID, 20 | 21 | val authority: Authority, 22 | 23 | val expirationTime: Int 24 | ) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/mapper/AuthCodeMapper.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth.mapper 2 | 3 | import org.mapstruct.Mapper 4 | import team.comit.simtong.domain.auth.model.AuthCode 5 | import team.comit.simtong.persistence.GenericMapper 6 | import team.comit.simtong.persistence.auth.entity.AuthCodeEntity 7 | 8 | /** 9 | * 10 | * AuthCodeEntity와 DomainAuthCode를 변환하는 AuthCodeMapper 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/09/25 14 | * @version 1.0.0 15 | **/ 16 | @Mapper 17 | abstract class AuthCodeMapper : GenericMapper { 18 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/repository/RefreshTokenRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth.repository 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.auth.entity.RefreshTokenEntity 6 | 7 | /** 8 | * 9 | * Spring Repository의 기능을 이용하는 RefreshTokenRepository 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/09/01 13 | * @version 1.0.0 14 | **/ 15 | @Repository 16 | interface RefreshTokenRepository : CrudRepository { 17 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | shutdown: graceful 4 | 5 | spring: 6 | lifecycle: 7 | timeout-per-shutdown-phase: "60s" 8 | 9 | jackson: 10 | property-naming-strategy: SNAKE_CASE 11 | 12 | sentry: 13 | dsn: ${SENTRY_DSN} 14 | traces-sample-rate: 1.0 15 | 16 | logging: 17 | level: 18 | com: 19 | amazonaws: 20 | util: 21 | EC2MetadataUtils: error 22 | 23 | --- 24 | 25 | spring: 26 | config: 27 | activate: 28 | on-profile: dev 29 | 30 | --- 31 | 32 | spring: 33 | config: 34 | activate: 35 | on-profile: prod -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/holiday/repository/HolidayJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.holiday.repository 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.holiday.entity.HolidayJpaEntity 6 | 7 | /** 8 | * 9 | * Spring Repository의 기능을 이용하는 HolidayJpaRepository 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/12/02 13 | * @version 1.0.0 14 | **/ 15 | @Repository 16 | interface HolidayJpaRepository : CrudRepository { 17 | } -------------------------------------------------------------------------------- /simtong-presentation/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.springframework.boot") version PluginVersions.SPRING_BOOT_VERSION 3 | id("io.spring.dependency-management") version PluginVersions.DEPENDENCY_MANAGER_VERSION 4 | kotlin("plugin.spring") version PluginVersions.SPRING_PLUGIN_VERSION 5 | } 6 | 7 | dependencies { 8 | // impl project 9 | implementation(project(":simtong-application")) 10 | 11 | // web 12 | implementation(Dependencies.SPRING_WEB) 13 | 14 | // validation 15 | implementation(Dependencies.SPRING_VALIDATION) 16 | } 17 | 18 | tasks.getByName("bootJar") { 19 | enabled = false 20 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | bin/ 16 | !**/src/main/**/bin/ 17 | !**/src/test/**/bin/ 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | out/ 25 | !**/src/main/**/out/ 26 | !**/src/test/**/out/ 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /dist/ 32 | /nbdist/ 33 | /.nb-gradle/ 34 | 35 | ### VS Code ### 36 | .vscode/ 37 | 38 | ### DS_Store ### 39 | .DS_Store -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/common/dto/request/ChangePasswordWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.common.dto.request 2 | 3 | import team.comit.simtong.domain.user.value.Password 4 | import javax.validation.constraints.Pattern 5 | 6 | /** 7 | * 8 | * 비밀번호 변경을 요청하는 ChangePasswordWebRequest 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/10/14 12 | * @version 1.2.3 13 | **/ 14 | data class ChangePasswordWebRequest( 15 | @field:Pattern(regexp = Password.PATTERN) 16 | val password: Password, 17 | 18 | @field:Pattern(regexp = Password.PATTERN) 19 | val newPassword: Password 20 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/spi/SecurityPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.spi 2 | 3 | import team.comit.simtong.domain.holiday.spi.HolidaySecurityPort 4 | import team.comit.simtong.domain.menu.spi.MenuSecurityPort 5 | import team.comit.simtong.domain.schedule.spi.ScheduleSecurityPort 6 | import team.comit.simtong.domain.user.spi.UserSecurityPort 7 | 8 | /** 9 | * 10 | * Security에 관한 요청을 하는 SecurityPort 11 | * 12 | * @author kimbeomjin 13 | * @date 2022/09/18 14 | * @version 1.0.0 15 | **/ 16 | interface SecurityPort : UserSecurityPort, MenuSecurityPort, ScheduleSecurityPort, HolidaySecurityPort -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/schedule/dto/ChangeIndividualScheduleRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.dto 2 | 3 | import java.time.LocalDate 4 | import java.time.LocalTime 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * 개인 일정 변경 요청 정보를 전달하는 ChangeIndividualScheduleRequest 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/11/27 13 | * @version 1.0.0 14 | **/ 15 | data class ChangeIndividualScheduleRequest( 16 | 17 | val scheduleId: UUID, 18 | 19 | val title: String, 20 | 21 | val startAt: LocalDate, 22 | 23 | val endAt: LocalDate, 24 | 25 | val alarm: LocalTime 26 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/notification/model/Notification.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.notification.model 2 | 3 | import java.time.LocalDateTime 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * 알림 Aggregate의 Root를 담당하는 Notification 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/12/29 12 | * @version 1.1.0 13 | **/ 14 | data class Notification( 15 | val id: UUID = UUID(0, 0), 16 | 17 | val title: String, 18 | 19 | val content: String, 20 | 21 | val type: NotificationType, 22 | 23 | val identify: UUID?, 24 | 25 | val createdAt: LocalDateTime = LocalDateTime.now() 26 | ) 27 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/dto/QueryAdminInfoResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto 2 | 3 | import java.util.UUID 4 | 5 | /** 6 | * 7 | * 관리자의 정보를 전송하는 QueryAdminInfoResponse 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/11 11 | * @version 1.0.0 12 | **/ 13 | data class QueryAdminInfoResponse( 14 | val name: String, 15 | 16 | val email: String, 17 | 18 | val nickname: String, 19 | 20 | val spot: SpotResponse, 21 | 22 | val profileImagePath: String 23 | ) { 24 | data class SpotResponse( 25 | val id: UUID, 26 | val name: String 27 | ) 28 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/repository/AuthCodeLimitRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth.repository 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.auth.entity.AuthCodeLimitEntity 6 | 7 | /** 8 | * 9 | * Spring Repository의 기능을 이용하는 AuthCodeLimitRepository 10 | * 11 | * @author Chokyunghyeon 12 | * @author kimbeomjin 13 | * @date 2022/08/29 14 | * @version 1.0.0 15 | **/ 16 | @Repository 17 | interface AuthCodeLimitRepository : CrudRepository { 18 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/spi/vo/EmployeeHoliday.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.spi.vo 2 | 3 | import team.comit.simtong.domain.holiday.model.HolidayType 4 | import java.time.LocalDate 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * Holiday와 User 정보를 가져오는 EmployeeHoliday 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/12/22 13 | * @version 1.0.0 14 | **/ 15 | open class EmployeeHoliday( 16 | val date: LocalDate, 17 | 18 | val type: HolidayType, 19 | 20 | val userId: UUID, 21 | 22 | val userName: String, 23 | 24 | val teamName: String, 25 | 26 | val spotName: String 27 | ) 28 | -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/mapper/RefreshTokenMapper.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth.mapper 2 | 3 | import org.mapstruct.Mapper 4 | import team.comit.simtong.domain.auth.model.RefreshToken 5 | import team.comit.simtong.persistence.GenericMapper 6 | import team.comit.simtong.persistence.auth.entity.RefreshTokenEntity 7 | 8 | /** 9 | * 10 | * RefreshTokenEntity와 DomainRefreshToken을 변환하는 RefreshTokenMapper 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/09/18 14 | * @version 1.0.0 15 | **/ 16 | @Mapper 17 | abstract class RefreshTokenMapper : GenericMapper { 18 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build with Gradle 2 | 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | pull_request: 8 | branches: 9 | - develop 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | 18 | - name: Set up JDK 19 | uses: actions/setup-java@v2 20 | with: 21 | java-version: '17' 22 | distribution: 'zulu' 23 | 24 | - name: Build Gradle 25 | uses: gradle/gradle-build-action@v2 26 | with: 27 | arguments: | 28 | build 29 | --build-cache 30 | --configure-on-demand -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/file/converter/FileExtensions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.converter 2 | 3 | /** 4 | * 5 | * 파일 확장자를 관리하는 FileExtensions 6 | * 7 | * @author Chokyunghyeon 8 | * @author kimbeomjin 9 | * @date 2022/12/06 10 | * @version 1.0.0 11 | **/ 12 | object FileExtensions { 13 | 14 | const val JPG = "JPG" 15 | const val JPEG = "JPEG" 16 | const val PNG = "PNG" 17 | const val HEIC = "HEIC" 18 | 19 | /** 20 | * Excel 2003 이전 형식 확장자 21 | */ 22 | const val XLS = "XLS" 23 | 24 | /** 25 | * Excel 2007 이후 형식 확장자 26 | */ 27 | const val XLSX = "XLSX" 28 | 29 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/notification/spi/SendPushMessagePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.notification.spi 2 | 3 | import team.comit.simtong.domain.notification.model.NotificationType 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * 푸시 알림 전송을 요청하는 SendPushMessagePort 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/12/30 12 | * @version 1.1.0 13 | **/ 14 | interface SendPushMessagePort { 15 | fun sendMessage(token: String, title: String, content: String, type: NotificationType, identify: UUID?) 16 | 17 | fun sendMessage(tokens: List, title: String, content: String, type: NotificationType, identify: UUID?) 18 | 19 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/email/dto/request/CheckAuthCodeWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.email.dto.request 2 | 3 | import org.hibernate.validator.constraints.Length 4 | import javax.validation.constraints.Email 5 | import javax.validation.constraints.NotBlank 6 | 7 | /** 8 | * 9 | * 이메일 인증 코드 확인을 요청하는 CheckAuthCodeWebRequest 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/09/24 13 | * @version 1.0.0 14 | **/ 15 | data class CheckAuthCodeWebRequest( 16 | @field:NotBlank 17 | @field:Email 18 | val email: String, 19 | 20 | @field:NotBlank 21 | @field:Length(min = 6, max = 6) 22 | val code: String 23 | ) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/holiday/repository/HolidayPeriodJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.holiday.repository 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.holiday.entity.HolidayPeriodJpaEntity 6 | 7 | /** 8 | * 9 | * Spring Jpa 기능을 사용해 휴무일 작성 기간의 데이터를 가져오는 HolidayPeriodJpaRepository 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/12/20 13 | * @version 1.0.0 14 | **/ 15 | @Repository 16 | interface HolidayPeriodJpaRepository : CrudRepository { 17 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/notification/repository/NotificationJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.notification.repository 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.notification.entity.NotificationJpaEntity 6 | import java.util.UUID 7 | 8 | /** 9 | * 10 | * 알림의 Spring Repository를 담당하는 NotificationJpaRepository 11 | * 12 | * @author kimbeomjin 13 | * @date 2022/12/29 14 | * @version 1.1.0 15 | **/ 16 | @Repository 17 | interface NotificationJpaRepository : CrudRepository { 18 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/spi/QueryHolidayPeriodPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.spi 2 | 3 | import team.comit.simtong.domain.holiday.model.HolidayPeriod 4 | import java.time.LocalDate 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * 휴무일 작성 기간에 관한 Query를 요청하는 QueryHolidayPeriodPort 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/12/21 13 | * @version 1.0.0 14 | **/ 15 | interface QueryHolidayPeriodPort { 16 | 17 | fun queryHolidayPeriodByYearAndMonthAndSpotId(year: Int, month: Int, spotId: UUID) : HolidayPeriod? 18 | 19 | fun existsHolidayPeriodByWithinPeriodAndSpotId(date: LocalDate, spotId: UUID) : Boolean 20 | 21 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/mapper/AuthCodeLimitMapper.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth.mapper 2 | 3 | import org.mapstruct.Mapper 4 | import team.comit.simtong.domain.auth.model.AuthCodeLimit 5 | import team.comit.simtong.persistence.GenericMapper 6 | import team.comit.simtong.persistence.auth.entity.AuthCodeLimitEntity 7 | 8 | /** 9 | * 10 | * AuthCodeLimitEntity와 DomainAuthCodeLimit을 변환하는 AuthCodeLimitMapper 11 | * 12 | * @author Chokyunghyeon 13 | * @author kimbeomjin 14 | * @date 2022/09/11 15 | * @version 1.0.0 16 | **/ 17 | @Mapper 18 | abstract class AuthCodeLimitMapper : GenericMapper { 19 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/notification/mapper/NotificationMapper.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.notification.mapper 2 | 3 | import org.mapstruct.Mapper 4 | import team.comit.simtong.domain.notification.model.Notification 5 | import team.comit.simtong.persistence.GenericMapper 6 | import team.comit.simtong.persistence.notification.entity.NotificationJpaEntity 7 | 8 | /** 9 | * 10 | * Notification Entity와 Notification Aggregate를 변환하는 NotificationMapper 11 | * 12 | * @author kimbeomjin 13 | * @date 2022/12/29 14 | * @version 1.1.0 15 | **/ 16 | @Mapper 17 | abstract class NotificationMapper : GenericMapper { 18 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/spot/SpotJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.spot 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.spot.entity.SpotJpaEntity 6 | import java.util.UUID 7 | 8 | /** 9 | * 10 | * Spring Repository의 기능을 이용하는 SpotJpaRepository 11 | * 12 | * @author kimbeomjin 13 | * @author Chokyunghyeon 14 | * @date 2022/08/21 15 | * @version 1.0.0 16 | **/ 17 | @Repository 18 | interface SpotJpaRepository : CrudRepository { 19 | 20 | fun querySpotJpaEntityByName(name: String): SpotJpaEntity? 21 | 22 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/user/value/NickName.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.value 2 | 3 | /** 4 | * 5 | * 닉네임을 의미하는 NickName 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2023/01/04 9 | * @version 1.2.3 10 | **/ 11 | @JvmInline 12 | value class NickName( 13 | val value: String 14 | ) { 15 | companion object { 16 | const val MIN_LENGTH = 1 17 | const val MAX_LENGTH = 20 18 | 19 | /** 20 | * first word & Last word - space X 21 | * 22 | * space , _ , . , a ~ z , A ~ Z , 가 ~ 힣 , 0 ~ 9 23 | */ 24 | const val PATTERN = """(?=^\S)(?=^[\w\s가-힣.]{$MIN_LENGTH,$MAX_LENGTH}$).*(?=\S$).""" 25 | } 26 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/spi/UserPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import team.comit.simtong.domain.holiday.spi.HolidayQueryUserPort 4 | import team.comit.simtong.domain.menu.spi.MenuQueryUserPort 5 | import team.comit.simtong.domain.notification.spi.NotificationQueryUserPort 6 | import team.comit.simtong.domain.schedule.spi.ScheduleQueryUserPort 7 | 8 | /** 9 | * 10 | * User Domain에 관한 요청하는 UserPort 11 | * 12 | * @author kimbeomjin 13 | * @date 2022/09/18 14 | * @version 1.0.0 15 | **/ 16 | interface UserPort : QueryUserPort, CommandUserPort, CommandDeviceTokenPort, MenuQueryUserPort, ScheduleQueryUserPort, HolidayQueryUserPort, NotificationQueryUserPort -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/config/QuerydslConfig.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.config 2 | 3 | import com.querydsl.jpa.impl.JPAQueryFactory 4 | import org.springframework.context.annotation.Bean 5 | import org.springframework.context.annotation.Configuration 6 | import javax.persistence.EntityManager 7 | 8 | /** 9 | * 10 | * JPAQueryFactory를 Bean 등록하는 QuerydslConfig 11 | * 12 | * @author kimbeomjin 13 | * @date 2022/09/26 14 | * @version 1.0.0 15 | **/ 16 | @Configuration 17 | class QuerydslConfig( 18 | private val entityManager: EntityManager 19 | ) { 20 | 21 | @Bean 22 | protected fun queryFactory(): JPAQueryFactory = JPAQueryFactory(entityManager) 23 | } 24 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/dto/QueryIndividualHolidaysResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto 2 | 3 | import java.time.LocalDate 4 | 5 | /** 6 | * 7 | * 개인 휴무일 정보를 전송하는 QueryIndividualHolidayResponse 8 | * 9 | * @author Chokyunghyeon 10 | * @date 2022/12/05 11 | * @version 1.0.0 12 | **/ 13 | data class QueryIndividualHolidaysResponse( 14 | val holidays: List 15 | ) 16 | 17 | /** 18 | * 19 | * 개인 휴무일 정보를 전송하는 IndividualHolidayResponse 20 | * 21 | * @author Chokyunghyeon 22 | * @date 2022/12/05 23 | * @version 1.0.0 24 | **/ 25 | data class IndividualHolidayResponse( 26 | val date: LocalDate, 27 | val type: String 28 | ) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/notification/repository/NotificationReceiverJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.notification.repository 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.notification.entity.NotificationReceiverJpaEntity 6 | 7 | /** 8 | * 9 | * 알림 수신자의 Spring Repository를 담당하는 NotificationReceiverRepository 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/12/29 13 | * @version 1.1.0 14 | **/ 15 | @Repository 16 | interface NotificationReceiverJpaRepository : CrudRepository { 17 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/common/dto/request/FindEmployeeNumberWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.common.dto.request 2 | 3 | import java.util.UUID 4 | import javax.validation.constraints.Email 5 | import javax.validation.constraints.NotBlank 6 | import javax.validation.constraints.NotNull 7 | 8 | /** 9 | * 10 | * 사원 번호 찾기를 요청하는 FindEmployeeNumberWebRequest 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/09/11 14 | * @version 1.0.0 15 | **/ 16 | data class FindEmployeeNumberWebRequest( 17 | @field:NotBlank 18 | val name: String, 19 | 20 | @field:NotNull 21 | val spotId: UUID, 22 | 23 | @field:NotBlank 24 | @field:Email 25 | val email: String 26 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/spot/exception/SpotExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.spot.exception 2 | 3 | import team.comit.simtong.global.exception.BusinessException 4 | 5 | /** 6 | * 7 | * Spot Domain에서 발생하는 Exception을 관리하는 SpotExceptions 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/16 11 | * @version 1.0.0 12 | **/ 13 | sealed class SpotExceptions( 14 | override val status: Int, 15 | override val message: String 16 | ) : BusinessException(status, message) { 17 | 18 | // 404 19 | class NotFound(message: String = NOT_FOUND) : SpotExceptions(404, message) 20 | 21 | companion object { 22 | private const val NOT_FOUND = "지점을 찾을 수 없습니다." 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/file/mapper/EmployeeCertificateMapper.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.file.mapper 2 | 3 | import org.mapstruct.Mapper 4 | import team.comit.simtong.domain.file.model.EmployeeCertificate 5 | import team.comit.simtong.persistence.GenericMapper 6 | import team.comit.simtong.persistence.file.entity.EmployeeCertificateJpaEntity 7 | 8 | /** 9 | * 10 | * EmployeeCertificate Entity와 Employee Certificate Aggregate를 변환하는 EmployeeCertificateMapper 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/12/06 14 | * @version 1.0.0 15 | **/ 16 | @Mapper 17 | abstract class EmployeeCertificateMapper : GenericMapper { 18 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/user/value/Password.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.value 2 | 3 | /** 4 | * 5 | * 비밀번호를 의미하는 Password 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2023/01/04 9 | * @version 1.2.3 10 | **/ 11 | @JvmInline 12 | value class Password( 13 | val value: String 14 | ) { 15 | companion object { 16 | const val MIN_LENGTH = 8 17 | const val MAX_LENGTH = 20 18 | 19 | /** 20 | * a ~ z or A ~ Z & 0 ~ 9 - must more than once 21 | * 22 | * non-space, $ , + , - , _ , a ~ z , A ~ Z , 0 ~ 9 23 | */ 24 | const val PATTERN = """(?=.*[a-zA-Z])(?=.*\d)(?=^[\w$+-]{$MIN_LENGTH,$MAX_LENGTH$).*""" 25 | } 26 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/team/exception/TeamExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.team.exception 2 | 3 | import team.comit.simtong.global.exception.BusinessException 4 | 5 | /** 6 | * 7 | * Team Domain에서 발생하는 Exception을 관리하는 TeamExceptions 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/16 11 | * @version 1.0.0 12 | **/ 13 | sealed class TeamExceptions private constructor( 14 | override val status: Int, 15 | override val message: String 16 | ) : BusinessException(status, message) { 17 | 18 | // 404 19 | class NotFound(message: String = NOT_FOUND) : TeamExceptions(404, message) 20 | 21 | companion object { 22 | private const val NOT_FOUND = "팀을 찾을 수 없습니다." 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/menu/spi/QueryMenuPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.spi 2 | 3 | import team.comit.simtong.domain.menu.model.Menu 4 | import java.time.LocalDate 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * Menu Domain에 관한 Query룰 요청하는 QueryMenuPort 10 | * 11 | * @author kimbeomjin 12 | * @author Chokyunghyeon 13 | * @date 2022/09/21 14 | * @version 1.0.0 15 | **/ 16 | interface QueryMenuPort { 17 | 18 | fun existsMenuByMonthAndSpotId(date: LocalDate, spotId: UUID): Boolean 19 | 20 | fun queryMenusByPeriodAndSpotId(startAt: LocalDate, endAt: LocalDate, spotId: UUID): List 21 | 22 | fun queryMenusByPeriodAndSpotName(startAt: LocalDate, endAt: LocalDate, spotName: String): List 23 | 24 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/spot/model/Spot.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.spot.model 2 | 3 | import team.comit.simtong.global.DomainProperties 4 | import team.comit.simtong.global.DomainPropertiesPrefix 5 | import team.comit.simtong.global.annotation.Aggregate 6 | import java.util.UUID 7 | 8 | /** 9 | * 10 | * SpotAggregate Root를 담당하는 Spot 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/09/18 14 | * @version 1.0.0 15 | **/ 16 | @Aggregate 17 | data class Spot( 18 | val id: UUID = UUID(0, 0), 19 | 20 | val name: String, 21 | 22 | val location: String 23 | ) { 24 | companion object { 25 | @JvmField 26 | val HEAD_SHOP = DomainProperties.getProperty(DomainPropertiesPrefix.HEAD_SHOP) 27 | } 28 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/BaseUUIDEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence 2 | 3 | import org.hibernate.annotations.GenericGenerator 4 | import java.util.* 5 | import javax.persistence.Column 6 | import javax.persistence.GeneratedValue 7 | import javax.persistence.Id 8 | import javax.persistence.MappedSuperclass 9 | 10 | /** 11 | * 12 | * pk의 타입이 UUID 일 경우 상속받는 BaseUUIDEntity 13 | * 14 | * @author kimbeomjin 15 | * @date 2022/08/21 16 | * @version 1.0.0 17 | **/ 18 | @MappedSuperclass 19 | abstract class BaseUUIDEntity( 20 | @Id 21 | @GeneratedValue(generator = "uuid2") 22 | @GenericGenerator(name = "uuid2", strategy = "uuid2") 23 | @Column(columnDefinition = "BINARY(16)") 24 | val id: UUID? 25 | ) -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/holiday/dto/request/AppointHolidayPeriodWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto.request 2 | 3 | import org.hibernate.validator.constraints.Range 4 | import java.time.LocalDate 5 | import javax.validation.constraints.NotNull 6 | 7 | /** 8 | * 9 | * 휴무표 작성 기간 설정을 요청하는 AppointHolidayPeriodWebRequest 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/12/22 13 | * @version 1.0.0 14 | **/ 15 | data class AppointHolidayPeriodWebRequest( 16 | @field:NotNull 17 | val year: Int, 18 | 19 | @field:NotNull 20 | @field:Range( 21 | min = 1, 22 | max = 12 23 | ) 24 | val month: Int, 25 | 26 | val startAt: LocalDate, 27 | 28 | val endAt: LocalDate 29 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/file/usecase/UploadImageUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.usecase 2 | 3 | import team.comit.simtong.domain.file.spi.UploadFilePort 4 | import team.comit.simtong.global.annotation.UseCase 5 | import java.io.File 6 | 7 | /** 8 | * 9 | * 이미지 업로드 기능을 담당하는 UploadImageUseCase 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/09/20 13 | * @version 1.0.0 14 | **/ 15 | @UseCase 16 | class UploadImageUseCase( 17 | private val uploadFilePort: UploadFilePort 18 | ) { 19 | 20 | fun execute(file: File): String { 21 | return uploadFilePort.upload(file) 22 | } 23 | 24 | fun execute(files: List): List { 25 | return uploadFilePort.upload(files) 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/menu/exception/MenuExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.exception 2 | 3 | import team.comit.simtong.global.exception.BusinessException 4 | 5 | /** 6 | * 7 | * Menu Domain에서 발생하는 Exception을 관리하는 MenuExceptions 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/17 11 | * @version 1.0.0 12 | **/ 13 | sealed class MenuExceptions( 14 | override val status: Int, 15 | override val message: String 16 | ) : BusinessException(status, message) { 17 | 18 | // 409 19 | class AlreadyExistsSameMonth(message: String = ALREADY_EXISTS_SAME_MONTH) : MenuExceptions(409, message) 20 | 21 | companion object { 22 | private const val ALREADY_EXISTS_SAME_MONTH = "같은 달에 메뉴가 이미 존재합니다." 23 | } 24 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/admin/dto/request/SignInWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.admin.dto.request 2 | 3 | import org.hibernate.validator.constraints.Range 4 | import team.comit.simtong.domain.user.value.EmployeeNumber 5 | import team.comit.simtong.domain.user.value.Password 6 | import javax.validation.constraints.NotBlank 7 | 8 | /** 9 | * 10 | * 관리자가 로그인을 요청하는 SignInWebRequest 11 | * 12 | * @author kimbeomjin 13 | * @author Chokyunghyeon 14 | * @date 2023/01/01 15 | * @version 1.2.3 16 | **/ 17 | data class SignInWebRequest( 18 | 19 | @Range(min = EmployeeNumber.MIN_VALUE, max = EmployeeNumber.MAX_VALUE) 20 | val employeeNumber: EmployeeNumber, 21 | 22 | @field:NotBlank 23 | val password: Password 24 | ) -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/file/exception/WebFileExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.exception 2 | 3 | import team.comit.simtong.global.exception.WebException 4 | 5 | /** 6 | * 7 | * 표현 계층에서 발생하는 예외를 관리하는 WebFileExceptions 8 | * 9 | * @author kimbeomjin 10 | * @author Chokyunghyeon 11 | * @date 2022/12/17 12 | * @version 1.2.3 13 | **/ 14 | sealed class WebFileExceptions( 15 | override val status: Int, 16 | override val message: String 17 | ) : WebException(status, message) { 18 | 19 | // 400 20 | class InvalidExtension(message: String = INVALID_EXTENSION) : WebFileExceptions(400, message) 21 | 22 | companion object { 23 | private const val INVALID_EXTENSION = "확장자가 유효하지 않습니다." 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/user/dto/request/SignInWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto.request 2 | 3 | import team.comit.simtong.domain.user.value.EmployeeNumber 4 | import team.comit.simtong.domain.user.value.Password 5 | import javax.validation.constraints.NotBlank 6 | import javax.validation.constraints.NotNull 7 | 8 | /** 9 | * 10 | * 일반 사용자가 로그인을 요청하는 SignInWebRequest 11 | * 12 | * @author kimbeomjin 13 | * @author Chokyunghyeon 14 | * @date 2022/09/08 15 | * @version 1.2.3 16 | **/ 17 | data class SignInWebRequest( 18 | 19 | @field:NotNull 20 | val employeeNumber: EmployeeNumber, 21 | 22 | @field:NotBlank 23 | val password: Password, 24 | 25 | @field:NotBlank 26 | val deviceToken: String 27 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/notification/exception/NotificationExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.notification.exception 2 | 3 | import team.comit.simtong.global.exception.BusinessException 4 | 5 | /** 6 | * 7 | * Notification Domain에서 발생하는 Exception을 관리하는 NotificationExceptions 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/29 11 | * @version 1.1.0 12 | **/ 13 | sealed class NotificationExceptions( 14 | override val status: Int, 15 | override val message: String, 16 | ) : BusinessException(status, message) { 17 | 18 | // 404 19 | class NotFound(message: String = NOT_FOUND) : NotificationExceptions(404, message) 20 | 21 | companion object { 22 | private const val NOT_FOUND = "알림을 찾을 수 없습니다." 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/team/TeamJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.team 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.team.entity.TeamJpaEntity 6 | import java.util.* 7 | 8 | /** 9 | * 10 | * Spring Repository의 기능을 이용하는 TeamJpaRepository 11 | * 12 | * @author kimbeomjin 13 | * @author Chokyunghyeon 14 | * @date 2022/08/21 15 | * @version 1.0.0 16 | **/ 17 | @Repository 18 | interface TeamJpaRepository : CrudRepository { 19 | 20 | fun queryTeamJpaEntityByName(name: String): TeamJpaEntity? 21 | 22 | fun queryTeamJpaEntitiesBySpotId(spotId: UUID): List 23 | 24 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/BaseEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence 2 | 3 | import org.hibernate.annotations.GenericGenerator 4 | import java.util.* 5 | import javax.persistence.Column 6 | import javax.persistence.GeneratedValue 7 | import javax.persistence.Id 8 | import javax.persistence.MappedSuperclass 9 | 10 | /** 11 | * 12 | * pk의 타입이 UUID 이고 created_at 컬럼이 필요한 경우 상속받는 BaseEntity 13 | * 14 | * @author kimbeomjin 15 | * @date 2022/08/21 16 | * @version 1.0.0 17 | **/ 18 | @MappedSuperclass 19 | abstract class BaseEntity( 20 | @Id 21 | @GeneratedValue(generator = "uuid2") 22 | @GenericGenerator(name = "uuid2", strategy = "uuid2") 23 | @Column(columnDefinition = "BINARY(16)") 24 | val id: UUID? 25 | ) : BaseTimeEntity() -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/file/converter/ExcelFileConverter.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.converter 2 | 3 | import org.springframework.web.multipart.MultipartFile 4 | import team.comit.simtong.domain.file.converter.FileExtensions.XLS 5 | import team.comit.simtong.domain.file.converter.FileExtensions.XLSX 6 | 7 | /** 8 | * 9 | * Excel File의 변환을 담당하는 ExcelFileConverter 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/12/09 13 | * @version 1.0.0 14 | **/ 15 | object ExcelFileConverter : FileConverter { 16 | 17 | override fun isCorrectExtension(multipartFile: MultipartFile): Boolean { 18 | return when (multipartFile.extension) { 19 | XLS, XLSX -> true 20 | else -> false 21 | } 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/config/DomainPropertiesConfig.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.config 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties 4 | import org.springframework.boot.context.properties.ConstructorBinding 5 | import team.comit.simtong.global.DomainProperties 6 | 7 | /** 8 | * 9 | * Domain Aggregate의 설정을 불러오는 DomainConfig 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/11/25 13 | * @version 1.0.0 14 | **/ 15 | @ConstructorBinding 16 | @ConfigurationProperties("domain") 17 | class DomainPropertiesConfig( 18 | model: Map 19 | ) { 20 | 21 | /** 22 | * Domain 설정 변수를 Domain Properties에 매핑 23 | */ 24 | init { 25 | DomainProperties.putAll(model) 26 | } 27 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/notification/spi/CommandNotificationPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.notification.spi 2 | 3 | import team.comit.simtong.domain.notification.model.Notification 4 | import team.comit.simtong.domain.notification.model.NotificationReceiver 5 | 6 | /** 7 | * 8 | * Notification Domain에 관한 명령을 하는 CommandNotificationPort 9 | * 10 | * @author kimbeomjin 11 | * @date 2022/12/30 12 | * @version 1.2.3 13 | **/ 14 | interface CommandNotificationPort { 15 | 16 | fun saveNotification(notification: Notification): Notification 17 | 18 | fun saveNotificationReceiver(notificationReceiver: NotificationReceiver): NotificationReceiver 19 | 20 | fun saveAllNotificationReceiver(notificationReceivers: List) 21 | 22 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/user/repository/DeviceTokenJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.user.repository 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.user.entity.DeviceTokenJpaEntity 6 | import java.util.* 7 | 8 | /** 9 | * 10 | * Spring Repository의 기능을 이용하는 DeviceTokenJpaRepository 11 | * 12 | * @author kimbeomjin 13 | * @date 2022/08/21 14 | * @version 1.0.0 15 | **/ 16 | @Repository 17 | interface DeviceTokenJpaRepository : CrudRepository { 18 | 19 | fun findByUserId(userId: UUID): DeviceTokenJpaEntity? 20 | 21 | fun findAllByUserIdIsIn(userIds: List): List 22 | 23 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/usecase/CheckEmailDuplicationUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.usecase 2 | 3 | import team.comit.simtong.domain.auth.exception.AuthExceptions 4 | import team.comit.simtong.domain.user.spi.QueryUserPort 5 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 6 | 7 | /** 8 | * 9 | * 중복된 이메일인지 검사를 담당하는 CheckEmailDuplicationUseCase 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/10/14 13 | * @version 1.0.0 14 | **/ 15 | @ReadOnlyUseCase 16 | class CheckEmailDuplicationUseCase( 17 | private val queryUserPort: QueryUserPort 18 | ) { 19 | 20 | fun execute(email: String) { 21 | if (queryUserPort.existsUserByEmail(email)) { 22 | throw AuthExceptions.AlreadyUsedEmail() 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/usecase/CheckNicknameDuplicationUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.usecase 2 | 3 | import team.comit.simtong.domain.user.exception.UserExceptions 4 | import team.comit.simtong.domain.user.spi.QueryUserPort 5 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 6 | 7 | /** 8 | * 9 | * 닉네임 중복 여부를 확인하는 CheckNicknameDuplicationUseCase 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/10/15 13 | * @version 1.0.0 14 | **/ 15 | @ReadOnlyUseCase 16 | class CheckNicknameDuplicationUseCase( 17 | private val queryUserPort: QueryUserPort 18 | ) { 19 | 20 | fun execute(nickname: String) { 21 | if (queryUserPort.existsUserByNickname(nickname)) { 22 | throw UserExceptions.AlreadyUsedNickname() 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/spot/entity/SpotJpaEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.spot.entity 2 | 3 | import team.comit.simtong.persistence.BaseUUIDEntity 4 | import java.util.UUID 5 | import javax.persistence.Column 6 | import javax.persistence.Entity 7 | import javax.persistence.Table 8 | 9 | /** 10 | * 11 | * 지점에 대해 관리하는 SpotJpaEntity 12 | * 13 | * @author kimbeomjin 14 | * @date 2022/08/21 15 | * @version 1.2.3 16 | **/ 17 | @Entity 18 | @Table(name = "tbl_spot") 19 | class SpotJpaEntity( 20 | override val id: UUID?, 21 | 22 | @Column(columnDefinition = "VARCHAR(20)", unique = true, nullable = false) 23 | val name: String, 24 | 25 | @Column(columnDefinition = "VARCHAR(40)", unique = true, nullable = false) 26 | val location: String 27 | ) : BaseUUIDEntity(id) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/security/SecurityProperties.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.security 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties 4 | import org.springframework.boot.context.properties.ConstructorBinding 5 | import java.util.* 6 | 7 | /** 8 | * 9 | * security와 관련된 환경변수를 불러오는 SecurityProperties 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/08/31 13 | * @version 1.0.0 14 | **/ 15 | @ConfigurationProperties("spring.security") 16 | @ConstructorBinding 17 | class SecurityProperties( 18 | secretKey: String, 19 | accessExp: Int, 20 | refreshExp: Int 21 | ) { 22 | val accessExpiredTime = accessExp * 1000 23 | val refreshExpiredTime = refreshExp * 1000 24 | val encodingSecretKey = Base64.getEncoder().encodeToString(secretKey.toByteArray())!! 25 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/schedule/dto/request/ChangeSpotScheduleWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.dto.request 2 | 3 | import org.hibernate.validator.constraints.Length 4 | import java.time.LocalDate 5 | import javax.validation.constraints.FutureOrPresent 6 | import javax.validation.constraints.NotBlank 7 | import javax.validation.constraints.NotNull 8 | 9 | /** 10 | * 11 | * 지점 일정을 변경 요청하는 ChangeSpotScheduleWebRequest 12 | * 13 | * @author Chokyunghyeon 14 | * @date 2022/11/22 15 | * @version 1.0.0 16 | **/ 17 | data class ChangeSpotScheduleWebRequest( 18 | @field:NotBlank 19 | @field:Length(max = 20) 20 | val title: String, 21 | 22 | @field:NotNull 23 | val startAt: LocalDate, 24 | 25 | // TODO 시작일, 종료일 검증 Resolver 구현 26 | @field:NotNull 27 | val endAt: LocalDate 28 | ) -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/schedule/dto/request/AddSpotScheduleWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.dto.request 2 | 3 | import org.hibernate.validator.constraints.Length 4 | import java.time.LocalDate 5 | import javax.validation.constraints.FutureOrPresent 6 | import javax.validation.constraints.NotBlank 7 | import javax.validation.constraints.NotNull 8 | 9 | /** 10 | * 11 | * 관리자가 지점 일정을 추가 요청하는 AddSpotScheduleWebRequest 12 | * 13 | * @author Chokyunghyeon 14 | * @date 2022/11/21 15 | * @version 1.0.0 16 | **/ 17 | data class AddSpotScheduleWebRequest( 18 | 19 | @field:NotBlank 20 | @field:Length(max = 20) 21 | val title: String, 22 | 23 | @field:NotNull 24 | val startAt: LocalDate, 25 | 26 | // TODO 시작일, 종료일 검증 Resolver 구현 27 | @field:NotNull 28 | val endAt: LocalDate 29 | ) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/schedule/vo/SpotScheduleVo.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.schedule.vo 2 | 3 | import com.querydsl.core.annotations.QueryProjection 4 | import team.comit.simtong.domain.schedule.vo.SpotSchedule 5 | import java.time.LocalDate 6 | import java.util.UUID 7 | 8 | /** 9 | * 10 | * Schedule과 Schedule의 Spot 정보를 가져오는 SpotScheduleVo 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/11/26 14 | * @version 1.0.0 15 | **/ 16 | class SpotScheduleVo @QueryProjection constructor( 17 | id: UUID, 18 | title: String, 19 | startAt: LocalDate, 20 | endAt: LocalDate, 21 | spotId: UUID, 22 | spotName: String 23 | ) : SpotSchedule( 24 | id = id, 25 | title = title, 26 | startAt = startAt, 27 | endAt = endAt, 28 | spotId = spotId, 29 | spotName = spotName 30 | ) 31 | -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/entity/AuthCodeEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth.entity 2 | 3 | import org.hibernate.validator.constraints.Length 4 | import org.springframework.data.annotation.Id 5 | import org.springframework.data.redis.core.RedisHash 6 | import org.springframework.data.redis.core.TimeToLive 7 | import javax.validation.constraints.NotNull 8 | 9 | /** 10 | * 11 | * 이메일 인증시 사용자의 이메일인지 확인하는 AuthCodeEntity 12 | * 13 | * @author Chokyunghyeon 14 | * @author kimbeomjin 15 | * @date 2022/08/22 16 | * @version 1.0.0 17 | **/ 18 | @RedisHash("tbl_authcode") 19 | data class AuthCodeEntity( 20 | @Id 21 | val key: String, 22 | 23 | @field:NotNull 24 | @field:Length(max = 6) 25 | val code: String, 26 | 27 | @field:NotNull 28 | @TimeToLive 29 | val expirationTime: Int, 30 | ) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/file/EmployeeCertificateJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.file 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.file.entity.EmployeeCertificateJpaEntity 6 | 7 | /** 8 | * 9 | * Spring Repository 기능을 이용하는 EmployeeCertificateJpaRepository 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/12/06 13 | * @version 1.0.0 14 | **/ 15 | @Repository 16 | interface EmployeeCertificateJpaRepository : CrudRepository { 17 | 18 | fun existsByNameAndEmployeeNumber(name: String, employeeNumber: Int): Boolean 19 | 20 | fun queryEmployeeCertificateJpaEntityByNameAndEmployeeNumber(name: String, employeeNumber: Int): EmployeeCertificateJpaEntity? 21 | 22 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/schedule/model/Schedule.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.model 2 | 3 | import java.time.LocalDate 4 | import java.time.LocalTime 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * 일정 Aggregate의 Root를 담당하는 Schedule 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/11/21 13 | * @version 1.0.0 14 | **/ 15 | data class Schedule( 16 | val id: UUID = UUID(0, 0), 17 | 18 | val userId: UUID, 19 | 20 | val spotId: UUID, 21 | 22 | val title: String, 23 | 24 | val scope: Scope, 25 | 26 | val startAt: LocalDate, 27 | 28 | val endAt: LocalDate, 29 | 30 | val alarmTime: LocalTime = DEFAULT_ALARM_TIME 31 | ) { 32 | 33 | companion object { 34 | 35 | /** 36 | * Default AM 8:30 37 | */ 38 | val DEFAULT_ALARM_TIME: LocalTime = LocalTime.of(8,30) 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/schedule/dto/QueryIndividualSpotScheduleResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.dto 2 | 3 | import team.comit.simtong.domain.schedule.model.Scope 4 | import java.time.LocalDate 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * 개인 일정과 소속 지점의 일정 조회 정보를 다루는 QueryIndividualSpotScheduleResponse 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/12/02 13 | * @version 1.0.0 14 | **/ 15 | data class QueryIndividualSpotScheduleResponse( 16 | val schedules: List 17 | ) 18 | 19 | /** 20 | * 21 | * 일정 조회 정보를 다루는 ScheduleResponse 22 | * 23 | * @author kimbeomjin 24 | * @date 2022/12/02 25 | * @version 1.0.0 26 | **/ 27 | data class ScheduleResponse( 28 | val id: UUID, 29 | 30 | val startAt: LocalDate, 31 | 32 | val endAt: LocalDate, 33 | 34 | val title: String, 35 | 36 | val scope: Scope 37 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/spot/usecase/ShowSpotListUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.spot.usecase 2 | 3 | import team.comit.simtong.domain.spot.dto.SpotResponse 4 | import team.comit.simtong.domain.spot.spi.QuerySpotPort 5 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 6 | 7 | /** 8 | * 9 | * 지점 리스트를 담당하는 ShowSpotListUseCase 10 | * 11 | * @author Chokyunghyeon 12 | * @date 2022/10/18 13 | * @version 1.0.0 14 | **/ 15 | @ReadOnlyUseCase 16 | class ShowSpotListUseCase( 17 | private val querySpotPort: QuerySpotPort 18 | ) { 19 | 20 | fun execute(): SpotResponse { 21 | val result = querySpotPort.queryAllSpot().map { 22 | SpotResponse.SpotElement( 23 | id = it.id, 24 | name = it.name, 25 | location = it.location 26 | ) 27 | } 28 | 29 | return SpotResponse(result) 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/file/converter/ImageFileConverter.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.converter 2 | 3 | import org.springframework.web.multipart.MultipartFile 4 | import team.comit.simtong.domain.file.converter.FileExtensions.HEIC 5 | import team.comit.simtong.domain.file.converter.FileExtensions.JPEG 6 | import team.comit.simtong.domain.file.converter.FileExtensions.JPG 7 | import team.comit.simtong.domain.file.converter.FileExtensions.PNG 8 | 9 | /** 10 | * 11 | * Image File의 변환을 담당하는 ImageFileConverter 12 | * 13 | * @author Chokyunghyeon 14 | * @date 2022/12/09 15 | * @version 1.0.0 16 | **/ 17 | object ImageFileConverter : FileConverter { 18 | 19 | override fun isCorrectExtension(multipartFile: MultipartFile): Boolean { 20 | return when (multipartFile.extension) { 21 | JPG, JPEG, PNG, HEIC -> true 22 | else -> false 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/schedule/dto/request/AddIndividualScheduleWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.dto.request 2 | 3 | import org.hibernate.validator.constraints.Length 4 | import java.time.LocalDate 5 | import java.time.LocalTime 6 | import javax.validation.constraints.FutureOrPresent 7 | import javax.validation.constraints.NotBlank 8 | import javax.validation.constraints.NotNull 9 | 10 | /** 11 | * 12 | * 개인 일정 추가를 요청하는 AddIndividualScheduleWebRequest 13 | * 14 | * @author Chokyunghyeon 15 | * @date 2022/11/26 16 | * @version 1.0.0 17 | **/ 18 | data class AddIndividualScheduleWebRequest( 19 | 20 | @field:NotBlank 21 | @field:Length(max = 20) 22 | val title: String, 23 | 24 | @field:NotNull 25 | val startAt: LocalDate, 26 | 27 | // TODO 시작일, 종료일 검증 Resolver 구현 28 | @field:NotNull 29 | val endAt: LocalDate, 30 | 31 | val alarm: LocalTime? 32 | ) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/holiday/vo/EmployeeHolidayVO.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.holiday.vo 2 | 3 | import com.querydsl.core.annotations.QueryProjection 4 | import team.comit.simtong.domain.holiday.model.HolidayType 5 | import team.comit.simtong.domain.holiday.spi.vo.EmployeeHoliday 6 | import java.time.LocalDate 7 | import java.util.UUID 8 | 9 | /** 10 | * 11 | * Holiday와 User 정보를 가져오는 EmployeeHolidayVO 12 | * 13 | * @author kimbeomjin 14 | * @date 2022/12/22 15 | * @version 1.0.0 16 | **/ 17 | class EmployeeHolidayVO @QueryProjection constructor( 18 | date: LocalDate, 19 | type: HolidayType, 20 | userId: UUID, 21 | userName: String, 22 | teamName: String, 23 | spotName: String 24 | ) : EmployeeHoliday( 25 | date = date, 26 | type = type, 27 | userId = userId, 28 | userName = userName, 29 | teamName = teamName, 30 | spotName = spotName 31 | ) 32 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/global/DomainPropertiesPrefix.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global 2 | 3 | /** 4 | * 5 | * Domain Properties의 Key를 관리하는 DomainPropertiesPrefix 6 | * 7 | * @author Chokyunghyeon 8 | * @date 2022/10/29 9 | * @version 1.0.0 10 | **/ 11 | object DomainPropertiesPrefix { 12 | 13 | // User 14 | const val USER_DEFAULT_IMAGE = "user.default-image" 15 | 16 | // AuthCode 17 | const val AUTHCODE_EXP = "authcode.exp" 18 | 19 | // AuthCodeLimit 20 | const val AUTHCODELIMIT_EXP = "authcodelimit.exp" 21 | const val AUTHCODELIMIT_VERIFIED_EXP = "authcodelimit.verified-exp" 22 | const val AUTHCODELIMIT_MAX_ATTEMPT_COUNT = "authcodelimit.max-attempt-count" 23 | 24 | // spot 25 | const val HEAD_SHOP = "spot.head" 26 | 27 | // holiday 28 | const val WEEK_HOLIDAY_LIMIT = "holiday.week-holiday-limit" 29 | const val ANNUAL_LEAVE_LIMIT = "holiday.annual-leave-limit" 30 | 31 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/schedule/dto/QueryEntireSpotScheduleResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.dto 2 | 3 | import java.time.LocalDate 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * 모든 지점 일정 조회 정보를 전송하는 QueryEntireSpotScheduleResponse 9 | * 10 | * @author Chokyunghyeon 11 | * @date 2022/11/26 12 | * @version 1.0.0 13 | **/ 14 | data class QueryEntireSpotScheduleResponse( 15 | val schedules: List 16 | ) 17 | 18 | /** 19 | * 20 | * 지점 일정 정보를 전송하는 SpotScheduleResponse 21 | * 22 | * @author Chokyunghyeon 23 | * @date 2022/11/26 24 | * @version 1.0.0 25 | **/ 26 | data class SpotScheduleResponse( 27 | val id: UUID, 28 | 29 | val startAt: LocalDate, 30 | 31 | val endAt: LocalDate, 32 | 33 | val title: String, 34 | 35 | val spot: SpotElement 36 | ) { 37 | data class SpotElement( 38 | val id: UUID, 39 | val name: String 40 | ) 41 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/schedule/dto/request/ChangeIndividualScheduleWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.dto.request 2 | 3 | import org.hibernate.validator.constraints.Length 4 | import java.time.LocalDate 5 | import java.time.LocalTime 6 | import javax.validation.constraints.FutureOrPresent 7 | import javax.validation.constraints.NotBlank 8 | import javax.validation.constraints.NotNull 9 | 10 | /** 11 | * 12 | * 개인 일정 변경을 요청하는 ChangeIndividualWebRequest 13 | * 14 | * @author Chokyunghyeon 15 | * @date 2022/11/27 16 | * @version 1.0.0 17 | **/ 18 | data class ChangeIndividualScheduleWebRequest( 19 | @field:NotBlank 20 | @field:Length(max = 20) 21 | val title: String, 22 | 23 | @field:NotNull 24 | val startAt: LocalDate, 25 | 26 | // TODO 시작일, 종료일 검증 Resolver 구현 27 | @field:NotNull 28 | val endAt: LocalDate, 29 | 30 | @field:NotNull 31 | val alarm: LocalTime 32 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/usecase/CheckMatchedAccountUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.usecase 2 | 3 | import team.comit.simtong.domain.user.dto.CheckMatchedAccountRequest 4 | import team.comit.simtong.domain.user.exception.UserExceptions 5 | import team.comit.simtong.domain.user.spi.QueryUserPort 6 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 7 | 8 | /** 9 | * 10 | * 해당 사원번호와 이메일을 가진 계정 여부 확인을 담당하는 CheckMatchedAccountUseCase 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/10/15 14 | * @version 1.0.0 15 | **/ 16 | @ReadOnlyUseCase 17 | class CheckMatchedAccountUseCase( 18 | private val queryUserPort: QueryUserPort 19 | ) { 20 | 21 | fun execute(request: CheckMatchedAccountRequest) { 22 | 23 | if (!queryUserPort.existsUserByEmployeeNumberAndEmail(request.employeeNumber, request.email)) { 24 | throw UserExceptions.NotFound() 25 | } 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/usecase/FindEmployeeNumberUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.usecase 2 | 3 | import team.comit.simtong.domain.user.dto.FindEmployeeNumberRequest 4 | import team.comit.simtong.domain.user.exception.UserExceptions 5 | import team.comit.simtong.domain.user.spi.QueryUserPort 6 | import team.comit.simtong.global.annotation.UseCase 7 | 8 | /** 9 | * 10 | * 사원 번호 찾기 기능을 담당하는 FindEmployeeNumberUseCase 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/09/11 14 | * @version 1.0.0 15 | **/ 16 | @UseCase 17 | class FindEmployeeNumberUseCase( 18 | private val queryUserPort: QueryUserPort 19 | ) { 20 | 21 | fun execute(request: FindEmployeeNumberRequest): Int { 22 | val user = queryUserPort.queryUserByNameAndSpotAndEmail(request.name, request.spotId, request.email) 23 | ?: throw UserExceptions.NotFound() 24 | 25 | return user.employeeNumber 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/common/dto/request/ResetPasswordWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.common.dto.request 2 | 3 | import org.hibernate.validator.constraints.Range 4 | import team.comit.simtong.domain.user.value.EmployeeNumber 5 | import team.comit.simtong.domain.user.value.Password 6 | import javax.validation.constraints.Email 7 | import javax.validation.constraints.NotBlank 8 | import javax.validation.constraints.Pattern 9 | 10 | /** 11 | * 12 | * 비밀번호 초기화를 요청하는 ResetPasswordWebRequest 13 | * 14 | * @author Chokyunghyeon 15 | * @date 2022/10/03 16 | * @version 1.2.3 17 | **/ 18 | data class ResetPasswordWebRequest( 19 | @field:NotBlank 20 | @field:Email 21 | val email: String, 22 | 23 | @field:Range(min = EmployeeNumber.MIN_VALUE, max = EmployeeNumber.MAX_VALUE) 24 | val employeeNumber: EmployeeNumber, 25 | 26 | @field:Pattern(regexp = Password.PATTERN) 27 | val newPassword: Password 28 | ) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/entity/AuthCodeLimitEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth.entity 2 | 3 | import org.hibernate.annotations.ColumnDefault 4 | import org.springframework.data.annotation.Id 5 | import org.springframework.data.redis.core.RedisHash 6 | import org.springframework.data.redis.core.TimeToLive 7 | import javax.validation.constraints.NotNull 8 | 9 | /** 10 | * 11 | * 이메일 인증 정책을 관리하는 AuthCodeLimitEntity 12 | * 13 | * @author Chokyunghyeon 14 | * @author kimbeomjin 15 | * @date 2022/08/29 16 | * @version 1.0.0 17 | **/ 18 | @RedisHash("tbl_authcode_limit") 19 | data class AuthCodeLimitEntity( 20 | @Id 21 | val key: String, 22 | 23 | @field:NotNull 24 | @TimeToLive 25 | val expirationTime: Int, 26 | 27 | @field:NotNull 28 | @ColumnDefault("1") 29 | val attemptCount: Short, 30 | 31 | @field:NotNull 32 | @ColumnDefault("false") 33 | val verified: Boolean 34 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/menu/usecase/QueryPublicMenuUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.usecase 2 | 3 | import team.comit.simtong.domain.menu.dto.MenuResponse 4 | import team.comit.simtong.domain.menu.spi.QueryMenuPort 5 | import team.comit.simtong.domain.spot.model.Spot 6 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 7 | import java.time.LocalDate 8 | 9 | /** 10 | * 11 | * 공개된 Menu를 조회하는 QueryPublicMenuUseCase 12 | * 13 | * @author kimbeomjin 14 | * @date 2022/10/11 15 | * @version 1.0.0 16 | **/ 17 | @ReadOnlyUseCase 18 | class QueryPublicMenuUseCase( 19 | private val queryMenuPort: QueryMenuPort, 20 | ) { 21 | 22 | fun execute(startAt: LocalDate, endAt: LocalDate): MenuResponse { 23 | val menu = queryMenuPort.queryMenusByPeriodAndSpotName(startAt, endAt, Spot.HEAD_SHOP) 24 | val result = menu.map { MenuResponse.MenuElement(it.date, it.meal) } 25 | 26 | return MenuResponse(result) 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/file/usecase/CheckEmployeeUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.usecase 2 | 3 | import team.comit.simtong.domain.file.exception.FileExceptions 4 | import team.comit.simtong.domain.file.spi.QueryEmployeeCertificatePort 5 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 6 | 7 | /** 8 | * 9 | * 입력받은 이름과 사원번호를 DB에서 비교해 일치하는지 확인하는 CheckEmployeeUseCase 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/12/07 13 | * @version 1.0.0 14 | **/ 15 | @ReadOnlyUseCase 16 | class CheckEmployeeUseCase( 17 | private val queryEmployeeCertificatePort: QueryEmployeeCertificatePort 18 | ) { 19 | 20 | fun execute(name: String, employeeNumber: Int) { 21 | val isEmployee = queryEmployeeCertificatePort.existsEmployeeCertificateByNameAndEmployeeNumber( 22 | name, employeeNumber 23 | ) 24 | 25 | if (!isEmployee) { 26 | throw FileExceptions.NotExistsEmployee() 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/team/usecase/QueryTeamsUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.team.usecase 2 | 3 | import team.comit.simtong.domain.team.dto.QueryTeamsResponse 4 | import team.comit.simtong.domain.team.spi.QueryTeamPort 5 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 6 | import java.util.UUID 7 | 8 | /** 9 | * 10 | * 팀 리스트를 조회하는 QueryTeamsUseCase 11 | * 12 | * @author kimbeomjin 13 | * @date 2022/12/20 14 | * @version 1.0.0 15 | **/ 16 | @ReadOnlyUseCase 17 | class QueryTeamsUseCase( 18 | private val queryTeamPort: QueryTeamPort 19 | ) { 20 | 21 | fun execute(spotId: UUID): QueryTeamsResponse { 22 | val teams = queryTeamPort.queryTeamsBySpotId(spotId) 23 | 24 | return QueryTeamsResponse( 25 | teams.map { 26 | QueryTeamsResponse.TeamElement( 27 | id = it.id, 28 | name = it.name 29 | ) 30 | } 31 | ) 32 | } 33 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/entity/RefreshTokenEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth.entity 2 | 3 | import org.hibernate.validator.constraints.Length 4 | import org.springframework.data.annotation.Id 5 | import org.springframework.data.redis.core.RedisHash 6 | import org.springframework.data.redis.core.TimeToLive 7 | import team.comit.simtong.domain.user.model.Authority 8 | import java.util.* 9 | import javax.validation.constraints.NotNull 10 | 11 | /** 12 | * 13 | * Access 토큰을 재발급하기 위한 RefreshTokenEntity 14 | * 15 | * @author Chokyunghyeon 16 | * @date 2022/09/01 17 | * @version 1.0.0 18 | **/ 19 | @RedisHash("tbl_refresh_token") 20 | data class RefreshTokenEntity( 21 | @Id 22 | val token: String, 23 | 24 | @field:NotNull 25 | val userId: UUID, 26 | 27 | @field:NotNull 28 | @field:Length(max = 11) 29 | val authority: Authority, 30 | 31 | @field:NotNull 32 | @TimeToLive 33 | val expirationTime: Int 34 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/model/Holiday.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.model 2 | 3 | import team.comit.simtong.global.DomainProperties.getProperty 4 | import team.comit.simtong.global.DomainPropertiesPrefix 5 | import team.comit.simtong.global.annotation.Aggregate 6 | import java.time.LocalDate 7 | import java.util.UUID 8 | 9 | /** 10 | * 11 | * 휴무일의 Root Aggregate를 담당하는 Holiday 12 | * 13 | * @author Chokyunghyeon 14 | * @date 2022/12/02 15 | * @version 1.0.0 16 | **/ 17 | @Aggregate 18 | data class Holiday( 19 | val date: LocalDate, 20 | 21 | val userId: UUID, 22 | 23 | val type: HolidayType, 24 | 25 | val spotId: UUID, 26 | 27 | val status: HolidayStatus 28 | 29 | ) { 30 | companion object { 31 | 32 | val WEEK_HOLIDAY_LIMIT: Long = getProperty(DomainPropertiesPrefix.WEEK_HOLIDAY_LIMIT).toLong() 33 | 34 | val ANNUAL_LEAVE_LIMIT: Long = getProperty(DomainPropertiesPrefix.ANNUAL_LEAVE_LIMIT).toLong() 35 | 36 | } 37 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/user/entity/DeviceTokenJpaEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.user.entity 2 | 3 | import java.util.UUID 4 | import javax.persistence.Entity 5 | import javax.persistence.FetchType 6 | import javax.persistence.Id 7 | import javax.persistence.JoinColumn 8 | import javax.persistence.ManyToOne 9 | import javax.persistence.MapsId 10 | import javax.persistence.Table 11 | import javax.validation.constraints.NotNull 12 | 13 | /** 14 | * 15 | * 유저(직원, 관리자)의 디바이스 토큰을 관리하는 DeviceTokenJpaEntity 16 | * 17 | * @author kimbeomjin 18 | * @date 2022/08/21 19 | * @version 1.0.0 20 | **/ 21 | @Entity 22 | @Table(name = "tbl_device") 23 | class DeviceTokenJpaEntity( 24 | @Id 25 | val userId: UUID, 26 | 27 | @MapsId 28 | @field:NotNull 29 | @ManyToOne(fetch = FetchType.LAZY) 30 | @JoinColumn(name = "user_id", columnDefinition = "BINARY(16)") 31 | val user: UserJpaEntity, 32 | 33 | @field:NotNull 34 | val token: String 35 | ) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/file/usecase/RegisterEmployeeCertificateUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.usecase 2 | 3 | import team.comit.simtong.domain.file.spi.CommandEmployeeCertificatePort 4 | import team.comit.simtong.domain.file.spi.ParseEmployeeCertificateFilePort 5 | import team.comit.simtong.global.annotation.UseCase 6 | import java.io.File 7 | 8 | /** 9 | * 10 | * 직원 명부 등록 요청을 담당하는 RegisterEmployeeCertificateUseCase 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/12/06 14 | * @version 1.0.0 15 | **/ 16 | @UseCase 17 | class RegisterEmployeeCertificateUseCase( 18 | private val commandEmployeeCertificatePort: CommandEmployeeCertificatePort, 19 | private val parseEmployeeCertificateFilePort: ParseEmployeeCertificateFilePort 20 | ) { 21 | 22 | fun execute(file: File) { 23 | val employeeList = parseEmployeeCertificateFilePort.importEmployeeCertificate(file) 24 | 25 | commandEmployeeCertificatePort.saveAll(employeeList) 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/schedule/spi/QuerySchedulePort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.spi 2 | 3 | import team.comit.simtong.domain.schedule.model.Schedule 4 | import team.comit.simtong.domain.schedule.model.Scope 5 | import team.comit.simtong.domain.schedule.vo.SpotSchedule 6 | import java.time.LocalDate 7 | import java.util.UUID 8 | 9 | /** 10 | * 11 | * Schedule에 관한 Query를 요청하는 QuerySchedulePort 12 | * 13 | * @author Chokyunghyeon 14 | * @date 2022/11/22 15 | * @version 1.0.0 16 | **/ 17 | interface QuerySchedulePort { 18 | 19 | fun queryScheduleById(id: UUID): Schedule? 20 | 21 | fun querySpotSchedulesByPeriodAndScope(startAt: LocalDate, endAt: LocalDate, scope: Scope): List 22 | 23 | fun querySchedulesByPeriodAndSpotIdAndScope(startAt: LocalDate, endAt: LocalDate, spotId: UUID, scope: Scope): List 24 | 25 | fun querySchedulesByPeriodAndUserIdAndScope(startAt: LocalDate, endAt: LocalDate, userId: UUID, scope: Scope): List 26 | 27 | } -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | pull_request: 8 | branches: 9 | - develop 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: codecov/codecov-action@v1 18 | with: 19 | token: ${{ secrets.CODECOV_TOKEN }} 20 | 21 | - name: Set up JDK 22 | uses: actions/setup-java@v2 23 | with: 24 | java-version: '17' 25 | distribution: 'zulu' 26 | 27 | - name: Setup Gradle 28 | uses: gradle/gradle-build-action@v2 29 | 30 | - name: Grant execute permission for gradlew 31 | run: chmod +x gradlew 32 | 33 | - name: codecov gradle 34 | run: ./gradlew test jacocoRootReport 35 | 36 | - name: Upload coverage to Codecov 37 | uses: codecov/codecov-action@v2 38 | with: 39 | token: ${{ secrets.CODECOV_TOKEN }} 40 | files: ./build/reports/jacoco/test/jacocoTestReport.xml -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/auth/model/AuthCode.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.model 2 | 3 | import net.bytebuddy.utility.RandomString 4 | import team.comit.simtong.global.DomainProperties.getProperty 5 | import team.comit.simtong.global.DomainPropertiesPrefix 6 | import team.comit.simtong.global.annotation.Aggregate 7 | import team.comit.simtong.global.annotation.Default 8 | 9 | /** 10 | * 11 | * AuthCodeAggregate Root를 담당하는 AuthCode 12 | * 13 | * @author Chokyunghyeon 14 | * @date 2022/09/24 15 | * @version 1.0.0 16 | **/ 17 | @Aggregate 18 | data class AuthCode @Default constructor( 19 | val key: String, 20 | 21 | val code: String, 22 | 23 | val expirationTime: Int 24 | ) { 25 | 26 | constructor(email: String) : this( 27 | key = email, 28 | code = RandomString(6).nextString(), 29 | expirationTime = EXPIRED 30 | ) 31 | 32 | companion object { 33 | @JvmField 34 | val EXPIRED = getProperty(DomainPropertiesPrefix.AUTHCODE_EXP).toInt() 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/usecase/ComparePasswordUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.usecase 2 | 3 | import team.comit.simtong.domain.user.exception.UserExceptions 4 | import team.comit.simtong.domain.user.spi.QueryUserPort 5 | import team.comit.simtong.domain.user.spi.UserSecurityPort 6 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 7 | 8 | /** 9 | * 10 | * 비밀번호 일치 여부 요청을 담당하는 MatchPasswordUseCase 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/12/05 14 | * @version 1.0.0 15 | **/ 16 | @ReadOnlyUseCase 17 | class ComparePasswordUseCase( 18 | private val queryUserPort: QueryUserPort, 19 | private val securityPort: UserSecurityPort 20 | ) { 21 | 22 | fun execute(password: String) { 23 | val user = queryUserPort.queryUserById(securityPort.getCurrentUserId()) 24 | ?: throw UserExceptions.NotFound() 25 | 26 | if (!securityPort.compare(password, user.password)) { 27 | throw UserExceptions.DifferentPassword() 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/file/entity/EmployeeCertificateJpaEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.file.entity 2 | 3 | import javax.persistence.Column 4 | import javax.persistence.Entity 5 | import javax.persistence.Id 6 | import javax.persistence.Index 7 | import javax.persistence.Table 8 | 9 | /** 10 | * 11 | * 사원 명부를 관리하는 EmployeeCertificateJpaEntity 12 | * 13 | * @author Chokyunghyeon 14 | * @date 2022/12/08 15 | * @version 1.0.0 16 | **/ 17 | @Entity 18 | @Table( 19 | name = "tbl_employee_certificate", 20 | indexes = [ 21 | Index(name = "idx_name", columnList = "name ASC") 22 | ] 23 | ) 24 | data class EmployeeCertificateJpaEntity( 25 | @Id 26 | val employeeNumber: Int, 27 | 28 | @Column(columnDefinition = "VARCHAR(10)", nullable = false) 29 | val name: String, 30 | 31 | @Column(columnDefinition = "VARCHAR(20)", nullable = false) 32 | val spotName: String, 33 | 34 | @Column(columnDefinition = "VARCHAR(8)", nullable = false) 35 | val teamName: String 36 | ) -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/model/User.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.model 2 | 3 | import team.comit.simtong.global.DomainProperties.getProperty 4 | import team.comit.simtong.global.DomainPropertiesPrefix 5 | import team.comit.simtong.global.annotation.Aggregate 6 | import java.util.UUID 7 | 8 | /** 9 | * 10 | * UserAggregate Root를 담당하는 User 11 | * 12 | * @author Chokyunghyeon 13 | * @author kimbeomjin 14 | * @date 2022/09/04 15 | * @version 1.0.0 16 | **/ 17 | @Aggregate 18 | data class User( 19 | val id: UUID = UUID(0, 0), 20 | 21 | val nickname: String, 22 | 23 | val name: String, 24 | 25 | val email: String, 26 | 27 | val password: String, 28 | 29 | val employeeNumber: Int, 30 | 31 | val authority: Authority, 32 | 33 | val spotId: UUID, 34 | 35 | val teamId: UUID, 36 | 37 | val profileImagePath: String 38 | ) { 39 | 40 | companion object { 41 | @JvmField 42 | val DEFAULT_IMAGE = getProperty(DomainPropertiesPrefix.USER_DEFAULT_IMAGE) 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/exception/GlobalExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.exception 2 | 3 | /** 4 | * 5 | * 애플리케이션에서 발생하는 예외를 관리하는 GlobalExceptions 6 | * 7 | * @author kimbeomjin 8 | * @author Chokyunghyeon 9 | * @date 2022/12/17 10 | * @version 1.2.3 11 | **/ 12 | sealed class GlobalExceptions( 13 | override val status: Int, 14 | override val message: String 15 | ) : BusinessException(status, message) { 16 | 17 | // 400 18 | class BadRequest(message: String = BAD_REQUEST) : GlobalExceptions(400, message) 19 | 20 | // 405 21 | class MethodNotAllowed(message: String = METHOD_NOT_ALLOWED) : GlobalExceptions(405, message) 22 | 23 | // 500 24 | class InternalServerError(message: String = INTERNAL_SERVER_ERROR) : GlobalExceptions(500, message) 25 | 26 | companion object { 27 | private const val BAD_REQUEST = "잘못된 입력값입니다." 28 | private const val METHOD_NOT_ALLOWED = "허용되지 않은 HTTP 메소드입니다." 29 | private const val INTERNAL_SERVER_ERROR = "서버 에러가 발생하였습니다." 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/user/mapper/DeviceTokenMapper.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.user.mapper 2 | 3 | import org.mapstruct.Mapper 4 | import org.mapstruct.Mapping 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import team.comit.simtong.domain.user.model.DeviceToken 7 | import team.comit.simtong.persistence.GenericMapper 8 | import team.comit.simtong.persistence.user.entity.DeviceTokenJpaEntity 9 | import team.comit.simtong.persistence.user.repository.UserJpaRepository 10 | 11 | /** 12 | * 13 | * DeviceToken Entity와 DeviceToken Aggregate를 변환하는 DeviceTokenMapper 14 | * 15 | * @author kimbeomjin 16 | * @date 2023/01/01 17 | * @version 1.1.0 18 | **/ 19 | @Mapper 20 | abstract class DeviceTokenMapper : GenericMapper { 21 | 22 | @Autowired 23 | protected lateinit var userRepository: UserJpaRepository 24 | 25 | @Mapping(target = "user", expression = "java(userRepository.findById(model.getUserId()).orElse(null))") 26 | abstract override fun toEntity(model: DeviceToken): DeviceTokenJpaEntity 27 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/team/TeamPersistenceAdapter.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.team 2 | 3 | import org.springframework.stereotype.Component 4 | import team.comit.simtong.domain.team.model.Team 5 | import team.comit.simtong.domain.team.spi.TeamPort 6 | import team.comit.simtong.persistence.team.mapper.TeamMapper 7 | import java.util.UUID 8 | 9 | /** 10 | * 11 | * Team의 영속성을 관리하는 TeamPersistenceAdapter 12 | * 13 | * @author Chokyunghyeon 14 | * @date 2022/09/18 15 | * @version 1.2.3 16 | **/ 17 | @Component 18 | class TeamPersistenceAdapter( 19 | private val teamJpaRepository: TeamJpaRepository, 20 | private val teamMapper: TeamMapper 21 | ) : TeamPort { 22 | 23 | override fun queryTeamsBySpotId(spotId: UUID): List { 24 | return teamJpaRepository.queryTeamJpaEntitiesBySpotId(spotId) 25 | .map(teamMapper::toDomainNotNull) 26 | } 27 | 28 | override fun queryTeamByName(name: String): Team? { 29 | return teamJpaRepository.queryTeamJpaEntityByName(name) 30 | .let(teamMapper::toDomain) 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/team/entity/TeamJpaEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.team.entity 2 | 3 | import org.hibernate.validator.constraints.Length 4 | import team.comit.simtong.persistence.BaseUUIDEntity 5 | import team.comit.simtong.persistence.spot.entity.SpotJpaEntity 6 | import java.util.UUID 7 | import javax.persistence.Entity 8 | import javax.persistence.FetchType 9 | import javax.persistence.JoinColumn 10 | import javax.persistence.ManyToOne 11 | import javax.persistence.Table 12 | import javax.validation.constraints.NotNull 13 | 14 | /** 15 | * 16 | * 지점에 속한 팀을 관리하는 TeamJpaEntity 17 | * 18 | * @author kimbeomjin 19 | * @date 2022/08/21 20 | * @version 1.2.3 21 | **/ 22 | @Entity 23 | @Table(name = "tbl_team") 24 | class TeamJpaEntity( 25 | override val id: UUID?, 26 | 27 | @field:NotNull 28 | @field:Length(max = 8) 29 | val name: String, 30 | 31 | @field:NotNull 32 | @ManyToOne(fetch = FetchType.LAZY) 33 | @JoinColumn(name = "spot_id", columnDefinition = "BINARY(16)") 34 | val spot: SpotJpaEntity, 35 | ) : BaseUUIDEntity(id) -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/error/GlobalErrorHandler.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.error 2 | 3 | import org.springframework.dao.DataIntegrityViolationException 4 | import org.springframework.http.HttpStatus 5 | import org.springframework.web.bind.annotation.ExceptionHandler 6 | import org.springframework.web.bind.annotation.ResponseStatus 7 | import org.springframework.web.bind.annotation.RestControllerAdvice 8 | import team.comit.simtong.global.error.dto.ErrorResponse 9 | 10 | /** 11 | * 12 | * 전체적으로 발생하는 예외를 핸들링하는 GlobalErrorHandler 13 | * 14 | * @author kimbeomjin 15 | * @date 2022/08/22 16 | * @version 1.2.3 17 | **/ 18 | @RestControllerAdvice 19 | class GlobalErrorHandler { 20 | 21 | /** 22 | * SQL 문을 통한 데이터의 삽입/수정이 무결성 제약 조건을 위반할 경우 발생 23 | */ 24 | @ExceptionHandler(DataIntegrityViolationException::class) 25 | @ResponseStatus(HttpStatus.BAD_REQUEST) 26 | protected fun handleDataIntegrityViolationException( 27 | exception: DataIntegrityViolationException 28 | ): ErrorResponse? { 29 | return ErrorResponse.of(exception) 30 | } 31 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/usecase/QueryRemainAnnualUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.usecase 2 | 3 | import team.comit.simtong.domain.holiday.model.Holiday 4 | import team.comit.simtong.domain.holiday.model.HolidayType 5 | import team.comit.simtong.domain.holiday.spi.HolidaySecurityPort 6 | import team.comit.simtong.domain.holiday.spi.QueryHolidayPort 7 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 8 | 9 | /** 10 | * 11 | * 남은 연차 개수 확인을 담당하는 QueryRemainAnnualUseCase 12 | * 13 | * @author Chokyunghyeon 14 | * @date 2022/12/20 15 | * @version 1.0.0 16 | **/ 17 | @ReadOnlyUseCase 18 | class QueryRemainAnnualUseCase( 19 | private val queryHolidayPort: QueryHolidayPort, 20 | private val securityPort: HolidaySecurityPort 21 | ) { 22 | 23 | fun execute(year: Int): Long { 24 | val currentUserId = securityPort.getCurrentUserId() 25 | 26 | val countAnnual = queryHolidayPort.countHolidayByYearAndUserIdAndType(year, currentUserId, HolidayType.ANNUAL) 27 | 28 | return Holiday.ANNUAL_LEAVE_LIMIT - countAnnual 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/schedule/exception/ScheduleExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.exception 2 | 3 | import team.comit.simtong.global.exception.BusinessException 4 | 5 | /** 6 | * 7 | * Schedule Domain에서 발생하는 Exception을 관리하는 ScheduleExceptions 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/16 11 | * @version 1.0.0 12 | **/ 13 | sealed class ScheduleExceptions( 14 | override val status: Int, 15 | override val message: String 16 | ) : BusinessException(status, message) { 17 | 18 | // 401 19 | class DifferentScope(message: String = DIFFERENT_SCOPE) : ScheduleExceptions(401, message) 20 | 21 | // 403 22 | class NotScheduleOwner(message: String = NOT_SCHEDULE_OWNER) : ScheduleExceptions(403, message) 23 | 24 | // 404 25 | class NotFound(message: String = NOT_FOUND) : ScheduleExceptions(404, message) 26 | 27 | companion object { 28 | private const val DIFFERENT_SCOPE = "일정의 범위가 다릅니다." 29 | private const val NOT_SCHEDULE_OWNER = "일정의 소유자가 아닙니다." 30 | private const val NOT_FOUND = "일정을 찾을 수 없습니다." 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/security/SecurityAdapter.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.security 2 | 3 | import org.springframework.security.core.context.SecurityContextHolder 4 | import org.springframework.security.crypto.password.PasswordEncoder 5 | import org.springframework.stereotype.Component 6 | import team.comit.simtong.domain.auth.spi.SecurityPort 7 | import java.util.* 8 | 9 | /** 10 | * 11 | * Security 관련 기능을 관리하는 SecurityAdapter 12 | * 13 | * @author Chokyunghyeon 14 | * @date 2022/09/05 15 | * @version 1.0.0 16 | **/ 17 | @Component 18 | class SecurityAdapter( 19 | private val passwordEncoder: PasswordEncoder 20 | ) : SecurityPort { 21 | override fun compare(target: String, encryptedPassword: String): Boolean { 22 | return passwordEncoder.matches(target, encryptedPassword) 23 | } 24 | 25 | override fun encode(password: String): String { 26 | return passwordEncoder.encode(password) 27 | } 28 | 29 | override fun getCurrentUserId(): UUID { 30 | return UUID.fromString(SecurityContextHolder.getContext().authentication.name) 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/usecase/ReissueTokenUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.usecase 2 | 3 | import team.comit.simtong.domain.auth.dto.TokenResponse 4 | import team.comit.simtong.domain.auth.exception.AuthExceptions 5 | import team.comit.simtong.domain.auth.spi.JwtPort 6 | import team.comit.simtong.domain.auth.spi.QueryRefreshTokenPort 7 | import team.comit.simtong.global.annotation.UseCase 8 | 9 | /** 10 | * 11 | * 토큰 재발급을 담당하는 ReissueTokenUseCase 12 | * 13 | * @author Chokyunghyeon 14 | * @author kimbeomjin 15 | * @date 2022/09/18 16 | * @version 1.0.0 17 | **/ 18 | @UseCase 19 | class ReissueTokenUseCase( 20 | private val jwtPort: JwtPort, 21 | private val queryRefreshTokenPort: QueryRefreshTokenPort 22 | ) { 23 | 24 | fun execute(request: String): TokenResponse { 25 | val token = queryRefreshTokenPort.queryRefreshTokenByToken(request) 26 | ?: throw AuthExceptions.RefreshTokenNotFound() 27 | 28 | return jwtPort.receiveToken( 29 | userId = token.userId, 30 | authority = token.authority 31 | ) 32 | } 33 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/aop/GlobalComponentScan.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.aop 2 | 3 | import org.springframework.context.annotation.ComponentScan 4 | import org.springframework.context.annotation.ComponentScan.Filter 5 | import org.springframework.context.annotation.Configuration 6 | import org.springframework.context.annotation.FilterType 7 | import team.comit.simtong.global.annotation.DomainService 8 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 9 | import team.comit.simtong.global.annotation.UseCase 10 | 11 | /** 12 | * 13 | * Annotation을 scan해 Bean으로 등록하는 GlobalComponentScan 14 | * 15 | * @author kimbeomjin 16 | * @date 2022/08/27 17 | * @version 1.0.0 18 | **/ 19 | @Configuration 20 | @ComponentScan( 21 | basePackages = ["team.comit.simtong"], 22 | includeFilters = [ 23 | Filter( 24 | type = FilterType.ANNOTATION, 25 | classes = [ 26 | UseCase::class, 27 | ReadOnlyUseCase::class, 28 | DomainService::class 29 | ] 30 | ) 31 | ] 32 | ) 33 | class GlobalComponentScan { 34 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/RefreshTokenPersistenceAdapter.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth 2 | 3 | import org.springframework.data.repository.findByIdOrNull 4 | import org.springframework.stereotype.Component 5 | import team.comit.simtong.domain.auth.model.RefreshToken 6 | import team.comit.simtong.domain.auth.spi.RefreshTokenPort 7 | import team.comit.simtong.persistence.auth.mapper.RefreshTokenMapper 8 | import team.comit.simtong.persistence.auth.repository.RefreshTokenRepository 9 | 10 | /** 11 | * 12 | * Refresh Token을 관리하는 RefreshTokenAdapter 13 | * 14 | * @author Chokyunghyeon 15 | * @author kimbeomjin 16 | * @date 2022/09/18 17 | * @version 1.0.0 18 | **/ 19 | @Component 20 | class RefreshTokenPersistenceAdapter( 21 | private val refreshTokenRepository: RefreshTokenRepository, 22 | private val refreshTokenMapper: RefreshTokenMapper 23 | ) : RefreshTokenPort { 24 | 25 | override fun queryRefreshTokenByToken(token: String): RefreshToken? { 26 | return refreshTokenRepository.findByIdOrNull(token) 27 | .let(refreshTokenMapper::toDomain) 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/spi/QueryUserPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.spi 2 | 3 | import team.comit.simtong.domain.user.model.User 4 | import java.util.UUID 5 | 6 | /** 7 | * 8 | * User Domain에 관한 Query를 요청하는 QueryUserPort 9 | * 10 | * @author Chokyunghyeon 11 | * @author kimbeomjin 12 | * @date 2022/09/04 13 | * @version 1.0.0 14 | **/ 15 | interface QueryUserPort { 16 | 17 | fun existsUserByEmployeeNumber(employeeNumber: Int): Boolean 18 | 19 | fun existsUserByEmail(email: String): Boolean 20 | 21 | fun existsUserByNickname(nickname: String): Boolean 22 | 23 | fun existsUserByEmployeeNumberAndEmail(employeeNumber: Int, email: String): Boolean 24 | 25 | fun queryUserById(id: UUID): User? 26 | 27 | fun queryUserByEmployeeNumber(employeeNumber: Int): User? 28 | 29 | fun queryUserByEmail(email: String): User? 30 | 31 | fun queryUserByNickName(nickName: String): User? 32 | 33 | fun queryUserByNameAndSpotAndEmail(name: String, spotId: UUID, email: String): User? 34 | 35 | fun queryUserByEmailAndEmployeeNumber(email: String, employeeNumber: Int): User? 36 | 37 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/security/principle/AuthDetailsService.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.security.principle 2 | 3 | import org.springframework.data.repository.findByIdOrNull 4 | import org.springframework.security.core.userdetails.UserDetails 5 | import org.springframework.security.core.userdetails.UserDetailsService 6 | import org.springframework.stereotype.Component 7 | import team.comit.simtong.global.security.exception.SecurityExceptions 8 | import team.comit.simtong.persistence.user.repository.UserJpaRepository 9 | import java.util.UUID 10 | 11 | /** 12 | * 13 | * 사용자의 인증 정보를 불러오는 AuthDetailsService 14 | * 15 | * @author kimbeomjin 16 | * @date 2022/08/31 17 | * @version 1.0.0 18 | **/ 19 | @Component 20 | class AuthDetailsService( 21 | private val userRepository: UserJpaRepository 22 | ) : UserDetailsService { 23 | 24 | override fun loadUserByUsername(userId: String): UserDetails { 25 | val id = UUID.fromString(userId) 26 | val user = userRepository.findByIdOrNull(id) ?: throw SecurityExceptions.InvalidToken("토큰 ID에 해당하는 유저를 찾을 수 없습니다.") 27 | 28 | return AuthDetails(id, user.authority) 29 | } 30 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/team/mapper/TeamMapper.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.team.mapper 2 | 3 | import org.mapstruct.Mapper 4 | import org.mapstruct.Mapping 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import team.comit.simtong.domain.team.model.Team 7 | import team.comit.simtong.persistence.GenericMapper 8 | import team.comit.simtong.persistence.spot.SpotJpaRepository 9 | import team.comit.simtong.persistence.team.entity.TeamJpaEntity 10 | 11 | /** 12 | * 13 | * TeamEntity와 DomainTeam을 변환하는 TeamMapper 14 | * 15 | * @author Chokyunghyeon 16 | * @date 2022/09/18 17 | * @version 1.0.0 18 | **/ 19 | @Mapper 20 | abstract class TeamMapper : GenericMapper { 21 | 22 | @Autowired 23 | protected lateinit var spotJpaRepository: SpotJpaRepository 24 | 25 | @Mapping(target = "spot", expression = "java(spotJpaRepository.findById(model.getSpotId()).orElse(null))") 26 | abstract override fun toEntity(model: Team): TeamJpaEntity 27 | 28 | @Mapping(target = "spotId", expression = "java(entity.getSpot().getId())") 29 | abstract override fun toDomain(entity: TeamJpaEntity?): Team? 30 | 31 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/error/dto/CustomFieldError.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.error.dto 2 | 3 | import org.springframework.validation.BindingResult 4 | 5 | /** 6 | * 7 | * 상세한 예외를 보여주기 위한 CustomFieldError 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/09/07 11 | * @version 1.0.0 12 | **/ 13 | class CustomFieldError( 14 | val field: String, 15 | val value: String, 16 | val reason: String 17 | ) { 18 | companion object { 19 | fun of(field: String, value: String, reason: String): List { 20 | val fieldErrors: MutableList = ArrayList() 21 | fieldErrors.add(CustomFieldError(field, value, reason)) 22 | return fieldErrors 23 | } 24 | 25 | fun of(bindingResult: BindingResult): List { 26 | val fieldErrors = bindingResult.fieldErrors 27 | return fieldErrors.map { error -> 28 | CustomFieldError( 29 | field = error.field, 30 | value = error.rejectedValue.toString(), 31 | reason = error.defaultMessage!! 32 | ) 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/user/dto/request/SignUpWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.dto.request 2 | 3 | import org.hibernate.validator.constraints.Range 4 | import team.comit.simtong.domain.user.value.EmployeeNumber 5 | import team.comit.simtong.domain.user.value.NickName 6 | import team.comit.simtong.domain.user.value.Password 7 | import javax.validation.constraints.Email 8 | import javax.validation.constraints.NotBlank 9 | import javax.validation.constraints.Pattern 10 | 11 | /** 12 | * 13 | * 회원 가입을 요청하는 SignUpWebRequest 14 | * 15 | * @author Chokyunghyeon 16 | * @date 2022/09/04 17 | * @version 1.2.3 18 | **/ 19 | data class SignUpWebRequest( 20 | 21 | @field:NotBlank 22 | val name: String, 23 | 24 | @field:NotBlank 25 | @field:Email 26 | val email: String, 27 | 28 | @Range(min = EmployeeNumber.MIN_VALUE, max = EmployeeNumber.MAX_VALUE) 29 | val employeeNumber: EmployeeNumber, 30 | 31 | @Pattern(regexp = Password.PATTERN) 32 | val password: Password, 33 | 34 | @Pattern(regexp = NickName.PATTERN) 35 | val nickname: NickName, 36 | 37 | val profileImagePath: String?, 38 | 39 | @field:NotBlank 40 | val deviceToken: String 41 | ) 42 | -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/dto/QueryEmployeeHolidayResponse.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.dto 2 | 3 | import team.comit.simtong.domain.holiday.model.HolidayType 4 | import java.time.LocalDate 5 | import java.util.UUID 6 | 7 | /** 8 | * 9 | * 지점 직원의 휴무일을 반환하는 QueryEmployeeHolidayResponse 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/12/22 13 | * @version 1.0.0 14 | **/ 15 | data class QueryEmployeeHolidayResponse( 16 | val holidays: List 17 | ) { 18 | 19 | /** 20 | * 21 | * 지점 직원의 휴무일 정보를 반환하는 Holiday 22 | * 23 | * @author kimbeomjin 24 | * @date 2022/12/22 25 | * @version 1.0.0 26 | **/ 27 | data class Holiday( 28 | val date: LocalDate, 29 | val type: HolidayType, 30 | val user: Employee 31 | ) { 32 | 33 | /** 34 | * 35 | * 지점 직원 정보를 반환하는 Employee 36 | * 37 | * @author kimbeomjin 38 | * @date 2022/12/22 39 | * @version 1.0.0 40 | **/ 41 | data class Employee( 42 | val id: UUID, 43 | val name: String, 44 | val team: String, 45 | val spot: String 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/notification/dto/SendNotificationWebRequest.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.notification.dto 2 | 3 | import java.util.UUID 4 | import javax.validation.constraints.NotBlank 5 | import javax.validation.constraints.NotNull 6 | 7 | /** 8 | * 9 | * 단일 알림 전송을 요청하는 SendNotificationWebRequest 10 | * 11 | * @author kimbeomjin 12 | * @date 2022/12/30 13 | * @version 1.1.0 14 | **/ 15 | data class SendNotificationWebRequest( 16 | 17 | @field:NotNull 18 | val userId: UUID?, 19 | 20 | @field:NotBlank 21 | val title: String?, 22 | 23 | @field:NotBlank 24 | val content: String?, 25 | 26 | @field:NotNull 27 | val type: WebNotificationType?, 28 | 29 | val identify: UUID? 30 | ) 31 | 32 | /** 33 | * 34 | * 다중 알림 전송을 요청하는 SendMultiNotificationWebRequest 35 | * 36 | * @author kimbeomjin 37 | * @date 2022/12/30 38 | * @version 1.1.0 39 | **/ 40 | data class SendMultiNotificationWebRequest( 41 | val userIds: List, 42 | 43 | @field:NotBlank 44 | val title: String?, 45 | 46 | @field:NotBlank 47 | val content: String?, 48 | 49 | @field:NotNull 50 | val type: WebNotificationType?, 51 | 52 | val identify: UUID? 53 | ) 54 | -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/security/exception/SecurityExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.security.exception 2 | 3 | import team.comit.simtong.global.exception.BusinessException 4 | 5 | /** 6 | * 7 | * Security에서 발생하는 Exception을 관리하는 SecurityExceptions 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/17 11 | * @version 1.0.0 12 | **/ 13 | sealed class SecurityExceptions( 14 | override val status: Int, 15 | override val message: String 16 | ) : BusinessException(status, message) { 17 | 18 | // 401 19 | class ExpiredToken(message: String = EXPIRED_TOKEN) : SecurityExceptions(401, message) 20 | class UnexpectedToken(message: String = UNEXPECTED_TOKEN) : SecurityExceptions(401, message) 21 | class WrongTypeToken(message: String = WRONG_TYPE_TOKEN) : SecurityExceptions(401, message) 22 | class InvalidToken(message: String = INVALID_TOKEN) : SecurityExceptions(401, message) 23 | 24 | companion object { 25 | private const val EXPIRED_TOKEN = "토큰이 만료되었습니다." 26 | private const val UNEXPECTED_TOKEN = "알 수 없는 토큰입니다." 27 | private const val WRONG_TYPE_TOKEN = "토큰 유형이 적합하지 않습니다." 28 | private const val INVALID_TOKEN = "토큰이 유효하지 않습니다." 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/global/config/WebMvcConfig.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.config 2 | 3 | import org.springframework.context.annotation.Configuration 4 | import org.springframework.format.FormatterRegistry 5 | import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar 6 | import org.springframework.web.servlet.config.annotation.CorsRegistry 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer 8 | 9 | /** 10 | * 11 | * WebMvc 관련 설정을 하는 WebMvcConfig 12 | * LocalDateTime, LocalDate, LocalTime 요청값을 ISO-8601으로 설정 13 | * 14 | * @author Chokyunghyeon 15 | * @author kimbeomjin 16 | * @date 2022/12/08 17 | * @version 1.0.0 18 | **/ 19 | @Configuration 20 | class WebMvcConfig : WebMvcConfigurer { 21 | 22 | override fun addCorsMappings(registry: CorsRegistry) { 23 | registry.addMapping("/**") 24 | .allowedHeaders("*") 25 | .allowedMethods("*") 26 | .allowedOrigins("*") 27 | } 28 | 29 | override fun addFormatters(registry: FormatterRegistry) { 30 | val registrar = DateTimeFormatterRegistrar().apply { 31 | setUseIsoFormat(true) 32 | } 33 | registrar.registerFormatters(registry) 34 | } 35 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/auth/usecase/CheckAuthCodeUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.auth.usecase 2 | 3 | import team.comit.simtong.domain.auth.exception.AuthExceptions 4 | import team.comit.simtong.domain.auth.model.AuthCodeLimit 5 | import team.comit.simtong.domain.auth.spi.CommandAuthCodeLimitPort 6 | import team.comit.simtong.domain.auth.spi.QueryAuthCodePort 7 | import team.comit.simtong.global.annotation.UseCase 8 | 9 | /** 10 | * 11 | * 이메일 인증 코드 확인을 담당하는 CheckAuthCodeUseCase 12 | * 13 | * @author Chokyunghyeon 14 | * @author kimbeomjin 15 | * @date 2022/09/24 16 | * @version 1.0.0 17 | **/ 18 | @UseCase 19 | class CheckAuthCodeUseCase( 20 | private val commandAuthCodeLimitPort: CommandAuthCodeLimitPort, 21 | private val queryAuthCodePort: QueryAuthCodePort 22 | ) { 23 | 24 | fun execute(email: String, code: String) { 25 | val authCode = queryAuthCodePort.queryAuthCodeByEmail(email) ?: throw AuthExceptions.RequiredNewEmailAuthentication() 26 | 27 | if (authCode.code != code) { 28 | throw AuthExceptions.DifferentAuthCode() 29 | } 30 | 31 | commandAuthCodeLimitPort.save( 32 | AuthCodeLimit.certified(email) 33 | ) 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/extension/QuerydslExtensionUtils.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.extension 2 | 3 | import com.querydsl.core.types.dsl.BooleanExpression 4 | import com.querydsl.core.types.dsl.DatePath 5 | import java.time.DayOfWeek 6 | import java.time.LocalDate 7 | 8 | /** 9 | * 10 | * Querydsl의 확장 함수를 관리하는 QuerydslExtensionUtils 11 | * 12 | * @author Chokyunghyeon 13 | * @date 2022/12/03 14 | * @version 1.0.0 15 | **/ 16 | object QuerydslExtensionUtils { 17 | 18 | /** 19 | * 입력된 날짜가 포함된 달 20 | */ 21 | fun DatePath.sameMonthFilter(date: LocalDate) : BooleanExpression { 22 | val startOfMonth = date.withDayOfMonth(1) 23 | val endOfMonth = date.withDayOfMonth(date.lengthOfMonth()) 24 | 25 | return between(startOfMonth, endOfMonth) 26 | } 27 | 28 | /** 29 | * 입력된 날짜가 포함된 주 30 | */ 31 | fun DatePath.sameWeekFilter(date: LocalDate) : BooleanExpression { 32 | val startOfWeek = date.with(DayOfWeek.MONDAY) 33 | val endOfWeek = date.with(DayOfWeek.SUNDAY) 34 | 35 | return between(startOfWeek, endOfWeek) 36 | } 37 | 38 | infix fun BooleanExpression.or(exp: BooleanExpression) : BooleanExpression = this.or(exp) 39 | 40 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/auth/AuthCodePersistenceAdapter.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.auth 2 | 3 | import org.springframework.data.repository.findByIdOrNull 4 | import org.springframework.stereotype.Component 5 | import team.comit.simtong.domain.auth.model.AuthCode 6 | import team.comit.simtong.domain.auth.spi.AuthCodePort 7 | import team.comit.simtong.persistence.auth.mapper.AuthCodeMapper 8 | import team.comit.simtong.persistence.auth.repository.AuthCodeRepository 9 | 10 | /** 11 | * 12 | * AuthCode를 관리하는 AuthCodePersistenceAdapter 13 | * 14 | * @author Chokyunghyeon 15 | * @date 2022/09/25 16 | * @version 1.2.3 17 | **/ 18 | @Component 19 | class AuthCodePersistenceAdapter( 20 | private val authCodeMapper: AuthCodeMapper, 21 | private val authCodeRepository: AuthCodeRepository 22 | ): AuthCodePort { 23 | 24 | override fun queryAuthCodeByEmail(email: String): AuthCode? { 25 | return authCodeRepository.findByIdOrNull(email) 26 | .let(authCodeMapper::toDomain) 27 | } 28 | 29 | override fun save(authCode: AuthCode): AuthCode { 30 | return authCodeRepository.save( 31 | authCodeMapper.toEntity(authCode) 32 | ).let(authCodeMapper::toDomainNotNull) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/filter/FilterConfig.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.filter 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import org.springframework.security.config.annotation.SecurityConfigurerAdapter 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity 6 | import org.springframework.security.web.DefaultSecurityFilterChain 7 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter 8 | import org.springframework.stereotype.Component 9 | import team.comit.simtong.global.security.token.JwtParser 10 | 11 | /** 12 | * 13 | * SecurityFilterChain에 직접 만든 Filter를 등록하는 FilterConfig 14 | * 15 | * @author kimbeomjin 16 | * @author Chokyunghyeon 17 | * @date 2022/08/31 18 | * @version 1.0.0 19 | **/ 20 | @Component 21 | class FilterConfig( 22 | private val objectMapper: ObjectMapper, 23 | private val jwtParser: JwtParser 24 | ) : SecurityConfigurerAdapter() { 25 | 26 | override fun configure(builder: HttpSecurity) { 27 | builder.addFilterBefore(JwtFilter(jwtParser), UsernamePasswordAuthenticationFilter::class.java) 28 | builder.addFilterBefore(ExceptionFilter(objectMapper), JwtFilter::class.java) 29 | } 30 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/file/exception/FileExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.file.exception 2 | 3 | import team.comit.simtong.global.exception.BusinessException 4 | 5 | /** 6 | * 7 | * File Domain에서 발생하는 Exception을 관리하는 FileExceptions 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/16 11 | * @version 1.0.0 12 | **/ 13 | sealed class FileExceptions( 14 | override val status: Int, 15 | override val message: String 16 | ) : BusinessException(status, message) { 17 | 18 | // 400 19 | class NotValidContent(message: String = NOT_VALID_CONTENT) : FileExceptions(400, message) 20 | 21 | // 401 22 | class NotExistsEmployee(message: String = NOT_EXISTS_EMPLOYEE) : FileExceptions(401, message) 23 | 24 | // 404 25 | class PathNotFound(message: String = PATH_NOT_FOUND) : FileExceptions(404, message) 26 | 27 | // 500 28 | class IOInterrupted(message: String = IO_INTERRUPTED) : FileExceptions(500, message) 29 | 30 | companion object { 31 | private const val NOT_VALID_CONTENT = "파일의 내용이 올바르지 않습니다." 32 | private const val NOT_EXISTS_EMPLOYEE = "일치하는 사원 정보가 존재하지 않습니다." 33 | private const val PATH_NOT_FOUND = "휴무일을 찾을 수 없습니다." 34 | private const val IO_INTERRUPTED = "파일 입출력 처리가 중단되었습니다." 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /simtong-application/src/test/kotlin/team/comit/simtong/global/DomainPropertiesInitialization.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global 2 | 3 | import team.comit.simtong.global.DomainPropertiesPrefix.ANNUAL_LEAVE_LIMIT 4 | import team.comit.simtong.global.DomainPropertiesPrefix.AUTHCODELIMIT_EXP 5 | import team.comit.simtong.global.DomainPropertiesPrefix.AUTHCODELIMIT_MAX_ATTEMPT_COUNT 6 | import team.comit.simtong.global.DomainPropertiesPrefix.AUTHCODELIMIT_VERIFIED_EXP 7 | import team.comit.simtong.global.DomainPropertiesPrefix.AUTHCODE_EXP 8 | import team.comit.simtong.global.DomainPropertiesPrefix.HEAD_SHOP 9 | import team.comit.simtong.global.DomainPropertiesPrefix.USER_DEFAULT_IMAGE 10 | import team.comit.simtong.global.DomainPropertiesPrefix.WEEK_HOLIDAY_LIMIT 11 | 12 | object DomainPropertiesInitialization { 13 | init { 14 | DomainProperties.putAll( 15 | mapOf( 16 | USER_DEFAULT_IMAGE to "test", 17 | AUTHCODE_EXP to "12345", 18 | AUTHCODELIMIT_EXP to "12345", 19 | AUTHCODELIMIT_VERIFIED_EXP to "12345", 20 | AUTHCODELIMIT_MAX_ATTEMPT_COUNT to "12345", 21 | HEAD_SHOP to "test", 22 | WEEK_HOLIDAY_LIMIT to "12345", 23 | ANNUAL_LEAVE_LIMIT to "12345" 24 | ) 25 | ) 26 | } 27 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/menu/usecase/SaveMenuUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.usecase 2 | 3 | import team.comit.simtong.domain.menu.dto.SaveMenuRequest 4 | import team.comit.simtong.domain.menu.exception.MenuExceptions 5 | import team.comit.simtong.domain.menu.spi.CommandMenuPort 6 | import team.comit.simtong.domain.menu.spi.ParseMenuFilePort 7 | import team.comit.simtong.domain.menu.spi.QueryMenuPort 8 | import team.comit.simtong.global.annotation.UseCase 9 | import java.time.LocalDate 10 | 11 | /** 12 | * 13 | * 지점별로 월별 메뉴를 저장을 담당하는 SaveMenuUseCase 14 | * 15 | * @author kimbeomjin 16 | * @date 2022/12/10 17 | * @version 1.0.0 18 | **/ 19 | @UseCase 20 | class SaveMenuUseCase( 21 | private val parseMenuFilePort: ParseMenuFilePort, 22 | private val queryMenuPort: QueryMenuPort, 23 | private val commandMenuPort: CommandMenuPort 24 | ) { 25 | 26 | fun execute(request: SaveMenuRequest) { 27 | val (file, year, month, spotId) = request 28 | 29 | val menu = parseMenuFilePort.importMenu(file, year, month, spotId) 30 | 31 | if (queryMenuPort.existsMenuByMonthAndSpotId(LocalDate.of(year, month, 1), spotId)) { 32 | throw MenuExceptions.AlreadyExistsSameMonth("${year}년 ${month}월 메뉴가 이미 존재합니다.") 33 | } 34 | 35 | commandMenuPort.saveAll(menu) 36 | } 37 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/holiday/usecase/CheckHolidayPeriodUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.usecase 2 | 3 | import team.comit.simtong.domain.holiday.exception.HolidayExceptions 4 | import team.comit.simtong.domain.holiday.spi.HolidayQueryUserPort 5 | import team.comit.simtong.domain.holiday.spi.HolidaySecurityPort 6 | import team.comit.simtong.domain.holiday.spi.QueryHolidayPeriodPort 7 | import team.comit.simtong.domain.user.exception.UserExceptions 8 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 9 | import java.time.LocalDate 10 | 11 | /** 12 | * 13 | * 휴무일 작성 기간인지 확인을 담당하는 CheckHolidayPeriodUseCase 14 | * 15 | * @author Chokyunghyeon 16 | * @date 2022/12/22 17 | * @version 1.2.3 18 | **/ 19 | @ReadOnlyUseCase 20 | class CheckHolidayPeriodUseCase( 21 | private val queryHolidayPeriodPort: QueryHolidayPeriodPort, 22 | private val queryUserPort: HolidayQueryUserPort, 23 | private val securityPort: HolidaySecurityPort 24 | ) { 25 | 26 | fun execute() { 27 | val user = queryUserPort.queryUserById(securityPort.getCurrentUserId()) 28 | ?: throw UserExceptions.NotFound() 29 | 30 | if (!queryHolidayPeriodPort.existsHolidayPeriodByWithinPeriodAndSpotId(LocalDate.now(), user.spotId)) { 31 | throw HolidayExceptions.NotWritablePeriod() 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/usecase/ChangeNicknameUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.usecase 2 | 3 | import team.comit.simtong.domain.user.dto.ChangeNicknameRequest 4 | import team.comit.simtong.domain.user.exception.UserExceptions 5 | import team.comit.simtong.domain.user.spi.CommandUserPort 6 | import team.comit.simtong.domain.user.spi.QueryUserPort 7 | import team.comit.simtong.domain.user.spi.UserSecurityPort 8 | import team.comit.simtong.global.annotation.UseCase 9 | 10 | /** 11 | * 12 | * 유저의 닉네임 변경을 담당하는 ChangeNicknameUseCase 13 | * 14 | * @author Chokyunghyeon 15 | * @date 2022/10/03 16 | * @version 1.0.0 17 | **/ 18 | @UseCase 19 | class ChangeNicknameUseCase( 20 | private val queryUserPort: QueryUserPort, 21 | private val securityPort: UserSecurityPort, 22 | private val commandUserPort: CommandUserPort 23 | ) { 24 | 25 | fun execute(request: ChangeNicknameRequest) { 26 | if (queryUserPort.existsUserByNickname(request.nickname)) { 27 | throw UserExceptions.AlreadyUsedNickname() 28 | } 29 | 30 | val currentUserId = securityPort.getCurrentUserId() 31 | val user = queryUserPort.queryUserById(currentUserId) ?: throw UserExceptions.NotFound() 32 | 33 | commandUserPort.save( 34 | user.copy( 35 | nickname = request.nickname 36 | ) 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/notification/entity/NotificationJpaEntity.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.notification.entity 2 | 3 | import team.comit.simtong.domain.notification.model.NotificationType 4 | import team.comit.simtong.persistence.BaseEntity 5 | import java.time.LocalDateTime 6 | import java.util.UUID 7 | import javax.persistence.Column 8 | import javax.persistence.Entity 9 | import javax.persistence.EnumType 10 | import javax.persistence.Enumerated 11 | import javax.persistence.Table 12 | import javax.validation.constraints.NotNull 13 | 14 | /** 15 | * 16 | * 알림에 관해 관리하는 JPA 엔티티인 NotificationJpaEntity 17 | * 18 | * @author kimbeomjin 19 | * @date 2022/12/29 20 | * @version 1.1.0 21 | **/ 22 | @Entity 23 | @Table(name = "tbl_notification") 24 | class NotificationJpaEntity( 25 | override val id: UUID, 26 | 27 | @field:NotNull 28 | @Column(columnDefinition = "VARCHAR(50)") 29 | val title: String, 30 | 31 | @field:NotNull 32 | @Column(columnDefinition = "VARCHAR(255)") 33 | val content: String, 34 | 35 | @field:NotNull 36 | @Column(columnDefinition = "VARCHAR(20)") 37 | @Enumerated(EnumType.STRING) 38 | val type: NotificationType, 39 | 40 | @Column(columnDefinition = "BINARY(16)") 41 | val identify: UUID?, 42 | 43 | @field:NotNull 44 | override val createdAt: LocalDateTime 45 | 46 | ) : BaseEntity(id) -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/menu/usecase/QueryMenuByMonthUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.menu.usecase 2 | 3 | import team.comit.simtong.domain.menu.dto.MenuResponse 4 | import team.comit.simtong.domain.menu.spi.MenuQueryUserPort 5 | import team.comit.simtong.domain.menu.spi.MenuSecurityPort 6 | import team.comit.simtong.domain.menu.spi.QueryMenuPort 7 | import team.comit.simtong.domain.user.exception.UserExceptions 8 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 9 | import java.time.LocalDate 10 | 11 | /** 12 | * 13 | * Menu 조회 기능을 담당하는 QueryMenuByMonthUseCase 14 | * 15 | * @author kimbeomjin 16 | * @date 2022/09/21 17 | * @version 1.0.0 18 | **/ 19 | @ReadOnlyUseCase 20 | class QueryMenuByMonthUseCase( 21 | private val queryMenuPort: QueryMenuPort, 22 | private val queryUserPort: MenuQueryUserPort, 23 | private val menuSecurityPort: MenuSecurityPort 24 | ) { 25 | 26 | fun execute(startAt: LocalDate, endAt: LocalDate): MenuResponse { 27 | val currentUserId = menuSecurityPort.getCurrentUserId() 28 | val user = queryUserPort.queryUserById(currentUserId) ?: throw UserExceptions.NotFound() 29 | 30 | val menu = queryMenuPort.queryMenusByPeriodAndSpotId(startAt, endAt, user.spotId) 31 | val result = menu.map { MenuResponse.MenuElement(it.date, it.meal) } 32 | 33 | return MenuResponse(result) 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/spot/SpotPersistenceAdapter.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.spot 2 | 3 | import org.springframework.data.repository.findByIdOrNull 4 | import org.springframework.stereotype.Component 5 | import team.comit.simtong.domain.spot.model.Spot 6 | import team.comit.simtong.domain.spot.spi.SpotPort 7 | import team.comit.simtong.persistence.spot.mapper.SpotMapper 8 | import java.util.UUID 9 | 10 | /** 11 | * 12 | * Spot의 영속성을 관리하는 SpotPersistenceAdapter 13 | * 14 | * @author Chokyunghyeon 15 | * @date 2022/09/18 16 | * @version 1.2.3 17 | **/ 18 | @Component 19 | class SpotPersistenceAdapter( 20 | private val spotJpaRepository: SpotJpaRepository, 21 | private val spotMapper: SpotMapper 22 | ) : SpotPort { 23 | 24 | override fun existsSpotById(id: UUID): Boolean { 25 | return spotJpaRepository.existsById(id) 26 | } 27 | 28 | override fun queryAllSpot(): List { 29 | return spotJpaRepository.findAll() 30 | .map(spotMapper::toDomainNotNull) 31 | } 32 | 33 | override fun querySpotByName(name: String): Spot? { 34 | return spotJpaRepository.querySpotJpaEntityByName(name) 35 | .let(spotMapper::toDomain) 36 | } 37 | 38 | override fun querySpotById(id: UUID): Spot? { 39 | return spotJpaRepository.findByIdOrNull(id) 40 | .let(spotMapper::toDomain) 41 | } 42 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/security/principle/AuthDetails.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.security.principle 2 | 3 | import org.springframework.security.core.GrantedAuthority 4 | import org.springframework.security.core.authority.SimpleGrantedAuthority 5 | import org.springframework.security.core.userdetails.UserDetails 6 | import team.comit.simtong.domain.user.model.Authority 7 | import java.util.* 8 | 9 | /** 10 | * 11 | * 사용자의 인증 정보를 관리하는 AuthDetails 12 | * 13 | * @author kimbeomjin 14 | * @date 2022/08/31 15 | * @version 1.0.0 16 | **/ 17 | class AuthDetails( 18 | private val userId: UUID, 19 | private val authority: Authority 20 | ) : UserDetails { 21 | 22 | override fun getAuthorities(): MutableCollection { 23 | return mutableListOf(SimpleGrantedAuthority(authority.name)) 24 | } 25 | 26 | override fun getPassword(): String? { 27 | return null 28 | } 29 | 30 | override fun getUsername(): String { 31 | return userId.toString() 32 | } 33 | 34 | override fun isAccountNonExpired(): Boolean { 35 | return true 36 | } 37 | 38 | override fun isAccountNonLocked(): Boolean { 39 | return true 40 | } 41 | 42 | override fun isCredentialsNonExpired(): Boolean { 43 | return true 44 | } 45 | 46 | override fun isEnabled(): Boolean { 47 | return true 48 | } 49 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/persistence/user/repository/UserJpaRepository.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.persistence.user.repository 2 | 3 | import org.springframework.data.repository.CrudRepository 4 | import org.springframework.stereotype.Repository 5 | import team.comit.simtong.persistence.user.entity.UserJpaEntity 6 | import java.util.UUID 7 | 8 | /** 9 | * 10 | * Spring Repository의 기능을 이용하는 UserJpaRepository 11 | * 12 | * @author kimbeomjin 13 | * @author Chokyunghyeon 14 | * @date 2022/08/21 15 | * @version 1.0.0 16 | **/ 17 | @Repository 18 | interface UserJpaRepository : CrudRepository { 19 | 20 | fun queryUserJpaEntityByEmployeeNumber(employeeNumber: Int): UserJpaEntity? 21 | 22 | fun queryUserJpaEntityByEmail(email: String): UserJpaEntity? 23 | 24 | fun queryUserJpaEntityByNickname(nickName: String): UserJpaEntity? 25 | 26 | fun existsUserJpaEntitiesByEmail(email: String): Boolean 27 | 28 | fun existsUserJpaEntityByEmployeeNumberAndEmail(employeeNumber: Int, email: String): Boolean 29 | 30 | fun existsUserJpaEntitiesByNickname(nickname: String): Boolean 31 | 32 | fun existsUserJpaEntitiesByEmployeeNumber(employeeNumber: Int): Boolean 33 | 34 | fun queryUserJpaEntityByNameAndSpotIdAndEmail(name: String, spotId: UUID, email: String): UserJpaEntity? 35 | 36 | fun queryUserJpaEntityByEmailAndEmployeeNumber(email: String, employeeNumber: Int): UserJpaEntity? 37 | 38 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/usecase/ChangePasswordUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.usecase 2 | 3 | import team.comit.simtong.domain.user.dto.ChangePasswordRequest 4 | import team.comit.simtong.domain.user.exception.UserExceptions 5 | import team.comit.simtong.domain.user.spi.CommandUserPort 6 | import team.comit.simtong.domain.user.spi.QueryUserPort 7 | import team.comit.simtong.domain.user.spi.UserSecurityPort 8 | import team.comit.simtong.global.annotation.UseCase 9 | 10 | /** 11 | * 12 | * 사용자의 비밀번호 변경을 담당하는 ChangePasswordUseCase 13 | * 14 | * @author Chokyunghyeon 15 | * @date 2022/10/14 16 | * @version 1.0.0 17 | **/ 18 | @UseCase 19 | class ChangePasswordUseCase( 20 | private val queryUserPort: QueryUserPort, 21 | private val userSecurityPort: UserSecurityPort, 22 | private val commandUserPort: CommandUserPort 23 | ) { 24 | 25 | fun execute(request: ChangePasswordRequest) { 26 | val currentUserId = userSecurityPort.getCurrentUserId() 27 | val user = queryUserPort.queryUserById(currentUserId) ?: throw UserExceptions.NotFound() 28 | 29 | if (!userSecurityPort.compare(request.password, user.password)) { 30 | throw UserExceptions.DifferentPassword() 31 | } 32 | 33 | commandUserPort.save( 34 | user.copy( 35 | password = userSecurityPort.encode(request.newPassword) 36 | ) 37 | ) 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/usecase/ChangeSpotUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.usecase 2 | 3 | import team.comit.simtong.domain.spot.exception.SpotExceptions 4 | import team.comit.simtong.domain.user.exception.UserExceptions 5 | import team.comit.simtong.domain.user.spi.CommandUserPort 6 | import team.comit.simtong.domain.user.spi.QueryUserPort 7 | import team.comit.simtong.domain.user.spi.UserQuerySpotPort 8 | import team.comit.simtong.domain.user.spi.UserSecurityPort 9 | import team.comit.simtong.global.annotation.UseCase 10 | import java.util.UUID 11 | 12 | /** 13 | * 14 | * 자신의 지점 변경을 담당하는 ChangeSpotUseCase 15 | * 16 | * @author kimbeomjin 17 | * @date 2022/10/15 18 | * @version 1.0.0 19 | **/ 20 | @UseCase 21 | class ChangeSpotUseCase( 22 | private val queryUserPort: QueryUserPort, 23 | private val commandUserPort: CommandUserPort, 24 | private val querySpotPort: UserQuerySpotPort, 25 | private val securityPort: UserSecurityPort 26 | ) { 27 | 28 | fun execute(newSpotId: UUID) { 29 | val user = queryUserPort.queryUserById(securityPort.getCurrentUserId()) 30 | ?: throw UserExceptions.NotFound() 31 | 32 | if (!querySpotPort.existsSpotById(newSpotId)) { 33 | throw SpotExceptions.NotFound() 34 | } 35 | 36 | commandUserPort.save( 37 | user.copy( 38 | spotId = newSpotId 39 | ) 40 | ) 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /simtong-presentation/src/main/kotlin/team/comit/simtong/domain/email/WebEmailAdapter.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.email 2 | 3 | import org.springframework.web.bind.annotation.GetMapping 4 | import org.springframework.web.bind.annotation.PostMapping 5 | import org.springframework.web.bind.annotation.RequestBody 6 | import org.springframework.web.bind.annotation.RequestMapping 7 | import org.springframework.web.bind.annotation.RestController 8 | import team.comit.simtong.domain.auth.usecase.CheckAuthCodeUseCase 9 | import team.comit.simtong.domain.auth.usecase.SendAuthCodeUseCase 10 | import team.comit.simtong.domain.email.dto.request.CheckAuthCodeWebRequest 11 | import team.comit.simtong.domain.email.dto.request.SendAuthCodeWebRequest 12 | import javax.validation.Valid 13 | 14 | /** 15 | * 16 | * 이메일에 관한 요청을 받는 WebEmailAdapter 17 | * 18 | * @author Chokyunghyeon 19 | * @date 2022/09/24 20 | * @version 1.0.0 21 | **/ 22 | @RestController 23 | @RequestMapping("/emails") 24 | class WebEmailAdapter( 25 | private val sendAuthCodeUseCase: SendAuthCodeUseCase, 26 | private val checkAuthCodeUseCase: CheckAuthCodeUseCase 27 | ) { 28 | 29 | @PostMapping("/code") 30 | fun sendAuthCode(@Valid @RequestBody request: SendAuthCodeWebRequest) { 31 | sendAuthCodeUseCase.execute(request.email) 32 | } 33 | 34 | @GetMapping 35 | fun checkAuthCode(@Valid request: CheckAuthCodeWebRequest) { 36 | checkAuthCodeUseCase.execute(request.email, request.code) 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/user/exception/UserExceptions.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.exception 2 | 3 | import team.comit.simtong.global.exception.BusinessException 4 | 5 | /** 6 | * 7 | * User Domain에서 발생하는 Exception을 관리하는 UserExceptions 8 | * 9 | * @author kimbeomjin 10 | * @date 2022/12/16 11 | * @version 1.0.0 12 | **/ 13 | sealed class UserExceptions( 14 | override val status: Int, 15 | override val message: String 16 | ) : BusinessException(status, message) { 17 | 18 | // 401 19 | class DifferentPassword(message: String = DIFFERENT_SECRET) : UserExceptions(401, message) 20 | class DifferentPermissionAccount(message: String = DIFFERENT_PERMISSION_ACCOUNT) : UserExceptions(401, message) 21 | 22 | // 403 23 | class NotEnoughPermission(message: String = NOT_ENOUGH_PERMISSION) : UserExceptions(403, message) 24 | 25 | // 404 26 | class NotFound(message: String = NOT_FOUND) : UserExceptions(404, message) 27 | 28 | // 409 29 | class AlreadyUsedNickname(message: String = ALREADY_USED_NICKNAME) : UserExceptions(409, message) 30 | 31 | companion object { 32 | private const val DIFFERENT_SECRET = "비밀번호가 일치하지 않습니다." 33 | private const val DIFFERENT_PERMISSION_ACCOUNT = "다른 권한의 계정입니다." 34 | private const val NOT_ENOUGH_PERMISSION = "권한이 부족한 동작입니다." 35 | private const val NOT_FOUND = "사용자를 찾을 수 없습니다." 36 | private const val ALREADY_USED_NICKNAME = "이미 사용중인 닉네임입니다." 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /simtong-domain/src/main/kotlin/team/comit/simtong/domain/holiday/spi/QueryHolidayPort.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.holiday.spi 2 | 3 | import team.comit.simtong.domain.holiday.model.Holiday 4 | import team.comit.simtong.domain.holiday.model.HolidayStatus 5 | import team.comit.simtong.domain.holiday.model.HolidayType 6 | import team.comit.simtong.domain.holiday.spi.vo.EmployeeHoliday 7 | import java.time.LocalDate 8 | import java.util.UUID 9 | 10 | /** 11 | * 12 | * Holiday Domain에 관한 Query를 요청하는 QueryHolidayPort 13 | * 14 | * @author Chokyunghyeon 15 | * @author kimbeomjin 16 | * @date 2022/12/03 17 | * @version 1.0.0 18 | **/ 19 | interface QueryHolidayPort { 20 | 21 | fun countHolidayByYearAndUserIdAndType(year: Int, userId: UUID, type: HolidayType): Long 22 | 23 | fun countHolidayByWeekAndUserIdAndType(date: LocalDate, userId: UUID, type: HolidayType): Long 24 | 25 | fun queryHolidayByDateAndUserId(date: LocalDate, userId: UUID) : Holiday? 26 | 27 | fun queryHolidaysByPeriodAndUserIdAndStatus(startAt: LocalDate, endAt: LocalDate, userId: UUID, status: HolidayStatus) : List 28 | 29 | fun queryHolidaysByYearAndMonthAndSpotIdAndType(year: Int, month: Int, spotId: UUID, type: HolidayType) : List 30 | 31 | fun queryHolidaysByYearAndMonthAndTeamId(year: Int, month: Int, type: HolidayType?, spotId: UUID, teamId: UUID?) : List 32 | 33 | fun existsHolidayByDateAndUserIdAndType(date: LocalDate, userId: UUID, type: HolidayType) : Boolean 34 | 35 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/schedule/usecase/QueryEntireSpotScheduleUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.schedule.usecase 2 | 3 | import team.comit.simtong.domain.schedule.dto.QueryEntireSpotScheduleResponse 4 | import team.comit.simtong.domain.schedule.dto.SpotScheduleResponse 5 | import team.comit.simtong.domain.schedule.model.Scope 6 | import team.comit.simtong.domain.schedule.spi.QuerySchedulePort 7 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 8 | import java.time.LocalDate 9 | 10 | /** 11 | * 12 | * 모든 지점 일정 조회 요청을 담당하는 QueryEntireSpotScheduleUseCase 13 | * 14 | * @author Chokyunghyeon 15 | * @date 2022/11/26 16 | * @version 1.0.0 17 | **/ 18 | @ReadOnlyUseCase 19 | class QueryEntireSpotScheduleUseCase( 20 | private val querySchedulePort: QuerySchedulePort 21 | ) { 22 | 23 | fun execute(startAt: LocalDate, endAt: LocalDate): QueryEntireSpotScheduleResponse { 24 | val list = querySchedulePort.querySpotSchedulesByPeriodAndScope(startAt, endAt, Scope.ENTIRE) 25 | 26 | val response = list.map { 27 | SpotScheduleResponse( 28 | id = it.id, 29 | startAt = it.startAt, 30 | endAt = it.endAt, 31 | title = it.title, 32 | spot = SpotScheduleResponse.SpotElement( 33 | id = it.spotId, 34 | name = it.spotName 35 | ) 36 | ) 37 | } 38 | 39 | return QueryEntireSpotScheduleResponse(response) 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/global/filter/JwtFilter.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.global.filter 2 | 3 | import org.springframework.security.core.context.SecurityContextHolder 4 | import org.springframework.web.filter.OncePerRequestFilter 5 | import team.comit.simtong.global.security.token.JwtComponent 6 | import team.comit.simtong.global.security.token.JwtParser 7 | import javax.servlet.FilterChain 8 | import javax.servlet.http.HttpServletRequest 9 | import javax.servlet.http.HttpServletResponse 10 | 11 | /** 12 | * 13 | * Jwt를 검사해 권한을 인증하는 JwtFilter 14 | * 15 | * @author Chokyunghyeon 16 | * @date 2022/09/03 17 | * @version 1.0.0 18 | **/ 19 | class JwtFilter( 20 | private val jwtParser: JwtParser 21 | ): OncePerRequestFilter() { 22 | 23 | override fun doFilterInternal( 24 | request: HttpServletRequest, 25 | response: HttpServletResponse, 26 | filterChain: FilterChain 27 | ) { 28 | val token = resolvedToken(request) 29 | 30 | token?.run { 31 | SecurityContextHolder.getContext().authentication = jwtParser.getAuthentication(this) 32 | } 33 | 34 | filterChain.doFilter(request, response) 35 | } 36 | 37 | private fun resolvedToken(request: HttpServletRequest): String? { 38 | val bearerToken = request.getHeader(JwtComponent.HEADER) 39 | 40 | if(bearerToken != null && (bearerToken.startsWith(JwtComponent.PREFIX))) { 41 | return bearerToken.substring(7) 42 | } 43 | 44 | return null 45 | } 46 | } -------------------------------------------------------------------------------- /simtong-application/src/main/kotlin/team/comit/simtong/domain/user/usecase/QueryUserInfoUseCase.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.domain.user.usecase 2 | 3 | import team.comit.simtong.domain.spot.exception.SpotExceptions 4 | import team.comit.simtong.domain.user.dto.QueryUserInfoResponse 5 | import team.comit.simtong.domain.user.exception.UserExceptions 6 | import team.comit.simtong.domain.user.spi.QueryUserPort 7 | import team.comit.simtong.domain.user.spi.UserQuerySpotPort 8 | import team.comit.simtong.domain.user.spi.UserSecurityPort 9 | import team.comit.simtong.global.annotation.ReadOnlyUseCase 10 | 11 | /** 12 | * 13 | * 유저 계정의 정보 조회를 담당하는 QueryUserInfoUseCase 14 | * 15 | * @author Chokyunghyeon 16 | * @date 2022/09/27 17 | * @version 1.0.0 18 | **/ 19 | @ReadOnlyUseCase 20 | class QueryUserInfoUseCase( 21 | private val queryUserPort: QueryUserPort, 22 | private val securityPort: UserSecurityPort, 23 | private val querySpotPort: UserQuerySpotPort 24 | ) { 25 | 26 | fun execute(): QueryUserInfoResponse { 27 | val currentUserId = securityPort.getCurrentUserId() 28 | 29 | val user = queryUserPort.queryUserById(currentUserId) ?: throw UserExceptions.NotFound() 30 | val spot = querySpotPort.querySpotById(user.spotId) ?: throw SpotExceptions.NotFound() 31 | 32 | return QueryUserInfoResponse( 33 | name = user.name, 34 | email = user.email, 35 | nickname = user.nickname, 36 | spot = spot.name, 37 | profileImagePath = user.profileImagePath 38 | ) 39 | } 40 | } -------------------------------------------------------------------------------- /simtong-infrastructure/src/main/kotlin/team/comit/simtong/thirdparty/notification/FcmConfig.kt: -------------------------------------------------------------------------------- 1 | package team.comit.simtong.thirdparty.notification 2 | 3 | import com.google.auth.oauth2.GoogleCredentials 4 | import com.google.firebase.FirebaseApp 5 | import com.google.firebase.FirebaseOptions 6 | import org.springframework.beans.factory.annotation.Value 7 | import org.springframework.context.annotation.Configuration 8 | import java.io.ByteArrayInputStream 9 | import java.io.IOException 10 | import java.nio.charset.StandardCharsets 11 | import javax.annotation.PostConstruct 12 | 13 | /** 14 | * 15 | * FCM 관련 설정을 하는 FcmConfig 16 | * 17 | * @author kimbeomjin 18 | * @date 2022/12/27 19 | * @version 1.1.0 20 | **/ 21 | @Configuration 22 | class FcmConfig( 23 | @Value("\${fcm.value}") 24 | private val fcmValue: String 25 | ) { 26 | 27 | @PostConstruct 28 | private fun initialize() { 29 | try { 30 | if (FirebaseApp.getApps().isEmpty()) { 31 | val options: FirebaseOptions = FirebaseOptions.builder() 32 | .setCredentials( 33 | GoogleCredentials.fromStream( 34 | ByteArrayInputStream( 35 | fcmValue.toByteArray(StandardCharsets.UTF_8) 36 | ) 37 | ) 38 | ) 39 | .build() 40 | FirebaseApp.initializeApp(options) 41 | } 42 | } catch (e: IOException) { 43 | e.printStackTrace() 44 | } 45 | } 46 | } --------------------------------------------------------------------------------