From ed41ef24cb39adce162551971c2c71c6734774cd Mon Sep 17 00:00:00 2001 From: sdw086 Date: Mon, 20 Jul 2026 17:49:24 +0900 Subject: [PATCH] =?UTF-8?q?admin/front=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-api-core/build.gradle | 34 + .../exception/GlobalExceptionHandler.java | 6 +- .../common/file/service/FileImageService.java | 155 ++++ .../file/service/FileStorageService.java | 288 +++++++ .../common/file/vo/FileStorageUploadVo.java | 17 + .../common}/jwt/JwtAccessDeniedHandler.java | 6 +- .../jwt/JwtAuthenticationEntryPoint.java | 6 +- .../common/jwt/JwtAuthenticationFilter.java | 101 +++ .../core/common}/jwt/JwtTokenProvider.java | 30 +- .../api/core/common/paging/PagingRequest.java | 6 +- .../core/common/paging/PagingResponse.java | 16 +- .../core}/common/response/ApiResponse.java | 2 +- .../common/response/ApiResponseCode.java | 2 +- .../core}/common/utils/ApiKeyGenerator.java | 2 +- .../api/core}/common/utils/HashUtils.java | 2 +- .../api/core}/common/utils/PagingUtil.java | 2 +- .../api/core}/common/utils/SecurityUtil.java | 2 +- .../api/core}/common/utils/SessionUtil.java | 2 +- .../alist/api/core/config/ApiCoreConfig.java | 9 + .../alist/api/core/config/cors/CorsCache.java | 89 ++ .../core/config/cors/CorsCachePublisher.java | 19 + .../config/cors/CorsCacheRefreshEvent.java | 4 + .../cors/CorsCacheRefreshEventListener.java | 22 + .../core/config/cors/CorsCacheSubscriber.java | 19 + .../api/core/config/cors/CorsRedisConfig.java | 21 + .../datasource}/AlistDataSourceConfig.java | 14 +- .../datasource}/EltownDataSourceConfig.java | 14 +- .../datasource}/MainDataSourceConfig.java | 21 +- .../core/config/file}/FileUploadConfig.java | 5 +- .../config/file/FileUploadProperties.java | 38 + .../core/config/filter/DynamicCorsFilter.java | 44 + .../dto/FileDetailTusFileAuthDto.java | 14 + .../dto/FileDetailTusFileListDto.java | 11 + .../dto/FileDetailTusFileViewDto.java | 11 + .../mapper/FileDetailTusFileMapper.java | 27 + .../service/FileDetailTusFileService.java | 56 ++ .../vo/FileDetailTusFileListItemVo.java | 15 + .../vo/FileDetailTusFileViewVo.java | 25 + .../dto/FileMasterTusFileModifyDto.java | 10 + .../mapper/FileMasterTusFileMapper.java | 9 + .../service/FileMasterTusFileService.java | 20 + .../notice/dto/NoticeNoticeListDto.java | 6 +- .../notice/dto/NoticeNoticeViewDto.java | 10 + .../notice/mapper/NoticeNoticeMapper.java | 23 + .../notice/service/NoticeNoticeService.java | 46 ++ .../vo/NoticeNoticeCampusListItemVo.java | 11 + .../notice/vo/NoticeNoticeFileListItemVo.java | 19 + .../notice/vo/NoticeNoticeListItemVo.java | 21 + .../bespoke/notice/vo/NoticeNoticeViewVo.java | 24 + .../dto/TestUserAdminMemberListDto.java | 15 + .../dto/TestUserAdminMemberViewDto.java | 10 + .../testUser/dto/TestUserLoginViewDto.java | 11 + .../testUser/dto/TestUserSsoLoginViewDto.java | 10 + .../dto/TestUserUserPasswordViewDto.java | 10 + .../dto/TestUserUserProfileViewDto.java | 10 + .../mapper/TestUserAdminMemberMapper.java | 18 + .../testUser/mapper/TestUserAuthMapper.java | 10 + .../testUser/mapper/TestUserSsoMapper.java | 10 + .../testUser/mapper/TestUserUserMapper.java | 14 + .../service/TestUserAdminMemberService.java | 34 + .../testUser/service/TestUserAuthService.java | 21 + .../testUser/service/TestUserSsoService.java | 21 + .../testUser/service/TestUserUserService.java | 28 + .../vo/TestUserAdminMemberListItemVo.java | 24 + .../vo/TestUserAdminMemberViewVo.java | 4 + .../testUser/vo/TestUserLoginViewVo.java | 6 +- .../testUser/vo/TestUserSsoLoginViewVo.java | 15 + .../vo/TestUserUserPasswordViewVo.java | 11 + .../vo/TestUserUserProfileViewVo.java | 15 + .../dto/TestUserTokenApiKeyLoginViewDto.java | 14 + .../dto/TestUserTokenLoginCheckedViewDto.java | 11 + .../dto/TestUserTokenRefreshViewDto.java | 14 + .../mapper/TestUserTokenAuthMapper.java | 18 + .../service/TestUserTokenAuthService.java | 35 + .../vo/TestUserTokenApiKeyLoginViewVo.java | 13 +- .../vo/TestUserTokenLoginCheckedViewVo.java | 14 + .../vo/TestUserTokenRefreshViewVo.java | 15 +- .../alist/user/dto/AlistUserMigrationDto.java | 4 +- .../user/mapper/AlistUserMigrationMapper.java | 12 + .../service/AlistUserMigrationService.java | 27 + .../alist/user/vo/AlistUserMigrationVo.java | 15 + .../user/dto/EltownUserMigrationDto.java | 4 +- .../mapper/EltownUserMigrationMapper.java | 12 + .../service/EltownUserMigrationService.java | 30 + .../eltown/user/vo/EltownUserMigrationVo.java | 15 + .../alCalendar/dto/AlCalendarAddDto.java | 20 + .../alCalendar/dto/AlCalendarDeleteDto.java | 10 + .../alCalendar/dto/AlCalendarListDto.java | 14 + .../alCalendar/dto/AlCalendarModifyDto.java | 31 + .../alCalendar/dto/AlCalendarViewDto.java | 10 + .../alCalendar/mapper/AlCalendarMapper.java | 27 + .../alCalendar/service/AlCalendarService.java | 82 ++ .../alCalendar/vo/AlCalendarAddVo.java | 14 + .../alCalendar/vo/AlCalendarDeleteVo.java | 14 + .../alCalendar/vo/AlCalendarListItemVo.java | 20 + .../alCalendar/vo/AlCalendarListVo.java | 13 + .../alCalendar/vo/AlCalendarModifyVo.java | 14 + .../alCalendar/vo/AlCalendarViewVo.java | 20 + .../fileDetail/dto/FileDetailAddDto.java | 35 + .../fileDetail/dto/FileDetailDeleteDto.java | 11 + .../fileDetail/dto/FileDetailListDto.java | 16 + .../fileDetail/dto/FileDetailModifyDto.java | 54 ++ .../fileDetail/dto/FileDetailViewDto.java | 10 + .../fileDetail/mapper/FileDetailMapper.java | 17 + .../fileDetail/service/FileDetailService.java | 75 ++ .../fileDetail/vo/FileDetailAddVo.java | 14 + .../fileDetail/vo/FileDetailDeleteVo.java | 14 + .../fileDetail/vo/FileDetailListItemVo.java | 26 + .../fileDetail/vo/FileDetailListVo.java | 13 + .../fileDetail/vo/FileDetailModifyVo.java | 14 + .../fileDetail/vo/FileDetailViewVo.java | 37 + .../dto/FileDownloadEventLogAddDto.java | 20 + .../dto/FileDownloadEventLogDeleteDto.java | 10 + .../dto/FileDownloadEventLogListDto.java | 17 + .../dto/FileDownloadEventLogModifyDto.java | 33 + .../dto/FileDownloadEventLogViewDto.java | 10 + .../mapper/FileDownloadEventLogMapper.java | 27 + .../service/FileDownloadEventLogService.java | 82 ++ .../vo/FileDownloadEventLogAddVo.java | 14 + .../vo/FileDownloadEventLogDeleteVo.java | 14 + .../vo/FileDownloadEventLogListItemVo.java | 20 + .../vo/FileDownloadEventLogListVo.java | 13 + .../vo/FileDownloadEventLogModifyVo.java | 14 + .../vo/FileDownloadEventLogViewVo.java | 23 + .../fileMaster/dto/FileMasterAddDto.java | 19 + .../fileMaster/dto/FileMasterDeleteDto.java | 11 + .../fileMaster/dto/FileMasterListDto.java | 14 + .../fileMaster/dto/FileMasterModifyDto.java | 26 + .../fileMaster/dto/FileMasterViewDto.java | 10 + .../fileMaster/mapper/FileMasterMapper.java | 27 + .../fileMaster/service/FileMasterService.java | 84 ++ .../fileMaster/vo/FileMasterAddVo.java | 15 + .../fileMaster/vo/FileMasterDeleteVo.java | 15 + .../fileMaster/vo/FileMasterListItemVo.java | 21 + .../fileMaster/vo/FileMasterListVo.java | 13 + .../fileMaster/vo/FileMasterModifyVo.java | 15 + .../fileMaster/vo/FileMasterViewVo.java | 23 + .../dto/FileUploadEventLogAddDto.java | 17 + .../dto/FileUploadEventLogDeleteDto.java | 10 + .../dto/FileUploadEventLogListDto.java | 14 + .../dto/FileUploadEventLogModifyDto.java | 27 + .../dto/FileUploadEventLogViewDto.java | 10 + .../mapper/FileUploadEventLogMapper.java | 27 + .../service/FileUploadEventLogService.java | 82 ++ .../vo/FileUploadEventLogAddVo.java | 14 + .../vo/FileUploadEventLogDeleteVo.java | 14 + .../vo/FileUploadEventLogListItemVo.java | 19 + .../vo/FileUploadEventLogListVo.java | 13 + .../vo/FileUploadEventLogModifyVo.java | 14 + .../vo/FileUploadEventLogViewVo.java | 20 + .../standard/notice/dto/NoticeAddDto.java | 23 + .../standard/notice/dto/NoticeDeleteDto.java | 11 + .../standard/notice/dto/NoticeListDto.java | 16 + .../standard/notice/dto/NoticeModifyDto.java | 34 + .../standard/notice/dto/NoticeViewDto.java | 10 + .../standard/notice/mapper/NoticeMapper.java | 27 + .../notice/service/NoticeService.java | 82 ++ .../standard/notice/vo/NoticeAddVo.java | 14 + .../standard/notice/vo/NoticeDeleteVo.java | 14 + .../standard/notice/vo/NoticeListItemVo.java | 22 + .../standard/notice/vo/NoticeListVo.java | 13 + .../standard/notice/vo/NoticeModifyVo.java | 14 + .../standard/notice/vo/NoticeViewVo.java | 25 + .../noticeCampus/dto/NoticeCampusAddDto.java | 13 + .../dto/NoticeCampusDeleteDto.java | 10 + .../noticeCampus/dto/NoticeCampusListDto.java | 13 + .../dto/NoticeCampusModifyDto.java | 16 + .../noticeCampus/dto/NoticeCampusViewDto.java | 10 + .../mapper/NoticeCampusMapper.java | 27 + .../service/NoticeCampusService.java | 82 ++ .../noticeCampus/vo/NoticeCampusAddVo.java | 14 + .../noticeCampus/vo/NoticeCampusDeleteVo.java | 14 + .../vo/NoticeCampusListItemVo.java | 16 + .../noticeCampus/vo/NoticeCampusListVo.java | 13 + .../noticeCampus/vo/NoticeCampusModifyVo.java | 14 + .../noticeCampus/vo/NoticeCampusViewVo.java | 16 + .../noticeFile/dto/NoticeFileAddDto.java | 19 + .../noticeFile/dto/NoticeFileDeleteDto.java | 11 + .../noticeFile/dto/NoticeFileListDto.java | 13 + .../noticeFile/dto/NoticeFileModifyDto.java | 28 + .../noticeFile/dto/NoticeFileViewDto.java | 10 + .../noticeFile/mapper/NoticeFileMapper.java | 27 + .../noticeFile/service/NoticeFileService.java | 82 ++ .../noticeFile/vo/NoticeFileAddVo.java | 14 + .../noticeFile/vo/NoticeFileDeleteVo.java | 14 + .../noticeFile/vo/NoticeFileListItemVo.java | 7 +- .../noticeFile/vo/NoticeFileListVo.java | 13 + .../noticeFile/vo/NoticeFileModifyVo.java | 14 + .../noticeFile/vo/NoticeFileViewVo.java | 23 + .../scheduleJob/dto/ScheduleJobAddDto.java | 4 +- .../scheduleJob/dto/ScheduleJobDeleteDto.java | 4 +- .../scheduleJob/dto/ScheduleJobListDto.java | 16 + .../scheduleJob/dto/ScheduleJobModifyDto.java | 46 ++ .../scheduleJob/dto/ScheduleJobViewDto.java | 10 + .../scheduleJob/mapper/ScheduleJobMapper.java | 31 + .../service/ScheduleJobService.java | 94 +++ .../scheduleJob/vo/ScheduleJobAddVo.java | 15 + .../scheduleJob/vo/ScheduleJobDeleteVo.java | 15 + .../scheduleJob/vo/ScheduleJobListItemVo.java | 28 + .../scheduleJob/vo/ScheduleJobListVo.java | 13 + .../scheduleJob/vo/ScheduleJobModifyVo.java | 15 + .../scheduleJob/vo/ScheduleJobViewVo.java | 30 + .../dto/ScheduleJobLogAddDto.java | 23 + .../dto/ScheduleJobLogDeleteDto.java | 10 + .../dto/ScheduleJobLogListDto.java | 17 + .../dto/ScheduleJobLogModifyDto.java | 31 + .../dto/ScheduleJobLogViewDto.java | 10 + .../mapper/ScheduleJobLogMapper.java | 27 + .../service/ScheduleJobLogService.java | 84 ++ .../vo/ScheduleJobLogAddVo.java | 15 + .../vo/ScheduleJobLogDeleteVo.java | 15 + .../vo/ScheduleJobLogListItemVo.java | 25 + .../vo/ScheduleJobLogListVo.java | 13 + .../vo/ScheduleJobLogModifyVo.java | 15 + .../vo/ScheduleJobLogViewVo.java | 25 + .../ssoClient/dto/SsoClientAddDto.java | 4 +- .../dto/SsoClientAuthorizeViewDto.java | 11 + .../ssoClient/dto/SsoClientDeleteDto.java | 6 +- .../ssoClient/dto/SsoClientListDto.java | 15 + .../ssoClient/dto/SsoClientModifyDto.java | 24 + .../ssoClient/dto/SsoClientViewDto.java | 10 + .../ssoClient/mapper/SsoClientMapper.java | 28 + .../ssoClient/service/SsoClientService.java | 103 +++ .../standard/ssoClient/vo/SsoClientAddVo.java | 15 + .../vo/SsoClientAuthorizeViewVo.java | 8 +- .../ssoClient/vo/SsoClientDeleteVo.java | 15 + .../ssoClient/vo/SsoClientListItemVo.java | 9 +- .../ssoClient/vo/SsoClientListVo.java | 13 + .../ssoClient/vo/SsoClientModifyVo.java | 15 + .../ssoClient/vo/SsoClientViewVo.java | 21 + .../dto/TestCorsAllowedListAddDto.java | 12 + .../dto/TestCorsAllowedListDeleteDto.java | 10 + .../dto/TestCorsAllowedListListDto.java | 13 + .../dto/TestCorsAllowedListModifyDto.java | 16 + .../dto/TestCorsAllowedListViewDto.java | 10 + .../mapper/TestCorsAllowedListMapper.java | 38 + .../service/TestCorsAllowedListService.java | 113 +++ .../vo/TestCorsAllowedListAddVo.java | 15 + .../vo/TestCorsAllowedListDeleteVo.java | 15 + .../vo/TestCorsAllowedListListItemVo.java | 15 + .../vo/TestCorsAllowedListListVo.java | 13 + .../vo/TestCorsAllowedListModifyVo.java | 15 + .../vo/TestCorsAllowedListViewVo.java | 15 + .../standard/testUser/dto/TestUserAddDto.java | 14 + .../testUser/dto/TestUserDeleteDto.java | 10 + .../testUser/dto/TestUserListDto.java | 16 + .../testUser/dto/TestUserModifyDto.java | 33 + .../testUser/dto/TestUserViewDto.java | 10 + .../testUser/mapper/TestUserMapper.java | 29 + .../testUser/service/TestUserService.java | 98 +++ .../standard/testUser/vo/TestUserAddVo.java | 15 + .../testUser/vo/TestUserDeleteVo.java | 15 + .../testUser/vo/TestUserListItemVo.java | 22 + .../standard/testUser/vo/TestUserListVo.java | 13 + .../testUser/vo/TestUserModifyVo.java | 15 + .../standard/testUser/vo/TestUserViewVo.java | 22 + .../dto/TestUserTokenAddDto.java | 18 + .../dto/TestUserTokenDeleteDto.java | 10 + .../dto/TestUserTokenListDto.java | 15 + .../dto/TestUserTokenModifyDto.java | 27 + .../dto/TestUserTokenViewDto.java | 10 + .../mapper/TestUserTokenMapper.java | 26 + .../service/TestUserTokenService.java | 89 ++ .../testUserToken/vo/TestUserTokenAddVo.java | 15 + .../vo/TestUserTokenDeleteVo.java | 15 + .../vo/TestUserTokenListItemVo.java | 19 + .../testUserToken/vo/TestUserTokenListVo.java | 13 + .../vo/TestUserTokenModifyVo.java | 15 + .../testUserToken/vo/TestUserTokenViewVo.java | 19 + .../main/resources/log4jdbc.log4j2.properties | 0 .../fileDetail/FileDetailTusFileMapper.xml | 92 +++ .../fileMaster/FileMasterTusFileMapper.xml | 28 + .../bespoke/notice/NoticeNoticeMapper.xml | 107 +++ .../testUser/TestUserAdminMemberMapper.xml | 82 ++ .../bespoke/testUser/TestUserAuthMapper.xml | 20 + .../bespoke/testUser/TestUserSsoMapper.xml | 20 + .../bespoke/testUser/TestUserUserMapper.xml | 33 + .../testUserToken/TestUserTokenAuthMapper.xml | 70 ++ .../migration/alist/user/AlistUserMapper.xml | 6 +- .../eltown/user/EltownUserMapper.xml | 6 +- .../standard/alCalendar/AlCalendarMapper.xml | 123 +++ .../standard/fileDetail/FileDetailMapper.xml | 145 ++++ .../FileDownloadEventLogMapper.xml | 149 ++++ .../standard/fileMaster/FileMasterMapper.xml | 127 +++ .../FileUploadEventLogMapper.xml | 115 +++ .../mapper/standard/notice/NoticeMapper.xml | 131 +++ .../noticeCampus/NoticeCampusMapper.xml | 82 ++ .../standard/noticeFile/NoticeFileMapper.xml | 111 +++ .../scheduleJob/ScheduleJobMapper.xml | 197 +++++ .../scheduleJobLog/ScheduleJobLogMapper.xml | 146 ++++ .../standard/ssoClient/SsoClientMapper.xml | 156 ++++ .../TestCorsAllowedListMapper.xml | 103 +++ .../standard/testUser/TestUserMapper.xml | 151 ++++ .../testUserToken/TestUserTokenMapper.xml | 122 +++ 02-api-admin/Dockerfile | 13 + 02-api-admin/build.gradle | 22 + .../alist/api/admin/ApiAdminApplication.java | 15 + .../admin/config/AdminCorsCacheBootstrap.java | 24 + .../api/admin}/config/OpenApiConfig.java | 43 +- .../api/admin}/config/SecurityConfig.java | 38 +- .../admin/modules/auth/AuthController.java | 142 ++++ .../auth/form/AuthApiKeyLoginForm.java | 16 + .../modules/auth/form/AuthApiRefreshForm.java | 19 + .../modules/auth/form/AuthLoginForm.java | 19 + .../modules/auth/service/AuthService.java | 322 ++++++++ .../modules/auth/vo/AuthApiKeyLoginVo.java | 22 + .../modules/auth/vo/AuthApiRefreshVo.java | 22 + .../modules/auth/vo/AuthLoginCheckedVo.java | 34 + .../admin/modules/auth/vo/AuthLoginVo.java | 30 + .../admin/modules/auth/vo/AuthLogoutVo.java | 13 + .../admin/modules/auth/vo/AuthRefreshVo.java | 19 + .../backOffice/AdminMemberController.java | 164 ++++ .../modules/backOffice/CorsController.java | 90 +++ .../backOffice/ScheduleController.java | 123 +++ .../modules/backOffice/SsoController.java | 142 ++++ .../backOffice/form/AdminMemberAddForm.java | 55 ++ .../form/AdminMemberDeleteForm.java | 17 + .../form/AdminMemberDormantModifyForm.java | 22 + .../backOffice/form/AdminMemberListForm.java | 26 + .../form/AdminMemberModifyForm.java | 42 + .../backOffice/form/AdminMemberViewForm.java | 15 + .../form/AdminMemberWithdrawModifyForm.java | 22 + .../modules/backOffice/form/CorsAddForm.java | 20 + .../backOffice/form/CorsDeleteForm.java | 15 + .../modules/backOffice/form/CorsListForm.java | 17 + .../backOffice/form/ScheduleAddForm.java | 22 + .../backOffice/form/ScheduleDeleteForm.java | 13 + .../backOffice/form/ScheduleListForm.java | 16 + .../backOffice/form/ScheduleLogListForm.java | 16 + .../backOffice/form/ScheduleModifyForm.java | 24 + .../backOffice/form/ScheduleViewForm.java | 14 + .../modules/backOffice/form/SsoAddForm.java | 35 +- .../backOffice/form/SsoDeleteForm.java | 15 + .../modules/backOffice/form/SsoListForm.java | 17 + .../backOffice/form/SsoModifyForm.java | 38 +- .../modules/backOffice/form/SsoViewForm.java | 15 + .../service/AdminMemberService.java | 310 +++++++ .../backOffice/service/CorsService.java | 142 ++++ .../backOffice/service/ScheduleService.java | 281 +++++++ .../backOffice/service/SsoService.java | 211 +++++ .../backOffice/vo/AdminMemberAddVo.java | 28 + .../backOffice/vo/AdminMemberDeleteVo.java | 24 + .../vo/AdminMemberDormantModifyVo.java | 20 + .../backOffice/vo/AdminMemberListItemVo.java | 51 ++ .../backOffice/vo/AdminMemberListVo.java | 16 + .../backOffice/vo/AdminMemberModifyVo.java | 20 + .../backOffice/vo/AdminMemberViewVo.java | 51 ++ .../vo/AdminMemberWithdrawModifyVo.java | 20 + .../modules/backOffice/vo/CorsAddVo.java | 21 + .../modules/backOffice/vo/CorsDeleteVo.java | 21 + .../modules/backOffice/vo/CorsListItemVo.java | 24 + .../modules/backOffice/vo/CorsListVo.java | 16 + .../modules/backOffice/vo/CorsReloadVo.java | 23 + .../modules/backOffice/vo/ScheduleAddVo.java | 15 + .../backOffice/vo/ScheduleDeleteVo.java | 15 + .../backOffice/vo/ScheduleListItemVo.java | 30 + .../modules/backOffice/vo/ScheduleListVo.java | 14 + .../backOffice/vo/ScheduleLogListItemVo.java | 26 + .../backOffice/vo/ScheduleLogListVo.java | 14 + .../backOffice/vo/ScheduleModifyVo.java | 15 + .../modules/backOffice/vo/ScheduleViewVo.java | 31 + .../admin/modules/backOffice/vo/SsoAddVo.java | 12 +- .../modules/backOffice/vo/SsoDeleteVo.java | 19 + .../modules/backOffice/vo/SsoListItemVo.java | 23 +- .../modules/backOffice/vo/SsoListVo.java | 16 + .../modules/backOffice/vo/SsoModifyVo.java | 19 + .../modules/backOffice/vo/SsoViewVo.java | 30 +- .../admin/modules/file/FileController.java | 78 ++ .../file/form/SunEditorUploadForm.java | 17 + .../modules/file/service/FileService.java | 58 ++ .../admin/modules/file/vo/FileUploadVo.java | 34 + .../modules/file/vo/SunEditorUploadVo.java | 39 + .../admin}/modules/main/MainController.java | 2 +- .../modules/support/NoticeController.java | 114 +++ .../modules/support/form/NoticeAddForm.java | 56 ++ .../support/form/NoticeDeleteForm.java | 15 + .../modules/support/form/NoticeFileForm.java | 26 +- .../modules/support/form/NoticeListForm.java | 29 + .../support/form/NoticeModifyForm.java | 20 + .../modules/support/form/NoticeViewForm.java | 15 + .../support/service/NoticeService.java | 373 +++++++++ .../admin/modules/support/vo/NoticeAddVo.java | 10 +- .../modules/support/vo/NoticeCampusVo.java | 13 + .../modules/support/vo/NoticeDeleteVo.java | 21 + .../modules/support/vo/NoticeFileVo.java | 13 +- .../modules/support/vo/NoticeListItemVo.java | 14 +- .../modules/support/vo/NoticeListVo.java | 16 + .../modules/support/vo/NoticeModifyVo.java | 21 + .../modules/support/vo/NoticeViewVo.java | 37 +- .../admin/modules/tus/TusFileController.java | 127 +++ .../modules/tus/form/TusFileDeleteForm.java | 15 + .../modules/tus/form/TusFileHookForm.java | 47 ++ .../tus/form/TusFileUploadCancelForm.java | 15 + .../tus/form/TusFileUploadInitForm.java | 46 ++ .../tus/form/TusFileUploadStatusForm.java | 15 + .../modules/tus/service/TusFileService.java | 586 ++++++++++++++ .../modules/tus}/vo/TusFileDeleteVo.java | 6 +- .../admin/modules/tus/vo/TusFileListVo.java | 33 + .../tus}/vo/TusFileUploadCancelVo.java | 7 +- .../modules/tus/vo/TusFileUploadInitVo.java | 38 + .../modules/tus/vo/TusFileUploadStatusVo.java | 23 + .../admin/modules/user/UserController.java | 44 + .../admin/modules/user/form/UserAddForm.java | 26 + .../modules/user/service/UserService.java | 79 ++ .../api/admin/modules/user/vo/UserAddVo.java | 30 + .../src/main/resources/application-local.yaml | 77 ++ .../src/main/resources/application-pjt.yaml | 79 ++ .../src}/main/resources/banner.txt | 0 .../src}/main/resources/logback-local.xml | 2 +- .../src}/main/resources/logback-pjt.xml | 9 +- .../src}/main/resources/robots.txt | 0 03-api-front/Dockerfile | 13 + 03-api-front/build.gradle | 22 + .../alist/api/front/ApiFrontApplication.java | 15 + .../alist/api/front/config/OpenApiConfig.java | 37 + .../api/front/config/SecurityConfig.java | 97 +++ .../front/modules/auth/AuthController.java | 87 ++ .../auth/form/AuthApiKeyLoginForm.java | 15 + .../modules/auth/form/AuthTokenForm.java | 15 + .../modules/auth/service/AuthService.java | 208 +++++ .../front/modules}/auth/vo/AuthAccessVo.java | 13 +- .../modules}/auth/vo/AuthApiKeyLoginVo.java | 10 +- .../front/modules}/auth/vo/AuthRefreshVo.java | 8 +- .../front/modules/auth/vo/AuthTokenVo.java | 13 + .../front/modules/file/FileController.java | 78 ++ .../file/form/SunEditorUploadForm.java | 17 + .../modules/file/service/FileService.java | 58 ++ .../front/modules/file/vo/FileUploadVo.java | 34 + .../modules/file/vo/SunEditorUploadVo.java | 39 + .../front/modules/main/MainController.java | 15 + .../api/front/modules/sso/SsoController.java | 106 +++ .../modules/sso/form/SsoExchangeForm.java | 19 + .../front/modules/sso/form/SsoLoginForm.java | 19 + .../front/modules/sso/service/SsoService.java | 311 +++++++ .../front/modules/sso/vo/SsoExchangeVo.java | 15 +- .../modules/sso}/vo/SsoLoginCheckedVo.java | 10 +- .../api/front/modules/sso}/vo/SsoLoginVo.java | 22 +- .../front/modules/sso}/vo/SsoLogoutVo.java | 6 +- .../front/modules/user/UserController.java | 85 ++ .../front/modules/user/form/UserAddForm.java | 22 + .../user/form/UserDormantModifyForm.java | 12 + .../user/form/UserMigrationListForm.java | 12 + .../user/form/UserPasswordCheckForm.java | 11 + .../user/form/UserPasswordModifyForm.java | 14 + .../user/form/UserProfileModifyForm.java | 14 + .../user/form/UserWithdrawModifyForm.java | 12 + .../modules/user/service/UserService.java | 177 ++++ .../api/front/modules/user/vo/UserAddVo.java | 15 + .../modules/user/vo/UserDormantModifyVo.java | 12 + .../modules/user/vo/UserMigrationItemVo.java | 15 + .../modules/user/vo/UserMigrationListVo.java | 13 + .../modules/user/vo/UserPasswordCheckVo.java | 12 + .../modules/user/vo/UserPasswordModifyVo.java | 12 + .../modules/user/vo/UserProfileModifyVo.java | 12 + .../modules/user/vo/UserProfileViewVo.java | 15 + .../modules/user/vo/UserWithdrawModifyVo.java | 12 + .../main/resources/application-local.yaml | 0 .../src}/main/resources/application-pjt.yaml | 0 .../src/main/resources/application.yaml | 47 ++ 03-api-front/src/main/resources/banner.txt | 18 + .../src/main/resources/logback-local.xml | 42 + .../src/main/resources/logback-pjt.xml | 57 ++ 03-api-front/src/main/resources/robots.txt | 2 + AGENTS.md | 14 +- Dockerfile | 14 - README.md | 398 ++------- build.gradle | 85 +- ...pose.pjt.yml => api-admin-compose.pjt.yml} | 12 +- deploy/pjt/compose/api-front-compose.pjt.yml | 40 + docs/agent-workflow.md | 52 ++ docs/architecture-at-a-glance.md | 264 ++++++ docs/code-conventions.md | 77 +- docs/codebase-notes.md | 54 +- docs/core-module-design.md | 280 +++++++ docs/project-overview.md | 79 +- docs/runtime-config.md | 107 ++- docs/security-and-response.md | 71 +- docs/testing-strategy.md | 89 ++ docs/verification-checklist.md | 66 +- settings.gradle | 6 +- .../java/com/alist/api/ApiApplication.java | 15 - .../com/alist/api/config/cache/CorsCache.java | 75 -- .../api/config/filter/DynamicCorsFilter.java | 68 -- .../config/jwt/JwtAuthenticationFilter.java | 108 --- .../api/config/properties/JwtProperties.java | 14 - .../admin/auth/AdminAuthController.java | 145 ---- .../auth/dto/AdminAuthApiKeyLoginDto.java | 16 - .../admin/auth/dto/AdminAuthLoginDto.java | 18 - .../auth/form/AdminAuthApiKeyLoginForm.java | 26 - .../admin/auth/form/AdminAuthLoginForm.java | 36 - .../admin/auth/mapper/AdminAuthMapper.java | 24 - .../admin/auth/service/AdminAuthService.java | 193 ----- .../admin/auth/vo/AdminAuthApiKeyLoginVo.java | 40 - .../auth/vo/AdminAuthLoginCheckedVo.java | 34 - .../admin/auth/vo/AdminAuthLoginVo.java | 42 - .../admin/auth/vo/AdminAuthLogoutVo.java | 13 - .../admin/auth/vo/AdminAuthRefreshVo.java | 13 - .../admin/member/AdminMemberController.java | 144 ---- .../member/dto/AdminMemberDeleteDto.java | 12 - .../dto/AdminMemberDormantModifyDto.java | 11 - .../admin/member/dto/AdminMemberListDto.java | 15 - .../member/dto/AdminMemberModifyDto.java | 14 - .../dto/AdminMemberWithdrawModifyDto.java | 11 - .../admin/member/form/AdminMemberAddForm.java | 95 --- .../member/form/AdminMemberDeleteForm.java | 32 - .../form/AdminMemberDormantModifyForm.java | 30 - .../member/form/AdminMemberListForm.java | 39 - .../member/form/AdminMemberModifyForm.java | 40 - .../form/AdminMemberWithdrawModifyForm.java | 30 - .../member/mapper/AdminMemberMapper.java | 25 - .../member/service/AdminMemberService.java | 100 --- .../admin/member/vo/AdminMemberAddVo.java | 28 - .../admin/member/vo/AdminMemberDeleteVo.java | 19 - .../member/vo/AdminMemberDormantModifyVo.java | 13 - .../admin/member/vo/AdminMemberListVo.java | 16 - .../admin/member/vo/AdminMemberModifyVo.java | 13 - .../admin/member/vo/AdminMemberViewVo.java | 51 -- .../admin/member/vo/AdminMemberVo.java | 51 -- .../vo/AdminMemberWithdrawModifyVo.java | 13 - .../admin/notice/AdminNoticeController.java | 139 ---- .../admin/notice/dto/AdminNoticeAddDto.java | 24 - .../notice/dto/AdminNoticeCampusDto.java | 12 - .../notice/dto/AdminNoticeDeleteDto.java | 11 - .../notice/dto/AdminNoticeModifyDto.java | 24 - .../admin/notice/form/AdminNoticeAddForm.java | 72 -- .../notice/form/AdminNoticeDeleteForm.java | 22 - .../notice/form/AdminNoticeListForm.java | 43 - .../notice/form/AdminNoticeModifyForm.java | 82 -- .../notice/mapper/AdminNoticeMapper.java | 48 -- .../notice/service/AdminNoticeService.java | 213 ----- .../admin/notice/vo/AdminNoticeDeleteVo.java | 18 - .../admin/notice/vo/AdminNoticeListVo.java | 16 - .../admin/notice/vo/AdminNoticeModifyVo.java | 18 - .../schedule/AdminScheduleController.java | 143 ---- .../schedule/dto/AdminScheduleListDto.java | 14 - .../schedule/dto/AdminScheduleLogListDto.java | 14 - .../schedule/dto/AdminScheduleModifyDto.java | 18 - .../schedule/form/AdminScheduleAddForm.java | 57 -- .../form/AdminScheduleDeleteForm.java | 22 - .../schedule/form/AdminScheduleListForm.java | 35 - .../form/AdminScheduleLogListForm.java | 36 - .../form/AdminScheduleModifyForm.java | 59 -- .../schedule/mapper/AdminScheduleMapper.java | 32 - .../service/AdminScheduleService.java | 160 ---- .../admin/schedule/vo/AdminScheduleAddVo.java | 21 - .../schedule/vo/AdminScheduleDeleteVo.java | 18 - .../schedule/vo/AdminScheduleListVo.java | 16 - .../schedule/vo/AdminScheduleLogListVo.java | 16 - .../admin/schedule/vo/AdminScheduleLogVo.java | 54 -- .../schedule/vo/AdminScheduleModifyVo.java | 18 - .../schedule/vo/AdminScheduleViewVo.java | 72 -- .../admin/schedule/vo/AdminScheduleVo.java | 63 -- .../modules/admin/sso/AdminSsoController.java | 127 --- .../admin/sso/dto/AdminSsoListDto.java | 12 - .../admin/sso/form/AdminSsoDeleteForm.java | 22 - .../admin/sso/form/AdminSsoListForm.java | 27 - .../admin/sso/mapper/AdminSsoMapper.java | 30 - .../admin/sso/service/AdminSsoService.java | 151 ---- .../admin/sso/vo/AdminSsoDeleteVo.java | 18 - .../modules/admin/sso/vo/AdminSsoListVo.java | 16 - .../admin/sso/vo/AdminSsoModifyVo.java | 18 - .../admin/user/AdminUserController.java | 44 - .../admin/user/dto/AdminUserAddDto.java | 27 - .../admin/user/form/AdminUserAddForm.java | 61 -- .../admin/user/service/AdminUserService.java | 42 - .../modules/admin/user/vo/AdminUserAddVo.java | 35 - .../api/modules/cors/CorsController.java | 96 --- .../api/modules/cors/dto/CorsAddDto.java | 10 - .../api/modules/cors/dto/CorsDeleteDto.java | 10 - .../api/modules/cors/dto/CorsListDto.java | 11 - .../api/modules/cors/form/CorsAddForm.java | 28 - .../api/modules/cors/form/CorsDeleteForm.java | 23 - .../api/modules/cors/form/CorsListForm.java | 24 - .../api/modules/cors/mapper/CorsMapper.java | 25 - .../api/modules/cors/service/CorsService.java | 62 -- .../alist/api/modules/cors/vo/CorsAddVo.java | 13 - .../api/modules/cors/vo/CorsCacheVo.java | 13 - .../api/modules/cors/vo/CorsDeleteVo.java | 13 - .../alist/api/modules/cors/vo/CorsListVo.java | 16 - .../com/alist/api/modules/cors/vo/CorsVo.java | 16 - .../api/modules/file/FileController.java | 108 --- .../file/properties/FileUploadProperties.java | 45 -- .../api/modules/file/service/FileService.java | 244 ------ .../file/service/FileUploadImageService.java | 138 ---- .../api/modules/file/vo/FileUploadVo.java | 34 - .../modules/file/vo/SunEditorUploadVo.java | 39 - .../modules/front/auth/AuthController.java | 145 ---- .../api/modules/front/auth/SsoController.java | 172 ---- .../modules/front/auth/dto/AuthTokenDto.java | 24 - .../api/modules/front/auth/dto/LoginDto.java | 52 -- .../modules/front/auth/dto/SsoLoginDto.java | 26 - .../front/auth/form/AuthApiKeyLoginForm.java | 27 - .../front/auth/form/AuthTokenForm.java | 24 - .../front/auth/form/SsoExchangeForm.java | 35 - .../modules/front/auth/form/SsoLoginForm.java | 35 - .../modules/front/auth/mapper/AuthMapper.java | 23 - .../modules/front/auth/mapper/SsoMapper.java | 9 - .../front/auth/service/AuthService.java | 174 ---- .../front/auth/service/SsoService.java | 223 ----- .../modules/front/auth/vo/AuthTokenVo.java | 24 - .../modules/front/auth/vo/LoginTokenVo.java | 12 - .../modules/front/auth/vo/SsoExchangeVo.java | 34 - .../modules/front/user/UserController.java | 183 ----- .../front/user/dto/UserDormantModifyDto.java | 11 - .../api/modules/front/user/dto/UserDto.java | 27 - .../front/user/dto/UserPasswordCheckDto.java | 11 - .../front/user/dto/UserPasswordModifyDto.java | 12 - .../front/user/dto/UserProfileModifyDto.java | 13 - .../front/user/dto/UserWithdrawModifyDto.java | 11 - .../modules/front/user/form/UserAddForm.java | 64 -- .../user/form/UserDormantModifyForm.java | 28 - .../user/form/UserMigrationListForm.java | 29 - .../user/form/UserPasswordCheckForm.java | 26 - .../user/form/UserPasswordModifyForm.java | 35 - .../user/form/UserProfileModifyForm.java | 36 - .../user/form/UserWithdrawModifyForm.java | 28 - .../modules/front/user/mapper/UserMapper.java | 27 - .../front/user/service/UserService.java | 299 ------- .../front/user/vo/UserDormantModifyVo.java | 18 - .../front/user/vo/UserMigrationListVo.java | 20 - .../front/user/vo/UserPasswordCheckVo.java | 18 - .../front/user/vo/UserPasswordModifyVo.java | 18 - .../front/user/vo/UserProfileModifyVo.java | 18 - .../front/user/vo/UserProfileViewVo.java | 28 - .../api/modules/front/user/vo/UserVo.java | 33 - .../front/user/vo/UserWithdrawModifyVo.java | 18 - .../alist/user/mapper/AlistUserMapper.java | 10 - .../alist/user/service/AlistUserService.java | 21 - .../migration/alist/user/vo/AlistUserVo.java | 28 - .../eltown/user/mapper/EltownUserMapper.java | 10 - .../user/service/EltownUserService.java | 25 - .../eltown/user/vo/EltownUserVo.java | 28 - .../modules/tusFile/TusFileController.java | 291 ------- .../modules/tusFile/dto/TusFileDeleteDto.java | 16 - .../modules/tusFile/dto/TusFileHookDto.java | 18 - .../modules/tusFile/dto/TusFileListDto.java | 16 - .../tusFile/dto/TusFileMoveTaskDto.java | 28 - .../tusFile/dto/TusFileUploadAuthDto.java | 30 - .../tusFile/dto/TusFileUploadCancelDto.java | 14 - .../tusFile/dto/TusFileUploadInitDto.java | 46 -- .../tusFile/dto/TusFileUploadInitItemDto.java | 39 - .../tusFile/dto/TusFileUploadMetaDto.java | 15 - .../tusFile/dto/TusFileUploadStatusDto.java | 10 - .../tusFile/dto/TusFileViewOrDownloadDto.java | 28 - .../tusFile/form/TusFileDeleteForm.java | 26 - .../tusFile/form/TusFileHookEventForm.java | 17 - .../modules/tusFile/form/TusFileHookForm.java | 45 -- .../tusFile/form/TusFileHookUploadForm.java | 34 - .../tusFile/form/TusFileUploadCancelForm.java | 26 - .../tusFile/form/TusFileUploadInitForm.java | 62 -- .../form/TusFileUploadInitItemForm.java | 33 - .../tusFile/form/TusFileUploadStatusForm.java | 26 - .../modules/tusFile/mapper/TusFileMapper.java | 56 -- .../tusFile/service/TusFileService.java | 760 ------------------ .../modules/tusFile/vo/TusFileListItemVo.java | 34 - .../api/modules/tusFile/vo/TusFileListVo.java | 23 - .../tusFile/vo/TusFileUploadAuthVo.java | 15 - .../vo/TusFileUploadCancelProgressVo.java | 12 - .../tusFile/vo/TusFileUploadInitItemVo.java | 28 - .../tusFile/vo/TusFileUploadInitVo.java | 21 - .../tusFile/vo/TusFileUploadStatusVo.java | 28 - .../tusFile/vo/TusFileViewOrDownloadVo.java | 49 -- .../mapper/admin/auth/AdminAuthMapper.xml | 89 -- .../mapper/admin/member/AdminMemberMapper.xml | 141 ---- .../mapper/admin/notice/AdminNoticeMapper.xml | 229 ------ .../admin/schedule/AdminScheduleMapper.xml | 196 ----- .../mapper/admin/sso/AdminSsoMapper.xml | 123 --- src/main/resources/mapper/cors/CorsMapper.xml | 65 -- .../mapper/front/User/UserMapper.xml | 109 --- .../mapper/front/auth/LoginMapper.xml | 73 -- .../resources/mapper/front/auth/SsoMapper.xml | 18 - .../mapper/tusFile/TusFileMapper.xml | 356 -------- .../com/alist/api/ApiApplicationTests.java | 15 - 673 files changed, 16782 insertions(+), 11277 deletions(-) create mode 100644 01-api-core/build.gradle rename {src/main/java/com/alist/api/config => 01-api-core/src/main/java/com/alist/api/core/common}/exception/GlobalExceptionHandler.java (96%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/common/file/service/FileImageService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/common/file/service/FileStorageService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/common/file/vo/FileStorageUploadVo.java rename {src/main/java/com/alist/api/config => 01-api-core/src/main/java/com/alist/api/core/common}/jwt/JwtAccessDeniedHandler.java (85%) rename {src/main/java/com/alist/api/config => 01-api-core/src/main/java/com/alist/api/core/common}/jwt/JwtAuthenticationEntryPoint.java (85%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtAuthenticationFilter.java rename {src/main/java/com/alist/api/config => 01-api-core/src/main/java/com/alist/api/core/common}/jwt/JwtTokenProvider.java (80%) rename src/main/java/com/alist/api/common/paging/PageRequest.java => 01-api-core/src/main/java/com/alist/api/core/common/paging/PagingRequest.java (84%) rename src/main/java/com/alist/api/common/paging/PageResponse.java => 01-api-core/src/main/java/com/alist/api/core/common/paging/PagingResponse.java (70%) rename {src/main/java/com/alist/api => 01-api-core/src/main/java/com/alist/api/core}/common/response/ApiResponse.java (96%) rename {src/main/java/com/alist/api => 01-api-core/src/main/java/com/alist/api/core}/common/response/ApiResponseCode.java (97%) rename {src/main/java/com/alist/api => 01-api-core/src/main/java/com/alist/api/core}/common/utils/ApiKeyGenerator.java (91%) rename {src/main/java/com/alist/api => 01-api-core/src/main/java/com/alist/api/core}/common/utils/HashUtils.java (94%) rename {src/main/java/com/alist/api => 01-api-core/src/main/java/com/alist/api/core}/common/utils/PagingUtil.java (96%) rename {src/main/java/com/alist/api => 01-api-core/src/main/java/com/alist/api/core}/common/utils/SecurityUtil.java (92%) rename {src/main/java/com/alist/api => 01-api-core/src/main/java/com/alist/api/core}/common/utils/SessionUtil.java (97%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/config/ApiCoreConfig.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCache.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCachePublisher.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheRefreshEvent.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheRefreshEventListener.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheSubscriber.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/config/cors/CorsRedisConfig.java rename {src/main/java/com/alist/api/config/migration => 01-api-core/src/main/java/com/alist/api/core/config/datasource}/AlistDataSourceConfig.java (83%) rename {src/main/java/com/alist/api/config/migration => 01-api-core/src/main/java/com/alist/api/core/config/datasource}/EltownDataSourceConfig.java (83%) rename {src/main/java/com/alist/api/config => 01-api-core/src/main/java/com/alist/api/core/config/datasource}/MainDataSourceConfig.java (86%) rename {src/main/java/com/alist/api/modules/file/config => 01-api-core/src/main/java/com/alist/api/core/config/file}/FileUploadConfig.java (69%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/config/file/FileUploadProperties.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/config/filter/DynamicCorsFilter.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileAuthDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/mapper/FileDetailTusFileMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/service/FileDetailTusFileService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/vo/FileDetailTusFileListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/vo/FileDetailTusFileViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/dto/FileMasterTusFileModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/mapper/FileMasterTusFileMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/service/FileMasterTusFileService.java rename src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeListDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/dto/NoticeNoticeListDto.java (56%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/dto/NoticeNoticeViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/mapper/NoticeNoticeMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/service/NoticeNoticeService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeCampusListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeFileListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserAdminMemberListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserAdminMemberViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserLoginViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserSsoLoginViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserUserPasswordViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserUserProfileViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserAdminMemberMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserAuthMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserSsoMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserUserMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserAdminMemberService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserAuthService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserSsoService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserUserService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserAdminMemberListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserAdminMemberViewVo.java rename src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberAddDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserLoginViewVo.java (52%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserSsoLoginViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserUserPasswordViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserUserProfileViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenApiKeyLoginViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenLoginCheckedViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenRefreshViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/mapper/TestUserTokenAuthMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/service/TestUserTokenAuthService.java rename src/main/java/com/alist/api/modules/front/user/dto/UserTokenDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenApiKeyLoginViewVo.java (57%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenLoginCheckedViewVo.java rename src/main/java/com/alist/api/modules/front/auth/vo/LoginVo.java => 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenRefreshViewVo.java (51%) rename src/main/java/com/alist/api/modules/migration/alist/user/dto/AlistUserDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/dto/AlistUserMigrationDto.java (52%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/mapper/AlistUserMigrationMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/service/AlistUserMigrationService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/vo/AlistUserMigrationVo.java rename src/main/java/com/alist/api/modules/migration/eltown/user/dto/EltownUserDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/dto/EltownUserMigrationDto.java (58%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/mapper/EltownUserMigrationMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/service/EltownUserMigrationService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/vo/EltownUserMigrationVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/mapper/AlCalendarMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/service/AlCalendarService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/mapper/FileDetailMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/service/FileDetailService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/mapper/FileDownloadEventLogMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/service/FileDownloadEventLogService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/mapper/FileMasterMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/service/FileMasterService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/mapper/FileUploadEventLogMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/service/FileUploadEventLogService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/mapper/NoticeMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/service/NoticeService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/mapper/NoticeCampusMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/service/NoticeCampusService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/mapper/NoticeFileMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/service/NoticeFileService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileDeleteVo.java rename src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeFileDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileListItemVo.java (64%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileViewVo.java rename src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleAddDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobAddDto.java (78%) rename src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleDeleteDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobDeleteDto.java (56%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/mapper/ScheduleJobMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/service/ScheduleJobService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/mapper/ScheduleJobLogMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/service/ScheduleJobLogService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogViewVo.java rename src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoAddDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientAddDto.java (76%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientAuthorizeViewDto.java rename src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoDeleteDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientDeleteDto.java (56%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/mapper/SsoClientMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/service/SsoClientService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientAddVo.java rename src/main/java/com/alist/api/modules/front/auth/vo/SsoClientVo.java => 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientAuthorizeViewVo.java (52%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientDeleteVo.java rename src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoModifyDto.java => 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientListItemVo.java (53%) create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/mapper/TestCorsAllowedListMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/service/TestCorsAllowedListService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/mapper/TestUserMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/service/TestUserService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserViewVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenAddDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenDeleteDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenListDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenModifyDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenViewDto.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/mapper/TestUserTokenMapper.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/service/TestUserTokenService.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenAddVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenDeleteVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenListItemVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenListVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenModifyVo.java create mode 100644 01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenViewVo.java rename {src => 01-api-core/src}/main/resources/log4jdbc.log4j2.properties (100%) create mode 100644 01-api-core/src/main/resources/mapper/bespoke/fileDetail/FileDetailTusFileMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/bespoke/fileMaster/FileMasterTusFileMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/bespoke/notice/NoticeNoticeMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserAdminMemberMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserAuthMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserSsoMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserUserMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/bespoke/testUserToken/TestUserTokenAuthMapper.xml rename {src => 01-api-core/src}/main/resources/mapper/migration/alist/user/AlistUserMapper.xml (66%) rename {src => 01-api-core/src}/main/resources/mapper/migration/eltown/user/EltownUserMapper.xml (77%) create mode 100644 01-api-core/src/main/resources/mapper/standard/alCalendar/AlCalendarMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/fileDetail/FileDetailMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/fileDownloadEventLog/FileDownloadEventLogMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/fileMaster/FileMasterMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/fileUploadEventLog/FileUploadEventLogMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/notice/NoticeMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/noticeCampus/NoticeCampusMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/noticeFile/NoticeFileMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/scheduleJob/ScheduleJobMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/scheduleJobLog/ScheduleJobLogMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/ssoClient/SsoClientMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/testCorsAllowedList/TestCorsAllowedListMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/testUser/TestUserMapper.xml create mode 100644 01-api-core/src/main/resources/mapper/standard/testUserToken/TestUserTokenMapper.xml create mode 100644 02-api-admin/Dockerfile create mode 100644 02-api-admin/build.gradle create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/ApiAdminApplication.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/config/AdminCorsCacheBootstrap.java rename {src/main/java/com/alist/api => 02-api-admin/src/main/java/com/alist/api/admin}/config/OpenApiConfig.java (56%) rename {src/main/java/com/alist/api => 02-api-admin/src/main/java/com/alist/api/admin}/config/SecurityConfig.java (75%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/AuthController.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthApiKeyLoginForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthApiRefreshForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthLoginForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/service/AuthService.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthApiKeyLoginVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthApiRefreshVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLoginCheckedVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLoginVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLogoutVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthRefreshVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/AdminMemberController.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/CorsController.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/ScheduleController.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/SsoController.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberAddForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberDeleteForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberDormantModifyForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberListForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberModifyForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberViewForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberWithdrawModifyForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsAddForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsDeleteForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsListForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleAddForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleDeleteForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleListForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleLogListForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleModifyForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleViewForm.java rename src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoAddForm.java => 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoAddForm.java (51%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoDeleteForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoListForm.java rename src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoModifyForm.java => 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoModifyForm.java (53%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoViewForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/AdminMemberService.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/CorsService.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/ScheduleService.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/SsoService.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberAddVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberDeleteVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberDormantModifyVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberListItemVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberListVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberModifyVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberViewVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberWithdrawModifyVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsAddVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsDeleteVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsListItemVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsListVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsReloadVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleAddVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleDeleteVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleListItemVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleListVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleLogListItemVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleLogListVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleModifyVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleViewVo.java rename src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoAddVo.java => 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoAddVo.java (57%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoDeleteVo.java rename src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoVo.java => 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoListItemVo.java (53%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoListVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoModifyVo.java rename src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoViewVo.java => 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoViewVo.java (52%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/file/FileController.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/file/form/SunEditorUploadForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/file/service/FileService.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/file/vo/FileUploadVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/file/vo/SunEditorUploadVo.java rename {src/main/java/com/alist/api => 02-api-admin/src/main/java/com/alist/api/admin}/modules/main/MainController.java (87%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/NoticeController.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeAddForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeDeleteForm.java rename src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeFileForm.java => 02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeFileForm.java (54%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeListForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeModifyForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeViewForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/service/NoticeService.java rename src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeAddVo.java => 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeAddVo.java (57%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeCampusVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeDeleteVo.java rename src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeFileVo.java => 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeFileVo.java (71%) rename src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeVo.java => 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeListItemVo.java (67%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeListVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeModifyVo.java rename src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeViewVo.java => 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeViewVo.java (55%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/TusFileController.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileDeleteForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileHookForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadCancelForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadInitForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadStatusForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/service/TusFileService.java rename {src/main/java/com/alist/api/modules/tusFile => 02-api-admin/src/main/java/com/alist/api/admin/modules/tus}/vo/TusFileDeleteVo.java (69%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileListVo.java rename {src/main/java/com/alist/api/modules/tusFile => 02-api-admin/src/main/java/com/alist/api/admin/modules/tus}/vo/TusFileUploadCancelVo.java (74%) create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadInitVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadStatusVo.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/user/UserController.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/user/form/UserAddForm.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/user/service/UserService.java create mode 100644 02-api-admin/src/main/java/com/alist/api/admin/modules/user/vo/UserAddVo.java create mode 100644 02-api-admin/src/main/resources/application-local.yaml create mode 100644 02-api-admin/src/main/resources/application-pjt.yaml rename {src => 02-api-admin/src}/main/resources/banner.txt (100%) rename {src => 02-api-admin/src}/main/resources/logback-local.xml (98%) rename {src => 02-api-admin/src}/main/resources/logback-pjt.xml (86%) rename {src => 02-api-admin/src}/main/resources/robots.txt (100%) create mode 100644 03-api-front/Dockerfile create mode 100644 03-api-front/build.gradle create mode 100644 03-api-front/src/main/java/com/alist/api/front/ApiFrontApplication.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/config/OpenApiConfig.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/config/SecurityConfig.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/auth/AuthController.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/auth/form/AuthApiKeyLoginForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/auth/form/AuthTokenForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/auth/service/AuthService.java rename {src/main/java/com/alist/api/modules/front => 03-api-front/src/main/java/com/alist/api/front/modules}/auth/vo/AuthAccessVo.java (61%) rename {src/main/java/com/alist/api/modules/front => 03-api-front/src/main/java/com/alist/api/front/modules}/auth/vo/AuthApiKeyLoginVo.java (55%) rename {src/main/java/com/alist/api/modules/front => 03-api-front/src/main/java/com/alist/api/front/modules}/auth/vo/AuthRefreshVo.java (73%) create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthTokenVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/file/FileController.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/file/form/SunEditorUploadForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/file/service/FileService.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/file/vo/FileUploadVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/file/vo/SunEditorUploadVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/main/MainController.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/sso/SsoController.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/sso/form/SsoExchangeForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/sso/form/SsoLoginForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/sso/service/SsoService.java rename src/main/java/com/alist/api/modules/front/auth/dto/SsoExchangeDto.java => 03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoExchangeVo.java (55%) rename {src/main/java/com/alist/api/modules/front/auth => 03-api-front/src/main/java/com/alist/api/front/modules/sso}/vo/SsoLoginCheckedVo.java (72%) rename {src/main/java/com/alist/api/modules/front/auth => 03-api-front/src/main/java/com/alist/api/front/modules/sso}/vo/SsoLoginVo.java (51%) rename {src/main/java/com/alist/api/modules/front/auth => 03-api-front/src/main/java/com/alist/api/front/modules/sso}/vo/SsoLogoutVo.java (59%) create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/UserController.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserAddForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserDormantModifyForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserMigrationListForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserPasswordCheckForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserPasswordModifyForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserProfileModifyForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserWithdrawModifyForm.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/service/UserService.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserAddVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserDormantModifyVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserMigrationItemVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserMigrationListVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserPasswordCheckVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserPasswordModifyVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserProfileModifyVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserProfileViewVo.java create mode 100644 03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserWithdrawModifyVo.java rename {src => 03-api-front/src}/main/resources/application-local.yaml (100%) rename {src => 03-api-front/src}/main/resources/application-pjt.yaml (100%) create mode 100644 03-api-front/src/main/resources/application.yaml create mode 100644 03-api-front/src/main/resources/banner.txt create mode 100644 03-api-front/src/main/resources/logback-local.xml create mode 100644 03-api-front/src/main/resources/logback-pjt.xml create mode 100644 03-api-front/src/main/resources/robots.txt delete mode 100644 Dockerfile rename deploy/pjt/compose/{api-compose.pjt.yml => api-admin-compose.pjt.yml} (70%) create mode 100644 deploy/pjt/compose/api-front-compose.pjt.yml create mode 100644 docs/architecture-at-a-glance.md create mode 100644 docs/core-module-design.md create mode 100644 docs/testing-strategy.md delete mode 100644 src/main/java/com/alist/api/ApiApplication.java delete mode 100644 src/main/java/com/alist/api/config/cache/CorsCache.java delete mode 100644 src/main/java/com/alist/api/config/filter/DynamicCorsFilter.java delete mode 100644 src/main/java/com/alist/api/config/jwt/JwtAuthenticationFilter.java delete mode 100644 src/main/java/com/alist/api/config/properties/JwtProperties.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/AdminAuthController.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/dto/AdminAuthApiKeyLoginDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/dto/AdminAuthLoginDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/form/AdminAuthApiKeyLoginForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/form/AdminAuthLoginForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/mapper/AdminAuthMapper.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/service/AdminAuthService.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthApiKeyLoginVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLoginCheckedVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLoginVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLogoutVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthRefreshVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/AdminMemberController.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberDeleteDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberDormantModifyDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberListDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberModifyDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberWithdrawModifyDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/form/AdminMemberAddForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/form/AdminMemberDeleteForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/form/AdminMemberDormantModifyForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/form/AdminMemberListForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/form/AdminMemberModifyForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/form/AdminMemberWithdrawModifyForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/mapper/AdminMemberMapper.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/service/AdminMemberService.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberAddVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberDeleteVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberDormantModifyVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberListVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberModifyVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberViewVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberWithdrawModifyVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/AdminNoticeController.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeAddDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeCampusDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeDeleteDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeModifyDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeAddForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeDeleteForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeListForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeModifyForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/mapper/AdminNoticeMapper.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/service/AdminNoticeService.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeDeleteVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeListVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeModifyVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/AdminScheduleController.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleListDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleLogListDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleModifyDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleAddForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleDeleteForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleListForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleLogListForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleModifyForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/mapper/AdminScheduleMapper.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/service/AdminScheduleService.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleAddVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleDeleteVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleListVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleLogListVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleLogVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleModifyVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleViewVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/sso/AdminSsoController.java delete mode 100644 src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoListDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoDeleteForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoListForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/sso/mapper/AdminSsoMapper.java delete mode 100644 src/main/java/com/alist/api/modules/admin/sso/service/AdminSsoService.java delete mode 100644 src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoDeleteVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoListVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoModifyVo.java delete mode 100644 src/main/java/com/alist/api/modules/admin/user/AdminUserController.java delete mode 100644 src/main/java/com/alist/api/modules/admin/user/dto/AdminUserAddDto.java delete mode 100644 src/main/java/com/alist/api/modules/admin/user/form/AdminUserAddForm.java delete mode 100644 src/main/java/com/alist/api/modules/admin/user/service/AdminUserService.java delete mode 100644 src/main/java/com/alist/api/modules/admin/user/vo/AdminUserAddVo.java delete mode 100644 src/main/java/com/alist/api/modules/cors/CorsController.java delete mode 100644 src/main/java/com/alist/api/modules/cors/dto/CorsAddDto.java delete mode 100644 src/main/java/com/alist/api/modules/cors/dto/CorsDeleteDto.java delete mode 100644 src/main/java/com/alist/api/modules/cors/dto/CorsListDto.java delete mode 100644 src/main/java/com/alist/api/modules/cors/form/CorsAddForm.java delete mode 100644 src/main/java/com/alist/api/modules/cors/form/CorsDeleteForm.java delete mode 100644 src/main/java/com/alist/api/modules/cors/form/CorsListForm.java delete mode 100644 src/main/java/com/alist/api/modules/cors/mapper/CorsMapper.java delete mode 100644 src/main/java/com/alist/api/modules/cors/service/CorsService.java delete mode 100644 src/main/java/com/alist/api/modules/cors/vo/CorsAddVo.java delete mode 100644 src/main/java/com/alist/api/modules/cors/vo/CorsCacheVo.java delete mode 100644 src/main/java/com/alist/api/modules/cors/vo/CorsDeleteVo.java delete mode 100644 src/main/java/com/alist/api/modules/cors/vo/CorsListVo.java delete mode 100644 src/main/java/com/alist/api/modules/cors/vo/CorsVo.java delete mode 100644 src/main/java/com/alist/api/modules/file/FileController.java delete mode 100644 src/main/java/com/alist/api/modules/file/properties/FileUploadProperties.java delete mode 100644 src/main/java/com/alist/api/modules/file/service/FileService.java delete mode 100644 src/main/java/com/alist/api/modules/file/service/FileUploadImageService.java delete mode 100644 src/main/java/com/alist/api/modules/file/vo/FileUploadVo.java delete mode 100644 src/main/java/com/alist/api/modules/file/vo/SunEditorUploadVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/AuthController.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/SsoController.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/dto/AuthTokenDto.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/dto/LoginDto.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/dto/SsoLoginDto.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/form/AuthApiKeyLoginForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/form/AuthTokenForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/form/SsoExchangeForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/form/SsoLoginForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/mapper/AuthMapper.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/mapper/SsoMapper.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/service/AuthService.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/service/SsoService.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/vo/AuthTokenVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/vo/LoginTokenVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/auth/vo/SsoExchangeVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/UserController.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/dto/UserDormantModifyDto.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/dto/UserDto.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/dto/UserPasswordCheckDto.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/dto/UserPasswordModifyDto.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/dto/UserProfileModifyDto.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/dto/UserWithdrawModifyDto.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/form/UserAddForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/form/UserDormantModifyForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/form/UserMigrationListForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/form/UserPasswordCheckForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/form/UserPasswordModifyForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/form/UserProfileModifyForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/form/UserWithdrawModifyForm.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/mapper/UserMapper.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/service/UserService.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/vo/UserDormantModifyVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/vo/UserMigrationListVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/vo/UserPasswordCheckVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/vo/UserPasswordModifyVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/vo/UserProfileModifyVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/vo/UserProfileViewVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/vo/UserVo.java delete mode 100644 src/main/java/com/alist/api/modules/front/user/vo/UserWithdrawModifyVo.java delete mode 100644 src/main/java/com/alist/api/modules/migration/alist/user/mapper/AlistUserMapper.java delete mode 100644 src/main/java/com/alist/api/modules/migration/alist/user/service/AlistUserService.java delete mode 100644 src/main/java/com/alist/api/modules/migration/alist/user/vo/AlistUserVo.java delete mode 100644 src/main/java/com/alist/api/modules/migration/eltown/user/mapper/EltownUserMapper.java delete mode 100644 src/main/java/com/alist/api/modules/migration/eltown/user/service/EltownUserService.java delete mode 100644 src/main/java/com/alist/api/modules/migration/eltown/user/vo/EltownUserVo.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/TusFileController.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileDeleteDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileHookDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileListDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileMoveTaskDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadAuthDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadCancelDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitItemDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadMetaDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadStatusDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileViewOrDownloadDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileDeleteForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileHookEventForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileHookForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileHookUploadForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadCancelForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitItemForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadStatusForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/mapper/TusFileMapper.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/service/TusFileService.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileListItemVo.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileListVo.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadAuthVo.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelProgressVo.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitItemVo.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitVo.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadStatusVo.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileViewOrDownloadVo.java delete mode 100644 src/main/resources/mapper/admin/auth/AdminAuthMapper.xml delete mode 100644 src/main/resources/mapper/admin/member/AdminMemberMapper.xml delete mode 100644 src/main/resources/mapper/admin/notice/AdminNoticeMapper.xml delete mode 100644 src/main/resources/mapper/admin/schedule/AdminScheduleMapper.xml delete mode 100644 src/main/resources/mapper/admin/sso/AdminSsoMapper.xml delete mode 100644 src/main/resources/mapper/cors/CorsMapper.xml delete mode 100644 src/main/resources/mapper/front/User/UserMapper.xml delete mode 100644 src/main/resources/mapper/front/auth/LoginMapper.xml delete mode 100644 src/main/resources/mapper/front/auth/SsoMapper.xml delete mode 100644 src/main/resources/mapper/tusFile/TusFileMapper.xml delete mode 100644 src/test/java/com/alist/api/ApiApplicationTests.java diff --git a/01-api-core/build.gradle b/01-api-core/build.gradle new file mode 100644 index 0000000..b63f259 --- /dev/null +++ b/01-api-core/build.gradle @@ -0,0 +1,34 @@ +plugins { + id 'java-library' +} + +dependencies { + api 'org.springframework.boot:spring-boot-starter' + api 'org.springframework.boot:spring-boot-starter-web' + api 'org.springframework.boot:spring-boot-starter-validation' + api 'org.springframework.boot:spring-boot-starter-security' + api 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' + + api 'io.swagger.core.v3:swagger-annotations-jakarta:2.2.27' + + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + + runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' + runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre11' + + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +jar { + enabled = true +} \ No newline at end of file diff --git a/src/main/java/com/alist/api/config/exception/GlobalExceptionHandler.java b/01-api-core/src/main/java/com/alist/api/core/common/exception/GlobalExceptionHandler.java similarity index 96% rename from src/main/java/com/alist/api/config/exception/GlobalExceptionHandler.java rename to 01-api-core/src/main/java/com/alist/api/core/common/exception/GlobalExceptionHandler.java index 7340656..7d31c0d 100644 --- a/src/main/java/com/alist/api/config/exception/GlobalExceptionHandler.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/exception/GlobalExceptionHandler.java @@ -1,7 +1,7 @@ -package com.alist.api.config.exception; +package com.alist.api.core.common.exception; -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.javassist.NotFoundException; import org.springframework.http.ResponseEntity; diff --git a/01-api-core/src/main/java/com/alist/api/core/common/file/service/FileImageService.java b/01-api-core/src/main/java/com/alist/api/core/common/file/service/FileImageService.java new file mode 100644 index 0000000..e7afc47 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/common/file/service/FileImageService.java @@ -0,0 +1,155 @@ +package com.alist.api.core.common.file.service; + +import com.alist.api.core.config.file.FileUploadProperties; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Iterator; + +@Service +public class FileImageService { + public record ImageSize(int width, int height) { + } + + public ImageSize resizeAndSave(MultipartFile multipartFile, Path savePath, FileUploadProperties.Resize resize, String extension) throws IOException { + BufferedImage source = ImageIO.read(multipartFile.getInputStream()); + + if (source == null) { + throw new IllegalArgumentException("invalid image"); + } + + BufferedImage target; + + if (resize.getWidth() != null && resize.getHeight() != null) { + target = cropAndResize(source, resize.getWidth(), resize.getHeight()); + } else if (resize.getMaxWidth() != null && source.getWidth() > resize.getMaxWidth()) { + target = resizeByMaxWidth(source, resize.getMaxWidth()); + } else { + target = source; + } + + writeImage(target, imageFormat(extension), savePath, resize.getQuality()); + + return new ImageSize(target.getWidth(), target.getHeight()); + } + + public ImageSize readSize(Path path) throws IOException { + BufferedImage image = ImageIO.read(path.toFile()); + + if (image == null) { + return null; + } + + return new ImageSize(image.getWidth(), image.getHeight()); + } + + private BufferedImage cropAndResize(BufferedImage source, int targetWidth, int targetHeight) { + double targetRatio = (double) targetWidth / targetHeight; + int sourceWidth = source.getWidth(); + int sourceHeight = source.getHeight(); + double sourceRatio = (double) sourceWidth / sourceHeight; + int cropWidth = sourceWidth; + int cropHeight = sourceHeight; + + if (sourceRatio > targetRatio) { + cropWidth = (int) Math.round(sourceHeight * targetRatio); + } else { + cropHeight = (int) Math.round(sourceWidth / targetRatio); + } + + BufferedImage cropped = source.getSubimage( + (sourceWidth - cropWidth) / 2 + , (sourceHeight - cropHeight) / 2 + , cropWidth + , cropHeight + ); + + return resize(cropped, targetWidth, targetHeight); + } + + private BufferedImage resizeByMaxWidth(BufferedImage source, int maxWidth) { + int targetWidth = maxWidth; + int targetHeight = (int) Math.round((double) source.getHeight() * targetWidth / source.getWidth()); + + return resize(source, targetWidth, targetHeight); + } + + private BufferedImage resize(BufferedImage source, int width, int height) { + BufferedImage target = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = target.createGraphics(); + + graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + graphics.drawImage(source, 0, 0, width, height, null); + graphics.dispose(); + + return target; + } + + private void writeImage(BufferedImage image, String format, Path savePath, Float quality) throws IOException { + if (!"jpeg".equals(format)) { + boolean written = ImageIO.write(image, format, savePath.toFile()); + + if (!written) { + throw new IllegalArgumentException("unsupported image format"); + } + + return; + } + + Iterator writers = ImageIO.getImageWritersByFormatName("jpeg"); + + if (!writers.hasNext()) { + throw new IllegalArgumentException("unsupported image format"); + } + + ImageWriter writer = writers.next(); + ImageWriteParam parameter = writer.getDefaultWriteParam(); + + if (parameter.canWriteCompressed()) { + parameter.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + parameter.setCompressionQuality(normalizeQuality(quality)); + } + + try (ImageOutputStream output = ImageIO.createImageOutputStream(savePath.toFile())) { + writer.setOutput(output); + writer.write(null, new IIOImage(image, null, null), parameter); + } finally { + writer.dispose(); + } + } + + private float normalizeQuality(Float quality) { + if (quality == null) { + return 0.9f; + } + + if (quality < 0.0f) { + return 0.0f; + } + + if (quality > 1.0f) { + return 1.0f; + } + + return quality; + } + + private String imageFormat(String extension) { + if ("jpg".equalsIgnoreCase(extension)) { + return "jpeg"; + } + + return extension.toLowerCase(); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/common/file/service/FileStorageService.java b/01-api-core/src/main/java/com/alist/api/core/common/file/service/FileStorageService.java new file mode 100644 index 0000000..5693ee6 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/common/file/service/FileStorageService.java @@ -0,0 +1,288 @@ +package com.alist.api.core.common.file.service; + +import com.alist.api.core.common.file.vo.FileStorageUploadVo; +import com.alist.api.core.config.file.FileUploadProperties; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; +import org.springframework.http.ContentDisposition; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.util.Locale; +import java.util.UUID; + +@Service +public class FileStorageService { + private static final String PUBLIC_PREFIX = "/uploads"; + + @Value("${file.storage.root-path}") + private String rootPath; + + @Value("${file.storage.view-domain}") + private String viewDomain; + + private final FileUploadProperties fileUploadProperties; + private final FileImageService fileImageService; + + public FileStorageService(FileUploadProperties fileUploadProperties, FileImageService fileImageService) { + this.fileUploadProperties = fileUploadProperties; + this.fileImageService = fileImageService; + } + + public FileStorageUploadVo saveFileUpload(String folder, MultipartFile multipartFile) { + if (folder == null || folder.isBlank()) { + throw new IllegalArgumentException("invalid folder"); + } + + if (multipartFile == null || multipartFile.isEmpty()) { + throw new IllegalArgumentException("empty file"); + } + + FileUploadProperties.UploadType uploadType = resolveUploadType(folder); + String originalFileName = cleanFileName(multipartFile.getOriginalFilename()); + String extension = extractExtension(originalFileName); + + validateExtension(extension); + validateSize(multipartFile.getSize(), uploadType); + + boolean image = isImageExtension(extension); + + if (uploadType.isImageOnly() && !image) { + throw new IllegalArgumentException("image only"); + } + + LocalDate today = LocalDate.now(); + String storedFileName = UUID.randomUUID().toString().replace("-", "") + "." + extension; + Path savePath = Paths.get( + rootPath + , uploadType.getFolder() + , String.valueOf(today.getYear()) + , "%02d".formatted(today.getMonthValue()) + , "%02d".formatted(today.getDayOfMonth()) + , storedFileName + ).normalize().toAbsolutePath(); + + ensureUnderRoot(savePath); + + try { + Files.createDirectories(savePath.getParent()); + + FileImageService.ImageSize imageSize = null; + + if (image && uploadType.getResize() != null && uploadType.getResize().isEnabled()) { + imageSize = fileImageService.resizeAndSave(multipartFile, savePath, uploadType.getResize(), extension); + } else { + multipartFile.transferTo(savePath); + + if (image) { + imageSize = fileImageService.readSize(savePath); + } + } + + FileStorageUploadVo fileStorageUploadVo = new FileStorageUploadVo(); + fileStorageUploadVo.setUploadPath(toUploadPath(savePath)); + fileStorageUploadVo.setOriginalFileName(originalFileName); + fileStorageUploadVo.setStoredFileName(storedFileName); + fileStorageUploadVo.setFileExtension(extension); + fileStorageUploadVo.setContentType(multipartFile.getContentType()); + fileStorageUploadVo.setFileSize(Files.size(savePath)); + fileStorageUploadVo.setWidth(imageSize == null ? null : imageSize.width()); + fileStorageUploadVo.setHeight(imageSize == null ? null : imageSize.height()); + + return fileStorageUploadVo; + } catch (IOException exception) { + throw new IllegalStateException("file save failed", exception); + } + } + + public ResponseEntity loadFileResource(String uploadPath, boolean inline) { + try { + Path filePath = resolveUploadPath(uploadPath); + + if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) { + return ResponseEntity.notFound().build(); + } + + String contentType = Files.probeContentType(filePath); + + if (contentType == null || contentType.isBlank()) { + contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE; + } + + ContentDisposition contentDisposition = inline + ? ContentDisposition.inline().filename(filePath.getFileName().toString(), StandardCharsets.UTF_8).build() + : ContentDisposition.attachment().filename(filePath.getFileName().toString(), StandardCharsets.UTF_8).build(); + + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType(contentType)) + .contentLength(Files.size(filePath)) + .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString()) + .body(new InputStreamResource(Files.newInputStream(filePath))); + } catch (IOException exception) { + throw new IllegalStateException("file read failed", exception); + } + } + + public String buildFileViewUrl(String uploadPath) { + if (viewDomain == null || viewDomain.isBlank()) { + throw new IllegalStateException("file.storage.view-domain is empty"); + } + + String fileDomain = viewDomain.endsWith("/") + ? viewDomain.substring(0, viewDomain.length() - 1) + : viewDomain; + String normalizedUploadPath = uploadPath.startsWith("/") ? uploadPath : "/" + uploadPath; + + return fileDomain + normalizedUploadPath; + } + + private Path resolveUploadPath(String uploadPath) { + if (uploadPath == null || uploadPath.isBlank()) { + throw new IllegalArgumentException("empty path"); + } + + String normalizedUploadPath = uploadPath.trim().replace("\\", "/"); + + if (!normalizedUploadPath.startsWith(PUBLIC_PREFIX + "/")) { + throw new IllegalArgumentException("invalid path"); + } + + String relativePath = normalizedUploadPath.substring((PUBLIC_PREFIX + "/").length()); + + if (relativePath.contains("..") || relativePath.startsWith("/") || relativePath.contains(":")) { + throw new IllegalArgumentException("invalid path"); + } + + Path rootPath = rootPath(); + Path resolvedPath = rootPath.resolve(relativePath).normalize().toAbsolutePath(); + + if (!resolvedPath.startsWith(rootPath)) { + throw new IllegalArgumentException("invalid path"); + } + + return resolvedPath; + } + + private String toUploadPath(Path savePath) { + Path rootPath = rootPath(); + Path absolutePath = savePath.normalize().toAbsolutePath(); + + if (!absolutePath.startsWith(rootPath)) { + throw new IllegalArgumentException("invalid path"); + } + + return PUBLIC_PREFIX + "/" + rootPath.relativize(absolutePath).toString().replace("\\", "/"); + } + + private void ensureUnderRoot(Path path) { + if (!path.startsWith(rootPath())) { + throw new IllegalArgumentException("invalid save path"); + } + } + + private Path rootPath() { + return Paths.get(rootPath).normalize().toAbsolutePath(); + } + + private void validateSize(long fileSize, FileUploadProperties.UploadType uploadType) { + long maxSize = uploadType.getMaxSize() == null + ? fileUploadProperties.getMaxSize().toBytes() + : uploadType.getMaxSize().toBytes(); + + if (fileSize > maxSize) { + throw new IllegalArgumentException("file size exceeded"); + } + } + + private void validateExtension(String extension) { + if (extension.isBlank() || fileUploadProperties.getAllowedExtensions().stream().noneMatch(extension::equalsIgnoreCase)) { + throw new IllegalArgumentException("invalid extension"); + } + } + + private String cleanFileName(String fileName) { + if (fileName == null || fileName.isBlank()) { + throw new IllegalArgumentException("empty file name"); + } + + return Paths.get(fileName).getFileName().toString(); + } + + private String extractExtension(String fileName) { + int extensionIndex = fileName.lastIndexOf('.'); + + if (extensionIndex < 0 || extensionIndex == fileName.length() - 1) { + return ""; + } + + return fileName.substring(extensionIndex + 1).trim().toLowerCase(Locale.ROOT); + } + + private boolean isImageExtension(String extension) { + return "jpg".equals(extension) + || "jpeg".equals(extension) + || "png".equals(extension) + || "gif".equals(extension); + } + + private FileUploadProperties.UploadType resolveUploadType(String folder) { + String normalizedFolder = normalizeFolder(folder); + FileUploadProperties.UploadType uploadType = fileUploadProperties.getTypes() == null + ? null + : fileUploadProperties.getTypes().get(normalizedFolder); + + if (uploadType != null) { + uploadType.setFolder(normalizeFolder(uploadType.getFolder())); + return uploadType; + } + + FileUploadProperties.UploadType fallbackUploadType = new FileUploadProperties.UploadType(); + fallbackUploadType.setFolder(normalizedFolder); + fallbackUploadType.setMaxSize(null); + fallbackUploadType.setImageOnly(false); + fallbackUploadType.setResize(new FileUploadProperties.Resize()); + fallbackUploadType.getResize().setEnabled(false); + + return fallbackUploadType; + } + + private String normalizeFolder(String folder) { + if (folder == null || folder.isBlank()) { + throw new IllegalArgumentException("invalid folder"); + } + + String normalizedFolder = folder.trim().replace("\\", "/"); + + while (normalizedFolder.startsWith("/")) { + normalizedFolder = normalizedFolder.substring(1); + } + + while (normalizedFolder.endsWith("/")) { + normalizedFolder = normalizedFolder.substring(0, normalizedFolder.length() - 1); + } + + if (normalizedFolder.isBlank() + || normalizedFolder.contains("..") + || normalizedFolder.contains(":") + || normalizedFolder.startsWith("http://") + || normalizedFolder.startsWith("https://")) { + throw new IllegalArgumentException("invalid folder"); + } + + if (!normalizedFolder.matches("^[a-zA-Z0-9/_-]+$")) { + throw new IllegalArgumentException("invalid folder"); + } + + return normalizedFolder; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/common/file/vo/FileStorageUploadVo.java b/01-api-core/src/main/java/com/alist/api/core/common/file/vo/FileStorageUploadVo.java new file mode 100644 index 0000000..e7461e9 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/common/file/vo/FileStorageUploadVo.java @@ -0,0 +1,17 @@ +package com.alist.api.core.common.file.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileStorageUploadVo { + private String uploadPath; + private String originalFileName; + private String storedFileName; + private String fileExtension; + private String contentType; + private Long fileSize; + private Integer width; + private Integer height; +} diff --git a/src/main/java/com/alist/api/config/jwt/JwtAccessDeniedHandler.java b/01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtAccessDeniedHandler.java similarity index 85% rename from src/main/java/com/alist/api/config/jwt/JwtAccessDeniedHandler.java rename to 01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtAccessDeniedHandler.java index 2f4f115..5d06e78 100644 --- a/src/main/java/com/alist/api/config/jwt/JwtAccessDeniedHandler.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtAccessDeniedHandler.java @@ -1,7 +1,7 @@ -package com.alist.api.config.jwt; +package com.alist.api.core.common.jwt; -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; diff --git a/src/main/java/com/alist/api/config/jwt/JwtAuthenticationEntryPoint.java b/01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtAuthenticationEntryPoint.java similarity index 85% rename from src/main/java/com/alist/api/config/jwt/JwtAuthenticationEntryPoint.java rename to 01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtAuthenticationEntryPoint.java index 478aaac..e1a149a 100644 --- a/src/main/java/com/alist/api/config/jwt/JwtAuthenticationEntryPoint.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtAuthenticationEntryPoint.java @@ -1,7 +1,7 @@ -package com.alist.api.config.jwt; +package com.alist.api.core.common.jwt; -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; diff --git a/01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtAuthenticationFilter.java b/01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtAuthenticationFilter.java new file mode 100644 index 0000000..1d98efa --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtAuthenticationFilter.java @@ -0,0 +1,101 @@ +package com.alist.api.core.common.jwt; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpHeaders; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.List; + +public class JwtAuthenticationFilter extends OncePerRequestFilter { + private final JwtTokenProvider jwtTokenProvider; + private final String requiredScope; + private final String accessTokenCookieName; + + public JwtAuthenticationFilter( + JwtTokenProvider jwtTokenProvider, + String requiredScope, + String accessTokenCookieName + ) { + this.jwtTokenProvider = jwtTokenProvider; + this.requiredScope = requiredScope; + this.accessTokenCookieName = accessTokenCookieName; + } + + @Override + protected void doFilterInternal( + HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain + ) throws ServletException, IOException { + try { + String token = resolveToken(request); + + if (token != null && jwtTokenProvider.validateToken(token)) { + String principal = jwtTokenProvider.getUserTokenIdx(token); + String role = jwtTokenProvider.getRole(token); + String scope = jwtTokenProvider.getScope(token); + String tokenType = jwtTokenProvider.getTokenType(token); + + boolean authenticatable = + requiredScope.equals(scope) + && role != null + && "ACCESS".equals(tokenType); + + if (authenticatable) { + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken( + principal, + null, + List.of(new SimpleGrantedAuthority("ROLE_" + role)) + ); + + authentication.setDetails( + new WebAuthenticationDetailsSource().buildDetails(request) + ); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + } catch (Exception e) { + SecurityContextHolder.clearContext(); + } + + filterChain.doFilter(request, response); + } + + private String resolveToken(HttpServletRequest request) { + String bearer = request.getHeader(HttpHeaders.AUTHORIZATION); + if (bearer != null && bearer.startsWith("Bearer ")) { + String token = bearer.substring(7).trim(); + if (!token.isEmpty()) { + return token; + } + } + + Cookie[] cookies = request.getCookies(); + if (cookies == null) { + return null; + } + + for (Cookie cookie : cookies) { + if (accessTokenCookieName.equals(cookie.getName())) { + String token = cookie.getValue(); + + if (token != null && !token.isBlank()) { + return token.trim(); + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/alist/api/config/jwt/JwtTokenProvider.java b/01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtTokenProvider.java similarity index 80% rename from src/main/java/com/alist/api/config/jwt/JwtTokenProvider.java rename to 01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtTokenProvider.java index a0f908d..b249cac 100644 --- a/src/main/java/com/alist/api/config/jwt/JwtTokenProvider.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/jwt/JwtTokenProvider.java @@ -1,11 +1,11 @@ -package com.alist.api.config.jwt; +package com.alist.api.core.common.jwt; -import com.alist.api.config.properties.JwtProperties; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.crypto.SecretKey; @@ -15,21 +15,25 @@ import java.util.Date; @Component public class JwtTokenProvider { - private final JwtProperties jwtProperties; private final SecretKey secretKey; + private final long accessTokenValiditySeconds; + private final long refreshTokenValiditySeconds; - public JwtTokenProvider(JwtProperties jwtProperties) { - this.jwtProperties = jwtProperties; - this.secretKey = Keys.hmacShaKeyFor( - jwtProperties.getSecret().getBytes(StandardCharsets.UTF_8) - ); + public JwtTokenProvider( + @Value("${jwt.secret}") String secret, + @Value("${jwt.access-token-validity-seconds}") long accessTokenValiditySeconds, + @Value("${jwt.refresh-token-validity-seconds}") long refreshTokenValiditySeconds + ) { + this.secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); + this.accessTokenValiditySeconds = accessTokenValiditySeconds; + this.refreshTokenValiditySeconds = refreshTokenValiditySeconds; } /** 토큰생성 **/ public String createToken(String userId) { Date now = new Date(); Date expiry = new Date( - now.getTime() + jwtProperties.getAccessTokenValiditySeconds() * 1000 + now.getTime() + accessTokenValiditySeconds * 1000 ); return Jwts.builder() @@ -46,7 +50,7 @@ public class JwtTokenProvider { /* 엑세스 토큰 생성 */ public String createAccessToken(Integer userTokenIdx, String role) { Instant now = Instant.now(); - Instant expiry = now.plusSeconds(jwtProperties.getAccessTokenValiditySeconds()); + Instant expiry = now.plusSeconds(accessTokenValiditySeconds); return Jwts.builder() .setSubject(String.valueOf(userTokenIdx)) @@ -62,7 +66,7 @@ public class JwtTokenProvider { /* 리프레시 토큰 생성 */ public String createRefreshToken(Integer userTokenIdx) { Instant now = Instant.now(); - Instant expiry = now.plusSeconds(jwtProperties.getRefreshTokenValiditySeconds()); + Instant expiry = now.plusSeconds(refreshTokenValiditySeconds); return Jwts.builder() .setSubject(String.valueOf(userTokenIdx)) @@ -122,7 +126,7 @@ public class JwtTokenProvider { public String createAdminAccessToken(long userTokenIdx) { Instant now = Instant.now(); - Instant expiry = now.plusSeconds(jwtProperties.getAccessTokenValiditySeconds()); + Instant expiry = now.plusSeconds(accessTokenValiditySeconds); return Jwts.builder() .setSubject(String.valueOf(userTokenIdx)) @@ -137,7 +141,7 @@ public class JwtTokenProvider { public String createAdminRefreshToken(long userTokenIdx) { Instant now = Instant.now(); - Instant expiry = now.plusSeconds(jwtProperties.getRefreshTokenValiditySeconds()); + Instant expiry = now.plusSeconds(refreshTokenValiditySeconds); return Jwts.builder() .setSubject(String.valueOf(userTokenIdx)) diff --git a/src/main/java/com/alist/api/common/paging/PageRequest.java b/01-api-core/src/main/java/com/alist/api/core/common/paging/PagingRequest.java similarity index 84% rename from src/main/java/com/alist/api/common/paging/PageRequest.java rename to 01-api-core/src/main/java/com/alist/api/core/common/paging/PagingRequest.java index 741a483..1ddeebb 100644 --- a/src/main/java/com/alist/api/common/paging/PageRequest.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/paging/PagingRequest.java @@ -1,11 +1,11 @@ -package com.alist.api.common.paging; +package com.alist.api.core.common.paging; -import com.alist.api.common.utils.PagingUtil; +import com.alist.api.core.common.utils.PagingUtil; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Setter; @Setter -public class PageRequest { +public class PagingRequest { @Schema(description = "페이지 번호", example = "1") private Integer page; diff --git a/src/main/java/com/alist/api/common/paging/PageResponse.java b/01-api-core/src/main/java/com/alist/api/core/common/paging/PagingResponse.java similarity index 70% rename from src/main/java/com/alist/api/common/paging/PageResponse.java rename to 01-api-core/src/main/java/com/alist/api/core/common/paging/PagingResponse.java index 7ad2496..b47bd17 100644 --- a/src/main/java/com/alist/api/common/paging/PageResponse.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/paging/PagingResponse.java @@ -1,13 +1,13 @@ -package com.alist.api.common.paging; +package com.alist.api.core.common.paging; -import com.alist.api.common.utils.PagingUtil; +import com.alist.api.core.common.utils.PagingUtil; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class PageResponse { +public class PagingResponse { @Schema(description = "현재 페이지 번호", example = "1") private int page; @@ -26,11 +26,11 @@ public class PageResponse { ) private int rowStartNum; - public void setPaging(PageRequest pageRequest, int totalCount) { - this.page = pageRequest.getPage(); - this.size = pageRequest.getSize(); + public void setPaging(PagingRequest pagingRequest, int totalCount) { + this.page = pagingRequest.getPage(); + this.size = pagingRequest.getSize(); this.totalCount = totalCount; - this.totalPage = PagingUtil.getTotalPage(totalCount, pageRequest.getSize()); - this.rowStartNum = PagingUtil.getRowStartNum(totalCount, pageRequest.getPage(), pageRequest.getSize()); + this.totalPage = PagingUtil.getTotalPage(totalCount, pagingRequest.getSize()); + this.rowStartNum = PagingUtil.getRowStartNum(totalCount, pagingRequest.getPage(), pagingRequest.getSize()); } } diff --git a/src/main/java/com/alist/api/common/response/ApiResponse.java b/01-api-core/src/main/java/com/alist/api/core/common/response/ApiResponse.java similarity index 96% rename from src/main/java/com/alist/api/common/response/ApiResponse.java rename to 01-api-core/src/main/java/com/alist/api/core/common/response/ApiResponse.java index d0ff98a..6a14e1f 100644 --- a/src/main/java/com/alist/api/common/response/ApiResponse.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/response/ApiResponse.java @@ -1,4 +1,4 @@ -package com.alist.api.common.response; +package com.alist.api.core.common.response; import lombok.Getter; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/com/alist/api/common/response/ApiResponseCode.java b/01-api-core/src/main/java/com/alist/api/core/common/response/ApiResponseCode.java similarity index 97% rename from src/main/java/com/alist/api/common/response/ApiResponseCode.java rename to 01-api-core/src/main/java/com/alist/api/core/common/response/ApiResponseCode.java index 44bec1f..45d89d8 100644 --- a/src/main/java/com/alist/api/common/response/ApiResponseCode.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/response/ApiResponseCode.java @@ -1,4 +1,4 @@ -package com.alist.api.common.response; +package com.alist.api.core.common.response; import org.springframework.http.HttpStatus; diff --git a/src/main/java/com/alist/api/common/utils/ApiKeyGenerator.java b/01-api-core/src/main/java/com/alist/api/core/common/utils/ApiKeyGenerator.java similarity index 91% rename from src/main/java/com/alist/api/common/utils/ApiKeyGenerator.java rename to 01-api-core/src/main/java/com/alist/api/core/common/utils/ApiKeyGenerator.java index 6b52442..e4be87d 100644 --- a/src/main/java/com/alist/api/common/utils/ApiKeyGenerator.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/utils/ApiKeyGenerator.java @@ -1,4 +1,4 @@ -package com.alist.api.common.utils; +package com.alist.api.core.common.utils; import java.security.SecureRandom; import java.util.Base64; diff --git a/src/main/java/com/alist/api/common/utils/HashUtils.java b/01-api-core/src/main/java/com/alist/api/core/common/utils/HashUtils.java similarity index 94% rename from src/main/java/com/alist/api/common/utils/HashUtils.java rename to 01-api-core/src/main/java/com/alist/api/core/common/utils/HashUtils.java index 4eedf5d..72d588f 100644 --- a/src/main/java/com/alist/api/common/utils/HashUtils.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/utils/HashUtils.java @@ -1,4 +1,4 @@ -package com.alist.api.common.utils; +package com.alist.api.core.common.utils; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; diff --git a/src/main/java/com/alist/api/common/utils/PagingUtil.java b/01-api-core/src/main/java/com/alist/api/core/common/utils/PagingUtil.java similarity index 96% rename from src/main/java/com/alist/api/common/utils/PagingUtil.java rename to 01-api-core/src/main/java/com/alist/api/core/common/utils/PagingUtil.java index ba43525..5d82151 100644 --- a/src/main/java/com/alist/api/common/utils/PagingUtil.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/utils/PagingUtil.java @@ -1,4 +1,4 @@ -package com.alist.api.common.utils; +package com.alist.api.core.common.utils; public final class PagingUtil { private static final int DEFAULT_PAGE = 1; diff --git a/src/main/java/com/alist/api/common/utils/SecurityUtil.java b/01-api-core/src/main/java/com/alist/api/core/common/utils/SecurityUtil.java similarity index 92% rename from src/main/java/com/alist/api/common/utils/SecurityUtil.java rename to 01-api-core/src/main/java/com/alist/api/core/common/utils/SecurityUtil.java index 34faed4..eaf74a3 100644 --- a/src/main/java/com/alist/api/common/utils/SecurityUtil.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/utils/SecurityUtil.java @@ -1,4 +1,4 @@ -package com.alist.api.common.utils; +package com.alist.api.core.common.utils; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; diff --git a/src/main/java/com/alist/api/common/utils/SessionUtil.java b/01-api-core/src/main/java/com/alist/api/core/common/utils/SessionUtil.java similarity index 97% rename from src/main/java/com/alist/api/common/utils/SessionUtil.java rename to 01-api-core/src/main/java/com/alist/api/core/common/utils/SessionUtil.java index d54087f..d2c2dfb 100644 --- a/src/main/java/com/alist/api/common/utils/SessionUtil.java +++ b/01-api-core/src/main/java/com/alist/api/core/common/utils/SessionUtil.java @@ -1,4 +1,4 @@ -package com.alist.api.common.utils; +package com.alist.api.core.common.utils; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; diff --git a/01-api-core/src/main/java/com/alist/api/core/config/ApiCoreConfig.java b/01-api-core/src/main/java/com/alist/api/core/config/ApiCoreConfig.java new file mode 100644 index 0000000..d01dcea --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/config/ApiCoreConfig.java @@ -0,0 +1,9 @@ +package com.alist.api.core.config; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan("com.alist.api.core") +public class ApiCoreConfig { +} diff --git a/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCache.java b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCache.java new file mode 100644 index 0000000..d52a55e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCache.java @@ -0,0 +1,89 @@ +package com.alist.api.core.config.cors; + +import com.alist.api.core.modules.standard.testCorsAllowedList.service.TestCorsAllowedListService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Slf4j +@Component +public class CorsCache { + public static final String REDIS_KEY = "alist:cors:allow-origins"; + + private final StringRedisTemplate stringRedisTemplate; + private final ObjectMapper objectMapper; + private final TestCorsAllowedListService testCorsAllowedListService; + + private volatile List allowedOriginList = List.of(); + + public CorsCache(StringRedisTemplate stringRedisTemplate, ObjectMapper objectMapper, TestCorsAllowedListService testCorsAllowedListService) { + this.stringRedisTemplate = stringRedisTemplate; + this.objectMapper = objectMapper; + this.testCorsAllowedListService = testCorsAllowedListService; + } + + @PostConstruct + public void initialize() { + refreshFromRedis(); + } + + @Scheduled(fixedDelayString = "${cors.cache.redis-refresh-interval-ms:60000}") + public void refreshFromRedis() { + String allowedOriginJson = stringRedisTemplate.opsForValue().get(REDIS_KEY); + + if (allowedOriginJson == null || allowedOriginJson.isBlank()) { + allowedOriginList = List.of(); + return; + } + + try { + List redisAllowedOriginList = objectMapper.readValue(allowedOriginJson, new TypeReference>() { + }); + + allowedOriginList = redisAllowedOriginList.stream() + .filter(Objects::nonNull) + .map(String::trim) + .filter(allowedOrigin -> !allowedOrigin.isEmpty()) + .distinct() + .toList(); + } catch (JsonProcessingException e) { + log.error("CORS Redis cache JSON parsing failed.", e); + } + } + + public void syncFromDatabase() { + List databaseAllowedOriginList = testCorsAllowedListService.selectTestCorsAllowedListAllowedOriginList(); + List allowedOriginList = new ArrayList<>(); + + for (String allowedOrigin : databaseAllowedOriginList) { + if (allowedOrigin != null && !allowedOrigin.isBlank()) { + allowedOriginList.add(allowedOrigin.trim()); + } + } + + try { + String allowedOriginJson = objectMapper.writeValueAsString(allowedOriginList); + stringRedisTemplate.opsForValue().set(REDIS_KEY, allowedOriginJson); + this.allowedOriginList = List.copyOf(allowedOriginList); + } catch (JsonProcessingException e) { + throw new IllegalStateException("CORS Redis cache JSON creation failed.", e); + } + } + + public boolean isAllowed(String origin) { + return allowedOriginList.contains("*") || allowedOriginList.contains(origin); + } + + public List getAllowedOriginList() { + return List.copyOf(allowedOriginList); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCachePublisher.java b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCachePublisher.java new file mode 100644 index 0000000..a7b6b4e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCachePublisher.java @@ -0,0 +1,19 @@ +package com.alist.api.core.config.cors; + +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +@Component +public class CorsCachePublisher { + public static final String CHANNEL = "alist:cors:allow-origins:changed"; + + private final StringRedisTemplate stringRedisTemplate; + + public CorsCachePublisher(StringRedisTemplate stringRedisTemplate) { + this.stringRedisTemplate = stringRedisTemplate; + } + + public void publishReload() { + stringRedisTemplate.convertAndSend(CHANNEL, "reload"); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheRefreshEvent.java b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheRefreshEvent.java new file mode 100644 index 0000000..1f7bacb --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheRefreshEvent.java @@ -0,0 +1,4 @@ +package com.alist.api.core.config.cors; + +public class CorsCacheRefreshEvent { +} diff --git a/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheRefreshEventListener.java b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheRefreshEventListener.java new file mode 100644 index 0000000..f636c54 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheRefreshEventListener.java @@ -0,0 +1,22 @@ +package com.alist.api.core.config.cors; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Component +public class CorsCacheRefreshEventListener { + private final CorsCache corsCache; + private final CorsCachePublisher corsCachePublisher; + + public CorsCacheRefreshEventListener(CorsCache corsCache, CorsCachePublisher corsCachePublisher) { + this.corsCache = corsCache; + this.corsCachePublisher = corsCachePublisher; + } + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void refreshCorsCache(CorsCacheRefreshEvent corsCacheRefreshEvent) { + corsCache.syncFromDatabase(); + corsCachePublisher.publishReload(); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheSubscriber.java b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheSubscriber.java new file mode 100644 index 0000000..bbade7f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsCacheSubscriber.java @@ -0,0 +1,19 @@ +package com.alist.api.core.config.cors; + +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.stereotype.Component; + +@Component +public class CorsCacheSubscriber implements MessageListener { + private final CorsCache corsCache; + + public CorsCacheSubscriber(CorsCache corsCache) { + this.corsCache = corsCache; + } + + @Override + public void onMessage(Message message, byte[] pattern) { + corsCache.refreshFromRedis(); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsRedisConfig.java b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsRedisConfig.java new file mode 100644 index 0000000..40321a1 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/config/cors/CorsRedisConfig.java @@ -0,0 +1,21 @@ +package com.alist.api.core.config.cors; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.ChannelTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.scheduling.annotation.EnableScheduling; + +@Configuration +@EnableScheduling +public class CorsRedisConfig { + @Bean + public RedisMessageListenerContainer corsRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, CorsCacheSubscriber corsCacheSubscriber) { + RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer(); + redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory); + redisMessageListenerContainer.addMessageListener(corsCacheSubscriber, new ChannelTopic(CorsCachePublisher.CHANNEL)); + + return redisMessageListenerContainer; + } +} diff --git a/src/main/java/com/alist/api/config/migration/AlistDataSourceConfig.java b/01-api-core/src/main/java/com/alist/api/core/config/datasource/AlistDataSourceConfig.java similarity index 83% rename from src/main/java/com/alist/api/config/migration/AlistDataSourceConfig.java rename to 01-api-core/src/main/java/com/alist/api/core/config/datasource/AlistDataSourceConfig.java index 56f8589..7c0225a 100644 --- a/src/main/java/com/alist/api/config/migration/AlistDataSourceConfig.java +++ b/01-api-core/src/main/java/com/alist/api/core/config/datasource/AlistDataSourceConfig.java @@ -1,4 +1,4 @@ -package com.alist.api.config.migration; +package com.alist.api.core.config.datasource; import javax.sql.DataSource; @@ -8,6 +8,7 @@ import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; @@ -16,8 +17,13 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @Configuration +@ConditionalOnProperty( + prefix = "migration.datasource.alist" + , name = "enabled" + , havingValue = "true" +) @MapperScan( - basePackages = "com.alist.api.modules.migration.alist", + basePackages = "com.alist.api.core.modules.migration.alist", sqlSessionFactoryRef = "migrationAlistSqlSessionFactory" ) public class AlistDataSourceConfig { @@ -36,11 +42,11 @@ public class AlistDataSourceConfig { ) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(migrationAlistDataSource); - factoryBean.setTypeAliasesPackage("com.alist.api.modules.migration.alist"); + factoryBean.setTypeAliasesPackage("com.alist.api.core.modules.migration.alist"); factoryBean.setConfiguration(mybatisConfiguration()); factoryBean.setMapperLocations( new PathMatchingResourcePatternResolver() - .getResources("classpath:mapper/migration/alist/**/*.xml") + .getResources("classpath*:mapper/migration/alist/**/*.xml") ); return factoryBean.getObject(); } diff --git a/src/main/java/com/alist/api/config/migration/EltownDataSourceConfig.java b/01-api-core/src/main/java/com/alist/api/core/config/datasource/EltownDataSourceConfig.java similarity index 83% rename from src/main/java/com/alist/api/config/migration/EltownDataSourceConfig.java rename to 01-api-core/src/main/java/com/alist/api/core/config/datasource/EltownDataSourceConfig.java index 3f34d12..97eb514 100644 --- a/src/main/java/com/alist/api/config/migration/EltownDataSourceConfig.java +++ b/01-api-core/src/main/java/com/alist/api/core/config/datasource/EltownDataSourceConfig.java @@ -1,4 +1,4 @@ -package com.alist.api.config.migration; +package com.alist.api.core.config.datasource; import javax.sql.DataSource; @@ -8,6 +8,7 @@ import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; @@ -16,8 +17,13 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @Configuration +@ConditionalOnProperty( + prefix = "migration.datasource.eltown" + , name = "enabled" + , havingValue = "true" +) @MapperScan( - basePackages = "com.alist.api.modules.migration.eltown", + basePackages = "com.alist.api.core.modules.migration.eltown", sqlSessionFactoryRef = "migrationEltownSqlSessionFactory" ) public class EltownDataSourceConfig { @@ -36,11 +42,11 @@ public class EltownDataSourceConfig { ) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(migrationEltownDataSource); - factoryBean.setTypeAliasesPackage("com.alist.api.modules.migration.eltown"); + factoryBean.setTypeAliasesPackage("com.alist.api.core.modules.migration.eltown"); factoryBean.setConfiguration(mybatisConfiguration()); factoryBean.setMapperLocations( new PathMatchingResourcePatternResolver() - .getResources("classpath:mapper/migration/eltown/**/*.xml") + .getResources("classpath*:mapper/migration/eltown/**/*.xml") ); return factoryBean.getObject(); } diff --git a/src/main/java/com/alist/api/config/MainDataSourceConfig.java b/01-api-core/src/main/java/com/alist/api/core/config/datasource/MainDataSourceConfig.java similarity index 86% rename from src/main/java/com/alist/api/config/MainDataSourceConfig.java rename to 01-api-core/src/main/java/com/alist/api/core/config/datasource/MainDataSourceConfig.java index fa73767..552fcad 100644 --- a/src/main/java/com/alist/api/config/MainDataSourceConfig.java +++ b/01-api-core/src/main/java/com/alist/api/core/config/datasource/MainDataSourceConfig.java @@ -1,4 +1,4 @@ -package com.alist.api.config; +package com.alist.api.core.config.datasource; import javax.sql.DataSource; @@ -23,7 +23,10 @@ import java.util.stream.Stream; @Configuration @MapperScan( - basePackages = "com.alist.api.modules", + basePackages = { + "com.alist.api.core.modules.standard", + "com.alist.api.core.modules.bespoke" + }, annotationClass = Mapper.class, sqlSessionFactoryRef = "mainSqlSessionFactory" ) @@ -53,18 +56,18 @@ public class MainDataSourceConfig { ) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(mainDataSource); - factoryBean.setTypeAliasesPackage("com.alist.api"); + factoryBean.setTypeAliasesPackage("com.alist.api.core"); factoryBean.setConfiguration(mybatisConfiguration()); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); - Resource[] commonMappers = resolver.getResources("classpath:mapper/*/*.xml"); - Resource[] adminMappers = resolver.getResources("classpath:mapper/admin/**/*.xml"); - Resource[] frontMappers = resolver.getResources("classpath:mapper/front/**/*.xml"); + Resource[] standardMappers = resolver.getResources("classpath*:mapper/standard/**/*.xml"); + + Resource[] bespokeMappers = resolver.getResources("classpath*:mapper/bespoke/**/*.xml"); + Resource[] mapperLocations = Stream.of( - Arrays.stream(commonMappers) - , Arrays.stream(adminMappers) - , Arrays.stream(frontMappers) + Arrays.stream(standardMappers) + , Arrays.stream(bespokeMappers) ).flatMap(stream -> stream).toArray(Resource[]::new); factoryBean.setMapperLocations(mapperLocations); diff --git a/src/main/java/com/alist/api/modules/file/config/FileUploadConfig.java b/01-api-core/src/main/java/com/alist/api/core/config/file/FileUploadConfig.java similarity index 69% rename from src/main/java/com/alist/api/modules/file/config/FileUploadConfig.java rename to 01-api-core/src/main/java/com/alist/api/core/config/file/FileUploadConfig.java index d36e197..c470d69 100644 --- a/src/main/java/com/alist/api/modules/file/config/FileUploadConfig.java +++ b/01-api-core/src/main/java/com/alist/api/core/config/file/FileUploadConfig.java @@ -1,10 +1,9 @@ -package com.alist.api.modules.file.config; +package com.alist.api.core.config.file; -import com.alist.api.modules.file.properties.FileUploadProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; -@EnableConfigurationProperties(FileUploadProperties.class) @Configuration +@EnableConfigurationProperties(FileUploadProperties.class) public class FileUploadConfig { } diff --git a/01-api-core/src/main/java/com/alist/api/core/config/file/FileUploadProperties.java b/01-api-core/src/main/java/com/alist/api/core/config/file/FileUploadProperties.java new file mode 100644 index 0000000..60764e0 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/config/file/FileUploadProperties.java @@ -0,0 +1,38 @@ +package com.alist.api.core.config.file; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.util.unit.DataSize; + +import java.util.List; +import java.util.Map; + +@Getter +@Setter +@ConfigurationProperties(prefix = "file.upload") +public class FileUploadProperties { + private DataSize maxSize; + private List allowedExtensions; + private Map types; + + @Getter + @Setter + public static class UploadType { + private String folder; + private DataSize maxSize; + private boolean imageOnly; + private Resize resize = new Resize(); + } + + @Getter + @Setter + public static class Resize { + private boolean enabled; + private Integer width; + private Integer height; + private Integer maxWidth; + private Float quality = 0.9f; + } + +} diff --git a/01-api-core/src/main/java/com/alist/api/core/config/filter/DynamicCorsFilter.java b/01-api-core/src/main/java/com/alist/api/core/config/filter/DynamicCorsFilter.java new file mode 100644 index 0000000..1d9b592 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/config/filter/DynamicCorsFilter.java @@ -0,0 +1,44 @@ +package com.alist.api.core.config.filter; + +import com.alist.api.core.config.cors.CorsCache; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class DynamicCorsFilter extends OncePerRequestFilter { + private final CorsCache corsCache; + + public DynamicCorsFilter(CorsCache corsCache) { + this.corsCache = corsCache; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String origin = request.getHeader("Origin"); + + if (origin != null && corsCache.isAllowed(origin)) { + response.setHeader("Access-Control-Allow-Origin", origin); + response.setHeader("Access-Control-Allow-Credentials", "true"); + response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); + response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With, Accept, Origin, Referer, Content-Disposition, Content-Length"); + response.setHeader("Access-Control-Expose-Headers", "Authorization, Set-Cookie"); + response.setHeader("Access-Control-Max-Age", "3600"); + } + + if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { + response.setStatus(HttpServletResponse.SC_OK); + return; + } + + filterChain.doFilter(request, response); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileAuthDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileAuthDto.java new file mode 100644 index 0000000..4f8082e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileAuthDto.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.bespoke.fileDetail.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailTusFileAuthDto { + private Integer userTokenIdx; + private String fileUuid; + private String originName; + private Long sizeBytes; + private String contentType; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileListDto.java new file mode 100644 index 0000000..414686b --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileListDto.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.bespoke.fileDetail.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailTusFileListDto { + private Long fileMasterIdx; + private Integer userTokenIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileViewDto.java new file mode 100644 index 0000000..1baddb4 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/dto/FileDetailTusFileViewDto.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.bespoke.fileDetail.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailTusFileViewDto { + private String fileUuid; + private Integer userTokenIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/mapper/FileDetailTusFileMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/mapper/FileDetailTusFileMapper.java new file mode 100644 index 0000000..c33ff71 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/mapper/FileDetailTusFileMapper.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.bespoke.fileDetail.mapper; + +import com.alist.api.core.modules.bespoke.fileDetail.dto.FileDetailTusFileAuthDto; +import com.alist.api.core.modules.bespoke.fileDetail.dto.FileDetailTusFileListDto; +import com.alist.api.core.modules.bespoke.fileDetail.dto.FileDetailTusFileViewDto; +import com.alist.api.core.modules.bespoke.fileDetail.vo.FileDetailTusFileListItemVo; +import com.alist.api.core.modules.bespoke.fileDetail.vo.FileDetailTusFileViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface FileDetailTusFileMapper { + int fileDetailTusFileUploadAuthCount(FileDetailTusFileAuthDto fileDetailTusFileAuthDto); + + FileDetailTusFileViewVo fileDetailTusFileMetaView(FileDetailTusFileViewDto fileDetailTusFileViewDto); + + FileDetailTusFileViewVo fileDetailTusFileMoveView(FileDetailTusFileViewDto fileDetailTusFileViewDto); + + FileDetailTusFileViewVo fileDetailTusFileUploadCancelView(FileDetailTusFileViewDto fileDetailTusFileViewDto); + + List fileDetailTusFileList(FileDetailTusFileListDto fileDetailTusFileListDto); + + FileDetailTusFileViewVo fileDetailTusFileView(FileDetailTusFileViewDto fileDetailTusFileViewDto); + + FileDetailTusFileViewVo fileDetailTusFileDeleteView(FileDetailTusFileViewDto fileDetailTusFileViewDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/service/FileDetailTusFileService.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/service/FileDetailTusFileService.java new file mode 100644 index 0000000..2576ceb --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/service/FileDetailTusFileService.java @@ -0,0 +1,56 @@ +package com.alist.api.core.modules.bespoke.fileDetail.service; + +import com.alist.api.core.modules.bespoke.fileDetail.dto.FileDetailTusFileAuthDto; +import com.alist.api.core.modules.bespoke.fileDetail.dto.FileDetailTusFileListDto; +import com.alist.api.core.modules.bespoke.fileDetail.dto.FileDetailTusFileViewDto; +import com.alist.api.core.modules.bespoke.fileDetail.mapper.FileDetailTusFileMapper; +import com.alist.api.core.modules.bespoke.fileDetail.vo.FileDetailTusFileListItemVo; +import com.alist.api.core.modules.bespoke.fileDetail.vo.FileDetailTusFileViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +public class FileDetailTusFileService { + private final FileDetailTusFileMapper fileDetailTusFileMapper; + + public FileDetailTusFileService(FileDetailTusFileMapper fileDetailTusFileMapper) { + this.fileDetailTusFileMapper = fileDetailTusFileMapper; + } + + @Transactional(readOnly = true) + public int fileDetailTusFileUploadAuthCount(FileDetailTusFileAuthDto fileDetailTusFileAuthDto) { + return fileDetailTusFileMapper.fileDetailTusFileUploadAuthCount(fileDetailTusFileAuthDto); + } + + @Transactional(readOnly = true) + public FileDetailTusFileViewVo fileDetailTusFileMetaView(FileDetailTusFileViewDto fileDetailTusFileViewDto) { + return fileDetailTusFileMapper.fileDetailTusFileMetaView(fileDetailTusFileViewDto); + } + + @Transactional(readOnly = true) + public FileDetailTusFileViewVo fileDetailTusFileMoveView(FileDetailTusFileViewDto fileDetailTusFileViewDto) { + return fileDetailTusFileMapper.fileDetailTusFileMoveView(fileDetailTusFileViewDto); + } + + @Transactional(readOnly = true) + public FileDetailTusFileViewVo fileDetailTusFileUploadCancelView(FileDetailTusFileViewDto fileDetailTusFileViewDto) { + return fileDetailTusFileMapper.fileDetailTusFileUploadCancelView(fileDetailTusFileViewDto); + } + + @Transactional(readOnly = true) + public List fileDetailTusFileList(FileDetailTusFileListDto fileDetailTusFileListDto) { + return fileDetailTusFileMapper.fileDetailTusFileList(fileDetailTusFileListDto); + } + + @Transactional(readOnly = true) + public FileDetailTusFileViewVo fileDetailTusFileView(FileDetailTusFileViewDto fileDetailTusFileViewDto) { + return fileDetailTusFileMapper.fileDetailTusFileView(fileDetailTusFileViewDto); + } + + @Transactional(readOnly = true) + public FileDetailTusFileViewVo fileDetailTusFileDeleteView(FileDetailTusFileViewDto fileDetailTusFileViewDto) { + return fileDetailTusFileMapper.fileDetailTusFileDeleteView(fileDetailTusFileViewDto); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/vo/FileDetailTusFileListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/vo/FileDetailTusFileListItemVo.java new file mode 100644 index 0000000..99692c1 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/vo/FileDetailTusFileListItemVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.bespoke.fileDetail.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailTusFileListItemVo { + private Long fileDetailIdx; + private Long fileMasterIdx; + private String fileUuid; + private String originName; + private String contentType; + private Long sizeBytes; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/vo/FileDetailTusFileViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/vo/FileDetailTusFileViewVo.java new file mode 100644 index 0000000..c093a76 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileDetail/vo/FileDetailTusFileViewVo.java @@ -0,0 +1,25 @@ +package com.alist.api.core.modules.bespoke.fileDetail.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailTusFileViewVo { + private Long fileDetailIdx; + private Long fileMasterIdx; + private String fileUuid; + private String originName; + private String contentType; + private Long sizeBytes; + private Long uploadedBytes; + private String tusUploadId; + private String folderPath; + private String saveName; + private String savePath; + private String ext; + private String moveYn; + private Integer moveTryCount; + private Integer status; + private Integer userIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/dto/FileMasterTusFileModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/dto/FileMasterTusFileModifyDto.java new file mode 100644 index 0000000..2f617ad --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/dto/FileMasterTusFileModifyDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.bespoke.fileMaster.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileMasterTusFileModifyDto { + private String fileUuid; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/mapper/FileMasterTusFileMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/mapper/FileMasterTusFileMapper.java new file mode 100644 index 0000000..77bcb98 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/mapper/FileMasterTusFileMapper.java @@ -0,0 +1,9 @@ +package com.alist.api.core.modules.bespoke.fileMaster.mapper; + +import com.alist.api.core.modules.bespoke.fileMaster.dto.FileMasterTusFileModifyDto; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface FileMasterTusFileMapper { + int fileMasterTusFileAggregateModify(FileMasterTusFileModifyDto fileMasterTusFileModifyDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/service/FileMasterTusFileService.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/service/FileMasterTusFileService.java new file mode 100644 index 0000000..06fcf20 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/fileMaster/service/FileMasterTusFileService.java @@ -0,0 +1,20 @@ +package com.alist.api.core.modules.bespoke.fileMaster.service; + +import com.alist.api.core.modules.bespoke.fileMaster.dto.FileMasterTusFileModifyDto; +import com.alist.api.core.modules.bespoke.fileMaster.mapper.FileMasterTusFileMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class FileMasterTusFileService { + private final FileMasterTusFileMapper fileMasterTusFileMapper; + + public FileMasterTusFileService(FileMasterTusFileMapper fileMasterTusFileMapper) { + this.fileMasterTusFileMapper = fileMasterTusFileMapper; + } + + @Transactional + public int fileMasterTusFileAggregateModify(FileMasterTusFileModifyDto fileMasterTusFileModifyDto) { + return fileMasterTusFileMapper.fileMasterTusFileAggregateModify(fileMasterTusFileModifyDto); + } +} diff --git a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/dto/NoticeNoticeListDto.java similarity index 56% rename from src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeListDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/dto/NoticeNoticeListDto.java index 0e69579..8a777e0 100644 --- a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeListDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/dto/NoticeNoticeListDto.java @@ -1,12 +1,12 @@ -package com.alist.api.modules.admin.notice.dto; +package com.alist.api.core.modules.bespoke.notice.dto; -import com.alist.api.common.paging.PageRequest; +import com.alist.api.core.common.paging.PagingRequest; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class AdminNoticeListDto extends PageRequest { +public class NoticeNoticeListDto extends PagingRequest { private String keyword; private String noticeType; private String targetScope; diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/dto/NoticeNoticeViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/dto/NoticeNoticeViewDto.java new file mode 100644 index 0000000..ebb8ad3 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/dto/NoticeNoticeViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.bespoke.notice.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeNoticeViewDto { + private Long noticeIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/mapper/NoticeNoticeMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/mapper/NoticeNoticeMapper.java new file mode 100644 index 0000000..d36541f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/mapper/NoticeNoticeMapper.java @@ -0,0 +1,23 @@ +package com.alist.api.core.modules.bespoke.notice.mapper; + +import com.alist.api.core.modules.bespoke.notice.dto.NoticeNoticeListDto; +import com.alist.api.core.modules.bespoke.notice.dto.NoticeNoticeViewDto; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeCampusListItemVo; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeFileListItemVo; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeListItemVo; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeViewVo; +import org.apache.ibatis.annotations.Mapper; +import java.util.List; + +@Mapper +public interface NoticeNoticeMapper { + int noticeNoticeListCount(NoticeNoticeListDto noticeNoticeListDto); + + List noticeNoticeList(NoticeNoticeListDto noticeNoticeListDto); + + NoticeNoticeViewVo noticeNoticeView(NoticeNoticeViewDto noticeNoticeViewDto); + + List noticeNoticeCampusList(NoticeNoticeViewDto noticeNoticeViewDto); + + List noticeNoticeFileList(NoticeNoticeViewDto noticeNoticeViewDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/service/NoticeNoticeService.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/service/NoticeNoticeService.java new file mode 100644 index 0000000..bf8dd84 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/service/NoticeNoticeService.java @@ -0,0 +1,46 @@ +package com.alist.api.core.modules.bespoke.notice.service; + +import com.alist.api.core.modules.bespoke.notice.dto.NoticeNoticeListDto; +import com.alist.api.core.modules.bespoke.notice.dto.NoticeNoticeViewDto; +import com.alist.api.core.modules.bespoke.notice.mapper.NoticeNoticeMapper; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeCampusListItemVo; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeFileListItemVo; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeListItemVo; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.util.List; + +@Service +public class NoticeNoticeService { + private final NoticeNoticeMapper noticeNoticeMapper; + + public NoticeNoticeService(NoticeNoticeMapper noticeNoticeMapper) { + this.noticeNoticeMapper = noticeNoticeMapper; + } + + @Transactional(readOnly = true) + public int noticeNoticeListCount(NoticeNoticeListDto noticeNoticeListDto) { + return noticeNoticeMapper.noticeNoticeListCount(noticeNoticeListDto); + } + + @Transactional(readOnly = true) + public List noticeNoticeList(NoticeNoticeListDto noticeNoticeListDto) { + return noticeNoticeMapper.noticeNoticeList(noticeNoticeListDto); + } + + @Transactional(readOnly = true) + public NoticeNoticeViewVo noticeNoticeView(NoticeNoticeViewDto noticeNoticeViewDto) { + return noticeNoticeMapper.noticeNoticeView(noticeNoticeViewDto); + } + + @Transactional(readOnly = true) + public List noticeNoticeCampusList(NoticeNoticeViewDto noticeNoticeViewDto) { + return noticeNoticeMapper.noticeNoticeCampusList(noticeNoticeViewDto); + } + + @Transactional(readOnly = true) + public List noticeNoticeFileList(NoticeNoticeViewDto noticeNoticeViewDto) { + return noticeNoticeMapper.noticeNoticeFileList(noticeNoticeViewDto); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeCampusListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeCampusListItemVo.java new file mode 100644 index 0000000..a5a0f81 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeCampusListItemVo.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.bespoke.notice.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeNoticeCampusListItemVo { + private Long noticeCampusIdx; + private Long campusIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeFileListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeFileListItemVo.java new file mode 100644 index 0000000..9929319 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeFileListItemVo.java @@ -0,0 +1,19 @@ +package com.alist.api.core.modules.bespoke.notice.vo; + +import lombok.Getter; +import lombok.Setter; +import java.time.LocalDateTime; + +@Getter +@Setter +public class NoticeNoticeFileListItemVo { + private Long noticeFileIdx; + private Long noticeIdx; + private String fileOriginalName; + private String fileSaveName; + private String filePath; + private Long fileSize; + private String fileExt; + private Integer sortOrder; + private LocalDateTime createDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeListItemVo.java new file mode 100644 index 0000000..76fc486 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeListItemVo.java @@ -0,0 +1,21 @@ +package com.alist.api.core.modules.bespoke.notice.vo; + +import lombok.Getter; +import lombok.Setter; +import java.time.LocalDateTime; + +@Getter +@Setter +public class NoticeNoticeListItemVo { + private Long noticeIdx; + private String noticeType; + private String targetScope; + private String title; + private String pinYn; + private LocalDateTime startDate; + private LocalDateTime endDate; + private Integer viewCount; + private String useYn; + private LocalDateTime createDate; + private LocalDateTime updateDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeViewVo.java new file mode 100644 index 0000000..12c8664 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/notice/vo/NoticeNoticeViewVo.java @@ -0,0 +1,24 @@ +package com.alist.api.core.modules.bespoke.notice.vo; + +import lombok.Getter; +import lombok.Setter; +import java.time.LocalDateTime; + +@Getter +@Setter +public class NoticeNoticeViewVo { + private Long noticeIdx; + private String noticeType; + private String targetScope; + private String title; + private String content; + private String pinYn; + private LocalDateTime startDate; + private LocalDateTime endDate; + private Integer viewCount; + private String useYn; + private LocalDateTime createDate; + private Long createMember; + private LocalDateTime updateDate; + private Long updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserAdminMemberListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserAdminMemberListDto.java new file mode 100644 index 0000000..8a62ddb --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserAdminMemberListDto.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.bespoke.testUser.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserAdminMemberListDto extends PagingRequest { + private String keyword; + private String userRole; + private String userType; + private String dormantYn; + private String withdrawStatus; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserAdminMemberViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserAdminMemberViewDto.java new file mode 100644 index 0000000..44969f9 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserAdminMemberViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.bespoke.testUser.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserAdminMemberViewDto { + private Integer userIdx; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserLoginViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserLoginViewDto.java new file mode 100644 index 0000000..c2e24e5 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserLoginViewDto.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.bespoke.testUser.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserLoginViewDto { + private String id; + private String userType; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserSsoLoginViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserSsoLoginViewDto.java new file mode 100644 index 0000000..59d16e2 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserSsoLoginViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.bespoke.testUser.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserSsoLoginViewDto { + private String id; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserUserPasswordViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserUserPasswordViewDto.java new file mode 100644 index 0000000..b29ec95 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserUserPasswordViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.bespoke.testUser.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserUserPasswordViewDto { + private Integer userTokenIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserUserProfileViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserUserProfileViewDto.java new file mode 100644 index 0000000..9c4d1a2 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/dto/TestUserUserProfileViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.bespoke.testUser.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserUserProfileViewDto { + private Integer userTokenIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserAdminMemberMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserAdminMemberMapper.java new file mode 100644 index 0000000..5da80bf --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserAdminMemberMapper.java @@ -0,0 +1,18 @@ +package com.alist.api.core.modules.bespoke.testUser.mapper; + +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserAdminMemberListDto; +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserAdminMemberViewDto; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserAdminMemberListItemVo; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserAdminMemberViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TestUserAdminMemberMapper { + int testUserAdminMemberCount(TestUserAdminMemberListDto testUserAdminMemberListDto); + + List testUserAdminMemberList(TestUserAdminMemberListDto testUserAdminMemberListDto); + + TestUserAdminMemberViewVo testUserAdminMemberView(TestUserAdminMemberViewDto testUserAdminMemberViewDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserAuthMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserAuthMapper.java new file mode 100644 index 0000000..e8416ef --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserAuthMapper.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.bespoke.testUser.mapper; + +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserLoginViewDto; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserLoginViewVo; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TestUserAuthMapper { + TestUserLoginViewVo testUserLoginView(TestUserLoginViewDto testUserLoginViewDto); +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserSsoMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserSsoMapper.java new file mode 100644 index 0000000..8072f65 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserSsoMapper.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.bespoke.testUser.mapper; + +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserSsoLoginViewDto; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserSsoLoginViewVo; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TestUserSsoMapper { + TestUserSsoLoginViewVo testUserSsoLoginView(TestUserSsoLoginViewDto testUserSsoLoginViewDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserUserMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserUserMapper.java new file mode 100644 index 0000000..5d62e6e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/mapper/TestUserUserMapper.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.bespoke.testUser.mapper; + +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserUserPasswordViewDto; +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserUserProfileViewDto; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserUserPasswordViewVo; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserUserProfileViewVo; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TestUserUserMapper { + TestUserUserPasswordViewVo testUserUserPasswordView(TestUserUserPasswordViewDto testUserUserPasswordViewDto); + + TestUserUserProfileViewVo testUserUserProfileView(TestUserUserProfileViewDto testUserUserProfileViewDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserAdminMemberService.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserAdminMemberService.java new file mode 100644 index 0000000..6e14ef8 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserAdminMemberService.java @@ -0,0 +1,34 @@ +package com.alist.api.core.modules.bespoke.testUser.service; + +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserAdminMemberListDto; +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserAdminMemberViewDto; +import com.alist.api.core.modules.bespoke.testUser.mapper.TestUserAdminMemberMapper; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserAdminMemberListItemVo; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserAdminMemberViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +@Service +public class TestUserAdminMemberService { + private final TestUserAdminMemberMapper testUserBackOfficeMapper; + + public TestUserAdminMemberService(TestUserAdminMemberMapper testUserBackOfficeMapper) { + this.testUserBackOfficeMapper = testUserBackOfficeMapper; + } + + @Transactional(readOnly = true) + public int testUserAdminMemberCount(TestUserAdminMemberListDto testUserAdminMemberListDto) { + return testUserBackOfficeMapper.testUserAdminMemberCount(testUserAdminMemberListDto); + } + + @Transactional(readOnly = true) + public List testUserAdminMemberList(TestUserAdminMemberListDto testUserAdminMemberListDto) { + return testUserBackOfficeMapper.testUserAdminMemberList(testUserAdminMemberListDto); + } + + @Transactional(readOnly = true) + public TestUserAdminMemberViewVo testUserAdminMemberView(TestUserAdminMemberViewDto testUserAdminMemberViewDto) { + return testUserBackOfficeMapper.testUserAdminMemberView(testUserAdminMemberViewDto); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserAuthService.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserAuthService.java new file mode 100644 index 0000000..c2e80b8 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserAuthService.java @@ -0,0 +1,21 @@ +package com.alist.api.core.modules.bespoke.testUser.service; + +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserLoginViewDto; +import com.alist.api.core.modules.bespoke.testUser.mapper.TestUserAuthMapper; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserLoginViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class TestUserAuthService { + private final TestUserAuthMapper testUserAuthMapper; + + public TestUserAuthService(TestUserAuthMapper testUserAuthMapper) { + this.testUserAuthMapper = testUserAuthMapper; + } + + @Transactional(readOnly = true) + public TestUserLoginViewVo testUserLoginView(TestUserLoginViewDto testUserLoginViewDto) { + return testUserAuthMapper.testUserLoginView(testUserLoginViewDto); + } +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserSsoService.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserSsoService.java new file mode 100644 index 0000000..71c5f15 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserSsoService.java @@ -0,0 +1,21 @@ +package com.alist.api.core.modules.bespoke.testUser.service; + +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserSsoLoginViewDto; +import com.alist.api.core.modules.bespoke.testUser.mapper.TestUserSsoMapper; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserSsoLoginViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class TestUserSsoService { + private final TestUserSsoMapper testUserSsoMapper; + + public TestUserSsoService(TestUserSsoMapper testUserSsoMapper) { + this.testUserSsoMapper = testUserSsoMapper; + } + + @Transactional(readOnly = true) + public TestUserSsoLoginViewVo testUserSsoLoginView(TestUserSsoLoginViewDto testUserSsoLoginViewDto) { + return testUserSsoMapper.testUserSsoLoginView(testUserSsoLoginViewDto); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserUserService.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserUserService.java new file mode 100644 index 0000000..d07bd40 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/service/TestUserUserService.java @@ -0,0 +1,28 @@ +package com.alist.api.core.modules.bespoke.testUser.service; + +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserUserPasswordViewDto; +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserUserProfileViewDto; +import com.alist.api.core.modules.bespoke.testUser.mapper.TestUserUserMapper; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserUserPasswordViewVo; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserUserProfileViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class TestUserUserService { + private final TestUserUserMapper testUserUserMapper; + + public TestUserUserService(TestUserUserMapper testUserUserMapper) { + this.testUserUserMapper = testUserUserMapper; + } + + @Transactional(readOnly = true) + public TestUserUserPasswordViewVo testUserUserPasswordView(TestUserUserPasswordViewDto testUserUserPasswordViewDto) { + return testUserUserMapper.testUserUserPasswordView(testUserUserPasswordViewDto); + } + + @Transactional(readOnly = true) + public TestUserUserProfileViewVo testUserUserProfileView(TestUserUserProfileViewDto testUserUserProfileViewDto) { + return testUserUserMapper.testUserUserProfileView(testUserUserProfileViewDto); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserAdminMemberListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserAdminMemberListItemVo.java new file mode 100644 index 0000000..60a308f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserAdminMemberListItemVo.java @@ -0,0 +1,24 @@ +package com.alist.api.core.modules.bespoke.testUser.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class TestUserAdminMemberListItemVo { + private Integer userIdx; + private Integer userTokenIdx; + private String id; + private String email; + private String hp; + private String userRole; + private String userType; + private String dormantYn; + private LocalDateTime dormantAt; + private String withdrawStatus; + private LocalDateTime withdrawAt; + private LocalDateTime createAt; + private LocalDateTime updateAt; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserAdminMemberViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserAdminMemberViewVo.java new file mode 100644 index 0000000..d600f6c --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserAdminMemberViewVo.java @@ -0,0 +1,4 @@ +package com.alist.api.core.modules.bespoke.testUser.vo; + +public class TestUserAdminMemberViewVo extends TestUserAdminMemberListItemVo { +} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserLoginViewVo.java similarity index 52% rename from src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberAddDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserLoginViewVo.java index 99f51d2..d1691be 100644 --- a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberAddDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserLoginViewVo.java @@ -1,13 +1,15 @@ -package com.alist.api.modules.admin.member.dto; +package com.alist.api.core.modules.bespoke.testUser.vo; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class AdminMemberAddDto { +public class TestUserLoginViewVo { + private Integer userIdx; private String id; private String password; + private Integer userTokenIdx; private String userRole; private String userType; } \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserSsoLoginViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserSsoLoginViewVo.java new file mode 100644 index 0000000..d806603 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserSsoLoginViewVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.bespoke.testUser.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserSsoLoginViewVo { + private Integer userIdx; + private Integer userTokenIdx; + private String id; + private String password; + private String userRole; + private String userType; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserUserPasswordViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserUserPasswordViewVo.java new file mode 100644 index 0000000..44cf787 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserUserPasswordViewVo.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.bespoke.testUser.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserUserPasswordViewVo { + private Integer userIdx; + private String password; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserUserProfileViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserUserProfileViewVo.java new file mode 100644 index 0000000..700210a --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUser/vo/TestUserUserProfileViewVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.bespoke.testUser.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserUserProfileViewVo { + private Integer userIdx; + private String id; + private String email; + private String hp; + private String userRole; + private String userType; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenApiKeyLoginViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenApiKeyLoginViewDto.java new file mode 100644 index 0000000..4a10f74 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenApiKeyLoginViewDto.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.bespoke.testUserToken.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class TestUserTokenApiKeyLoginViewDto { + private String userApiKey; + private String userType; + private List userTypes; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenLoginCheckedViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenLoginCheckedViewDto.java new file mode 100644 index 0000000..195e70c --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenLoginCheckedViewDto.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.bespoke.testUserToken.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserTokenLoginCheckedViewDto { + private Integer userTokenIdx; + private String userType; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenRefreshViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenRefreshViewDto.java new file mode 100644 index 0000000..6f18682 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/dto/TestUserTokenRefreshViewDto.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.bespoke.testUserToken.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class TestUserTokenRefreshViewDto { + private Integer userTokenIdx; + private String userType; + private List userTypes; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/mapper/TestUserTokenAuthMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/mapper/TestUserTokenAuthMapper.java new file mode 100644 index 0000000..0c6b0ab --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/mapper/TestUserTokenAuthMapper.java @@ -0,0 +1,18 @@ +package com.alist.api.core.modules.bespoke.testUserToken.mapper; + +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenApiKeyLoginViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenLoginCheckedViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenRefreshViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenApiKeyLoginViewVo; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenLoginCheckedViewVo; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenRefreshViewVo; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TestUserTokenAuthMapper { + TestUserTokenApiKeyLoginViewVo testUserTokenApiKeyLoginView(TestUserTokenApiKeyLoginViewDto testUserTokenApiKeyLoginViewDto); + + TestUserTokenLoginCheckedViewVo testUserTokenLoginCheckedView(TestUserTokenLoginCheckedViewDto testUserTokenLoginCheckedViewDto); + + TestUserTokenRefreshViewVo testUserTokenRefreshView(TestUserTokenRefreshViewDto testUserTokenRefreshViewDto); +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/service/TestUserTokenAuthService.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/service/TestUserTokenAuthService.java new file mode 100644 index 0000000..8512a6e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/service/TestUserTokenAuthService.java @@ -0,0 +1,35 @@ +package com.alist.api.core.modules.bespoke.testUserToken.service; + +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenApiKeyLoginViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenLoginCheckedViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenRefreshViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.mapper.TestUserTokenAuthMapper; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenApiKeyLoginViewVo; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenLoginCheckedViewVo; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenRefreshViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class TestUserTokenAuthService { + private final TestUserTokenAuthMapper testUserTokenAuthMapper; + + public TestUserTokenAuthService(TestUserTokenAuthMapper testUserTokenAuthMapper) { + this.testUserTokenAuthMapper = testUserTokenAuthMapper; + } + + @Transactional(readOnly = true) + public TestUserTokenApiKeyLoginViewVo testUserTokenApiKeyLoginView(TestUserTokenApiKeyLoginViewDto testUserTokenApiKeyLoginViewDto) { + return testUserTokenAuthMapper.testUserTokenApiKeyLoginView(testUserTokenApiKeyLoginViewDto); + } + + @Transactional(readOnly = true) + public TestUserTokenLoginCheckedViewVo testUserTokenLoginCheckedView(TestUserTokenLoginCheckedViewDto testUserTokenLoginCheckedViewDto) { + return testUserTokenAuthMapper.testUserTokenLoginCheckedView(testUserTokenLoginCheckedViewDto); + } + + @Transactional(readOnly = true) + public TestUserTokenRefreshViewVo testUserTokenRefreshView(TestUserTokenRefreshViewDto testUserTokenRefreshViewDto) { + return testUserTokenAuthMapper.testUserTokenRefreshView(testUserTokenRefreshViewDto); + } +} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/front/user/dto/UserTokenDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenApiKeyLoginViewVo.java similarity index 57% rename from src/main/java/com/alist/api/modules/front/user/dto/UserTokenDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenApiKeyLoginViewVo.java index 1e5ddfa..80efa73 100644 --- a/src/main/java/com/alist/api/modules/front/user/dto/UserTokenDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenApiKeyLoginViewVo.java @@ -1,15 +1,14 @@ -package com.alist.api.modules.front.user.dto; +package com.alist.api.core.modules.bespoke.testUserToken.vo; import lombok.Getter; import lombok.Setter; -@Setter @Getter -public class UserTokenDto { - private Integer userTokenIdx; +@Setter +public class TestUserTokenApiKeyLoginViewVo { private Integer userIdx; - private String userApiKey; + private String id; + private Integer userTokenIdx; private String userRole; private String userType; - private int resultCode; -} +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenLoginCheckedViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenLoginCheckedViewVo.java new file mode 100644 index 0000000..c622606 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenLoginCheckedViewVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.bespoke.testUserToken.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserTokenLoginCheckedViewVo { + private Integer userIdx; + private String id; + private Integer userTokenIdx; + private String userRole; + private String userType; +} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/LoginVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenRefreshViewVo.java similarity index 51% rename from src/main/java/com/alist/api/modules/front/auth/vo/LoginVo.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenRefreshViewVo.java index 0f52f8c..e117a97 100644 --- a/src/main/java/com/alist/api/modules/front/auth/vo/LoginVo.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/bespoke/testUserToken/vo/TestUserTokenRefreshViewVo.java @@ -1,22 +1,15 @@ -package com.alist.api.modules.front.auth.vo; +package com.alist.api.core.modules.bespoke.testUserToken.vo; import lombok.Getter; import lombok.Setter; -import java.time.Instant; - @Getter @Setter -public class LoginVo { +public class TestUserTokenRefreshViewVo { private Integer userIdx; private String id; - private String password; - - private String accessToken; - private String refreshToken; private Integer userTokenIdx; + private String refreshToken; private String userRole; private String userType; - private Instant expiresAt; - private int resultCode; -} +} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/migration/alist/user/dto/AlistUserDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/dto/AlistUserMigrationDto.java similarity index 52% rename from src/main/java/com/alist/api/modules/migration/alist/user/dto/AlistUserDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/dto/AlistUserMigrationDto.java index ff75052..298e185 100644 --- a/src/main/java/com/alist/api/modules/migration/alist/user/dto/AlistUserDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/dto/AlistUserMigrationDto.java @@ -1,11 +1,11 @@ -package com.alist.api.modules.migration.alist.user.dto; +package com.alist.api.core.modules.migration.alist.user.dto; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class AlistUserDto { +public class AlistUserMigrationDto { private String id; private String password; } diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/mapper/AlistUserMigrationMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/mapper/AlistUserMigrationMapper.java new file mode 100644 index 0000000..57ab994 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/mapper/AlistUserMigrationMapper.java @@ -0,0 +1,12 @@ +package com.alist.api.core.modules.migration.alist.user.mapper; + +import com.alist.api.core.modules.migration.alist.user.dto.AlistUserMigrationDto; +import com.alist.api.core.modules.migration.alist.user.vo.AlistUserMigrationVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface AlistUserMigrationMapper { + List selectAlistUserMigrationList(AlistUserMigrationDto alistUserMigrationDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/service/AlistUserMigrationService.java b/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/service/AlistUserMigrationService.java new file mode 100644 index 0000000..3423eff --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/service/AlistUserMigrationService.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.migration.alist.user.service; + +import com.alist.api.core.modules.migration.alist.user.dto.AlistUserMigrationDto; +import com.alist.api.core.modules.migration.alist.user.mapper.AlistUserMigrationMapper; +import com.alist.api.core.modules.migration.alist.user.vo.AlistUserMigrationVo; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@ConditionalOnProperty( + prefix = "migration.datasource.alist", + name = "enabled", + havingValue = "true" +) +public class AlistUserMigrationService { + private final AlistUserMigrationMapper alistUserMigrationMapper; + + public AlistUserMigrationService(AlistUserMigrationMapper alistUserMigrationMapper) { + this.alistUserMigrationMapper = alistUserMigrationMapper; + } + + public List selectAlistUserMigrationList(AlistUserMigrationDto alistUserMigrationDto) { + return alistUserMigrationMapper.selectAlistUserMigrationList(alistUserMigrationDto); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/vo/AlistUserMigrationVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/vo/AlistUserMigrationVo.java new file mode 100644 index 0000000..9e8eaf4 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/migration/alist/user/vo/AlistUserMigrationVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.migration.alist.user.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AlistUserMigrationVo { + private String id; + private String name; + private String hp; + private String email; + private String type; + private String joinDate; +} diff --git a/src/main/java/com/alist/api/modules/migration/eltown/user/dto/EltownUserDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/dto/EltownUserMigrationDto.java similarity index 58% rename from src/main/java/com/alist/api/modules/migration/eltown/user/dto/EltownUserDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/dto/EltownUserMigrationDto.java index fe92110..87ad66b 100644 --- a/src/main/java/com/alist/api/modules/migration/eltown/user/dto/EltownUserDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/dto/EltownUserMigrationDto.java @@ -1,11 +1,11 @@ -package com.alist.api.modules.migration.eltown.user.dto; +package com.alist.api.core.modules.migration.eltown.user.dto; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class EltownUserDto { +public class EltownUserMigrationDto { private String id; private String password; private String md5Password; diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/mapper/EltownUserMigrationMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/mapper/EltownUserMigrationMapper.java new file mode 100644 index 0000000..1415ef5 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/mapper/EltownUserMigrationMapper.java @@ -0,0 +1,12 @@ +package com.alist.api.core.modules.migration.eltown.user.mapper; + +import com.alist.api.core.modules.migration.eltown.user.dto.EltownUserMigrationDto; +import com.alist.api.core.modules.migration.eltown.user.vo.EltownUserMigrationVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface EltownUserMigrationMapper { + List selectEltownUserMigrationList(EltownUserMigrationDto eltownUserMigrationDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/service/EltownUserMigrationService.java b/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/service/EltownUserMigrationService.java new file mode 100644 index 0000000..a08ec73 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/service/EltownUserMigrationService.java @@ -0,0 +1,30 @@ +package com.alist.api.core.modules.migration.eltown.user.service; + +import com.alist.api.core.common.utils.HashUtils; +import com.alist.api.core.modules.migration.eltown.user.dto.EltownUserMigrationDto; +import com.alist.api.core.modules.migration.eltown.user.mapper.EltownUserMigrationMapper; +import com.alist.api.core.modules.migration.eltown.user.vo.EltownUserMigrationVo; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@ConditionalOnProperty( + prefix = "migration.datasource.eltown", + name = "enabled", + havingValue = "true" +) +public class EltownUserMigrationService { + private final EltownUserMigrationMapper eltownUserMigrationMapper; + + public EltownUserMigrationService(EltownUserMigrationMapper eltownUserMigrationMapper) { + this.eltownUserMigrationMapper = eltownUserMigrationMapper; + } + + public List selectEltownUserMigrationList(EltownUserMigrationDto eltownUserMigrationDto) { + eltownUserMigrationDto.setMd5Password(HashUtils.md5(eltownUserMigrationDto.getPassword())); + + return eltownUserMigrationMapper.selectEltownUserMigrationList(eltownUserMigrationDto); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/vo/EltownUserMigrationVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/vo/EltownUserMigrationVo.java new file mode 100644 index 0000000..edebfdc --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/migration/eltown/user/vo/EltownUserMigrationVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.migration.eltown.user.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class EltownUserMigrationVo { + private String id; + private String name; + private String hp; + private String email; + private String type; + private String joinDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarAddDto.java new file mode 100644 index 0000000..1887f6e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarAddDto.java @@ -0,0 +1,20 @@ +package com.alist.api.core.modules.standard.alCalendar.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDate; + +@Getter +@Setter +public class AlCalendarAddDto { + private String yyyymmdd; + private String yyyy; + private String mm; + private String dd; + private Short weekNo; + private String weekNm; + private String holidayYn; + private String holidayNm; + private LocalDate formatDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarDeleteDto.java new file mode 100644 index 0000000..69816d3 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarDeleteDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.alCalendar.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AlCalendarDeleteDto { + private String yyyymmdd; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarListDto.java new file mode 100644 index 0000000..a25c137 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarListDto.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.alCalendar.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AlCalendarListDto extends PagingRequest { + private String yyyymmdd; + private String yyyy; + private String mm; + private String holidayYn; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarModifyDto.java new file mode 100644 index 0000000..b8f4a08 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarModifyDto.java @@ -0,0 +1,31 @@ +package com.alist.api.core.modules.standard.alCalendar.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDate; + +@Getter +@Setter +public class AlCalendarModifyDto { + private String yyyymmdd; + private String yyyy; + private String mm; + private String dd; + private Short weekNo; + private String weekNm; + private String holidayYn; + private String holidayNm; + private LocalDate formatDate; + + public boolean hasModifyValue() { + return yyyy != null + || mm != null + || dd != null + || weekNo != null + || weekNm != null + || holidayYn != null + || holidayNm != null + || formatDate != null; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarViewDto.java new file mode 100644 index 0000000..dbbfd81 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/dto/AlCalendarViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.alCalendar.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AlCalendarViewDto { + private String yyyymmdd; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/mapper/AlCalendarMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/mapper/AlCalendarMapper.java new file mode 100644 index 0000000..a74ca7a --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/mapper/AlCalendarMapper.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.standard.alCalendar.mapper; + +import com.alist.api.core.modules.standard.alCalendar.dto.AlCalendarAddDto; +import com.alist.api.core.modules.standard.alCalendar.dto.AlCalendarDeleteDto; +import com.alist.api.core.modules.standard.alCalendar.dto.AlCalendarListDto; +import com.alist.api.core.modules.standard.alCalendar.dto.AlCalendarModifyDto; +import com.alist.api.core.modules.standard.alCalendar.dto.AlCalendarViewDto; +import com.alist.api.core.modules.standard.alCalendar.vo.AlCalendarListItemVo; +import com.alist.api.core.modules.standard.alCalendar.vo.AlCalendarViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface AlCalendarMapper { + int selectAlCalendarListCount(AlCalendarListDto alCalendarListDto); + + List selectAlCalendarList(AlCalendarListDto alCalendarListDto); + + AlCalendarViewVo selectAlCalendarView(AlCalendarViewDto alCalendarViewDto); + + int insertAlCalendarAdd(AlCalendarAddDto alCalendarAddDto); + + int updateAlCalendarModify(AlCalendarModifyDto alCalendarModifyDto); + + int deleteAlCalendarDelete(AlCalendarDeleteDto alCalendarDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/service/AlCalendarService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/service/AlCalendarService.java new file mode 100644 index 0000000..10b0fa7 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/service/AlCalendarService.java @@ -0,0 +1,82 @@ +package com.alist.api.core.modules.standard.alCalendar.service; + +import com.alist.api.core.modules.standard.alCalendar.dto.AlCalendarAddDto; +import com.alist.api.core.modules.standard.alCalendar.dto.AlCalendarDeleteDto; +import com.alist.api.core.modules.standard.alCalendar.dto.AlCalendarListDto; +import com.alist.api.core.modules.standard.alCalendar.dto.AlCalendarModifyDto; +import com.alist.api.core.modules.standard.alCalendar.dto.AlCalendarViewDto; +import com.alist.api.core.modules.standard.alCalendar.mapper.AlCalendarMapper; +import com.alist.api.core.modules.standard.alCalendar.vo.AlCalendarAddVo; +import com.alist.api.core.modules.standard.alCalendar.vo.AlCalendarDeleteVo; +import com.alist.api.core.modules.standard.alCalendar.vo.AlCalendarListVo; +import com.alist.api.core.modules.standard.alCalendar.vo.AlCalendarModifyVo; +import com.alist.api.core.modules.standard.alCalendar.vo.AlCalendarViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class AlCalendarService { + private final AlCalendarMapper alCalendarMapper; + + public AlCalendarService(AlCalendarMapper alCalendarMapper) { + this.alCalendarMapper = alCalendarMapper; + } + + @Transactional(readOnly = true) + public AlCalendarListVo selectAlCalendarList(AlCalendarListDto alCalendarListDto) { + int totalCount = alCalendarMapper.selectAlCalendarListCount(alCalendarListDto); + + AlCalendarListVo alCalendarListVo = new AlCalendarListVo(); + alCalendarListVo.setAlCalendarList(alCalendarMapper.selectAlCalendarList(alCalendarListDto)); + alCalendarListVo.setPaging(alCalendarListDto, totalCount); + + return alCalendarListVo; + } + + @Transactional(readOnly = true) + public AlCalendarViewVo selectAlCalendarView(AlCalendarViewDto alCalendarViewDto) { + return alCalendarMapper.selectAlCalendarView(alCalendarViewDto); + } + + @Transactional + public AlCalendarAddVo insertAlCalendarAdd(AlCalendarAddDto alCalendarAddDto) { + int insertCount = alCalendarMapper.insertAlCalendarAdd(alCalendarAddDto); + + AlCalendarAddVo alCalendarAddVo = new AlCalendarAddVo(); + alCalendarAddVo.setAdded(insertCount > 0); + alCalendarAddVo.setYyyymmdd(alCalendarAddDto.getYyyymmdd()); + alCalendarAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return alCalendarAddVo; + } + + @Transactional + public AlCalendarModifyVo updateAlCalendarModify(AlCalendarModifyDto alCalendarModifyDto) { + AlCalendarModifyVo alCalendarModifyVo = new AlCalendarModifyVo(); + alCalendarModifyVo.setYyyymmdd(alCalendarModifyDto.getYyyymmdd()); + + if (!alCalendarModifyDto.hasModifyValue()) { + alCalendarModifyVo.setUpdated(false); + alCalendarModifyVo.setResultCode(4001); + return alCalendarModifyVo; + } + + int updateCount = alCalendarMapper.updateAlCalendarModify(alCalendarModifyDto); + alCalendarModifyVo.setUpdated(updateCount > 0); + alCalendarModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return alCalendarModifyVo; + } + + @Transactional + public AlCalendarDeleteVo deleteAlCalendarDelete(AlCalendarDeleteDto alCalendarDeleteDto) { + int deleteCount = alCalendarMapper.deleteAlCalendarDelete(alCalendarDeleteDto); + + AlCalendarDeleteVo alCalendarDeleteVo = new AlCalendarDeleteVo(); + alCalendarDeleteVo.setDeleted(deleteCount > 0); + alCalendarDeleteVo.setYyyymmdd(alCalendarDeleteDto.getYyyymmdd()); + alCalendarDeleteVo.setResultCode(deleteCount > 0 ? 2005 : 2003); + + return alCalendarDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarAddVo.java new file mode 100644 index 0000000..007754e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarAddVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.alCalendar.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AlCalendarAddVo { + private boolean added; + private String yyyymmdd; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarDeleteVo.java new file mode 100644 index 0000000..9d8d126 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarDeleteVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.alCalendar.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AlCalendarDeleteVo { + private boolean deleted; + private String yyyymmdd; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarListItemVo.java new file mode 100644 index 0000000..94d1cf5 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarListItemVo.java @@ -0,0 +1,20 @@ +package com.alist.api.core.modules.standard.alCalendar.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDate; + +@Getter +@Setter +public class AlCalendarListItemVo { + private String yyyymmdd; + private String yyyy; + private String mm; + private String dd; + private Short weekNo; + private String weekNm; + private String holidayYn; + private String holidayNm; + private LocalDate formatDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarListVo.java new file mode 100644 index 0000000..f2a4478 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.alCalendar.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class AlCalendarListVo extends PagingResponse { + private List alCalendarList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarModifyVo.java new file mode 100644 index 0000000..6719534 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarModifyVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.alCalendar.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AlCalendarModifyVo { + private boolean updated; + private String yyyymmdd; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarViewVo.java new file mode 100644 index 0000000..a6ef266 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/alCalendar/vo/AlCalendarViewVo.java @@ -0,0 +1,20 @@ +package com.alist.api.core.modules.standard.alCalendar.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDate; + +@Getter +@Setter +public class AlCalendarViewVo { + private String yyyymmdd; + private String yyyy; + private String mm; + private String dd; + private Short weekNo; + private String weekNm; + private String holidayYn; + private String holidayNm; + private LocalDate formatDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailAddDto.java new file mode 100644 index 0000000..3069147 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailAddDto.java @@ -0,0 +1,35 @@ +package com.alist.api.core.modules.standard.fileDetail.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class FileDetailAddDto { + private Long fileDetailIdx; + private Long fileMasterIdx; + private Integer fileSeq; + private String fileUuid; + private String itemKey; + private String originName; + private String saveName; + private String savePath; + private String contentType; + private String ext; + private Long sizeBytes; + private Long uploadedBytes; + private String tusUploadId; + private LocalDateTime uploadTokenExpireAt; + private String checksumSha256; + private String moveYn; + private Integer moveTryCount; + private String moveLastError; + private LocalDateTime moveLockAt; + private LocalDateTime moveDate; + private String moveLockOwner; + private Short status; + private Integer createMember; + private Integer updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailDeleteDto.java new file mode 100644 index 0000000..9463e8d --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailDeleteDto.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.standard.fileDetail.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailDeleteDto { + private Long fileDetailIdx; + private Integer updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailListDto.java new file mode 100644 index 0000000..a9c7d70 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailListDto.java @@ -0,0 +1,16 @@ +package com.alist.api.core.modules.standard.fileDetail.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailListDto extends PagingRequest { + private Long fileDetailIdx; + private Long fileMasterIdx; + private String fileUuid; + private String itemKey; + private String moveYn; + private Short status; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailModifyDto.java new file mode 100644 index 0000000..4b1502c --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailModifyDto.java @@ -0,0 +1,54 @@ +package com.alist.api.core.modules.standard.fileDetail.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class FileDetailModifyDto { + private Long fileDetailIdx; + private String itemKey; + private String originName; + private String saveName; + private String savePath; + private String contentType; + private String ext; + private Long sizeBytes; + private Long uploadedBytes; + private String tusUploadId; + private LocalDateTime uploadTokenExpireAt; + private String checksumSha256; + private String moveYn; + private Integer moveTryCount; + private String moveLastError; + private LocalDateTime moveLockAt; + private LocalDateTime moveDate; + private String moveLockOwner; + private boolean moveLockClear; + private Short status; + private Integer updateMember; + + public boolean hasModifyValue() { + return itemKey != null + || originName != null + || saveName != null + || savePath != null + || contentType != null + || ext != null + || sizeBytes != null + || uploadedBytes != null + || tusUploadId != null + || uploadTokenExpireAt != null + || checksumSha256 != null + || moveYn != null + || moveTryCount != null + || moveLastError != null + || moveLockAt != null + || moveDate != null + || moveLockOwner != null + || moveLockClear + || status != null; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailViewDto.java new file mode 100644 index 0000000..dabc73f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/dto/FileDetailViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.fileDetail.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailViewDto { + private Long fileDetailIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/mapper/FileDetailMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/mapper/FileDetailMapper.java new file mode 100644 index 0000000..3c40dff --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/mapper/FileDetailMapper.java @@ -0,0 +1,17 @@ +package com.alist.api.core.modules.standard.fileDetail.mapper; + +import com.alist.api.core.modules.standard.fileDetail.dto.*; +import com.alist.api.core.modules.standard.fileDetail.vo.*; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface FileDetailMapper { + int selectFileDetailListCount(FileDetailListDto fileDetailListDto); + List selectFileDetailList(FileDetailListDto fileDetailListDto); + FileDetailViewVo selectFileDetailView(FileDetailViewDto fileDetailViewDto); + int insertFileDetailAdd(FileDetailAddDto fileDetailAddDto); + int updateFileDetailModify(FileDetailModifyDto fileDetailModifyDto); + int updateFileDetailDelete(FileDetailDeleteDto fileDetailDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/service/FileDetailService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/service/FileDetailService.java new file mode 100644 index 0000000..19e3329 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/service/FileDetailService.java @@ -0,0 +1,75 @@ +package com.alist.api.core.modules.standard.fileDetail.service; + +import com.alist.api.core.modules.standard.fileDetail.dto.*; +import com.alist.api.core.modules.standard.fileDetail.mapper.FileDetailMapper; +import com.alist.api.core.modules.standard.fileDetail.vo.*; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class FileDetailService { + private final FileDetailMapper fileDetailMapper; + + public FileDetailService(FileDetailMapper fileDetailMapper) { + this.fileDetailMapper = fileDetailMapper; + } + + @Transactional(readOnly = true) + public FileDetailListVo selectFileDetailList(FileDetailListDto fileDetailListDto) { + int totalCount = fileDetailMapper.selectFileDetailListCount(fileDetailListDto); + + FileDetailListVo fileDetailListVo = new FileDetailListVo(); + fileDetailListVo.setFileDetailList(fileDetailMapper.selectFileDetailList(fileDetailListDto)); + fileDetailListVo.setPaging(fileDetailListDto, totalCount); + + return fileDetailListVo; + } + + @Transactional(readOnly = true) + public FileDetailViewVo selectFileDetailView(FileDetailViewDto fileDetailViewDto) { + return fileDetailMapper.selectFileDetailView(fileDetailViewDto); + } + + @Transactional + public FileDetailAddVo insertFileDetailAdd(FileDetailAddDto fileDetailAddDto) { + int insertCount = fileDetailMapper.insertFileDetailAdd(fileDetailAddDto); + + FileDetailAddVo fileDetailAddVo = new FileDetailAddVo(); + fileDetailAddVo.setAdded(insertCount > 0); + fileDetailAddVo.setFileDetailIdx(fileDetailAddDto.getFileDetailIdx()); + fileDetailAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return fileDetailAddVo; + } + + @Transactional + public FileDetailModifyVo updateFileDetailModify(FileDetailModifyDto fileDetailModifyDto) { + FileDetailModifyVo fileDetailModifyVo = new FileDetailModifyVo(); + fileDetailModifyVo.setFileDetailIdx(fileDetailModifyDto.getFileDetailIdx()); + + if (!fileDetailModifyDto.hasModifyValue()) { + fileDetailModifyVo.setUpdated(false); + fileDetailModifyVo.setResultCode(4001); + return fileDetailModifyVo; + } + + int updateCount = fileDetailMapper.updateFileDetailModify(fileDetailModifyDto); + + fileDetailModifyVo.setUpdated(updateCount > 0); + fileDetailModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return fileDetailModifyVo; + } + + @Transactional + public FileDetailDeleteVo updateFileDetailDelete(FileDetailDeleteDto fileDetailDeleteDto) { + int updateCount = fileDetailMapper.updateFileDetailDelete(fileDetailDeleteDto); + + FileDetailDeleteVo fileDetailDeleteVo = new FileDetailDeleteVo(); + fileDetailDeleteVo.setDeleted(updateCount > 0); + fileDetailDeleteVo.setFileDetailIdx(fileDetailDeleteDto.getFileDetailIdx()); + fileDetailDeleteVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return fileDetailDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailAddVo.java new file mode 100644 index 0000000..687df04 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailAddVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileDetail.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailAddVo { + private boolean added; + private Long fileDetailIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailDeleteVo.java new file mode 100644 index 0000000..7460b30 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailDeleteVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileDetail.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailDeleteVo { + private boolean deleted; + private Long fileDetailIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailListItemVo.java new file mode 100644 index 0000000..5e274af --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailListItemVo.java @@ -0,0 +1,26 @@ +package com.alist.api.core.modules.standard.fileDetail.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class FileDetailListItemVo { + private Long fileDetailIdx; + private Long fileMasterIdx; + private Integer fileSeq; + private String fileUuid; + private String itemKey; + private String originName; + private String saveName; + private String contentType; + private Long sizeBytes; + private Long uploadedBytes; + private String moveYn; + private Short status; + private LocalDateTime moveDate; + private LocalDateTime createDate; + private LocalDateTime updateDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailListVo.java new file mode 100644 index 0000000..cc7fb26 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.fileDetail.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class FileDetailListVo extends PagingResponse { + private List fileDetailList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailModifyVo.java new file mode 100644 index 0000000..9785d55 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailModifyVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileDetail.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDetailModifyVo { + private boolean updated; + private Long fileDetailIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailViewVo.java new file mode 100644 index 0000000..1ee1a18 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDetail/vo/FileDetailViewVo.java @@ -0,0 +1,37 @@ +package com.alist.api.core.modules.standard.fileDetail.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class FileDetailViewVo { + private Long fileDetailIdx; + private Long fileMasterIdx; + private Integer fileSeq; + private String fileUuid; + private String itemKey; + private String originName; + private String saveName; + private String savePath; + private String contentType; + private String ext; + private Long sizeBytes; + private Long uploadedBytes; + private String tusUploadId; + private LocalDateTime uploadTokenExpireAt; + private String checksumSha256; + private String moveYn; + private Integer moveTryCount; + private String moveLastError; + private LocalDateTime moveLockAt; + private LocalDateTime moveDate; + private String moveLockOwner; + private Short status; + private LocalDateTime createDate; + private Integer createMember; + private LocalDateTime updateDate; + private Integer updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogAddDto.java new file mode 100644 index 0000000..f102542 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogAddDto.java @@ -0,0 +1,20 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDownloadEventLogAddDto { + private Long fileDownloadEventLogIdx; + private Long fileMasterIdx; + private Long fileDetailIdx; + private String fileUuid; + private String eventType; + private Integer userIdx; + private Integer userTokenIdx; + private String clientIp; + private String userAgent; + private String referer; + private Integer createMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogDeleteDto.java new file mode 100644 index 0000000..9fffc02 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogDeleteDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDownloadEventLogDeleteDto { + private Long fileDownloadEventLogIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogListDto.java new file mode 100644 index 0000000..5395ca8 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogListDto.java @@ -0,0 +1,17 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDownloadEventLogListDto extends PagingRequest { + private Long fileDownloadEventLogIdx; + private Long fileMasterIdx; + private Long fileDetailIdx; + private String fileUuid; + private String eventType; + private Integer userIdx; + private Integer userTokenIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogModifyDto.java new file mode 100644 index 0000000..429abf6 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogModifyDto.java @@ -0,0 +1,33 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDownloadEventLogModifyDto { + private Long fileDownloadEventLogIdx; + private Long fileMasterIdx; + private Long fileDetailIdx; + private String fileUuid; + private String eventType; + private Integer userIdx; + private Integer userTokenIdx; + private String clientIp; + private String userAgent; + private String referer; + private Integer createMember; + + public boolean hasModifyValue() { + return fileMasterIdx != null + || fileDetailIdx != null + || fileUuid != null + || eventType != null + || userIdx != null + || userTokenIdx != null + || clientIp != null + || userAgent != null + || referer != null + || createMember != null; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogViewDto.java new file mode 100644 index 0000000..1056328 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/dto/FileDownloadEventLogViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDownloadEventLogViewDto { + private Long fileDownloadEventLogIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/mapper/FileDownloadEventLogMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/mapper/FileDownloadEventLogMapper.java new file mode 100644 index 0000000..ad1abef --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/mapper/FileDownloadEventLogMapper.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.mapper; + +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogAddDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogDeleteDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogListDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogModifyDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogViewDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.vo.FileDownloadEventLogListItemVo; +import com.alist.api.core.modules.standard.fileDownloadEventLog.vo.FileDownloadEventLogViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface FileDownloadEventLogMapper { + int selectFileDownloadEventLogListCount(FileDownloadEventLogListDto fileDownloadEventLogListDto); + + List selectFileDownloadEventLogList(FileDownloadEventLogListDto fileDownloadEventLogListDto); + + FileDownloadEventLogViewVo selectFileDownloadEventLogView(FileDownloadEventLogViewDto fileDownloadEventLogViewDto); + + int insertFileDownloadEventLogAdd(FileDownloadEventLogAddDto fileDownloadEventLogAddDto); + + int updateFileDownloadEventLogModify(FileDownloadEventLogModifyDto fileDownloadEventLogModifyDto); + + int updateFileDownloadEventLogDelete(FileDownloadEventLogDeleteDto fileDownloadEventLogDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/service/FileDownloadEventLogService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/service/FileDownloadEventLogService.java new file mode 100644 index 0000000..870e5a4 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/service/FileDownloadEventLogService.java @@ -0,0 +1,82 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.service; + +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogAddDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogDeleteDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogListDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogModifyDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogViewDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.mapper.FileDownloadEventLogMapper; +import com.alist.api.core.modules.standard.fileDownloadEventLog.vo.FileDownloadEventLogAddVo; +import com.alist.api.core.modules.standard.fileDownloadEventLog.vo.FileDownloadEventLogDeleteVo; +import com.alist.api.core.modules.standard.fileDownloadEventLog.vo.FileDownloadEventLogListVo; +import com.alist.api.core.modules.standard.fileDownloadEventLog.vo.FileDownloadEventLogModifyVo; +import com.alist.api.core.modules.standard.fileDownloadEventLog.vo.FileDownloadEventLogViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class FileDownloadEventLogService { + private final FileDownloadEventLogMapper fileDownloadEventLogMapper; + + public FileDownloadEventLogService(FileDownloadEventLogMapper fileDownloadEventLogMapper) { + this.fileDownloadEventLogMapper = fileDownloadEventLogMapper; + } + + @Transactional(readOnly = true) + public FileDownloadEventLogListVo selectFileDownloadEventLogList(FileDownloadEventLogListDto fileDownloadEventLogListDto) { + int totalCount = fileDownloadEventLogMapper.selectFileDownloadEventLogListCount(fileDownloadEventLogListDto); + + FileDownloadEventLogListVo fileDownloadEventLogListVo = new FileDownloadEventLogListVo(); + fileDownloadEventLogListVo.setFileDownloadEventLogList(fileDownloadEventLogMapper.selectFileDownloadEventLogList(fileDownloadEventLogListDto)); + fileDownloadEventLogListVo.setPaging(fileDownloadEventLogListDto, totalCount); + + return fileDownloadEventLogListVo; + } + + @Transactional(readOnly = true) + public FileDownloadEventLogViewVo selectFileDownloadEventLogView(FileDownloadEventLogViewDto fileDownloadEventLogViewDto) { + return fileDownloadEventLogMapper.selectFileDownloadEventLogView(fileDownloadEventLogViewDto); + } + + @Transactional + public FileDownloadEventLogAddVo insertFileDownloadEventLogAdd(FileDownloadEventLogAddDto fileDownloadEventLogAddDto) { + int insertCount = fileDownloadEventLogMapper.insertFileDownloadEventLogAdd(fileDownloadEventLogAddDto); + + FileDownloadEventLogAddVo fileDownloadEventLogAddVo = new FileDownloadEventLogAddVo(); + fileDownloadEventLogAddVo.setAdded(insertCount > 0); + fileDownloadEventLogAddVo.setFileDownloadEventLogIdx(fileDownloadEventLogAddDto.getFileDownloadEventLogIdx()); + fileDownloadEventLogAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return fileDownloadEventLogAddVo; + } + + @Transactional + public FileDownloadEventLogModifyVo updateFileDownloadEventLogModify(FileDownloadEventLogModifyDto fileDownloadEventLogModifyDto) { + FileDownloadEventLogModifyVo fileDownloadEventLogModifyVo = new FileDownloadEventLogModifyVo(); + fileDownloadEventLogModifyVo.setFileDownloadEventLogIdx(fileDownloadEventLogModifyDto.getFileDownloadEventLogIdx()); + + if (!fileDownloadEventLogModifyDto.hasModifyValue()) { + fileDownloadEventLogModifyVo.setUpdated(false); + fileDownloadEventLogModifyVo.setResultCode(4001); + return fileDownloadEventLogModifyVo; + } + + int updateCount = fileDownloadEventLogMapper.updateFileDownloadEventLogModify(fileDownloadEventLogModifyDto); + fileDownloadEventLogModifyVo.setUpdated(updateCount > 0); + fileDownloadEventLogModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return fileDownloadEventLogModifyVo; + } + + @Transactional + public FileDownloadEventLogDeleteVo updateFileDownloadEventLogDelete(FileDownloadEventLogDeleteDto fileDownloadEventLogDeleteDto) { + int updateCount = fileDownloadEventLogMapper.updateFileDownloadEventLogDelete(fileDownloadEventLogDeleteDto); + + FileDownloadEventLogDeleteVo fileDownloadEventLogDeleteVo = new FileDownloadEventLogDeleteVo(); + fileDownloadEventLogDeleteVo.setDeleted(updateCount > 0); + fileDownloadEventLogDeleteVo.setFileDownloadEventLogIdx(fileDownloadEventLogDeleteDto.getFileDownloadEventLogIdx()); + fileDownloadEventLogDeleteVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return fileDownloadEventLogDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogAddVo.java new file mode 100644 index 0000000..e4f9f23 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogAddVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDownloadEventLogAddVo { + private boolean added; + private Long fileDownloadEventLogIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogDeleteVo.java new file mode 100644 index 0000000..38c8660 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogDeleteVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDownloadEventLogDeleteVo { + private boolean deleted; + private Long fileDownloadEventLogIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogListItemVo.java new file mode 100644 index 0000000..db80b6e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogListItemVo.java @@ -0,0 +1,20 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class FileDownloadEventLogListItemVo { + private Long fileDownloadEventLogIdx; + private Long fileMasterIdx; + private Long fileDetailIdx; + private String fileUuid; + private String eventType; + private Integer userIdx; + private Integer userTokenIdx; + private String clientIp; + private LocalDateTime createDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogListVo.java new file mode 100644 index 0000000..326c43f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class FileDownloadEventLogListVo extends PagingResponse { + private List fileDownloadEventLogList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogModifyVo.java new file mode 100644 index 0000000..6533e83 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogModifyVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileDownloadEventLogModifyVo { + private boolean updated; + private Long fileDownloadEventLogIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogViewVo.java new file mode 100644 index 0000000..429d53e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileDownloadEventLog/vo/FileDownloadEventLogViewVo.java @@ -0,0 +1,23 @@ +package com.alist.api.core.modules.standard.fileDownloadEventLog.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class FileDownloadEventLogViewVo { + private Long fileDownloadEventLogIdx; + private Long fileMasterIdx; + private Long fileDetailIdx; + private String fileUuid; + private String eventType; + private Integer userIdx; + private Integer userTokenIdx; + private String clientIp; + private String userAgent; + private String referer; + private Integer createMember; + private LocalDateTime createDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterAddDto.java new file mode 100644 index 0000000..4776cf2 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterAddDto.java @@ -0,0 +1,19 @@ +package com.alist.api.core.modules.standard.fileMaster.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileMasterAddDto { + private Long fileMasterIdx; + private Integer userIdx; + private String fileCategory; + private String folderPath; + private Integer totalCount; + private Integer doneCount; + private Integer failCount; + private Short status; + private Integer createMember; + private Integer updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterDeleteDto.java new file mode 100644 index 0000000..d1fabc5 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterDeleteDto.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.standard.fileMaster.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileMasterDeleteDto { + private Long fileMasterIdx; + private Integer updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterListDto.java new file mode 100644 index 0000000..69a6e1a --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterListDto.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileMaster.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileMasterListDto extends PagingRequest { + private Long fileMasterIdx; + private Integer userIdx; + private String fileCategory; + private Short status; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterModifyDto.java new file mode 100644 index 0000000..b256280 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterModifyDto.java @@ -0,0 +1,26 @@ +package com.alist.api.core.modules.standard.fileMaster.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileMasterModifyDto { + private Long fileMasterIdx; + private String fileCategory; + private String folderPath; + private Integer totalCount; + private Integer doneCount; + private Integer failCount; + private Short status; + private Integer updateMember; + + public boolean hasModifyValue() { + return fileCategory != null + || folderPath != null + || totalCount != null + || doneCount != null + || failCount != null + || status != null; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterViewDto.java new file mode 100644 index 0000000..0910509 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/dto/FileMasterViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.fileMaster.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileMasterViewDto { + private Long fileMasterIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/mapper/FileMasterMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/mapper/FileMasterMapper.java new file mode 100644 index 0000000..77106a0 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/mapper/FileMasterMapper.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.standard.fileMaster.mapper; + +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterAddDto; +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterDeleteDto; +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterListDto; +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterModifyDto; +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterViewDto; +import com.alist.api.core.modules.standard.fileMaster.vo.FileMasterListItemVo; +import com.alist.api.core.modules.standard.fileMaster.vo.FileMasterViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface FileMasterMapper { + int selectFileMasterListCount(FileMasterListDto fileMasterListDto); + + List selectFileMasterList(FileMasterListDto fileMasterListDto); + + FileMasterViewVo selectFileMasterView(FileMasterViewDto fileMasterViewDto); + + int insertFileMasterAdd(FileMasterAddDto fileMasterAddDto); + + int updateFileMasterModify(FileMasterModifyDto fileMasterModifyDto); + + int updateFileMasterDelete(FileMasterDeleteDto fileMasterDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/service/FileMasterService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/service/FileMasterService.java new file mode 100644 index 0000000..18c6a91 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/service/FileMasterService.java @@ -0,0 +1,84 @@ +package com.alist.api.core.modules.standard.fileMaster.service; + +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterAddDto; +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterDeleteDto; +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterListDto; +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterModifyDto; +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterViewDto; +import com.alist.api.core.modules.standard.fileMaster.mapper.FileMasterMapper; +import com.alist.api.core.modules.standard.fileMaster.vo.FileMasterAddVo; +import com.alist.api.core.modules.standard.fileMaster.vo.FileMasterDeleteVo; +import com.alist.api.core.modules.standard.fileMaster.vo.FileMasterListVo; +import com.alist.api.core.modules.standard.fileMaster.vo.FileMasterModifyVo; +import com.alist.api.core.modules.standard.fileMaster.vo.FileMasterViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class FileMasterService { + private final FileMasterMapper fileMasterMapper; + + public FileMasterService(FileMasterMapper fileMasterMapper) { + this.fileMasterMapper = fileMasterMapper; + } + + @Transactional(readOnly = true) + public FileMasterListVo selectFileMasterList(FileMasterListDto fileMasterListDto) { + int totalCount = fileMasterMapper.selectFileMasterListCount(fileMasterListDto); + + FileMasterListVo fileMasterListVo = new FileMasterListVo(); + fileMasterListVo.setFileMasterList(fileMasterMapper.selectFileMasterList(fileMasterListDto)); + fileMasterListVo.setPaging(fileMasterListDto, totalCount); + + return fileMasterListVo; + } + + @Transactional(readOnly = true) + public FileMasterViewVo selectFileMasterView(FileMasterViewDto fileMasterViewDto) { + return fileMasterMapper.selectFileMasterView(fileMasterViewDto); + } + + @Transactional + public FileMasterAddVo insertFileMasterAdd(FileMasterAddDto fileMasterAddDto) { + int insertCount = fileMasterMapper.insertFileMasterAdd(fileMasterAddDto); + + FileMasterAddVo fileMasterAddVo = new FileMasterAddVo(); + fileMasterAddVo.setAdded(insertCount > 0); + fileMasterAddVo.setFileMasterIdx(fileMasterAddDto.getFileMasterIdx()); + fileMasterAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return fileMasterAddVo; + } + + @Transactional + public FileMasterModifyVo updateFileMasterModify(FileMasterModifyDto fileMasterModifyDto) { + FileMasterModifyVo fileMasterModifyVo = new FileMasterModifyVo(); + fileMasterModifyVo.setFileMasterIdx(fileMasterModifyDto.getFileMasterIdx()); + + if (!fileMasterModifyDto.hasModifyValue()) { + fileMasterModifyVo.setUpdated(false); + fileMasterModifyVo.setResultCode(4001); + + return fileMasterModifyVo; + } + + int updateCount = fileMasterMapper.updateFileMasterModify(fileMasterModifyDto); + + fileMasterModifyVo.setUpdated(updateCount > 0); + fileMasterModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return fileMasterModifyVo; + } + + @Transactional + public FileMasterDeleteVo updateFileMasterDelete(FileMasterDeleteDto fileMasterDeleteDto) { + int updateCount = fileMasterMapper.updateFileMasterDelete(fileMasterDeleteDto); + + FileMasterDeleteVo fileMasterDeleteVo = new FileMasterDeleteVo(); + fileMasterDeleteVo.setDeleted(updateCount > 0); + fileMasterDeleteVo.setFileMasterIdx(fileMasterDeleteDto.getFileMasterIdx()); + fileMasterDeleteVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return fileMasterDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterAddVo.java new file mode 100644 index 0000000..2eeb0ea --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterAddVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.fileMaster.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileMasterAddVo { + private boolean added; + private Long fileMasterIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterDeleteVo.java new file mode 100644 index 0000000..3de5f0a --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterDeleteVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.fileMaster.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileMasterDeleteVo { + private boolean deleted; + private Long fileMasterIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterListItemVo.java new file mode 100644 index 0000000..b1c8f6a --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterListItemVo.java @@ -0,0 +1,21 @@ +package com.alist.api.core.modules.standard.fileMaster.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class FileMasterListItemVo { + private Long fileMasterIdx; + private Integer userIdx; + private String fileCategory; + private String folderPath; + private Integer totalCount; + private Integer doneCount; + private Integer failCount; + private Short status; + private LocalDateTime createDate; + private LocalDateTime updateDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterListVo.java new file mode 100644 index 0000000..254de03 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.fileMaster.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class FileMasterListVo extends PagingResponse { + private List fileMasterList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterModifyVo.java new file mode 100644 index 0000000..d670b1d --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterModifyVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.fileMaster.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileMasterModifyVo { + private boolean updated; + private Long fileMasterIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterViewVo.java new file mode 100644 index 0000000..bc838ea --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileMaster/vo/FileMasterViewVo.java @@ -0,0 +1,23 @@ +package com.alist.api.core.modules.standard.fileMaster.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class FileMasterViewVo { + private Long fileMasterIdx; + private Integer userIdx; + private String fileCategory; + private String folderPath; + private Integer totalCount; + private Integer doneCount; + private Integer failCount; + private Short status; + private LocalDateTime createDate; + private Integer createMember; + private LocalDateTime updateDate; + private Integer updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogAddDto.java new file mode 100644 index 0000000..ac6c42a --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogAddDto.java @@ -0,0 +1,17 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileUploadEventLogAddDto { + private Long fileUploadEventLogIdx; + private Long fileDetailIdx; + private String eventType; + private Long offsetBytes; + private Long bytesWritten; + private Integer httpStatus; + private String message; + private String rawJson; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogDeleteDto.java new file mode 100644 index 0000000..aa80952 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogDeleteDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileUploadEventLogDeleteDto { + private Long fileUploadEventLogIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogListDto.java new file mode 100644 index 0000000..72aa9ba --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogListDto.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileUploadEventLogListDto extends PagingRequest { + private Long fileUploadEventLogIdx; + private Long fileDetailIdx; + private String eventType; + private Integer httpStatus; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogModifyDto.java new file mode 100644 index 0000000..26b4884 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogModifyDto.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileUploadEventLogModifyDto { + private Long fileUploadEventLogIdx; + private Long fileDetailIdx; + private String eventType; + private Long offsetBytes; + private Long bytesWritten; + private Integer httpStatus; + private String message; + private String rawJson; + + public boolean hasModifyValue() { + return fileDetailIdx != null + || eventType != null + || offsetBytes != null + || bytesWritten != null + || httpStatus != null + || message != null + || rawJson != null; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogViewDto.java new file mode 100644 index 0000000..6573307 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/dto/FileUploadEventLogViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileUploadEventLogViewDto { + private Long fileUploadEventLogIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/mapper/FileUploadEventLogMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/mapper/FileUploadEventLogMapper.java new file mode 100644 index 0000000..d5fe959 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/mapper/FileUploadEventLogMapper.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.mapper; + +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogAddDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogDeleteDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogListDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogModifyDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogViewDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.vo.FileUploadEventLogListItemVo; +import com.alist.api.core.modules.standard.fileUploadEventLog.vo.FileUploadEventLogViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface FileUploadEventLogMapper { + int selectFileUploadEventLogListCount(FileUploadEventLogListDto fileUploadEventLogListDto); + + List selectFileUploadEventLogList(FileUploadEventLogListDto fileUploadEventLogListDto); + + FileUploadEventLogViewVo selectFileUploadEventLogView(FileUploadEventLogViewDto fileUploadEventLogViewDto); + + int insertFileUploadEventLogAdd(FileUploadEventLogAddDto fileUploadEventLogAddDto); + + int updateFileUploadEventLogModify(FileUploadEventLogModifyDto fileUploadEventLogModifyDto); + + int deleteFileUploadEventLogDelete(FileUploadEventLogDeleteDto fileUploadEventLogDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/service/FileUploadEventLogService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/service/FileUploadEventLogService.java new file mode 100644 index 0000000..748596a --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/service/FileUploadEventLogService.java @@ -0,0 +1,82 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.service; + +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogAddDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogDeleteDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogListDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogModifyDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogViewDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.mapper.FileUploadEventLogMapper; +import com.alist.api.core.modules.standard.fileUploadEventLog.vo.FileUploadEventLogAddVo; +import com.alist.api.core.modules.standard.fileUploadEventLog.vo.FileUploadEventLogDeleteVo; +import com.alist.api.core.modules.standard.fileUploadEventLog.vo.FileUploadEventLogListVo; +import com.alist.api.core.modules.standard.fileUploadEventLog.vo.FileUploadEventLogModifyVo; +import com.alist.api.core.modules.standard.fileUploadEventLog.vo.FileUploadEventLogViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class FileUploadEventLogService { + private final FileUploadEventLogMapper fileUploadEventLogMapper; + + public FileUploadEventLogService(FileUploadEventLogMapper fileUploadEventLogMapper) { + this.fileUploadEventLogMapper = fileUploadEventLogMapper; + } + + @Transactional(readOnly = true) + public FileUploadEventLogListVo selectFileUploadEventLogList(FileUploadEventLogListDto fileUploadEventLogListDto) { + int totalCount = fileUploadEventLogMapper.selectFileUploadEventLogListCount(fileUploadEventLogListDto); + + FileUploadEventLogListVo fileUploadEventLogListVo = new FileUploadEventLogListVo(); + fileUploadEventLogListVo.setFileUploadEventLogList(fileUploadEventLogMapper.selectFileUploadEventLogList(fileUploadEventLogListDto)); + fileUploadEventLogListVo.setPaging(fileUploadEventLogListDto, totalCount); + + return fileUploadEventLogListVo; + } + + @Transactional(readOnly = true) + public FileUploadEventLogViewVo selectFileUploadEventLogView(FileUploadEventLogViewDto fileUploadEventLogViewDto) { + return fileUploadEventLogMapper.selectFileUploadEventLogView(fileUploadEventLogViewDto); + } + + @Transactional + public FileUploadEventLogAddVo insertFileUploadEventLogAdd(FileUploadEventLogAddDto fileUploadEventLogAddDto) { + int insertCount = fileUploadEventLogMapper.insertFileUploadEventLogAdd(fileUploadEventLogAddDto); + + FileUploadEventLogAddVo fileUploadEventLogAddVo = new FileUploadEventLogAddVo(); + fileUploadEventLogAddVo.setAdded(insertCount > 0); + fileUploadEventLogAddVo.setFileUploadEventLogIdx(fileUploadEventLogAddDto.getFileUploadEventLogIdx()); + fileUploadEventLogAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return fileUploadEventLogAddVo; + } + + @Transactional + public FileUploadEventLogModifyVo updateFileUploadEventLogModify(FileUploadEventLogModifyDto fileUploadEventLogModifyDto) { + FileUploadEventLogModifyVo fileUploadEventLogModifyVo = new FileUploadEventLogModifyVo(); + fileUploadEventLogModifyVo.setFileUploadEventLogIdx(fileUploadEventLogModifyDto.getFileUploadEventLogIdx()); + + if (!fileUploadEventLogModifyDto.hasModifyValue()) { + fileUploadEventLogModifyVo.setUpdated(false); + fileUploadEventLogModifyVo.setResultCode(4001); + return fileUploadEventLogModifyVo; + } + + int updateCount = fileUploadEventLogMapper.updateFileUploadEventLogModify(fileUploadEventLogModifyDto); + fileUploadEventLogModifyVo.setUpdated(updateCount > 0); + fileUploadEventLogModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return fileUploadEventLogModifyVo; + } + + @Transactional + public FileUploadEventLogDeleteVo deleteFileUploadEventLogDelete(FileUploadEventLogDeleteDto fileUploadEventLogDeleteDto) { + int deleteCount = fileUploadEventLogMapper.deleteFileUploadEventLogDelete(fileUploadEventLogDeleteDto); + + FileUploadEventLogDeleteVo fileUploadEventLogDeleteVo = new FileUploadEventLogDeleteVo(); + fileUploadEventLogDeleteVo.setDeleted(deleteCount > 0); + fileUploadEventLogDeleteVo.setFileUploadEventLogIdx(fileUploadEventLogDeleteDto.getFileUploadEventLogIdx()); + fileUploadEventLogDeleteVo.setResultCode(deleteCount > 0 ? 2005 : 2003); + + return fileUploadEventLogDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogAddVo.java new file mode 100644 index 0000000..569ddf8 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogAddVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileUploadEventLogAddVo { + private boolean added; + private Long fileUploadEventLogIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogDeleteVo.java new file mode 100644 index 0000000..a5daeda --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogDeleteVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileUploadEventLogDeleteVo { + private boolean deleted; + private Long fileUploadEventLogIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogListItemVo.java new file mode 100644 index 0000000..106c97f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogListItemVo.java @@ -0,0 +1,19 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class FileUploadEventLogListItemVo { + private Long fileUploadEventLogIdx; + private Long fileDetailIdx; + private String eventType; + private Long offsetBytes; + private Long bytesWritten; + private Integer httpStatus; + private String message; + private LocalDateTime createDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogListVo.java new file mode 100644 index 0000000..b2614c7 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class FileUploadEventLogListVo extends PagingResponse { + private List fileUploadEventLogList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogModifyVo.java new file mode 100644 index 0000000..098ff0e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogModifyVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FileUploadEventLogModifyVo { + private boolean updated; + private Long fileUploadEventLogIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogViewVo.java new file mode 100644 index 0000000..ebf4027 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/fileUploadEventLog/vo/FileUploadEventLogViewVo.java @@ -0,0 +1,20 @@ +package com.alist.api.core.modules.standard.fileUploadEventLog.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class FileUploadEventLogViewVo { + private Long fileUploadEventLogIdx; + private Long fileDetailIdx; + private String eventType; + private Long offsetBytes; + private Long bytesWritten; + private Integer httpStatus; + private String message; + private String rawJson; + private LocalDateTime createDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeAddDto.java new file mode 100644 index 0000000..cdc00c4 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeAddDto.java @@ -0,0 +1,23 @@ +package com.alist.api.core.modules.standard.notice.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class NoticeAddDto { + private Long noticeIdx; + private String noticeType; + private String targetScope; + private String title; + private String content; + private String pinYn; + private LocalDateTime startDate; + private LocalDateTime endDate; + private Integer viewCount; + private String useYn; + private Long createMember; + private Long updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeDeleteDto.java new file mode 100644 index 0000000..efad271 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeDeleteDto.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.standard.notice.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeDeleteDto { + private Long noticeIdx; + private Long updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeListDto.java new file mode 100644 index 0000000..0f9405b --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeListDto.java @@ -0,0 +1,16 @@ +package com.alist.api.core.modules.standard.notice.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeListDto extends PagingRequest { + private Long noticeIdx; + private String noticeType; + private String targetScope; + private String title; + private String pinYn; + private String useYn; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeModifyDto.java new file mode 100644 index 0000000..cca3c8a --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeModifyDto.java @@ -0,0 +1,34 @@ +package com.alist.api.core.modules.standard.notice.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class NoticeModifyDto { + private Long noticeIdx; + private String noticeType; + private String targetScope; + private String title; + private String content; + private String pinYn; + private LocalDateTime startDate; + private LocalDateTime endDate; + private Integer viewCount; + private String useYn; + private Long updateMember; + + public boolean hasModifyValue() { + return noticeType != null + || targetScope != null + || title != null + || content != null + || pinYn != null + || startDate != null + || endDate != null + || viewCount != null + || useYn != null; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeViewDto.java new file mode 100644 index 0000000..9b87a18 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/dto/NoticeViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.notice.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeViewDto { + private Long noticeIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/mapper/NoticeMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/mapper/NoticeMapper.java new file mode 100644 index 0000000..9339770 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/mapper/NoticeMapper.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.standard.notice.mapper; + +import com.alist.api.core.modules.standard.notice.dto.NoticeAddDto; +import com.alist.api.core.modules.standard.notice.dto.NoticeDeleteDto; +import com.alist.api.core.modules.standard.notice.dto.NoticeListDto; +import com.alist.api.core.modules.standard.notice.dto.NoticeModifyDto; +import com.alist.api.core.modules.standard.notice.dto.NoticeViewDto; +import com.alist.api.core.modules.standard.notice.vo.NoticeListItemVo; +import com.alist.api.core.modules.standard.notice.vo.NoticeViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface NoticeMapper { + int selectNoticeListCount(NoticeListDto noticeListDto); + + List selectNoticeList(NoticeListDto noticeListDto); + + NoticeViewVo selectNoticeView(NoticeViewDto noticeViewDto); + + int insertNoticeAdd(NoticeAddDto noticeAddDto); + + int updateNoticeModify(NoticeModifyDto noticeModifyDto); + + int updateNoticeDelete(NoticeDeleteDto noticeDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/service/NoticeService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/service/NoticeService.java new file mode 100644 index 0000000..e724acf --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/service/NoticeService.java @@ -0,0 +1,82 @@ +package com.alist.api.core.modules.standard.notice.service; + +import com.alist.api.core.modules.standard.notice.dto.NoticeAddDto; +import com.alist.api.core.modules.standard.notice.dto.NoticeDeleteDto; +import com.alist.api.core.modules.standard.notice.dto.NoticeListDto; +import com.alist.api.core.modules.standard.notice.dto.NoticeModifyDto; +import com.alist.api.core.modules.standard.notice.dto.NoticeViewDto; +import com.alist.api.core.modules.standard.notice.mapper.NoticeMapper; +import com.alist.api.core.modules.standard.notice.vo.NoticeAddVo; +import com.alist.api.core.modules.standard.notice.vo.NoticeDeleteVo; +import com.alist.api.core.modules.standard.notice.vo.NoticeListVo; +import com.alist.api.core.modules.standard.notice.vo.NoticeModifyVo; +import com.alist.api.core.modules.standard.notice.vo.NoticeViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class NoticeService { + private final NoticeMapper noticeMapper; + + public NoticeService(NoticeMapper noticeMapper) { + this.noticeMapper = noticeMapper; + } + + @Transactional(readOnly = true) + public NoticeListVo selectNoticeList(NoticeListDto noticeListDto) { + int totalCount = noticeMapper.selectNoticeListCount(noticeListDto); + + NoticeListVo noticeListVo = new NoticeListVo(); + noticeListVo.setNoticeList(noticeMapper.selectNoticeList(noticeListDto)); + noticeListVo.setPaging(noticeListDto, totalCount); + + return noticeListVo; + } + + @Transactional(readOnly = true) + public NoticeViewVo selectNoticeView(NoticeViewDto noticeViewDto) { + return noticeMapper.selectNoticeView(noticeViewDto); + } + + @Transactional + public NoticeAddVo insertNoticeAdd(NoticeAddDto noticeAddDto) { + int insertCount = noticeMapper.insertNoticeAdd(noticeAddDto); + + NoticeAddVo noticeAddVo = new NoticeAddVo(); + noticeAddVo.setAdded(insertCount > 0); + noticeAddVo.setNoticeIdx(noticeAddDto.getNoticeIdx()); + noticeAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return noticeAddVo; + } + + @Transactional + public NoticeModifyVo updateNoticeModify(NoticeModifyDto noticeModifyDto) { + NoticeModifyVo noticeModifyVo = new NoticeModifyVo(); + noticeModifyVo.setNoticeIdx(noticeModifyDto.getNoticeIdx()); + + if (!noticeModifyDto.hasModifyValue()) { + noticeModifyVo.setUpdated(false); + noticeModifyVo.setResultCode(4001); + return noticeModifyVo; + } + + int updateCount = noticeMapper.updateNoticeModify(noticeModifyDto); + noticeModifyVo.setUpdated(updateCount > 0); + noticeModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return noticeModifyVo; + } + + @Transactional + public NoticeDeleteVo updateNoticeDelete(NoticeDeleteDto noticeDeleteDto) { + int updateCount = noticeMapper.updateNoticeDelete(noticeDeleteDto); + + NoticeDeleteVo noticeDeleteVo = new NoticeDeleteVo(); + noticeDeleteVo.setDeleted(updateCount > 0); + noticeDeleteVo.setNoticeIdx(noticeDeleteDto.getNoticeIdx()); + noticeDeleteVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return noticeDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeAddVo.java new file mode 100644 index 0000000..65408c4 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeAddVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.notice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeAddVo { + private boolean added; + private Long noticeIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeDeleteVo.java new file mode 100644 index 0000000..e7b6627 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeDeleteVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.notice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeDeleteVo { + private boolean deleted; + private Long noticeIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeListItemVo.java new file mode 100644 index 0000000..5308411 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeListItemVo.java @@ -0,0 +1,22 @@ +package com.alist.api.core.modules.standard.notice.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class NoticeListItemVo { + private Long noticeIdx; + private String noticeType; + private String targetScope; + private String title; + private String pinYn; + private LocalDateTime startDate; + private LocalDateTime endDate; + private Integer viewCount; + private String useYn; + private LocalDateTime createDate; + private LocalDateTime updateDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeListVo.java new file mode 100644 index 0000000..9f8de35 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.notice.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class NoticeListVo extends PagingResponse { + private List noticeList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeModifyVo.java new file mode 100644 index 0000000..bfa3a7b --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeModifyVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.notice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeModifyVo { + private boolean updated; + private Long noticeIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeViewVo.java new file mode 100644 index 0000000..b680d30 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/notice/vo/NoticeViewVo.java @@ -0,0 +1,25 @@ +package com.alist.api.core.modules.standard.notice.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class NoticeViewVo { + private Long noticeIdx; + private String noticeType; + private String targetScope; + private String title; + private String content; + private String pinYn; + private LocalDateTime startDate; + private LocalDateTime endDate; + private Integer viewCount; + private String useYn; + private LocalDateTime createDate; + private Long createMember; + private LocalDateTime updateDate; + private Long updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusAddDto.java new file mode 100644 index 0000000..fd9bb8f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusAddDto.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.noticeCampus.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeCampusAddDto { + private Long noticeCampusIdx; + private Long noticeIdx; + private Long campusIdx; + private Long createMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusDeleteDto.java new file mode 100644 index 0000000..c2a921c --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusDeleteDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.noticeCampus.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeCampusDeleteDto { + private Long noticeCampusIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusListDto.java new file mode 100644 index 0000000..028f310 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusListDto.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.noticeCampus.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeCampusListDto extends PagingRequest { + private Long noticeCampusIdx; + private Long noticeIdx; + private Long campusIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusModifyDto.java new file mode 100644 index 0000000..1917ae3 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusModifyDto.java @@ -0,0 +1,16 @@ +package com.alist.api.core.modules.standard.noticeCampus.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeCampusModifyDto { + private Long noticeCampusIdx; + private Long noticeIdx; + private Long campusIdx; + + public boolean hasModifyValue() { + return noticeIdx != null || campusIdx != null; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusViewDto.java new file mode 100644 index 0000000..5fa82d0 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/dto/NoticeCampusViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.noticeCampus.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeCampusViewDto { + private Long noticeCampusIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/mapper/NoticeCampusMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/mapper/NoticeCampusMapper.java new file mode 100644 index 0000000..9ef70e0 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/mapper/NoticeCampusMapper.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.standard.noticeCampus.mapper; + +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusAddDto; +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusDeleteDto; +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusListDto; +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusModifyDto; +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusViewDto; +import com.alist.api.core.modules.standard.noticeCampus.vo.NoticeCampusListItemVo; +import com.alist.api.core.modules.standard.noticeCampus.vo.NoticeCampusViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface NoticeCampusMapper { + int selectNoticeCampusListCount(NoticeCampusListDto noticeCampusListDto); + + List selectNoticeCampusList(NoticeCampusListDto noticeCampusListDto); + + NoticeCampusViewVo selectNoticeCampusView(NoticeCampusViewDto noticeCampusViewDto); + + int insertNoticeCampusAdd(NoticeCampusAddDto noticeCampusAddDto); + + int updateNoticeCampusModify(NoticeCampusModifyDto noticeCampusModifyDto); + + int deleteNoticeCampusDelete(NoticeCampusDeleteDto noticeCampusDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/service/NoticeCampusService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/service/NoticeCampusService.java new file mode 100644 index 0000000..442687f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/service/NoticeCampusService.java @@ -0,0 +1,82 @@ +package com.alist.api.core.modules.standard.noticeCampus.service; + +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusAddDto; +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusDeleteDto; +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusListDto; +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusModifyDto; +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusViewDto; +import com.alist.api.core.modules.standard.noticeCampus.mapper.NoticeCampusMapper; +import com.alist.api.core.modules.standard.noticeCampus.vo.NoticeCampusAddVo; +import com.alist.api.core.modules.standard.noticeCampus.vo.NoticeCampusDeleteVo; +import com.alist.api.core.modules.standard.noticeCampus.vo.NoticeCampusListVo; +import com.alist.api.core.modules.standard.noticeCampus.vo.NoticeCampusModifyVo; +import com.alist.api.core.modules.standard.noticeCampus.vo.NoticeCampusViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class NoticeCampusService { + private final NoticeCampusMapper noticeCampusMapper; + + public NoticeCampusService(NoticeCampusMapper noticeCampusMapper) { + this.noticeCampusMapper = noticeCampusMapper; + } + + @Transactional(readOnly = true) + public NoticeCampusListVo selectNoticeCampusList(NoticeCampusListDto noticeCampusListDto) { + int totalCount = noticeCampusMapper.selectNoticeCampusListCount(noticeCampusListDto); + + NoticeCampusListVo noticeCampusListVo = new NoticeCampusListVo(); + noticeCampusListVo.setNoticeCampusList(noticeCampusMapper.selectNoticeCampusList(noticeCampusListDto)); + noticeCampusListVo.setPaging(noticeCampusListDto, totalCount); + + return noticeCampusListVo; + } + + @Transactional(readOnly = true) + public NoticeCampusViewVo selectNoticeCampusView(NoticeCampusViewDto noticeCampusViewDto) { + return noticeCampusMapper.selectNoticeCampusView(noticeCampusViewDto); + } + + @Transactional + public NoticeCampusAddVo insertNoticeCampusAdd(NoticeCampusAddDto noticeCampusAddDto) { + int insertCount = noticeCampusMapper.insertNoticeCampusAdd(noticeCampusAddDto); + + NoticeCampusAddVo noticeCampusAddVo = new NoticeCampusAddVo(); + noticeCampusAddVo.setAdded(insertCount > 0); + noticeCampusAddVo.setNoticeCampusIdx(noticeCampusAddDto.getNoticeCampusIdx()); + noticeCampusAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return noticeCampusAddVo; + } + + @Transactional + public NoticeCampusModifyVo updateNoticeCampusModify(NoticeCampusModifyDto noticeCampusModifyDto) { + NoticeCampusModifyVo noticeCampusModifyVo = new NoticeCampusModifyVo(); + noticeCampusModifyVo.setNoticeCampusIdx(noticeCampusModifyDto.getNoticeCampusIdx()); + + if (!noticeCampusModifyDto.hasModifyValue()) { + noticeCampusModifyVo.setUpdated(false); + noticeCampusModifyVo.setResultCode(4001); + return noticeCampusModifyVo; + } + + int updateCount = noticeCampusMapper.updateNoticeCampusModify(noticeCampusModifyDto); + noticeCampusModifyVo.setUpdated(updateCount > 0); + noticeCampusModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return noticeCampusModifyVo; + } + + @Transactional + public NoticeCampusDeleteVo deleteNoticeCampusDelete(NoticeCampusDeleteDto noticeCampusDeleteDto) { + int deleteCount = noticeCampusMapper.deleteNoticeCampusDelete(noticeCampusDeleteDto); + + NoticeCampusDeleteVo noticeCampusDeleteVo = new NoticeCampusDeleteVo(); + noticeCampusDeleteVo.setDeleted(deleteCount > 0); + noticeCampusDeleteVo.setNoticeCampusIdx(noticeCampusDeleteDto.getNoticeCampusIdx()); + noticeCampusDeleteVo.setResultCode(deleteCount > 0 ? 2005 : 2003); + + return noticeCampusDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusAddVo.java new file mode 100644 index 0000000..8674f48 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusAddVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.noticeCampus.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeCampusAddVo { + private boolean added; + private Long noticeCampusIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusDeleteVo.java new file mode 100644 index 0000000..4f39b9c --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusDeleteVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.noticeCampus.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeCampusDeleteVo { + private boolean deleted; + private Long noticeCampusIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusListItemVo.java new file mode 100644 index 0000000..9ae2f0d --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusListItemVo.java @@ -0,0 +1,16 @@ +package com.alist.api.core.modules.standard.noticeCampus.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class NoticeCampusListItemVo { + private Long noticeCampusIdx; + private Long noticeIdx; + private Long campusIdx; + private LocalDateTime createDate; + private Long createMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusListVo.java new file mode 100644 index 0000000..997d406 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.noticeCampus.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class NoticeCampusListVo extends PagingResponse { + private List noticeCampusList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusModifyVo.java new file mode 100644 index 0000000..f61bed4 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusModifyVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.noticeCampus.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeCampusModifyVo { + private boolean updated; + private Long noticeCampusIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusViewVo.java new file mode 100644 index 0000000..f34d84f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeCampus/vo/NoticeCampusViewVo.java @@ -0,0 +1,16 @@ +package com.alist.api.core.modules.standard.noticeCampus.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class NoticeCampusViewVo { + private Long noticeCampusIdx; + private Long noticeIdx; + private Long campusIdx; + private LocalDateTime createDate; + private Long createMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileAddDto.java new file mode 100644 index 0000000..9846001 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileAddDto.java @@ -0,0 +1,19 @@ +package com.alist.api.core.modules.standard.noticeFile.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeFileAddDto { + private Long noticeFileIdx; + private Long noticeIdx; + private String fileOriginalName; + private String fileSaveName; + private String filePath; + private Long fileSize; + private String fileExt; + private Integer sortOrder; + private Long createMember; + private Long updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileDeleteDto.java new file mode 100644 index 0000000..28c97e5 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileDeleteDto.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.standard.noticeFile.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeFileDeleteDto { + private Long noticeFileIdx; + private Long updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileListDto.java new file mode 100644 index 0000000..7c27a2d --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileListDto.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.noticeFile.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeFileListDto extends PagingRequest { + private Long noticeFileIdx; + private Long noticeIdx; + private String fileExt; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileModifyDto.java new file mode 100644 index 0000000..20fcab7 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileModifyDto.java @@ -0,0 +1,28 @@ +package com.alist.api.core.modules.standard.noticeFile.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeFileModifyDto { + private Long noticeFileIdx; + private Long noticeIdx; + private String fileOriginalName; + private String fileSaveName; + private String filePath; + private Long fileSize; + private String fileExt; + private Integer sortOrder; + private Long updateMember; + + public boolean hasModifyValue() { + return noticeIdx != null + || fileOriginalName != null + || fileSaveName != null + || filePath != null + || fileSize != null + || fileExt != null + || sortOrder != null; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileViewDto.java new file mode 100644 index 0000000..6658385 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/dto/NoticeFileViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.noticeFile.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeFileViewDto { + private Long noticeFileIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/mapper/NoticeFileMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/mapper/NoticeFileMapper.java new file mode 100644 index 0000000..1078845 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/mapper/NoticeFileMapper.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.standard.noticeFile.mapper; + +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileAddDto; +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileDeleteDto; +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileListDto; +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileModifyDto; +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileViewDto; +import com.alist.api.core.modules.standard.noticeFile.vo.NoticeFileListItemVo; +import com.alist.api.core.modules.standard.noticeFile.vo.NoticeFileViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface NoticeFileMapper { + int selectNoticeFileListCount(NoticeFileListDto noticeFileListDto); + + List selectNoticeFileList(NoticeFileListDto noticeFileListDto); + + NoticeFileViewVo selectNoticeFileView(NoticeFileViewDto noticeFileViewDto); + + int insertNoticeFileAdd(NoticeFileAddDto noticeFileAddDto); + + int updateNoticeFileModify(NoticeFileModifyDto noticeFileModifyDto); + + int updateNoticeFileDelete(NoticeFileDeleteDto noticeFileDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/service/NoticeFileService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/service/NoticeFileService.java new file mode 100644 index 0000000..fc54a7d --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/service/NoticeFileService.java @@ -0,0 +1,82 @@ +package com.alist.api.core.modules.standard.noticeFile.service; + +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileAddDto; +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileDeleteDto; +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileListDto; +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileModifyDto; +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileViewDto; +import com.alist.api.core.modules.standard.noticeFile.mapper.NoticeFileMapper; +import com.alist.api.core.modules.standard.noticeFile.vo.NoticeFileAddVo; +import com.alist.api.core.modules.standard.noticeFile.vo.NoticeFileDeleteVo; +import com.alist.api.core.modules.standard.noticeFile.vo.NoticeFileListVo; +import com.alist.api.core.modules.standard.noticeFile.vo.NoticeFileModifyVo; +import com.alist.api.core.modules.standard.noticeFile.vo.NoticeFileViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class NoticeFileService { + private final NoticeFileMapper noticeFileMapper; + + public NoticeFileService(NoticeFileMapper noticeFileMapper) { + this.noticeFileMapper = noticeFileMapper; + } + + @Transactional(readOnly = true) + public NoticeFileListVo selectNoticeFileList(NoticeFileListDto noticeFileListDto) { + int totalCount = noticeFileMapper.selectNoticeFileListCount(noticeFileListDto); + + NoticeFileListVo noticeFileListVo = new NoticeFileListVo(); + noticeFileListVo.setNoticeFileList(noticeFileMapper.selectNoticeFileList(noticeFileListDto)); + noticeFileListVo.setPaging(noticeFileListDto, totalCount); + + return noticeFileListVo; + } + + @Transactional(readOnly = true) + public NoticeFileViewVo selectNoticeFileView(NoticeFileViewDto noticeFileViewDto) { + return noticeFileMapper.selectNoticeFileView(noticeFileViewDto); + } + + @Transactional + public NoticeFileAddVo insertNoticeFileAdd(NoticeFileAddDto noticeFileAddDto) { + int insertCount = noticeFileMapper.insertNoticeFileAdd(noticeFileAddDto); + + NoticeFileAddVo noticeFileAddVo = new NoticeFileAddVo(); + noticeFileAddVo.setAdded(insertCount > 0); + noticeFileAddVo.setNoticeFileIdx(noticeFileAddDto.getNoticeFileIdx()); + noticeFileAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return noticeFileAddVo; + } + + @Transactional + public NoticeFileModifyVo updateNoticeFileModify(NoticeFileModifyDto noticeFileModifyDto) { + NoticeFileModifyVo noticeFileModifyVo = new NoticeFileModifyVo(); + noticeFileModifyVo.setNoticeFileIdx(noticeFileModifyDto.getNoticeFileIdx()); + + if (!noticeFileModifyDto.hasModifyValue()) { + noticeFileModifyVo.setUpdated(false); + noticeFileModifyVo.setResultCode(4001); + return noticeFileModifyVo; + } + + int updateCount = noticeFileMapper.updateNoticeFileModify(noticeFileModifyDto); + noticeFileModifyVo.setUpdated(updateCount > 0); + noticeFileModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return noticeFileModifyVo; + } + + @Transactional + public NoticeFileDeleteVo updateNoticeFileDelete(NoticeFileDeleteDto noticeFileDeleteDto) { + int updateCount = noticeFileMapper.updateNoticeFileDelete(noticeFileDeleteDto); + + NoticeFileDeleteVo noticeFileDeleteVo = new NoticeFileDeleteVo(); + noticeFileDeleteVo.setDeleted(updateCount > 0); + noticeFileDeleteVo.setNoticeFileIdx(noticeFileDeleteDto.getNoticeFileIdx()); + noticeFileDeleteVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return noticeFileDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileAddVo.java new file mode 100644 index 0000000..340216d --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileAddVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.noticeFile.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeFileAddVo { + private boolean added; + private Long noticeFileIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileDeleteVo.java new file mode 100644 index 0000000..4860059 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileDeleteVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.noticeFile.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeFileDeleteVo { + private boolean deleted; + private Long noticeFileIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeFileDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileListItemVo.java similarity index 64% rename from src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeFileDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileListItemVo.java index 1e6bd46..06b1733 100644 --- a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeFileDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileListItemVo.java @@ -1,11 +1,12 @@ -package com.alist.api.modules.admin.notice.dto; +package com.alist.api.core.modules.standard.noticeFile.vo; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class AdminNoticeFileDto { +public class NoticeFileListItemVo { + private Long noticeFileIdx; private Long noticeIdx; private String fileOriginalName; private String fileSaveName; @@ -13,6 +14,4 @@ public class AdminNoticeFileDto { private Long fileSize; private String fileExt; private Integer sortOrder; - private Integer createMember; - private Integer updateMember; } diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileListVo.java new file mode 100644 index 0000000..967ffe1 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.noticeFile.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class NoticeFileListVo extends PagingResponse { + private List noticeFileList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileModifyVo.java new file mode 100644 index 0000000..409cc3c --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileModifyVo.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.noticeFile.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NoticeFileModifyVo { + private boolean updated; + private Long noticeFileIdx; + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileViewVo.java new file mode 100644 index 0000000..0d649d4 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/noticeFile/vo/NoticeFileViewVo.java @@ -0,0 +1,23 @@ +package com.alist.api.core.modules.standard.noticeFile.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class NoticeFileViewVo { + private Long noticeFileIdx; + private Long noticeIdx; + private String fileOriginalName; + private String fileSaveName; + private String filePath; + private Long fileSize; + private String fileExt; + private Integer sortOrder; + private LocalDateTime createDate; + private Long createMember; + private LocalDateTime updateDate; + private Long updateMember; +} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobAddDto.java similarity index 78% rename from src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleAddDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobAddDto.java index 2de263d..d45429c 100644 --- a/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleAddDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobAddDto.java @@ -1,11 +1,11 @@ -package com.alist.api.modules.admin.schedule.dto; +package com.alist.api.core.modules.standard.scheduleJob.dto; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class AdminScheduleAddDto { +public class ScheduleJobAddDto { private Long scheduleJobIdx; private String jobName; private String jobGroup; diff --git a/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobDeleteDto.java similarity index 56% rename from src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleDeleteDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobDeleteDto.java index b8deab8..51fbb4c 100644 --- a/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleDeleteDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobDeleteDto.java @@ -1,11 +1,11 @@ -package com.alist.api.modules.admin.schedule.dto; +package com.alist.api.core.modules.standard.scheduleJob.dto; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class AdminScheduleDeleteDto { +public class ScheduleJobDeleteDto { private Long scheduleJobIdx; private Integer updateMember; } diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobListDto.java new file mode 100644 index 0000000..3721724 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobListDto.java @@ -0,0 +1,16 @@ +package com.alist.api.core.modules.standard.scheduleJob.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobListDto extends PagingRequest { + private Long scheduleJobIdx; + private String keyword; + private String jobName; + private String jobGroup; + private Short lastStatus; + private String useYn; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobModifyDto.java new file mode 100644 index 0000000..7974ce8 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobModifyDto.java @@ -0,0 +1,46 @@ +package com.alist.api.core.modules.standard.scheduleJob.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class ScheduleJobModifyDto { + private Long scheduleJobIdx; + private String jobName; + private String jobGroup; + private String jobDescription; + private String cronExpr; + private String timezoneId; + private Integer maxRunningSeconds; + private String useYn; + private LocalDateTime lastRunDate; + private LocalDateTime lastEndDate; + private LocalDateTime lastSuccessDate; + private Short lastStatus; + private String lastMessage; + private LocalDateTime runLockDate; + private String runLockOwner; + private boolean runLockClear; + private Integer updateMember; + + public boolean hasModifyValue() { + return jobName != null + || jobGroup != null + || jobDescription != null + || cronExpr != null + || timezoneId != null + || maxRunningSeconds != null + || useYn != null + || lastRunDate != null + || lastEndDate != null + || lastSuccessDate != null + || lastStatus != null + || lastMessage != null + || runLockDate != null + || runLockOwner != null + || runLockClear; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobViewDto.java new file mode 100644 index 0000000..3161514 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/dto/ScheduleJobViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.scheduleJob.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobViewDto { + private Long scheduleJobIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/mapper/ScheduleJobMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/mapper/ScheduleJobMapper.java new file mode 100644 index 0000000..6455d96 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/mapper/ScheduleJobMapper.java @@ -0,0 +1,31 @@ +package com.alist.api.core.modules.standard.scheduleJob.mapper; + +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobAddDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobDeleteDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobListDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobModifyDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobViewDto; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobListItemVo; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ScheduleJobMapper { + int selectScheduleJobListCount(ScheduleJobListDto scheduleJobListDto); + + List selectScheduleJobList(ScheduleJobListDto scheduleJobListDto); + + ScheduleJobViewVo selectScheduleJobView(ScheduleJobViewDto scheduleJobViewDto); + + int selectScheduleJobDuplicateCount(ScheduleJobAddDto scheduleJobAddDto); + + int selectScheduleJobModifyDuplicateCount(ScheduleJobModifyDto scheduleJobModifyDto); + + int insertScheduleJobAdd(ScheduleJobAddDto scheduleJobAddDto); + + int updateScheduleJobModify(ScheduleJobModifyDto scheduleJobModifyDto); + + int updateScheduleJobDelete(ScheduleJobDeleteDto scheduleJobDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/service/ScheduleJobService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/service/ScheduleJobService.java new file mode 100644 index 0000000..b1b101f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/service/ScheduleJobService.java @@ -0,0 +1,94 @@ +package com.alist.api.core.modules.standard.scheduleJob.service; + +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobAddDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobDeleteDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobListDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobModifyDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobViewDto; +import com.alist.api.core.modules.standard.scheduleJob.mapper.ScheduleJobMapper; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobAddVo; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobDeleteVo; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobListVo; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobModifyVo; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class ScheduleJobService { + private final ScheduleJobMapper scheduleJobMapper; + + public ScheduleJobService(ScheduleJobMapper scheduleJobMapper) { + this.scheduleJobMapper = scheduleJobMapper; + } + + @Transactional(readOnly = true) + public ScheduleJobListVo selectScheduleJobList(ScheduleJobListDto scheduleJobListDto) { + int totalCount = scheduleJobMapper.selectScheduleJobListCount(scheduleJobListDto); + + ScheduleJobListVo scheduleJobListVo = new ScheduleJobListVo(); + scheduleJobListVo.setScheduleJobList(scheduleJobMapper.selectScheduleJobList(scheduleJobListDto)); + scheduleJobListVo.setPaging(scheduleJobListDto, totalCount); + + return scheduleJobListVo; + } + + @Transactional(readOnly = true) + public ScheduleJobViewVo selectScheduleJobView(ScheduleJobViewDto scheduleJobViewDto) { + return scheduleJobMapper.selectScheduleJobView(scheduleJobViewDto); + } + + @Transactional(readOnly = true) + public int selectScheduleJobDuplicateCount(ScheduleJobAddDto scheduleJobAddDto) { + return scheduleJobMapper.selectScheduleJobDuplicateCount(scheduleJobAddDto); + } + + @Transactional(readOnly = true) + public int selectScheduleJobModifyDuplicateCount(ScheduleJobModifyDto scheduleJobModifyDto) { + return scheduleJobMapper.selectScheduleJobModifyDuplicateCount(scheduleJobModifyDto); + } + + @Transactional + public ScheduleJobAddVo insertScheduleJobAdd(ScheduleJobAddDto scheduleJobAddDto) { + int insertCount = scheduleJobMapper.insertScheduleJobAdd(scheduleJobAddDto); + + ScheduleJobAddVo scheduleJobAddVo = new ScheduleJobAddVo(); + scheduleJobAddVo.setAdded(insertCount > 0); + scheduleJobAddVo.setScheduleJobIdx(scheduleJobAddDto.getScheduleJobIdx()); + scheduleJobAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return scheduleJobAddVo; + } + + @Transactional + public ScheduleJobModifyVo updateScheduleJobModify(ScheduleJobModifyDto scheduleJobModifyDto) { + ScheduleJobModifyVo scheduleJobModifyVo = new ScheduleJobModifyVo(); + scheduleJobModifyVo.setScheduleJobIdx(scheduleJobModifyDto.getScheduleJobIdx()); + + if (!scheduleJobModifyDto.hasModifyValue()) { + scheduleJobModifyVo.setUpdated(false); + scheduleJobModifyVo.setResultCode(4001); + + return scheduleJobModifyVo; + } + + int updateCount = scheduleJobMapper.updateScheduleJobModify(scheduleJobModifyDto); + + scheduleJobModifyVo.setUpdated(updateCount > 0); + scheduleJobModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return scheduleJobModifyVo; + } + + @Transactional + public ScheduleJobDeleteVo updateScheduleJobDelete(ScheduleJobDeleteDto scheduleJobDeleteDto) { + int updateCount = scheduleJobMapper.updateScheduleJobDelete(scheduleJobDeleteDto); + + ScheduleJobDeleteVo scheduleJobDeleteVo = new ScheduleJobDeleteVo(); + scheduleJobDeleteVo.setDeleted(updateCount > 0); + scheduleJobDeleteVo.setScheduleJobIdx(scheduleJobDeleteDto.getScheduleJobIdx()); + scheduleJobDeleteVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return scheduleJobDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobAddVo.java new file mode 100644 index 0000000..f45d693 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobAddVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.scheduleJob.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobAddVo { + private boolean added; + private Long scheduleJobIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobDeleteVo.java new file mode 100644 index 0000000..a600c77 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobDeleteVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.scheduleJob.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobDeleteVo { + private boolean deleted; + private Long scheduleJobIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobListItemVo.java new file mode 100644 index 0000000..8e947d9 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobListItemVo.java @@ -0,0 +1,28 @@ +package com.alist.api.core.modules.standard.scheduleJob.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class ScheduleJobListItemVo { + private Long scheduleJobIdx; + private String jobName; + private String jobGroup; + private String jobDescription; + private String cronExpr; + private String timezoneId; + private Integer maxRunningSeconds; + private LocalDateTime lastRunDate; + private LocalDateTime lastEndDate; + private LocalDateTime lastSuccessDate; + private Short lastStatus; + private String lastMessage; + private LocalDateTime runLockDate; + private String runLockOwner; + private String useYn; + private LocalDateTime createDate; + private LocalDateTime updateDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobListVo.java new file mode 100644 index 0000000..97eb2a0 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.scheduleJob.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class ScheduleJobListVo extends PagingResponse { + private List scheduleJobList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobModifyVo.java new file mode 100644 index 0000000..79d15cf --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobModifyVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.scheduleJob.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobModifyVo { + private boolean updated; + private Long scheduleJobIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobViewVo.java new file mode 100644 index 0000000..e3cdbfb --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJob/vo/ScheduleJobViewVo.java @@ -0,0 +1,30 @@ +package com.alist.api.core.modules.standard.scheduleJob.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class ScheduleJobViewVo { + private Long scheduleJobIdx; + private String jobName; + private String jobGroup; + private String jobDescription; + private String cronExpr; + private String timezoneId; + private Integer maxRunningSeconds; + private LocalDateTime lastRunDate; + private LocalDateTime lastEndDate; + private LocalDateTime lastSuccessDate; + private Short lastStatus; + private String lastMessage; + private LocalDateTime runLockDate; + private String runLockOwner; + private String useYn; + private LocalDateTime createDate; + private Integer createMember; + private LocalDateTime updateDate; + private Integer updateMember; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogAddDto.java new file mode 100644 index 0000000..440ceb1 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogAddDto.java @@ -0,0 +1,23 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class ScheduleJobLogAddDto { + private Long scheduleJobLogIdx; + private Long scheduleJobIdx; + private String runId; + private String jobName; + private LocalDateTime startDate; + private Integer targetCount; + private Integer successCount; + private Integer failCount; + private String message; + private String errorMessage; + private String lockOwner; + private Short status; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogDeleteDto.java new file mode 100644 index 0000000..3218408 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogDeleteDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobLogDeleteDto { + private Long scheduleJobLogIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogListDto.java new file mode 100644 index 0000000..e3fe4e3 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogListDto.java @@ -0,0 +1,17 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobLogListDto extends PagingRequest { + private Long scheduleJobLogIdx; + private Long scheduleJobIdx; + private String runId; + private String jobName; + private Short status; + private String startDateFrom; + private String startDateTo; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogModifyDto.java new file mode 100644 index 0000000..a7791b2 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogModifyDto.java @@ -0,0 +1,31 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class ScheduleJobLogModifyDto { + private Long scheduleJobLogIdx; + private LocalDateTime endDate; + private Integer targetCount; + private Integer successCount; + private Integer failCount; + private String message; + private String errorMessage; + private String lockOwner; + private Short status; + + public boolean hasModifyValue() { + return endDate != null + || targetCount != null + || successCount != null + || failCount != null + || message != null + || errorMessage != null + || lockOwner != null + || status != null; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogViewDto.java new file mode 100644 index 0000000..90865dd --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/dto/ScheduleJobLogViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobLogViewDto { + private Long scheduleJobLogIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/mapper/ScheduleJobLogMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/mapper/ScheduleJobLogMapper.java new file mode 100644 index 0000000..69ad10c --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/mapper/ScheduleJobLogMapper.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.mapper; + +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogAddDto; +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogDeleteDto; +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogListDto; +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogModifyDto; +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogViewDto; +import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogListItemVo; +import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ScheduleJobLogMapper { + int selectScheduleJobLogListCount(ScheduleJobLogListDto scheduleJobLogListDto); + + List selectScheduleJobLogList(ScheduleJobLogListDto scheduleJobLogListDto); + + ScheduleJobLogViewVo selectScheduleJobLogView(ScheduleJobLogViewDto scheduleJobLogViewDto); + + int insertScheduleJobLogAdd(ScheduleJobLogAddDto scheduleJobLogAddDto); + + int updateScheduleJobLogModify(ScheduleJobLogModifyDto scheduleJobLogModifyDto); + + int deleteScheduleJobLogDelete(ScheduleJobLogDeleteDto scheduleJobLogDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/service/ScheduleJobLogService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/service/ScheduleJobLogService.java new file mode 100644 index 0000000..016f1ab --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/service/ScheduleJobLogService.java @@ -0,0 +1,84 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.service; + +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogAddDto; +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogDeleteDto; +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogListDto; +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogModifyDto; +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogViewDto; +import com.alist.api.core.modules.standard.scheduleJobLog.mapper.ScheduleJobLogMapper; +import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogAddVo; +import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogDeleteVo; +import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogListVo; +import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogModifyVo; +import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class ScheduleJobLogService { + private final ScheduleJobLogMapper scheduleJobLogMapper; + + public ScheduleJobLogService(ScheduleJobLogMapper scheduleJobLogMapper) { + this.scheduleJobLogMapper = scheduleJobLogMapper; + } + + @Transactional(readOnly = true) + public ScheduleJobLogListVo selectScheduleJobLogList(ScheduleJobLogListDto scheduleJobLogListDto) { + int totalCount = scheduleJobLogMapper.selectScheduleJobLogListCount(scheduleJobLogListDto); + + ScheduleJobLogListVo scheduleJobLogListVo = new ScheduleJobLogListVo(); + scheduleJobLogListVo.setScheduleJobLogList(scheduleJobLogMapper.selectScheduleJobLogList(scheduleJobLogListDto)); + scheduleJobLogListVo.setPaging(scheduleJobLogListDto, totalCount); + + return scheduleJobLogListVo; + } + + @Transactional(readOnly = true) + public ScheduleJobLogViewVo selectScheduleJobLogView(ScheduleJobLogViewDto scheduleJobLogViewDto) { + return scheduleJobLogMapper.selectScheduleJobLogView(scheduleJobLogViewDto); + } + + @Transactional + public ScheduleJobLogAddVo insertScheduleJobLogAdd(ScheduleJobLogAddDto scheduleJobLogAddDto) { + int insertCount = scheduleJobLogMapper.insertScheduleJobLogAdd(scheduleJobLogAddDto); + + ScheduleJobLogAddVo scheduleJobLogAddVo = new ScheduleJobLogAddVo(); + scheduleJobLogAddVo.setAdded(insertCount > 0); + scheduleJobLogAddVo.setScheduleJobLogIdx(scheduleJobLogAddDto.getScheduleJobLogIdx()); + scheduleJobLogAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return scheduleJobLogAddVo; + } + + @Transactional + public ScheduleJobLogModifyVo updateScheduleJobLogModify(ScheduleJobLogModifyDto scheduleJobLogModifyDto) { + ScheduleJobLogModifyVo scheduleJobLogModifyVo = new ScheduleJobLogModifyVo(); + scheduleJobLogModifyVo.setScheduleJobLogIdx(scheduleJobLogModifyDto.getScheduleJobLogIdx()); + + if (!scheduleJobLogModifyDto.hasModifyValue()) { + scheduleJobLogModifyVo.setUpdated(false); + scheduleJobLogModifyVo.setResultCode(4001); + + return scheduleJobLogModifyVo; + } + + int updateCount = scheduleJobLogMapper.updateScheduleJobLogModify(scheduleJobLogModifyDto); + + scheduleJobLogModifyVo.setUpdated(updateCount > 0); + scheduleJobLogModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return scheduleJobLogModifyVo; + } + + @Transactional + public ScheduleJobLogDeleteVo deleteScheduleJobLogDelete(ScheduleJobLogDeleteDto scheduleJobLogDeleteDto) { + int deleteCount = scheduleJobLogMapper.deleteScheduleJobLogDelete(scheduleJobLogDeleteDto); + + ScheduleJobLogDeleteVo scheduleJobLogDeleteVo = new ScheduleJobLogDeleteVo(); + scheduleJobLogDeleteVo.setDeleted(deleteCount > 0); + scheduleJobLogDeleteVo.setScheduleJobLogIdx(scheduleJobLogDeleteDto.getScheduleJobLogIdx()); + scheduleJobLogDeleteVo.setResultCode(deleteCount > 0 ? 2005 : 2003); + + return scheduleJobLogDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogAddVo.java new file mode 100644 index 0000000..3dea3cc --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogAddVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobLogAddVo { + private boolean added; + private Long scheduleJobLogIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogDeleteVo.java new file mode 100644 index 0000000..9b1aff0 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogDeleteVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobLogDeleteVo { + private boolean deleted; + private Long scheduleJobLogIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogListItemVo.java new file mode 100644 index 0000000..6795bc3 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogListItemVo.java @@ -0,0 +1,25 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class ScheduleJobLogListItemVo { + private Long scheduleJobLogIdx; + private Long scheduleJobIdx; + private String runId; + private String jobName; + private LocalDateTime startDate; + private LocalDateTime endDate; + private Integer targetCount; + private Integer successCount; + private Integer failCount; + private String message; + private String errorMessage; + private String lockOwner; + private Short status; + private LocalDateTime createDate; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogListVo.java new file mode 100644 index 0000000..8c15a5b --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class ScheduleJobLogListVo extends PagingResponse { + private List scheduleJobLogList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogModifyVo.java new file mode 100644 index 0000000..0e61cec --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogModifyVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ScheduleJobLogModifyVo { + private boolean updated; + private Long scheduleJobLogIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogViewVo.java new file mode 100644 index 0000000..3f22989 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/scheduleJobLog/vo/ScheduleJobLogViewVo.java @@ -0,0 +1,25 @@ +package com.alist.api.core.modules.standard.scheduleJobLog.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class ScheduleJobLogViewVo { + private Long scheduleJobLogIdx; + private Long scheduleJobIdx; + private String runId; + private String jobName; + private LocalDateTime startDate; + private LocalDateTime endDate; + private Integer targetCount; + private Integer successCount; + private Integer failCount; + private String message; + private String errorMessage; + private String lockOwner; + private Short status; + private LocalDateTime createDate; +} diff --git a/src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientAddDto.java similarity index 76% rename from src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoAddDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientAddDto.java index ac7f2e4..97bb953 100644 --- a/src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoAddDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientAddDto.java @@ -1,11 +1,11 @@ -package com.alist.api.modules.admin.sso.dto; +package com.alist.api.core.modules.standard.ssoClient.dto; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class AdminSsoAddDto { +public class SsoClientAddDto { private Integer ssoClientIdx; private String clientId; private String clientName; diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientAuthorizeViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientAuthorizeViewDto.java new file mode 100644 index 0000000..ae86708 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientAuthorizeViewDto.java @@ -0,0 +1,11 @@ +package com.alist.api.core.modules.standard.ssoClient.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SsoClientAuthorizeViewDto { + private String clientId; + private String redirectUri; +} diff --git a/src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientDeleteDto.java similarity index 56% rename from src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoDeleteDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientDeleteDto.java index c935a77..67dbf93 100644 --- a/src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoDeleteDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientDeleteDto.java @@ -1,11 +1,11 @@ -package com.alist.api.modules.admin.sso.dto; +package com.alist.api.core.modules.standard.ssoClient.dto; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class AdminSsoDeleteDto { +public class SsoClientDeleteDto { private Integer ssoClientIdx; private Integer updateMember; -} +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientListDto.java new file mode 100644 index 0000000..88f7254 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientListDto.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.ssoClient.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SsoClientListDto extends PagingRequest { + private Integer ssoClientIdx; + private String keyword; + private String clientId; + private String clientName; + private String useYn; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientModifyDto.java new file mode 100644 index 0000000..8adaf9a --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientModifyDto.java @@ -0,0 +1,24 @@ +package com.alist.api.core.modules.standard.ssoClient.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SsoClientModifyDto { + private Integer ssoClientIdx; + private String clientId; + private String clientName; + private String redirectUri; + private String memo; + private String useYn; + private Integer updateMember; + + public boolean hasModifyValue() { + return clientId != null + || clientName != null + || redirectUri != null + || memo != null + || useYn != null; + } +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientViewDto.java new file mode 100644 index 0000000..c774753 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/dto/SsoClientViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.ssoClient.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SsoClientViewDto { + private Integer ssoClientIdx; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/mapper/SsoClientMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/mapper/SsoClientMapper.java new file mode 100644 index 0000000..d40b3a4 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/mapper/SsoClientMapper.java @@ -0,0 +1,28 @@ +package com.alist.api.core.modules.standard.ssoClient.mapper; + +import com.alist.api.core.modules.standard.ssoClient.dto.*; +import com.alist.api.core.modules.standard.ssoClient.vo.*; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface SsoClientMapper { + int selectSsoClientListCount(SsoClientListDto ssoClientListDto); + + List selectSsoClientList(SsoClientListDto ssoClientListDto); + + SsoClientViewVo selectSsoClientView(SsoClientViewDto ssoClientViewDto); + + SsoClientAuthorizeViewVo selectSsoClientAuthorizeView(SsoClientAuthorizeViewDto ssoClientAuthorizeViewDto); + + int selectSsoClientDuplicateCount(String clientId); + + int selectSsoClientModifyDuplicateCount(SsoClientModifyDto ssoClientModifyDto); + + int insertSsoClientAdd(SsoClientAddDto ssoClientAddDto); + + int updateSsoClientModify(SsoClientModifyDto ssoClientModifyDto); + + int updateSsoClientDelete(SsoClientDeleteDto ssoClientDeleteDto); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/service/SsoClientService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/service/SsoClientService.java new file mode 100644 index 0000000..8be860a --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/service/SsoClientService.java @@ -0,0 +1,103 @@ +package com.alist.api.core.modules.standard.ssoClient.service; + +import com.alist.api.core.modules.standard.ssoClient.dto.*; +import com.alist.api.core.modules.standard.ssoClient.mapper.SsoClientMapper; +import com.alist.api.core.modules.standard.ssoClient.vo.*; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class SsoClientService { + private final SsoClientMapper ssoClientMapper; + + public SsoClientService(SsoClientMapper ssoClientMapper) { + this.ssoClientMapper = ssoClientMapper; + } + + @Transactional(readOnly = true) + public SsoClientListVo selectSsoClientList(SsoClientListDto ssoClientListDto) { + int totalCount = ssoClientMapper.selectSsoClientListCount(ssoClientListDto); + + SsoClientListVo ssoClientListVo = new SsoClientListVo(); + ssoClientListVo.setSsoClientList(ssoClientMapper.selectSsoClientList(ssoClientListDto)); + ssoClientListVo.setPaging(ssoClientListDto, totalCount); + + return ssoClientListVo; + } + + @Transactional(readOnly = true) + public SsoClientViewVo selectSsoClientView(SsoClientViewDto ssoClientViewDto) { + return ssoClientMapper.selectSsoClientView(ssoClientViewDto); + } + + @Transactional(readOnly = true) + public SsoClientAuthorizeViewVo selectSsoClientAuthorizeView(SsoClientAuthorizeViewDto ssoClientAuthorizeViewDto) { + return ssoClientMapper.selectSsoClientAuthorizeView(ssoClientAuthorizeViewDto); + } + + @Transactional(readOnly = true) + public int selectSsoClientDuplicateCount(String clientId) { + return ssoClientMapper.selectSsoClientDuplicateCount(clientId); + } + + @Transactional(readOnly = true) + public int selectSsoClientModifyDuplicateCount(SsoClientModifyDto ssoClientModifyDto) { + return ssoClientMapper.selectSsoClientModifyDuplicateCount(ssoClientModifyDto); + } + + @Transactional + public SsoClientAddVo insertSsoClientAdd(SsoClientAddDto ssoClientAddDto) { + SsoClientAddVo ssoClientAddVo = new SsoClientAddVo(); + + try { + int insertCount = ssoClientMapper.insertSsoClientAdd(ssoClientAddDto); + + ssoClientAddVo.setAdded(insertCount > 0); + ssoClientAddVo.setSsoClientIdx(ssoClientAddDto.getSsoClientIdx()); + ssoClientAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + } catch (DuplicateKeyException e) { + ssoClientAddVo.setAdded(false); + ssoClientAddVo.setResultCode(2004); + } + + return ssoClientAddVo; + } + + @Transactional + public SsoClientModifyVo updateSsoClientModify(SsoClientModifyDto ssoClientModifyDto) { + SsoClientModifyVo ssoClientModifyVo = new SsoClientModifyVo(); + ssoClientModifyVo.setSsoClientIdx(ssoClientModifyDto.getSsoClientIdx()); + + if (!ssoClientModifyDto.hasModifyValue()) { + ssoClientModifyVo.setUpdated(false); + ssoClientModifyVo.setResultCode(4001); + + return ssoClientModifyVo; + } + + try { + int updateCount = ssoClientMapper.updateSsoClientModify(ssoClientModifyDto); + + ssoClientModifyVo.setUpdated(updateCount > 0); + ssoClientModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + } catch (DuplicateKeyException e) { + ssoClientModifyVo.setUpdated(false); + ssoClientModifyVo.setResultCode(2004); + } + + return ssoClientModifyVo; + } + + @Transactional + public SsoClientDeleteVo updateSsoClientDelete(SsoClientDeleteDto ssoClientDeleteDto) { + int updateCount = ssoClientMapper.updateSsoClientDelete(ssoClientDeleteDto); + + SsoClientDeleteVo ssoClientDeleteVo = new SsoClientDeleteVo(); + ssoClientDeleteVo.setDeleted(updateCount > 0); + ssoClientDeleteVo.setSsoClientIdx(ssoClientDeleteDto.getSsoClientIdx()); + ssoClientDeleteVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return ssoClientDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientAddVo.java new file mode 100644 index 0000000..5786f0b --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientAddVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.ssoClient.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SsoClientAddVo { + private boolean added; + private Integer ssoClientIdx; + + @JsonIgnore + private Integer resultCode; +} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/SsoClientVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientAuthorizeViewVo.java similarity index 52% rename from src/main/java/com/alist/api/modules/front/auth/vo/SsoClientVo.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientAuthorizeViewVo.java index 4999f86..d2b33bd 100644 --- a/src/main/java/com/alist/api/modules/front/auth/vo/SsoClientVo.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientAuthorizeViewVo.java @@ -1,13 +1,13 @@ -package com.alist.api.modules.front.auth.vo; +package com.alist.api.core.modules.standard.ssoClient.vo; import lombok.Getter; +import lombok.Setter; @Getter -public class SsoClientVo { +@Setter +public class SsoClientAuthorizeViewVo { private Integer ssoClientIdx; private String clientId; - private String clientName; private String redirectUri; - private String memo; private String useYn; } diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientDeleteVo.java new file mode 100644 index 0000000..5ce320b --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientDeleteVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.ssoClient.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SsoClientDeleteVo { + private boolean deleted; + private Integer ssoClientIdx; + + @JsonIgnore + private Integer resultCode; +} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientListItemVo.java similarity index 53% rename from src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoModifyDto.java rename to 01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientListItemVo.java index bc11818..7313a6c 100644 --- a/src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoModifyDto.java +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientListItemVo.java @@ -1,16 +1,19 @@ -package com.alist.api.modules.admin.sso.dto; +package com.alist.api.core.modules.standard.ssoClient.vo; import lombok.Getter; import lombok.Setter; +import java.time.LocalDateTime; + @Getter @Setter -public class AdminSsoModifyDto { +public class SsoClientListItemVo { private Integer ssoClientIdx; private String clientId; private String clientName; private String redirectUri; private String memo; private String useYn; - private Integer updateMember; + private LocalDateTime createDate; + private LocalDateTime updateDate; } diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientListVo.java new file mode 100644 index 0000000..ac55f5c --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.ssoClient.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class SsoClientListVo extends PagingResponse { + private List ssoClientList; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientModifyVo.java new file mode 100644 index 0000000..9f52008 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientModifyVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.ssoClient.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SsoClientModifyVo { + private boolean updated; + private Integer ssoClientIdx; + + @JsonIgnore + private Integer resultCode; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientViewVo.java new file mode 100644 index 0000000..7efe3f3 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/ssoClient/vo/SsoClientViewVo.java @@ -0,0 +1,21 @@ +package com.alist.api.core.modules.standard.ssoClient.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class SsoClientViewVo { + private Integer ssoClientIdx; + private String clientId; + private String clientName; + private String redirectUri; + private String memo; + private String useYn; + private LocalDateTime createDate; + private Integer createMember; + private LocalDateTime updateDate; + private Integer updateMember; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListAddDto.java new file mode 100644 index 0000000..2e850a5 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListAddDto.java @@ -0,0 +1,12 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestCorsAllowedListAddDto { + private Integer corsIdx; + private Integer userTokenIdx; + private String allowedOrigin; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListDeleteDto.java new file mode 100644 index 0000000..7780407 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListDeleteDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestCorsAllowedListDeleteDto { + private Integer corsIdx; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListListDto.java new file mode 100644 index 0000000..ba145eb --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListListDto.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestCorsAllowedListListDto extends PagingRequest { + private Integer corsIdx; + private Integer userTokenIdx; + private String allowedOrigin; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListModifyDto.java new file mode 100644 index 0000000..3196864 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListModifyDto.java @@ -0,0 +1,16 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestCorsAllowedListModifyDto { + private Integer corsIdx; + private Integer userTokenIdx; + private String allowedOrigin; + + public boolean hasModifyValue() { + return userTokenIdx != null || allowedOrigin != null; + } +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListViewDto.java new file mode 100644 index 0000000..5b4c3f3 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/dto/TestCorsAllowedListViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestCorsAllowedListViewDto { + private Integer corsIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/mapper/TestCorsAllowedListMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/mapper/TestCorsAllowedListMapper.java new file mode 100644 index 0000000..94f2719 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/mapper/TestCorsAllowedListMapper.java @@ -0,0 +1,38 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.mapper; + +import com.alist.api.core.modules.standard.testCorsAllowedList.dto.*; +import com.alist.api.core.modules.standard.testCorsAllowedList.vo.*; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TestCorsAllowedListMapper { + int selectTestCorsAllowedListListCount( + TestCorsAllowedListListDto testCorsAllowedListListDto + ); + + List selectTestCorsAllowedListList( + TestCorsAllowedListListDto testCorsAllowedListListDto + ); + + TestCorsAllowedListViewVo selectTestCorsAllowedListView( + TestCorsAllowedListViewDto testCorsAllowedListViewDto + ); + + List selectTestCorsAllowedListAllowedOriginList(); + + int selectTestCorsAllowedListAllowedOriginCount(String allowedOrigin); + + int insertTestCorsAllowedListAdd( + TestCorsAllowedListAddDto testCorsAllowedListAddDto + ); + + int updateTestCorsAllowedListModify( + TestCorsAllowedListModifyDto testCorsAllowedListModifyDto + ); + + int updateTestCorsAllowedListDelete( + TestCorsAllowedListDeleteDto testCorsAllowedListDeleteDto + ); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/service/TestCorsAllowedListService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/service/TestCorsAllowedListService.java new file mode 100644 index 0000000..9685f24 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/service/TestCorsAllowedListService.java @@ -0,0 +1,113 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.service; + +import com.alist.api.core.modules.standard.testCorsAllowedList.dto.*; +import com.alist.api.core.modules.standard.testCorsAllowedList.mapper.TestCorsAllowedListMapper; +import com.alist.api.core.modules.standard.testCorsAllowedList.vo.*; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +public class TestCorsAllowedListService { + private final TestCorsAllowedListMapper testCorsAllowedListMapper; + + public TestCorsAllowedListService(TestCorsAllowedListMapper testCorsAllowedListMapper) { + this.testCorsAllowedListMapper = testCorsAllowedListMapper; + } + + @Transactional(readOnly = true) + public TestCorsAllowedListListVo selectTestCorsAllowedListList(TestCorsAllowedListListDto testCorsAllowedListListDto) { + int totalCount = testCorsAllowedListMapper + .selectTestCorsAllowedListListCount(testCorsAllowedListListDto); + + TestCorsAllowedListListVo testCorsAllowedListListVo = + new TestCorsAllowedListListVo(); + + testCorsAllowedListListVo.setTestCorsAllowedList( + testCorsAllowedListMapper.selectTestCorsAllowedListList( + testCorsAllowedListListDto + ) + ); + testCorsAllowedListListVo.setPaging(testCorsAllowedListListDto, totalCount); + + return testCorsAllowedListListVo; + } + + @Transactional(readOnly = true) + public TestCorsAllowedListViewVo selectTestCorsAllowedListView(TestCorsAllowedListViewDto testCorsAllowedListViewDto) { + return testCorsAllowedListMapper.selectTestCorsAllowedListView( + testCorsAllowedListViewDto + ); + } + + @Transactional(readOnly = true) + public List selectTestCorsAllowedListAllowedOriginList() { + return testCorsAllowedListMapper.selectTestCorsAllowedListAllowedOriginList(); + } + + @Transactional(readOnly = true) + public int selectTestCorsAllowedListAllowedOriginCount(String allowedOrigin) { + return testCorsAllowedListMapper.selectTestCorsAllowedListAllowedOriginCount(allowedOrigin); + } + + @Transactional + public TestCorsAllowedListAddVo insertTestCorsAllowedListAdd(TestCorsAllowedListAddDto testCorsAllowedListAddDto) { + int insertCount = testCorsAllowedListMapper.insertTestCorsAllowedListAdd( + testCorsAllowedListAddDto + ); + + TestCorsAllowedListAddVo testCorsAllowedListAddVo = + new TestCorsAllowedListAddVo(); + + testCorsAllowedListAddVo.setAdded(insertCount > 0); + testCorsAllowedListAddVo.setCorsIdx(testCorsAllowedListAddDto.getCorsIdx()); + testCorsAllowedListAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return testCorsAllowedListAddVo; + } + + @Transactional + public TestCorsAllowedListModifyVo updateTestCorsAllowedListModify(TestCorsAllowedListModifyDto testCorsAllowedListModifyDto) { + TestCorsAllowedListModifyVo testCorsAllowedListModifyVo = + new TestCorsAllowedListModifyVo(); + + testCorsAllowedListModifyVo.setCorsIdx( + testCorsAllowedListModifyDto.getCorsIdx() + ); + + if (!testCorsAllowedListModifyDto.hasModifyValue()) { + testCorsAllowedListModifyVo.setUpdated(false); + testCorsAllowedListModifyVo.setResultCode(4001); + + return testCorsAllowedListModifyVo; + } + + int updateCount = testCorsAllowedListMapper.updateTestCorsAllowedListModify( + testCorsAllowedListModifyDto + ); + + testCorsAllowedListModifyVo.setUpdated(updateCount > 0); + testCorsAllowedListModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return testCorsAllowedListModifyVo; + } + + @Transactional + public TestCorsAllowedListDeleteVo updateTestCorsAllowedListDelete(TestCorsAllowedListDeleteDto testCorsAllowedListDeleteDto) { + int updateCount = testCorsAllowedListMapper.updateTestCorsAllowedListDelete( + testCorsAllowedListDeleteDto + ); + + TestCorsAllowedListDeleteVo testCorsAllowedListDeleteVo = + new TestCorsAllowedListDeleteVo(); + + testCorsAllowedListDeleteVo.setDeleted(updateCount > 0); + testCorsAllowedListDeleteVo.setCorsIdx( + testCorsAllowedListDeleteDto.getCorsIdx() + ); + testCorsAllowedListDeleteVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return testCorsAllowedListDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListAddVo.java new file mode 100644 index 0000000..8b8f068 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListAddVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestCorsAllowedListAddVo { + private boolean added; + private Integer corsIdx; + + @JsonIgnore + private Integer resultCode; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListDeleteVo.java new file mode 100644 index 0000000..48f9470 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListDeleteVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestCorsAllowedListDeleteVo { + private boolean deleted; + private Integer corsIdx; + + @JsonIgnore + private Integer resultCode; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListListItemVo.java new file mode 100644 index 0000000..5dd7a0c --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListListItemVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class TestCorsAllowedListListItemVo { + private Integer corsIdx; + private Integer userTokenIdx; + private String allowedOrigin; + private LocalDateTime createdAt; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListListVo.java new file mode 100644 index 0000000..87414f7 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class TestCorsAllowedListListVo extends PagingResponse { + private List testCorsAllowedList; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListModifyVo.java new file mode 100644 index 0000000..e21fd2d --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListModifyVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestCorsAllowedListModifyVo { + private boolean updated; + private Integer corsIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListViewVo.java new file mode 100644 index 0000000..e9f8821 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testCorsAllowedList/vo/TestCorsAllowedListViewVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testCorsAllowedList.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class TestCorsAllowedListViewVo { + private Integer corsIdx; + private Integer userTokenIdx; + private String allowedOrigin; + private LocalDateTime createdAt; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserAddDto.java new file mode 100644 index 0000000..72bf3f6 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserAddDto.java @@ -0,0 +1,14 @@ +package com.alist.api.core.modules.standard.testUser.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserAddDto { + private Integer userIdx; + private String id; + private String password; + private String email; + private String hp; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserDeleteDto.java new file mode 100644 index 0000000..133a30d --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserDeleteDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.testUser.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserDeleteDto { + private Integer userIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserListDto.java new file mode 100644 index 0000000..2c6700f --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserListDto.java @@ -0,0 +1,16 @@ +package com.alist.api.core.modules.standard.testUser.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserListDto extends PagingRequest { + private Integer userIdx; + private String id; + private String email; + private String hp; + private String dormantYn; + private String withdrawStatus; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserModifyDto.java new file mode 100644 index 0000000..d1c8617 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserModifyDto.java @@ -0,0 +1,33 @@ +package com.alist.api.core.modules.standard.testUser.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class TestUserModifyDto { + private Integer userIdx; + private String id; + private String password; + private String email; + private String hp; + private String dormantYn; + private LocalDateTime dormantAt; + private String withdrawStatus; + private LocalDateTime withdrawAt; + private LocalDateTime lastLoginAt; + + public boolean hasModifyValue() { + return id != null + || password != null + || email != null + || hp != null + || dormantYn != null + || dormantAt != null + || withdrawStatus != null + || withdrawAt != null + || lastLoginAt != null; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserViewDto.java new file mode 100644 index 0000000..037a9ae --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/dto/TestUserViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.testUser.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserViewDto { + private Integer userIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/mapper/TestUserMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/mapper/TestUserMapper.java new file mode 100644 index 0000000..034d868 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/mapper/TestUserMapper.java @@ -0,0 +1,29 @@ +package com.alist.api.core.modules.standard.testUser.mapper; + +import com.alist.api.core.modules.standard.testUser.dto.TestUserAddDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserDeleteDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserListDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserModifyDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserViewDto; +import com.alist.api.core.modules.standard.testUser.vo.TestUserListItemVo; +import com.alist.api.core.modules.standard.testUser.vo.TestUserViewVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TestUserMapper { + int selectTestUserListCount(TestUserListDto testUserListDto); + + List selectTestUserList(TestUserListDto testUserListDto); + + TestUserViewVo selectTestUserView(TestUserViewDto testUserViewDto); + + int insertTestUserAdd(TestUserAddDto testUserAddDto); + + int updateTestUserModify(TestUserModifyDto testUserModifyDto); + + int updateTestUserDelete(TestUserDeleteDto testUserDeleteDto); + + int selectTestUserDuplicateCount(String id); +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/service/TestUserService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/service/TestUserService.java new file mode 100644 index 0000000..dedff1b --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/service/TestUserService.java @@ -0,0 +1,98 @@ +package com.alist.api.core.modules.standard.testUser.service; + +import com.alist.api.core.modules.standard.testUser.dto.TestUserAddDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserDeleteDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserListDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserModifyDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserViewDto; +import com.alist.api.core.modules.standard.testUser.mapper.TestUserMapper; +import com.alist.api.core.modules.standard.testUser.vo.TestUserAddVo; +import com.alist.api.core.modules.standard.testUser.vo.TestUserDeleteVo; +import com.alist.api.core.modules.standard.testUser.vo.TestUserListVo; +import com.alist.api.core.modules.standard.testUser.vo.TestUserModifyVo; +import com.alist.api.core.modules.standard.testUser.vo.TestUserViewVo; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class TestUserService { + private final TestUserMapper testUserMapper; + private final PasswordEncoder passwordEncoder; + + public TestUserService(TestUserMapper testUserMapper, PasswordEncoder passwordEncoder) { + this.testUserMapper = testUserMapper; + this.passwordEncoder = passwordEncoder; + } + + @Transactional(readOnly = true) + public TestUserListVo selectTestUserList(TestUserListDto testUserListDto) { + int totalCount = testUserMapper.selectTestUserListCount(testUserListDto); + + TestUserListVo testUserListVo = new TestUserListVo(); + testUserListVo.setTestUserList(testUserMapper.selectTestUserList(testUserListDto)); + testUserListVo.setPaging(testUserListDto, totalCount); + + return testUserListVo; + } + + @Transactional(readOnly = true) + public TestUserViewVo selectTestUserView(TestUserViewDto testUserViewDto) { + return testUserMapper.selectTestUserView(testUserViewDto); + } + + @Transactional + public TestUserAddVo insertTestUserAdd(TestUserAddDto testUserAddDto) { + testUserAddDto.setPassword(passwordEncoder.encode(testUserAddDto.getPassword())); + + int insertCount = testUserMapper.insertTestUserAdd(testUserAddDto); + + TestUserAddVo testUserAddVo = new TestUserAddVo(); + testUserAddVo.setAdded(insertCount > 0); + testUserAddVo.setUserIdx(testUserAddDto.getUserIdx()); + testUserAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + + return testUserAddVo; + } + + @Transactional + public TestUserModifyVo updateTestUserModify(TestUserModifyDto testUserModifyDto) { + TestUserModifyVo testUserModifyVo = new TestUserModifyVo(); + testUserModifyVo.setUserIdx(testUserModifyDto.getUserIdx()); + + if (!testUserModifyDto.hasModifyValue()) { + testUserModifyVo.setUpdated(false); + testUserModifyVo.setResultCode(4001); + return testUserModifyVo; + } + + if (testUserModifyDto.getPassword() != null) { + testUserModifyDto.setPassword(passwordEncoder.encode(testUserModifyDto.getPassword())); + } + + int updateCount = testUserMapper.updateTestUserModify(testUserModifyDto); + + testUserModifyVo.setUpdated(updateCount > 0); + testUserModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return testUserModifyVo; + } + + @Transactional + public TestUserDeleteVo updateTestUserDelete(TestUserDeleteDto testUserDeleteDto) { + TestUserDeleteVo testUserDeleteVo = new TestUserDeleteVo(); + testUserDeleteVo.setUserIdx(testUserDeleteDto.getUserIdx()); + + int updateCount = testUserMapper.updateTestUserDelete(testUserDeleteDto); + + testUserDeleteVo.setDeleted(updateCount > 0); + testUserDeleteVo.setResultCode(updateCount > 0 ? 2005 : 2003); + + return testUserDeleteVo; + } + + @Transactional(readOnly = true) + public int selectTestUserDuplicateCount(String id) { + return testUserMapper.selectTestUserDuplicateCount(id); + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserAddVo.java new file mode 100644 index 0000000..6b0bcd8 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserAddVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testUser.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserAddVo { + private boolean added; + private Integer userIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserDeleteVo.java new file mode 100644 index 0000000..c9f4b4e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserDeleteVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testUser.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserDeleteVo { + private boolean deleted; + private Integer userIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserListItemVo.java new file mode 100644 index 0000000..8c32a11 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserListItemVo.java @@ -0,0 +1,22 @@ +package com.alist.api.core.modules.standard.testUser.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class TestUserListItemVo { + private Integer userIdx; + private String id; + private String email; + private String hp; + private String dormantYn; + private LocalDateTime dormantAt; + private String withdrawStatus; + private LocalDateTime withdrawAt; + private LocalDateTime lastLoginAt; + private LocalDateTime createAt; + private LocalDateTime updateAt; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserListVo.java new file mode 100644 index 0000000..e10b742 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.testUser.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class TestUserListVo extends PagingResponse { + private List testUserList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserModifyVo.java new file mode 100644 index 0000000..dd902da --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserModifyVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testUser.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserModifyVo { + private boolean updated; + private Integer userIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserViewVo.java new file mode 100644 index 0000000..b82397b --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUser/vo/TestUserViewVo.java @@ -0,0 +1,22 @@ +package com.alist.api.core.modules.standard.testUser.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class TestUserViewVo { + private Integer userIdx; + private String id; + private String email; + private String hp; + private String dormantYn; + private LocalDateTime dormantAt; + private String withdrawStatus; + private LocalDateTime withdrawAt; + private LocalDateTime lastLoginAt; + private LocalDateTime createAt; + private LocalDateTime updateAt; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenAddDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenAddDto.java new file mode 100644 index 0000000..8f839b4 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenAddDto.java @@ -0,0 +1,18 @@ +package com.alist.api.core.modules.standard.testUserToken.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class TestUserTokenAddDto { + private Integer userTokenIdx; + private Integer userIdx; + private String userApiKey; + private String userRole; + private String userType; + private String refreshToken; + private LocalDateTime expiresAt; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenDeleteDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenDeleteDto.java new file mode 100644 index 0000000..f2ec9df --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenDeleteDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.testUserToken.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserTokenDeleteDto { + private Integer userTokenIdx; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenListDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenListDto.java new file mode 100644 index 0000000..24a5d9e --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenListDto.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testUserToken.dto; + +import com.alist.api.core.common.paging.PagingRequest; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserTokenListDto extends PagingRequest { + private Integer userTokenIdx; + private Integer userIdx; + private String userApiKey; + private String userRole; + private String userType; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenModifyDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenModifyDto.java new file mode 100644 index 0000000..08860f2 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenModifyDto.java @@ -0,0 +1,27 @@ +package com.alist.api.core.modules.standard.testUserToken.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class TestUserTokenModifyDto { + private Integer userTokenIdx; + private Integer userIdx; + private String userApiKey; + private String userRole; + private String userType; + private String refreshToken; + private LocalDateTime expiresAt; + + public boolean hasModifyValue() { + return userIdx != null + || userApiKey != null + || userRole != null + || userType != null + || refreshToken != null + || expiresAt != null; + } +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenViewDto.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenViewDto.java new file mode 100644 index 0000000..560006b --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/dto/TestUserTokenViewDto.java @@ -0,0 +1,10 @@ +package com.alist.api.core.modules.standard.testUserToken.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserTokenViewDto { + private Integer userTokenIdx; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/mapper/TestUserTokenMapper.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/mapper/TestUserTokenMapper.java new file mode 100644 index 0000000..5534a37 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/mapper/TestUserTokenMapper.java @@ -0,0 +1,26 @@ +package com.alist.api.core.modules.standard.testUserToken.mapper; + +import com.alist.api.core.modules.standard.testUserToken.dto.*; +import com.alist.api.core.modules.standard.testUserToken.vo.*; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TestUserTokenMapper { + int selectTestUserTokenListCount(TestUserTokenListDto testUserTokenListDto); + + List selectTestUserTokenList( + TestUserTokenListDto testUserTokenListDto + ); + + TestUserTokenViewVo selectTestUserTokenView( + TestUserTokenViewDto testUserTokenViewDto + ); + + int insertTestUserTokenAdd(TestUserTokenAddDto testUserTokenAddDto); + + int updateTestUserTokenModify(TestUserTokenModifyDto testUserTokenModifyDto); + + int deleteTestUserTokenDelete(TestUserTokenDeleteDto testUserTokenDeleteDto); +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/service/TestUserTokenService.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/service/TestUserTokenService.java new file mode 100644 index 0000000..c958f2c --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/service/TestUserTokenService.java @@ -0,0 +1,89 @@ +package com.alist.api.core.modules.standard.testUserToken.service; + +import com.alist.api.core.modules.standard.testUserToken.dto.*; +import com.alist.api.core.modules.standard.testUserToken.mapper.TestUserTokenMapper; +import com.alist.api.core.modules.standard.testUserToken.vo.*; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class TestUserTokenService { + private final TestUserTokenMapper testUserTokenMapper; + + public TestUserTokenService(TestUserTokenMapper testUserTokenMapper) { + this.testUserTokenMapper = testUserTokenMapper; + } + + @Transactional(readOnly = true) + public TestUserTokenListVo selectTestUserTokenList(TestUserTokenListDto testUserTokenListDto) { + int totalCount = testUserTokenMapper.selectTestUserTokenListCount(testUserTokenListDto); + + TestUserTokenListVo testUserTokenListVo = new TestUserTokenListVo(); + testUserTokenListVo.setTestUserTokenList( + testUserTokenMapper.selectTestUserTokenList(testUserTokenListDto) + ); + testUserTokenListVo.setPaging(testUserTokenListDto, totalCount); + + return testUserTokenListVo; + } + + @Transactional(readOnly = true) + public TestUserTokenViewVo selectTestUserTokenView(TestUserTokenViewDto testUserTokenViewDto) { + return testUserTokenMapper.selectTestUserTokenView(testUserTokenViewDto); + } + + @Transactional + public TestUserTokenAddVo insertTestUserTokenAdd(TestUserTokenAddDto testUserTokenAddDto) { + TestUserTokenAddVo testUserTokenAddVo = new TestUserTokenAddVo(); + + try { + int insertCount = testUserTokenMapper.insertTestUserTokenAdd(testUserTokenAddDto); + + testUserTokenAddVo.setAdded(insertCount > 0); + testUserTokenAddVo.setUserTokenIdx(testUserTokenAddDto.getUserTokenIdx()); + testUserTokenAddVo.setResultCode(insertCount > 0 ? 2002 : 500); + } catch (DuplicateKeyException e) { + testUserTokenAddVo.setAdded(false); + testUserTokenAddVo.setResultCode(2004); + } + + return testUserTokenAddVo; + } + + @Transactional + public TestUserTokenModifyVo updateTestUserTokenModify(TestUserTokenModifyDto testUserTokenModifyDto) { + TestUserTokenModifyVo testUserTokenModifyVo = new TestUserTokenModifyVo(); + testUserTokenModifyVo.setUserTokenIdx(testUserTokenModifyDto.getUserTokenIdx()); + + if (!testUserTokenModifyDto.hasModifyValue()) { + testUserTokenModifyVo.setUpdated(false); + testUserTokenModifyVo.setResultCode(4001); + return testUserTokenModifyVo; + } + + try { + int updateCount = testUserTokenMapper.updateTestUserTokenModify(testUserTokenModifyDto); + + testUserTokenModifyVo.setUpdated(updateCount > 0); + testUserTokenModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); + } catch (DuplicateKeyException e) { + testUserTokenModifyVo.setUpdated(false); + testUserTokenModifyVo.setResultCode(2004); + } + + return testUserTokenModifyVo; + } + + @Transactional + public TestUserTokenDeleteVo deleteTestUserTokenDelete(TestUserTokenDeleteDto testUserTokenDeleteDto) { + int deleteCount = testUserTokenMapper.deleteTestUserTokenDelete(testUserTokenDeleteDto); + + TestUserTokenDeleteVo testUserTokenDeleteVo = new TestUserTokenDeleteVo(); + testUserTokenDeleteVo.setDeleted(deleteCount > 0); + testUserTokenDeleteVo.setUserTokenIdx(testUserTokenDeleteDto.getUserTokenIdx()); + testUserTokenDeleteVo.setResultCode(deleteCount > 0 ? 2005 : 2003); + + return testUserTokenDeleteVo; + } +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenAddVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenAddVo.java new file mode 100644 index 0000000..890ec21 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenAddVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testUserToken.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserTokenAddVo { + private boolean added; + private Integer userTokenIdx; + + @JsonIgnore + private Integer resultCode; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenDeleteVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenDeleteVo.java new file mode 100644 index 0000000..58b7974 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenDeleteVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testUserToken.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserTokenDeleteVo { + private boolean deleted; + private Integer userTokenIdx; + + @JsonIgnore + private Integer resultCode; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenListItemVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenListItemVo.java new file mode 100644 index 0000000..24bcbaa --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenListItemVo.java @@ -0,0 +1,19 @@ +package com.alist.api.core.modules.standard.testUserToken.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class TestUserTokenListItemVo { + private Integer userTokenIdx; + private Integer userIdx; + private String userApiKey; + private String userRole; + private String userType; + private LocalDateTime expiresAt; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; +} \ No newline at end of file diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenListVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenListVo.java new file mode 100644 index 0000000..d3865e9 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.core.modules.standard.testUserToken.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class TestUserTokenListVo extends PagingResponse { + private List testUserTokenList; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenModifyVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenModifyVo.java new file mode 100644 index 0000000..9306b00 --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenModifyVo.java @@ -0,0 +1,15 @@ +package com.alist.api.core.modules.standard.testUserToken.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestUserTokenModifyVo { + private boolean updated; + private Integer userTokenIdx; + + @JsonIgnore + private Integer resultCode; +} diff --git a/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenViewVo.java b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenViewVo.java new file mode 100644 index 0000000..d8a911b --- /dev/null +++ b/01-api-core/src/main/java/com/alist/api/core/modules/standard/testUserToken/vo/TestUserTokenViewVo.java @@ -0,0 +1,19 @@ +package com.alist.api.core.modules.standard.testUserToken.vo; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class TestUserTokenViewVo { + private Integer userTokenIdx; + private Integer userIdx; + private String userApiKey; + private String userRole; + private String userType; + private LocalDateTime expiresAt; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; +} \ No newline at end of file diff --git a/src/main/resources/log4jdbc.log4j2.properties b/01-api-core/src/main/resources/log4jdbc.log4j2.properties similarity index 100% rename from src/main/resources/log4jdbc.log4j2.properties rename to 01-api-core/src/main/resources/log4jdbc.log4j2.properties diff --git a/01-api-core/src/main/resources/mapper/bespoke/fileDetail/FileDetailTusFileMapper.xml b/01-api-core/src/main/resources/mapper/bespoke/fileDetail/FileDetailTusFileMapper.xml new file mode 100644 index 0000000..f16f7d5 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/bespoke/fileDetail/FileDetailTusFileMapper.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + diff --git a/01-api-core/src/main/resources/mapper/bespoke/fileMaster/FileMasterTusFileMapper.xml b/01-api-core/src/main/resources/mapper/bespoke/fileMaster/FileMasterTusFileMapper.xml new file mode 100644 index 0000000..eccc02e --- /dev/null +++ b/01-api-core/src/main/resources/mapper/bespoke/fileMaster/FileMasterTusFileMapper.xml @@ -0,0 +1,28 @@ + + + + + + /*FileMasterTusFileMapper.fileMasterTusFileAggregateModify*/ + UPDATE ALISTLMS.FILE_MASTER FM + INNER JOIN ( + SELECT FD.FILE_MASTER_IDX + , COUNT(*) AS TOTAL_COUNT + , SUM(CASE WHEN FD.STATUS = 3 THEN 1 ELSE 0 END) AS DONE_COUNT + , SUM(CASE WHEN FD.STATUS IN (4, 5) THEN 1 ELSE 0 END) AS FAIL_COUNT + FROM ALISTLMS.FILE_DETAIL FD + WHERE FD.FILE_MASTER_IDX = (SELECT FILE_MASTER_IDX FROM ALISTLMS.FILE_DETAIL WHERE FILE_UUID = #{fileUuid} LIMIT 1) + AND FD.DEL_YN = 'N' + ) AGGREGATE ON AGGREGATE.FILE_MASTER_IDX = FM.FILE_MASTER_IDX + SET FM.TOTAL_COUNT = AGGREGATE.TOTAL_COUNT + , FM.DONE_COUNT = AGGREGATE.DONE_COUNT + , FM.FAIL_COUNT = AGGREGATE.FAIL_COUNT + , FM.STATUS = CASE + WHEN AGGREGATE.DONE_COUNT = AGGREGATE.TOTAL_COUNT THEN 3 + WHEN AGGREGATE.DONE_COUNT = 0 AND AGGREGATE.FAIL_COUNT = AGGREGATE.TOTAL_COUNT THEN 4 + WHEN AGGREGATE.DONE_COUNT > 0 AND AGGREGATE.FAIL_COUNT > 0 THEN 5 + ELSE 1 + END + , FM.UPDATE_DATE = NOW() + + diff --git a/01-api-core/src/main/resources/mapper/bespoke/notice/NoticeNoticeMapper.xml b/01-api-core/src/main/resources/mapper/bespoke/notice/NoticeNoticeMapper.xml new file mode 100644 index 0000000..6e94807 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/bespoke/notice/NoticeNoticeMapper.xml @@ -0,0 +1,107 @@ + + + + + + + FROM ALISTLMS.NOTICE N + WHERE N.DEL_YN = 'N' + + AND (N.TITLE LIKE CONCAT('%', #{keyword}, '%') OR N.CONTENT LIKE CONCAT('%', #{keyword}, '%')) + + + AND N.NOTICE_TYPE = #{noticeType} + + + AND N.TARGET_SCOPE = #{targetScope} + + + AND EXISTS ( + SELECT 1 + FROM ALISTLMS.NOTICE_CAMPUS NC + WHERE NC.NOTICE_IDX = N.NOTICE_IDX + AND NC.CAMPUS_IDX = #{campusIdx} + ) + + + AND N.PIN_YN = #{pinYn} + + + AND N.USE_YN = #{useYn} + + + + + + + + + + + + + diff --git a/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserAdminMemberMapper.xml b/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserAdminMemberMapper.xml new file mode 100644 index 0000000..669d3e1 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserAdminMemberMapper.xml @@ -0,0 +1,82 @@ + + + + + + + FROM ALISTLMS.test_user ATU + INNER JOIN ALISTLMS.test_user_token ATUT + ON ATUT.user_idx = ATU.user_idx + WHERE ATU.del_yn = 1 + + AND ( + ATU.id LIKE CONCAT('%', #{keyword}, '%') + OR ATU.email LIKE CONCAT('%', #{keyword}, '%') + OR ATU.hp LIKE CONCAT('%', #{keyword}, '%') + ) + + + AND ATUT.user_role = #{userRole} + + + AND ATUT.user_type = #{userType} + + + AND ATU.dormant_yn = #{dormantYn} + + + AND ATU.withdraw_status = #{withdrawStatus} + + + + + + + + + + \ No newline at end of file diff --git a/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserAuthMapper.xml b/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserAuthMapper.xml new file mode 100644 index 0000000..fc6c925 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserAuthMapper.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserSsoMapper.xml b/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserSsoMapper.xml new file mode 100644 index 0000000..1142176 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserSsoMapper.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserUserMapper.xml b/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserUserMapper.xml new file mode 100644 index 0000000..2b31405 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/bespoke/testUser/TestUserUserMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/01-api-core/src/main/resources/mapper/bespoke/testUserToken/TestUserTokenAuthMapper.xml b/01-api-core/src/main/resources/mapper/bespoke/testUserToken/TestUserTokenAuthMapper.xml new file mode 100644 index 0000000..c017c42 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/bespoke/testUserToken/TestUserTokenAuthMapper.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + diff --git a/src/main/resources/mapper/migration/alist/user/AlistUserMapper.xml b/01-api-core/src/main/resources/mapper/migration/alist/user/AlistUserMapper.xml similarity index 66% rename from src/main/resources/mapper/migration/alist/user/AlistUserMapper.xml rename to 01-api-core/src/main/resources/mapper/migration/alist/user/AlistUserMapper.xml index bb061a2..295e014 100644 --- a/src/main/resources/mapper/migration/alist/user/AlistUserMapper.xml +++ b/01-api-core/src/main/resources/mapper/migration/alist/user/AlistUserMapper.xml @@ -1,9 +1,9 @@ - - + /*AlistUserMigrationMapper.selectAlistUserMigrationList*/ SELECT memid AS ID , memname AS NAME , memhp AS HP diff --git a/src/main/resources/mapper/migration/eltown/user/EltownUserMapper.xml b/01-api-core/src/main/resources/mapper/migration/eltown/user/EltownUserMapper.xml similarity index 77% rename from src/main/resources/mapper/migration/eltown/user/EltownUserMapper.xml rename to 01-api-core/src/main/resources/mapper/migration/eltown/user/EltownUserMapper.xml index 3fad31f..3961a7b 100644 --- a/src/main/resources/mapper/migration/eltown/user/EltownUserMapper.xml +++ b/01-api-core/src/main/resources/mapper/migration/eltown/user/EltownUserMapper.xml @@ -1,9 +1,9 @@ - - + /*EltownUserMigrationMapper.selectEltownUserMigrationList*/ SELECT '학생' AS TYPE , User_Id AS ID , User_Name AS NAME diff --git a/01-api-core/src/main/resources/mapper/standard/alCalendar/AlCalendarMapper.xml b/01-api-core/src/main/resources/mapper/standard/alCalendar/AlCalendarMapper.xml new file mode 100644 index 0000000..402be68 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/alCalendar/AlCalendarMapper.xml @@ -0,0 +1,123 @@ + + + + + + + FROM ALISTLMS.AL_CALENDAR + WHERE 1 = 1 + + AND YYYYMMDD = #{yyyymmdd} + + + AND YYYY = #{yyyy} + + + AND MM = #{mm} + + + AND HOLIDAY_YN = #{holidayYn} + + + + + + + + + + + /*AlCalendarMapper.insertAlCalendarAdd*/ + INSERT INTO ALISTLMS.AL_CALENDAR ( + YYYYMMDD + , YYYY + , MM + , DD + , WEEK_NO + , WEEK_NM + , HOLIDAY_YN + , HOLIDAY_NM + , FORMAT_DATE + ) VALUES ( + #{yyyymmdd} + , #{yyyy} + , #{mm} + , #{dd} + , #{weekNo} + , #{weekNm} + , #{holidayYn} + , #{holidayNm} + , #{formatDate} + ) + + + + /*AlCalendarMapper.updateAlCalendarModify*/ + UPDATE ALISTLMS.AL_CALENDAR + + + YYYY = #{yyyy}, + + + MM = #{mm}, + + + DD = #{dd}, + + + WEEK_NO = #{weekNo}, + + + WEEK_NM = #{weekNm}, + + + HOLIDAY_YN = #{holidayYn}, + + + HOLIDAY_NM = #{holidayNm}, + + + FORMAT_DATE = #{formatDate}, + + + WHERE YYYYMMDD = #{yyyymmdd} + + + + /*AlCalendarMapper.deleteAlCalendarDelete*/ + DELETE FROM ALISTLMS.AL_CALENDAR + WHERE YYYYMMDD = #{yyyymmdd} + + diff --git a/01-api-core/src/main/resources/mapper/standard/fileDetail/FileDetailMapper.xml b/01-api-core/src/main/resources/mapper/standard/fileDetail/FileDetailMapper.xml new file mode 100644 index 0000000..8034440 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/fileDetail/FileDetailMapper.xml @@ -0,0 +1,145 @@ + + + + + + FROM ALISTLMS.FILE_DETAIL + WHERE DEL_YN = 'N' + + AND FILE_DETAIL_IDX = #{fileDetailIdx} + + + AND FILE_MASTER_IDX = #{fileMasterIdx} + + + AND FILE_UUID = #{fileUuid} + + + AND ITEM_KEY = #{itemKey} + + + AND MOVE_YN = #{moveYn} + + + AND STATUS = #{status} + + + + + + + + + + + /*FileDetailMapper.insertFileDetailAdd*/ + INSERT INTO ALISTLMS.FILE_DETAIL ( + FILE_MASTER_IDX, FILE_SEQ, FILE_UUID, ITEM_KEY, ORIGIN_NAME + , SAVE_NAME, SAVE_PATH, CONTENT_TYPE, EXT, SIZE_BYTES + , UPLOADED_BYTES, TUS_UPLOAD_ID, UPLOAD_TOKEN_EXPIRE_AT, CHECKSUM_SHA256 + , MOVE_YN, MOVE_TRY_COUNT, MOVE_LAST_ERROR, MOVE_LOCK_AT, MOVE_DATE, MOVE_LOCK_OWNER + , STATUS, DEL_YN, CREATE_MEMBER, UPDATE_MEMBER + ) VALUES ( + #{fileMasterIdx}, #{fileSeq}, #{fileUuid}, #{itemKey}, #{originName} + , #{saveName}, #{savePath}, #{contentType}, #{ext}, #{sizeBytes} + , COALESCE(#{uploadedBytes}, 0), #{tusUploadId}, #{uploadTokenExpireAt}, #{checksumSha256} + , COALESCE(#{moveYn}, 'N'), COALESCE(#{moveTryCount}, 0), #{moveLastError}, #{moveLockAt}, #{moveDate}, #{moveLockOwner} + , COALESCE(#{status}, 0), 'N', #{createMember}, #{updateMember} + ) + + + + /*FileDetailMapper.updateFileDetailModify*/ + UPDATE ALISTLMS.FILE_DETAIL + SET UPDATE_MEMBER = #{updateMember} + , ITEM_KEY = #{itemKey} + , ORIGIN_NAME = #{originName} + , SAVE_NAME = #{saveName} + , SAVE_PATH = #{savePath} + , CONTENT_TYPE = #{contentType} + , EXT = #{ext} + , SIZE_BYTES = #{sizeBytes} + , UPLOADED_BYTES = #{uploadedBytes} + , TUS_UPLOAD_ID = #{tusUploadId} + , UPLOAD_TOKEN_EXPIRE_AT = #{uploadTokenExpireAt} + , CHECKSUM_SHA256 = #{checksumSha256} + , MOVE_YN = #{moveYn} + , MOVE_TRY_COUNT = #{moveTryCount} + , MOVE_LAST_ERROR = #{moveLastError} + , MOVE_LOCK_AT = #{moveLockAt} + , MOVE_DATE = #{moveDate} + , MOVE_LOCK_OWNER = #{moveLockOwner} + + , MOVE_LOCK_AT = NULL + , MOVE_LOCK_OWNER = NULL + + , STATUS = #{status} + WHERE FILE_DETAIL_IDX = #{fileDetailIdx} + AND DEL_YN = 'N' + + + + /*FileDetailMapper.updateFileDetailDelete*/ + UPDATE ALISTLMS.FILE_DETAIL + SET DEL_YN = 'Y' + , UPDATE_MEMBER = #{updateMember} + WHERE FILE_DETAIL_IDX = #{fileDetailIdx} + AND DEL_YN = 'N' + + diff --git a/01-api-core/src/main/resources/mapper/standard/fileDownloadEventLog/FileDownloadEventLogMapper.xml b/01-api-core/src/main/resources/mapper/standard/fileDownloadEventLog/FileDownloadEventLogMapper.xml new file mode 100644 index 0000000..87548b4 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/fileDownloadEventLog/FileDownloadEventLogMapper.xml @@ -0,0 +1,149 @@ + + + + + + + FROM ALISTLMS.FILE_DOWNLOAD_EVENT_LOG + WHERE DEL_YN = 'N' + + AND FILE_DOWNLOAD_EVENT_LOG_IDX = #{fileDownloadEventLogIdx} + + + AND FILE_MASTER_IDX = #{fileMasterIdx} + + + AND FILE_DETAIL_IDX = #{fileDetailIdx} + + + AND FILE_UUID = #{fileUuid} + + + AND EVENT_TYPE = #{eventType} + + + AND USER_IDX = #{userIdx} + + + AND USER_TOKEN_IDX = #{userTokenIdx} + + + + + + + + + + + /*FileDownloadEventLogMapper.insertFileDownloadEventLogAdd*/ + INSERT INTO ALISTLMS.FILE_DOWNLOAD_EVENT_LOG ( + FILE_MASTER_IDX + , FILE_DETAIL_IDX + , FILE_UUID + , EVENT_TYPE + , USER_IDX + , USER_TOKEN_IDX + , CLIENT_IP + , USER_AGENT + , REFERER + , DEL_YN + , CREATE_MEMBER + ) VALUES ( + #{fileMasterIdx} + , #{fileDetailIdx} + , #{fileUuid} + , #{eventType} + , #{userIdx} + , #{userTokenIdx} + , #{clientIp} + , #{userAgent} + , #{referer} + , 'N' + , #{createMember} + ) + + + + /*FileDownloadEventLogMapper.updateFileDownloadEventLogModify*/ + UPDATE ALISTLMS.FILE_DOWNLOAD_EVENT_LOG + + + FILE_MASTER_IDX = #{fileMasterIdx}, + + + FILE_DETAIL_IDX = #{fileDetailIdx}, + + + FILE_UUID = #{fileUuid}, + + + EVENT_TYPE = #{eventType}, + + + USER_IDX = #{userIdx}, + + + USER_TOKEN_IDX = #{userTokenIdx}, + + + CLIENT_IP = #{clientIp}, + + + USER_AGENT = #{userAgent}, + + + REFERER = #{referer}, + + + CREATE_MEMBER = #{createMember}, + + + WHERE FILE_DOWNLOAD_EVENT_LOG_IDX = #{fileDownloadEventLogIdx} + AND DEL_YN = 'N' + + + + /*FileDownloadEventLogMapper.updateFileDownloadEventLogDelete*/ + UPDATE ALISTLMS.FILE_DOWNLOAD_EVENT_LOG + SET DEL_YN = 'Y' + WHERE FILE_DOWNLOAD_EVENT_LOG_IDX = #{fileDownloadEventLogIdx} + AND DEL_YN = 'N' + + diff --git a/01-api-core/src/main/resources/mapper/standard/fileMaster/FileMasterMapper.xml b/01-api-core/src/main/resources/mapper/standard/fileMaster/FileMasterMapper.xml new file mode 100644 index 0000000..21bf2f7 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/fileMaster/FileMasterMapper.xml @@ -0,0 +1,127 @@ + + + + + + + FROM ALISTLMS.FILE_MASTER + WHERE DEL_YN = 'N' + + AND FILE_MASTER_IDX = #{fileMasterIdx} + + + AND USER_IDX = #{userIdx} + + + AND FILE_CATEGORY = #{fileCategory} + + + AND STATUS = #{status} + + + + + + + + + + + /*FileMasterMapper.insertFileMasterAdd*/ + INSERT INTO ALISTLMS.FILE_MASTER ( + USER_IDX + , FILE_CATEGORY + , FOLDER_PATH + , TOTAL_COUNT + , DONE_COUNT + , FAIL_COUNT + , STATUS + , DEL_YN + , CREATE_MEMBER + , UPDATE_MEMBER + ) VALUES ( + #{userIdx} + , COALESCE(#{fileCategory}, 'COMMON') + , COALESCE(#{folderPath}, '/') + , COALESCE(#{totalCount}, 0) + , COALESCE(#{doneCount}, 0) + , COALESCE(#{failCount}, 0) + , COALESCE(#{status}, 0) + , 'N' + , #{createMember} + , #{updateMember} + ) + + + + /*FileMasterMapper.updateFileMasterModify*/ + UPDATE ALISTLMS.FILE_MASTER + SET UPDATE_MEMBER = #{updateMember} + + , FILE_CATEGORY = #{fileCategory} + + + , FOLDER_PATH = #{folderPath} + + + , TOTAL_COUNT = #{totalCount} + + + , DONE_COUNT = #{doneCount} + + + , FAIL_COUNT = #{failCount} + + + , STATUS = #{status} + + WHERE FILE_MASTER_IDX = #{fileMasterIdx} + AND DEL_YN = 'N' + + + + /*FileMasterMapper.updateFileMasterDelete*/ + UPDATE ALISTLMS.FILE_MASTER + SET DEL_YN = 'Y' + , UPDATE_MEMBER = #{updateMember} + WHERE FILE_MASTER_IDX = #{fileMasterIdx} + AND DEL_YN = 'N' + + diff --git a/01-api-core/src/main/resources/mapper/standard/fileUploadEventLog/FileUploadEventLogMapper.xml b/01-api-core/src/main/resources/mapper/standard/fileUploadEventLog/FileUploadEventLogMapper.xml new file mode 100644 index 0000000..2ba66e8 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/fileUploadEventLog/FileUploadEventLogMapper.xml @@ -0,0 +1,115 @@ + + + + + + + FROM ALISTLMS.FILE_UPLOAD_EVENT_LOG + WHERE 1 = 1 + + AND FILE_UPLOAD_EVENT_LOG_IDX = #{fileUploadEventLogIdx} + + + AND FILE_DETAIL_IDX = #{fileDetailIdx} + + + AND EVENT_TYPE = #{eventType} + + + AND HTTP_STATUS = #{httpStatus} + + + + + + + + + + + /*FileUploadEventLogMapper.insertFileUploadEventLogAdd*/ + INSERT INTO ALISTLMS.FILE_UPLOAD_EVENT_LOG ( + FILE_DETAIL_IDX + , EVENT_TYPE + , OFFSET_BYTES + , BYTES_WRITTEN + , HTTP_STATUS + , MESSAGE + , RAW_JSON + ) VALUES ( + #{fileDetailIdx} + , #{eventType} + , #{offsetBytes} + , #{bytesWritten} + , #{httpStatus} + , #{message} + , #{rawJson} + ) + + + + /*FileUploadEventLogMapper.updateFileUploadEventLogModify*/ + UPDATE ALISTLMS.FILE_UPLOAD_EVENT_LOG + + + FILE_DETAIL_IDX = #{fileDetailIdx}, + + + EVENT_TYPE = #{eventType}, + + + OFFSET_BYTES = #{offsetBytes}, + + + BYTES_WRITTEN = #{bytesWritten}, + + + HTTP_STATUS = #{httpStatus}, + + + MESSAGE = #{message}, + + + RAW_JSON = #{rawJson}, + + + WHERE FILE_UPLOAD_EVENT_LOG_IDX = #{fileUploadEventLogIdx} + + + + /*FileUploadEventLogMapper.deleteFileUploadEventLogDelete*/ + DELETE FROM ALISTLMS.FILE_UPLOAD_EVENT_LOG + WHERE FILE_UPLOAD_EVENT_LOG_IDX = #{fileUploadEventLogIdx} + + diff --git a/01-api-core/src/main/resources/mapper/standard/notice/NoticeMapper.xml b/01-api-core/src/main/resources/mapper/standard/notice/NoticeMapper.xml new file mode 100644 index 0000000..699e6c1 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/notice/NoticeMapper.xml @@ -0,0 +1,131 @@ + + + + + + + FROM ALISTLMS.NOTICE + WHERE DEL_YN = 'N' + + AND NOTICE_IDX = #{noticeIdx} + + + AND NOTICE_TYPE = #{noticeType} + + + AND TARGET_SCOPE = #{targetScope} + + + AND TITLE LIKE CONCAT('%', #{title}, '%') + + + AND PIN_YN = #{pinYn} + + + AND USE_YN = #{useYn} + + + + + + + + + + + /*NoticeMapper.insertNoticeAdd*/ + INSERT INTO ALISTLMS.NOTICE ( + NOTICE_TYPE + , TARGET_SCOPE + , TITLE + , CONTENT + , PIN_YN + , START_DATE + , END_DATE + , VIEW_COUNT + , USE_YN + , DEL_YN + , CREATE_MEMBER + , UPDATE_MEMBER + ) VALUES ( + COALESCE(#{noticeType}, 'GENERAL') + , COALESCE(#{targetScope}, 'ALL') + , #{title} + , #{content} + , COALESCE(#{pinYn}, 'N') + , #{startDate} + , #{endDate} + , COALESCE(#{viewCount}, 0) + , COALESCE(#{useYn}, 'Y') + , 'N' + , #{createMember} + , #{updateMember} + ) + + + + /*NoticeMapper.updateNoticeModify*/ + UPDATE ALISTLMS.NOTICE + SET UPDATE_MEMBER = #{updateMember} + , NOTICE_TYPE = #{noticeType} + , TARGET_SCOPE = #{targetScope} + , TITLE = #{title} + , CONTENT = #{content} + , PIN_YN = #{pinYn} + , START_DATE = #{startDate} + , END_DATE = #{endDate} + , VIEW_COUNT = #{viewCount} + , USE_YN = #{useYn} + WHERE NOTICE_IDX = #{noticeIdx} + AND DEL_YN = 'N' + + + + /*NoticeMapper.updateNoticeDelete*/ + UPDATE ALISTLMS.NOTICE + SET DEL_YN = 'Y' + , UPDATE_MEMBER = #{updateMember} + WHERE NOTICE_IDX = #{noticeIdx} + AND DEL_YN = 'N' + + diff --git a/01-api-core/src/main/resources/mapper/standard/noticeCampus/NoticeCampusMapper.xml b/01-api-core/src/main/resources/mapper/standard/noticeCampus/NoticeCampusMapper.xml new file mode 100644 index 0000000..da4b7de --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/noticeCampus/NoticeCampusMapper.xml @@ -0,0 +1,82 @@ + + + + + + + FROM ALISTLMS.NOTICE_CAMPUS + WHERE 1 = 1 + + AND NOTICE_CAMPUS_IDX = #{noticeCampusIdx} + + + AND NOTICE_IDX = #{noticeIdx} + + + AND CAMPUS_IDX = #{campusIdx} + + + + + + + + + + + /*NoticeCampusMapper.insertNoticeCampusAdd*/ + INSERT INTO ALISTLMS.NOTICE_CAMPUS ( + NOTICE_IDX + , CAMPUS_IDX + , CREATE_MEMBER + ) VALUES ( + #{noticeIdx} + , #{campusIdx} + , #{createMember} + ) + + + + /*NoticeCampusMapper.updateNoticeCampusModify*/ + UPDATE ALISTLMS.NOTICE_CAMPUS + + + NOTICE_IDX = #{noticeIdx}, + + + CAMPUS_IDX = #{campusIdx}, + + + WHERE NOTICE_CAMPUS_IDX = #{noticeCampusIdx} + + + + /*NoticeCampusMapper.deleteNoticeCampusDelete*/ + DELETE FROM ALISTLMS.NOTICE_CAMPUS + WHERE NOTICE_CAMPUS_IDX = #{noticeCampusIdx} + + diff --git a/01-api-core/src/main/resources/mapper/standard/noticeFile/NoticeFileMapper.xml b/01-api-core/src/main/resources/mapper/standard/noticeFile/NoticeFileMapper.xml new file mode 100644 index 0000000..8841823 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/noticeFile/NoticeFileMapper.xml @@ -0,0 +1,111 @@ + + + + + + + FROM ALISTLMS.NOTICE_FILE + WHERE DEL_YN = 'N' + + AND NOTICE_FILE_IDX = #{noticeFileIdx} + + + AND NOTICE_IDX = #{noticeIdx} + + + AND FILE_EXT = #{fileExt} + + + + + + + + + + + /*NoticeFileMapper.insertNoticeFileAdd*/ + INSERT INTO ALISTLMS.NOTICE_FILE ( + NOTICE_IDX + , FILE_ORIGINAL_NAME + , FILE_SAVE_NAME + , FILE_PATH + , FILE_SIZE + , FILE_EXT + , SORT_ORDER + , DEL_YN + , CREATE_MEMBER + , UPDATE_MEMBER + ) VALUES ( + #{noticeIdx} + , #{fileOriginalName} + , #{fileSaveName} + , #{filePath} + , #{fileSize} + , #{fileExt} + , COALESCE(#{sortOrder}, 0) + , 'N' + , #{createMember} + , #{updateMember} + ) + + + + /*NoticeFileMapper.updateNoticeFileModify*/ + UPDATE ALISTLMS.NOTICE_FILE + SET UPDATE_MEMBER = #{updateMember} + , NOTICE_IDX = #{noticeIdx} + , FILE_ORIGINAL_NAME = #{fileOriginalName} + , FILE_SAVE_NAME = #{fileSaveName} + , FILE_PATH = #{filePath} + , FILE_SIZE = #{fileSize} + , FILE_EXT = #{fileExt} + , SORT_ORDER = #{sortOrder} + WHERE NOTICE_FILE_IDX = #{noticeFileIdx} + AND DEL_YN = 'N' + + + + /*NoticeFileMapper.updateNoticeFileDelete*/ + UPDATE ALISTLMS.NOTICE_FILE + SET DEL_YN = 'Y' + , UPDATE_MEMBER = #{updateMember} + WHERE NOTICE_FILE_IDX = #{noticeFileIdx} + AND DEL_YN = 'N' + + diff --git a/01-api-core/src/main/resources/mapper/standard/scheduleJob/ScheduleJobMapper.xml b/01-api-core/src/main/resources/mapper/standard/scheduleJob/ScheduleJobMapper.xml new file mode 100644 index 0000000..c636d5a --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/scheduleJob/ScheduleJobMapper.xml @@ -0,0 +1,197 @@ + + + + + + + FROM ALISTLMS.SCHEDULE_JOB + WHERE DEL_YN = 'N' + + AND SCHEDULE_JOB_IDX = #{scheduleJobIdx} + + + AND ( + JOB_NAME LIKE CONCAT('%', #{keyword}, '%') + OR JOB_DESCRIPTION LIKE CONCAT('%', #{keyword}, '%') + ) + + + AND JOB_NAME LIKE CONCAT('%', #{jobName}, '%') + + + AND JOB_GROUP = #{jobGroup} + + + AND LAST_STATUS = #{lastStatus} + + + AND USE_YN = #{useYn} + + + + + + + + + + + + + + + /*ScheduleJobMapper.insertScheduleJobAdd*/ + INSERT INTO ALISTLMS.SCHEDULE_JOB ( + JOB_NAME + , JOB_GROUP + , JOB_DESCRIPTION + , CRON_EXPR + , TIMEZONE_ID + , MAX_RUNNING_SECONDS + , USE_YN + , DEL_YN + , CREATE_MEMBER + , UPDATE_MEMBER + ) VALUES ( + #{jobName} + , COALESCE(#{jobGroup}, 'DEFAULT') + , #{jobDescription} + , #{cronExpr} + , COALESCE(#{timezoneId}, 'Asia/Seoul') + , COALESCE(#{maxRunningSeconds}, 1800) + , COALESCE(#{useYn}, 'Y') + , 'N' + , #{createMember} + , #{updateMember} + ) + + + + /*ScheduleJobMapper.updateScheduleJobModify*/ + UPDATE ALISTLMS.SCHEDULE_JOB + SET UPDATE_MEMBER = #{updateMember} + + , JOB_NAME = #{jobName} + + + , JOB_GROUP = #{jobGroup} + + + , JOB_DESCRIPTION = #{jobDescription} + + + , CRON_EXPR = #{cronExpr} + + + , TIMEZONE_ID = #{timezoneId} + + + , MAX_RUNNING_SECONDS = #{maxRunningSeconds} + + + , LAST_RUN_DATE = #{lastRunDate} + + + , LAST_END_DATE = #{lastEndDate} + + + , LAST_SUCCESS_DATE = #{lastSuccessDate} + + + , LAST_STATUS = #{lastStatus} + + + , LAST_MESSAGE = #{lastMessage} + + + , RUN_LOCK_DATE = #{runLockDate} + + + , RUN_LOCK_OWNER = #{runLockOwner} + + + , RUN_LOCK_DATE = NULL + , RUN_LOCK_OWNER = NULL + + + , USE_YN = #{useYn} + + WHERE SCHEDULE_JOB_IDX = #{scheduleJobIdx} + AND DEL_YN = 'N' + + + + /*ScheduleJobMapper.updateScheduleJobDelete*/ + UPDATE ALISTLMS.SCHEDULE_JOB + SET DEL_YN = 'Y' + , UPDATE_MEMBER = #{updateMember} + WHERE SCHEDULE_JOB_IDX = #{scheduleJobIdx} + AND DEL_YN = 'N' + + diff --git a/01-api-core/src/main/resources/mapper/standard/scheduleJobLog/ScheduleJobLogMapper.xml b/01-api-core/src/main/resources/mapper/standard/scheduleJobLog/ScheduleJobLogMapper.xml new file mode 100644 index 0000000..57d3b35 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/scheduleJobLog/ScheduleJobLogMapper.xml @@ -0,0 +1,146 @@ + + + + + + + FROM ALISTLMS.SCHEDULE_JOB_LOG + WHERE 1 = 1 + + AND SCHEDULE_JOB_LOG_IDX = #{scheduleJobLogIdx} + + + AND SCHEDULE_JOB_IDX = #{scheduleJobIdx} + + + AND RUN_ID = #{runId} + + + AND JOB_NAME LIKE CONCAT('%', #{jobName}, '%') + + + AND STATUS = #{status} + + + AND START_DATE =]]> #{startDateFrom} + + + AND START_DATE #{startDateTo} + + + + + + + + + + + /*ScheduleJobLogMapper.insertScheduleJobLogAdd*/ + INSERT INTO ALISTLMS.SCHEDULE_JOB_LOG ( + SCHEDULE_JOB_IDX + , RUN_ID + , JOB_NAME + , START_DATE + , TARGET_COUNT + , SUCCESS_COUNT + , FAIL_COUNT + , MESSAGE + , ERROR_MESSAGE + , LOCK_OWNER + , STATUS + ) VALUES ( + #{scheduleJobIdx} + , #{runId} + , #{jobName} + , #{startDate} + , COALESCE(#{targetCount}, 0) + , COALESCE(#{successCount}, 0) + , COALESCE(#{failCount}, 0) + , #{message} + , #{errorMessage} + , #{lockOwner} + , COALESCE(#{status}, 1) + ) + + + + /*ScheduleJobLogMapper.updateScheduleJobLogModify*/ + UPDATE ALISTLMS.SCHEDULE_JOB_LOG + + + END_DATE = #{endDate}, + + + TARGET_COUNT = #{targetCount}, + + + SUCCESS_COUNT = #{successCount}, + + + FAIL_COUNT = #{failCount}, + + + MESSAGE = #{message}, + + + ERROR_MESSAGE = #{errorMessage}, + + + LOCK_OWNER = #{lockOwner}, + + + STATUS = #{status}, + + + WHERE SCHEDULE_JOB_LOG_IDX = #{scheduleJobLogIdx} + + + + /*ScheduleJobLogMapper.deleteScheduleJobLogDelete*/ + DELETE FROM ALISTLMS.SCHEDULE_JOB_LOG + WHERE SCHEDULE_JOB_LOG_IDX = #{scheduleJobLogIdx} + + diff --git a/01-api-core/src/main/resources/mapper/standard/ssoClient/SsoClientMapper.xml b/01-api-core/src/main/resources/mapper/standard/ssoClient/SsoClientMapper.xml new file mode 100644 index 0000000..2c1ba0a --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/ssoClient/SsoClientMapper.xml @@ -0,0 +1,156 @@ + + + + + + + FROM ALISTLMS.SSO_CLIENT + WHERE DEL_YN = 'N' + + AND SSO_CLIENT_IDX = #{ssoClientIdx} + + + AND ( + CLIENT_ID LIKE CONCAT('%', #{keyword}, '%') + OR CLIENT_NAME LIKE CONCAT('%', #{keyword}, '%') + OR REDIRECT_URI LIKE CONCAT('%', #{keyword}, '%') + OR MEMO LIKE CONCAT('%', #{keyword}, '%') + ) + + + AND CLIENT_ID LIKE CONCAT('%', #{clientId}, '%') + + + AND CLIENT_NAME LIKE CONCAT('%', #{clientName}, '%') + + + AND USE_YN = #{useYn} + + + + + + + + + + + + + + + + + /*SsoClientMapper.insertSsoClientAdd*/ + INSERT INTO ALISTLMS.SSO_CLIENT ( + CLIENT_ID + , CLIENT_NAME + , REDIRECT_URI + , MEMO + , USE_YN + , DEL_YN + , CREATE_MEMBER + , UPDATE_MEMBER + ) VALUES ( + #{clientId} + , #{clientName} + , #{redirectUri} + , #{memo} + , COALESCE(#{useYn}, 'Y') + , 'N' + , #{createMember} + , #{updateMember} + ) + + + + /*SsoClientMapper.updateSsoClientModify*/ + UPDATE ALISTLMS.SSO_CLIENT + SET UPDATE_MEMBER = #{updateMember} + + , CLIENT_ID = #{clientId} + + + , CLIENT_NAME = #{clientName} + + + , REDIRECT_URI = #{redirectUri} + + + , MEMO = #{memo} + + + , USE_YN = #{useYn} + + WHERE SSO_CLIENT_IDX = #{ssoClientIdx} + AND DEL_YN = 'N' + + + + /*SsoClientMapper.updateSsoClientDelete*/ + UPDATE ALISTLMS.SSO_CLIENT + SET DEL_YN = 'Y' + , UPDATE_MEMBER = #{updateMember} + WHERE SSO_CLIENT_IDX = #{ssoClientIdx} + AND DEL_YN = 'N' + + + diff --git a/01-api-core/src/main/resources/mapper/standard/testCorsAllowedList/TestCorsAllowedListMapper.xml b/01-api-core/src/main/resources/mapper/standard/testCorsAllowedList/TestCorsAllowedListMapper.xml new file mode 100644 index 0000000..965cbe7 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/testCorsAllowedList/TestCorsAllowedListMapper.xml @@ -0,0 +1,103 @@ + + + + + + + FROM ALISTLMS.test_cors_allowed_list + WHERE DEL_YN = 1 + + AND CORS_IDX = #{corsIdx} + + + AND USER_TOKEN_IDX = #{userTokenIdx} + + + AND ALLOWED_ORIGIN LIKE CONCAT('%', #{allowedOrigin}, '%') + + + + + + + + + + + + + + + /*TestCorsAllowedListMapper.insertTestCorsAllowedListAdd*/ + INSERT INTO ALISTLMS.test_cors_allowed_list ( + USER_TOKEN_IDX + , ALLOWED_ORIGIN + , DEL_YN + , CREATED_AT + ) VALUES ( + COALESCE(#{userTokenIdx}, 0) + , #{allowedOrigin} + , 1 + , NOW() + ) + + + + /*TestCorsAllowedListMapper.updateTestCorsAllowedListModify*/ + UPDATE ALISTLMS.test_cors_allowed_list + + + USER_TOKEN_IDX = #{userTokenIdx}, + + + ALLOWED_ORIGIN = #{allowedOrigin}, + + + WHERE CORS_IDX = #{corsIdx} + AND DEL_YN = 1 + + + + /*TestCorsAllowedListMapper.updateTestCorsAllowedListDelete*/ + UPDATE ALISTLMS.test_cors_allowed_list + SET DEL_YN = 2 + WHERE CORS_IDX = #{corsIdx} + AND DEL_YN = 1 + + + diff --git a/01-api-core/src/main/resources/mapper/standard/testUser/TestUserMapper.xml b/01-api-core/src/main/resources/mapper/standard/testUser/TestUserMapper.xml new file mode 100644 index 0000000..7994108 --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/testUser/TestUserMapper.xml @@ -0,0 +1,151 @@ + + + + + + + FROM ALISTLMS.test_user + WHERE DEL_YN = 1 + + AND USER_IDX = #{userIdx} + + + AND ID LIKE CONCAT('%', #{id}, '%') + + + AND EMAIL LIKE CONCAT('%', #{email}, '%') + + + AND HP LIKE CONCAT('%', #{hp}, '%') + + + AND DORMANT_YN = #{dormantYn} + + + AND WITHDRAW_STATUS = #{withdrawStatus} + + + + + + + + + + + + + /*TestUserMapper.insertTestUserAdd*/ + INSERT INTO ALISTLMS.test_user ( + ID + , PASSWORD + , EMAIL + , HP + , DORMANT_YN + , WITHDRAW_STATUS + , DEL_YN + , CREATE_AT + , UPDATE_AT + ) VALUES ( + #{id} + , #{password} + , COALESCE(#{email}, '') + , COALESCE(#{hp}, '') + , 'N' + , 'N' + , 1 + , NOW() + , NOW() + ) + + + + /*TestUserMapper.updateTestUserModify*/ + UPDATE ALISTLMS.test_user + SET UPDATE_AT = NOW() + + , ID = #{id} + + + , PASSWORD = #{password} + + + , EMAIL = #{email} + + + , HP = #{hp} + + + , DORMANT_YN = #{dormantYn} + , DORMANT_AT = CASE + WHEN #{dormantYn} = 'Y' THEN NOW() + WHEN #{dormantYn} = 'N' THEN NULL + ELSE DORMANT_AT + END + + + , WITHDRAW_STATUS = #{withdrawStatus} + , WITHDRAW_AT = CASE + WHEN #{withdrawStatus} IN ('P', 'Y') THEN NOW() + WHEN #{withdrawStatus} = 'N' THEN NULL + ELSE WITHDRAW_AT + END + + + , LAST_LOGIN_AT = #{lastLoginAt} + + WHERE USER_IDX = #{userIdx} + AND DEL_YN = 1 + + + + /*TestUserMapper.updateTestUserDelete*/ + UPDATE ALISTLMS.test_user + SET DEL_YN = 2 + , UPDATE_AT = NOW() + WHERE USER_IDX = #{userIdx} + AND DEL_YN = 1 + + diff --git a/01-api-core/src/main/resources/mapper/standard/testUserToken/TestUserTokenMapper.xml b/01-api-core/src/main/resources/mapper/standard/testUserToken/TestUserTokenMapper.xml new file mode 100644 index 0000000..108445b --- /dev/null +++ b/01-api-core/src/main/resources/mapper/standard/testUserToken/TestUserTokenMapper.xml @@ -0,0 +1,122 @@ + + + + + + FROM ALISTLMS.test_user_token + WHERE 1 = 1 + + AND USER_TOKEN_IDX = #{userTokenIdx} + + + AND USER_IDX = #{userIdx} + + + AND USER_API_KEY LIKE CONCAT('%', #{userApiKey}, '%') + + + AND USER_ROLE = #{userRole} + + + AND USER_TYPE = #{userType} + + + + + + + + + + + /*TestUserTokenMapper.insertTestUserTokenAdd*/ + INSERT INTO ALISTLMS.test_user_token ( + USER_IDX + , USER_API_KEY + , USER_ROLE + , USER_TYPE + , REFRESH_TOKEN + , EXPIRES_AT + , CREATED_AT + , UPDATED_AT + ) VALUES ( + #{userIdx} + , #{userApiKey} + , #{userRole} + , #{userType} + , #{refreshToken} + , #{expiresAt} + , NOW() + , NOW() + ) + + + + /*TestUserTokenMapper.updateTestUserTokenModify*/ + UPDATE ALISTLMS.test_user_token + SET UPDATED_AT = NOW() + + , USER_IDX = #{userIdx} + + + , USER_API_KEY = #{userApiKey} + + + , USER_ROLE = #{userRole} + + + , USER_TYPE = #{userType} + + + + + , REFRESH_TOKEN = NULL + , EXPIRES_AT = NULL + + + , REFRESH_TOKEN = #{refreshToken} + + + + + , EXPIRES_AT = #{expiresAt} + + WHERE USER_TOKEN_IDX = #{userTokenIdx} + + + + /*TestUserTokenMapper.deleteTestUserTokenDelete*/ + DELETE FROM ALISTLMS.test_user_token + WHERE USER_TOKEN_IDX = #{userTokenIdx} + + \ No newline at end of file diff --git a/02-api-admin/Dockerfile b/02-api-admin/Dockerfile new file mode 100644 index 0000000..8b39dc4 --- /dev/null +++ b/02-api-admin/Dockerfile @@ -0,0 +1,13 @@ +FROM amazoncorretto:21 + +RUN yum -y install curl && yum clean all + +WORKDIR /api + +COPY build/libs/api-admin.jar api-admin.jar + +ENV JAVA_OPTS="" + +EXPOSE 8111 + +ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /api/api-admin.jar"] diff --git a/02-api-admin/build.gradle b/02-api-admin/build.gradle new file mode 100644 index 0000000..06f5afb --- /dev/null +++ b/02-api-admin/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'org.springframework.boot' +} + +dependencies { + implementation project(':01-api-core') + + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.0' + + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +bootJar { + archiveFileName = 'api-admin.jar' +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/ApiAdminApplication.java b/02-api-admin/src/main/java/com/alist/api/admin/ApiAdminApplication.java new file mode 100644 index 0000000..f19bf55 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/ApiAdminApplication.java @@ -0,0 +1,15 @@ +package com.alist.api.admin; + +import com.alist.api.core.config.ApiCoreConfig; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Import; + +@Import(ApiCoreConfig.class) +@SpringBootApplication +public class ApiAdminApplication { + + public static void main(String[] args) { + SpringApplication.run(ApiAdminApplication.class, args); + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/config/AdminCorsCacheBootstrap.java b/02-api-admin/src/main/java/com/alist/api/admin/config/AdminCorsCacheBootstrap.java new file mode 100644 index 0000000..9cdc726 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/config/AdminCorsCacheBootstrap.java @@ -0,0 +1,24 @@ +package com.alist.api.admin.config; + +import com.alist.api.core.config.cors.CorsCache; +import com.alist.api.core.config.cors.CorsCachePublisher; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +@Component +public class AdminCorsCacheBootstrap { + private final CorsCache corsCache; + private final CorsCachePublisher corsCachePublisher; + + public AdminCorsCacheBootstrap(CorsCache corsCache, CorsCachePublisher corsCachePublisher) { + this.corsCache = corsCache; + this.corsCachePublisher = corsCachePublisher; + } + + @EventListener(ApplicationReadyEvent.class) + public void initializeCorsCache() { + corsCache.syncFromDatabase(); + corsCachePublisher.publishReload(); + } +} diff --git a/src/main/java/com/alist/api/config/OpenApiConfig.java b/02-api-admin/src/main/java/com/alist/api/admin/config/OpenApiConfig.java similarity index 56% rename from src/main/java/com/alist/api/config/OpenApiConfig.java rename to 02-api-admin/src/main/java/com/alist/api/admin/config/OpenApiConfig.java index 847d5b7..47b0504 100644 --- a/src/main/java/com/alist/api/config/OpenApiConfig.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/config/OpenApiConfig.java @@ -1,11 +1,10 @@ -package com.alist.api.config; +package com.alist.api.admin.config; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; -import org.springdoc.core.models.GroupedOpenApi; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -21,9 +20,9 @@ public class OpenApiConfig { public OpenAPI openAPI() { return new OpenAPI() .info(new Info() - .title("A*List Api App") + .title("A*List Admin API") .version(apiVersion) - .description("A*List Api App 입니다.!")) + .description("A*List Admin API 입니다.!")) .addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME)) .components(new Components() .addSecuritySchemes(SECURITY_SCHEME_NAME, @@ -35,40 +34,4 @@ public class OpenApiConfig { ) ); } - - @Bean - public GroupedOpenApi frontApi() { - return GroupedOpenApi.builder() - .group("100. front") - .pathsToMatch("/**") - .pathsToExclude( - "/admin/**" - , "/cors/**" - , "/file/**" - , "/tus/file/**" - , "/error" - , "/actuator/**" - ) - .build(); - } - - @Bean - public GroupedOpenApi adminApi() { - return GroupedOpenApi.builder() - .group("500. admin") - .pathsToMatch("/admin/**") - .build(); - } - - @Bean - public GroupedOpenApi commonApi() { - return GroupedOpenApi.builder() - .group("900. common") - .pathsToMatch( - "/cors/**" - , "/file/**" - , "/tus/file/**" - ) - .build(); - } } diff --git a/src/main/java/com/alist/api/config/SecurityConfig.java b/02-api-admin/src/main/java/com/alist/api/admin/config/SecurityConfig.java similarity index 75% rename from src/main/java/com/alist/api/config/SecurityConfig.java rename to 02-api-admin/src/main/java/com/alist/api/admin/config/SecurityConfig.java index ad3ac45..92bae53 100644 --- a/src/main/java/com/alist/api/config/SecurityConfig.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/config/SecurityConfig.java @@ -1,9 +1,9 @@ -package com.alist.api.config; +package com.alist.api.admin.config; -import com.alist.api.config.jwt.JwtAccessDeniedHandler; -import com.alist.api.config.jwt.JwtAuthenticationEntryPoint; -import com.alist.api.config.jwt.JwtAuthenticationFilter; -import com.alist.api.config.jwt.JwtTokenProvider; +import com.alist.api.core.common.jwt.JwtAccessDeniedHandler; +import com.alist.api.core.common.jwt.JwtAuthenticationEntryPoint; +import com.alist.api.core.common.jwt.JwtAuthenticationFilter; +import com.alist.api.core.common.jwt.JwtTokenProvider; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -33,7 +33,7 @@ public class SecurityConfig { @Bean public JwtAuthenticationFilter jwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { - return new JwtAuthenticationFilter(jwtTokenProvider); + return new JwtAuthenticationFilter(jwtTokenProvider, "ADMIN", "adminAccessToken"); } @Bean @@ -50,28 +50,6 @@ public class SecurityConfig { @Bean @Order(2) - public SecurityFilterChain adminFilterChain( - HttpSecurity http, - JwtAuthenticationFilter jwtAuthenticationFilter - ) throws Exception { - http - .securityMatcher("/admin/**") - .cors(Customizer.withDefaults()) - .csrf(csrf -> csrf.disable()) - .authorizeHttpRequests(auth -> auth - .requestMatchers( - "/admin/auth/**" - , "/admin/user/add" - ).permitAll() - .anyRequest().hasRole("ADMIN") - ) - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - - return http.build(); - } - - @Bean - @Order(3) public SecurityFilterChain apiFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception { http .cors(Customizer.withDefaults()) @@ -84,15 +62,13 @@ public class SecurityConfig { .requestMatchers( "/" , "/actuator/health" - , "/sso/**" , "/auth/**" , "/user/add" - , "/user/migration/list" , "/tus/file/hook" , "/tus/file/upload/auth" , "/file/**" ).permitAll() - .anyRequest().authenticated() + .anyRequest().hasRole("ADMIN") ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/AuthController.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/AuthController.java new file mode 100644 index 0000000..0ecd328 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/AuthController.java @@ -0,0 +1,142 @@ +package com.alist.api.admin.modules.auth; + +import com.alist.api.admin.modules.auth.form.AuthApiKeyLoginForm; +import com.alist.api.admin.modules.auth.form.AuthLoginForm; +import com.alist.api.admin.modules.auth.service.AuthService; +import com.alist.api.admin.modules.auth.vo.AuthApiKeyLoginVo; +import com.alist.api.admin.modules.auth.vo.AuthLoginCheckedVo; +import com.alist.api.admin.modules.auth.vo.AuthLoginVo; +import com.alist.api.admin.modules.auth.vo.AuthLogoutVo; +import com.alist.api.admin.modules.auth.vo.AuthRefreshVo; +import com.alist.api.admin.modules.auth.form.AuthApiRefreshForm; +import com.alist.api.admin.modules.auth.vo.AuthApiRefreshVo; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import com.alist.api.core.common.utils.SecurityUtil; +import com.alist.api.core.common.utils.SessionUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag( + name = "A-1. Admin 로그인" + , description = "관리자 인증 및 토큰 발급 API" +) +@RestController +@RequestMapping("/auth") +public class AuthController { + private final AuthService authService; + + public AuthController(AuthService authService) { + this.authService = authService; + } + + @Operation( + summary = "관리자 API Key 로그인" + , description = "Swagger 및 외부 API 테스트용으로 Access Token과 Refresh Token을 JSON으로 반환합니다." + ) + @PostMapping("/apiKeyLogin") + public ResponseEntity> apiKeyLogin( + @Valid @RequestBody AuthApiKeyLoginForm authApiKeyLoginForm + ) { + AuthApiKeyLoginVo authApiKeyLoginVo = authService.selectApiKeyLogin(authApiKeyLoginForm); + + if (authApiKeyLoginVo.getResultCode() == 2003) { + return ApiResponse.entity(authApiKeyLoginVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(authApiKeyLoginVo, ApiResponseCode.CODE_2001, "관리자 Access Token 발급"); + } + + @Operation( + summary = "관리자 로그인" + , description = "아이디와 비밀번호를 검증하고 HttpOnly Access/Refresh Token 쿠키를 발급합니다." + ) + @PostMapping("/login") + public ResponseEntity> login( + @Valid @RequestBody AuthLoginForm authLoginForm + , HttpServletResponse response + ) { + AuthLoginVo authLoginVo = authService.selectLogin(authLoginForm, response); + + if (authLoginVo.getResultCode() == 2003) { + return ApiResponse.entity(authLoginVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(authLoginVo, ApiResponseCode.CODE_2001, "관리자 로그인"); + } + + @Operation( + summary = "관리자 로그인 상태 확인" + , description = "현재 Access/Refresh Token 쿠키 존재 여부와 로그인한 관리자 정보를 조회합니다." + ) + @GetMapping("/loginChecked") + public ResponseEntity> loginChecked( + HttpServletRequest request + ) { + String accessToken = SessionUtil.resolveSsoCookieValue(request, "adminAccessToken"); + String refreshToken = SessionUtil.resolveSsoCookieValue(request, "adminRefreshToken"); + + AuthLoginCheckedVo authLoginCheckedVo = authService.selectLoginChecked(SecurityUtil.getLoginUserTokenIdx(), accessToken != null && !accessToken.isBlank(), refreshToken != null && !refreshToken.isBlank()); + + return ApiResponse.entity(authLoginCheckedVo, ApiResponseCode.CODE_200); + } + + @Operation( + summary = "관리자 로그아웃" + , description = "저장된 Refresh Token을 무효화하고 Access/Refresh Token 쿠키를 삭제합니다." + ) + @PostMapping("/logout") + public ResponseEntity> logout( + @CookieValue(name = "adminRefreshToken", required = false) String refreshToken + , HttpServletResponse response + ) { + AuthLogoutVo authLogoutVo = authService.updateLogout(refreshToken, response); + + return ApiResponse.entity(authLogoutVo, ApiResponseCode.CODE_200); + } + + @Operation( + summary = "관리자 Access Token 재발급" + , description = "유효한 Refresh Token을 검증하고 Access/Refresh Token 쿠키를 새로 발급합니다." + ) + @PostMapping("/refresh") + public ResponseEntity> refresh( + @CookieValue(name = "adminRefreshToken", required = false) String refreshToken + , HttpServletResponse response + ) { + AuthRefreshVo authRefreshVo = authService.selectRefresh(refreshToken, response); + + if (authRefreshVo.getResultCode() == 401) { + return ApiResponse.entity(authRefreshVo, ApiResponseCode.CODE_401); + } + + return ApiResponse.entity(authRefreshVo, ApiResponseCode.CODE_200); + } + + @Operation( + summary = "외부 API 관리자 토큰 재발급" + , description = "JSON의 관리자 Refresh Token을 검증하고 새 Access/Refresh Token을 JSON으로 반환합니다. Cookie를 사용하지 않습니다." + ) + @PostMapping("/apiRefresh") + public ResponseEntity> apiRefresh( + @Valid @RequestBody AuthApiRefreshForm authApiRefreshForm + ) { + AuthApiRefreshVo authApiRefreshVo = authService.selectApiRefresh(authApiRefreshForm); + + if (authApiRefreshVo.getResultCode() == 401) { + return ApiResponse.entity(authApiRefreshVo, ApiResponseCode.CODE_401); + } + + return ApiResponse.entity(authApiRefreshVo, ApiResponseCode.CODE_200, "관리자 API 토큰 재발급"); + } +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthApiKeyLoginForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthApiKeyLoginForm.java new file mode 100644 index 0000000..4001c89 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthApiKeyLoginForm.java @@ -0,0 +1,16 @@ +package com.alist.api.admin.modules.auth.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 API Key 로그인 요청") +@Getter +@Setter +public class AuthApiKeyLoginForm { + @Schema(description = "발급된 API Key", example = "ypdz9hl7WAp2D03HC5h9koEC49R6LAkLSuoSdgEwVQA", + requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "API Key는 필수입니다.") + private String userApiKey; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthApiRefreshForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthApiRefreshForm.java new file mode 100644 index 0000000..49bb47f --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthApiRefreshForm.java @@ -0,0 +1,19 @@ +package com.alist.api.admin.modules.auth.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "외부 API 관리자 refresh 요청") +@Getter +@Setter +public class AuthApiRefreshForm { + @Schema( + description = "API Key 로그인 또는 이전 API refresh 응답에서 받은 관리자 Refresh Token" + , example = "eyJhbGciOiJIUzI1NiJ9..." + , requiredMode = Schema.RequiredMode.REQUIRED + ) + @NotBlank(message = "관리자 Refresh Token은 필수입니다.") + private String adminRefreshToken; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthLoginForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthLoginForm.java new file mode 100644 index 0000000..1d70c17 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/form/AuthLoginForm.java @@ -0,0 +1,19 @@ +package com.alist.api.admin.modules.auth.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 로그인 요청") +@Getter +@Setter +public class AuthLoginForm { + @Schema(description = "관리자 아이디", example = "admin", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "아이디는 필수입니다.") + private String id; + + @Schema(description = "관리자 비밀번호", example = "password", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "비밀번호는 필수입니다.") + private String password; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/service/AuthService.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/service/AuthService.java new file mode 100644 index 0000000..9341331 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/service/AuthService.java @@ -0,0 +1,322 @@ +package com.alist.api.admin.modules.auth.service; + +import com.alist.api.admin.modules.auth.form.AuthApiKeyLoginForm; +import com.alist.api.admin.modules.auth.form.AuthLoginForm; +import com.alist.api.admin.modules.auth.vo.AuthApiKeyLoginVo; +import com.alist.api.admin.modules.auth.vo.AuthLoginCheckedVo; +import com.alist.api.admin.modules.auth.vo.AuthLoginVo; +import com.alist.api.admin.modules.auth.vo.AuthLogoutVo; +import com.alist.api.admin.modules.auth.vo.AuthRefreshVo; +import com.alist.api.admin.modules.auth.form.AuthApiRefreshForm; +import com.alist.api.admin.modules.auth.vo.AuthApiRefreshVo; +import com.alist.api.core.common.jwt.JwtTokenProvider; +import com.alist.api.core.common.utils.SessionUtil; +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserLoginViewDto; +import com.alist.api.core.modules.bespoke.testUser.service.TestUserAuthService; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserLoginViewVo; +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenApiKeyLoginViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenLoginCheckedViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenRefreshViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.service.TestUserTokenAuthService; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenApiKeyLoginViewVo; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenLoginCheckedViewVo; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenRefreshViewVo; +import com.alist.api.core.modules.standard.testUser.dto.TestUserModifyDto; +import com.alist.api.core.modules.standard.testUser.service.TestUserService; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenModifyDto; +import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Slf4j +@Service +public class AuthService { + private static final String ADMIN_USER_TYPE = "A"; + private static final String ADMIN_ROLE = "ADMIN"; + private static final String ADMIN_SCOPE = "ADMIN"; + private static final String REFRESH_TOKEN_TYPE = "REFRESH"; + + private static final String ACCESS_TOKEN_COOKIE_NAME = "adminAccessToken"; + private static final String REFRESH_TOKEN_COOKIE_NAME = "adminRefreshToken"; + + @Value("${cookie.secure}") + private boolean cookieSecure; + + @Value("${cookie.domain:}") + private String cookieDomain; + + @Value("${cookie.same-site:Lax}") + private String cookieSameSite; + + @Value("${jwt.access-token-validity-seconds}") + private long accessTokenValiditySeconds; + + @Value("${jwt.refresh-token-validity-seconds}") + private long refreshTokenValiditySeconds; + + private final TestUserAuthService testUserAuthService; + private final TestUserTokenAuthService testUserTokenAuthService; + private final TestUserService testUserService; + private final TestUserTokenService testUserTokenService; + private final JwtTokenProvider jwtTokenProvider; + private final PasswordEncoder passwordEncoder; + + public AuthService(TestUserAuthService testUserAuthService + , TestUserTokenAuthService testUserTokenAuthService + , TestUserService testUserService + , TestUserTokenService testUserTokenService + , JwtTokenProvider jwtTokenProvider + , PasswordEncoder passwordEncoder + ) { + this.testUserAuthService = testUserAuthService; + this.testUserTokenAuthService = testUserTokenAuthService; + this.testUserService = testUserService; + this.testUserTokenService = testUserTokenService; + this.jwtTokenProvider = jwtTokenProvider; + this.passwordEncoder = passwordEncoder; + } + + @Transactional + public AuthLoginVo selectLogin(AuthLoginForm authLoginForm, HttpServletResponse response) { + TestUserLoginViewDto testUserLoginViewDto = new TestUserLoginViewDto(); + testUserLoginViewDto.setId(authLoginForm.getId().trim()); + testUserLoginViewDto.setUserType(ADMIN_USER_TYPE); + + TestUserLoginViewVo testUserLoginViewVo = + testUserAuthService.testUserLoginView(testUserLoginViewDto); + + if (testUserLoginViewVo == null + || !passwordEncoder.matches( + authLoginForm.getPassword() + , testUserLoginViewVo.getPassword() + )) { + return createLoginFailVo(); + } + + issueTokenCookie(testUserLoginViewVo.getUserTokenIdx(), response); + updateLastLoginAt(testUserLoginViewVo.getUserIdx()); + + AuthLoginVo authLoginVo = new AuthLoginVo(); + authLoginVo.setLogin(true); + authLoginVo.setUserIdx(testUserLoginViewVo.getUserIdx()); + authLoginVo.setId(testUserLoginViewVo.getId()); + authLoginVo.setUserRole(testUserLoginViewVo.getUserRole()); + authLoginVo.setUserType(testUserLoginViewVo.getUserType()); + authLoginVo.setResultCode(2001); + + return authLoginVo; + } + + @Transactional + public AuthApiKeyLoginVo selectApiKeyLogin(AuthApiKeyLoginForm authApiKeyLoginForm) { + TestUserTokenApiKeyLoginViewDto testUserTokenApiKeyLoginViewDto = new TestUserTokenApiKeyLoginViewDto(); + testUserTokenApiKeyLoginViewDto.setUserApiKey(authApiKeyLoginForm.getUserApiKey().trim()); + testUserTokenApiKeyLoginViewDto.setUserType(ADMIN_USER_TYPE); + + TestUserTokenApiKeyLoginViewVo testUserTokenApiKeyLoginViewVo = testUserTokenAuthService.testUserTokenApiKeyLoginView(testUserTokenApiKeyLoginViewDto); + + if (testUserTokenApiKeyLoginViewVo == null) { + return createApiKeyLoginFailVo(); + } + + String adminAccessToken = jwtTokenProvider.createAdminAccessToken(testUserTokenApiKeyLoginViewVo.getUserTokenIdx()); + String adminRefreshToken = jwtTokenProvider.createAdminRefreshToken(testUserTokenApiKeyLoginViewVo.getUserTokenIdx()); + + updateRefreshToken(testUserTokenApiKeyLoginViewVo.getUserTokenIdx(), adminRefreshToken); + updateLastLoginAt(testUserTokenApiKeyLoginViewVo.getUserIdx()); + + AuthApiKeyLoginVo authApiKeyLoginVo = new AuthApiKeyLoginVo(); + authApiKeyLoginVo.setLogin(true); + authApiKeyLoginVo.setUserIdx(testUserTokenApiKeyLoginViewVo.getUserIdx()); + authApiKeyLoginVo.setId(testUserTokenApiKeyLoginViewVo.getId()); + authApiKeyLoginVo.setUserRole(testUserTokenApiKeyLoginViewVo.getUserRole()); + authApiKeyLoginVo.setUserType(testUserTokenApiKeyLoginViewVo.getUserType()); + authApiKeyLoginVo.setAdminAccessToken(adminAccessToken); + authApiKeyLoginVo.setAdminRefreshToken(adminRefreshToken); + authApiKeyLoginVo.setResultCode(2001); + + return authApiKeyLoginVo; + } + + @Transactional(readOnly = true) + public AuthLoginCheckedVo selectLoginChecked(Integer userTokenIdx, boolean accessTokenExists, boolean refreshTokenExists) { + AuthLoginCheckedVo authLoginCheckedVo = new AuthLoginCheckedVo(); + authLoginCheckedVo.setAccessToken(accessTokenExists); + authLoginCheckedVo.setRefreshToken(refreshTokenExists); + + if (userTokenIdx == null) { + authLoginCheckedVo.setLoggedIn(false); + return authLoginCheckedVo; + } + + TestUserTokenLoginCheckedViewDto testUserTokenLoginCheckedViewDto = new TestUserTokenLoginCheckedViewDto(); + testUserTokenLoginCheckedViewDto.setUserTokenIdx(userTokenIdx); + testUserTokenLoginCheckedViewDto.setUserType(ADMIN_USER_TYPE); + + TestUserTokenLoginCheckedViewVo testUserTokenLoginCheckedViewVo = testUserTokenAuthService.testUserTokenLoginCheckedView(testUserTokenLoginCheckedViewDto); + + if (testUserTokenLoginCheckedViewVo == null) { + authLoginCheckedVo.setLoggedIn(false); + return authLoginCheckedVo; + } + + authLoginCheckedVo.setLoggedIn(true); + authLoginCheckedVo.setUserIdx(testUserTokenLoginCheckedViewVo.getUserIdx()); + authLoginCheckedVo.setUserTokenIdx(testUserTokenLoginCheckedViewVo.getUserTokenIdx()); + authLoginCheckedVo.setId(testUserTokenLoginCheckedViewVo.getId()); + authLoginCheckedVo.setUserRole(testUserTokenLoginCheckedViewVo.getUserRole()); + authLoginCheckedVo.setUserType(testUserTokenLoginCheckedViewVo.getUserType()); + + return authLoginCheckedVo; + } + + @Transactional + public AuthRefreshVo selectRefresh(String refreshToken, HttpServletResponse response) { + AuthRefreshVo authRefreshVo = new AuthRefreshVo(); + + TestUserTokenRefreshViewVo testUserTokenRefreshViewVo = selectRefreshTokenOwner(refreshToken); + + if (testUserTokenRefreshViewVo == null) { + authRefreshVo.setRefreshed(false); + authRefreshVo.setResultCode(401); + return authRefreshVo; + } + + issueTokenCookie(testUserTokenRefreshViewVo.getUserTokenIdx(), response); + + authRefreshVo.setRefreshed(true); + authRefreshVo.setResultCode(200); + + return authRefreshVo; + } + + @Transactional + public AuthApiRefreshVo selectApiRefresh(AuthApiRefreshForm authApiRefreshForm) { + AuthApiRefreshVo authApiRefreshVo = new AuthApiRefreshVo(); + + String previousRefreshToken = authApiRefreshForm.getAdminRefreshToken().trim(); + + TestUserTokenRefreshViewVo testUserTokenRefreshViewVo = selectRefreshTokenOwner(previousRefreshToken); + + if (testUserTokenRefreshViewVo == null) { + authApiRefreshVo.setRefreshed(false); + authApiRefreshVo.setResultCode(401); + return authApiRefreshVo; + } + + String adminAccessToken = jwtTokenProvider.createAdminAccessToken(testUserTokenRefreshViewVo.getUserTokenIdx()); + String adminRefreshToken = jwtTokenProvider.createAdminRefreshToken(testUserTokenRefreshViewVo.getUserTokenIdx()); + + updateRefreshToken(testUserTokenRefreshViewVo.getUserTokenIdx(), adminRefreshToken); + + authApiRefreshVo.setRefreshed(true); + authApiRefreshVo.setAdminAccessToken(adminAccessToken); + authApiRefreshVo.setAdminRefreshToken(adminRefreshToken); + authApiRefreshVo.setResultCode(200); + + return authApiRefreshVo; + } + + @Transactional + public AuthLogoutVo updateLogout(String refreshToken, HttpServletResponse response) { + TestUserTokenRefreshViewVo testUserTokenRefreshViewVo = selectRefreshTokenOwner(refreshToken); + + if (testUserTokenRefreshViewVo != null) { + TestUserTokenModifyDto testUserTokenModifyDto = new TestUserTokenModifyDto(); + testUserTokenModifyDto.setUserTokenIdx(testUserTokenRefreshViewVo.getUserTokenIdx()); + testUserTokenModifyDto.setRefreshToken(""); + + testUserTokenService.updateTestUserTokenModify(testUserTokenModifyDto); + } + + SessionUtil.expireCookie(response, ACCESS_TOKEN_COOKIE_NAME, cookieDomain, cookieSecure, cookieSameSite); + SessionUtil.expireCookie(response, REFRESH_TOKEN_COOKIE_NAME, cookieDomain, cookieSecure, cookieSameSite); + + AuthLogoutVo authLogoutVo = new AuthLogoutVo(); + authLogoutVo.setLoggedOut(true); + return authLogoutVo; + } + + private void issueTokenCookie(Integer userTokenIdx, HttpServletResponse response) { + String accessToken = jwtTokenProvider.createAdminAccessToken(userTokenIdx); + String refreshToken = jwtTokenProvider.createAdminRefreshToken(userTokenIdx); + + updateRefreshToken(userTokenIdx, refreshToken); + + SessionUtil.addTokenCookie(response, ACCESS_TOKEN_COOKIE_NAME, accessToken, cookieDomain, cookieSecure, cookieSameSite, accessTokenValiditySeconds); + SessionUtil.addTokenCookie(response, REFRESH_TOKEN_COOKIE_NAME, refreshToken, cookieDomain, cookieSecure, cookieSameSite, refreshTokenValiditySeconds); + } + + private void updateRefreshToken(Integer userTokenIdx, String refreshToken) { + TestUserTokenModifyDto testUserTokenModifyDto = new TestUserTokenModifyDto(); + testUserTokenModifyDto.setUserTokenIdx(userTokenIdx); + testUserTokenModifyDto.setRefreshToken(refreshToken); + testUserTokenModifyDto.setExpiresAt(LocalDateTime.now().plusSeconds(refreshTokenValiditySeconds)); + + testUserTokenService.updateTestUserTokenModify(testUserTokenModifyDto); + } + + private void updateLastLoginAt(Integer userIdx) { + TestUserModifyDto testUserModifyDto = new TestUserModifyDto(); + testUserModifyDto.setUserIdx(userIdx); + testUserModifyDto.setLastLoginAt(LocalDateTime.now()); + + testUserService.updateTestUserModify(testUserModifyDto); + } + + private TestUserTokenRefreshViewVo selectRefreshTokenOwner(String refreshToken) { + if (!isAdminRefreshToken(refreshToken)) { + return null; + } + + Integer userTokenIdx; + + try { + userTokenIdx = Integer.valueOf(jwtTokenProvider.getUserTokenIdx(refreshToken)); + } catch (NumberFormatException e) { + return null; + } + + TestUserTokenRefreshViewDto testUserTokenRefreshViewDto = new TestUserTokenRefreshViewDto(); + testUserTokenRefreshViewDto.setUserTokenIdx(userTokenIdx); + testUserTokenRefreshViewDto.setUserType(ADMIN_USER_TYPE); + + TestUserTokenRefreshViewVo testUserTokenRefreshViewVo = testUserTokenAuthService.testUserTokenRefreshView(testUserTokenRefreshViewDto); + + if (testUserTokenRefreshViewVo == null || !refreshToken.equals(testUserTokenRefreshViewVo.getRefreshToken())) { + return null; + } + + return testUserTokenRefreshViewVo; + } + + private boolean isAdminRefreshToken(String refreshToken) { + return refreshToken != null + && jwtTokenProvider.validateToken(refreshToken) + && ADMIN_ROLE.equals(jwtTokenProvider.getRole(refreshToken)) + && ADMIN_SCOPE.equals(jwtTokenProvider.getScope(refreshToken)) + && REFRESH_TOKEN_TYPE.equals( + jwtTokenProvider.getTokenType(refreshToken) + ); + } + + private AuthLoginVo createLoginFailVo() { + AuthLoginVo authLoginVo = new AuthLoginVo(); + authLoginVo.setLogin(false); + authLoginVo.setResultCode(2003); + return authLoginVo; + } + + private AuthApiKeyLoginVo createApiKeyLoginFailVo() { + AuthApiKeyLoginVo authApiKeyLoginVo = new AuthApiKeyLoginVo(); + authApiKeyLoginVo.setLogin(false); + authApiKeyLoginVo.setResultCode(2003); + return authApiKeyLoginVo; + } +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthApiKeyLoginVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthApiKeyLoginVo.java new file mode 100644 index 0000000..45773ed --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthApiKeyLoginVo.java @@ -0,0 +1,22 @@ +package com.alist.api.admin.modules.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 API Key 로그인 응답") +@Getter +@Setter +public class AuthApiKeyLoginVo extends AuthLoginVo { + @Schema( + description = "관리자 Access Token. Authorization Bearer 토큰으로 사용합니다." + , example = "eyJhbGciOiJIUzI1NiJ9..." + ) + private String adminAccessToken; + + @Schema( + description = "관리자 Refresh Token. 관리자 refresh 처리용 토큰입니다." + , example = "eyJhbGciOiJIUzI1NiJ9..." + ) + private String adminRefreshToken; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthApiRefreshVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthApiRefreshVo.java new file mode 100644 index 0000000..cb0ca2e --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthApiRefreshVo.java @@ -0,0 +1,22 @@ +package com.alist.api.admin.modules.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "외부 API 관리자 refresh 응답") +@Getter +@Setter +public class AuthApiRefreshVo extends AuthRefreshVo { + @Schema( + description = "새로 발급된 관리자 Access Token" + , example = "eyJhbGciOiJIUzI1NiJ9..." + ) + private String adminAccessToken; + + @Schema( + description = "새로 발급된 관리자 Refresh Token. 다음 API refresh 요청에 사용합니다." + , example = "eyJhbGciOiJIUzI1NiJ9..." + ) + private String adminRefreshToken; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLoginCheckedVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLoginCheckedVo.java new file mode 100644 index 0000000..c2b473c --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLoginCheckedVo.java @@ -0,0 +1,34 @@ +package com.alist.api.admin.modules.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 로그인 상태 응답") +@Getter +@Setter +public class AuthLoginCheckedVo { + @Schema(description = "로그인 여부", example = "true") + private boolean loggedIn; + + @Schema(description = "Access Token 쿠키 존재 여부", example = "true") + private boolean accessToken; + + @Schema(description = "Refresh Token 쿠키 존재 여부", example = "true") + private boolean refreshToken; + + @Schema(description = "회원 PK", example = "1") + private Integer userIdx; + + @Schema(description = "사용자 토큰 PK", example = "1") + private Integer userTokenIdx; + + @Schema(description = "관리자 아이디", example = "admin") + private String id; + + @Schema(description = "권한 코드", example = "A") + private String userRole; + + @Schema(description = "회원 유형", example = "A") + private String userType; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLoginVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLoginVo.java new file mode 100644 index 0000000..e2ecfbf --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLoginVo.java @@ -0,0 +1,30 @@ +package com.alist.api.admin.modules.auth.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 로그인 응답") +@Getter +@Setter +public class AuthLoginVo { + @Schema(description = "로그인 성공 여부", example = "true") + private boolean login; + + @Schema(description = "회원 PK", example = "1") + private Integer userIdx; + + @Schema(description = "관리자 아이디", example = "admin") + private String id; + + @Schema(description = "권한 코드", example = "A") + private String userRole; + + @Schema(description = "회원 유형", example = "A") + private String userType; + + @JsonIgnore + @Schema(hidden = true) + private Integer resultCode; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLogoutVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLogoutVo.java new file mode 100644 index 0000000..425e420 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthLogoutVo.java @@ -0,0 +1,13 @@ +package com.alist.api.admin.modules.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "Admin 로그아웃 응답") +@Getter +@Setter +public class AuthLogoutVo { + @Schema(description = "로그아웃 처리 여부", example = "true") + private boolean loggedOut; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthRefreshVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthRefreshVo.java new file mode 100644 index 0000000..b986541 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/auth/vo/AuthRefreshVo.java @@ -0,0 +1,19 @@ +package com.alist.api.admin.modules.auth.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "Admin 리프레시 토큰 발급 응답") +@Getter +@Setter +public class AuthRefreshVo { + @Schema(description = "리프레시 처리 여부", example = "true") + private boolean refreshed; + + @JsonIgnore + @Schema(hidden = true) + private Integer resultCode; +} + diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/AdminMemberController.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/AdminMemberController.java new file mode 100644 index 0000000..3acb880 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/AdminMemberController.java @@ -0,0 +1,164 @@ +package com.alist.api.admin.modules.backOffice; + +import com.alist.api.admin.modules.backOffice.form.AdminMemberAddForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberDeleteForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberDormantModifyForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberListForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberModifyForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberViewForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberWithdrawModifyForm; +import com.alist.api.admin.modules.backOffice.service.AdminMemberService; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberAddVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberDeleteVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberDormantModifyVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberListVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberModifyVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberViewVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberWithdrawModifyVo; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag( + name = "B-1. 관리자 계정 관리" + , description = "백오피스 관리자 계정 관리 API" +) +@RestController +@RequestMapping("/backOffice/adminMember") +public class AdminMemberController { + private final AdminMemberService adminMemberService; + + public AdminMemberController(AdminMemberService adminMemberService) { + this.adminMemberService = adminMemberService; + } + + @Operation( + summary = "관리자 계정 목록 조회" + , description = "검색 조건과 페이징으로 관리자 계정 목록을 조회합니다." + ) + @GetMapping("/list") + public ResponseEntity> adminMemberList( + @Valid @ModelAttribute AdminMemberListForm adminMemberListForm + ) { + AdminMemberListVo adminMemberListVo = adminMemberService.selectAdminMemberList(adminMemberListForm); + + if (adminMemberListVo.getAdminMemberList().isEmpty()) { + return ApiResponse.entity(adminMemberListVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(adminMemberListVo, ApiResponseCode.CODE_2001, "관리자 계정"); + } + + @Operation( + summary = "관리자 계정 상세 조회" + , description = "회원 PK 기준으로 관리자 계정 상세 정보를 조회합니다." + ) + @GetMapping("/view") + public ResponseEntity> adminMemberView( + @Valid @ModelAttribute AdminMemberViewForm adminMemberViewForm + ) { + AdminMemberViewVo adminMemberViewVo = adminMemberService.selectAdminMemberView(adminMemberViewForm); + + if (adminMemberViewVo == null) { + return ApiResponse.entity((AdminMemberViewVo) null, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(adminMemberViewVo, ApiResponseCode.CODE_2001, "관리자 계정"); + } + + @Operation( + summary = "관리자 계정 등록" + , description = "아이디, 비밀번호, 권한, 유형을 입력하여 관리자 계정을 등록합니다." + ) + @PostMapping("/add") + public ResponseEntity> adminMemberAdd( + @Valid @RequestBody AdminMemberAddForm adminMemberAddForm + ) { + AdminMemberAddVo adminMemberAddVo = adminMemberService.insertAdminMemberAdd(adminMemberAddForm); + + if (adminMemberAddVo.getResultCode() == 2004) { + return ApiResponse.entity(adminMemberAddVo, ApiResponseCode.CODE_2004, "아이디"); + } + + return ApiResponse.entity(adminMemberAddVo, ApiResponseCode.CODE_2002, "관리자 계정"); + } + + @Operation( + summary = "관리자 계정 정보 수정" + , description = "이메일, 휴대폰 번호, 권한, 회원 유형을 수정합니다." + ) + @PutMapping("/modify") + public ResponseEntity> adminMemberModify( + @Valid @RequestBody AdminMemberModifyForm adminMemberModifyForm + ) { + AdminMemberModifyVo adminMemberModifyVo = adminMemberService.updateAdminMemberModify(adminMemberModifyForm); + + if (!adminMemberModifyVo.isUpdated()) { + return ApiResponse.entity(adminMemberModifyVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(adminMemberModifyVo, ApiResponseCode.CODE_2005, "관리자 계정"); + } + + @Operation( + summary = "관리자 계정 휴면 상태 변경" + , description = "휴면 여부를 변경합니다. Y면 휴면 처리일을 기록하고 N이면 휴면 처리일을 비웁니다." + ) + @PatchMapping("/dormant/modify") + public ResponseEntity> adminMemberDormantModify( + @Valid @RequestBody AdminMemberDormantModifyForm adminMemberDormantModifyForm + ) { + AdminMemberDormantModifyVo adminMemberDormantModifyVo = adminMemberService.updateAdminMemberDormantModify(adminMemberDormantModifyForm); + + if (!adminMemberDormantModifyVo.isUpdated()) { + return ApiResponse.entity(adminMemberDormantModifyVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(adminMemberDormantModifyVo, ApiResponseCode.CODE_2005, "관리자 계정 휴면 상태"); + } + + @Operation( + summary = "관리자 계정 탈퇴 상태 변경" + , description = "탈퇴 상태를 변경합니다. P 또는 Y면 탈퇴 처리일을 기록하고 N이면 탈퇴 처리일을 비웁니다." + ) + @PatchMapping("/withdraw/modify") + public ResponseEntity> adminMemberWithdrawModify( + @Valid @RequestBody AdminMemberWithdrawModifyForm adminMemberWithdrawModifyForm + ) { + AdminMemberWithdrawModifyVo adminMemberWithdrawModifyVo = adminMemberService.updateAdminMemberWithdrawModify(adminMemberWithdrawModifyForm); + + if (!adminMemberWithdrawModifyVo.isUpdated()) { + return ApiResponse.entity(adminMemberWithdrawModifyVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(adminMemberWithdrawModifyVo, ApiResponseCode.CODE_2005, "관리자 계정 탈퇴 상태"); + } + + @Operation( + summary = "관리자 계정 삭제" + , description = "회원 PK 목록을 논리 삭제 처리합니다." + ) + @PatchMapping("/delete") + public ResponseEntity> adminMemberDelete( + @Valid @RequestBody AdminMemberDeleteForm adminMemberDeleteForm + ) { + AdminMemberDeleteVo adminMemberDeleteVo = adminMemberService.updateAdminMemberDelete(adminMemberDeleteForm); + + if (!adminMemberDeleteVo.isDeleted()) { + return ApiResponse.entity(adminMemberDeleteVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(adminMemberDeleteVo, ApiResponseCode.CODE_2005, "관리자 계정"); + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/CorsController.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/CorsController.java new file mode 100644 index 0000000..ead4c37 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/CorsController.java @@ -0,0 +1,90 @@ +package com.alist.api.admin.modules.backOffice; + +import com.alist.api.admin.modules.backOffice.form.CorsAddForm; +import com.alist.api.admin.modules.backOffice.form.CorsDeleteForm; +import com.alist.api.admin.modules.backOffice.form.CorsListForm; +import com.alist.api.admin.modules.backOffice.service.CorsService; +import com.alist.api.admin.modules.backOffice.vo.CorsAddVo; +import com.alist.api.admin.modules.backOffice.vo.CorsDeleteVo; +import com.alist.api.admin.modules.backOffice.vo.CorsListVo; +import com.alist.api.admin.modules.backOffice.vo.CorsReloadVo; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "B-4. CORS 관리", description = "백오피스 CORS 허용 Origin 관리 API") +@RestController +@RequestMapping("/backOffice/cors") +public class CorsController { + private final CorsService corsService; + + public CorsController(CorsService corsService) { + this.corsService = corsService; + } + + @Operation(summary = "CORS 허용 Origin 목록 조회", description = "검색 조건과 페이징으로 CORS 허용 Origin 목록을 조회합니다.") + @GetMapping("/list") + public ResponseEntity> corsList(@Valid @ModelAttribute CorsListForm corsListForm) { + CorsListVo corsListVo = corsService.selectCorsList(corsListForm); + + if (corsListVo.getCorsList().isEmpty()) { + return ApiResponse.entity(corsListVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(corsListVo, ApiResponseCode.CODE_2001, "CORS 허용 Origin"); + } + + @Operation(summary = "CORS 허용 Origin 등록", description = "허용 Origin을 등록하고 Front와 Admin의 CORS 캐시를 갱신합니다.") + @PostMapping("/add") + public ResponseEntity> corsAdd(@Valid @RequestBody CorsAddForm corsAddForm) { + CorsAddVo corsAddVo = corsService.insertCorsAdd(corsAddForm); + + if (corsAddVo.getResultCode() == 401) { + return ApiResponse.entity(corsAddVo, ApiResponseCode.CODE_401); + } + + if (corsAddVo.getResultCode() == 2004) { + return ApiResponse.entity(corsAddVo, ApiResponseCode.CODE_2004, "CORS 허용 Origin"); + } + + return ApiResponse.entity(corsAddVo, ApiResponseCode.CODE_2002, "CORS 허용 Origin"); + } + + @Operation(summary = "CORS 허용 Origin 삭제", description = "허용 Origin을 논리 삭제하고 Front와 Admin의 CORS 캐시를 갱신합니다.") + @PatchMapping("/delete") + public ResponseEntity> corsDelete(@Valid @RequestBody CorsDeleteForm corsDeleteForm) { + CorsDeleteVo corsDeleteVo = corsService.updateCorsDelete(corsDeleteForm); + + if (corsDeleteVo.getResultCode() == 401) { + return ApiResponse.entity(corsDeleteVo, ApiResponseCode.CODE_401); + } + + if (!corsDeleteVo.isDeleted()) { + return ApiResponse.entity(corsDeleteVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(corsDeleteVo, ApiResponseCode.CODE_2005, "CORS 허용 Origin"); + } + + @Operation(summary = "CORS Redis 목록 강제 갱신", description = "DB의 허용 Origin 목록을 Redis에 다시 반영하고 Front와 Admin에 갱신 신호를 전송합니다.") + @PostMapping("/reload") + public ResponseEntity> corsReload() { + CorsReloadVo corsReloadVo = corsService.updateCorsReload(); + + if (corsReloadVo.getResultCode() == 401) { + return ApiResponse.entity(corsReloadVo, ApiResponseCode.CODE_401); + } + + return ApiResponse.entity(corsReloadVo, ApiResponseCode.CODE_2005, "CORS Redis 목록"); + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/ScheduleController.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/ScheduleController.java new file mode 100644 index 0000000..bcf4a73 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/ScheduleController.java @@ -0,0 +1,123 @@ +package com.alist.api.admin.modules.backOffice; + +import com.alist.api.admin.modules.backOffice.form.*; +import com.alist.api.admin.modules.backOffice.service.ScheduleService; +import com.alist.api.admin.modules.backOffice.vo.*; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "B-3. 스케줄 관리", description = "백오피스 스케줄 작업 관리 API") +@RestController +@RequestMapping("/backOffice/schedule") +public class ScheduleController { + private final ScheduleService scheduleService; + + public ScheduleController(ScheduleService scheduleService) { + this.scheduleService = scheduleService; + } + + @Operation(summary = "스케줄 작업 목록 조회", description = "검색 조건과 페이징으로 스케줄 작업 목록을 조회합니다.") + @GetMapping("/list") + public ResponseEntity> scheduleList( + @Valid @ModelAttribute ScheduleListForm scheduleListForm + ) { + ScheduleListVo scheduleListVo = scheduleService.selectScheduleList(scheduleListForm); + + if (scheduleListVo.getScheduleList().isEmpty()) { + return ApiResponse.entity(scheduleListVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(scheduleListVo, ApiResponseCode.CODE_2001, "스케줄 작업"); + } + + @Operation(summary = "스케줄 작업 상세 조회", description = "스케줄 작업 PK 기준으로 상세 정보를 조회합니다.") + @GetMapping("/view") + public ResponseEntity> scheduleView( + @Valid @ModelAttribute ScheduleViewForm scheduleViewForm + ) { + ScheduleViewVo scheduleViewVo = scheduleService.selectScheduleView(scheduleViewForm); + + if (scheduleViewVo == null) { + return ApiResponse.entity((ScheduleViewVo) null, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(scheduleViewVo, ApiResponseCode.CODE_2001, "스케줄 작업"); + } + + @Operation(summary = "스케줄 작업 등록", description = "스케줄 작업을 등록합니다. 작업 코드와 작업 그룹 조합은 중복될 수 없습니다.") + @PostMapping("/add") + public ResponseEntity> scheduleAdd( + @Valid @RequestBody ScheduleAddForm scheduleAddForm + ) { + ScheduleAddVo scheduleAddVo = scheduleService.insertScheduleAdd(scheduleAddForm); + + if (scheduleAddVo.getResultCode() == 401) { + return ApiResponse.entity(scheduleAddVo, ApiResponseCode.CODE_401); + } + + if (scheduleAddVo.getResultCode() == 2004) { + return ApiResponse.entity(scheduleAddVo, ApiResponseCode.CODE_2004, "스케줄 작업"); + } + + return ApiResponse.entity(scheduleAddVo, ApiResponseCode.CODE_2002, "스케줄 작업"); + } + + @Operation(summary = "스케줄 작업 수정", description = "스케줄 작업의 기본 정보를 수정합니다.") + @PutMapping("/modify") + public ResponseEntity> scheduleModify( + @Valid @RequestBody ScheduleModifyForm scheduleModifyForm + ) { + ScheduleModifyVo scheduleModifyVo = scheduleService.updateScheduleModify(scheduleModifyForm); + + if (scheduleModifyVo.getResultCode() == 401) { + return ApiResponse.entity(scheduleModifyVo, ApiResponseCode.CODE_401); + } + + if (scheduleModifyVo.getResultCode() == 2004) { + return ApiResponse.entity(scheduleModifyVo, ApiResponseCode.CODE_2004, "스케줄 작업"); + } + + if (!scheduleModifyVo.isUpdated()) { + return ApiResponse.entity(scheduleModifyVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(scheduleModifyVo, ApiResponseCode.CODE_2005, "스케줄 작업"); + } + + @Operation(summary = "스케줄 작업 삭제", description = "스케줄 작업을 논리 삭제 처리합니다.") + @PatchMapping("/delete") + public ResponseEntity> scheduleDelete( + @Valid @RequestBody ScheduleDeleteForm scheduleDeleteForm + ) { + ScheduleDeleteVo scheduleDeleteVo = scheduleService.updateScheduleDelete(scheduleDeleteForm); + + if (scheduleDeleteVo.getResultCode() == 401) { + return ApiResponse.entity(scheduleDeleteVo, ApiResponseCode.CODE_401); + } + + if (!scheduleDeleteVo.isDeleted()) { + return ApiResponse.entity(scheduleDeleteVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(scheduleDeleteVo, ApiResponseCode.CODE_2005, "스케줄 작업"); + } + + @Operation(summary = "스케줄 작업 실행 로그 목록 조회", description = "검색 조건과 페이징으로 스케줄 실행 로그를 조회합니다.") + @GetMapping("/log/list") + public ResponseEntity> scheduleLogList( + @Valid @ModelAttribute ScheduleLogListForm scheduleLogListForm + ) { + ScheduleLogListVo scheduleLogListVo = scheduleService.selectScheduleLogList(scheduleLogListForm); + + if (scheduleLogListVo.getScheduleLogList().isEmpty()) { + return ApiResponse.entity(scheduleLogListVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(scheduleLogListVo, ApiResponseCode.CODE_2001, "스케줄 작업 실행 로그"); + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/SsoController.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/SsoController.java new file mode 100644 index 0000000..ffd14dd --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/SsoController.java @@ -0,0 +1,142 @@ +package com.alist.api.admin.modules.backOffice; + +import com.alist.api.admin.modules.backOffice.form.SsoAddForm; +import com.alist.api.admin.modules.backOffice.form.SsoDeleteForm; +import com.alist.api.admin.modules.backOffice.form.SsoListForm; +import com.alist.api.admin.modules.backOffice.form.SsoModifyForm; +import com.alist.api.admin.modules.backOffice.form.SsoViewForm; +import com.alist.api.admin.modules.backOffice.service.SsoService; +import com.alist.api.admin.modules.backOffice.vo.SsoAddVo; +import com.alist.api.admin.modules.backOffice.vo.SsoDeleteVo; +import com.alist.api.admin.modules.backOffice.vo.SsoListVo; +import com.alist.api.admin.modules.backOffice.vo.SsoModifyVo; +import com.alist.api.admin.modules.backOffice.vo.SsoViewVo; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag( + name = "B-2. SSO 클라이언트 관리" + , description = "백오피스 SSO 클라이언트 관리 API" +) +@RestController +@RequestMapping("/backOffice/sso") +public class SsoController { + private final SsoService ssoService; + + public SsoController(SsoService ssoService) { + this.ssoService = ssoService; + } + + @Operation( + summary = "SSO 클라이언트 목록 조회" + , description = "검색 조건과 페이징으로 SSO 클라이언트 목록을 조회합니다." + ) + @GetMapping("/list") + public ResponseEntity> ssoList( + @Valid @ModelAttribute SsoListForm ssoListForm + ) { + SsoListVo ssoListVo = ssoService.selectSsoList(ssoListForm); + + if (ssoListVo.getSsoList().isEmpty()) { + return ApiResponse.entity(ssoListVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(ssoListVo, ApiResponseCode.CODE_2001, "SSO 클라이언트"); + } + + @Operation( + summary = "SSO 클라이언트 상세 조회" + , description = "SSO 클라이언트 PK 기준으로 상세 정보를 조회합니다." + ) + @GetMapping("/view") + public ResponseEntity> ssoView( + @Valid @ModelAttribute SsoViewForm ssoViewForm + ) { + SsoViewVo ssoViewVo = ssoService.selectSsoView(ssoViewForm); + + if (ssoViewVo == null) { + return ApiResponse.entity((SsoViewVo) null, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(ssoViewVo, ApiResponseCode.CODE_2001, "SSO 클라이언트"); + } + + @Operation( + summary = "SSO 클라이언트 등록" + , description = "새 SSO 클라이언트를 등록합니다. CLIENT_ID 중복 시 등록할 수 없습니다." + ) + @PostMapping("/add") + public ResponseEntity> ssoAdd( + @Valid @RequestBody SsoAddForm ssoAddForm + ) { + SsoAddVo ssoAddVo = ssoService.insertSsoAdd(ssoAddForm); + + if (ssoAddVo.getResultCode() == 401) { + return ApiResponse.entity(ssoAddVo, ApiResponseCode.CODE_401); + } + + if (ssoAddVo.getResultCode() == 2004) { + return ApiResponse.entity(ssoAddVo, ApiResponseCode.CODE_2004, "SSO 클라이언트"); + } + + return ApiResponse.entity(ssoAddVo, ApiResponseCode.CODE_2002, "SSO 클라이언트"); + } + + @Operation( + summary = "SSO 클라이언트 수정" + , description = "SSO 클라이언트의 식별자, 이름, Redirect URI, 사용 여부를 수정합니다." + ) + @PutMapping("/modify") + public ResponseEntity> ssoModify( + @Valid @RequestBody SsoModifyForm ssoModifyForm + ) { + SsoModifyVo ssoModifyVo = ssoService.updateSsoModify(ssoModifyForm); + + if (ssoModifyVo.getResultCode() == 401) { + return ApiResponse.entity(ssoModifyVo, ApiResponseCode.CODE_401); + } + + if (ssoModifyVo.getResultCode() == 2004) { + return ApiResponse.entity(ssoModifyVo, ApiResponseCode.CODE_2004, "SSO 클라이언트"); + } + + if (!ssoModifyVo.isUpdated()) { + return ApiResponse.entity(ssoModifyVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(ssoModifyVo, ApiResponseCode.CODE_2005, "SSO 클라이언트"); + } + + @Operation( + summary = "SSO 클라이언트 삭제" + , description = "SSO 클라이언트를 논리 삭제 처리합니다." + ) + @PatchMapping("/delete") + public ResponseEntity> ssoDelete( + @Valid @RequestBody SsoDeleteForm ssoDeleteForm + ) { + SsoDeleteVo ssoDeleteVo = ssoService.updateSsoDelete(ssoDeleteForm); + + if (ssoDeleteVo.getResultCode() == 401) { + return ApiResponse.entity(ssoDeleteVo, ApiResponseCode.CODE_401); + } + + if (!ssoDeleteVo.isDeleted()) { + return ApiResponse.entity(ssoDeleteVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(ssoDeleteVo, ApiResponseCode.CODE_2005, "SSO 클라이언트"); + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberAddForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberAddForm.java new file mode 100644 index 0000000..243a2b6 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberAddForm.java @@ -0,0 +1,55 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 등록 요청") +@Getter +@Setter +public class AdminMemberAddForm { + @Schema(description = "아이디", example = "admin01", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "아이디를 입력해주세요.") + private String id; + + @Schema(description = "비밀번호. 영문과 숫자를 포함한 8~64자", example = "pass1234", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "비밀번호를 입력해주세요.") + @Size(min = 8, max = 64, message = "비밀번호는 8~64자여야 합니다.") + @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)\\S+$", message = "비밀번호는 영문과 숫자를 포함하고 공백이 없어야 합니다.") + private String password; + + @Schema(description = "회원 권한. 관리자: A, 교사: CCM/SCM/I, 학생: R/E", example = "A", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "회원 권한을 입력해주세요.") + @Pattern(regexp = "^(CCM|SCM|I|R|E|A)$", message = "회원 권한 값을 확인해주세요.") + private String userRole; + + @Schema(description = "회원 유형. A: 관리자, T: 교사, S: 학생", example = "A", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "회원 유형을 입력해주세요.") + @Pattern(regexp = "^(A|T|S)$", message = "회원 유형은 A, T, S만 가능합니다.") + private String userType; + + @AssertTrue(message = "회원 유형과 권한 조합을 확인해주세요.") + @Schema(hidden = true) + public boolean isValidUserTypeAndRole() { + if (userType == null || userRole == null) { + return true; + } + + if ("A".equals(userType)) { + return "A".equals(userRole); + } + + if ("T".equals(userType)) { + return "CCM".equals(userRole) + || "SCM".equals(userRole) + || "I".equals(userRole); + } + + return "S".equals(userType) + && ("R".equals(userRole) || "E".equals(userRole)); + } +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberDeleteForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberDeleteForm.java new file mode 100644 index 0000000..2334537 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberDeleteForm.java @@ -0,0 +1,17 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 삭제 요청") +@Getter +@Setter +public class AdminMemberDeleteForm { + @Schema(description = "삭제할 회원 PK 목록. 콤마로 구분", example = "16,17,18", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "삭제할 회원 PK를 입력해주세요.") + @Pattern(regexp = "^\\d+(,\\d+)*$", message = "회원 PK 목록은 16,17,18 형식으로 입력해주세요.") + private String userIdxList; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberDormantModifyForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberDormantModifyForm.java new file mode 100644 index 0000000..250d895 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberDormantModifyForm.java @@ -0,0 +1,22 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 휴면 상태 변경 요청") +@Getter +@Setter +public class AdminMemberDormantModifyForm { + @Schema(description = "회원 PK", example = "16", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "회원 PK는 필수입니다.") + private Integer userIdx; + + @Schema(description = "휴면 여부. Y: 휴면, N: 정상", example = "Y", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "휴면 여부는 필수입니다.") + @Pattern(regexp = "^[YN]$", message = "휴면 여부는 Y 또는 N만 가능합니다.") + private String dormantYn; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberListForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberListForm.java new file mode 100644 index 0000000..bcca7ac --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberListForm.java @@ -0,0 +1,26 @@ +package com.alist.api.admin.modules.backOffice.form; + +import com.alist.api.core.common.paging.PagingRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 목록 검색 조건") +@Getter +@Setter +public class AdminMemberListForm extends PagingRequest { + @Schema(description = "검색어. 아이디, 이메일, 휴대폰 번호 검색", example = "admin") + private String keyword; + + @Schema(description = "회원 권한 코드", example = "A") + private String userRole; + + @Schema(description = "회원 유형 코드. A: 관리자, T: 교사, S: 학생", example = "A") + private String userType; + + @Schema(description = "휴면 여부. N: 정상, Y: 휴면", example = "N") + private String dormantYn; + + @Schema(description = "탈퇴 상태. N: 정상, P: 대기, Y: 완료", example = "N") + private String withdrawStatus; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberModifyForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberModifyForm.java new file mode 100644 index 0000000..5a6ca59 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberModifyForm.java @@ -0,0 +1,42 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 정보 수정 요청") +@Getter +@Setter +public class AdminMemberModifyForm { + @Schema(description = "회원 PK", example = "16", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "회원 PK는 필수입니다.") + private Integer userIdx; + + @Schema(description = "이메일", example = "admin@alist.co.kr") + @Email(message = "이메일 형식을 확인해주세요.") + private String email; + + @Schema(description = "휴대폰 번호", example = "01012345678") + private String hp; + + @Schema(description = "회원 권한", example = "A") + @Pattern(regexp = "^(CCM|SCM|I|R|E|A)?$", message = "회원 권한 값을 확인해주세요.") + private String userRole; + + @Schema(description = "회원 유형. A: 관리자, T: 교사, S: 학생", example = "A") + @Pattern(regexp = "^(A|T|S)?$", message = "회원 유형은 A, T, S만 가능합니다.") + private String userType; + + @AssertTrue(message = "수정할 정보를 하나 이상 입력해주세요.") + @Schema(hidden = true) + public boolean isModifyValue() { + return (email != null && !email.isBlank()) + || (hp != null && !hp.isBlank()) + || (userRole != null && !userRole.isBlank()) + || (userType != null && !userType.isBlank()); + } +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberViewForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberViewForm.java new file mode 100644 index 0000000..7578684 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberViewForm.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 상세 조회 요청") +@Getter +@Setter +public class AdminMemberViewForm { + @Schema(description = "회원 PK", example = "16", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "회원 PK는 필수입니다.") + private Integer userIdx; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberWithdrawModifyForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberWithdrawModifyForm.java new file mode 100644 index 0000000..e6740bd --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/AdminMemberWithdrawModifyForm.java @@ -0,0 +1,22 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 탈퇴 상태 변경 요청") +@Getter +@Setter +public class AdminMemberWithdrawModifyForm { + @Schema(description = "회원 PK", example = "16", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "회원 PK는 필수입니다.") + private Integer userIdx; + + @Schema(description = "탈퇴 상태. N: 정상, P: 대기, Y: 완료", example = "P", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "탈퇴 상태는 필수입니다.") + @Pattern(regexp = "^[NPY]$", message = "탈퇴 상태는 N, P, Y만 가능합니다.") + private String withdrawStatus; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsAddForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsAddForm.java new file mode 100644 index 0000000..695b82c --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsAddForm.java @@ -0,0 +1,20 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "CORS 허용 Origin 등록 요청") +@Getter +@Setter +public class CorsAddForm { + @Schema(description = "API 토큰 PK. 미입력 시 전체 적용", example = "0") + private Integer userTokenIdx; + + @NotBlank(message = "허용 Origin을 입력해주세요.") + @Size(max = 255) + @Schema(description = "허용 Origin", example = "https://admin.alist.co.kr", requiredMode = Schema.RequiredMode.REQUIRED) + private String allowedOrigin; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsDeleteForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsDeleteForm.java new file mode 100644 index 0000000..e641777 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsDeleteForm.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "CORS 허용 Origin 삭제 요청") +@Getter +@Setter +public class CorsDeleteForm { + @NotNull(message = "CORS 설정 PK를 입력해주세요.") + @Schema(description = "CORS 설정 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED) + private Integer corsIdx; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsListForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsListForm.java new file mode 100644 index 0000000..f031cf7 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/CorsListForm.java @@ -0,0 +1,17 @@ +package com.alist.api.admin.modules.backOffice.form; + +import com.alist.api.core.common.paging.PagingRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "CORS 허용 Origin 목록 검색 조건") +@Getter +@Setter +public class CorsListForm extends PagingRequest { + @Schema(description = "API 토큰 PK. 0은 전체 적용 Origin", example = "0") + private Integer userTokenIdx; + + @Schema(description = "허용 Origin 검색어", example = "https://admin.alist.co.kr") + private String allowedOrigin; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleAddForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleAddForm.java new file mode 100644 index 0000000..3a1ac9a --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleAddForm.java @@ -0,0 +1,22 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "스케줄 작업 등록 요청") +@Getter +@Setter +public class ScheduleAddForm { + @NotBlank(message = "작업 코드를 입력해주세요.") @Size(max = 100) @Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY", requiredMode = Schema.RequiredMode.REQUIRED) private String jobName; + @Size(max = 50) @Schema(description = "작업 그룹. 미입력 시 DEFAULT", example = "DEFAULT") private String jobGroup; + @Size(max = 255) @Schema(description = "작업 설명", example = "일별 학습 통계 집계") private String jobDescription; + @NotBlank(message = "크론식을 입력해주세요.") @Size(max = 100) @Schema(description = "크론식", example = "0 0 2 * * *", requiredMode = Schema.RequiredMode.REQUIRED) private String cronExpr; + @Size(max = 50) @Schema(description = "타임존. 미입력 시 Asia/Seoul", example = "Asia/Seoul") private String timezoneId; + @Min(value = 1, message = "최대 실행 허용 시간은 1초 이상이어야 합니다.") @Schema(description = "최대 실행 허용 시간(초). 미입력 시 1800", example = "1800") private Integer maxRunningSeconds; + @Pattern(regexp = "^(Y|N)$", message = "사용 여부는 Y 또는 N만 가능합니다.") @Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y") private String useYn; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleDeleteForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleDeleteForm.java new file mode 100644 index 0000000..bd25ce1 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleDeleteForm.java @@ -0,0 +1,13 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "스케줄 작업 삭제 요청") +@Getter +@Setter +public class ScheduleDeleteForm { + @NotNull(message = "스케줄 작업 PK를 입력해주세요.") @Schema(description = "스케줄 작업 PK", example = "9", requiredMode = Schema.RequiredMode.REQUIRED) private Long scheduleJobIdx; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleListForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleListForm.java new file mode 100644 index 0000000..38aa2b8 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleListForm.java @@ -0,0 +1,16 @@ +package com.alist.api.admin.modules.backOffice.form; + +import com.alist.api.core.common.paging.PagingRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "스케줄 작업 목록 검색 조건") +@Getter +@Setter +public class ScheduleListForm extends PagingRequest { + @Schema(description = "검색어. 작업 코드와 작업 설명 검색", example = "DAILY") private String keyword; + @Schema(description = "작업 그룹", example = "DEFAULT") private String jobGroup; + @Schema(description = "마지막 실행 상태. 0: IDLE, 1: RUNNING, 2: SUCCESS, 3: SKIP, 4: FAIL", example = "2") private Short lastStatus; + @Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y") private String useYn; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleLogListForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleLogListForm.java new file mode 100644 index 0000000..5f243cc --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleLogListForm.java @@ -0,0 +1,16 @@ +package com.alist.api.admin.modules.backOffice.form; + +import com.alist.api.core.common.paging.PagingRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "스케줄 작업 실행 로그 목록 검색 조건") +@Getter +@Setter +public class ScheduleLogListForm extends PagingRequest { + @Schema(description = "스케줄 작업 PK", example = "9") private Long scheduleJobIdx; + @Schema(description = "실행 상태. 1: RUNNING, 2: SUCCESS, 3: SKIP, 4: FAIL", example = "2") private Short status; + @Schema(description = "실행 시작일 검색 시작값", example = "2026-05-01") private String startDateFrom; + @Schema(description = "실행 시작일 검색 종료값", example = "2026-05-28") private String startDateTo; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleModifyForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleModifyForm.java new file mode 100644 index 0000000..0f5935f --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleModifyForm.java @@ -0,0 +1,24 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "스케줄 작업 수정 요청") +@Getter +@Setter +public class ScheduleModifyForm { + @NotNull(message = "스케줄 작업 PK를 입력해주세요.") @Schema(description = "스케줄 작업 PK", example = "9", requiredMode = Schema.RequiredMode.REQUIRED) private Long scheduleJobIdx; + @NotBlank(message = "작업 코드를 입력해주세요.") @Size(max = 100) @Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY", requiredMode = Schema.RequiredMode.REQUIRED) private String jobName; + @Size(max = 50) @Schema(description = "작업 그룹. 미입력 시 DEFAULT", example = "DEFAULT") private String jobGroup; + @Size(max = 255) @Schema(description = "작업 설명", example = "일별 학습 통계 집계") private String jobDescription; + @NotBlank(message = "크론식을 입력해주세요.") @Size(max = 100) @Schema(description = "크론식", example = "0 0 2 * * *", requiredMode = Schema.RequiredMode.REQUIRED) private String cronExpr; + @Size(max = 50) @Schema(description = "타임존. 미입력 시 Asia/Seoul", example = "Asia/Seoul") private String timezoneId; + @Min(value = 1, message = "최대 실행 허용 시간은 1초 이상이어야 합니다.") @Schema(description = "최대 실행 허용 시간(초). 미입력 시 1800", example = "1800") private Integer maxRunningSeconds; + @Pattern(regexp = "^(Y|N)$", message = "사용 여부는 Y 또는 N만 가능합니다.") @Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y") private String useYn; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleViewForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleViewForm.java new file mode 100644 index 0000000..8584582 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/ScheduleViewForm.java @@ -0,0 +1,14 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "스케줄 작업 상세 조회 요청") +@Getter +@Setter +public class ScheduleViewForm { + @NotNull(message = "스케줄 작업 PK를 입력해주세요.") + @Schema(description = "스케줄 작업 PK", example = "9", requiredMode = Schema.RequiredMode.REQUIRED) private Long scheduleJobIdx; +} diff --git a/src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoAddForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoAddForm.java similarity index 51% rename from src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoAddForm.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoAddForm.java index 2f36a9a..62ab674 100644 --- a/src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoAddForm.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoAddForm.java @@ -1,6 +1,5 @@ -package com.alist.api.modules.admin.sso.form; +package com.alist.api.admin.modules.backOffice.form; -import com.alist.api.modules.admin.sso.dto.AdminSsoAddDto; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; @@ -8,40 +7,30 @@ import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; -@Schema(description = "관리자 SSO 클라이언트 등록 요청") +@Schema(description = "SSO 클라이언트 등록 요청") @Getter @Setter -public class AdminSsoAddForm { - @NotBlank - @Size(max = 100) +public class SsoAddForm { + @NotBlank(message = "클라이언트 식별자를 입력해주세요.") + @Size(max = 100, message = "클라이언트 식별자는 100자 이하여야 합니다.") @Schema(description = "클라이언트 식별자", example = "a-campus-client", requiredMode = Schema.RequiredMode.REQUIRED) private String clientId; - @NotBlank - @Size(max = 200) + @NotBlank(message = "클라이언트명을 입력해주세요.") + @Size(max = 200, message = "클라이언트명은 200자 이하여야 합니다.") @Schema(description = "클라이언트명", example = "a-campus.co.kr", requiredMode = Schema.RequiredMode.REQUIRED) private String clientName; - @NotBlank - @Size(max = 500) - @Schema(description = "허용 redirect URI", example = "https://a-campus.co.kr/auth/callback", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "Redirect URI를 입력해주세요.") + @Size(max = 500, message = "Redirect URI는 500자 이하여야 합니다.") + @Schema(description = "허용 Redirect URI", example = "https://a-campus.co.kr/auth/callback", requiredMode = Schema.RequiredMode.REQUIRED) private String redirectUri; - @Size(max = 500) + @Size(max = 500, message = "비고는 500자 이하여야 합니다.") @Schema(description = "비고", example = "운영") private String memo; - @Pattern(regexp = "Y|N") + @Pattern(regexp = "^(Y|N)$", message = "사용 여부는 Y 또는 N만 가능합니다.") @Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y") private String useYn; - - public AdminSsoAddDto toDto() { - AdminSsoAddDto adminSsoAddDto = new AdminSsoAddDto(); - adminSsoAddDto.setClientId(clientId.trim()); - adminSsoAddDto.setClientName(clientName.trim()); - adminSsoAddDto.setRedirectUri(redirectUri.trim()); - adminSsoAddDto.setMemo(memo == null ? null : memo.trim()); - adminSsoAddDto.setUseYn(useYn == null || useYn.trim().isEmpty() ? "Y" : useYn.trim()); - return adminSsoAddDto; - } } diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoDeleteForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoDeleteForm.java new file mode 100644 index 0000000..18646ba --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoDeleteForm.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "SSO 클라이언트 삭제 요청") +@Getter +@Setter +public class SsoDeleteForm { + @NotNull(message = "SSO 클라이언트 PK를 입력해주세요.") + @Schema(description = "SSO 클라이언트 PK", example = "19", requiredMode = Schema.RequiredMode.REQUIRED) + private Integer ssoClientIdx; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoListForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoListForm.java new file mode 100644 index 0000000..cc5df25 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoListForm.java @@ -0,0 +1,17 @@ +package com.alist.api.admin.modules.backOffice.form; + +import com.alist.api.core.common.paging.PagingRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "SSO 클라이언트 목록 검색 조건") +@Getter +@Setter +public class SsoListForm extends PagingRequest { + @Schema(description = "검색어. 클라이언트 ID, 클라이언트명, Redirect URI, 비고 검색", example = "a-campus") + private String keyword; + + @Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y") + private String useYn; +} diff --git a/src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoModifyForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoModifyForm.java similarity index 53% rename from src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoModifyForm.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoModifyForm.java index 98f5d5b..1fe9300 100644 --- a/src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoModifyForm.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoModifyForm.java @@ -1,6 +1,5 @@ -package com.alist.api.modules.admin.sso.form; +package com.alist.api.admin.modules.backOffice.form; -import com.alist.api.modules.admin.sso.dto.AdminSsoModifyDto; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -9,45 +8,34 @@ import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; -@Schema(description = "관리자 SSO 클라이언트 수정 요청") +@Schema(description = "SSO 클라이언트 수정 요청") @Getter @Setter -public class AdminSsoModifyForm { - @NotNull +public class SsoModifyForm { + @NotNull(message = "SSO 클라이언트 PK를 입력해주세요.") @Schema(description = "SSO 클라이언트 PK", example = "19", requiredMode = Schema.RequiredMode.REQUIRED) private Integer ssoClientIdx; - @NotBlank - @Size(max = 100) + @NotBlank(message = "클라이언트 식별자를 입력해주세요.") + @Size(max = 100, message = "클라이언트 식별자는 100자 이하여야 합니다.") @Schema(description = "클라이언트 식별자", example = "a-campus-client", requiredMode = Schema.RequiredMode.REQUIRED) private String clientId; - @NotBlank - @Size(max = 200) + @NotBlank(message = "클라이언트명을 입력해주세요.") + @Size(max = 200, message = "클라이언트명은 200자 이하여야 합니다.") @Schema(description = "클라이언트명", example = "a-campus.co.kr", requiredMode = Schema.RequiredMode.REQUIRED) private String clientName; - @NotBlank - @Size(max = 500) - @Schema(description = "허용 redirect URI", example = "https://a-campus.co.kr/auth/callback", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "Redirect URI를 입력해주세요.") + @Size(max = 500, message = "Redirect URI는 500자 이하여야 합니다.") + @Schema(description = "허용 Redirect URI", example = "https://a-campus.co.kr/auth/callback", requiredMode = Schema.RequiredMode.REQUIRED) private String redirectUri; - @Size(max = 500) + @Size(max = 500, message = "비고는 500자 이하여야 합니다.") @Schema(description = "비고", example = "운영") private String memo; - @Pattern(regexp = "Y|N") + @Pattern(regexp = "^(Y|N)$", message = "사용 여부는 Y 또는 N만 가능합니다.") @Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y") private String useYn; - - public AdminSsoModifyDto toDto() { - AdminSsoModifyDto adminSsoModifyDto = new AdminSsoModifyDto(); - adminSsoModifyDto.setSsoClientIdx(ssoClientIdx); - adminSsoModifyDto.setClientId(clientId.trim()); - adminSsoModifyDto.setClientName(clientName.trim()); - adminSsoModifyDto.setRedirectUri(redirectUri.trim()); - adminSsoModifyDto.setMemo(memo == null ? null : memo.trim()); - adminSsoModifyDto.setUseYn(useYn == null || useYn.trim().isEmpty() ? "Y" : useYn.trim()); - return adminSsoModifyDto; - } } diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoViewForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoViewForm.java new file mode 100644 index 0000000..ef16855 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/form/SsoViewForm.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.backOffice.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "SSO 클라이언트 상세 조회 요청") +@Getter +@Setter +public class SsoViewForm { + @NotNull(message = "SSO 클라이언트 PK를 입력해주세요.") + @Schema(description = "SSO 클라이언트 PK", example = "19", requiredMode = Schema.RequiredMode.REQUIRED) + private Integer ssoClientIdx; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/AdminMemberService.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/AdminMemberService.java new file mode 100644 index 0000000..a5071cd --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/AdminMemberService.java @@ -0,0 +1,310 @@ +package com.alist.api.admin.modules.backOffice.service; + +import com.alist.api.admin.modules.backOffice.form.AdminMemberAddForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberDeleteForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberDormantModifyForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberListForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberModifyForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberViewForm; +import com.alist.api.admin.modules.backOffice.form.AdminMemberWithdrawModifyForm; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberAddVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberDeleteVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberDormantModifyVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberListItemVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberListVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberModifyVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberViewVo; +import com.alist.api.admin.modules.backOffice.vo.AdminMemberWithdrawModifyVo; +import com.alist.api.core.common.utils.ApiKeyGenerator; +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserAdminMemberListDto; +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserAdminMemberViewDto; +import com.alist.api.core.modules.bespoke.testUser.service.TestUserAdminMemberService; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserAdminMemberListItemVo; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserAdminMemberViewVo; +import com.alist.api.core.modules.standard.testUser.dto.TestUserAddDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserDeleteDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserModifyDto; +import com.alist.api.core.modules.standard.testUser.service.TestUserService; +import com.alist.api.core.modules.standard.testUser.vo.TestUserAddVo; +import com.alist.api.core.modules.standard.testUser.vo.TestUserDeleteVo; +import com.alist.api.core.modules.standard.testUser.vo.TestUserModifyVo; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenAddDto; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenModifyDto; +import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService; +import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenAddVo; +import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenModifyVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Service +public class AdminMemberService { + private final TestUserAdminMemberService testUserAdminMemberService; + private final TestUserService testUserService; + private final TestUserTokenService testUserTokenService; + + public AdminMemberService(TestUserAdminMemberService testUserAdminMemberService, TestUserService testUserService, TestUserTokenService testUserTokenService) { + this.testUserAdminMemberService = testUserAdminMemberService; + this.testUserService = testUserService; + this.testUserTokenService = testUserTokenService; + } + + @Transactional(readOnly = true) + public AdminMemberListVo selectAdminMemberList(AdminMemberListForm adminMemberListForm) { + TestUserAdminMemberListDto testUserAdminMemberListDto = new TestUserAdminMemberListDto(); + testUserAdminMemberListDto.setPage(adminMemberListForm.getPage()); + testUserAdminMemberListDto.setSize(adminMemberListForm.getSize()); + testUserAdminMemberListDto.setKeyword(adminMemberListForm.getKeyword() == null || adminMemberListForm.getKeyword().isBlank() ? null : adminMemberListForm.getKeyword().trim()); + testUserAdminMemberListDto.setUserRole(adminMemberListForm.getUserRole() == null || adminMemberListForm.getUserRole().isBlank() ? null : adminMemberListForm.getUserRole().trim()); + testUserAdminMemberListDto.setUserType(adminMemberListForm.getUserType() == null || adminMemberListForm.getUserType().isBlank() ? null : adminMemberListForm.getUserType().trim()); + testUserAdminMemberListDto.setDormantYn(adminMemberListForm.getDormantYn() == null || adminMemberListForm.getDormantYn().isBlank() ? null : adminMemberListForm.getDormantYn().trim()); + testUserAdminMemberListDto.setWithdrawStatus(adminMemberListForm.getWithdrawStatus() == null || adminMemberListForm.getWithdrawStatus().isBlank() ? null : adminMemberListForm.getWithdrawStatus().trim()); + + int totalCount = testUserAdminMemberService.testUserAdminMemberCount(testUserAdminMemberListDto); + + List testUserAdminMemberListItemVoList = testUserAdminMemberService.testUserAdminMemberList(testUserAdminMemberListDto); + + List adminMemberList = new ArrayList<>(); + + for (TestUserAdminMemberListItemVo testUserAdminMemberListItemVo : testUserAdminMemberListItemVoList) { + AdminMemberListItemVo adminMemberListItemVo = new AdminMemberListItemVo(); + + adminMemberListItemVo.setUserIdx(testUserAdminMemberListItemVo.getUserIdx()); + adminMemberListItemVo.setUserTokenIdx(testUserAdminMemberListItemVo.getUserTokenIdx()); + adminMemberListItemVo.setId(testUserAdminMemberListItemVo.getId()); + adminMemberListItemVo.setEmail(testUserAdminMemberListItemVo.getEmail()); + adminMemberListItemVo.setHp(testUserAdminMemberListItemVo.getHp()); + adminMemberListItemVo.setUserRole(testUserAdminMemberListItemVo.getUserRole()); + adminMemberListItemVo.setUserType(testUserAdminMemberListItemVo.getUserType()); + adminMemberListItemVo.setDormantYn(testUserAdminMemberListItemVo.getDormantYn()); + adminMemberListItemVo.setDormantAt(testUserAdminMemberListItemVo.getDormantAt()); + adminMemberListItemVo.setWithdrawStatus(testUserAdminMemberListItemVo.getWithdrawStatus()); + adminMemberListItemVo.setWithdrawAt(testUserAdminMemberListItemVo.getWithdrawAt()); + adminMemberListItemVo.setCreateAt(testUserAdminMemberListItemVo.getCreateAt()); + adminMemberListItemVo.setUpdateAt(testUserAdminMemberListItemVo.getUpdateAt()); + + adminMemberList.add(adminMemberListItemVo); + } + + AdminMemberListVo adminMemberListVo = new AdminMemberListVo(); + adminMemberListVo.setAdminMemberList(adminMemberList); + adminMemberListVo.setPaging(testUserAdminMemberListDto, totalCount); + return adminMemberListVo; + } + + @Transactional(readOnly = true) + public AdminMemberViewVo selectAdminMemberView(AdminMemberViewForm adminMemberViewForm) { + TestUserAdminMemberViewDto testUserAdminMemberViewDto = new TestUserAdminMemberViewDto(); + testUserAdminMemberViewDto.setUserIdx(adminMemberViewForm.getUserIdx()); + + TestUserAdminMemberViewVo testUserAdminMemberViewVo = testUserAdminMemberService.testUserAdminMemberView(testUserAdminMemberViewDto); + + if (testUserAdminMemberViewVo == null) { + return null; + } + + AdminMemberViewVo adminMemberViewVo = new AdminMemberViewVo(); + adminMemberViewVo.setUserIdx(testUserAdminMemberViewVo.getUserIdx()); + adminMemberViewVo.setUserTokenIdx(testUserAdminMemberViewVo.getUserTokenIdx()); + adminMemberViewVo.setId(testUserAdminMemberViewVo.getId()); + adminMemberViewVo.setEmail(testUserAdminMemberViewVo.getEmail()); + adminMemberViewVo.setHp(testUserAdminMemberViewVo.getHp()); + adminMemberViewVo.setUserRole(testUserAdminMemberViewVo.getUserRole()); + adminMemberViewVo.setUserType(testUserAdminMemberViewVo.getUserType()); + adminMemberViewVo.setDormantYn(testUserAdminMemberViewVo.getDormantYn()); + adminMemberViewVo.setDormantAt(testUserAdminMemberViewVo.getDormantAt()); + adminMemberViewVo.setWithdrawStatus(testUserAdminMemberViewVo.getWithdrawStatus()); + adminMemberViewVo.setWithdrawAt(testUserAdminMemberViewVo.getWithdrawAt()); + adminMemberViewVo.setCreateAt(testUserAdminMemberViewVo.getCreateAt()); + adminMemberViewVo.setUpdateAt(testUserAdminMemberViewVo.getUpdateAt()); + return adminMemberViewVo; + } + + @Transactional + public AdminMemberAddVo insertAdminMemberAdd(AdminMemberAddForm adminMemberAddForm) { + String id = adminMemberAddForm.getId().trim(); + + AdminMemberAddVo adminMemberAddVo = new AdminMemberAddVo(); + adminMemberAddVo.setId(id); + adminMemberAddVo.setUserRole(adminMemberAddForm.getUserRole()); + adminMemberAddVo.setUserType(adminMemberAddForm.getUserType()); + + if (testUserService.selectTestUserDuplicateCount(id) > 0) { + adminMemberAddVo.setAdded(false); + adminMemberAddVo.setResultCode(2004); + return adminMemberAddVo; + } + + TestUserAddDto testUserAddDto = new TestUserAddDto(); + testUserAddDto.setId(id); + testUserAddDto.setPassword(adminMemberAddForm.getPassword()); + testUserAddDto.setEmail(""); + testUserAddDto.setHp(""); + + TestUserAddVo testUserAddVo = testUserService.insertTestUserAdd(testUserAddDto); + + if (!testUserAddVo.isAdded()) { + adminMemberAddVo.setAdded(false); + adminMemberAddVo.setResultCode(testUserAddVo.getResultCode()); + return adminMemberAddVo; + } + + TestUserTokenAddVo testUserTokenAddVo = null; + + for (int retryCount = 0; retryCount < 5; retryCount++) { + TestUserTokenAddDto testUserTokenAddDto = new TestUserTokenAddDto(); + testUserTokenAddDto.setUserIdx(testUserAddVo.getUserIdx()); + testUserTokenAddDto.setUserApiKey(ApiKeyGenerator.userApiKeyProc()); + testUserTokenAddDto.setUserRole(adminMemberAddForm.getUserRole()); + testUserTokenAddDto.setUserType(adminMemberAddForm.getUserType()); + + testUserTokenAddVo = testUserTokenService.insertTestUserTokenAdd(testUserTokenAddDto); + + if (testUserTokenAddVo.isAdded()) { + break; + } + + if (testUserTokenAddVo.getResultCode() != 2004) { + break; + } + } + + if (testUserTokenAddVo == null || !testUserTokenAddVo.isAdded()) { + throw new IllegalStateException("관리자 계정 토큰 정보 등록에 실패했습니다."); + } + + adminMemberAddVo.setAdded(true); + adminMemberAddVo.setUserIdx(testUserAddVo.getUserIdx()); + adminMemberAddVo.setUserTokenIdx(testUserTokenAddVo.getUserTokenIdx()); + adminMemberAddVo.setResultCode(2002); + return adminMemberAddVo; + } + + @Transactional + public AdminMemberModifyVo updateAdminMemberModify(AdminMemberModifyForm adminMemberModifyForm) { + AdminMemberModifyVo adminMemberModifyVo = new AdminMemberModifyVo(); + adminMemberModifyVo.setUserIdx(adminMemberModifyForm.getUserIdx()); + + TestUserAdminMemberViewDto testUserAdminMemberViewDto = new TestUserAdminMemberViewDto(); + + testUserAdminMemberViewDto.setUserIdx(adminMemberModifyForm.getUserIdx()); + + TestUserAdminMemberViewVo testUserAdminMemberViewVo = testUserAdminMemberService.testUserAdminMemberView(testUserAdminMemberViewDto); + + if (testUserAdminMemberViewVo == null) { + adminMemberModifyVo.setUpdated(false); + adminMemberModifyVo.setResultCode(2003); + return adminMemberModifyVo; + } + + String email = adminMemberModifyForm.getEmail() == null || adminMemberModifyForm.getEmail().isBlank() ? null : adminMemberModifyForm.getEmail().trim(); + String hp = adminMemberModifyForm.getHp() == null || adminMemberModifyForm.getHp().isBlank() ? null : adminMemberModifyForm.getHp().trim(); + String userRole = adminMemberModifyForm.getUserRole() == null || adminMemberModifyForm.getUserRole().isBlank() ? null : adminMemberModifyForm.getUserRole().trim(); + String userType = adminMemberModifyForm.getUserType() == null || adminMemberModifyForm.getUserType().isBlank() ? null : adminMemberModifyForm.getUserType().trim(); + + if (email == null && hp == null && userRole == null && userType == null) { + adminMemberModifyVo.setUpdated(false); + adminMemberModifyVo.setResultCode(4001); + return adminMemberModifyVo; + } + + if (email != null || hp != null) { + TestUserModifyDto testUserModifyDto = new TestUserModifyDto(); + testUserModifyDto.setUserIdx(adminMemberModifyForm.getUserIdx()); + testUserModifyDto.setEmail(email); + testUserModifyDto.setHp(hp); + + TestUserModifyVo testUserModifyVo = testUserService.updateTestUserModify(testUserModifyDto); + + if (!testUserModifyVo.isUpdated()) { + adminMemberModifyVo.setUpdated(false); + adminMemberModifyVo.setResultCode(testUserModifyVo.getResultCode()); + return adminMemberModifyVo; + } + } + + if (userRole != null || userType != null) { + TestUserTokenModifyDto testUserTokenModifyDto = new TestUserTokenModifyDto(); + + testUserTokenModifyDto.setUserTokenIdx(testUserAdminMemberViewVo.getUserTokenIdx()); + testUserTokenModifyDto.setUserRole(userRole); + testUserTokenModifyDto.setUserType(userType); + + TestUserTokenModifyVo testUserTokenModifyVo = testUserTokenService.updateTestUserTokenModify(testUserTokenModifyDto); + + if (!testUserTokenModifyVo.isUpdated()) { + adminMemberModifyVo.setUpdated(false); + adminMemberModifyVo.setResultCode(testUserTokenModifyVo.getResultCode()); + return adminMemberModifyVo; + } + } + + adminMemberModifyVo.setUpdated(true); + adminMemberModifyVo.setResultCode(2005); + return adminMemberModifyVo; + } + + @Transactional + public AdminMemberDormantModifyVo updateAdminMemberDormantModify(AdminMemberDormantModifyForm adminMemberDormantModifyForm) { + TestUserModifyDto testUserModifyDto = new TestUserModifyDto(); + testUserModifyDto.setUserIdx(adminMemberDormantModifyForm.getUserIdx()); + testUserModifyDto.setDormantYn(adminMemberDormantModifyForm.getDormantYn()); + + TestUserModifyVo testUserModifyVo = testUserService.updateTestUserModify(testUserModifyDto); + + AdminMemberDormantModifyVo adminMemberDormantModifyVo = new AdminMemberDormantModifyVo(); + + adminMemberDormantModifyVo.setUserIdx(adminMemberDormantModifyForm.getUserIdx()); + adminMemberDormantModifyVo.setUpdated(testUserModifyVo.isUpdated()); + adminMemberDormantModifyVo.setResultCode(testUserModifyVo.getResultCode()); + return adminMemberDormantModifyVo; + } + + @Transactional + public AdminMemberWithdrawModifyVo updateAdminMemberWithdrawModify(AdminMemberWithdrawModifyForm adminMemberWithdrawModifyForm) { + TestUserModifyDto testUserModifyDto = new TestUserModifyDto(); + testUserModifyDto.setUserIdx(adminMemberWithdrawModifyForm.getUserIdx()); + testUserModifyDto.setWithdrawStatus(adminMemberWithdrawModifyForm.getWithdrawStatus()); + + TestUserModifyVo testUserModifyVo = testUserService.updateTestUserModify(testUserModifyDto); + + AdminMemberWithdrawModifyVo adminMemberWithdrawModifyVo = new AdminMemberWithdrawModifyVo(); + + adminMemberWithdrawModifyVo.setUserIdx(adminMemberWithdrawModifyForm.getUserIdx()); + adminMemberWithdrawModifyVo.setUpdated(testUserModifyVo.isUpdated()); + adminMemberWithdrawModifyVo.setResultCode(testUserModifyVo.getResultCode()); + return adminMemberWithdrawModifyVo; + } + + @Transactional + public AdminMemberDeleteVo updateAdminMemberDelete(AdminMemberDeleteForm adminMemberDeleteForm) { + List userIdxList = Arrays.stream(adminMemberDeleteForm.getUserIdxList().split(",")) + .map(String::trim) + .map(Integer::valueOf) + .distinct() + .toList(); + + int deletedCount = 0; + + for (Integer userIdx : userIdxList) { + TestUserDeleteDto testUserDeleteDto = new TestUserDeleteDto(); + testUserDeleteDto.setUserIdx(userIdx); + + TestUserDeleteVo testUserDeleteVo = testUserService.updateTestUserDelete(testUserDeleteDto); + + if (testUserDeleteVo.isDeleted()) { + deletedCount++; + } + } + + AdminMemberDeleteVo adminMemberDeleteVo = new AdminMemberDeleteVo(); + adminMemberDeleteVo.setUserIdxList(userIdxList); + adminMemberDeleteVo.setDeleted(deletedCount > 0); + adminMemberDeleteVo.setDeletedCount(deletedCount); + adminMemberDeleteVo.setResultCode(deletedCount > 0 ? 2005 : 2003); + return adminMemberDeleteVo; + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/CorsService.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/CorsService.java new file mode 100644 index 0000000..44127eb --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/CorsService.java @@ -0,0 +1,142 @@ +package com.alist.api.admin.modules.backOffice.service; + +import com.alist.api.admin.modules.backOffice.form.CorsAddForm; +import com.alist.api.admin.modules.backOffice.form.CorsDeleteForm; +import com.alist.api.admin.modules.backOffice.form.CorsListForm; +import com.alist.api.admin.modules.backOffice.vo.CorsAddVo; +import com.alist.api.admin.modules.backOffice.vo.CorsDeleteVo; +import com.alist.api.admin.modules.backOffice.vo.CorsListItemVo; +import com.alist.api.admin.modules.backOffice.vo.CorsListVo; +import com.alist.api.admin.modules.backOffice.vo.CorsReloadVo; +import com.alist.api.core.common.utils.SecurityUtil; +import com.alist.api.core.config.cors.CorsCache; +import com.alist.api.core.config.cors.CorsCachePublisher; +import com.alist.api.core.config.cors.CorsCacheRefreshEvent; +import com.alist.api.core.modules.standard.testCorsAllowedList.dto.TestCorsAllowedListAddDto; +import com.alist.api.core.modules.standard.testCorsAllowedList.dto.TestCorsAllowedListDeleteDto; +import com.alist.api.core.modules.standard.testCorsAllowedList.dto.TestCorsAllowedListListDto; +import com.alist.api.core.modules.standard.testCorsAllowedList.service.TestCorsAllowedListService; +import com.alist.api.core.modules.standard.testCorsAllowedList.vo.TestCorsAllowedListAddVo; +import com.alist.api.core.modules.standard.testCorsAllowedList.vo.TestCorsAllowedListDeleteVo; +import com.alist.api.core.modules.standard.testCorsAllowedList.vo.TestCorsAllowedListListItemVo; +import com.alist.api.core.modules.standard.testCorsAllowedList.vo.TestCorsAllowedListListVo; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class CorsService { + private final TestCorsAllowedListService testCorsAllowedListService; + private final ApplicationEventPublisher applicationEventPublisher; + private final CorsCache corsCache; + private final CorsCachePublisher corsCachePublisher; + + public CorsService(TestCorsAllowedListService testCorsAllowedListService, ApplicationEventPublisher applicationEventPublisher, CorsCache corsCache, CorsCachePublisher corsCachePublisher) { + this.testCorsAllowedListService = testCorsAllowedListService; + this.applicationEventPublisher = applicationEventPublisher; + this.corsCache = corsCache; + this.corsCachePublisher = corsCachePublisher; + } + + @Transactional(readOnly = true) + public CorsListVo selectCorsList(CorsListForm corsListForm) { + TestCorsAllowedListListDto testCorsAllowedListListDto = new TestCorsAllowedListListDto(); + testCorsAllowedListListDto.setPage(corsListForm.getPage()); + testCorsAllowedListListDto.setSize(corsListForm.getSize()); + testCorsAllowedListListDto.setUserTokenIdx(corsListForm.getUserTokenIdx()); + testCorsAllowedListListDto.setAllowedOrigin(corsListForm.getAllowedOrigin() == null || corsListForm.getAllowedOrigin().isBlank() ? null : corsListForm.getAllowedOrigin().trim()); + + TestCorsAllowedListListVo testCorsAllowedListListVo = testCorsAllowedListService.selectTestCorsAllowedListList(testCorsAllowedListListDto); + List corsList = new ArrayList<>(); + + for (TestCorsAllowedListListItemVo testCorsAllowedListListItemVo : testCorsAllowedListListVo.getTestCorsAllowedList()) { + CorsListItemVo corsListItemVo = new CorsListItemVo(); + corsListItemVo.setCorsIdx(testCorsAllowedListListItemVo.getCorsIdx()); + corsListItemVo.setUserTokenIdx(testCorsAllowedListListItemVo.getUserTokenIdx()); + corsListItemVo.setAllowedOrigin(testCorsAllowedListListItemVo.getAllowedOrigin()); + corsListItemVo.setCreatedAt(testCorsAllowedListListItemVo.getCreatedAt()); + corsList.add(corsListItemVo); + } + + CorsListVo corsListVo = new CorsListVo(); + corsListVo.setCorsList(corsList); + corsListVo.setPaging(testCorsAllowedListListDto, testCorsAllowedListListVo.getTotalCount()); + return corsListVo; + } + + @Transactional + public CorsAddVo insertCorsAdd(CorsAddForm corsAddForm) { + CorsAddVo corsAddVo = new CorsAddVo(); + + if (SecurityUtil.getLoginUserTokenIdx() == null) { + corsAddVo.setResultCode(401); + return corsAddVo; + } + + String allowedOrigin = corsAddForm.getAllowedOrigin().trim(); + + if (testCorsAllowedListService.selectTestCorsAllowedListAllowedOriginCount(allowedOrigin) > 0) { + corsAddVo.setResultCode(2004); + return corsAddVo; + } + + TestCorsAllowedListAddDto testCorsAllowedListAddDto = new TestCorsAllowedListAddDto(); + testCorsAllowedListAddDto.setUserTokenIdx(corsAddForm.getUserTokenIdx() == null ? 0 : corsAddForm.getUserTokenIdx()); + testCorsAllowedListAddDto.setAllowedOrigin(allowedOrigin); + + TestCorsAllowedListAddVo testCorsAllowedListAddVo = testCorsAllowedListService.insertTestCorsAllowedListAdd(testCorsAllowedListAddDto); + corsAddVo.setAdded(testCorsAllowedListAddVo.isAdded()); + corsAddVo.setCorsIdx(testCorsAllowedListAddVo.getCorsIdx()); + corsAddVo.setResultCode(testCorsAllowedListAddVo.getResultCode()); + + if (corsAddVo.isAdded()) { + applicationEventPublisher.publishEvent(new CorsCacheRefreshEvent()); + } + + return corsAddVo; + } + + @Transactional + public CorsDeleteVo updateCorsDelete(CorsDeleteForm corsDeleteForm) { + CorsDeleteVo corsDeleteVo = new CorsDeleteVo(); + corsDeleteVo.setCorsIdx(corsDeleteForm.getCorsIdx()); + + if (SecurityUtil.getLoginUserTokenIdx() == null) { + corsDeleteVo.setResultCode(401); + return corsDeleteVo; + } + + TestCorsAllowedListDeleteDto testCorsAllowedListDeleteDto = new TestCorsAllowedListDeleteDto(); + testCorsAllowedListDeleteDto.setCorsIdx(corsDeleteForm.getCorsIdx()); + + TestCorsAllowedListDeleteVo testCorsAllowedListDeleteVo = testCorsAllowedListService.updateTestCorsAllowedListDelete(testCorsAllowedListDeleteDto); + corsDeleteVo.setDeleted(testCorsAllowedListDeleteVo.isDeleted()); + corsDeleteVo.setResultCode(testCorsAllowedListDeleteVo.getResultCode()); + + if (corsDeleteVo.isDeleted()) { + applicationEventPublisher.publishEvent(new CorsCacheRefreshEvent()); + } + + return corsDeleteVo; + } + + @Transactional(readOnly = true) + public CorsReloadVo updateCorsReload() { + CorsReloadVo corsReloadVo = new CorsReloadVo(); + + if (SecurityUtil.getLoginUserTokenIdx() == null) { + corsReloadVo.setResultCode(401); + return corsReloadVo; + } + + corsCache.syncFromDatabase(); + corsCachePublisher.publishReload(); + corsReloadVo.setReloaded(true); + corsReloadVo.setAllowedOriginList(corsCache.getAllowedOriginList()); + corsReloadVo.setResultCode(2005); + return corsReloadVo; + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/ScheduleService.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/ScheduleService.java new file mode 100644 index 0000000..ca198fb --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/ScheduleService.java @@ -0,0 +1,281 @@ +package com.alist.api.admin.modules.backOffice.service; + +import com.alist.api.admin.modules.backOffice.form.ScheduleAddForm; +import com.alist.api.admin.modules.backOffice.form.ScheduleDeleteForm; +import com.alist.api.admin.modules.backOffice.form.ScheduleListForm; +import com.alist.api.admin.modules.backOffice.form.ScheduleLogListForm; +import com.alist.api.admin.modules.backOffice.form.ScheduleModifyForm; +import com.alist.api.admin.modules.backOffice.form.ScheduleViewForm; +import com.alist.api.admin.modules.backOffice.vo.ScheduleAddVo; +import com.alist.api.admin.modules.backOffice.vo.ScheduleDeleteVo; +import com.alist.api.admin.modules.backOffice.vo.ScheduleListItemVo; +import com.alist.api.admin.modules.backOffice.vo.ScheduleListVo; +import com.alist.api.admin.modules.backOffice.vo.ScheduleLogListItemVo; +import com.alist.api.admin.modules.backOffice.vo.ScheduleLogListVo; +import com.alist.api.admin.modules.backOffice.vo.ScheduleModifyVo; +import com.alist.api.admin.modules.backOffice.vo.ScheduleViewVo; +import com.alist.api.core.common.utils.SecurityUtil; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobAddDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobDeleteDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobListDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobModifyDto; +import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobViewDto; +import com.alist.api.core.modules.standard.scheduleJob.service.ScheduleJobService; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobAddVo; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobDeleteVo; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobListItemVo; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobListVo; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobModifyVo; +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobViewVo; +import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogListDto; +import com.alist.api.core.modules.standard.scheduleJobLog.service.ScheduleJobLogService; +import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogListItemVo; +import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogListVo; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenViewDto; +import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService; +import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class ScheduleService { + private final ScheduleJobService scheduleJobService; + private final ScheduleJobLogService scheduleJobLogService; + private final TestUserTokenService testUserTokenService; + + public ScheduleService(ScheduleJobService scheduleJobService, ScheduleJobLogService scheduleJobLogService, TestUserTokenService testUserTokenService) { + this.scheduleJobService = scheduleJobService; + this.scheduleJobLogService = scheduleJobLogService; + this.testUserTokenService = testUserTokenService; + } + + @Transactional(readOnly = true) + public ScheduleListVo selectScheduleList(ScheduleListForm scheduleListForm) { + ScheduleJobListDto scheduleJobListDto = new ScheduleJobListDto(); + scheduleJobListDto.setPage(scheduleListForm.getPage()); + scheduleJobListDto.setSize(scheduleListForm.getSize()); + scheduleJobListDto.setKeyword(scheduleListForm.getKeyword() == null || scheduleListForm.getKeyword().isBlank() ? null : scheduleListForm.getKeyword().trim()); + scheduleJobListDto.setJobGroup(scheduleListForm.getJobGroup() == null || scheduleListForm.getJobGroup().isBlank() ? null : scheduleListForm.getJobGroup().trim()); + scheduleJobListDto.setLastStatus(scheduleListForm.getLastStatus()); + scheduleJobListDto.setUseYn(scheduleListForm.getUseYn() == null || scheduleListForm.getUseYn().isBlank() ? null : scheduleListForm.getUseYn().trim()); + + ScheduleJobListVo scheduleJobListVo = scheduleJobService.selectScheduleJobList(scheduleJobListDto); + List scheduleList = new ArrayList<>(); + + for (ScheduleJobListItemVo scheduleJobListItemVo : scheduleJobListVo.getScheduleJobList()) { + ScheduleListItemVo scheduleListItemVo = new ScheduleListItemVo(); + scheduleListItemVo.setScheduleJobIdx(scheduleJobListItemVo.getScheduleJobIdx()); + scheduleListItemVo.setJobName(scheduleJobListItemVo.getJobName()); + scheduleListItemVo.setJobGroup(scheduleJobListItemVo.getJobGroup()); + scheduleListItemVo.setJobDescription(scheduleJobListItemVo.getJobDescription()); + scheduleListItemVo.setCronExpr(scheduleJobListItemVo.getCronExpr()); + scheduleListItemVo.setTimezoneId(scheduleJobListItemVo.getTimezoneId()); + scheduleListItemVo.setMaxRunningSeconds(scheduleJobListItemVo.getMaxRunningSeconds()); + scheduleListItemVo.setLastRunDate(scheduleJobListItemVo.getLastRunDate()); + scheduleListItemVo.setLastEndDate(scheduleJobListItemVo.getLastEndDate()); + scheduleListItemVo.setLastSuccessDate(scheduleJobListItemVo.getLastSuccessDate()); + scheduleListItemVo.setLastStatus(scheduleJobListItemVo.getLastStatus()); + scheduleListItemVo.setLastMessage(scheduleJobListItemVo.getLastMessage()); + scheduleListItemVo.setRunLockDate(scheduleJobListItemVo.getRunLockDate()); + scheduleListItemVo.setRunLockOwner(scheduleJobListItemVo.getRunLockOwner()); + scheduleListItemVo.setUseYn(scheduleJobListItemVo.getUseYn()); + scheduleListItemVo.setCreateDate(scheduleJobListItemVo.getCreateDate()); + scheduleListItemVo.setUpdateDate(scheduleJobListItemVo.getUpdateDate()); + scheduleList.add(scheduleListItemVo); + } + + ScheduleListVo scheduleListVo = new ScheduleListVo(); + scheduleListVo.setScheduleList(scheduleList); + scheduleListVo.setPaging(scheduleJobListDto, scheduleJobListVo.getTotalCount()); + return scheduleListVo; + } + + @Transactional(readOnly = true) + public ScheduleViewVo selectScheduleView(ScheduleViewForm scheduleViewForm) { + ScheduleJobViewDto scheduleJobViewDto = new ScheduleJobViewDto(); + scheduleJobViewDto.setScheduleJobIdx(scheduleViewForm.getScheduleJobIdx()); + + ScheduleJobViewVo scheduleJobViewVo = scheduleJobService.selectScheduleJobView(scheduleJobViewDto); + + if (scheduleJobViewVo == null) { + return null; + } + + ScheduleViewVo scheduleViewVo = new ScheduleViewVo(); + scheduleViewVo.setScheduleJobIdx(scheduleJobViewVo.getScheduleJobIdx()); + scheduleViewVo.setJobName(scheduleJobViewVo.getJobName()); + scheduleViewVo.setJobGroup(scheduleJobViewVo.getJobGroup()); + scheduleViewVo.setJobDescription(scheduleJobViewVo.getJobDescription()); + scheduleViewVo.setCronExpr(scheduleJobViewVo.getCronExpr()); + scheduleViewVo.setTimezoneId(scheduleJobViewVo.getTimezoneId()); + scheduleViewVo.setMaxRunningSeconds(scheduleJobViewVo.getMaxRunningSeconds()); + scheduleViewVo.setLastRunDate(scheduleJobViewVo.getLastRunDate()); + scheduleViewVo.setLastEndDate(scheduleJobViewVo.getLastEndDate()); + scheduleViewVo.setLastSuccessDate(scheduleJobViewVo.getLastSuccessDate()); + scheduleViewVo.setLastStatus(scheduleJobViewVo.getLastStatus()); + scheduleViewVo.setLastMessage(scheduleJobViewVo.getLastMessage()); + scheduleViewVo.setRunLockDate(scheduleJobViewVo.getRunLockDate()); + scheduleViewVo.setRunLockOwner(scheduleJobViewVo.getRunLockOwner()); + scheduleViewVo.setUseYn(scheduleJobViewVo.getUseYn()); + scheduleViewVo.setCreateDate(scheduleJobViewVo.getCreateDate()); + scheduleViewVo.setCreateMember(scheduleJobViewVo.getCreateMember()); + scheduleViewVo.setUpdateDate(scheduleJobViewVo.getUpdateDate()); + scheduleViewVo.setUpdateMember(scheduleJobViewVo.getUpdateMember()); + return scheduleViewVo; + } + + @Transactional + public ScheduleAddVo insertScheduleAdd(ScheduleAddForm scheduleAddForm) { + ScheduleAddVo scheduleAddVo = new ScheduleAddVo(); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + + if (userTokenIdx == null) { + scheduleAddVo.setResultCode(401); + return scheduleAddVo; + } + + TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto(); + testUserTokenViewDto.setUserTokenIdx(userTokenIdx); + TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto); + + if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) { + scheduleAddVo.setResultCode(401); + return scheduleAddVo; + } + + ScheduleJobAddDto scheduleJobAddDto = new ScheduleJobAddDto(); + scheduleJobAddDto.setJobName(scheduleAddForm.getJobName().trim()); + scheduleJobAddDto.setJobGroup(scheduleAddForm.getJobGroup() == null || scheduleAddForm.getJobGroup().isBlank() ? "DEFAULT" : scheduleAddForm.getJobGroup().trim()); + scheduleJobAddDto.setJobDescription(scheduleAddForm.getJobDescription() == null ? null : scheduleAddForm.getJobDescription().trim()); + scheduleJobAddDto.setCronExpr(scheduleAddForm.getCronExpr().trim()); + scheduleJobAddDto.setTimezoneId(scheduleAddForm.getTimezoneId() == null || scheduleAddForm.getTimezoneId().isBlank() ? "Asia/Seoul" : scheduleAddForm.getTimezoneId().trim()); + scheduleJobAddDto.setMaxRunningSeconds(scheduleAddForm.getMaxRunningSeconds() == null ? 1800 : scheduleAddForm.getMaxRunningSeconds()); + scheduleJobAddDto.setUseYn(scheduleAddForm.getUseYn() == null || scheduleAddForm.getUseYn().isBlank() ? "Y" : scheduleAddForm.getUseYn().trim()); + scheduleJobAddDto.setCreateMember(testUserTokenViewVo.getUserIdx()); + scheduleJobAddDto.setUpdateMember(testUserTokenViewVo.getUserIdx()); + + if (scheduleJobService.selectScheduleJobDuplicateCount(scheduleJobAddDto) > 0) { + scheduleAddVo.setResultCode(2004); + return scheduleAddVo; + } + + ScheduleJobAddVo scheduleJobAddVo = scheduleJobService.insertScheduleJobAdd(scheduleJobAddDto); + scheduleAddVo.setAdded(scheduleJobAddVo.isAdded()); + scheduleAddVo.setScheduleJobIdx(scheduleJobAddVo.getScheduleJobIdx()); + scheduleAddVo.setResultCode(scheduleJobAddVo.getResultCode()); + return scheduleAddVo; + } + + @Transactional + public ScheduleModifyVo updateScheduleModify(ScheduleModifyForm scheduleModifyForm) { + ScheduleModifyVo scheduleModifyVo = new ScheduleModifyVo(); + scheduleModifyVo.setScheduleJobIdx(scheduleModifyForm.getScheduleJobIdx()); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + + if (userTokenIdx == null) { + scheduleModifyVo.setResultCode(401); + return scheduleModifyVo; + } + + TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto(); + testUserTokenViewDto.setUserTokenIdx(userTokenIdx); + TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto); + + if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) { + scheduleModifyVo.setResultCode(401); + return scheduleModifyVo; + } + + ScheduleJobModifyDto scheduleJobModifyDto = new ScheduleJobModifyDto(); + scheduleJobModifyDto.setScheduleJobIdx(scheduleModifyForm.getScheduleJobIdx()); + scheduleJobModifyDto.setJobName(scheduleModifyForm.getJobName().trim()); + scheduleJobModifyDto.setJobGroup(scheduleModifyForm.getJobGroup() == null || scheduleModifyForm.getJobGroup().isBlank() ? "DEFAULT" : scheduleModifyForm.getJobGroup().trim()); + scheduleJobModifyDto.setJobDescription(scheduleModifyForm.getJobDescription() == null ? null : scheduleModifyForm.getJobDescription().trim()); + scheduleJobModifyDto.setCronExpr(scheduleModifyForm.getCronExpr().trim()); + scheduleJobModifyDto.setTimezoneId(scheduleModifyForm.getTimezoneId() == null || scheduleModifyForm.getTimezoneId().isBlank() ? "Asia/Seoul" : scheduleModifyForm.getTimezoneId().trim()); + scheduleJobModifyDto.setMaxRunningSeconds(scheduleModifyForm.getMaxRunningSeconds() == null ? 1800 : scheduleModifyForm.getMaxRunningSeconds()); + scheduleJobModifyDto.setUseYn(scheduleModifyForm.getUseYn() == null || scheduleModifyForm.getUseYn().isBlank() ? "Y" : scheduleModifyForm.getUseYn().trim()); + scheduleJobModifyDto.setUpdateMember(testUserTokenViewVo.getUserIdx()); + + if (scheduleJobService.selectScheduleJobModifyDuplicateCount(scheduleJobModifyDto) > 0) { + scheduleModifyVo.setResultCode(2004); + return scheduleModifyVo; + } + + ScheduleJobModifyVo scheduleJobModifyVo = scheduleJobService.updateScheduleJobModify(scheduleJobModifyDto); + scheduleModifyVo.setUpdated(scheduleJobModifyVo.isUpdated()); + scheduleModifyVo.setResultCode(scheduleJobModifyVo.getResultCode()); + return scheduleModifyVo; + } + + @Transactional + public ScheduleDeleteVo updateScheduleDelete(ScheduleDeleteForm scheduleDeleteForm) { + ScheduleDeleteVo scheduleDeleteVo = new ScheduleDeleteVo(); + scheduleDeleteVo.setScheduleJobIdx(scheduleDeleteForm.getScheduleJobIdx()); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + + if (userTokenIdx == null) { + scheduleDeleteVo.setResultCode(401); + return scheduleDeleteVo; + } + + TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto(); + testUserTokenViewDto.setUserTokenIdx(userTokenIdx); + TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto); + + if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) { + scheduleDeleteVo.setResultCode(401); + return scheduleDeleteVo; + } + + ScheduleJobDeleteDto scheduleJobDeleteDto = new ScheduleJobDeleteDto(); + scheduleJobDeleteDto.setScheduleJobIdx(scheduleDeleteForm.getScheduleJobIdx()); + scheduleJobDeleteDto.setUpdateMember(testUserTokenViewVo.getUserIdx()); + + ScheduleJobDeleteVo scheduleJobDeleteVo = scheduleJobService.updateScheduleJobDelete(scheduleJobDeleteDto); + scheduleDeleteVo.setDeleted(scheduleJobDeleteVo.isDeleted()); + scheduleDeleteVo.setResultCode(scheduleJobDeleteVo.getResultCode()); + return scheduleDeleteVo; + } + + @Transactional(readOnly = true) + public ScheduleLogListVo selectScheduleLogList(ScheduleLogListForm scheduleLogListForm) { + ScheduleJobLogListDto scheduleJobLogListDto = new ScheduleJobLogListDto(); + scheduleJobLogListDto.setPage(scheduleLogListForm.getPage()); + scheduleJobLogListDto.setSize(scheduleLogListForm.getSize()); + scheduleJobLogListDto.setScheduleJobIdx(scheduleLogListForm.getScheduleJobIdx()); + scheduleJobLogListDto.setStatus(scheduleLogListForm.getStatus()); + scheduleJobLogListDto.setStartDateFrom(scheduleLogListForm.getStartDateFrom() == null || scheduleLogListForm.getStartDateFrom().isBlank() ? null : scheduleLogListForm.getStartDateFrom().trim()); + scheduleJobLogListDto.setStartDateTo(scheduleLogListForm.getStartDateTo() == null || scheduleLogListForm.getStartDateTo().isBlank() ? null : scheduleLogListForm.getStartDateTo().trim()); + + ScheduleJobLogListVo scheduleJobLogListVo = scheduleJobLogService.selectScheduleJobLogList(scheduleJobLogListDto); + List scheduleLogList = new ArrayList<>(); + + for (ScheduleJobLogListItemVo scheduleJobLogListItemVo : scheduleJobLogListVo.getScheduleJobLogList()) { + ScheduleLogListItemVo scheduleLogListItemVo = new ScheduleLogListItemVo(); + scheduleLogListItemVo.setScheduleJobLogIdx(scheduleJobLogListItemVo.getScheduleJobLogIdx()); + scheduleLogListItemVo.setScheduleJobIdx(scheduleJobLogListItemVo.getScheduleJobIdx()); + scheduleLogListItemVo.setRunId(scheduleJobLogListItemVo.getRunId()); + scheduleLogListItemVo.setJobName(scheduleJobLogListItemVo.getJobName()); + scheduleLogListItemVo.setStartDate(scheduleJobLogListItemVo.getStartDate()); + scheduleLogListItemVo.setEndDate(scheduleJobLogListItemVo.getEndDate()); + scheduleLogListItemVo.setTargetCount(scheduleJobLogListItemVo.getTargetCount()); + scheduleLogListItemVo.setSuccessCount(scheduleJobLogListItemVo.getSuccessCount()); + scheduleLogListItemVo.setFailCount(scheduleJobLogListItemVo.getFailCount()); + scheduleLogListItemVo.setMessage(scheduleJobLogListItemVo.getMessage()); + scheduleLogListItemVo.setErrorMessage(scheduleJobLogListItemVo.getErrorMessage()); + scheduleLogListItemVo.setLockOwner(scheduleJobLogListItemVo.getLockOwner()); + scheduleLogListItemVo.setStatus(scheduleJobLogListItemVo.getStatus()); + scheduleLogListItemVo.setCreateDate(scheduleJobLogListItemVo.getCreateDate()); + scheduleLogList.add(scheduleLogListItemVo); + } + + ScheduleLogListVo scheduleLogListVo = new ScheduleLogListVo(); + scheduleLogListVo.setScheduleLogList(scheduleLogList); + scheduleLogListVo.setPaging(scheduleJobLogListDto, scheduleJobLogListVo.getTotalCount()); + return scheduleLogListVo; + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/SsoService.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/SsoService.java new file mode 100644 index 0000000..620f2fc --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/service/SsoService.java @@ -0,0 +1,211 @@ +package com.alist.api.admin.modules.backOffice.service; + +import com.alist.api.admin.modules.backOffice.form.SsoAddForm; +import com.alist.api.admin.modules.backOffice.form.SsoDeleteForm; +import com.alist.api.admin.modules.backOffice.form.SsoListForm; +import com.alist.api.admin.modules.backOffice.form.SsoModifyForm; +import com.alist.api.admin.modules.backOffice.form.SsoViewForm; +import com.alist.api.admin.modules.backOffice.vo.SsoAddVo; +import com.alist.api.admin.modules.backOffice.vo.SsoDeleteVo; +import com.alist.api.admin.modules.backOffice.vo.SsoListItemVo; +import com.alist.api.admin.modules.backOffice.vo.SsoListVo; +import com.alist.api.admin.modules.backOffice.vo.SsoModifyVo; +import com.alist.api.admin.modules.backOffice.vo.SsoViewVo; +import com.alist.api.core.common.utils.SecurityUtil; +import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientAddDto; +import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientDeleteDto; +import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientListDto; +import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientModifyDto; +import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientViewDto; +import com.alist.api.core.modules.standard.ssoClient.service.SsoClientService; +import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientAddVo; +import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientDeleteVo; +import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientListItemVo; +import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientListVo; +import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientModifyVo; +import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientViewVo; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenViewDto; +import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService; +import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class SsoService { + private final SsoClientService ssoClientService; + private final TestUserTokenService testUserTokenService; + + public SsoService(SsoClientService ssoClientService, TestUserTokenService testUserTokenService) { + this.ssoClientService = ssoClientService; + this.testUserTokenService = testUserTokenService; + } + + @Transactional(readOnly = true) + public SsoListVo selectSsoList(SsoListForm ssoListForm) { + SsoClientListDto ssoClientListDto = new SsoClientListDto(); + ssoClientListDto.setPage(ssoListForm.getPage()); + ssoClientListDto.setSize(ssoListForm.getSize()); + ssoClientListDto.setKeyword(ssoListForm.getKeyword() == null || ssoListForm.getKeyword().isBlank() ? null : ssoListForm.getKeyword().trim()); + ssoClientListDto.setUseYn(ssoListForm.getUseYn() == null || ssoListForm.getUseYn().isBlank() ? null : ssoListForm.getUseYn().trim()); + + SsoClientListVo ssoClientListVo = ssoClientService.selectSsoClientList(ssoClientListDto); + List ssoList = new ArrayList<>(); + + for (SsoClientListItemVo ssoClientListItemVo : ssoClientListVo.getSsoClientList()) { + SsoListItemVo ssoListItemVo = new SsoListItemVo(); + ssoListItemVo.setSsoClientIdx(ssoClientListItemVo.getSsoClientIdx()); + ssoListItemVo.setClientId(ssoClientListItemVo.getClientId()); + ssoListItemVo.setClientName(ssoClientListItemVo.getClientName()); + ssoListItemVo.setRedirectUri(ssoClientListItemVo.getRedirectUri()); + ssoListItemVo.setMemo(ssoClientListItemVo.getMemo()); + ssoListItemVo.setUseYn(ssoClientListItemVo.getUseYn()); + ssoListItemVo.setCreateDate(ssoClientListItemVo.getCreateDate()); + ssoListItemVo.setUpdateDate(ssoClientListItemVo.getUpdateDate()); + ssoList.add(ssoListItemVo); + } + + SsoListVo ssoListVo = new SsoListVo(); + ssoListVo.setSsoList(ssoList); + ssoListVo.setPaging(ssoClientListDto, ssoClientListVo.getTotalCount()); + return ssoListVo; + } + + @Transactional(readOnly = true) + public SsoViewVo selectSsoView(SsoViewForm ssoViewForm) { + SsoClientViewDto ssoClientViewDto = new SsoClientViewDto(); + ssoClientViewDto.setSsoClientIdx(ssoViewForm.getSsoClientIdx()); + + SsoClientViewVo ssoClientViewVo = ssoClientService.selectSsoClientView(ssoClientViewDto); + if (ssoClientViewVo == null) { + return null; + } + + SsoViewVo ssoViewVo = new SsoViewVo(); + ssoViewVo.setSsoClientIdx(ssoClientViewVo.getSsoClientIdx()); + ssoViewVo.setClientId(ssoClientViewVo.getClientId()); + ssoViewVo.setClientName(ssoClientViewVo.getClientName()); + ssoViewVo.setRedirectUri(ssoClientViewVo.getRedirectUri()); + ssoViewVo.setMemo(ssoClientViewVo.getMemo()); + ssoViewVo.setUseYn(ssoClientViewVo.getUseYn()); + ssoViewVo.setCreateDate(ssoClientViewVo.getCreateDate()); + ssoViewVo.setCreateMember(ssoClientViewVo.getCreateMember()); + ssoViewVo.setUpdateDate(ssoClientViewVo.getUpdateDate()); + ssoViewVo.setUpdateMember(ssoClientViewVo.getUpdateMember()); + return ssoViewVo; + } + + @Transactional + public SsoAddVo insertSsoAdd(SsoAddForm ssoAddForm) { + SsoAddVo ssoAddVo = new SsoAddVo(); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + if (userTokenIdx == null) { + ssoAddVo.setAdded(false); + ssoAddVo.setResultCode(401); + return ssoAddVo; + } + + TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto(); + testUserTokenViewDto.setUserTokenIdx(userTokenIdx); + TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto); + if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) { + ssoAddVo.setAdded(false); + ssoAddVo.setResultCode(401); + return ssoAddVo; + } + + SsoClientAddDto ssoClientAddDto = new SsoClientAddDto(); + ssoClientAddDto.setClientId(ssoAddForm.getClientId().trim()); + ssoClientAddDto.setClientName(ssoAddForm.getClientName().trim()); + ssoClientAddDto.setRedirectUri(ssoAddForm.getRedirectUri().trim()); + ssoClientAddDto.setMemo(ssoAddForm.getMemo() == null ? null : ssoAddForm.getMemo().trim()); + ssoClientAddDto.setUseYn(ssoAddForm.getUseYn() == null || ssoAddForm.getUseYn().isBlank() ? "Y" : ssoAddForm.getUseYn().trim()); + ssoClientAddDto.setCreateMember(testUserTokenViewVo.getUserIdx()); + ssoClientAddDto.setUpdateMember(testUserTokenViewVo.getUserIdx()); + + if (ssoClientService.selectSsoClientDuplicateCount(ssoClientAddDto.getClientId()) > 0) { + ssoAddVo.setAdded(false); + ssoAddVo.setResultCode(2004); + return ssoAddVo; + } + + SsoClientAddVo ssoClientAddVo = ssoClientService.insertSsoClientAdd(ssoClientAddDto); + ssoAddVo.setAdded(ssoClientAddVo.isAdded()); + ssoAddVo.setSsoClientIdx(ssoClientAddVo.getSsoClientIdx()); + ssoAddVo.setResultCode(ssoClientAddVo.getResultCode()); + return ssoAddVo; + } + + @Transactional + public SsoModifyVo updateSsoModify(SsoModifyForm ssoModifyForm) { + SsoModifyVo ssoModifyVo = new SsoModifyVo(); + ssoModifyVo.setSsoClientIdx(ssoModifyForm.getSsoClientIdx()); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + if (userTokenIdx == null) { + ssoModifyVo.setUpdated(false); + ssoModifyVo.setResultCode(401); + return ssoModifyVo; + } + + TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto(); + testUserTokenViewDto.setUserTokenIdx(userTokenIdx); + TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto); + if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) { + ssoModifyVo.setUpdated(false); + ssoModifyVo.setResultCode(401); + return ssoModifyVo; + } + + SsoClientModifyDto ssoClientModifyDto = new SsoClientModifyDto(); + ssoClientModifyDto.setSsoClientIdx(ssoModifyForm.getSsoClientIdx()); + ssoClientModifyDto.setClientId(ssoModifyForm.getClientId().trim()); + ssoClientModifyDto.setClientName(ssoModifyForm.getClientName().trim()); + ssoClientModifyDto.setRedirectUri(ssoModifyForm.getRedirectUri().trim()); + ssoClientModifyDto.setMemo(ssoModifyForm.getMemo() == null ? null : ssoModifyForm.getMemo().trim()); + ssoClientModifyDto.setUseYn(ssoModifyForm.getUseYn() == null || ssoModifyForm.getUseYn().isBlank() ? "Y" : ssoModifyForm.getUseYn().trim()); + ssoClientModifyDto.setUpdateMember(testUserTokenViewVo.getUserIdx()); + + if (ssoClientService.selectSsoClientModifyDuplicateCount(ssoClientModifyDto) > 0) { + ssoModifyVo.setUpdated(false); + ssoModifyVo.setResultCode(2004); + return ssoModifyVo; + } + + SsoClientModifyVo ssoClientModifyVo = ssoClientService.updateSsoClientModify(ssoClientModifyDto); + ssoModifyVo.setUpdated(ssoClientModifyVo.isUpdated()); + ssoModifyVo.setResultCode(ssoClientModifyVo.getResultCode()); + return ssoModifyVo; + } + + @Transactional + public SsoDeleteVo updateSsoDelete(SsoDeleteForm ssoDeleteForm) { + SsoDeleteVo ssoDeleteVo = new SsoDeleteVo(); + ssoDeleteVo.setSsoClientIdx(ssoDeleteForm.getSsoClientIdx()); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + if (userTokenIdx == null) { + ssoDeleteVo.setDeleted(false); + ssoDeleteVo.setResultCode(401); + return ssoDeleteVo; + } + + TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto(); + testUserTokenViewDto.setUserTokenIdx(userTokenIdx); + TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto); + if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) { + ssoDeleteVo.setDeleted(false); + ssoDeleteVo.setResultCode(401); + return ssoDeleteVo; + } + + SsoClientDeleteDto ssoClientDeleteDto = new SsoClientDeleteDto(); + ssoClientDeleteDto.setSsoClientIdx(ssoDeleteForm.getSsoClientIdx()); + ssoClientDeleteDto.setUpdateMember(testUserTokenViewVo.getUserIdx()); + + SsoClientDeleteVo ssoClientDeleteVo = ssoClientService.updateSsoClientDelete(ssoClientDeleteDto); + ssoDeleteVo.setDeleted(ssoClientDeleteVo.isDeleted()); + ssoDeleteVo.setResultCode(ssoClientDeleteVo.getResultCode()); + return ssoDeleteVo; + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberAddVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberAddVo.java new file mode 100644 index 0000000..ea1760d --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberAddVo.java @@ -0,0 +1,28 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 등록 응답") +@Getter +@Setter +public class AdminMemberAddVo { + @Schema(description = "등록 성공 여부", example = "true") + private boolean added; + @Schema(description = "회원 PK", example = "16") + private Integer userIdx; + @Schema(description = "회원 토큰 PK", example = "16") + private Integer userTokenIdx; + @Schema(description = "아이디", example = "admin01") + private String id; + @Schema(description = "회원 권한", example = "A") + private String userRole; + @Schema(description = "회원 유형", example = "A") + private String userType; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberDeleteVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberDeleteVo.java new file mode 100644 index 0000000..e46a7d2 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberDeleteVo.java @@ -0,0 +1,24 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "관리자 계정 삭제 응답") +@Getter +@Setter +public class AdminMemberDeleteVo { + @Schema(description = "삭제 성공 여부", example = "true") + private boolean deleted; + @Schema(description = "삭제 요청 회원 PK 목록", example = "[16, 17]") + private List userIdxList; + @Schema(description = "실제 삭제 처리 수", example = "2") + private int deletedCount; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberDormantModifyVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberDormantModifyVo.java new file mode 100644 index 0000000..956b43e --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberDormantModifyVo.java @@ -0,0 +1,20 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 휴면 상태 변경 응답") +@Getter +@Setter +public class AdminMemberDormantModifyVo { + @Schema(description = "수정 성공 여부", example = "true") + private boolean updated; + @Schema(description = "회원 PK", example = "16") + private Integer userIdx; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberListItemVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberListItemVo.java new file mode 100644 index 0000000..e8f9ef4 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberListItemVo.java @@ -0,0 +1,51 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Schema(description = "관리자 계정 목록 항목") +@Getter +@Setter +public class AdminMemberListItemVo { + @Schema(description = "회원 PK", example = "16") + private Integer userIdx; + + @Schema(description = "회원 토큰 PK", example = "16") + private Integer userTokenIdx; + + @Schema(description = "아이디", example = "admin01") + private String id; + + @Schema(description = "이메일", example = "admin@alist.co.kr") + private String email; + + @Schema(description = "휴대폰 번호", example = "01012345678") + private String hp; + + @Schema(description = "회원 권한", example = "A") + private String userRole; + + @Schema(description = "회원 유형. A: 관리자, T: 교사, S: 학생", example = "A") + private String userType; + + @Schema(description = "휴면 여부. N: 정상, Y: 휴면", example = "N") + private String dormantYn; + + @Schema(description = "휴면 처리일") + private LocalDateTime dormantAt; + + @Schema(description = "탈퇴 상태. N: 정상, P: 대기, Y: 완료", example = "N") + private String withdrawStatus; + + @Schema(description = "탈퇴 처리일") + private LocalDateTime withdrawAt; + + @Schema(description = "생성일") + private LocalDateTime createAt; + + @Schema(description = "수정일") + private LocalDateTime updateAt; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberListVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberListVo.java new file mode 100644 index 0000000..beb9dd4 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberListVo.java @@ -0,0 +1,16 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "관리자 계정 목록 조회 응답") +@Getter +@Setter +public class AdminMemberListVo extends PagingResponse { + @Schema(description = "관리자 계정 목록") + private List adminMemberList; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberModifyVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberModifyVo.java new file mode 100644 index 0000000..7a6ca26 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberModifyVo.java @@ -0,0 +1,20 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 수정 응답") +@Getter +@Setter +public class AdminMemberModifyVo { + @Schema(description = "수정 성공 여부", example = "true") + private boolean updated; + @Schema(description = "회원 PK", example = "16") + private Integer userIdx; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberViewVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberViewVo.java new file mode 100644 index 0000000..4441b76 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberViewVo.java @@ -0,0 +1,51 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Schema(description = "관리자 계정 상세 조회 응답") +@Getter +@Setter +public class AdminMemberViewVo { + @Schema(description = "회원 PK", example = "16") + private Integer userIdx; + + @Schema(description = "회원 토큰 PK", example = "16") + private Integer userTokenIdx; + + @Schema(description = "아이디", example = "admin01") + private String id; + + @Schema(description = "이메일", example = "admin@alist.co.kr") + private String email; + + @Schema(description = "휴대폰 번호", example = "01012345678") + private String hp; + + @Schema(description = "회원 권한", example = "A") + private String userRole; + + @Schema(description = "회원 유형. A: 관리자, T: 교사, S: 학생", example = "A") + private String userType; + + @Schema(description = "휴면 여부. N: 정상, Y: 휴면", example = "N") + private String dormantYn; + + @Schema(description = "휴면 처리일") + private LocalDateTime dormantAt; + + @Schema(description = "탈퇴 상태. N: 정상, P: 대기, Y: 완료", example = "N") + private String withdrawStatus; + + @Schema(description = "탈퇴 처리일") + private LocalDateTime withdrawAt; + + @Schema(description = "생성일") + private LocalDateTime createAt; + + @Schema(description = "수정일") + private LocalDateTime updateAt; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberWithdrawModifyVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberWithdrawModifyVo.java new file mode 100644 index 0000000..54de3d4 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/AdminMemberWithdrawModifyVo.java @@ -0,0 +1,20 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 계정 탈퇴 상태 변경 응답") +@Getter +@Setter +public class AdminMemberWithdrawModifyVo { + @Schema(description = "수정 성공 여부", example = "true") + private boolean updated; + @Schema(description = "회원 PK", example = "16") + private Integer userIdx; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsAddVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsAddVo.java new file mode 100644 index 0000000..ed31da1 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsAddVo.java @@ -0,0 +1,21 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "CORS 허용 Origin 등록 응답") +@Getter +@Setter +public class CorsAddVo { + @Schema(description = "등록 성공 여부", example = "true") + private boolean added; + + @Schema(description = "등록된 CORS 설정 PK", example = "1") + private Integer corsIdx; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsDeleteVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsDeleteVo.java new file mode 100644 index 0000000..3459333 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsDeleteVo.java @@ -0,0 +1,21 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "CORS 허용 Origin 삭제 응답") +@Getter +@Setter +public class CorsDeleteVo { + @Schema(description = "삭제 성공 여부", example = "true") + private boolean deleted; + + @Schema(description = "CORS 설정 PK", example = "1") + private Integer corsIdx; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsListItemVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsListItemVo.java new file mode 100644 index 0000000..7f4a6b8 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsListItemVo.java @@ -0,0 +1,24 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Schema(description = "CORS 허용 Origin 목록 항목") +@Getter +@Setter +public class CorsListItemVo { + @Schema(description = "CORS 설정 PK", example = "1") + private Integer corsIdx; + + @Schema(description = "API 토큰 PK. 0은 전체 적용", example = "0") + private Integer userTokenIdx; + + @Schema(description = "허용 Origin", example = "https://admin.alist.co.kr") + private String allowedOrigin; + + @Schema(description = "등록일시") + private LocalDateTime createdAt; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsListVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsListVo.java new file mode 100644 index 0000000..ff669a7 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsListVo.java @@ -0,0 +1,16 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "CORS 허용 Origin 목록 조회 응답") +@Getter +@Setter +public class CorsListVo extends PagingResponse { + @Schema(description = "CORS 허용 Origin 목록") + private List corsList; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsReloadVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsReloadVo.java new file mode 100644 index 0000000..34558eb --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/CorsReloadVo.java @@ -0,0 +1,23 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "CORS Redis 목록 강제 갱신 응답") +@Getter +@Setter +public class CorsReloadVo { + @Schema(description = "갱신 성공 여부", example = "true") + private boolean reloaded; + + @Schema(description = "Redis에 반영된 허용 Origin 목록") + private List allowedOriginList; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleAddVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleAddVo.java new file mode 100644 index 0000000..55c5b27 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleAddVo.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "스케줄 작업 등록 응답") +@Getter +@Setter +public class ScheduleAddVo { + @Schema(description = "등록 성공 여부", example = "true") private boolean added; + @Schema(description = "스케줄 작업 PK", example = "9") private Long scheduleJobIdx; + @JsonIgnore @Schema(hidden = true) private int resultCode; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleDeleteVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleDeleteVo.java new file mode 100644 index 0000000..fb7d329 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleDeleteVo.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "스케줄 작업 삭제 응답") +@Getter +@Setter +public class ScheduleDeleteVo { + @Schema(description = "삭제 성공 여부", example = "true") private boolean deleted; + @Schema(description = "스케줄 작업 PK", example = "9") private Long scheduleJobIdx; + @JsonIgnore @Schema(hidden = true) private int resultCode; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleListItemVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleListItemVo.java new file mode 100644 index 0000000..4102763 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleListItemVo.java @@ -0,0 +1,30 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobListItemVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import java.time.LocalDateTime; + +@Schema(description = "스케줄 작업 목록 항목") +@Getter +@Setter +public class ScheduleListItemVo { + @Schema(description = "스케줄 작업 PK", example = "9") private Long scheduleJobIdx; + @Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY") private String jobName; + @Schema(description = "작업 그룹", example = "DEFAULT") private String jobGroup; + @Schema(description = "작업 설명") private String jobDescription; + @Schema(description = "크론식") private String cronExpr; + @Schema(description = "타임존") private String timezoneId; + @Schema(description = "최대 실행 허용 시간(초)") private Integer maxRunningSeconds; + @Schema(description = "마지막 실행 시작 시각") private LocalDateTime lastRunDate; + @Schema(description = "마지막 실행 종료 시각") private LocalDateTime lastEndDate; + @Schema(description = "마지막 성공 시각") private LocalDateTime lastSuccessDate; + @Schema(description = "마지막 상태") private Short lastStatus; + @Schema(description = "마지막 메시지") private String lastMessage; + @Schema(description = "실행 락 시각") private LocalDateTime runLockDate; + @Schema(description = "실행 락 소유자") private String runLockOwner; + @Schema(description = "사용 여부") private String useYn; + @Schema(description = "등록일시") private LocalDateTime createDate; + @Schema(description = "수정일시") private LocalDateTime updateDate; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleListVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleListVo.java new file mode 100644 index 0000000..f45862e --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleListVo.java @@ -0,0 +1,14 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import java.util.List; + +@Schema(description = "스케줄 작업 목록 조회 응답") +@Getter +@Setter +public class ScheduleListVo extends PagingResponse { + @Schema(description = "스케줄 작업 목록") private List scheduleList; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleLogListItemVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleLogListItemVo.java new file mode 100644 index 0000000..350ce90 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleLogListItemVo.java @@ -0,0 +1,26 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import java.time.LocalDateTime; + +@Schema(description = "스케줄 작업 실행 로그 목록 항목") +@Getter +@Setter +public class ScheduleLogListItemVo { + @Schema(description = "실행 로그 PK") private Long scheduleJobLogIdx; + @Schema(description = "스케줄 작업 PK") private Long scheduleJobIdx; + @Schema(description = "실행 식별자") private String runId; + @Schema(description = "작업 코드") private String jobName; + @Schema(description = "실행 시작 시각") private LocalDateTime startDate; + @Schema(description = "실행 종료 시각") private LocalDateTime endDate; + @Schema(description = "대상 건수") private Integer targetCount; + @Schema(description = "성공 건수") private Integer successCount; + @Schema(description = "실패 건수") private Integer failCount; + @Schema(description = "요약 메시지") private String message; + @Schema(description = "오류 메시지") private String errorMessage; + @Schema(description = "실행 서버") private String lockOwner; + @Schema(description = "실행 상태") private Short status; + @Schema(description = "기록 시각") private LocalDateTime createDate; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleLogListVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleLogListVo.java new file mode 100644 index 0000000..d88f3eb --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleLogListVo.java @@ -0,0 +1,14 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import java.util.List; + +@Schema(description = "스케줄 작업 실행 로그 목록 조회 응답") +@Getter +@Setter +public class ScheduleLogListVo extends PagingResponse { + @Schema(description = "스케줄 작업 실행 로그 목록") private List scheduleLogList; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleModifyVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleModifyVo.java new file mode 100644 index 0000000..0459ba1 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleModifyVo.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "스케줄 작업 수정 응답") +@Getter +@Setter +public class ScheduleModifyVo { + @Schema(description = "수정 성공 여부", example = "true") private boolean updated; + @Schema(description = "스케줄 작업 PK", example = "9") private Long scheduleJobIdx; + @JsonIgnore @Schema(hidden = true) private int resultCode; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleViewVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleViewVo.java new file mode 100644 index 0000000..b7b40e6 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/ScheduleViewVo.java @@ -0,0 +1,31 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import java.time.LocalDateTime; + +@Schema(description = "스케줄 작업 상세 조회 응답") +@Getter +@Setter +public class ScheduleViewVo { + @Schema(description = "스케줄 작업 PK") private Long scheduleJobIdx; + @Schema(description = "작업 코드") private String jobName; + @Schema(description = "작업 그룹") private String jobGroup; + @Schema(description = "작업 설명") private String jobDescription; + @Schema(description = "크론식") private String cronExpr; + @Schema(description = "타임존") private String timezoneId; + @Schema(description = "최대 실행 허용 시간(초)") private Integer maxRunningSeconds; + @Schema(description = "마지막 실행 시작 시각") private LocalDateTime lastRunDate; + @Schema(description = "마지막 실행 종료 시각") private LocalDateTime lastEndDate; + @Schema(description = "마지막 성공 시각") private LocalDateTime lastSuccessDate; + @Schema(description = "마지막 상태") private Short lastStatus; + @Schema(description = "마지막 메시지") private String lastMessage; + @Schema(description = "실행 락 시각") private LocalDateTime runLockDate; + @Schema(description = "실행 락 소유자") private String runLockOwner; + @Schema(description = "사용 여부") private String useYn; + @Schema(description = "등록일시") private LocalDateTime createDate; + @Schema(description = "등록자 회원 PK") private Integer createMember; + @Schema(description = "수정일시") private LocalDateTime updateDate; + @Schema(description = "마지막 수정자 회원 PK") private Integer updateMember; +} diff --git a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoAddVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoAddVo.java similarity index 57% rename from src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoAddVo.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoAddVo.java index cba0e29..3650472 100644 --- a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoAddVo.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoAddVo.java @@ -1,20 +1,18 @@ -package com.alist.api.modules.admin.sso.vo; +package com.alist.api.admin.modules.backOffice.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.Setter; -@Schema(description = "관리자 SSO 클라이언트 등록 응답") +@Schema(description = "SSO 클라이언트 등록 응답") @Getter @Setter -public class AdminSsoAddVo { - @Schema(description = "SSO 클라이언트 등록 여부", example = "true") - private boolean processed; - +public class SsoAddVo { + @Schema(description = "등록 성공 여부", example = "true") + private boolean added; @Schema(description = "등록된 SSO 클라이언트 PK", example = "19") private Integer ssoClientIdx; - @JsonIgnore @Schema(hidden = true) private int resultCode; diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoDeleteVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoDeleteVo.java new file mode 100644 index 0000000..bddc4d8 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoDeleteVo.java @@ -0,0 +1,19 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "SSO 클라이언트 삭제 응답") +@Getter +@Setter +public class SsoDeleteVo { + @Schema(description = "삭제 성공 여부", example = "true") + private boolean deleted; + @Schema(description = "SSO 클라이언트 PK", example = "19") + private Integer ssoClientIdx; + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} diff --git a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoListItemVo.java similarity index 53% rename from src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoVo.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoListItemVo.java index a01b4b7..77b893d 100644 --- a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoVo.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoListItemVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.admin.sso.vo; +package com.alist.api.admin.modules.backOffice.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -6,31 +6,24 @@ import lombok.Setter; import java.time.LocalDateTime; -@Schema(description = "관리자 SSO 클라이언트 목록 항목") +@Schema(description = "SSO 클라이언트 목록 항목") @Getter @Setter -public class AdminSsoVo { +public class SsoListItemVo { @Schema(description = "SSO 클라이언트 PK", example = "19") private Integer ssoClientIdx; - @Schema(description = "클라이언트 식별자", example = "a-campus-client") private String clientId; - - @Schema(description = "클라이언트명", example = "https://a-campus.co.kr/") + @Schema(description = "클라이언트명", example = "a-campus.co.kr") private String clientName; - - @Schema(description = "허용 redirect URI", example = "https://a-campus.co.kr/") + @Schema(description = "허용 Redirect URI", example = "https://a-campus.co.kr/auth/callback") private String redirectUri; - @Schema(description = "비고", example = "운영") private String memo; - - @Schema(description = "사용 여부", example = "Y") + @Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y") private String useYn; - - @Schema(description = "생성일", example = "2026-04-06 17:28:56") + @Schema(description = "등록일시") private LocalDateTime createDate; - - @Schema(description = "수정일", example = "2026-04-06 17:28:56") + @Schema(description = "수정일시") private LocalDateTime updateDate; } diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoListVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoListVo.java new file mode 100644 index 0000000..1ee0490 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoListVo.java @@ -0,0 +1,16 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "SSO 클라이언트 목록 조회 응답") +@Getter +@Setter +public class SsoListVo extends PagingResponse { + @Schema(description = "SSO 클라이언트 목록") + private List ssoList; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoModifyVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoModifyVo.java new file mode 100644 index 0000000..2ebb03c --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoModifyVo.java @@ -0,0 +1,19 @@ +package com.alist.api.admin.modules.backOffice.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "SSO 클라이언트 수정 응답") +@Getter +@Setter +public class SsoModifyVo { + @Schema(description = "수정 성공 여부", example = "true") + private boolean updated; + @Schema(description = "SSO 클라이언트 PK", example = "19") + private Integer ssoClientIdx; + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} diff --git a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoViewVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoViewVo.java similarity index 52% rename from src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoViewVo.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoViewVo.java index c014622..14693bb 100644 --- a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoViewVo.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/backOffice/vo/SsoViewVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.admin.sso.vo; +package com.alist.api.admin.modules.backOffice.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -6,40 +6,28 @@ import lombok.Setter; import java.time.LocalDateTime; -@Schema(description = "관리자 SSO 클라이언트 상세 조회 응답") +@Schema(description = "SSO 클라이언트 상세 조회 응답") @Getter @Setter -public class AdminSsoViewVo { +public class SsoViewVo { @Schema(description = "SSO 클라이언트 PK", example = "19") private Integer ssoClientIdx; - @Schema(description = "클라이언트 식별자", example = "a-campus-client") private String clientId; - @Schema(description = "클라이언트명", example = "a-campus.co.kr") private String clientName; - - @Schema(description = "허용 redirect URI", example = "https://a-campus.co.kr/") + @Schema(description = "허용 Redirect URI", example = "https://a-campus.co.kr/auth/callback") private String redirectUri; - @Schema(description = "비고", example = "운영") private String memo; - - @Schema(description = "사용 여부", example = "Y") + @Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y") private String useYn; - - @Schema(description = "삭제 여부", example = "N") - private String delYn; - - @Schema(description = "등록일시", example = "2026-04-06 17:28:56") + @Schema(description = "등록일시") private LocalDateTime createDate; - - @Schema(description = "등록자", example = "1") + @Schema(description = "등록자 회원 PK", example = "1") private Integer createMember; - - @Schema(description = "수정일시", example = "2026-04-06 17:28:56") + @Schema(description = "수정일시") private LocalDateTime updateDate; - - @Schema(description = "마지막 수정자", example = "1") + @Schema(description = "마지막 수정자 회원 PK", example = "1") private Integer updateMember; } diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/file/FileController.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/file/FileController.java new file mode 100644 index 0000000..0f5cf4b --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/file/FileController.java @@ -0,0 +1,78 @@ +package com.alist.api.admin.modules.file; + +import com.alist.api.admin.modules.file.service.FileService; +import com.alist.api.admin.modules.file.form.SunEditorUploadForm; +import com.alist.api.admin.modules.file.vo.FileUploadVo; +import com.alist.api.admin.modules.file.vo.SunEditorUploadVo; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +@Tag(name = "D-1. 파일 업로드", description = "공통 파일 저장, 보기, 다운로드 API") +@RestController +@RequestMapping("/file") +public class FileController { + private final FileService fileService; + + public FileController(FileService fileService) { + this.fileService = fileService; + } + + @Operation(summary = "단순 파일 업로드", description = "DB 기록 없이 파일을 저장하고 업로드 경로를 반환합니다.") + @PostMapping( + value = "/upload" + , consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ) + public ResponseEntity> fileUpload( + @Parameter(description = "저장 폴더 키", example = "notice", required = true) @RequestParam String folder + , @Parameter(description = "업로드 파일", required = true) @RequestPart("file") MultipartFile multipartFile + ) { + FileUploadVo fileUploadVo = fileService.insertFileUpload(folder, multipartFile); + + return ApiResponse.entity(fileUploadVo, ApiResponseCode.CODE_200, "파일 업로드"); + } + + @Operation( + summary = "SunEditor 이미지 업로드" + , description = "여러 이미지를 저장하고 SunEditor result 배열 형식으로 공개 파일 URL을 반환합니다." + , requestBody = @RequestBody(content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE, schema = @Schema(implementation = SunEditorUploadForm.class))) + ) + @PostMapping( + value = "/suneditor/upload" + , consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ) + public ResponseEntity fileSunEditorUpload( + @Parameter(description = "저장 폴더 키", example = "editor") @RequestParam(value = "folder", defaultValue = "editor") String folder + , MultipartHttpServletRequest multipartHttpServletRequest + ) { + return ResponseEntity.ok(fileService.insertFileSunEditorUpload(folder, multipartHttpServletRequest)); + } + + @Operation(summary = "파일 보기", description = "uploadPath 기준으로 파일을 inline 응답합니다.") + @GetMapping("/view") + public ResponseEntity fileView(@Parameter(description = "업로드 파일 경로", example = "/uploads/notice/2026/07/20/a1b2c3d4.png", required = true) @RequestParam String path) { + return fileService.selectFileView(path); + } + + @Operation(summary = "파일 다운로드", description = "uploadPath 기준으로 파일을 attachment 응답합니다.") + @GetMapping("/download") + public ResponseEntity fileDownload(@Parameter(description = "업로드 파일 경로", example = "/uploads/notice/2026/07/20/a1b2c3d4.png", required = true) @RequestParam String path) { + return fileService.selectFileDownload(path); + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/file/form/SunEditorUploadForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/file/form/SunEditorUploadForm.java new file mode 100644 index 0000000..548bac9 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/file/form/SunEditorUploadForm.java @@ -0,0 +1,17 @@ +package com.alist.api.admin.modules.file.form; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@Schema(description = "SunEditor 이미지 업로드 요청") +@Getter +@Setter +public class SunEditorUploadForm { + @ArraySchema(schema = @Schema(type = "string", format = "binary", description = "업로드 이미지 파일")) + private List files; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/file/service/FileService.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/file/service/FileService.java new file mode 100644 index 0000000..33caed8 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/file/service/FileService.java @@ -0,0 +1,58 @@ +package com.alist.api.admin.modules.file.service; + +import com.alist.api.admin.modules.file.vo.FileUploadVo; +import com.alist.api.admin.modules.file.vo.SunEditorUploadVo; +import com.alist.api.core.common.file.service.FileStorageService; +import com.alist.api.core.common.file.vo.FileStorageUploadVo; +import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class FileService { + private final FileStorageService fileStorageService; + + public FileService(FileStorageService fileStorageService) { + this.fileStorageService = fileStorageService; + } + + public FileUploadVo insertFileUpload(String folder, MultipartFile multipartFile) { + FileStorageUploadVo fileStorageUploadVo = fileStorageService.saveFileUpload(folder, multipartFile); + FileUploadVo fileUploadVo = new FileUploadVo(); + fileUploadVo.setUploadPath(fileStorageUploadVo.getUploadPath()); + fileUploadVo.setOriginalFileName(fileStorageUploadVo.getOriginalFileName()); + fileUploadVo.setStoredFileName(fileStorageUploadVo.getStoredFileName()); + fileUploadVo.setFileExtension(fileStorageUploadVo.getFileExtension()); + fileUploadVo.setContentType(fileStorageUploadVo.getContentType()); + fileUploadVo.setFileSize(fileStorageUploadVo.getFileSize()); + fileUploadVo.setWidth(fileStorageUploadVo.getWidth()); + fileUploadVo.setHeight(fileStorageUploadVo.getHeight()); + + return fileUploadVo; + } + + public SunEditorUploadVo insertFileSunEditorUpload(String folder, MultipartHttpServletRequest multipartHttpServletRequest) { + List sunEditorUploadItemList = new ArrayList<>(); + + for (MultipartFile multipartFile : multipartHttpServletRequest.getFileMap().values()) { + FileStorageUploadVo fileStorageUploadVo = fileStorageService.saveFileUpload(folder, multipartFile); + String fileViewUrl = fileStorageService.buildFileViewUrl(fileStorageUploadVo.getUploadPath()); + sunEditorUploadItemList.add(new SunEditorUploadVo.Item(fileViewUrl, fileStorageUploadVo.getOriginalFileName(), fileStorageUploadVo.getFileSize())); + } + + return new SunEditorUploadVo(sunEditorUploadItemList); + } + + public ResponseEntity selectFileView(String path) { + return fileStorageService.loadFileResource(path, true); + } + + public ResponseEntity selectFileDownload(String path) { + return fileStorageService.loadFileResource(path, false); + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/file/vo/FileUploadVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/file/vo/FileUploadVo.java new file mode 100644 index 0000000..7a2ed47 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/file/vo/FileUploadVo.java @@ -0,0 +1,34 @@ +package com.alist.api.admin.modules.file.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "파일 업로드 응답") +@Getter +@Setter +public class FileUploadVo { + @Schema(description = "업로드 파일 접근 경로", example = "/uploads/notice/2026/07/20/a1b2c3d4.png") + private String uploadPath; + + @Schema(description = "원본 파일명", example = "sample.png") + private String originalFileName; + + @Schema(description = "저장 파일명", example = "a1b2c3d4.png") + private String storedFileName; + + @Schema(description = "파일 확장자", example = "png") + private String fileExtension; + + @Schema(description = "Content-Type", example = "image/png") + private String contentType; + + @Schema(description = "파일 크기(byte)", example = "102400") + private Long fileSize; + + @Schema(description = "이미지 너비(px)", example = "800") + private Integer width; + + @Schema(description = "이미지 높이(px)", example = "600") + private Integer height; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/file/vo/SunEditorUploadVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/file/vo/SunEditorUploadVo.java new file mode 100644 index 0000000..29fc8b5 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/file/vo/SunEditorUploadVo.java @@ -0,0 +1,39 @@ +package com.alist.api.admin.modules.file.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "SunEditor 이미지 업로드 응답") +@Getter +@Setter +public class SunEditorUploadVo { + @Schema(description = "업로드 결과 목록") + private List result; + + public SunEditorUploadVo(List result) { + this.result = result; + } + + @Schema(description = "SunEditor 업로드 파일 항목") + @Getter + @Setter + public static class Item { + @Schema(description = "업로드 파일 URL", example = "https://file-alist.pjt.kr/uploads/editor/2026/07/20/a1b2c3d4.png") + private String url; + + @Schema(description = "원본 파일명", example = "sample.png") + private String name; + + @Schema(description = "파일 크기(byte)", example = "102400") + private Long size; + + public Item(String url, String name, Long size) { + this.url = url; + this.name = name; + this.size = size; + } + } +} diff --git a/src/main/java/com/alist/api/modules/main/MainController.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/main/MainController.java similarity index 87% rename from src/main/java/com/alist/api/modules/main/MainController.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/main/MainController.java index 30b0c45..eec48f6 100644 --- a/src/main/java/com/alist/api/modules/main/MainController.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/main/MainController.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.main; +package com.alist.api.admin.modules.main; import io.swagger.v3.oas.annotations.Hidden; import org.springframework.stereotype.Controller; diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/NoticeController.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/NoticeController.java new file mode 100644 index 0000000..c160863 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/NoticeController.java @@ -0,0 +1,114 @@ +package com.alist.api.admin.modules.support; + +import com.alist.api.admin.modules.support.form.NoticeAddForm; +import com.alist.api.admin.modules.support.form.NoticeDeleteForm; +import com.alist.api.admin.modules.support.form.NoticeListForm; +import com.alist.api.admin.modules.support.form.NoticeModifyForm; +import com.alist.api.admin.modules.support.form.NoticeViewForm; +import com.alist.api.admin.modules.support.service.NoticeService; +import com.alist.api.admin.modules.support.vo.NoticeAddVo; +import com.alist.api.admin.modules.support.vo.NoticeDeleteVo; +import com.alist.api.admin.modules.support.vo.NoticeListVo; +import com.alist.api.admin.modules.support.vo.NoticeModifyVo; +import com.alist.api.admin.modules.support.vo.NoticeViewVo; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "C-1. 공지사항 관리", description = "지원 기능 공지사항 관리 API") +@RestController +@RequestMapping("/support/notice") +public class NoticeController { + private final NoticeService noticeService; + + public NoticeController(NoticeService noticeService) { + this.noticeService = noticeService; + } + + @Operation(summary = "공지사항 목록 조회", description = "검색 조건과 페이징으로 공지사항 목록을 조회합니다.") + @GetMapping("/list") + public ResponseEntity> noticeList(@Valid @ModelAttribute NoticeListForm noticeListForm) { + NoticeListVo noticeListVo = noticeService.selectNoticeList(noticeListForm); + + if (noticeListVo.getNoticeList().isEmpty()) { + return ApiResponse.entity(noticeListVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(noticeListVo, ApiResponseCode.CODE_2001, "공지사항"); + } + + @Operation(summary = "공지사항 상세 조회", description = "공지사항 PK 기준으로 상세 정보와 대상 캠퍼스, 첨부파일을 조회합니다.") + @GetMapping("/view") + public ResponseEntity> noticeView(@Valid @ModelAttribute NoticeViewForm noticeViewForm) { + NoticeViewVo noticeViewVo = noticeService.selectNoticeView(noticeViewForm); + + if (noticeViewVo == null) { + return ApiResponse.entity((NoticeViewVo) null, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(noticeViewVo, ApiResponseCode.CODE_2001, "공지사항"); + } + + @Operation(summary = "공지사항 등록", description = "공지사항과 대상 캠퍼스, 첨부파일을 등록합니다.") + @PostMapping("/add") + public ResponseEntity> noticeAdd(@Valid @RequestBody NoticeAddForm noticeAddForm) { + NoticeAddVo noticeAddVo = noticeService.insertNoticeAdd(noticeAddForm); + + if (noticeAddVo.getResultCode() == 401) { + return ApiResponse.entity(noticeAddVo, ApiResponseCode.CODE_401); + } + + if (noticeAddVo.getResultCode() == 4001) { + return ApiResponse.entity(noticeAddVo, ApiResponseCode.CODE_4001); + } + + return ApiResponse.entity(noticeAddVo, ApiResponseCode.CODE_2002, "공지사항"); + } + + @Operation(summary = "공지사항 수정", description = "공지사항 기본 정보와 대상 캠퍼스, 첨부파일을 수정합니다.") + @PutMapping("/modify") + public ResponseEntity> noticeModify(@Valid @RequestBody NoticeModifyForm noticeModifyForm) { + NoticeModifyVo noticeModifyVo = noticeService.updateNoticeModify(noticeModifyForm); + + if (noticeModifyVo.getResultCode() == 401) { + return ApiResponse.entity(noticeModifyVo, ApiResponseCode.CODE_401); + } + + if (noticeModifyVo.getResultCode() == 4001) { + return ApiResponse.entity(noticeModifyVo, ApiResponseCode.CODE_4001); + } + + if (!noticeModifyVo.isUpdated()) { + return ApiResponse.entity(noticeModifyVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(noticeModifyVo, ApiResponseCode.CODE_2005, "공지사항"); + } + + @Operation(summary = "공지사항 삭제", description = "공지사항과 등록된 첨부파일을 논리 삭제 처리합니다.") + @PatchMapping("/delete") + public ResponseEntity> noticeDelete(@Valid @RequestBody NoticeDeleteForm noticeDeleteForm) { + NoticeDeleteVo noticeDeleteVo = noticeService.updateNoticeDelete(noticeDeleteForm); + + if (noticeDeleteVo.getResultCode() == 401) { + return ApiResponse.entity(noticeDeleteVo, ApiResponseCode.CODE_401); + } + + if (!noticeDeleteVo.isDeleted()) { + return ApiResponse.entity(noticeDeleteVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(noticeDeleteVo, ApiResponseCode.CODE_2005, "공지사항"); + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeAddForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeAddForm.java new file mode 100644 index 0000000..c4de025 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeAddForm.java @@ -0,0 +1,56 @@ +package com.alist.api.admin.modules.support.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "공지사항 등록 요청") +@Getter +@Setter +public class NoticeAddForm { + @NotBlank + @Size(max = 30) + @Schema(description = "공지 유형. GENERAL, SYSTEM, EVENT", example = "GENERAL", requiredMode = Schema.RequiredMode.REQUIRED) + private String noticeType; + + @NotBlank + @Size(max = 30) + @Schema(description = "노출 범위. ALL, ADMIN, TEACHER, STUDENT, CAMPUS", example = "CAMPUS", requiredMode = Schema.RequiredMode.REQUIRED) + private String targetScope; + + @Schema(description = "대상 캠퍼스 PK 목록. targetScope가 CAMPUS일 때 필수", example = "[1, 2]") + private List campusIdxList; + + @NotBlank + @Size(max = 200) + @Schema(description = "제목", example = "서비스 점검 안내", requiredMode = Schema.RequiredMode.REQUIRED) + private String title; + + @NotBlank + @Schema(description = "내용", example = "서비스 점검이 예정되어 있습니다.", requiredMode = Schema.RequiredMode.REQUIRED) + private String content; + + @Pattern(regexp = "^(Y|N)$") + @Schema(description = "상단 고정 여부. 미입력 시 N", example = "N") + private String pinYn; + + @Schema(description = "노출 시작일", example = "2026-07-01T00:00:00") + private String startDate; + + @Schema(description = "노출 종료일", example = "2026-07-31T23:59:59") + private String endDate; + + @Pattern(regexp = "^(Y|N)$") + @Schema(description = "사용 여부. 미입력 시 Y", example = "Y") + private String useYn; + + @Valid + @Schema(description = "첨부파일 목록") + private List fileList; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeDeleteForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeDeleteForm.java new file mode 100644 index 0000000..aafa722 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeDeleteForm.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.support.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "공지사항 삭제 요청") +@Getter +@Setter +public class NoticeDeleteForm { + @NotNull + @Schema(description = "공지사항 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED) + private Long noticeIdx; +} diff --git a/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeFileForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeFileForm.java similarity index 54% rename from src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeFileForm.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeFileForm.java index 11ff224..7d01699 100644 --- a/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeFileForm.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeFileForm.java @@ -1,6 +1,5 @@ -package com.alist.api.modules.admin.notice.form; +package com.alist.api.admin.modules.support.form; -import com.alist.api.modules.admin.notice.dto.AdminNoticeFileDto; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; @@ -8,23 +7,23 @@ import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; -@Schema(description = "관리자 공지사항 첨부파일 요청") +@Schema(description = "공지사항 첨부파일 요청") @Getter @Setter -public class AdminNoticeFileForm { - @NotBlank +public class NoticeFileForm { + @NotBlank(message = "원본 파일명을 입력해주세요.") @Size(max = 255) @Schema(description = "원본 파일명", example = "notice.pdf", requiredMode = Schema.RequiredMode.REQUIRED) private String fileOriginalName; - @NotBlank + @NotBlank(message = "저장 파일명을 입력해주세요.") @Size(max = 255) @Schema(description = "저장 파일명", example = "20260701120000_notice.pdf", requiredMode = Schema.RequiredMode.REQUIRED) private String fileSaveName; - @NotBlank + @NotBlank(message = "파일 경로를 입력해주세요.") @Size(max = 500) - @Schema(description = "파일 경로", example = "/uploads/notice/2026/07/01/20260701120000_notice.pdf", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "파일 경로", example = "/uploads/notice/2026/07/notice.pdf", requiredMode = Schema.RequiredMode.REQUIRED) private String filePath; @Min(0) @@ -38,15 +37,4 @@ public class AdminNoticeFileForm { @Min(0) @Schema(description = "정렬 순서. 미입력 시 0", example = "0") private Integer sortOrder; - - public AdminNoticeFileDto toDto() { - AdminNoticeFileDto adminNoticeFileDto = new AdminNoticeFileDto(); - adminNoticeFileDto.setFileOriginalName(fileOriginalName.trim()); - adminNoticeFileDto.setFileSaveName(fileSaveName.trim()); - adminNoticeFileDto.setFilePath(filePath.trim()); - adminNoticeFileDto.setFileSize(fileSize); - adminNoticeFileDto.setFileExt(fileExt == null ? null : fileExt.trim()); - adminNoticeFileDto.setSortOrder(sortOrder == null ? 0 : sortOrder); - return adminNoticeFileDto; - } } diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeListForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeListForm.java new file mode 100644 index 0000000..dd052dd --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeListForm.java @@ -0,0 +1,29 @@ +package com.alist.api.admin.modules.support.form; + +import com.alist.api.core.common.paging.PagingRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "공지사항 목록 검색 조건") +@Getter +@Setter +public class NoticeListForm extends PagingRequest { + @Schema(description = "검색어. 제목과 내용 검색", example = "점검") + private String keyword; + + @Schema(description = "공지 유형. GENERAL, SYSTEM, EVENT", example = "GENERAL") + private String noticeType; + + @Schema(description = "노출 범위. ALL, ADMIN, TEACHER, STUDENT, CAMPUS", example = "CAMPUS") + private String targetScope; + + @Schema(description = "캠퍼스 PK", example = "1") + private Long campusIdx; + + @Schema(description = "상단 고정 여부. Y: 고정, N: 미고정", example = "Y") + private String pinYn; + + @Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y") + private String useYn; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeModifyForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeModifyForm.java new file mode 100644 index 0000000..0af17d8 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeModifyForm.java @@ -0,0 +1,20 @@ +package com.alist.api.admin.modules.support.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "공지사항 수정 요청") +@Getter +@Setter +public class NoticeModifyForm extends NoticeAddForm { + @NotNull + @Schema(description = "공지사항 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED) + private Long noticeIdx; + + @Schema(description = "삭제할 기존 첨부파일 PK 목록", example = "[1, 2]") + private List deleteFileIdxList; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeViewForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeViewForm.java new file mode 100644 index 0000000..5d9cdd8 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/form/NoticeViewForm.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.support.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "공지사항 상세 조회 요청") +@Getter +@Setter +public class NoticeViewForm { + @NotNull(message = "공지사항 PK를 입력해주세요.") + @Schema(description = "공지사항 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED) + private Long noticeIdx; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/service/NoticeService.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/service/NoticeService.java new file mode 100644 index 0000000..7e90a1e --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/service/NoticeService.java @@ -0,0 +1,373 @@ +package com.alist.api.admin.modules.support.service; + +import com.alist.api.admin.modules.support.form.NoticeAddForm; +import com.alist.api.admin.modules.support.form.NoticeDeleteForm; +import com.alist.api.admin.modules.support.form.NoticeFileForm; +import com.alist.api.admin.modules.support.form.NoticeListForm; +import com.alist.api.admin.modules.support.form.NoticeModifyForm; +import com.alist.api.admin.modules.support.form.NoticeViewForm; +import com.alist.api.admin.modules.support.vo.NoticeAddVo; +import com.alist.api.admin.modules.support.vo.NoticeCampusVo; +import com.alist.api.admin.modules.support.vo.NoticeDeleteVo; +import com.alist.api.admin.modules.support.vo.NoticeFileVo; +import com.alist.api.admin.modules.support.vo.NoticeListItemVo; +import com.alist.api.admin.modules.support.vo.NoticeListVo; +import com.alist.api.admin.modules.support.vo.NoticeModifyVo; +import com.alist.api.admin.modules.support.vo.NoticeViewVo; +import com.alist.api.core.common.utils.SecurityUtil; +import com.alist.api.core.modules.bespoke.notice.dto.NoticeNoticeListDto; +import com.alist.api.core.modules.bespoke.notice.dto.NoticeNoticeViewDto; +import com.alist.api.core.modules.bespoke.notice.service.NoticeNoticeService; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeCampusListItemVo; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeFileListItemVo; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeListItemVo; +import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeViewVo; +import com.alist.api.core.modules.standard.notice.dto.NoticeAddDto; +import com.alist.api.core.modules.standard.notice.dto.NoticeDeleteDto; +import com.alist.api.core.modules.standard.notice.dto.NoticeModifyDto; +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusAddDto; +import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusDeleteDto; +import com.alist.api.core.modules.standard.noticeCampus.service.NoticeCampusService; +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileAddDto; +import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileDeleteDto; +import com.alist.api.core.modules.standard.noticeFile.service.NoticeFileService; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenViewDto; +import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService; +import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenViewVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Service("supportNoticeService") +public class NoticeService { + private final com.alist.api.core.modules.standard.notice.service.NoticeService noticeStandardService; + private final NoticeCampusService noticeCampusService; + private final NoticeFileService noticeFileService; + private final NoticeNoticeService noticeNoticeService; + private final TestUserTokenService testUserTokenService; + + public NoticeService(com.alist.api.core.modules.standard.notice.service.NoticeService noticeStandardService, NoticeCampusService noticeCampusService, NoticeFileService noticeFileService, NoticeNoticeService noticeNoticeService, TestUserTokenService testUserTokenService) { + this.noticeStandardService = noticeStandardService; + this.noticeCampusService = noticeCampusService; + this.noticeFileService = noticeFileService; + this.noticeNoticeService = noticeNoticeService; + this.testUserTokenService = testUserTokenService; + } + + @Transactional(readOnly = true) + public NoticeListVo selectNoticeList(NoticeListForm noticeListForm) { + NoticeNoticeListDto noticeNoticeListDto = new NoticeNoticeListDto(); + noticeNoticeListDto.setPage(noticeListForm.getPage()); + noticeNoticeListDto.setSize(noticeListForm.getSize()); + noticeNoticeListDto.setKeyword(noticeListForm.getKeyword() == null || noticeListForm.getKeyword().isBlank() ? null : noticeListForm.getKeyword().trim()); + noticeNoticeListDto.setNoticeType(noticeListForm.getNoticeType() == null || noticeListForm.getNoticeType().isBlank() ? null : noticeListForm.getNoticeType().trim()); + noticeNoticeListDto.setTargetScope(noticeListForm.getTargetScope() == null || noticeListForm.getTargetScope().isBlank() ? null : noticeListForm.getTargetScope().trim()); + noticeNoticeListDto.setCampusIdx(noticeListForm.getCampusIdx()); + noticeNoticeListDto.setPinYn(noticeListForm.getPinYn() == null || noticeListForm.getPinYn().isBlank() ? null : noticeListForm.getPinYn().trim()); + noticeNoticeListDto.setUseYn(noticeListForm.getUseYn() == null || noticeListForm.getUseYn().isBlank() ? null : noticeListForm.getUseYn().trim()); + + int totalCount = noticeNoticeService.noticeNoticeListCount(noticeNoticeListDto); + List noticeNoticeList = noticeNoticeService.noticeNoticeList(noticeNoticeListDto); + List noticeList = new ArrayList<>(); + + for (NoticeNoticeListItemVo noticeNoticeListItemVo : noticeNoticeList) { + NoticeListItemVo noticeListItemVo = new NoticeListItemVo(); + noticeListItemVo.setNoticeIdx(noticeNoticeListItemVo.getNoticeIdx()); + noticeListItemVo.setNoticeType(noticeNoticeListItemVo.getNoticeType()); + noticeListItemVo.setTargetScope(noticeNoticeListItemVo.getTargetScope()); + noticeListItemVo.setTitle(noticeNoticeListItemVo.getTitle()); + noticeListItemVo.setPinYn(noticeNoticeListItemVo.getPinYn()); + noticeListItemVo.setStartDate(noticeNoticeListItemVo.getStartDate()); + noticeListItemVo.setEndDate(noticeNoticeListItemVo.getEndDate()); + noticeListItemVo.setViewCount(noticeNoticeListItemVo.getViewCount()); + noticeListItemVo.setUseYn(noticeNoticeListItemVo.getUseYn()); + noticeListItemVo.setCreateDate(noticeNoticeListItemVo.getCreateDate()); + noticeListItemVo.setUpdateDate(noticeNoticeListItemVo.getUpdateDate()); + noticeList.add(noticeListItemVo); + } + + NoticeListVo noticeListVo = new NoticeListVo(); + noticeListVo.setNoticeList(noticeList); + noticeListVo.setPaging(noticeNoticeListDto, totalCount); + return noticeListVo; + } + + @Transactional(readOnly = true) + public NoticeViewVo selectNoticeView(NoticeViewForm noticeViewForm) { + NoticeNoticeViewDto noticeNoticeViewDto = new NoticeNoticeViewDto(); + noticeNoticeViewDto.setNoticeIdx(noticeViewForm.getNoticeIdx()); + + NoticeNoticeViewVo noticeNoticeViewVo = noticeNoticeService.noticeNoticeView(noticeNoticeViewDto); + + if (noticeNoticeViewVo == null) { + return null; + } + + NoticeViewVo noticeViewVo = new NoticeViewVo(); + noticeViewVo.setNoticeIdx(noticeNoticeViewVo.getNoticeIdx()); + noticeViewVo.setNoticeType(noticeNoticeViewVo.getNoticeType()); + noticeViewVo.setTargetScope(noticeNoticeViewVo.getTargetScope()); + noticeViewVo.setTitle(noticeNoticeViewVo.getTitle()); + noticeViewVo.setContent(noticeNoticeViewVo.getContent()); + noticeViewVo.setPinYn(noticeNoticeViewVo.getPinYn()); + noticeViewVo.setStartDate(noticeNoticeViewVo.getStartDate()); + noticeViewVo.setEndDate(noticeNoticeViewVo.getEndDate()); + noticeViewVo.setViewCount(noticeNoticeViewVo.getViewCount()); + noticeViewVo.setUseYn(noticeNoticeViewVo.getUseYn()); + noticeViewVo.setCreateDate(noticeNoticeViewVo.getCreateDate()); + noticeViewVo.setCreateMember(noticeNoticeViewVo.getCreateMember()); + noticeViewVo.setUpdateDate(noticeNoticeViewVo.getUpdateDate()); + noticeViewVo.setUpdateMember(noticeNoticeViewVo.getUpdateMember()); + + List campusList = new ArrayList<>(); + List noticeNoticeCampusList = noticeNoticeService.noticeNoticeCampusList(noticeNoticeViewDto); + + for (NoticeNoticeCampusListItemVo noticeNoticeCampusListItemVo : noticeNoticeCampusList) { + NoticeCampusVo noticeCampusVo = new NoticeCampusVo(); + noticeCampusVo.setCampusIdx(noticeNoticeCampusListItemVo.getCampusIdx()); + campusList.add(noticeCampusVo); + } + + noticeViewVo.setCampusList(campusList); + + List fileList = new ArrayList<>(); + List noticeNoticeFileList = noticeNoticeService.noticeNoticeFileList(noticeNoticeViewDto); + + for (NoticeNoticeFileListItemVo noticeNoticeFileListItemVo : noticeNoticeFileList) { + NoticeFileVo noticeFileVo = new NoticeFileVo(); + noticeFileVo.setNoticeFileIdx(noticeNoticeFileListItemVo.getNoticeFileIdx()); + noticeFileVo.setFileOriginalName(noticeNoticeFileListItemVo.getFileOriginalName()); + noticeFileVo.setFileSaveName(noticeNoticeFileListItemVo.getFileSaveName()); + noticeFileVo.setFilePath(noticeNoticeFileListItemVo.getFilePath()); + noticeFileVo.setFileSize(noticeNoticeFileListItemVo.getFileSize()); + noticeFileVo.setFileExt(noticeNoticeFileListItemVo.getFileExt()); + noticeFileVo.setSortOrder(noticeNoticeFileListItemVo.getSortOrder()); + noticeFileVo.setCreateDate(noticeNoticeFileListItemVo.getCreateDate()); + fileList.add(noticeFileVo); + } + + noticeViewVo.setFileList(fileList); + return noticeViewVo; + } + + @Transactional + public NoticeAddVo insertNoticeAdd(NoticeAddForm noticeAddForm) { + NoticeAddVo noticeAddVo = new NoticeAddVo(); + + if ("CAMPUS".equals(noticeAddForm.getTargetScope()) && (noticeAddForm.getCampusIdxList() == null || noticeAddForm.getCampusIdxList().isEmpty())) { + noticeAddVo.setResultCode(4001); + return noticeAddVo; + } + + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + + if (userTokenIdx == null) { + noticeAddVo.setResultCode(401); + return noticeAddVo; + } + + TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto(); + testUserTokenViewDto.setUserTokenIdx(userTokenIdx); + TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto); + + if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) { + noticeAddVo.setResultCode(401); + return noticeAddVo; + } + + Long userIdx = Long.valueOf(testUserTokenViewVo.getUserIdx()); + NoticeAddDto noticeAddDto = new NoticeAddDto(); + noticeAddDto.setNoticeType(noticeAddForm.getNoticeType().trim()); + noticeAddDto.setTargetScope(noticeAddForm.getTargetScope().trim()); + noticeAddDto.setTitle(noticeAddForm.getTitle().trim()); + noticeAddDto.setContent(noticeAddForm.getContent().trim()); + noticeAddDto.setPinYn(noticeAddForm.getPinYn() == null || noticeAddForm.getPinYn().isBlank() ? "N" : noticeAddForm.getPinYn().trim()); + noticeAddDto.setStartDate(noticeAddForm.getStartDate() == null || noticeAddForm.getStartDate().isBlank() ? null : LocalDateTime.parse(noticeAddForm.getStartDate().trim())); + noticeAddDto.setEndDate(noticeAddForm.getEndDate() == null || noticeAddForm.getEndDate().isBlank() ? null : LocalDateTime.parse(noticeAddForm.getEndDate().trim())); + noticeAddDto.setUseYn(noticeAddForm.getUseYn() == null || noticeAddForm.getUseYn().isBlank() ? "Y" : noticeAddForm.getUseYn().trim()); + noticeAddDto.setCreateMember(userIdx); + noticeAddDto.setUpdateMember(userIdx); + + com.alist.api.core.modules.standard.notice.vo.NoticeAddVo noticeStandardAddVo = noticeStandardService.insertNoticeAdd(noticeAddDto); + noticeAddVo.setAdded(noticeStandardAddVo.isAdded()); + noticeAddVo.setNoticeIdx(noticeStandardAddVo.getNoticeIdx()); + noticeAddVo.setResultCode(noticeStandardAddVo.getResultCode()); + + if (!noticeAddVo.isAdded()) { + return noticeAddVo; + } + + if (noticeAddForm.getCampusIdxList() != null) { + for (Long campusIdx : noticeAddForm.getCampusIdxList()) { + NoticeCampusAddDto noticeCampusAddDto = new NoticeCampusAddDto(); + noticeCampusAddDto.setNoticeIdx(noticeAddVo.getNoticeIdx()); + noticeCampusAddDto.setCampusIdx(campusIdx); + noticeCampusAddDto.setCreateMember(userIdx); + noticeCampusService.insertNoticeCampusAdd(noticeCampusAddDto); + } + } + + if (noticeAddForm.getFileList() != null) { + for (NoticeFileForm noticeFileForm : noticeAddForm.getFileList()) { + NoticeFileAddDto noticeFileAddDto = new NoticeFileAddDto(); + noticeFileAddDto.setNoticeIdx(noticeAddVo.getNoticeIdx()); + noticeFileAddDto.setFileOriginalName(noticeFileForm.getFileOriginalName().trim()); + noticeFileAddDto.setFileSaveName(noticeFileForm.getFileSaveName().trim()); + noticeFileAddDto.setFilePath(noticeFileForm.getFilePath().trim()); + noticeFileAddDto.setFileSize(noticeFileForm.getFileSize()); + noticeFileAddDto.setFileExt(noticeFileForm.getFileExt() == null ? null : noticeFileForm.getFileExt().trim()); + noticeFileAddDto.setSortOrder(noticeFileForm.getSortOrder()); + noticeFileAddDto.setCreateMember(userIdx); + noticeFileAddDto.setUpdateMember(userIdx); + noticeFileService.insertNoticeFileAdd(noticeFileAddDto); + } + } + + return noticeAddVo; + } + + @Transactional + public NoticeModifyVo updateNoticeModify(NoticeModifyForm noticeModifyForm) { + NoticeModifyVo noticeModifyVo = new NoticeModifyVo(); + noticeModifyVo.setNoticeIdx(noticeModifyForm.getNoticeIdx()); + + if ("CAMPUS".equals(noticeModifyForm.getTargetScope()) && (noticeModifyForm.getCampusIdxList() == null || noticeModifyForm.getCampusIdxList().isEmpty())) { + noticeModifyVo.setResultCode(4001); + return noticeModifyVo; + } + + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + + if (userTokenIdx == null) { + noticeModifyVo.setResultCode(401); + return noticeModifyVo; + } + + TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto(); + testUserTokenViewDto.setUserTokenIdx(userTokenIdx); + TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto); + + if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) { + noticeModifyVo.setResultCode(401); + return noticeModifyVo; + } + + Long userIdx = Long.valueOf(testUserTokenViewVo.getUserIdx()); + NoticeModifyDto noticeModifyDto = new NoticeModifyDto(); + noticeModifyDto.setNoticeIdx(noticeModifyForm.getNoticeIdx()); + noticeModifyDto.setNoticeType(noticeModifyForm.getNoticeType().trim()); + noticeModifyDto.setTargetScope(noticeModifyForm.getTargetScope().trim()); + noticeModifyDto.setTitle(noticeModifyForm.getTitle().trim()); + noticeModifyDto.setContent(noticeModifyForm.getContent().trim()); + noticeModifyDto.setPinYn(noticeModifyForm.getPinYn() == null || noticeModifyForm.getPinYn().isBlank() ? "N" : noticeModifyForm.getPinYn().trim()); + noticeModifyDto.setStartDate(noticeModifyForm.getStartDate() == null || noticeModifyForm.getStartDate().isBlank() ? null : LocalDateTime.parse(noticeModifyForm.getStartDate().trim())); + noticeModifyDto.setEndDate(noticeModifyForm.getEndDate() == null || noticeModifyForm.getEndDate().isBlank() ? null : LocalDateTime.parse(noticeModifyForm.getEndDate().trim())); + noticeModifyDto.setUseYn(noticeModifyForm.getUseYn() == null || noticeModifyForm.getUseYn().isBlank() ? "Y" : noticeModifyForm.getUseYn().trim()); + noticeModifyDto.setUpdateMember(userIdx); + + com.alist.api.core.modules.standard.notice.vo.NoticeModifyVo noticeStandardModifyVo = noticeStandardService.updateNoticeModify(noticeModifyDto); + noticeModifyVo.setUpdated(noticeStandardModifyVo.isUpdated()); + noticeModifyVo.setResultCode(noticeStandardModifyVo.getResultCode()); + + if (!noticeModifyVo.isUpdated()) { + return noticeModifyVo; + } + + NoticeNoticeViewDto noticeNoticeViewDto = new NoticeNoticeViewDto(); + noticeNoticeViewDto.setNoticeIdx(noticeModifyForm.getNoticeIdx()); + List noticeNoticeCampusList = noticeNoticeService.noticeNoticeCampusList(noticeNoticeViewDto); + + for (NoticeNoticeCampusListItemVo noticeNoticeCampusListItemVo : noticeNoticeCampusList) { + NoticeCampusDeleteDto noticeCampusDeleteDto = new NoticeCampusDeleteDto(); + noticeCampusDeleteDto.setNoticeCampusIdx(noticeNoticeCampusListItemVo.getNoticeCampusIdx()); + noticeCampusService.deleteNoticeCampusDelete(noticeCampusDeleteDto); + } + + if (noticeModifyForm.getCampusIdxList() != null) { + for (Long campusIdx : noticeModifyForm.getCampusIdxList()) { + NoticeCampusAddDto noticeCampusAddDto = new NoticeCampusAddDto(); + noticeCampusAddDto.setNoticeIdx(noticeModifyForm.getNoticeIdx()); + noticeCampusAddDto.setCampusIdx(campusIdx); + noticeCampusAddDto.setCreateMember(userIdx); + noticeCampusService.insertNoticeCampusAdd(noticeCampusAddDto); + } + } + + if (noticeModifyForm.getDeleteFileIdxList() != null) { + for (Long noticeFileIdx : noticeModifyForm.getDeleteFileIdxList()) { + NoticeFileDeleteDto noticeFileDeleteDto = new NoticeFileDeleteDto(); + noticeFileDeleteDto.setNoticeFileIdx(noticeFileIdx); + noticeFileDeleteDto.setUpdateMember(userIdx); + noticeFileService.updateNoticeFileDelete(noticeFileDeleteDto); + } + } + + if (noticeModifyForm.getFileList() != null) { + for (NoticeFileForm noticeFileForm : noticeModifyForm.getFileList()) { + NoticeFileAddDto noticeFileAddDto = new NoticeFileAddDto(); + noticeFileAddDto.setNoticeIdx(noticeModifyForm.getNoticeIdx()); + noticeFileAddDto.setFileOriginalName(noticeFileForm.getFileOriginalName().trim()); + noticeFileAddDto.setFileSaveName(noticeFileForm.getFileSaveName().trim()); + noticeFileAddDto.setFilePath(noticeFileForm.getFilePath().trim()); + noticeFileAddDto.setFileSize(noticeFileForm.getFileSize()); + noticeFileAddDto.setFileExt(noticeFileForm.getFileExt() == null ? null : noticeFileForm.getFileExt().trim()); + noticeFileAddDto.setSortOrder(noticeFileForm.getSortOrder()); + noticeFileAddDto.setCreateMember(userIdx); + noticeFileAddDto.setUpdateMember(userIdx); + noticeFileService.insertNoticeFileAdd(noticeFileAddDto); + } + } + + return noticeModifyVo; + } + + @Transactional + public NoticeDeleteVo updateNoticeDelete(NoticeDeleteForm noticeDeleteForm) { + NoticeDeleteVo noticeDeleteVo = new NoticeDeleteVo(); + noticeDeleteVo.setNoticeIdx(noticeDeleteForm.getNoticeIdx()); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + + if (userTokenIdx == null) { + noticeDeleteVo.setResultCode(401); + return noticeDeleteVo; + } + + TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto(); + testUserTokenViewDto.setUserTokenIdx(userTokenIdx); + TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto); + + if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) { + noticeDeleteVo.setResultCode(401); + return noticeDeleteVo; + } + + Long userIdx = Long.valueOf(testUserTokenViewVo.getUserIdx()); + NoticeDeleteDto noticeDeleteDto = new NoticeDeleteDto(); + noticeDeleteDto.setNoticeIdx(noticeDeleteForm.getNoticeIdx()); + noticeDeleteDto.setUpdateMember(userIdx); + + com.alist.api.core.modules.standard.notice.vo.NoticeDeleteVo noticeStandardDeleteVo = noticeStandardService.updateNoticeDelete(noticeDeleteDto); + noticeDeleteVo.setDeleted(noticeStandardDeleteVo.isDeleted()); + noticeDeleteVo.setResultCode(noticeStandardDeleteVo.getResultCode()); + + if (!noticeDeleteVo.isDeleted()) { + return noticeDeleteVo; + } + + NoticeNoticeViewDto noticeNoticeViewDto = new NoticeNoticeViewDto(); + noticeNoticeViewDto.setNoticeIdx(noticeDeleteForm.getNoticeIdx()); + List noticeNoticeFileList = noticeNoticeService.noticeNoticeFileList(noticeNoticeViewDto); + + for (NoticeNoticeFileListItemVo noticeNoticeFileListItemVo : noticeNoticeFileList) { + NoticeFileDeleteDto noticeFileDeleteDto = new NoticeFileDeleteDto(); + noticeFileDeleteDto.setNoticeFileIdx(noticeNoticeFileListItemVo.getNoticeFileIdx()); + noticeFileDeleteDto.setUpdateMember(userIdx); + noticeFileService.updateNoticeFileDelete(noticeFileDeleteDto); + } + + return noticeDeleteVo; + } +} diff --git a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeAddVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeAddVo.java similarity index 57% rename from src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeAddVo.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeAddVo.java index 2398d4e..fc2466f 100644 --- a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeAddVo.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeAddVo.java @@ -1,16 +1,16 @@ -package com.alist.api.modules.admin.notice.vo; +package com.alist.api.admin.modules.support.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.Setter; -@Schema(description = "관리자 공지사항 등록 응답") +@Schema(description = "공지사항 등록 응답") @Getter @Setter -public class AdminNoticeAddVo { - @Schema(description = "공지사항 등록 여부", example = "true") - private boolean processed; +public class NoticeAddVo { + @Schema(description = "등록 성공 여부", example = "true") + private boolean added; @Schema(description = "등록된 공지사항 PK", example = "1") private Long noticeIdx; diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeCampusVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeCampusVo.java new file mode 100644 index 0000000..4d06067 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeCampusVo.java @@ -0,0 +1,13 @@ +package com.alist.api.admin.modules.support.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "공지사항 대상 캠퍼스") +@Getter +@Setter +public class NoticeCampusVo { + @Schema(description = "캠퍼스 PK", example = "1") + private Long campusIdx; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeDeleteVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeDeleteVo.java new file mode 100644 index 0000000..494e2bc --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeDeleteVo.java @@ -0,0 +1,21 @@ +package com.alist.api.admin.modules.support.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "공지사항 삭제 응답") +@Getter +@Setter +public class NoticeDeleteVo { + @Schema(description = "삭제 성공 여부", example = "true") + private boolean deleted; + + @Schema(description = "공지사항 PK", example = "1") + private Long noticeIdx; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} diff --git a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeFileVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeFileVo.java similarity index 71% rename from src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeFileVo.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeFileVo.java index 8ed2ac0..a058094 100644 --- a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeFileVo.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeFileVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.admin.notice.vo; +package com.alist.api.admin.modules.support.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -6,23 +6,20 @@ import lombok.Setter; import java.time.LocalDateTime; -@Schema(description = "관리자 공지사항 첨부파일 응답") +@Schema(description = "공지사항 첨부파일 응답") @Getter @Setter -public class AdminNoticeFileVo { +public class NoticeFileVo { @Schema(description = "공지사항 첨부파일 PK", example = "1") private Long noticeFileIdx; - @Schema(description = "공지사항 PK", example = "1") - private Long noticeIdx; - @Schema(description = "원본 파일명", example = "notice.pdf") private String fileOriginalName; @Schema(description = "저장 파일명", example = "20260701120000_notice.pdf") private String fileSaveName; - @Schema(description = "파일 경로", example = "/uploads/notice/2026/07/01/20260701120000_notice.pdf") + @Schema(description = "파일 경로", example = "/uploads/notice/2026/07/notice.pdf") private String filePath; @Schema(description = "파일 크기", example = "2048") @@ -34,6 +31,6 @@ public class AdminNoticeFileVo { @Schema(description = "정렬 순서", example = "0") private Integer sortOrder; - @Schema(description = "생성일", example = "2026-07-01 10:00:00") + @Schema(description = "생성일시") private LocalDateTime createDate; } diff --git a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeListItemVo.java similarity index 67% rename from src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeVo.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeListItemVo.java index 0063c54..fb01f5d 100644 --- a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeVo.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeListItemVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.admin.notice.vo; +package com.alist.api.admin.modules.support.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -6,10 +6,10 @@ import lombok.Setter; import java.time.LocalDateTime; -@Schema(description = "관리자 공지사항 목록 항목") +@Schema(description = "공지사항 목록 항목") @Getter @Setter -public class AdminNoticeVo { +public class NoticeListItemVo { @Schema(description = "공지사항 PK", example = "1") private Long noticeIdx; @@ -25,10 +25,10 @@ public class AdminNoticeVo { @Schema(description = "상단 고정 여부", example = "N") private String pinYn; - @Schema(description = "노출 시작일", example = "2026-07-01 00:00:00") + @Schema(description = "노출 시작일") private LocalDateTime startDate; - @Schema(description = "노출 종료일", example = "2026-07-31 23:59:59") + @Schema(description = "노출 종료일") private LocalDateTime endDate; @Schema(description = "조회수", example = "0") @@ -37,9 +37,9 @@ public class AdminNoticeVo { @Schema(description = "사용 여부", example = "Y") private String useYn; - @Schema(description = "생성일", example = "2026-07-01 10:00:00") + @Schema(description = "생성일시") private LocalDateTime createDate; - @Schema(description = "수정일", example = "2026-07-01 10:00:00") + @Schema(description = "수정일시") private LocalDateTime updateDate; } diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeListVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeListVo.java new file mode 100644 index 0000000..bd78f0a --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeListVo.java @@ -0,0 +1,16 @@ +package com.alist.api.admin.modules.support.vo; + +import com.alist.api.core.common.paging.PagingResponse; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "공지사항 목록 조회 응답") +@Getter +@Setter +public class NoticeListVo extends PagingResponse { + @Schema(description = "공지사항 목록") + private List noticeList; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeModifyVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeModifyVo.java new file mode 100644 index 0000000..1c6e0f0 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeModifyVo.java @@ -0,0 +1,21 @@ +package com.alist.api.admin.modules.support.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "공지사항 수정 응답") +@Getter +@Setter +public class NoticeModifyVo { + @Schema(description = "수정 성공 여부", example = "true") + private boolean updated; + + @Schema(description = "공지사항 PK", example = "1") + private Long noticeIdx; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; +} diff --git a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeViewVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeViewVo.java similarity index 55% rename from src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeViewVo.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeViewVo.java index b245fd3..58ea591 100644 --- a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeViewVo.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/support/vo/NoticeViewVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.admin.notice.vo; +package com.alist.api.admin.modules.support.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -7,10 +7,10 @@ import lombok.Setter; import java.time.LocalDateTime; import java.util.List; -@Schema(description = "관리자 공지사항 상세 조회 응답") +@Schema(description = "공지사항 상세 조회 응답") @Getter @Setter -public class AdminNoticeViewVo { +public class NoticeViewVo { @Schema(description = "공지사항 PK", example = "1") private Long noticeIdx; @@ -20,9 +20,6 @@ public class AdminNoticeViewVo { @Schema(description = "노출 범위", example = "CAMPUS") private String targetScope; - @Schema(description = "캠퍼스 IDX 목록", example = "[1, 2, 3]") - private List campusIdxList; - @Schema(description = "제목", example = "서비스 점검 안내") private String title; @@ -32,10 +29,10 @@ public class AdminNoticeViewVo { @Schema(description = "상단 고정 여부", example = "N") private String pinYn; - @Schema(description = "노출 시작일", example = "2026-07-01 00:00:00") + @Schema(description = "노출 시작일") private LocalDateTime startDate; - @Schema(description = "노출 종료일", example = "2026-07-31 23:59:59") + @Schema(description = "노출 종료일") private LocalDateTime endDate; @Schema(description = "조회수", example = "0") @@ -44,21 +41,21 @@ public class AdminNoticeViewVo { @Schema(description = "사용 여부", example = "Y") private String useYn; - @Schema(description = "삭제 여부", example = "N") - private String delYn; - - @Schema(description = "첨부파일 목록") - private List fileList; - - @Schema(description = "생성일", example = "2026-07-01 10:00:00") + @Schema(description = "생성일시") private LocalDateTime createDate; - @Schema(description = "생성자 회원 PK", example = "1") - private Integer createMember; + @Schema(description = "생성 회원 PK", example = "1") + private Long createMember; - @Schema(description = "수정일", example = "2026-07-01 10:00:00") + @Schema(description = "수정일시") private LocalDateTime updateDate; - @Schema(description = "수정자 회원 PK", example = "1") - private Integer updateMember; + @Schema(description = "수정 회원 PK", example = "1") + private Long updateMember; + + @Schema(description = "대상 캠퍼스 목록") + private List campusList; + + @Schema(description = "첨부파일 목록") + private List fileList; } diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/TusFileController.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/TusFileController.java new file mode 100644 index 0000000..46ff77e --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/TusFileController.java @@ -0,0 +1,127 @@ +package com.alist.api.admin.modules.tus; + +import com.alist.api.admin.modules.tus.form.TusFileDeleteForm; +import com.alist.api.admin.modules.tus.form.TusFileHookForm; +import com.alist.api.admin.modules.tus.form.TusFileUploadCancelForm; +import com.alist.api.admin.modules.tus.form.TusFileUploadInitForm; +import com.alist.api.admin.modules.tus.form.TusFileUploadStatusForm; +import com.alist.api.admin.modules.tus.service.TusFileService; +import com.alist.api.admin.modules.tus.vo.TusFileDeleteVo; +import com.alist.api.admin.modules.tus.vo.TusFileListVo; +import com.alist.api.admin.modules.tus.vo.TusFileUploadCancelVo; +import com.alist.api.admin.modules.tus.vo.TusFileUploadInitVo; +import com.alist.api.admin.modules.tus.vo.TusFileUploadStatusVo; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "D-2. TUS 업로드", description = "관리자 TUS 기반 대용량 파일 업로드 API") +@RestController +@RequestMapping("/tus/file") +public class TusFileController { + private final TusFileService tusFileService; + + public TusFileController(TusFileService tusFileService) { + this.tusFileService = tusFileService; + } + + @Operation(summary = "파일 업로드 초기화", description = "파일 마스터와 상세를 등록하고 TUS 업로드 토큰을 발급합니다.") + @PostMapping("/upload/init") + public ResponseEntity> tusFileUploadInit(@Valid @RequestBody TusFileUploadInitForm tusFileUploadInitForm) { + TusFileUploadInitVo tusFileUploadInitVo = tusFileService.insertTusFileUploadInit(tusFileUploadInitForm); + if (tusFileUploadInitVo.getFileMasterIdx() == null) { + return ApiResponse.entity(tusFileUploadInitVo, ApiResponseCode.CODE_401); + } + return ApiResponse.entity(tusFileUploadInitVo, ApiResponseCode.CODE_2002, "파일 업로드"); + } + + @Operation(summary = "업로드 권한 검증", description = "tusd가 업로드 전용 Bearer 토큰과 TUS 메타데이터를 검증합니다.") + @GetMapping("/upload/auth") + public ResponseEntity> tusFileUploadAuth(@RequestHeader(value = "Authorization", required = false) String authorization, @RequestHeader(value = "x-File-Uuid", required = false) String fileUuid, @RequestHeader(value = "Upload-Metadata", required = false) String uploadMetadata, @RequestHeader(value = "Upload-Length", required = false) String uploadLength) { + int resultCode = tusFileService.selectTusFileUploadAuth(authorization, fileUuid, uploadMetadata, uploadLength); + if (resultCode == 401) { + return ApiResponse.entity("", ApiResponseCode.CODE_401); + } + if (resultCode == 403) { + return ApiResponse.entity("", ApiResponseCode.CODE_403); + } + return ApiResponse.entity("", ApiResponseCode.CODE_200); + } + + @Operation(summary = "업로드 상태 조회", description = "Redis에 저장된 TUS 업로드 진행 상태를 조회합니다.") + @PostMapping("/upload/status") + public ResponseEntity> tusFileUploadStatus(@Valid @RequestBody TusFileUploadStatusForm tusFileUploadStatusForm) { + TusFileUploadStatusVo tusFileUploadStatusVo = tusFileService.selectTusFileUploadStatus(tusFileUploadStatusForm); + if (tusFileUploadStatusVo == null) { + return ApiResponse.entity(new TusFileUploadStatusVo(), ApiResponseCode.CODE_2003); + } + return ApiResponse.entity(tusFileUploadStatusVo, ApiResponseCode.CODE_2001, "업로드 상태"); + } + + @Operation(summary = "TUS Hook 수신", description = "tusd의 업로드 이벤트를 파일 상세, 마스터 집계, Redis 상태에 반영합니다.") + @PostMapping("/hook") + public ResponseEntity> tusFileHook(@RequestBody TusFileHookForm tusFileHookForm) { + tusFileService.updateTusFileHook(tusFileHookForm); + return ApiResponse.entity("", ApiResponseCode.CODE_200); + } + + @Operation(summary = "업로드 취소", description = "진행 중인 파일을 CANCELED 상태로 변경합니다.") + @PostMapping("/upload/cancel") + public ResponseEntity> tusFileUploadCancel(@Valid @RequestBody TusFileUploadCancelForm tusFileUploadCancelForm) { + TusFileUploadCancelVo tusFileUploadCancelVo = tusFileService.updateTusFileUploadCancel(tusFileUploadCancelForm); + if (tusFileUploadCancelVo.getResultCode() == 401) { + return ApiResponse.entity(tusFileUploadCancelVo, ApiResponseCode.CODE_401); + } + if (!tusFileUploadCancelVo.isCanceled()) { + return ApiResponse.entity(tusFileUploadCancelVo, ApiResponseCode.CODE_2003); + } + return ApiResponse.entity(tusFileUploadCancelVo, ApiResponseCode.CODE_2005, "업로드 취소"); + } + + @Operation(summary = "파일 목록 조회", description = "파일 마스터 기준으로 완료된 파일 목록을 조회합니다.") + @GetMapping("/list/{fileMasterIdx}") + public ResponseEntity> tusFileList(@PathVariable Long fileMasterIdx) { + TusFileListVo tusFileListVo = tusFileService.selectTusFileList(fileMasterIdx); + if (tusFileListVo.getResultCode() == 401) { + return ApiResponse.entity(tusFileListVo, ApiResponseCode.CODE_401); + } + return ApiResponse.entity(tusFileListVo, ApiResponseCode.CODE_2001, "파일"); + } + + @Operation(summary = "파일 보기", description = "완료된 파일을 inline으로 반환하고 조회 로그를 남깁니다.") + @GetMapping("/view/{fileUuid}") + public ResponseEntity tusFileView(@PathVariable String fileUuid, HttpServletRequest request) { + return tusFileService.selectTusFileResource(fileUuid, "VIEW", request.getRemoteAddr(), request.getHeader("User-Agent"), request.getHeader("Referer"), true); + } + + @Operation(summary = "파일 다운로드", description = "완료된 파일을 attachment로 반환하고 다운로드 로그를 남깁니다.") + @GetMapping("/download/{fileUuid}") + public ResponseEntity tusFileDownload(@PathVariable String fileUuid, HttpServletRequest request) { + return tusFileService.selectTusFileResource(fileUuid, "DOWNLOAD", request.getRemoteAddr(), request.getHeader("User-Agent"), request.getHeader("Referer"), false); + } + + @Operation(summary = "파일 삭제", description = "완료된 파일을 논리 삭제합니다.") + @PostMapping("/delete") + public ResponseEntity> tusFileDelete(@Valid @RequestBody TusFileDeleteForm tusFileDeleteForm) { + TusFileDeleteVo tusFileDeleteVo = tusFileService.updateTusFileDelete(tusFileDeleteForm); + if (tusFileDeleteVo.getResultCode() == 401) { + return ApiResponse.entity(tusFileDeleteVo, ApiResponseCode.CODE_401); + } + if (!tusFileDeleteVo.isDeleted()) { + return ApiResponse.entity(tusFileDeleteVo, ApiResponseCode.CODE_2003); + } + return ApiResponse.entity(tusFileDeleteVo, ApiResponseCode.CODE_2005, "파일 삭제"); + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileDeleteForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileDeleteForm.java new file mode 100644 index 0000000..4f4b840 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileDeleteForm.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.tus.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 파일 삭제 요청") +@Getter +@Setter +public class TusFileDeleteForm { + @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "파일 UUID는 필수입니다.") + private String fileUuid; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileHookForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileHookForm.java new file mode 100644 index 0000000..0231882 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileHookForm.java @@ -0,0 +1,47 @@ +package com.alist.api.admin.modules.tus.form; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.Map; + +@Schema(description = "tusd Hook 요청") +@Getter +@Setter +@JsonIgnoreProperties(ignoreUnknown = true) +public class TusFileHookForm { + @Schema(description = "Hook 이벤트 유형", example = "post-finish") + @JsonProperty("Type") + private String type; + + @JsonProperty("Event") + private Event event; + + @Getter + @Setter + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Event { + @JsonProperty("Upload") + private Upload upload; + } + + @Getter + @Setter + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Upload { + @JsonProperty("ID") + private String id; + + @JsonProperty("Size") + private Long size; + + @JsonProperty("Offset") + private Long offset; + + @JsonProperty("MetaData") + private Map metaData; + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadCancelForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadCancelForm.java new file mode 100644 index 0000000..9d97cb7 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadCancelForm.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.tus.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 업로드 취소 요청") +@Getter +@Setter +public class TusFileUploadCancelForm { + @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "파일 UUID는 필수입니다.") + private String fileUuid; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadInitForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadInitForm.java new file mode 100644 index 0000000..6bdcec6 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadInitForm.java @@ -0,0 +1,46 @@ +package com.alist.api.admin.modules.tus.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "TUS 파일 업로드 초기화 요청") +@Getter +@Setter +public class TusFileUploadInitForm { + @Schema(description = "파일 업무 구분", example = "BOARD", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "파일 카테고리는 필수입니다.") + private String fileCategory; + + @Schema(description = "논리 폴더 경로", example = "/board") + private String folderPath; + + @Schema(description = "업로드 파일 목록", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "업로드 파일 목록은 필수입니다.") + @Valid + private List itemList; + + @Schema(description = "TUS 업로드 파일 항목") + @Getter + @Setter + public static class Item { + @Schema(description = "원본 파일명", example = "sample.png", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "원본 파일명은 필수입니다.") + private String originName; + + @Schema(description = "파일 크기(byte)", example = "102400", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "파일 크기는 필수입니다.") + @Positive(message = "파일 크기는 0보다 커야 합니다.") + private Long sizeBytes; + + @Schema(description = "Content-Type", example = "image/png") + private String contentType; + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadStatusForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadStatusForm.java new file mode 100644 index 0000000..c096321 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/form/TusFileUploadStatusForm.java @@ -0,0 +1,15 @@ +package com.alist.api.admin.modules.tus.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 업로드 상태 조회 요청") +@Getter +@Setter +public class TusFileUploadStatusForm { + @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "파일 UUID는 필수입니다.") + private String fileUuid; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/service/TusFileService.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/service/TusFileService.java new file mode 100644 index 0000000..6221dcc --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/service/TusFileService.java @@ -0,0 +1,586 @@ +package com.alist.api.admin.modules.tus.service; + +import com.alist.api.admin.modules.tus.form.TusFileDeleteForm; +import com.alist.api.admin.modules.tus.form.TusFileHookForm; +import com.alist.api.admin.modules.tus.form.TusFileUploadCancelForm; +import com.alist.api.admin.modules.tus.form.TusFileUploadInitForm; +import com.alist.api.admin.modules.tus.form.TusFileUploadStatusForm; +import com.alist.api.admin.modules.tus.vo.TusFileDeleteVo; +import com.alist.api.admin.modules.tus.vo.TusFileListVo; +import com.alist.api.admin.modules.tus.vo.TusFileUploadCancelVo; +import com.alist.api.admin.modules.tus.vo.TusFileUploadInitVo; +import com.alist.api.admin.modules.tus.vo.TusFileUploadStatusVo; +import com.alist.api.core.common.jwt.JwtTokenProvider; +import com.alist.api.core.common.utils.SecurityUtil; +import com.alist.api.core.modules.bespoke.fileDetail.dto.FileDetailTusFileAuthDto; +import com.alist.api.core.modules.bespoke.fileDetail.dto.FileDetailTusFileListDto; +import com.alist.api.core.modules.bespoke.fileDetail.dto.FileDetailTusFileViewDto; +import com.alist.api.core.modules.bespoke.fileDetail.service.FileDetailTusFileService; +import com.alist.api.core.modules.bespoke.fileDetail.vo.FileDetailTusFileListItemVo; +import com.alist.api.core.modules.bespoke.fileDetail.vo.FileDetailTusFileViewVo; +import com.alist.api.core.modules.bespoke.fileMaster.dto.FileMasterTusFileModifyDto; +import com.alist.api.core.modules.bespoke.fileMaster.service.FileMasterTusFileService; +import com.alist.api.core.modules.standard.fileDetail.dto.FileDetailAddDto; +import com.alist.api.core.modules.standard.fileDetail.dto.FileDetailListDto; +import com.alist.api.core.modules.standard.fileDetail.dto.FileDetailModifyDto; +import com.alist.api.core.modules.standard.fileDetail.service.FileDetailService; +import com.alist.api.core.modules.standard.fileDetail.vo.FileDetailListVo; +import com.alist.api.core.modules.standard.fileDownloadEventLog.dto.FileDownloadEventLogAddDto; +import com.alist.api.core.modules.standard.fileDownloadEventLog.service.FileDownloadEventLogService; +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterAddDto; +import com.alist.api.core.modules.standard.fileMaster.dto.FileMasterDeleteDto; +import com.alist.api.core.modules.standard.fileMaster.service.FileMasterService; +import com.alist.api.core.modules.standard.fileUploadEventLog.dto.FileUploadEventLogAddDto; +import com.alist.api.core.modules.standard.fileUploadEventLog.service.FileUploadEventLogService; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenViewDto; +import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService; +import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenViewVo; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.http.ContentDisposition; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; + +@Service +public class TusFileService { + @Value("${tus-file.upload.tus-endpoint}") + private String tusEndpoint; + + @Value("${tus-file.upload.tmp-root}") + private String uploadTmpRoot; + + @Value("${tus-file.upload.auth-cache.ttl-seconds:20}") + private long uploadAuthCacheTtlSeconds; + + @Value("${file.storage.root-path}") + private String fileStorageRootPath; + + private final FileMasterService fileMasterService; + private final FileDetailService fileDetailService; + private final FileUploadEventLogService fileUploadEventLogService; + private final FileDownloadEventLogService fileDownloadEventLogService; + private final TestUserTokenService testUserTokenService; + private final FileDetailTusFileService fileDetailTusFileService; + private final FileMasterTusFileService fileMasterTusFileService; + private final JwtTokenProvider jwtTokenProvider; + private final StringRedisTemplate stringRedisTemplate; + + public TusFileService( + FileMasterService fileMasterService + , FileDetailService fileDetailService + , FileUploadEventLogService fileUploadEventLogService + , FileDownloadEventLogService fileDownloadEventLogService + , TestUserTokenService testUserTokenService + , FileDetailTusFileService fileDetailTusFileService + , FileMasterTusFileService fileMasterTusFileService + , JwtTokenProvider jwtTokenProvider + , StringRedisTemplate stringRedisTemplate + ) { + this.fileMasterService = fileMasterService; + this.fileDetailService = fileDetailService; + this.fileUploadEventLogService = fileUploadEventLogService; + this.fileDownloadEventLogService = fileDownloadEventLogService; + this.testUserTokenService = testUserTokenService; + this.fileDetailTusFileService = fileDetailTusFileService; + this.fileMasterTusFileService = fileMasterTusFileService; + this.jwtTokenProvider = jwtTokenProvider; + this.stringRedisTemplate = stringRedisTemplate; + } + + @Transactional + public TusFileUploadInitVo insertTusFileUploadInit(TusFileUploadInitForm tusFileUploadInitForm) { + TusFileUploadInitVo tusFileUploadInitVo = new TusFileUploadInitVo(); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + + if (userTokenIdx == null) { + return tusFileUploadInitVo; + } + + TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto(); + testUserTokenViewDto.setUserTokenIdx(userTokenIdx); + TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto); + + if (testUserTokenViewVo == null) { + return tusFileUploadInitVo; + } + + FileMasterAddDto fileMasterAddDto = new FileMasterAddDto(); + fileMasterAddDto.setUserIdx(testUserTokenViewVo.getUserIdx()); + fileMasterAddDto.setFileCategory(tusFileUploadInitForm.getFileCategory().trim()); + fileMasterAddDto.setFolderPath(normalizeFolderPath(tusFileUploadInitForm.getFolderPath())); + fileMasterAddDto.setTotalCount(tusFileUploadInitForm.getItemList().size()); + fileMasterAddDto.setDoneCount(0); + fileMasterAddDto.setFailCount(0); + fileMasterAddDto.setStatus((short) 0); + fileMasterAddDto.setCreateMember(testUserTokenViewVo.getUserIdx()); + fileMasterAddDto.setUpdateMember(testUserTokenViewVo.getUserIdx()); + fileMasterService.insertFileMasterAdd(fileMasterAddDto); + + List tusFileUploadInitItemList = new ArrayList<>(); + int fileSeq = 1; + + for (TusFileUploadInitForm.Item tusFileUploadInitItemForm : tusFileUploadInitForm.getItemList()) { + String fileUuid = UUID.randomUUID().toString(); + String uploadToken = jwtTokenProvider.createUploadToken(userTokenIdx); + + FileDetailAddDto fileDetailAddDto = new FileDetailAddDto(); + fileDetailAddDto.setFileMasterIdx(fileMasterAddDto.getFileMasterIdx()); + fileDetailAddDto.setFileSeq(fileSeq); + fileDetailAddDto.setFileUuid(fileUuid); + fileDetailAddDto.setOriginName(tusFileUploadInitItemForm.getOriginName().trim()); + fileDetailAddDto.setContentType(trimToNull(tusFileUploadInitItemForm.getContentType())); + fileDetailAddDto.setSizeBytes(tusFileUploadInitItemForm.getSizeBytes()); + fileDetailAddDto.setUploadedBytes(0L); + fileDetailAddDto.setMoveYn("N"); + fileDetailAddDto.setMoveTryCount(0); + fileDetailAddDto.setStatus((short) 0); + fileDetailAddDto.setCreateMember(testUserTokenViewVo.getUserIdx()); + fileDetailAddDto.setUpdateMember(testUserTokenViewVo.getUserIdx()); + fileDetailService.insertFileDetailAdd(fileDetailAddDto); + + TusFileUploadInitVo.Item tusFileUploadInitItemVo = new TusFileUploadInitVo.Item(); + tusFileUploadInitItemVo.setFileSeq(fileSeq++); + tusFileUploadInitItemVo.setFileUuid(fileUuid); + tusFileUploadInitItemVo.setUploadToken(uploadToken); + tusFileUploadInitItemVo.setOriginName(fileDetailAddDto.getOriginName()); + tusFileUploadInitItemVo.setSizeBytes(fileDetailAddDto.getSizeBytes()); + tusFileUploadInitItemVo.setContentType(fileDetailAddDto.getContentType()); + tusFileUploadInitItemList.add(tusFileUploadInitItemVo); + } + + tusFileUploadInitVo.setFileMasterIdx(fileMasterAddDto.getFileMasterIdx()); + tusFileUploadInitVo.setTusEndpoint(tusEndpoint); + tusFileUploadInitVo.setItemList(tusFileUploadInitItemList); + return tusFileUploadInitVo; + } + + @Transactional(readOnly = true) + public int selectTusFileUploadAuth(String authorization, String fileUuid, String uploadMetadata, String uploadLength) { + if (authorization == null || !authorization.startsWith("Bearer ") || fileUuid == null || fileUuid.isBlank()) { + return 401; + } + + String uploadToken = authorization.substring(7).trim(); + if (!jwtTokenProvider.validateToken(uploadToken)) { + return 401; + } + + if (!jwtTokenProvider.hasUploadScope(uploadToken)) { + return 403; + } + + Integer userTokenIdx; + try { + userTokenIdx = Integer.valueOf(jwtTokenProvider.getUserTokenIdx(uploadToken)); + } catch (NumberFormatException exception) { + return 401; + } + + FileDetailTusFileAuthDto fileDetailTusFileAuthDto = new FileDetailTusFileAuthDto(); + fileDetailTusFileAuthDto.setUserTokenIdx(userTokenIdx); + fileDetailTusFileAuthDto.setFileUuid(fileUuid.trim()); + applyUploadMetadata(fileDetailTusFileAuthDto, uploadMetadata, uploadLength); + + String cacheKey = "alist:upload:auth:" + fileDetailTusFileAuthDto.getFileUuid() + ":" + userTokenIdx; + String cachedResult = stringRedisTemplate.opsForValue().get(cacheKey); + if (cachedResult != null) { + return Integer.parseInt(cachedResult); + } + + int resultCode = fileDetailTusFileService.fileDetailTusFileUploadAuthCount(fileDetailTusFileAuthDto) > 0 ? 200 : 403; + stringRedisTemplate.opsForValue().set(cacheKey, String.valueOf(resultCode), Duration.ofSeconds(uploadAuthCacheTtlSeconds > 0 ? uploadAuthCacheTtlSeconds : 20)); + return resultCode; + } + + @Transactional(readOnly = true) + public TusFileUploadStatusVo selectTusFileUploadStatus(TusFileUploadStatusForm tusFileUploadStatusForm) { + Map statusMap = stringRedisTemplate.opsForHash().entries(statusKey(tusFileUploadStatusForm.getFileUuid().trim())); + if (statusMap.isEmpty()) { + return null; + } + + TusFileUploadStatusVo tusFileUploadStatusVo = new TusFileUploadStatusVo(); + tusFileUploadStatusVo.setFileUuid(String.valueOf(statusMap.getOrDefault("fileUuid", tusFileUploadStatusForm.getFileUuid()))); + tusFileUploadStatusVo.setStatus(String.valueOf(statusMap.getOrDefault("status", "PENDING"))); + tusFileUploadStatusVo.setUploadedBytes(parseLong(statusMap.get("uploadedBytes"))); + tusFileUploadStatusVo.setTotalBytes(parseLong(statusMap.get("totalBytes"))); + tusFileUploadStatusVo.setPercent((int) parseLong(statusMap.get("percent"))); + tusFileUploadStatusVo.setUpdatedAt(String.valueOf(statusMap.getOrDefault("updatedAt", ""))); + return tusFileUploadStatusVo; + } + + @Transactional + public boolean updateTusFileHook(TusFileHookForm tusFileHookForm) { + if (tusFileHookForm.getEvent() == null || tusFileHookForm.getEvent().getUpload() == null || tusFileHookForm.getEvent().getUpload().getMetaData() == null) { + return true; + } + + TusFileHookForm.Upload tusFileHookUploadForm = tusFileHookForm.getEvent().getUpload(); + String fileUuid = trimToNull(tusFileHookUploadForm.getMetaData().get("fileUuid")); + if (fileUuid == null) { + return true; + } + + FileDetailTusFileViewDto fileDetailTusFileViewDto = new FileDetailTusFileViewDto(); + fileDetailTusFileViewDto.setFileUuid(fileUuid); + FileDetailTusFileViewVo fileDetailTusFileViewVo = fileDetailTusFileService.fileDetailTusFileMetaView(fileDetailTusFileViewDto); + if (fileDetailTusFileViewVo == null) { + return true; + } + + FileUploadEventLogAddDto fileUploadEventLogAddDto = new FileUploadEventLogAddDto(); + fileUploadEventLogAddDto.setFileDetailIdx(fileDetailTusFileViewVo.getFileDetailIdx()); + fileUploadEventLogAddDto.setEventType(trimToNull(tusFileHookForm.getType())); + fileUploadEventLogAddDto.setOffsetBytes(tusFileHookUploadForm.getOffset()); + fileUploadEventLogService.insertFileUploadEventLogAdd(fileUploadEventLogAddDto); + + if (isMetadataMismatch(fileDetailTusFileViewVo, tusFileHookUploadForm)) { + updateDetailStatus(fileDetailTusFileViewVo.getFileDetailIdx(), (short) 5, tusFileHookUploadForm.getOffset(), null, null); + updateMasterAggregate(fileUuid); + saveStatus(fileUuid, "CANCELED", tusFileHookUploadForm.getOffset(), tusFileHookUploadForm.getSize()); + return false; + } + + String type = trimToNull(tusFileHookForm.getType()); + short status = "post-finish".equals(type) ? (short) 3 : "post-terminate".equals(type) ? (short) 5 : (short) 1; + Long uploadedBytes = "post-finish".equals(type) ? tusFileHookUploadForm.getSize() : null; + if (!"post-receive".equals(type)) { + updateDetailStatus(fileDetailTusFileViewVo.getFileDetailIdx(), status, uploadedBytes, tusFileHookUploadForm.getId(), fileDetailTusFileViewVo.getUserIdx()); + } + + if ("post-finish".equals(type) || "post-terminate".equals(type)) { + updateMasterAggregate(fileUuid); + } + + if ("post-finish".equals(type)) { + moveFinishedFile(fileUuid); + } + + saveStatus(fileUuid, status == 3 ? "DONE" : status == 5 ? "CANCELED" : "UPLOADING", tusFileHookUploadForm.getOffset(), tusFileHookUploadForm.getSize()); + return true; + } + + @Transactional + public TusFileUploadCancelVo updateTusFileUploadCancel(TusFileUploadCancelForm tusFileUploadCancelForm) { + TusFileUploadCancelVo tusFileUploadCancelVo = new TusFileUploadCancelVo(); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + if (userTokenIdx == null) { + tusFileUploadCancelVo.setResultCode(401); + return tusFileUploadCancelVo; + } + + FileDetailTusFileViewDto fileDetailTusFileViewDto = new FileDetailTusFileViewDto(); + fileDetailTusFileViewDto.setFileUuid(tusFileUploadCancelForm.getFileUuid().trim()); + fileDetailTusFileViewDto.setUserTokenIdx(userTokenIdx); + FileDetailTusFileViewVo fileDetailTusFileViewVo = fileDetailTusFileService.fileDetailTusFileUploadCancelView(fileDetailTusFileViewDto); + if (fileDetailTusFileViewVo == null) { + tusFileUploadCancelVo.setResultCode(2003); + return tusFileUploadCancelVo; + } + + updateDetailStatus(fileDetailTusFileViewVo.getFileDetailIdx(), (short) 5, fileDetailTusFileViewVo.getUploadedBytes(), null, fileDetailTusFileViewVo.getUserIdx()); + updateMasterAggregate(fileDetailTusFileViewVo.getFileUuid()); + saveStatus(fileDetailTusFileViewVo.getFileUuid(), "CANCELED", fileDetailTusFileViewVo.getUploadedBytes(), fileDetailTusFileViewVo.getSizeBytes()); + tusFileUploadCancelVo.setCanceled(true); + tusFileUploadCancelVo.setResultCode(200); + return tusFileUploadCancelVo; + } + + @Transactional(readOnly = true) + public TusFileListVo selectTusFileList(Long fileMasterIdx) { + TusFileListVo tusFileListVo = new TusFileListVo(); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + if (userTokenIdx == null) { + tusFileListVo.setResultCode(401); + return tusFileListVo; + } + + FileDetailTusFileListDto fileDetailTusFileListDto = new FileDetailTusFileListDto(); + fileDetailTusFileListDto.setFileMasterIdx(fileMasterIdx); + fileDetailTusFileListDto.setUserTokenIdx(userTokenIdx); + List fileDetailTusFileList = fileDetailTusFileService.fileDetailTusFileList(fileDetailTusFileListDto); + List tusFileListItemList = new ArrayList<>(); + + for (FileDetailTusFileListItemVo fileDetailTusFileListItemVo : fileDetailTusFileList) { + TusFileListVo.Item tusFileListItemVo = new TusFileListVo.Item(); + tusFileListItemVo.setFileDetailIdx(fileDetailTusFileListItemVo.getFileDetailIdx()); + tusFileListItemVo.setFileMasterIdx(fileDetailTusFileListItemVo.getFileMasterIdx()); + tusFileListItemVo.setFileUuid(fileDetailTusFileListItemVo.getFileUuid()); + tusFileListItemVo.setOriginName(fileDetailTusFileListItemVo.getOriginName()); + tusFileListItemVo.setContentType(fileDetailTusFileListItemVo.getContentType()); + tusFileListItemVo.setSizeBytes(fileDetailTusFileListItemVo.getSizeBytes()); + tusFileListItemVo.setViewUrl("/tus/file/view/" + fileDetailTusFileListItemVo.getFileUuid()); + tusFileListItemVo.setDownloadUrl("/tus/file/download/" + fileDetailTusFileListItemVo.getFileUuid()); + tusFileListItemList.add(tusFileListItemVo); + } + + tusFileListVo.setFileMasterIdx(fileMasterIdx); + tusFileListVo.setItemList(tusFileListItemList); + tusFileListVo.setResultCode(200); + return tusFileListVo; + } + + @Transactional + public ResponseEntity selectTusFileResource( + String fileUuid + , String eventType + , String clientIp + , String userAgent + , String referer + , boolean inline + ) { + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + if (userTokenIdx == null) { + return ResponseEntity.notFound().build(); + } + + FileDetailTusFileViewDto fileDetailTusFileViewDto = new FileDetailTusFileViewDto(); + fileDetailTusFileViewDto.setFileUuid(fileUuid.trim()); + fileDetailTusFileViewDto.setUserTokenIdx(userTokenIdx); + FileDetailTusFileViewVo fileDetailTusFileViewVo = fileDetailTusFileService.fileDetailTusFileView(fileDetailTusFileViewDto); + if (fileDetailTusFileViewVo == null) { + return ResponseEntity.notFound().build(); + } + + FileDownloadEventLogAddDto fileDownloadEventLogAddDto = new FileDownloadEventLogAddDto(); + fileDownloadEventLogAddDto.setFileMasterIdx(fileDetailTusFileViewVo.getFileMasterIdx()); + fileDownloadEventLogAddDto.setFileDetailIdx(fileDetailTusFileViewVo.getFileDetailIdx()); + fileDownloadEventLogAddDto.setFileUuid(fileDetailTusFileViewVo.getFileUuid()); + fileDownloadEventLogAddDto.setEventType(eventType); + fileDownloadEventLogAddDto.setUserIdx(fileDetailTusFileViewVo.getUserIdx()); + fileDownloadEventLogAddDto.setUserTokenIdx(userTokenIdx); + fileDownloadEventLogAddDto.setClientIp(clientIp); + fileDownloadEventLogAddDto.setUserAgent(userAgent); + fileDownloadEventLogAddDto.setReferer(referer); + fileDownloadEventLogAddDto.setCreateMember(fileDetailTusFileViewVo.getUserIdx()); + fileDownloadEventLogService.insertFileDownloadEventLogAdd(fileDownloadEventLogAddDto); + + try { + Path filePath = resolveStoredFilePath(fileDetailTusFileViewVo.getSavePath()); + if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) { + return ResponseEntity.notFound().build(); + } + String contentType = fileDetailTusFileViewVo.getContentType() == null ? MediaType.APPLICATION_OCTET_STREAM_VALUE : fileDetailTusFileViewVo.getContentType(); + ContentDisposition disposition = inline ? ContentDisposition.inline().filename(fileDetailTusFileViewVo.getOriginName(), StandardCharsets.UTF_8).build() : ContentDisposition.attachment().filename(fileDetailTusFileViewVo.getOriginName(), StandardCharsets.UTF_8).build(); + return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType)).contentLength(Files.size(filePath)).header(HttpHeaders.CONTENT_DISPOSITION, disposition.toString()).body(new InputStreamResource(Files.newInputStream(filePath))); + } catch (IOException exception) { + throw new IllegalStateException("tus file read failed", exception); + } + } + + @Transactional + public TusFileDeleteVo updateTusFileDelete(TusFileDeleteForm tusFileDeleteForm) { + TusFileDeleteVo tusFileDeleteVo = new TusFileDeleteVo(); + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + if (userTokenIdx == null) { + tusFileDeleteVo.setResultCode(401); + return tusFileDeleteVo; + } + + FileDetailTusFileViewDto fileDetailTusFileViewDto = new FileDetailTusFileViewDto(); + fileDetailTusFileViewDto.setFileUuid(tusFileDeleteForm.getFileUuid().trim()); + fileDetailTusFileViewDto.setUserTokenIdx(userTokenIdx); + FileDetailTusFileViewVo fileDetailTusFileViewVo = fileDetailTusFileService.fileDetailTusFileDeleteView(fileDetailTusFileViewDto); + if (fileDetailTusFileViewVo == null) { + tusFileDeleteVo.setResultCode(2003); + return tusFileDeleteVo; + } + + FileDetailModifyDto fileDetailModifyDto = new FileDetailModifyDto(); + fileDetailModifyDto.setFileDetailIdx(fileDetailTusFileViewVo.getFileDetailIdx()); + fileDetailModifyDto.setStatus((short) 3); + fileDetailModifyDto.setUpdateMember(fileDetailTusFileViewVo.getUserIdx()); + fileDetailService.updateFileDetailModify(fileDetailModifyDto); + + com.alist.api.core.modules.standard.fileDetail.dto.FileDetailDeleteDto fileDetailDeleteDto = new com.alist.api.core.modules.standard.fileDetail.dto.FileDetailDeleteDto(); + fileDetailDeleteDto.setFileDetailIdx(fileDetailTusFileViewVo.getFileDetailIdx()); + fileDetailDeleteDto.setUpdateMember(fileDetailTusFileViewVo.getUserIdx()); + fileDetailService.updateFileDetailDelete(fileDetailDeleteDto); + + FileDetailListDto fileDetailListDto = new FileDetailListDto(); + fileDetailListDto.setFileMasterIdx(fileDetailTusFileViewVo.getFileMasterIdx()); + FileDetailListVo fileDetailListVo = fileDetailService.selectFileDetailList(fileDetailListDto); + + if (fileDetailListVo.getTotalCount() == 0) { + FileMasterDeleteDto fileMasterDeleteDto = new FileMasterDeleteDto(); + fileMasterDeleteDto.setFileMasterIdx(fileDetailTusFileViewVo.getFileMasterIdx()); + fileMasterDeleteDto.setUpdateMember(fileDetailTusFileViewVo.getUserIdx()); + fileMasterService.updateFileMasterDelete(fileMasterDeleteDto); + } + tusFileDeleteVo.setDeleted(true); + tusFileDeleteVo.setResultCode(200); + return tusFileDeleteVo; + } + + private void updateDetailStatus(Long fileDetailIdx, Short status, Long uploadedBytes, String tusUploadId, Integer updateMember) { + FileDetailModifyDto fileDetailModifyDto = new FileDetailModifyDto(); + fileDetailModifyDto.setFileDetailIdx(fileDetailIdx); + fileDetailModifyDto.setStatus(status); + fileDetailModifyDto.setUploadedBytes(uploadedBytes); + fileDetailModifyDto.setTusUploadId(trimToNull(tusUploadId)); + fileDetailModifyDto.setUpdateMember(updateMember == null ? 0 : updateMember); + fileDetailService.updateFileDetailModify(fileDetailModifyDto); + } + + private void updateMasterAggregate(String fileUuid) { + FileMasterTusFileModifyDto fileMasterTusFileModifyDto = new FileMasterTusFileModifyDto(); + fileMasterTusFileModifyDto.setFileUuid(fileUuid); + fileMasterTusFileService.fileMasterTusFileAggregateModify(fileMasterTusFileModifyDto); + } + + private void moveFinishedFile(String fileUuid) { + FileDetailTusFileViewDto fileDetailTusFileViewDto = new FileDetailTusFileViewDto(); + fileDetailTusFileViewDto.setFileUuid(fileUuid); + FileDetailTusFileViewVo fileDetailTusFileViewVo = fileDetailTusFileService.fileDetailTusFileMoveView(fileDetailTusFileViewDto); + if (fileDetailTusFileViewVo == null || !"N".equalsIgnoreCase(fileDetailTusFileViewVo.getMoveYn()) || fileDetailTusFileViewVo.getTusUploadId() == null) { + return; + } + + try { + Path tmpPath = Paths.get(uploadTmpRoot, fileDetailTusFileViewVo.getTusUploadId()); + String ext = extractExtension(fileDetailTusFileViewVo.getOriginName()); + LocalDate today = LocalDate.now(); + Path finalPath = Paths.get(fileStorageRootPath, normalizeFolderPath(fileDetailTusFileViewVo.getFolderPath()), String.valueOf(today.getYear()), "%02d".formatted(today.getMonthValue()), "%02d".formatted(today.getDayOfMonth()), fileDetailTusFileViewVo.getFileUuid() + (ext.isBlank() ? "" : "." + ext)).normalize().toAbsolutePath(); + Files.createDirectories(finalPath.getParent()); + try { + Files.move(tmpPath, finalPath, StandardCopyOption.ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException exception) { + Files.copy(tmpPath, finalPath, StandardCopyOption.REPLACE_EXISTING); + Files.deleteIfExists(tmpPath); + } + + FileDetailModifyDto fileDetailModifyDto = new FileDetailModifyDto(); + fileDetailModifyDto.setFileDetailIdx(fileDetailTusFileViewVo.getFileDetailIdx()); + fileDetailModifyDto.setSaveName(finalPath.getFileName().toString()); + fileDetailModifyDto.setSavePath("/" + Paths.get(fileStorageRootPath).normalize().toAbsolutePath().relativize(finalPath).toString().replace("\\", "/")); + fileDetailModifyDto.setExt(ext); + fileDetailModifyDto.setMoveYn("Y"); + fileDetailModifyDto.setMoveDate(java.time.LocalDateTime.now()); + fileDetailModifyDto.setMoveLockClear(true); + fileDetailModifyDto.setUpdateMember(fileDetailTusFileViewVo.getUserIdx()); + fileDetailService.updateFileDetailModify(fileDetailModifyDto); + } catch (Exception exception) { + FileDetailModifyDto fileDetailModifyDto = new FileDetailModifyDto(); + fileDetailModifyDto.setFileDetailIdx(fileDetailTusFileViewVo.getFileDetailIdx()); + fileDetailModifyDto.setMoveYn("N"); + fileDetailModifyDto.setMoveTryCount((fileDetailTusFileViewVo.getMoveTryCount() == null ? 0 : fileDetailTusFileViewVo.getMoveTryCount()) + 1); + fileDetailModifyDto.setMoveLastError(exception.toString().substring(0, Math.min(exception.toString().length(), 500))); + fileDetailModifyDto.setUpdateMember(fileDetailTusFileViewVo.getUserIdx()); + fileDetailService.updateFileDetailModify(fileDetailModifyDto); + } + } + + private void applyUploadMetadata(FileDetailTusFileAuthDto fileDetailTusFileAuthDto, String uploadMetadata, String uploadLength) { + try { + fileDetailTusFileAuthDto.setSizeBytes(uploadLength == null || uploadLength.isBlank() ? null : Long.parseLong(uploadLength.trim())); + } catch (NumberFormatException exception) { + fileDetailTusFileAuthDto.setSizeBytes(null); + } + if (uploadMetadata == null || uploadMetadata.isBlank()) { + return; + } + for (String metadata : uploadMetadata.split(",")) { + String[] values = metadata.trim().split(" ", 2); + if (values.length != 2) { + continue; + } + try { + String value = new String(java.util.Base64.getDecoder().decode(values[1]), StandardCharsets.UTF_8); + if ("filename".equals(values[0])) { + fileDetailTusFileAuthDto.setOriginName(value); + } + if ("filetype".equals(values[0])) { + fileDetailTusFileAuthDto.setContentType(value); + } + } catch (IllegalArgumentException ignored) { + } + } + } + + private boolean isMetadataMismatch(FileDetailTusFileViewVo fileDetailTusFileViewVo, TusFileHookForm.Upload tusFileHookUploadForm) { + Map metadata = tusFileHookUploadForm.getMetaData(); + return (tusFileHookUploadForm.getSize() != null && !tusFileHookUploadForm.getSize().equals(fileDetailTusFileViewVo.getSizeBytes())) + || (metadata.get("filename") != null && !metadata.get("filename").equals(fileDetailTusFileViewVo.getOriginName())) + || (metadata.get("filetype") != null && fileDetailTusFileViewVo.getContentType() != null && !metadata.get("filetype").equalsIgnoreCase(fileDetailTusFileViewVo.getContentType())); + } + + private void saveStatus(String fileUuid, String status, Long uploadedBytes, Long totalBytes) { + long uploaded = uploadedBytes == null ? 0L : uploadedBytes; + long total = totalBytes == null ? 0L : totalBytes; + String key = statusKey(fileUuid); + stringRedisTemplate.opsForHash().put(key, "fileUuid", fileUuid); + stringRedisTemplate.opsForHash().put(key, "status", status); + stringRedisTemplate.opsForHash().put(key, "uploadedBytes", String.valueOf(uploaded)); + stringRedisTemplate.opsForHash().put(key, "totalBytes", String.valueOf(total)); + stringRedisTemplate.opsForHash().put(key, "percent", String.valueOf(total > 0 ? Math.min(100, uploaded * 100 / total) : 0)); + stringRedisTemplate.opsForHash().put(key, "updatedAt", Instant.now().toString()); + stringRedisTemplate.expire(key, Duration.ofHours(24)); + } + + private Path resolveStoredFilePath(String savePath) { + Path rootPath = Paths.get(fileStorageRootPath).normalize().toAbsolutePath(); + Path filePath = rootPath.resolve(savePath.startsWith("/") ? savePath.substring(1) : savePath).normalize().toAbsolutePath(); + if (!filePath.startsWith(rootPath)) { + throw new IllegalArgumentException("invalid save path"); + } + return filePath; + } + + private String normalizeFolderPath(String folderPath) { + if (folderPath == null || folderPath.isBlank() || "/".equals(folderPath.trim())) { + return "default"; + } + String normalizedFolderPath = folderPath.trim().replace("\\", "/"); + while (normalizedFolderPath.startsWith("/")) { + normalizedFolderPath = normalizedFolderPath.substring(1); + } + while (normalizedFolderPath.endsWith("/")) { + normalizedFolderPath = normalizedFolderPath.substring(0, normalizedFolderPath.length() - 1); + } + return normalizedFolderPath.contains("..") || normalizedFolderPath.isBlank() ? "default" : normalizedFolderPath; + } + + private String extractExtension(String fileName) { + if (fileName == null || fileName.lastIndexOf('.') < 0 || fileName.endsWith(".")) { + return ""; + } + return fileName.substring(fileName.lastIndexOf('.') + 1).trim().toLowerCase(Locale.ROOT); + } + + private String trimToNull(String value) { + return value == null || value.isBlank() ? null : value.trim(); + } + + private long parseLong(Object value) { + try { + return value == null ? 0L : Long.parseLong(String.valueOf(value)); + } catch (NumberFormatException exception) { + return 0L; + } + } + + private String statusKey(String fileUuid) { + return "alist:upload:status:" + fileUuid; + } +} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileDeleteVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileDeleteVo.java similarity index 69% rename from src/main/java/com/alist/api/modules/tusFile/vo/TusFileDeleteVo.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileDeleteVo.java index a623b9a..8e6124d 100644 --- a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileDeleteVo.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileDeleteVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.tusFile.vo; +package com.alist.api.admin.modules.tus.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,10 +9,8 @@ import lombok.Setter; @Getter @Setter public class TusFileDeleteVo { - @Schema(description = "파일 삭제 처리 여부", example = "true") + @Schema(description = "파일 삭제 여부", example = "true") private boolean deleted; - @JsonIgnore - @Schema(hidden = true) private Integer resultCode; } diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileListVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileListVo.java new file mode 100644 index 0000000..c9c40fe --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileListVo.java @@ -0,0 +1,33 @@ +package com.alist.api.admin.modules.tus.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "완료된 TUS 파일 목록 응답") +@Getter +@Setter +public class TusFileListVo { + @Schema(description = "파일 마스터 PK", example = "1") + private Long fileMasterIdx; + @Schema(description = "파일 목록") + private List itemList; + @JsonIgnore + private Integer resultCode; + + @Getter + @Setter + public static class Item { + private Long fileDetailIdx; + private Long fileMasterIdx; + private String fileUuid; + private String originName; + private String contentType; + private Long sizeBytes; + private String viewUrl; + private String downloadUrl; + } +} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadCancelVo.java similarity index 74% rename from src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelVo.java rename to 02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadCancelVo.java index f190b12..af84c5c 100644 --- a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelVo.java +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadCancelVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.tusFile.vo; +package com.alist.api.admin.modules.tus.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,11 +9,8 @@ import lombok.Setter; @Getter @Setter public class TusFileUploadCancelVo { - @JsonIgnore - @Schema(hidden = true) + @Schema(description = "업로드 취소 여부", example = "true") private boolean canceled; - @JsonIgnore - @Schema(hidden = true) private Integer resultCode; } diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadInitVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadInitVo.java new file mode 100644 index 0000000..55f06b9 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadInitVo.java @@ -0,0 +1,38 @@ +package com.alist.api.admin.modules.tus.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "TUS 파일 업로드 초기화 응답") +@Getter +@Setter +public class TusFileUploadInitVo { + @Schema(description = "파일 마스터 PK", example = "1") + private Long fileMasterIdx; + + @Schema(description = "TUS 업로드 엔드포인트", example = "https://file-alist.pjt.kr/tus/file/") + private String tusEndpoint; + + @Schema(description = "업로드 파일별 UUID 및 업로드 토큰") + private List itemList; + + @Getter + @Setter + public static class Item { + @Schema(description = "배치 내 파일 순번", example = "1") + private Integer fileSeq; + @Schema(description = "파일 UUID") + private String fileUuid; + @Schema(description = "TUS 업로드 전용 Bearer 토큰") + private String uploadToken; + @Schema(description = "원본 파일명") + private String originName; + @Schema(description = "파일 크기(byte)") + private Long sizeBytes; + @Schema(description = "Content-Type") + private String contentType; + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadStatusVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadStatusVo.java new file mode 100644 index 0000000..5e9a6ee --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/tus/vo/TusFileUploadStatusVo.java @@ -0,0 +1,23 @@ +package com.alist.api.admin.modules.tus.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 업로드 상태 응답") +@Getter +@Setter +public class TusFileUploadStatusVo { + @Schema(description = "파일 UUID") + private String fileUuid; + @Schema(description = "PENDING, UPLOADING, DONE, CANCELED") + private String status; + @Schema(description = "업로드 완료 byte") + private Long uploadedBytes; + @Schema(description = "전체 파일 byte") + private Long totalBytes; + @Schema(description = "진행률", example = "50") + private Integer percent; + @Schema(description = "상태 갱신 시각") + private String updatedAt; +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/user/UserController.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/user/UserController.java new file mode 100644 index 0000000..0656fe2 --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/user/UserController.java @@ -0,0 +1,44 @@ +package com.alist.api.admin.modules.user; + +import com.alist.api.admin.modules.user.form.UserAddForm; +import com.alist.api.admin.modules.user.service.UserService; +import com.alist.api.admin.modules.user.vo.UserAddVo; +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "A-2. ADMIN 사용자 관리", description = "ADMIN 사용자 회원가입 및 관리 API") +@RestController +@RequestMapping("/user") +public class UserController { + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + + @Operation(summary = "관리자 회원가입", description = "관리자 사용자를 등록합니다.") + @PostMapping("/add") + public ResponseEntity> userAdd( + @Valid @RequestBody UserAddForm userAddForm + ) { + UserAddVo userAddVo = userService.insertUserAdd(userAddForm); + + if (userAddVo.getResultCode() == 2004) { + return ApiResponse.entity(userAddVo, ApiResponseCode.CODE_2004, "아이디"); + } + + if (userAddVo.getResultCode() != 2002) { + return ApiResponse.entity(userAddVo, ApiResponseCode.CODE_500); + } + + return ApiResponse.entity(userAddVo, ApiResponseCode.CODE_2002, "관리자 사용자"); + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/user/form/UserAddForm.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/user/form/UserAddForm.java new file mode 100644 index 0000000..c8f8e6c --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/user/form/UserAddForm.java @@ -0,0 +1,26 @@ +package com.alist.api.admin.modules.user.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 사용자 등록 요청") +@Getter +@Setter +public class UserAddForm { + @Schema(description = "아이디", example = "admin01") + @NotBlank(message = "아이디를 입력해주세요.") + private String id; + + @Schema(description = "비밀번호", example = "pass1234") + @NotBlank(message = "비밀번호를 입력해주세요.") + @Size(min = 8, max = 64, message = "비밀번호는 8~64자여야 합니다.") + @Pattern( + regexp = "^(?=.*[A-Za-z])(?=.*\\d)\\S+$" + , message = "비밀번호는 영문과 숫자를 포함하고 공백이 없어야 합니다." + ) + private String password; +} \ No newline at end of file diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/user/service/UserService.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/user/service/UserService.java new file mode 100644 index 0000000..8fc728f --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/user/service/UserService.java @@ -0,0 +1,79 @@ +package com.alist.api.admin.modules.user.service; + +import com.alist.api.admin.modules.user.form.UserAddForm; +import com.alist.api.admin.modules.user.vo.UserAddVo; +import com.alist.api.core.common.utils.ApiKeyGenerator; +import com.alist.api.core.modules.standard.testUser.dto.TestUserAddDto; +import com.alist.api.core.modules.standard.testUser.service.TestUserService; +import com.alist.api.core.modules.standard.testUser.vo.TestUserAddVo; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenAddDto; +import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService; +import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenAddVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.interceptor.TransactionAspectSupport; + +@Service +public class UserService { + private static final String ADMIN_ROLE = "A"; + private static final String ADMIN_TYPE = "A"; + + private final TestUserService testUserService; + private final TestUserTokenService testUserTokenService; + + public UserService(TestUserService testUserService, TestUserTokenService testUserTokenService) { + this.testUserService = testUserService; + this.testUserTokenService = testUserTokenService; + } + + @Transactional + public UserAddVo insertUserAdd(UserAddForm userAddForm) { + UserAddVo userAddVo = new UserAddVo(); + String id = userAddForm.getId().trim(); + + userAddVo.setId(id); + userAddVo.setUserRole(ADMIN_ROLE); + userAddVo.setUserType(ADMIN_TYPE); + + if (testUserService.selectTestUserDuplicateCount(id) > 0) { + userAddVo.setResultCode(2004); + return userAddVo; + } + + TestUserAddDto testUserAddDto = new TestUserAddDto(); + testUserAddDto.setId(id); + testUserAddDto.setPassword(userAddForm.getPassword()); + + TestUserAddVo testUserAddVo = testUserService.insertTestUserAdd(testUserAddDto); + + if (!testUserAddVo.isAdded()) { + userAddVo.setResultCode(testUserAddVo.getResultCode()); + return userAddVo; + } + + for (int retry = 0; retry < 5; retry++) { + TestUserTokenAddDto testUserTokenAddDto = new TestUserTokenAddDto(); + testUserTokenAddDto.setUserIdx(testUserAddVo.getUserIdx()); + testUserTokenAddDto.setUserApiKey(ApiKeyGenerator.userApiKeyProc()); + testUserTokenAddDto.setUserRole(ADMIN_ROLE); + testUserTokenAddDto.setUserType(ADMIN_TYPE); + + TestUserTokenAddVo testUserTokenAddVo = testUserTokenService.insertTestUserTokenAdd(testUserTokenAddDto); + + if (testUserTokenAddVo.isAdded()) { + userAddVo.setAdded(true); + userAddVo.setUserIdx(testUserAddVo.getUserIdx()); + userAddVo.setResultCode(2002); + return userAddVo; + } + + if (testUserTokenAddVo.getResultCode() != 2004) { + break; + } + } + + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); + userAddVo.setResultCode(500); + return userAddVo; + } +} diff --git a/02-api-admin/src/main/java/com/alist/api/admin/modules/user/vo/UserAddVo.java b/02-api-admin/src/main/java/com/alist/api/admin/modules/user/vo/UserAddVo.java new file mode 100644 index 0000000..b34a3ba --- /dev/null +++ b/02-api-admin/src/main/java/com/alist/api/admin/modules/user/vo/UserAddVo.java @@ -0,0 +1,30 @@ +package com.alist.api.admin.modules.user.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "관리자 사용자 등록 응답") +@Getter +@Setter +public class UserAddVo { + @Schema(description = "등록 성공 여부", example = "true") + private boolean added; + + @Schema(description = "생성된 사용자 IDX", example = "101") + private Integer userIdx; + + @Schema(description = "등록된 관리자 아이디", example = "admin01") + private String id; + + @Schema(description = "관리자 권한 코드", example = "A") + private String userRole; + + @Schema(description = "관리자 사용자 유형 코드", example = "A") + private String userType; + + @JsonIgnore + @Schema(hidden = true) + private Integer resultCode; +} diff --git a/02-api-admin/src/main/resources/application-local.yaml b/02-api-admin/src/main/resources/application-local.yaml new file mode 100644 index 0000000..c79c837 --- /dev/null +++ b/02-api-admin/src/main/resources/application-local.yaml @@ -0,0 +1,77 @@ +spring: + datasource: + url: jdbc:log4jdbc:mariadb://121.160.234.222:3000/ALISTLMS?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Seoul&connectionCollation=utf8mb4_uca1400_ai_ci + username: alist_dev + password: 1qaz2wsx!@ + driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 10000 + idle-timeout: 600000 + data: + redis: + host: 121.160.234.222 + port: 3001 + password: 1qaz2wsx!@ + servlet: + multipart: + max-file-size: -1 + max-request-size: -1 + +logging: + config: classpath:logback-local.xml + +jwt: + secret: "F9k3s!29dkF#1lP0X9QZx8eW!2m@0AbD" + access-token-validity-seconds: 3600 + refresh-token-validity-seconds: 2592000 + +cookie: + secure: false # (HTTP) -> false + domain: # (HTTP) -> 비워두세요 + same-site: Lax # (HTTP) -> Lax + +swagger: + login: + id: alist + password: "1qaz2wsx!@" + +file: + storage: + root-path: /srv/project/alist/uploads + view-domain: http://localhost:8110 + upload: + max-size: 10MB + allowed-extensions: [jpg, jpeg, png, gif, pdf, hwp, hwpx, doc, docx, xls, xlsx, ppt, pptx, txt, csv, zip] + types: + profile: + folder: profile + max-size: 5MB + image-only: true + resize: + enabled: true + width: 400 + height: 400 + notice: + folder: notice + max-size: 20MB + image-only: false + resize: + enabled: true + max-width: 1200 + +tus-file: + upload: + tus-endpoint: https://file-alist.pjt.kr/tus/file/ + tmp-root: /srv/project/alist/uploads/tmp + interrupt-seconds: 30 + auth-cache: + ttl-seconds: 20 + +springdoc: + api-docs: + enabled: true + swagger-ui: + enabled: true + diff --git a/02-api-admin/src/main/resources/application-pjt.yaml b/02-api-admin/src/main/resources/application-pjt.yaml new file mode 100644 index 0000000..f761f18 --- /dev/null +++ b/02-api-admin/src/main/resources/application-pjt.yaml @@ -0,0 +1,79 @@ +spring: + main: + banner-mode: log + datasource: + url: jdbc:log4jdbc:mariadb://${DB_HOST}:${DB_PORT}/${DB_NAME}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Seoul&connectionCollation=utf8mb4_uca1400_ai_ci + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 10000 + idle-timeout: 600000 + data: + redis: + host: ${REDIS_HOST} + port: ${REDIS_PORT} + password: ${REDIS_PASSWORD} + servlet: + multipart: + max-file-size: -1 + max-request-size: -1 + +logging: + config: classpath:logback-pjt.xml + +jwt: + secret: ${JWT_SECRET} + access-token-validity-seconds: 3600 + refresh-token-validity-seconds: 2592000 + +cookie: + secure: true # 프로젝트 환경 (HTTPS) + domain: apiadmin-alist.pjt.kr + same-site: None + +swagger: + login: + id: ${SWAGGER_ID} + password: ${SWAGGER_PASSWORD} + +file: + storage: + root-path: /srv/project/alist/uploads + view-domain: https://file-alist.pjt.kr + upload: + max-size: 10MB + allowed-extensions: [jpg, jpeg, png, gif, pdf, hwp, hwpx, doc, docx, xls, xlsx, ppt, pptx, txt, csv, zip] + types: + profile: + folder: profile + max-size: 5MB + image-only: true + resize: + enabled: true + width: 400 + height: 400 + notice: + folder: notice + max-size: 20MB + image-only: false + resize: + enabled: true + max-width: 1200 + +tus-file: + upload: + tus-endpoint: https://file-alist.pjt.kr/tus/file/ + tmp-root: /srv/project/alist/uploads/tmp + interrupt-seconds: 30 + auth-cache: + ttl-seconds: 20 + +springdoc: + api-docs: + enabled: true + swagger-ui: + enabled: true + diff --git a/src/main/resources/banner.txt b/02-api-admin/src/main/resources/banner.txt similarity index 100% rename from src/main/resources/banner.txt rename to 02-api-admin/src/main/resources/banner.txt diff --git a/src/main/resources/logback-local.xml b/02-api-admin/src/main/resources/logback-local.xml similarity index 98% rename from src/main/resources/logback-local.xml rename to 02-api-admin/src/main/resources/logback-local.xml index 17ddb53..829c415 100644 --- a/src/main/resources/logback-local.xml +++ b/02-api-admin/src/main/resources/logback-local.xml @@ -1,5 +1,5 @@ - + diff --git a/src/main/resources/logback-pjt.xml b/02-api-admin/src/main/resources/logback-pjt.xml similarity index 86% rename from src/main/resources/logback-pjt.xml rename to 02-api-admin/src/main/resources/logback-pjt.xml index 772bdd7..e77616d 100644 --- a/src/main/resources/logback-pjt.xml +++ b/02-api-admin/src/main/resources/logback-pjt.xml @@ -1,20 +1,21 @@ - + + - ${LOG_PATH}/api.log + ${LOG_PATH}/${LOG_FILE_NAME}.log - ${LOG_PATH}/%d{yyyy/MM}/api.log-%d{yyyy-MM-dd}.%i + ${LOG_PATH}/%d{yyyy/MM}/${LOG_FILE_NAME}.log-%d{yyyy-MM-dd}.%i @@ -40,7 +41,7 @@ - + diff --git a/src/main/resources/robots.txt b/02-api-admin/src/main/resources/robots.txt similarity index 100% rename from src/main/resources/robots.txt rename to 02-api-admin/src/main/resources/robots.txt diff --git a/03-api-front/Dockerfile b/03-api-front/Dockerfile new file mode 100644 index 0000000..3fd31dc --- /dev/null +++ b/03-api-front/Dockerfile @@ -0,0 +1,13 @@ +FROM amazoncorretto:21 + +RUN yum -y install curl && yum clean all + +WORKDIR /api + +COPY build/libs/api-front.jar api-front.jar + +ENV JAVA_OPTS="" + +EXPOSE 8112 + +ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /api/api-front.jar"] diff --git a/03-api-front/build.gradle b/03-api-front/build.gradle new file mode 100644 index 0000000..6ca48ce --- /dev/null +++ b/03-api-front/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'org.springframework.boot' +} + +dependencies { + implementation project(':01-api-core') + + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.0' + + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +bootJar { + archiveFileName = 'api-front.jar' +} diff --git a/03-api-front/src/main/java/com/alist/api/front/ApiFrontApplication.java b/03-api-front/src/main/java/com/alist/api/front/ApiFrontApplication.java new file mode 100644 index 0000000..dad6191 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/ApiFrontApplication.java @@ -0,0 +1,15 @@ +package com.alist.api.front; + +import com.alist.api.core.config.ApiCoreConfig; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Import; + +@Import(ApiCoreConfig.class) +@SpringBootApplication +public class ApiFrontApplication { + + public static void main(String[] args) { + SpringApplication.run(ApiFrontApplication.class, args); + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/config/OpenApiConfig.java b/03-api-front/src/main/java/com/alist/api/front/config/OpenApiConfig.java new file mode 100644 index 0000000..dc37bb2 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/config/OpenApiConfig.java @@ -0,0 +1,37 @@ +package com.alist.api.front.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OpenApiConfig { + private static final String SECURITY_SCHEME_NAME = "bearerAuth"; + + @Value("${api.version}") + private String apiVersion; + + @Bean + public OpenAPI openAPI() { + return new OpenAPI() + .info(new Info() + .title("A*List Front API") + .version(apiVersion) + .description("A*List Front API 입니다.!")) + .addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME)) + .components(new Components() + .addSecuritySchemes(SECURITY_SCHEME_NAME, + new SecurityScheme() + .name("Authorization") + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + ) + ); + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/config/SecurityConfig.java b/03-api-front/src/main/java/com/alist/api/front/config/SecurityConfig.java new file mode 100644 index 0000000..a50fe85 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/config/SecurityConfig.java @@ -0,0 +1,97 @@ +package com.alist.api.front.config; + +import com.alist.api.core.common.jwt.JwtAccessDeniedHandler; +import com.alist.api.core.common.jwt.JwtAuthenticationEntryPoint; +import com.alist.api.core.common.jwt.JwtAuthenticationFilter; +import com.alist.api.core.common.jwt.JwtTokenProvider; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Slf4j +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Value("${swagger.login.id}") + private String swaggerLoginId; + + @Value("${swagger.login.password}") + private String swaggerLoginPassword; + + @Bean + public JwtAuthenticationFilter jwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { + return new JwtAuthenticationFilter( + jwtTokenProvider, + "USER", + "accessToken" + ); + } + + @Bean + @Order(1) + public SecurityFilterChain swaggerFilterChain(HttpSecurity http) throws Exception { + http + .securityMatcher("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html") + .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) + .httpBasic(Customizer.withDefaults()) + .csrf(csrf -> csrf.disable()); + + return http.build(); + } + + @Bean + @Order(2) + public SecurityFilterChain apiFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception { + http + .cors(Customizer.withDefaults()) + .csrf(csrf -> csrf.disable()) + .exceptionHandling(ex -> ex + .authenticationEntryPoint(new JwtAuthenticationEntryPoint()) + .accessDeniedHandler(new JwtAccessDeniedHandler()) + ) + .authorizeHttpRequests(auth -> auth + .requestMatchers( + "/" + , "/actuator/health" + , "/sso/**" + , "/auth/**" + , "/user/add" + , "/user/migration/list" + , "/file/**" + ).permitAll() + .anyRequest().authenticated() + ) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + @Bean + public UserDetailsService userDetailsService() { + UserDetails alist = User.builder() + .username(swaggerLoginId) + .password(passwordEncoder().encode(swaggerLoginPassword)) + .roles("ADMIN") + .build(); + + return new InMemoryUserDetailsManager(alist); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/auth/AuthController.java b/03-api-front/src/main/java/com/alist/api/front/modules/auth/AuthController.java new file mode 100644 index 0000000..54616cc --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/auth/AuthController.java @@ -0,0 +1,87 @@ +package com.alist.api.front.modules.auth; + +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import com.alist.api.front.modules.auth.form.AuthApiKeyLoginForm; +import com.alist.api.front.modules.auth.form.AuthTokenForm; +import com.alist.api.front.modules.auth.service.AuthService; +import com.alist.api.front.modules.auth.vo.AuthAccessVo; +import com.alist.api.front.modules.auth.vo.AuthApiKeyLoginVo; +import com.alist.api.front.modules.auth.vo.AuthRefreshVo; +import com.alist.api.front.modules.auth.vo.AuthTokenVo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "A-2. 엑세스 토큰 발급", description = "SSO 기반 Access Token 발급 및 갱신 API") +@RestController +@RequestMapping("/auth") +public class AuthController { + private final AuthService authService; + + public AuthController(AuthService authService) { + this.authService = authService; + } + + @Operation(summary = "SSO 기반 토큰 발급", description = "유효한 SSO 세션을 확인한 뒤 Access Token과 Refresh Token 쿠키를 발급합니다.") + @PostMapping("/access") + public ResponseEntity> authAccess( + HttpServletRequest request, + HttpServletResponse response + ) { + AuthAccessVo authAccessVo = authService.selectAuthAccess(request, response); + + if (authAccessVo.getResultCode() == 401) { + return ApiResponse.entity(authAccessVo, ApiResponseCode.CODE_401); + } + + return ApiResponse.entity(authAccessVo, ApiResponseCode.CODE_2001, "Access Token"); + } + + @Operation(summary = "외부 API Key 로그인", description = "외부 연동용으로 Access Token과 Refresh Token을 JSON으로 반환합니다.") + @PostMapping("/apiKeyLogin") + public ResponseEntity> authApiKeyLogin( + @Valid @RequestBody AuthApiKeyLoginForm authApiKeyLoginForm + ) { + AuthApiKeyLoginVo authApiKeyLoginVo = authService.selectAuthApiKeyLogin(authApiKeyLoginForm); + + if (authApiKeyLoginVo.getResultCode() == 2003) { + return ApiResponse.entity(authApiKeyLoginVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(authApiKeyLoginVo, ApiResponseCode.CODE_2001, "Access Token"); + } + + @Operation(summary = "Access Token 재발급", description = "유효한 Refresh Token 쿠키를 검증하고 새 Access Token과 Refresh Token 쿠키를 발급합니다.") + @PostMapping("/refresh") + public ResponseEntity> authRefresh( + @CookieValue(name = "refreshToken", required = false) String refreshToken, + HttpServletResponse response + ) { + AuthRefreshVo authRefreshVo = authService.selectAuthRefresh(refreshToken, response); + + if (authRefreshVo.getResultCode() == 401) { + return ApiResponse.entity(authRefreshVo, ApiResponseCode.CODE_401); + } + + return ApiResponse.entity(authRefreshVo, ApiResponseCode.CODE_200); + } + + @Operation(summary = "테스트 Access Token 발급", description = "개발 테스트 전용 Access Token을 반환합니다. 실서비스 인증에는 사용하지 않습니다.") + @PostMapping("/token") + public ResponseEntity> authToken( + @Valid @RequestBody AuthTokenForm authTokenForm + ) { + AuthTokenVo authTokenVo = authService.selectAuthToken(authTokenForm); + + return ApiResponse.entity(authTokenVo, ApiResponseCode.CODE_2001, "테스트 Access Token"); + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/auth/form/AuthApiKeyLoginForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/auth/form/AuthApiKeyLoginForm.java new file mode 100644 index 0000000..595c25e --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/auth/form/AuthApiKeyLoginForm.java @@ -0,0 +1,15 @@ +package com.alist.api.front.modules.auth.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "외부 API Key 로그인 요청") +@Getter +@Setter +public class AuthApiKeyLoginForm { + @Schema(description = "발급된 사용자 API Key", example = "YjeWJr9-N-XaJ7ix2M2spnfjOyG9nTmRlY9t6sKw4_M", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "API Key는 필수입니다.") + private String userApiKey; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/auth/form/AuthTokenForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/auth/form/AuthTokenForm.java new file mode 100644 index 0000000..9df66b6 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/auth/form/AuthTokenForm.java @@ -0,0 +1,15 @@ +package com.alist.api.front.modules.auth.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "테스트 Access Token 요청") +@Getter +@Setter +public class AuthTokenForm { + @Schema(description = "사용자명", example = "test", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "사용자명은 필수입니다.") + private String id; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/auth/service/AuthService.java b/03-api-front/src/main/java/com/alist/api/front/modules/auth/service/AuthService.java new file mode 100644 index 0000000..edabc7b --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/auth/service/AuthService.java @@ -0,0 +1,208 @@ +package com.alist.api.front.modules.auth.service; + +import com.alist.api.core.common.jwt.JwtTokenProvider; +import com.alist.api.core.common.utils.SessionUtil; +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenApiKeyLoginViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenRefreshViewDto; +import com.alist.api.core.modules.bespoke.testUserToken.service.TestUserTokenAuthService; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenApiKeyLoginViewVo; +import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenRefreshViewVo; +import com.alist.api.core.modules.standard.testUser.dto.TestUserModifyDto; +import com.alist.api.core.modules.standard.testUser.service.TestUserService; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenModifyDto; +import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService; +import com.alist.api.front.modules.auth.form.AuthApiKeyLoginForm; +import com.alist.api.front.modules.auth.form.AuthTokenForm; +import com.alist.api.front.modules.auth.vo.AuthAccessVo; +import com.alist.api.front.modules.auth.vo.AuthApiKeyLoginVo; +import com.alist.api.front.modules.auth.vo.AuthRefreshVo; +import com.alist.api.front.modules.auth.vo.AuthTokenVo; +import com.alist.api.front.modules.sso.service.SsoService; +import com.alist.api.front.modules.sso.vo.SsoLoginCheckedVo; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +@Service +public class AuthService { + private static final List FRONT_USER_TYPES = List.of("T", "S"); + private static final String USER_SCOPE = "USER"; + private static final String REFRESH_TOKEN_TYPE = "REFRESH"; + private static final String ACCESS_TOKEN_COOKIE_NAME = "accessToken"; + private static final String REFRESH_TOKEN_COOKIE_NAME = "refreshToken"; + + @Value("${cookie.secure}") + private boolean cookieSecure; + + @Value("${cookie.domain:}") + private String cookieDomain; + + @Value("${cookie.same-site:Lax}") + private String cookieSameSite; + + @Value("${jwt.access-token-validity-seconds}") + private long accessTokenValiditySeconds; + + @Value("${jwt.refresh-token-validity-seconds}") + private long refreshTokenValiditySeconds; + + private final SsoService ssoService; + private final TestUserTokenAuthService testUserTokenAuthService; + private final TestUserTokenService testUserTokenService; + private final TestUserService testUserService; + private final JwtTokenProvider jwtTokenProvider; + + public AuthService( + SsoService ssoService, + TestUserTokenAuthService testUserTokenAuthService, + TestUserTokenService testUserTokenService, + TestUserService testUserService, + JwtTokenProvider jwtTokenProvider + ) { + this.ssoService = ssoService; + this.testUserTokenAuthService = testUserTokenAuthService; + this.testUserTokenService = testUserTokenService; + this.testUserService = testUserService; + this.jwtTokenProvider = jwtTokenProvider; + } + + @Transactional + public AuthAccessVo selectAuthAccess(HttpServletRequest request, HttpServletResponse response) { + SsoLoginCheckedVo ssoLoginCheckedVo = ssoService.selectSsoLoginChecked(request, response); + + if (!ssoLoginCheckedVo.isLoggedIn() || ssoLoginCheckedVo.getUserTokenIdx() == null) { + AuthAccessVo authAccessVo = new AuthAccessVo(); + authAccessVo.setAccessToken(false); + authAccessVo.setResultCode(401); + + return authAccessVo; + } + + issueTokenCookie(ssoLoginCheckedVo.getUserTokenIdx(), ssoLoginCheckedVo.getUserRole(), response); + updateLastLoginAt(ssoLoginCheckedVo.getUserIdx()); + + AuthAccessVo authAccessVo = new AuthAccessVo(); + authAccessVo.setAccessToken(true); + authAccessVo.setUserIdx(ssoLoginCheckedVo.getUserIdx()); + authAccessVo.setUserId(ssoLoginCheckedVo.getUserId()); + authAccessVo.setUserRole(ssoLoginCheckedVo.getUserRole()); + authAccessVo.setUserType(ssoLoginCheckedVo.getUserType()); + authAccessVo.setResultCode(2001); + + return authAccessVo; + } + + @Transactional + public AuthApiKeyLoginVo selectAuthApiKeyLogin(AuthApiKeyLoginForm authApiKeyLoginForm) { + TestUserTokenApiKeyLoginViewDto testUserTokenApiKeyLoginViewDto = new TestUserTokenApiKeyLoginViewDto(); + testUserTokenApiKeyLoginViewDto.setUserApiKey(authApiKeyLoginForm.getUserApiKey().trim()); + testUserTokenApiKeyLoginViewDto.setUserTypes(FRONT_USER_TYPES); + + TestUserTokenApiKeyLoginViewVo testUserTokenApiKeyLoginViewVo = testUserTokenAuthService.testUserTokenApiKeyLoginView(testUserTokenApiKeyLoginViewDto); + + if (testUserTokenApiKeyLoginViewVo == null) { + AuthApiKeyLoginVo authApiKeyLoginVo = new AuthApiKeyLoginVo(); + authApiKeyLoginVo.setResultCode(2003); + + return authApiKeyLoginVo; + } + + String accessToken = jwtTokenProvider.createAccessToken(testUserTokenApiKeyLoginViewVo.getUserTokenIdx(), testUserTokenApiKeyLoginViewVo.getUserRole()); + String refreshToken = jwtTokenProvider.createRefreshToken(testUserTokenApiKeyLoginViewVo.getUserTokenIdx()); + + updateRefreshToken(testUserTokenApiKeyLoginViewVo.getUserTokenIdx(), refreshToken); + updateLastLoginAt(testUserTokenApiKeyLoginViewVo.getUserIdx()); + + AuthApiKeyLoginVo authApiKeyLoginVo = new AuthApiKeyLoginVo(); + authApiKeyLoginVo.setId(testUserTokenApiKeyLoginViewVo.getId()); + authApiKeyLoginVo.setAccessToken(accessToken); + authApiKeyLoginVo.setRefreshToken(refreshToken); + authApiKeyLoginVo.setResultCode(2001); + + return authApiKeyLoginVo; + } + + @Transactional + public AuthRefreshVo selectAuthRefresh(String refreshToken, HttpServletResponse response) { + AuthRefreshVo authRefreshVo = new AuthRefreshVo(); + + if (refreshToken == null + || !jwtTokenProvider.validateToken(refreshToken) + || !USER_SCOPE.equals(jwtTokenProvider.getScope(refreshToken)) + || !REFRESH_TOKEN_TYPE.equals(jwtTokenProvider.getTokenType(refreshToken))) { + authRefreshVo.setRefreshed(false); + authRefreshVo.setResultCode(401); + + return authRefreshVo; + } + + Integer userTokenIdx; + try { + userTokenIdx = Integer.valueOf(jwtTokenProvider.getUserTokenIdx(refreshToken)); + } catch (NumberFormatException e) { + authRefreshVo.setRefreshed(false); + authRefreshVo.setResultCode(401); + + return authRefreshVo; + } + + TestUserTokenRefreshViewDto testUserTokenRefreshViewDto = new TestUserTokenRefreshViewDto(); + testUserTokenRefreshViewDto.setUserTokenIdx(userTokenIdx); + testUserTokenRefreshViewDto.setUserTypes(FRONT_USER_TYPES); + + TestUserTokenRefreshViewVo testUserTokenRefreshViewVo = testUserTokenAuthService.testUserTokenRefreshView(testUserTokenRefreshViewDto); + if (testUserTokenRefreshViewVo == null || !refreshToken.equals(testUserTokenRefreshViewVo.getRefreshToken())) { + authRefreshVo.setRefreshed(false); + authRefreshVo.setResultCode(401); + + return authRefreshVo; + } + + issueTokenCookie(testUserTokenRefreshViewVo.getUserTokenIdx(), testUserTokenRefreshViewVo.getUserRole(), response); + + authRefreshVo.setRefreshed(true); + authRefreshVo.setResultCode(200); + + return authRefreshVo; + } + + @Transactional(readOnly = true) + public AuthTokenVo selectAuthToken(AuthTokenForm authTokenForm) { + AuthTokenVo authTokenVo = new AuthTokenVo(); + authTokenVo.setAccessToken(jwtTokenProvider.createToken(authTokenForm.getId().trim())); + + return authTokenVo; + } + + private void issueTokenCookie(Integer userTokenIdx, String userRole, HttpServletResponse response) { + String accessToken = jwtTokenProvider.createAccessToken(userTokenIdx, userRole); + String refreshToken = jwtTokenProvider.createRefreshToken(userTokenIdx); + + updateRefreshToken(userTokenIdx, refreshToken); + + SessionUtil.addTokenCookie(response, ACCESS_TOKEN_COOKIE_NAME, accessToken, cookieDomain, cookieSecure, cookieSameSite, accessTokenValiditySeconds); + SessionUtil.addTokenCookie(response, REFRESH_TOKEN_COOKIE_NAME, refreshToken, cookieDomain, cookieSecure, cookieSameSite, refreshTokenValiditySeconds); + } + + private void updateRefreshToken(Integer userTokenIdx, String refreshToken) { + TestUserTokenModifyDto testUserTokenModifyDto = new TestUserTokenModifyDto(); + testUserTokenModifyDto.setUserTokenIdx(userTokenIdx); + testUserTokenModifyDto.setRefreshToken(refreshToken); + testUserTokenModifyDto.setExpiresAt(LocalDateTime.now().plusSeconds(refreshTokenValiditySeconds)); + + testUserTokenService.updateTestUserTokenModify(testUserTokenModifyDto); + } + + private void updateLastLoginAt(Integer userIdx) { + TestUserModifyDto testUserModifyDto = new TestUserModifyDto(); + testUserModifyDto.setUserIdx(userIdx); + testUserModifyDto.setLastLoginAt(LocalDateTime.now()); + + testUserService.updateTestUserModify(testUserModifyDto); + } +} diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/AuthAccessVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthAccessVo.java similarity index 61% rename from src/main/java/com/alist/api/modules/front/auth/vo/AuthAccessVo.java rename to 03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthAccessVo.java index 1d9b7b1..6e070af 100644 --- a/src/main/java/com/alist/api/modules/front/auth/vo/AuthAccessVo.java +++ b/03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthAccessVo.java @@ -1,5 +1,6 @@ -package com.alist.api.modules.front.auth.vo; +package com.alist.api.front.modules.auth.vo; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -16,12 +17,16 @@ public class AuthAccessVo { @Schema(description = "회원 PK", example = "1") private Integer userIdx; - @Schema(description = "회원 아이디", example = "test") + @Schema(description = "회원 아이디", example = "teacher01") private String userId; - @Schema(description = "회원 권한 코드", example = "CCD/SCM/I/R/E") + @Schema(description = "회원 권한", example = "TEACHER") private String userRole; - @Schema(description = "회원 유형 코드. T: 교사, S: 학생", example = "S") + @Schema(description = "회원 유형", example = "T") private String userType; + + @JsonIgnore + @Schema(hidden = true) + private int resultCode; } diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/AuthApiKeyLoginVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthApiKeyLoginVo.java similarity index 55% rename from src/main/java/com/alist/api/modules/front/auth/vo/AuthApiKeyLoginVo.java rename to 03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthApiKeyLoginVo.java index b03e18e..242d791 100644 --- a/src/main/java/com/alist/api/modules/front/auth/vo/AuthApiKeyLoginVo.java +++ b/03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthApiKeyLoginVo.java @@ -1,21 +1,21 @@ -package com.alist.api.modules.front.auth.vo; +package com.alist.api.front.modules.auth.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.Setter; -@Schema(description = "API Key 로그인 응답") +@Schema(description = "외부 API Key 로그인 응답") @Getter @Setter public class AuthApiKeyLoginVo { - @Schema(description = "아이디") + @Schema(description = "회원 아이디", example = "teacher01") private String id; - @Schema(description = "엑세스 토큰") + @Schema(description = "외부 API용 Access Token") private String accessToken; - @Schema(description = "리플레시 토큰") + @Schema(description = "외부 API용 Refresh Token") private String refreshToken; @JsonIgnore diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/AuthRefreshVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthRefreshVo.java similarity index 73% rename from src/main/java/com/alist/api/modules/front/auth/vo/AuthRefreshVo.java rename to 03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthRefreshVo.java index 5f7d8c5..2117978 100644 --- a/src/main/java/com/alist/api/modules/front/auth/vo/AuthRefreshVo.java +++ b/03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthRefreshVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.front.auth.vo; +package com.alist.api.front.modules.auth.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; @@ -14,9 +14,5 @@ public class AuthRefreshVo { @JsonIgnore @Schema(hidden = true) - private String accessToken; - - @JsonIgnore - @Schema(hidden = true) - private String refreshToken; + private int resultCode; } diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthTokenVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthTokenVo.java new file mode 100644 index 0000000..02aa2f1 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/auth/vo/AuthTokenVo.java @@ -0,0 +1,13 @@ +package com.alist.api.front.modules.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "테스트 Access Token 응답") +@Getter +@Setter +public class AuthTokenVo { + @Schema(description = "테스트용 Access Token") + private String accessToken; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/file/FileController.java b/03-api-front/src/main/java/com/alist/api/front/modules/file/FileController.java new file mode 100644 index 0000000..906cd55 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/file/FileController.java @@ -0,0 +1,78 @@ +package com.alist.api.front.modules.file; + +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import com.alist.api.front.modules.file.form.SunEditorUploadForm; +import com.alist.api.front.modules.file.service.FileService; +import com.alist.api.front.modules.file.vo.FileUploadVo; +import com.alist.api.front.modules.file.vo.SunEditorUploadVo; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +@Tag(name = "D-1. 파일 업로드", description = "공통 파일 저장, 보기, 다운로드 API") +@RestController +@RequestMapping("/file") +public class FileController { + private final FileService fileService; + + public FileController(FileService fileService) { + this.fileService = fileService; + } + + @Operation(summary = "단순 파일 업로드", description = "DB 기록 없이 파일을 저장하고 업로드 경로를 반환합니다.") + @PostMapping( + value = "/upload" + , consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ) + public ResponseEntity> fileUpload( + @Parameter(description = "저장 폴더 키", example = "notice", required = true) @RequestParam String folder + , @Parameter(description = "업로드 파일", required = true) @RequestPart("file") MultipartFile multipartFile + ) { + FileUploadVo fileUploadVo = fileService.insertFileUpload(folder, multipartFile); + + return ApiResponse.entity(fileUploadVo, ApiResponseCode.CODE_200, "파일 업로드"); + } + + @Operation( + summary = "SunEditor 이미지 업로드" + , description = "여러 이미지를 저장하고 SunEditor result 배열 형식으로 공개 파일 URL을 반환합니다." + , requestBody = @RequestBody(content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE, schema = @Schema(implementation = SunEditorUploadForm.class))) + ) + @PostMapping( + value = "/suneditor/upload" + , consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ) + public ResponseEntity fileSunEditorUpload( + @Parameter(description = "저장 폴더 키", example = "editor") @RequestParam(value = "folder", defaultValue = "editor") String folder + , MultipartHttpServletRequest multipartHttpServletRequest + ) { + return ResponseEntity.ok(fileService.insertFileSunEditorUpload(folder, multipartHttpServletRequest)); + } + + @Operation(summary = "파일 보기", description = "uploadPath 기준으로 파일을 inline 응답합니다.") + @GetMapping("/view") + public ResponseEntity fileView(@Parameter(description = "업로드 파일 경로", example = "/uploads/notice/2026/07/20/a1b2c3d4.png", required = true) @RequestParam String path) { + return fileService.selectFileView(path); + } + + @Operation(summary = "파일 다운로드", description = "uploadPath 기준으로 파일을 attachment 응답합니다.") + @GetMapping("/download") + public ResponseEntity fileDownload(@Parameter(description = "업로드 파일 경로", example = "/uploads/notice/2026/07/20/a1b2c3d4.png", required = true) @RequestParam String path) { + return fileService.selectFileDownload(path); + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/file/form/SunEditorUploadForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/file/form/SunEditorUploadForm.java new file mode 100644 index 0000000..1f597fa --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/file/form/SunEditorUploadForm.java @@ -0,0 +1,17 @@ +package com.alist.api.front.modules.file.form; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@Schema(description = "SunEditor 이미지 업로드 요청") +@Getter +@Setter +public class SunEditorUploadForm { + @ArraySchema(schema = @Schema(type = "string", format = "binary", description = "업로드 이미지 파일")) + private List files; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/file/service/FileService.java b/03-api-front/src/main/java/com/alist/api/front/modules/file/service/FileService.java new file mode 100644 index 0000000..b4f66ce --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/file/service/FileService.java @@ -0,0 +1,58 @@ +package com.alist.api.front.modules.file.service; + +import com.alist.api.core.common.file.service.FileStorageService; +import com.alist.api.core.common.file.vo.FileStorageUploadVo; +import com.alist.api.front.modules.file.vo.FileUploadVo; +import com.alist.api.front.modules.file.vo.SunEditorUploadVo; +import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class FileService { + private final FileStorageService fileStorageService; + + public FileService(FileStorageService fileStorageService) { + this.fileStorageService = fileStorageService; + } + + public FileUploadVo insertFileUpload(String folder, MultipartFile multipartFile) { + FileStorageUploadVo fileStorageUploadVo = fileStorageService.saveFileUpload(folder, multipartFile); + FileUploadVo fileUploadVo = new FileUploadVo(); + fileUploadVo.setUploadPath(fileStorageUploadVo.getUploadPath()); + fileUploadVo.setOriginalFileName(fileStorageUploadVo.getOriginalFileName()); + fileUploadVo.setStoredFileName(fileStorageUploadVo.getStoredFileName()); + fileUploadVo.setFileExtension(fileStorageUploadVo.getFileExtension()); + fileUploadVo.setContentType(fileStorageUploadVo.getContentType()); + fileUploadVo.setFileSize(fileStorageUploadVo.getFileSize()); + fileUploadVo.setWidth(fileStorageUploadVo.getWidth()); + fileUploadVo.setHeight(fileStorageUploadVo.getHeight()); + + return fileUploadVo; + } + + public SunEditorUploadVo insertFileSunEditorUpload(String folder, MultipartHttpServletRequest multipartHttpServletRequest) { + List sunEditorUploadItemList = new ArrayList<>(); + + for (MultipartFile multipartFile : multipartHttpServletRequest.getFileMap().values()) { + FileStorageUploadVo fileStorageUploadVo = fileStorageService.saveFileUpload(folder, multipartFile); + String fileViewUrl = fileStorageService.buildFileViewUrl(fileStorageUploadVo.getUploadPath()); + sunEditorUploadItemList.add(new SunEditorUploadVo.Item(fileViewUrl, fileStorageUploadVo.getOriginalFileName(), fileStorageUploadVo.getFileSize())); + } + + return new SunEditorUploadVo(sunEditorUploadItemList); + } + + public ResponseEntity selectFileView(String path) { + return fileStorageService.loadFileResource(path, true); + } + + public ResponseEntity selectFileDownload(String path) { + return fileStorageService.loadFileResource(path, false); + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/file/vo/FileUploadVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/file/vo/FileUploadVo.java new file mode 100644 index 0000000..209b426 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/file/vo/FileUploadVo.java @@ -0,0 +1,34 @@ +package com.alist.api.front.modules.file.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "파일 업로드 응답") +@Getter +@Setter +public class FileUploadVo { + @Schema(description = "업로드 파일 접근 경로", example = "/uploads/notice/2026/07/20/a1b2c3d4.png") + private String uploadPath; + + @Schema(description = "원본 파일명", example = "sample.png") + private String originalFileName; + + @Schema(description = "저장 파일명", example = "a1b2c3d4.png") + private String storedFileName; + + @Schema(description = "파일 확장자", example = "png") + private String fileExtension; + + @Schema(description = "Content-Type", example = "image/png") + private String contentType; + + @Schema(description = "파일 크기(byte)", example = "102400") + private Long fileSize; + + @Schema(description = "이미지 너비(px)", example = "800") + private Integer width; + + @Schema(description = "이미지 높이(px)", example = "600") + private Integer height; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/file/vo/SunEditorUploadVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/file/vo/SunEditorUploadVo.java new file mode 100644 index 0000000..ccd5b89 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/file/vo/SunEditorUploadVo.java @@ -0,0 +1,39 @@ +package com.alist.api.front.modules.file.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "SunEditor 이미지 업로드 응답") +@Getter +@Setter +public class SunEditorUploadVo { + @Schema(description = "업로드 결과 목록") + private List result; + + public SunEditorUploadVo(List result) { + this.result = result; + } + + @Schema(description = "SunEditor 업로드 파일 항목") + @Getter + @Setter + public static class Item { + @Schema(description = "업로드 파일 URL", example = "https://file-alist.pjt.kr/uploads/editor/2026/07/20/a1b2c3d4.png") + private String url; + + @Schema(description = "원본 파일명", example = "sample.png") + private String name; + + @Schema(description = "파일 크기(byte)", example = "102400") + private Long size; + + public Item(String url, String name, Long size) { + this.url = url; + this.name = name; + this.size = size; + } + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/main/MainController.java b/03-api-front/src/main/java/com/alist/api/front/modules/main/MainController.java new file mode 100644 index 0000000..57314e2 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/main/MainController.java @@ -0,0 +1,15 @@ +package com.alist.api.front.modules.main; + +import io.swagger.v3.oas.annotations.Hidden; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Hidden +@Controller +public class MainController { + + @GetMapping(value = "/") + public String index() { + return "redirect:/swagger-ui/index.html"; + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/sso/SsoController.java b/03-api-front/src/main/java/com/alist/api/front/modules/sso/SsoController.java new file mode 100644 index 0000000..891b22b --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/sso/SsoController.java @@ -0,0 +1,106 @@ +package com.alist.api.front.modules.sso; + +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import com.alist.api.core.common.utils.SessionUtil; +import com.alist.api.front.modules.sso.form.SsoExchangeForm; +import com.alist.api.front.modules.sso.form.SsoLoginForm; +import com.alist.api.front.modules.sso.service.SsoService; +import com.alist.api.front.modules.sso.vo.SsoExchangeVo; +import com.alist.api.front.modules.sso.vo.SsoLoginCheckedVo; +import com.alist.api.front.modules.sso.vo.SsoLoginVo; +import com.alist.api.front.modules.sso.vo.SsoLogoutVo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.net.URI; + +@Tag(name = "A-1. SSO 인증", description = "공통 로그인, 인가 코드 발급, 코드 교환, 로그아웃 API") +@RestController +@RequestMapping("/sso") +public class SsoController { + @Value("${sso.cookie.name}") + private String ssoCookieName; + + private final SsoService ssoService; + + public SsoController(SsoService ssoService) { + this.ssoService = ssoService; + } + + @Operation(summary = "SSO 로그인", description = "아이디와 비밀번호를 검증하고 공통 SSO HttpOnly 쿠키를 발급합니다.") + @PostMapping("/login") + public ResponseEntity> ssoLogin( + @Valid @RequestBody SsoLoginForm ssoLoginForm, + HttpServletResponse response + ) { + SsoLoginVo ssoLoginVo = ssoService.selectSsoLogin(ssoLoginForm, response); + + if (ssoLoginVo.getResultCode() == 2003) { + return ApiResponse.entity(ssoLoginVo, ApiResponseCode.CODE_2003); + } + + return ApiResponse.entity(ssoLoginVo, ApiResponseCode.CODE_2001, "SSO 로그인"); + } + + @Operation(summary = "SSO 로그인 상태 확인", description = "SSO 세션과 Access/Refresh Token 쿠키 존재 여부를 조회합니다.") + @GetMapping("/loginChecked") + public ResponseEntity> ssoLoginChecked( + HttpServletRequest request, + HttpServletResponse response + ) { + SsoLoginCheckedVo ssoLoginCheckedVo = ssoService.selectSsoLoginChecked(request, response); + + return ApiResponse.entity(ssoLoginCheckedVo, ApiResponseCode.CODE_200); + } + + @Operation(summary = "SSO 인가 코드 발급", description = "SSO 세션을 확인하고 등록된 서비스의 redirect URI로 1회용 인가 코드를 전달합니다.") + @GetMapping("/authorize") + public ResponseEntity ssoAuthorize( + @RequestParam String clientId, + @RequestParam String redirectUri, + @RequestParam(required = false) String state, + HttpServletRequest request + ) { + String ssoSessionId = SessionUtil.resolveSsoCookieValue(request, ssoCookieName); + String redirectUrl = ssoService.selectSsoAuthorize(clientId, redirectUri, state, ssoSessionId); + + return ResponseEntity.status(302).location(URI.create(redirectUrl)).build(); + } + + @Operation(summary = "SSO 인가 코드 교환", description = "1회용 인가 코드를 검증하고 서비스 세션 생성에 필요한 사용자 정보를 반환합니다.") + @PostMapping("/exchange") + public ResponseEntity> ssoExchange( + @Valid @RequestBody SsoExchangeForm ssoExchangeForm, + HttpServletResponse response + ) { + SsoExchangeVo ssoExchangeVo = ssoService.selectSsoExchange(ssoExchangeForm, response); + + return ApiResponse.entity(ssoExchangeVo, ApiResponseCode.CODE_2001, "SSO code 교환"); + } + + @Operation(summary = "SSO 로그아웃", description = "SSO 세션과 USER Refresh Token을 무효화하고 관련 쿠키를 삭제합니다.") + @PostMapping("/logout") + public ResponseEntity> ssoLogout( + HttpServletRequest request, + HttpServletResponse response, + @CookieValue(name = "refreshToken", required = false) String refreshToken + ) { + String ssoSessionId = SessionUtil.resolveSsoCookieValue(request, ssoCookieName); + SsoLogoutVo ssoLogoutVo = ssoService.updateSsoLogout(ssoSessionId, refreshToken, response); + + return ApiResponse.entity(ssoLogoutVo, ApiResponseCode.CODE_200); + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/sso/form/SsoExchangeForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/sso/form/SsoExchangeForm.java new file mode 100644 index 0000000..19dd97e --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/sso/form/SsoExchangeForm.java @@ -0,0 +1,19 @@ +package com.alist.api.front.modules.sso.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "SSO 인가 코드 교환 요청") +@Getter +@Setter +public class SsoExchangeForm { + @Schema(description = "SSO 인가 코드", example = "CODE_123e4567-e89b-12d3-a456-426614174000", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "인가 코드는 필수입니다.") + private String code; + + @Schema(description = "SSO 클라이언트 ID", example = "alist-main", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "클라이언트 ID는 필수입니다.") + private String clientId; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/sso/form/SsoLoginForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/sso/form/SsoLoginForm.java new file mode 100644 index 0000000..5d3bfe9 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/sso/form/SsoLoginForm.java @@ -0,0 +1,19 @@ +package com.alist.api.front.modules.sso.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "SSO 로그인 요청") +@Getter +@Setter +public class SsoLoginForm { + @Schema(description = "회원 아이디", example = "teacher01", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "아이디는 필수입니다.") + private String id; + + @Schema(description = "비밀번호", example = "password1234", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "비밀번호는 필수입니다.") + private String password; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/sso/service/SsoService.java b/03-api-front/src/main/java/com/alist/api/front/modules/sso/service/SsoService.java new file mode 100644 index 0000000..bb7f2ad --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/sso/service/SsoService.java @@ -0,0 +1,311 @@ +package com.alist.api.front.modules.sso.service; + +import com.alist.api.core.common.jwt.JwtTokenProvider; +import com.alist.api.core.common.utils.SessionUtil; +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserSsoLoginViewDto; +import com.alist.api.core.modules.bespoke.testUser.service.TestUserSsoService; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserSsoLoginViewVo; +import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientAuthorizeViewDto; +import com.alist.api.core.modules.standard.ssoClient.service.SsoClientService; +import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientAuthorizeViewVo; +import com.alist.api.core.modules.standard.testUser.dto.TestUserModifyDto; +import com.alist.api.core.modules.standard.testUser.service.TestUserService; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenModifyDto; +import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService; +import com.alist.api.front.modules.sso.form.SsoExchangeForm; +import com.alist.api.front.modules.sso.form.SsoLoginForm; +import com.alist.api.front.modules.sso.vo.SsoExchangeVo; +import com.alist.api.front.modules.sso.vo.SsoLoginCheckedVo; +import com.alist.api.front.modules.sso.vo.SsoLoginVo; +import com.alist.api.front.modules.sso.vo.SsoLogoutVo; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.UUID; + +@Service +public class SsoService { + private static final String SSO_SESSION_KEY_PREFIX = "alist:sso:session:"; + private static final String SSO_USER_KEY_PREFIX = "alist:sso:userIdx:"; + private static final String SSO_CODE_KEY_PREFIX = "alist:sso:code:"; + private static final String USER_SCOPE = "USER"; + private static final String REFRESH_TOKEN_TYPE = "REFRESH"; + + @Value("${cookie.secure}") + private boolean cookieSecure; + + @Value("${cookie.domain:}") + private String cookieDomain; + + @Value("${cookie.same-site:Lax}") + private String cookieSameSite; + + @Value("${sso.session.ttl-seconds}") + private long ssoSessionTtlSeconds; + + @Value("${sso.code.ttl-seconds}") + private long ssoCodeTtlSeconds; + + @Value("${sso.cookie.name}") + private String ssoCookieName; + + private final TestUserSsoService testUserSsoService; + private final SsoClientService ssoClientService; + private final TestUserService testUserService; + private final TestUserTokenService testUserTokenService; + private final StringRedisTemplate stringRedisTemplate; + private final PasswordEncoder passwordEncoder; + private final JwtTokenProvider jwtTokenProvider; + + public SsoService( + TestUserSsoService testUserSsoService, + SsoClientService ssoClientService, + TestUserService testUserService, + TestUserTokenService testUserTokenService, + StringRedisTemplate stringRedisTemplate, + PasswordEncoder passwordEncoder, + JwtTokenProvider jwtTokenProvider + ) { + this.testUserSsoService = testUserSsoService; + this.ssoClientService = ssoClientService; + this.testUserService = testUserService; + this.testUserTokenService = testUserTokenService; + this.stringRedisTemplate = stringRedisTemplate; + this.passwordEncoder = passwordEncoder; + this.jwtTokenProvider = jwtTokenProvider; + } + + @Transactional + public SsoLoginVo selectSsoLogin(SsoLoginForm ssoLoginForm, HttpServletResponse response) { + TestUserSsoLoginViewDto testUserSsoLoginViewDto = new TestUserSsoLoginViewDto(); + testUserSsoLoginViewDto.setId(ssoLoginForm.getId().trim()); + + TestUserSsoLoginViewVo testUserSsoLoginViewVo = testUserSsoService.testUserSsoLoginView(testUserSsoLoginViewDto); + + if (testUserSsoLoginViewVo == null + || !passwordEncoder.matches(ssoLoginForm.getPassword(), testUserSsoLoginViewVo.getPassword())) { + SsoLoginVo ssoLoginVo = new SsoLoginVo(); + ssoLoginVo.setLogin(false); + ssoLoginVo.setResultCode(2003); + + return ssoLoginVo; + } + + String ssoSessionId = "SSO_" + UUID.randomUUID(); + String ssoSessionKey = SSO_SESSION_KEY_PREFIX + ssoSessionId; + String ssoUserKey = SSO_USER_KEY_PREFIX + testUserSsoLoginViewVo.getUserIdx(); + String previousSsoSessionId = stringRedisTemplate.opsForValue().get(ssoUserKey); + + if (previousSsoSessionId != null && !previousSsoSessionId.isBlank()) { + stringRedisTemplate.delete(SSO_SESSION_KEY_PREFIX + previousSsoSessionId); + } + + stringRedisTemplate.opsForHash().put(ssoSessionKey, "userIdx", String.valueOf(testUserSsoLoginViewVo.getUserIdx())); + stringRedisTemplate.opsForHash().put(ssoSessionKey, "userTokenIdx", String.valueOf(testUserSsoLoginViewVo.getUserTokenIdx())); + stringRedisTemplate.opsForHash().put(ssoSessionKey, "userId", testUserSsoLoginViewVo.getId()); + stringRedisTemplate.opsForHash().put(ssoSessionKey, "userRole", testUserSsoLoginViewVo.getUserRole()); + stringRedisTemplate.opsForHash().put(ssoSessionKey, "userType", testUserSsoLoginViewVo.getUserType()); + stringRedisTemplate.expire(ssoSessionKey, java.time.Duration.ofSeconds(ssoSessionTtlSeconds)); + stringRedisTemplate.opsForValue().set(ssoUserKey, ssoSessionId, java.time.Duration.ofSeconds(ssoSessionTtlSeconds)); + + TestUserModifyDto testUserModifyDto = new TestUserModifyDto(); + testUserModifyDto.setUserIdx(testUserSsoLoginViewVo.getUserIdx()); + testUserModifyDto.setLastLoginAt(LocalDateTime.now()); + testUserService.updateTestUserModify(testUserModifyDto); + + SessionUtil.addTokenCookie(response, ssoCookieName, ssoSessionId, cookieDomain, cookieSecure, cookieSameSite, ssoSessionTtlSeconds); + + SsoLoginVo ssoLoginVo = new SsoLoginVo(); + ssoLoginVo.setLogin(true); + ssoLoginVo.setId(testUserSsoLoginViewVo.getId()); + ssoLoginVo.setUserIdx(testUserSsoLoginViewVo.getUserIdx()); + ssoLoginVo.setUserRole(testUserSsoLoginViewVo.getUserRole()); + ssoLoginVo.setUserType(testUserSsoLoginViewVo.getUserType()); + ssoLoginVo.setResultCode(2001); + + return ssoLoginVo; + } + + @Transactional(readOnly = true) + public SsoLoginCheckedVo selectSsoLoginChecked(HttpServletRequest request, HttpServletResponse response) { + String ssoSessionId = SessionUtil.resolveSsoCookieValue(request, ssoCookieName); + String accessToken = SessionUtil.resolveSsoCookieValue(request, "accessToken"); + String refreshToken = SessionUtil.resolveSsoCookieValue(request, "refreshToken"); + + SsoLoginCheckedVo ssoLoginCheckedVo = new SsoLoginCheckedVo(); + ssoLoginCheckedVo.setAccessToken(accessToken != null && !accessToken.isBlank()); + ssoLoginCheckedVo.setRefreshToken(refreshToken != null && !refreshToken.isBlank()); + + if (ssoSessionId == null || ssoSessionId.isBlank()) { + ssoLoginCheckedVo.setLoggedIn(false); + + return ssoLoginCheckedVo; + } + + String ssoSessionKey = SSO_SESSION_KEY_PREFIX + ssoSessionId; + String userIdx = (String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userIdx"); + + if (userIdx == null || userIdx.isBlank()) { + ssoLoginCheckedVo.setLoggedIn(false); + + return ssoLoginCheckedVo; + } + + String latestSsoSessionId = stringRedisTemplate.opsForValue().get(SSO_USER_KEY_PREFIX + userIdx); + if (!ssoSessionId.equals(latestSsoSessionId)) { + ssoLoginCheckedVo.setLoggedIn(false); + + return ssoLoginCheckedVo; + } + + refreshSsoSessionTtl(ssoSessionId, userIdx); + SessionUtil.addTokenCookie(response, ssoCookieName, ssoSessionId, cookieDomain, cookieSecure, cookieSameSite, ssoSessionTtlSeconds); + + ssoLoginCheckedVo.setLoggedIn(true); + ssoLoginCheckedVo.setSsoSessionId(ssoSessionId); + ssoLoginCheckedVo.setUserIdx(Integer.valueOf(userIdx)); + ssoLoginCheckedVo.setUserTokenIdx(Integer.valueOf((String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userTokenIdx"))); + ssoLoginCheckedVo.setUserId((String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userId")); + ssoLoginCheckedVo.setUserRole((String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userRole")); + ssoLoginCheckedVo.setUserType((String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userType")); + + return ssoLoginCheckedVo; + } + + @Transactional(readOnly = true) + public String selectSsoAuthorize(String clientId, String redirectUri, String state, String ssoSessionId) { + if (ssoSessionId == null || ssoSessionId.isBlank()) { + return "/login"; + } + + String ssoSessionKey = SSO_SESSION_KEY_PREFIX + ssoSessionId; + String userIdx = (String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userIdx"); + + if (userIdx == null || userIdx.isBlank()) { + return "/login"; + } + + String latestSsoSessionId = stringRedisTemplate.opsForValue().get(SSO_USER_KEY_PREFIX + userIdx); + if (!ssoSessionId.equals(latestSsoSessionId)) { + return "/login"; + } + + SsoClientAuthorizeViewDto ssoClientAuthorizeViewDto = new SsoClientAuthorizeViewDto(); + ssoClientAuthorizeViewDto.setClientId(clientId.trim()); + ssoClientAuthorizeViewDto.setRedirectUri(redirectUri.trim()); + + SsoClientAuthorizeViewVo ssoClientAuthorizeViewVo = ssoClientService.selectSsoClientAuthorizeView(ssoClientAuthorizeViewDto); + if (ssoClientAuthorizeViewVo == null) { + throw new IllegalArgumentException("허용되지 않은 clientId 또는 redirectUri 입니다."); + } + + refreshSsoSessionTtl(ssoSessionId, userIdx); + + String code = "CODE_" + UUID.randomUUID(); + String ssoCodeKey = SSO_CODE_KEY_PREFIX + code; + + stringRedisTemplate.opsForHash().put(ssoCodeKey, "ssoSessionId", ssoSessionId); + stringRedisTemplate.opsForHash().put(ssoCodeKey, "clientId", ssoClientAuthorizeViewVo.getClientId()); + stringRedisTemplate.opsForHash().put(ssoCodeKey, "redirectUri", ssoClientAuthorizeViewVo.getRedirectUri()); + stringRedisTemplate.expire(ssoCodeKey, java.time.Duration.ofSeconds(ssoCodeTtlSeconds)); + + URI uri = URI.create(ssoClientAuthorizeViewVo.getRedirectUri()); + StringBuilder redirectUrl = new StringBuilder(uri.getScheme() + "://" + uri.getAuthority() + uri.getPath()); + String encodedCode = URLEncoder.encode(code, StandardCharsets.UTF_8); + + if (uri.getQuery() == null || uri.getQuery().isBlank()) { + redirectUrl.append("?code=").append(encodedCode); + } else { + redirectUrl.append("?").append(uri.getQuery()).append("&code=").append(encodedCode); + } + + if (state != null && !state.isBlank()) { + redirectUrl.append("&state=").append(URLEncoder.encode(state, StandardCharsets.UTF_8)); + } + + return redirectUrl.toString(); + } + + @Transactional(readOnly = true) + public SsoExchangeVo selectSsoExchange(SsoExchangeForm ssoExchangeForm, HttpServletResponse response) { + String ssoCodeKey = SSO_CODE_KEY_PREFIX + ssoExchangeForm.getCode().trim(); + String ssoSessionId = (String) stringRedisTemplate.opsForHash().get(ssoCodeKey, "ssoSessionId"); + String clientId = (String) stringRedisTemplate.opsForHash().get(ssoCodeKey, "clientId"); + + if (ssoSessionId == null || !ssoExchangeForm.getClientId().trim().equals(clientId)) { + throw new IllegalArgumentException("유효하지 않은 code 입니다."); + } + + String ssoSessionKey = SSO_SESSION_KEY_PREFIX + ssoSessionId; + String userIdx = (String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userIdx"); + String latestSsoSessionId = stringRedisTemplate.opsForValue().get(SSO_USER_KEY_PREFIX + userIdx); + + if (!ssoSessionId.equals(latestSsoSessionId)) { + throw new IllegalArgumentException("다른 곳에서 다시 로그인되어 세션이 만료되었습니다."); + } + + refreshSsoSessionTtl(ssoSessionId, userIdx); + stringRedisTemplate.delete(ssoCodeKey); + SessionUtil.addTokenCookie(response, ssoCookieName, ssoSessionId, cookieDomain, cookieSecure, cookieSameSite, ssoSessionTtlSeconds); + + SsoExchangeVo ssoExchangeVo = new SsoExchangeVo(); + ssoExchangeVo.setSsoSessionId(ssoSessionId); + ssoExchangeVo.setUserIdx(Integer.valueOf(userIdx)); + ssoExchangeVo.setUserTokenIdx(Integer.valueOf((String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userTokenIdx"))); + ssoExchangeVo.setUserId((String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userId")); + ssoExchangeVo.setUserRole((String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userRole")); + ssoExchangeVo.setUserType((String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userType")); + + return ssoExchangeVo; + } + + @Transactional + public SsoLogoutVo updateSsoLogout(String ssoSessionId, String refreshToken, HttpServletResponse response) { + if (ssoSessionId != null && !ssoSessionId.isBlank()) { + String ssoSessionKey = SSO_SESSION_KEY_PREFIX + ssoSessionId; + String userIdx = (String) stringRedisTemplate.opsForHash().get(ssoSessionKey, "userIdx"); + + stringRedisTemplate.delete(ssoSessionKey); + + if (userIdx != null && !userIdx.isBlank()) { + stringRedisTemplate.delete(SSO_USER_KEY_PREFIX + userIdx); + } + } + + if (refreshToken != null + && jwtTokenProvider.validateToken(refreshToken) + && USER_SCOPE.equals(jwtTokenProvider.getScope(refreshToken)) + && REFRESH_TOKEN_TYPE.equals(jwtTokenProvider.getTokenType(refreshToken))) { + try { + TestUserTokenModifyDto testUserTokenModifyDto = new TestUserTokenModifyDto(); + testUserTokenModifyDto.setUserTokenIdx(Integer.valueOf(jwtTokenProvider.getUserTokenIdx(refreshToken))); + testUserTokenModifyDto.setRefreshToken(""); + testUserTokenService.updateTestUserTokenModify(testUserTokenModifyDto); + } catch (NumberFormatException ignored) { + } + } + + SessionUtil.expireCookie(response, ssoCookieName, cookieDomain, cookieSecure, cookieSameSite); + SessionUtil.expireCookie(response, "accessToken", cookieDomain, cookieSecure, cookieSameSite); + SessionUtil.expireCookie(response, "refreshToken", cookieDomain, cookieSecure, cookieSameSite); + + SsoLogoutVo ssoLogoutVo = new SsoLogoutVo(); + ssoLogoutVo.setLoggedOut(true); + + return ssoLogoutVo; + } + + private void refreshSsoSessionTtl(String ssoSessionId, String userIdx) { + stringRedisTemplate.expire(SSO_SESSION_KEY_PREFIX + ssoSessionId, java.time.Duration.ofSeconds(ssoSessionTtlSeconds)); + stringRedisTemplate.expire(SSO_USER_KEY_PREFIX + userIdx, java.time.Duration.ofSeconds(ssoSessionTtlSeconds)); + } +} diff --git a/src/main/java/com/alist/api/modules/front/auth/dto/SsoExchangeDto.java b/03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoExchangeVo.java similarity index 55% rename from src/main/java/com/alist/api/modules/front/auth/dto/SsoExchangeDto.java rename to 03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoExchangeVo.java index 6b7dc0c..6712b53 100644 --- a/src/main/java/com/alist/api/modules/front/auth/dto/SsoExchangeDto.java +++ b/03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoExchangeVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.front.auth.dto; +package com.alist.api.front.modules.sso.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -7,25 +7,22 @@ import lombok.Setter; @Schema(description = "SSO 인가 코드 교환 응답") @Getter @Setter -public class SsoExchangeDto { - - private String code; - private String clientId; - +public class SsoExchangeVo { @Schema(description = "SSO 세션 ID") private String ssoSessionId; @Schema(description = "회원 PK", example = "1") private Integer userIdx; + @Schema(description = "회원 토큰 PK", example = "1") private Integer userTokenIdx; - @Schema(description = "회원 아이디", example = "test") + @Schema(description = "회원 아이디", example = "teacher01") private String userId; - @Schema(description = "회원 권한 코드(CCD/SCM/I/R/E)", example = "SCM") + @Schema(description = "회원 권한", example = "TEACHER") private String userRole; - @Schema(description = "회원 유형 코드. T: 교사, S: 학생", example = "T") + @Schema(description = "회원 유형", example = "T") private String userType; } diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/SsoLoginCheckedVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoLoginCheckedVo.java similarity index 72% rename from src/main/java/com/alist/api/modules/front/auth/vo/SsoLoginCheckedVo.java rename to 03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoLoginCheckedVo.java index f2ee1a8..e3d7f2e 100644 --- a/src/main/java/com/alist/api/modules/front/auth/vo/SsoLoginCheckedVo.java +++ b/03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoLoginCheckedVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.front.auth.vo; +package com.alist.api.front.modules.sso.vo; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -17,13 +17,13 @@ public class SsoLoginCheckedVo { @Schema(description = "Refresh Token 쿠키 존재 여부", example = "true") private boolean refreshToken; - @Schema(description = "로그인 상태 여부", example = "true") + @Schema(description = "SSO 로그인 상태", example = "true") private boolean loggedIn; @Schema(description = "SSO 세션 ID") private String ssoSessionId; - @Schema(description = "회원 아이디", example = "test") + @Schema(description = "회원 아이디", example = "teacher01") private String userId; @Schema(description = "회원 PK", example = "1") @@ -32,9 +32,9 @@ public class SsoLoginCheckedVo { @Schema(description = "회원 토큰 PK", example = "1") private Integer userTokenIdx; - @Schema(description = "회원 권한 코드", example = "CCD/SCM/I/R/E") + @Schema(description = "회원 권한", example = "TEACHER") private String userRole; - @Schema(description = "회원 유형 코드. T: 교사, S: 학생", example = "S") + @Schema(description = "회원 유형", example = "T") private String userType; } diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/SsoLoginVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoLoginVo.java similarity index 51% rename from src/main/java/com/alist/api/modules/front/auth/vo/SsoLoginVo.java rename to 03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoLoginVo.java index ca33a8d..8bd79a0 100644 --- a/src/main/java/com/alist/api/modules/front/auth/vo/SsoLoginVo.java +++ b/03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoLoginVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.front.auth.vo; +package com.alist.api.front.modules.sso.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; @@ -10,32 +10,20 @@ import lombok.Setter; @Setter public class SsoLoginVo { @Schema(description = "로그인 성공 여부", example = "true") - private boolean loginIn; + private boolean login; - @Schema(description = "회원 아이디", example = "test") + @Schema(description = "회원 아이디", example = "teacher01") private String id; @Schema(description = "회원 PK", example = "1") private Integer userIdx; - @Schema(description = "회원 권한 코드", example = "CCD/SCM/I/R/E") + @Schema(description = "회원 권한", example = "TEACHER") private String userRole; - @Schema(description = "회원 유형 코드. T: 교사, S: 학생", example = "S") + @Schema(description = "회원 유형", example = "T") private String userType; - @JsonIgnore - @Schema(hidden = true) - private String ssoSessionId; - - @JsonIgnore - @Schema(hidden = true) - private Integer userTokenIdx; - - @JsonIgnore - @Schema(hidden = true) - private String userId; - @JsonIgnore @Schema(hidden = true) private int resultCode; diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/SsoLogoutVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoLogoutVo.java similarity index 59% rename from src/main/java/com/alist/api/modules/front/auth/vo/SsoLogoutVo.java rename to 03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoLogoutVo.java index 9e62e5d..c0f77b8 100644 --- a/src/main/java/com/alist/api/modules/front/auth/vo/SsoLogoutVo.java +++ b/03-api-front/src/main/java/com/alist/api/front/modules/sso/vo/SsoLogoutVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.front.auth.vo; +package com.alist.api.front.modules.sso.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -8,6 +8,6 @@ import lombok.Setter; @Getter @Setter public class SsoLogoutVo { - @Schema(description = "로그아웃 처리 여부", example = "true") - private boolean logout; + @Schema(description = "로그아웃 완료 여부", example = "true") + private boolean loggedOut; } diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/UserController.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/UserController.java new file mode 100644 index 0000000..e159074 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/UserController.java @@ -0,0 +1,85 @@ +package com.alist.api.front.modules.user; + +import com.alist.api.core.common.response.ApiResponse; +import com.alist.api.core.common.response.ApiResponseCode; +import com.alist.api.front.modules.user.form.*; +import com.alist.api.front.modules.user.service.UserService; +import com.alist.api.front.modules.user.vo.*; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "A-3. 사용자 관리", description = "사용자 회원가입 및 개인정보 관리 API") +@RestController +@RequestMapping("/user") +public class UserController { + private final UserService userService; + + public UserController(UserService userService) { this.userService = userService; } + + @Operation(summary = "사용자 회원가입", description = "새 사용자와 사용자 API Key를 등록합니다.") + @PostMapping("/add") + public ResponseEntity> userAdd(@Valid @RequestBody UserAddForm userAddForm) { + UserAddVo userAddVo = userService.insertUserAdd(userAddForm); + if (userAddVo.getResultCode() == 2004) return ApiResponse.entity(userAddVo, ApiResponseCode.CODE_2004, "아이디"); + return ApiResponse.entity(userAddVo, ApiResponseCode.CODE_2002, "사용자"); + } + + @Operation(summary = "이전 회원 정보 조회", description = "ALIST와 ELTOWN의 이전 회원 정보를 조회합니다.") + @PostMapping("/migration/list") + public ResponseEntity> userMigrationList(@Valid @RequestBody UserMigrationListForm userMigrationListForm) { + UserMigrationListVo userMigrationListVo = userService.selectUserMigrationList(userMigrationListForm); + if (userMigrationListVo.getAlistUserList().isEmpty() && userMigrationListVo.getEltownUserList().isEmpty()) return ApiResponse.entity(userMigrationListVo, ApiResponseCode.CODE_2003); + return ApiResponse.entity(userMigrationListVo, ApiResponseCode.CODE_2001, "이전 회원"); + } + + @Operation(summary = "사용자 개인정보 조회", description = "로그인한 사용자의 개인정보를 조회합니다.") + @GetMapping("/profile/view") + public ResponseEntity> userProfileView() { + UserProfileViewVo userProfileViewVo = userService.selectUserProfileView(); + if (userProfileViewVo == null) return ApiResponse.entity(new UserProfileViewVo(), ApiResponseCode.CODE_2003); + return ApiResponse.entity(userProfileViewVo, ApiResponseCode.CODE_2001, "개인정보"); + } + + @Operation(summary = "사용자 비밀번호 확인", description = "로그인한 사용자의 현재 비밀번호를 확인합니다.") + @PostMapping("/password/check") + public ResponseEntity> userPasswordCheck(@Valid @RequestBody UserPasswordCheckForm userPasswordCheckForm) { + UserPasswordCheckVo userPasswordCheckVo = userService.selectUserPasswordCheck(userPasswordCheckForm); + if (!userPasswordCheckVo.isPasswordMatched()) return ApiResponse.entity(userPasswordCheckVo, userPasswordCheckVo.getResultCode() == 4003 ? ApiResponseCode.CODE_4001 : ApiResponseCode.CODE_2003); + return ApiResponse.entity(userPasswordCheckVo, ApiResponseCode.CODE_200); + } + + @Operation(summary = "사용자 비밀번호 수정", description = "현재 비밀번호 확인 후 새 비밀번호로 변경합니다.") + @PatchMapping("/password/modify") + public ResponseEntity> userPasswordModify(@Valid @RequestBody UserPasswordModifyForm userPasswordModifyForm) { + UserPasswordModifyVo userPasswordModifyVo = userService.updateUserPasswordModify(userPasswordModifyForm); + if (!userPasswordModifyVo.isPasswordChanged()) return ApiResponse.entity(userPasswordModifyVo, userPasswordModifyVo.getResultCode() == 4001 || userPasswordModifyVo.getResultCode() == 4003 ? ApiResponseCode.CODE_4001 : ApiResponseCode.CODE_2003); + return ApiResponse.entity(userPasswordModifyVo, ApiResponseCode.CODE_2005, "비밀번호 변경"); + } + + @Operation(summary = "사용자 개인정보 수정", description = "현재 비밀번호 확인 후 이메일과 휴대폰 번호를 수정합니다.") + @PatchMapping("/profile/modify") + public ResponseEntity> userProfileModify(@Valid @RequestBody UserProfileModifyForm userProfileModifyForm) { + UserProfileModifyVo userProfileModifyVo = userService.updateUserProfileModify(userProfileModifyForm); + if (!userProfileModifyVo.isProfileUpdated()) return ApiResponse.entity(userProfileModifyVo, userProfileModifyVo.getResultCode() == 4001 || userProfileModifyVo.getResultCode() == 4003 ? ApiResponseCode.CODE_4001 : ApiResponseCode.CODE_2003); + return ApiResponse.entity(userProfileModifyVo, ApiResponseCode.CODE_2005, "개인정보 수정"); + } + + @Operation(summary = "사용자 탈퇴 상태 변경", description = "탈퇴 요청(P) 또는 철회(N)를 처리합니다.") + @PatchMapping("/withdraw/modify") + public ResponseEntity> userWithdrawModify(@Valid @RequestBody UserWithdrawModifyForm userWithdrawModifyForm) { + UserWithdrawModifyVo userWithdrawModifyVo = userService.updateUserWithdrawModify(userWithdrawModifyForm); + if (!userWithdrawModifyVo.isWithdrawStatusUpdated()) return ApiResponse.entity(userWithdrawModifyVo, userWithdrawModifyVo.getResultCode() == 4003 ? ApiResponseCode.CODE_4001 : ApiResponseCode.CODE_2003); + return ApiResponse.entity(userWithdrawModifyVo, ApiResponseCode.CODE_2005, "탈퇴 상태 변경"); + } + + @Operation(summary = "사용자 휴면 상태 변경", description = "휴면 처리(Y) 또는 해지(N)를 처리합니다.") + @PatchMapping("/dormant/modify") + public ResponseEntity> userDormantModify(@Valid @RequestBody UserDormantModifyForm userDormantModifyForm) { + UserDormantModifyVo userDormantModifyVo = userService.updateUserDormantModify(userDormantModifyForm); + if (!userDormantModifyVo.isDormantStatusUpdated()) return ApiResponse.entity(userDormantModifyVo, userDormantModifyVo.getResultCode() == 4003 ? ApiResponseCode.CODE_4001 : ApiResponseCode.CODE_2003); + return ApiResponse.entity(userDormantModifyVo, ApiResponseCode.CODE_2005, "휴면 상태 변경"); + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserAddForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserAddForm.java new file mode 100644 index 0000000..b8d24a2 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserAddForm.java @@ -0,0 +1,22 @@ +package com.alist.api.front.modules.user.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 회원가입 요청") +@Getter +@Setter +public class UserAddForm { + @Schema(description = "사용자 아이디", example = "teacher02", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "아이디를 입력해주세요.") private String id; + @Schema(description = "비밀번호", example = "pass1234", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "비밀번호를 입력해주세요.") @Size(min = 8, max = 64) @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)\\S+$", message = "비밀번호는 영문과 숫자를 포함하고 공백이 없어야 합니다.") private String password; + @Schema(description = "회원 권한", example = "I", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank @Pattern(regexp = "^(CCM|SCM|I|R|E)$") private String userRole; + @Schema(description = "회원 유형", example = "T", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank @Pattern(regexp = "^(S|T)$") private String userType; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserDormantModifyForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserDormantModifyForm.java new file mode 100644 index 0000000..dbf84b7 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserDormantModifyForm.java @@ -0,0 +1,12 @@ +package com.alist.api.front.modules.user.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 휴면 상태 변경 요청") @Getter @Setter +public class UserDormantModifyForm { + @Schema(description = "휴면 여부", example = "Y", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank @Pattern(regexp = "^[YN]$") private String dormantYn; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserMigrationListForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserMigrationListForm.java new file mode 100644 index 0000000..fc64841 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserMigrationListForm.java @@ -0,0 +1,12 @@ +package com.alist.api.front.modules.user.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "이전 회원 조회 요청") @Getter @Setter +public class UserMigrationListForm { + @Schema(description = "이전 회원 아이디", example = "test", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank private String id; + @Schema(description = "이전 회원 비밀번호", example = "pass1234", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank private String password; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserPasswordCheckForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserPasswordCheckForm.java new file mode 100644 index 0000000..8bd5918 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserPasswordCheckForm.java @@ -0,0 +1,11 @@ +package com.alist.api.front.modules.user.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 비밀번호 확인 요청") @Getter @Setter +public class UserPasswordCheckForm { + @Schema(description = "현재 비밀번호", example = "pass1234", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank private String password; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserPasswordModifyForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserPasswordModifyForm.java new file mode 100644 index 0000000..efb0484 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserPasswordModifyForm.java @@ -0,0 +1,14 @@ +package com.alist.api.front.modules.user.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 비밀번호 수정 요청") @Getter @Setter +public class UserPasswordModifyForm { + @Schema(description = "현재 비밀번호", example = "pass1234", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank private String currentPassword; + @Schema(description = "새 비밀번호", example = "newpass1234", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank @Size(min = 8, max = 64) @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)\\S+$") private String newPassword; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserProfileModifyForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserProfileModifyForm.java new file mode 100644 index 0000000..add074a --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserProfileModifyForm.java @@ -0,0 +1,14 @@ +package com.alist.api.front.modules.user.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 개인정보 수정 요청") @Getter @Setter +public class UserProfileModifyForm { + @Schema(description = "현재 비밀번호", example = "pass1234", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank private String currentPassword; + @Schema(description = "이메일", example = "user@example.com") @Email private String email; + @Schema(description = "휴대폰 번호", example = "01012345678") private String hp; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserWithdrawModifyForm.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserWithdrawModifyForm.java new file mode 100644 index 0000000..3e02d30 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/form/UserWithdrawModifyForm.java @@ -0,0 +1,12 @@ +package com.alist.api.front.modules.user.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 탈퇴 상태 변경 요청") @Getter @Setter +public class UserWithdrawModifyForm { + @Schema(description = "탈퇴 상태(P: 요청, N: 철회)", example = "P", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank @Pattern(regexp = "^[PN]$") private String withdrawStatus; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/service/UserService.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/service/UserService.java new file mode 100644 index 0000000..69c8269 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/service/UserService.java @@ -0,0 +1,177 @@ +package com.alist.api.front.modules.user.service; + +import com.alist.api.core.common.utils.ApiKeyGenerator; +import com.alist.api.core.common.utils.SecurityUtil; +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserUserPasswordViewDto; +import com.alist.api.core.modules.bespoke.testUser.dto.TestUserUserProfileViewDto; +import com.alist.api.core.modules.bespoke.testUser.service.TestUserUserService; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserUserPasswordViewVo; +import com.alist.api.core.modules.bespoke.testUser.vo.TestUserUserProfileViewVo; +import com.alist.api.core.modules.migration.alist.user.dto.AlistUserMigrationDto; +import com.alist.api.core.modules.migration.alist.user.service.AlistUserMigrationService; +import com.alist.api.core.modules.migration.alist.user.vo.AlistUserMigrationVo; +import com.alist.api.core.modules.migration.eltown.user.dto.EltownUserMigrationDto; +import com.alist.api.core.modules.migration.eltown.user.service.EltownUserMigrationService; +import com.alist.api.core.modules.migration.eltown.user.vo.EltownUserMigrationVo; +import com.alist.api.core.modules.standard.testUser.dto.TestUserAddDto; +import com.alist.api.core.modules.standard.testUser.dto.TestUserModifyDto; +import com.alist.api.core.modules.standard.testUser.service.TestUserService; +import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenAddDto; +import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService; +import com.alist.api.front.modules.user.form.*; +import com.alist.api.front.modules.user.vo.*; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class UserService { + private final TestUserService testUserService; + private final TestUserTokenService testUserTokenService; + private final TestUserUserService testUserUserService; + private final AlistUserMigrationService alistUserMigrationService; + private final EltownUserMigrationService eltownUserMigrationService; + private final PasswordEncoder passwordEncoder; + + public UserService(TestUserService testUserService, TestUserTokenService testUserTokenService, TestUserUserService testUserUserService, AlistUserMigrationService alistUserMigrationService, EltownUserMigrationService eltownUserMigrationService, PasswordEncoder passwordEncoder) { + this.testUserService = testUserService; + this.testUserTokenService = testUserTokenService; + this.testUserUserService = testUserUserService; + this.alistUserMigrationService = alistUserMigrationService; + this.eltownUserMigrationService = eltownUserMigrationService; + this.passwordEncoder = passwordEncoder; + } + + @Transactional + public UserAddVo insertUserAdd(UserAddForm userAddForm) { + UserAddVo userAddVo = new UserAddVo(); + userAddVo.setId(userAddForm.getId().trim()); + userAddVo.setUserRole(userAddForm.getUserRole().trim()); + userAddVo.setUserType(userAddForm.getUserType().trim()); + + if (testUserService.selectTestUserDuplicateCount(userAddVo.getId()) > 0) { + userAddVo.setResultCode(2004); + return userAddVo; + } + + TestUserAddDto testUserAddDto = new TestUserAddDto(); + testUserAddDto.setId(userAddVo.getId()); + testUserAddDto.setPassword(userAddForm.getPassword()); + var testUserAddVo = testUserService.insertTestUserAdd(testUserAddDto); + + if (!testUserAddVo.isAdded()) { + userAddVo.setResultCode(testUserAddVo.getResultCode()); + return userAddVo; + } + + for (int retryCount = 0; retryCount < 5; retryCount++) { + TestUserTokenAddDto testUserTokenAddDto = new TestUserTokenAddDto(); + testUserTokenAddDto.setUserIdx(testUserAddVo.getUserIdx()); + testUserTokenAddDto.setUserApiKey(ApiKeyGenerator.userApiKeyProc()); + testUserTokenAddDto.setUserRole(userAddVo.getUserRole()); + testUserTokenAddDto.setUserType(userAddVo.getUserType()); + + var testUserTokenAddVo = testUserTokenService.insertTestUserTokenAdd(testUserTokenAddDto); + if (testUserTokenAddVo.isAdded()) { + userAddVo.setUserIdx(testUserAddVo.getUserIdx()); + userAddVo.setResultCode(2002); + return userAddVo; + } + } + + throw new IllegalStateException("사용자 API Key 생성에 실패했습니다."); + } + + @Transactional(readOnly = true) + public UserProfileViewVo selectUserProfileView() { + Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + if (userTokenIdx == null) return null; + + TestUserUserProfileViewDto testUserUserProfileViewDto = new TestUserUserProfileViewDto(); + testUserUserProfileViewDto.setUserTokenIdx(userTokenIdx); + TestUserUserProfileViewVo testUserUserProfileViewVo = testUserUserService.testUserUserProfileView(testUserUserProfileViewDto); + if (testUserUserProfileViewVo == null) return null; + + UserProfileViewVo userProfileViewVo = new UserProfileViewVo(); + userProfileViewVo.setUserIdx(testUserUserProfileViewVo.getUserIdx()); + userProfileViewVo.setId(testUserUserProfileViewVo.getId()); + userProfileViewVo.setEmail(testUserUserProfileViewVo.getEmail()); + userProfileViewVo.setHp(testUserUserProfileViewVo.getHp()); + userProfileViewVo.setUserRole(testUserUserProfileViewVo.getUserRole()); + userProfileViewVo.setUserType(testUserUserProfileViewVo.getUserType()); + return userProfileViewVo; + } + + @Transactional(readOnly = true) + public UserPasswordCheckVo selectUserPasswordCheck(UserPasswordCheckForm userPasswordCheckForm) { + return passwordCheck(userPasswordCheckForm.getPassword()); + } + + @Transactional + public UserPasswordModifyVo updateUserPasswordModify(UserPasswordModifyForm userPasswordModifyForm) { + UserPasswordCheckVo userPasswordCheckVo = passwordCheck(userPasswordModifyForm.getCurrentPassword()); + UserPasswordModifyVo userPasswordModifyVo = new UserPasswordModifyVo(); + if (!userPasswordCheckVo.isPasswordMatched()) { userPasswordModifyVo.setResultCode(userPasswordCheckVo.getResultCode()); return userPasswordModifyVo; } + + TestUserUserPasswordViewDto testUserUserPasswordViewDto = new TestUserUserPasswordViewDto(); + testUserUserPasswordViewDto.setUserTokenIdx(SecurityUtil.getLoginUserTokenIdx()); + TestUserUserPasswordViewVo testUserUserPasswordViewVo = testUserUserService.testUserUserPasswordView(testUserUserPasswordViewDto); + TestUserModifyDto testUserModifyDto = new TestUserModifyDto(); + testUserModifyDto.setUserIdx(testUserUserPasswordViewVo.getUserIdx()); + testUserModifyDto.setPassword(userPasswordModifyForm.getNewPassword()); + var testUserModifyVo = testUserService.updateTestUserModify(testUserModifyDto); + userPasswordModifyVo.setPasswordChanged(testUserModifyVo.isUpdated()); userPasswordModifyVo.setResultCode(testUserModifyVo.getResultCode()); return userPasswordModifyVo; + } + + private UserPasswordCheckVo passwordCheck(String password) { + UserPasswordCheckVo userPasswordCheckVo = new UserPasswordCheckVo(); Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); + if (userTokenIdx == null || password == null) { userPasswordCheckVo.setResultCode(4003); return userPasswordCheckVo; } + TestUserUserPasswordViewDto testUserUserPasswordViewDto = new TestUserUserPasswordViewDto(); testUserUserPasswordViewDto.setUserTokenIdx(userTokenIdx); + TestUserUserPasswordViewVo testUserUserPasswordViewVo = testUserUserService.testUserUserPasswordView(testUserUserPasswordViewDto); + if (testUserUserPasswordViewVo == null) { userPasswordCheckVo.setResultCode(2003); return userPasswordCheckVo; } + userPasswordCheckVo.setPasswordMatched(passwordEncoder.matches(password, testUserUserPasswordViewVo.getPassword())); userPasswordCheckVo.setResultCode(userPasswordCheckVo.isPasswordMatched() ? 200 : 4001); return userPasswordCheckVo; + } + + @Transactional(readOnly = true) + public UserMigrationListVo selectUserMigrationList(UserMigrationListForm userMigrationListForm) { + AlistUserMigrationDto alistUserMigrationDto = new AlistUserMigrationDto(); + alistUserMigrationDto.setId(userMigrationListForm.getId().trim()); + alistUserMigrationDto.setPassword(userMigrationListForm.getPassword()); + EltownUserMigrationDto eltownUserMigrationDto = new EltownUserMigrationDto(); + eltownUserMigrationDto.setId(userMigrationListForm.getId().trim()); + eltownUserMigrationDto.setPassword(userMigrationListForm.getPassword()); + UserMigrationListVo userMigrationListVo = new UserMigrationListVo(); + List alistUserList = new ArrayList<>(); + for (AlistUserMigrationVo sourceVo : alistUserMigrationService.selectAlistUserMigrationList(alistUserMigrationDto)) { UserMigrationItemVo itemVo = new UserMigrationItemVo(); itemVo.setId(sourceVo.getId()); itemVo.setName(sourceVo.getName()); itemVo.setHp(sourceVo.getHp()); itemVo.setEmail(sourceVo.getEmail()); itemVo.setType(sourceVo.getType()); itemVo.setJoinDate(sourceVo.getJoinDate()); alistUserList.add(itemVo); } + List eltownUserList = new ArrayList<>(); + for (EltownUserMigrationVo sourceVo : eltownUserMigrationService.selectEltownUserMigrationList(eltownUserMigrationDto)) { UserMigrationItemVo itemVo = new UserMigrationItemVo(); itemVo.setId(sourceVo.getId()); itemVo.setName(sourceVo.getName()); itemVo.setHp(sourceVo.getHp()); itemVo.setEmail(sourceVo.getEmail()); itemVo.setType(sourceVo.getType()); itemVo.setJoinDate(sourceVo.getJoinDate()); eltownUserList.add(itemVo); } + userMigrationListVo.setAlistUserList(alistUserList); userMigrationListVo.setEltownUserList(eltownUserList); return userMigrationListVo; + } + + @Transactional + public UserProfileModifyVo updateUserProfileModify(UserProfileModifyForm userProfileModifyForm) { + UserPasswordCheckVo userPasswordCheckVo = passwordCheck(userProfileModifyForm.getCurrentPassword()); UserProfileModifyVo userProfileModifyVo = new UserProfileModifyVo(); + if (!userPasswordCheckVo.isPasswordMatched()) { userProfileModifyVo.setResultCode(userPasswordCheckVo.getResultCode()); return userProfileModifyVo; } + if (userProfileModifyForm.getEmail() == null && userProfileModifyForm.getHp() == null) { userProfileModifyVo.setResultCode(4001); return userProfileModifyVo; } + TestUserUserPasswordViewDto dto = new TestUserUserPasswordViewDto(); dto.setUserTokenIdx(SecurityUtil.getLoginUserTokenIdx()); TestUserUserPasswordViewVo passwordVo = testUserUserService.testUserUserPasswordView(dto); + TestUserModifyDto testUserModifyDto = new TestUserModifyDto(); testUserModifyDto.setUserIdx(passwordVo.getUserIdx()); testUserModifyDto.setEmail(userProfileModifyForm.getEmail()); testUserModifyDto.setHp(userProfileModifyForm.getHp()); + var coreVo = testUserService.updateTestUserModify(testUserModifyDto); userProfileModifyVo.setProfileUpdated(coreVo.isUpdated()); userProfileModifyVo.setResultCode(coreVo.getResultCode()); return userProfileModifyVo; + } + + @Transactional + public UserWithdrawModifyVo updateUserWithdrawModify(UserWithdrawModifyForm userWithdrawModifyForm) { + UserWithdrawModifyVo userWithdrawModifyVo = new UserWithdrawModifyVo(); Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); if (userTokenIdx == null) { userWithdrawModifyVo.setResultCode(4003); return userWithdrawModifyVo; } + TestUserUserPasswordViewDto dto = new TestUserUserPasswordViewDto(); dto.setUserTokenIdx(userTokenIdx); TestUserUserPasswordViewVo passwordVo = testUserUserService.testUserUserPasswordView(dto); if (passwordVo == null) { userWithdrawModifyVo.setResultCode(2003); return userWithdrawModifyVo; } + TestUserModifyDto testUserModifyDto = new TestUserModifyDto(); testUserModifyDto.setUserIdx(passwordVo.getUserIdx()); testUserModifyDto.setWithdrawStatus(userWithdrawModifyForm.getWithdrawStatus()); var coreVo = testUserService.updateTestUserModify(testUserModifyDto); userWithdrawModifyVo.setWithdrawStatusUpdated(coreVo.isUpdated()); userWithdrawModifyVo.setResultCode(coreVo.getResultCode()); return userWithdrawModifyVo; + } + + @Transactional + public UserDormantModifyVo updateUserDormantModify(UserDormantModifyForm userDormantModifyForm) { + UserDormantModifyVo userDormantModifyVo = new UserDormantModifyVo(); Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); if (userTokenIdx == null) { userDormantModifyVo.setResultCode(4003); return userDormantModifyVo; } + TestUserUserPasswordViewDto dto = new TestUserUserPasswordViewDto(); dto.setUserTokenIdx(userTokenIdx); TestUserUserPasswordViewVo passwordVo = testUserUserService.testUserUserPasswordView(dto); if (passwordVo == null) { userDormantModifyVo.setResultCode(2003); return userDormantModifyVo; } + TestUserModifyDto testUserModifyDto = new TestUserModifyDto(); testUserModifyDto.setUserIdx(passwordVo.getUserIdx()); testUserModifyDto.setDormantYn(userDormantModifyForm.getDormantYn()); var coreVo = testUserService.updateTestUserModify(testUserModifyDto); userDormantModifyVo.setDormantStatusUpdated(coreVo.isUpdated()); userDormantModifyVo.setResultCode(coreVo.getResultCode()); return userDormantModifyVo; + } +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserAddVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserAddVo.java new file mode 100644 index 0000000..2475a0b --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserAddVo.java @@ -0,0 +1,15 @@ +package com.alist.api.front.modules.user.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 회원가입 응답") @Getter @Setter +public class UserAddVo { + @Schema(description = "회원 PK", example = "2") private Integer userIdx; + @Schema(description = "회원 아이디", example = "teacher02") private String id; + @Schema(description = "회원 권한", example = "I") private String userRole; + @Schema(description = "회원 유형", example = "T") private String userType; + @JsonIgnore @Schema(hidden = true) private int resultCode; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserDormantModifyVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserDormantModifyVo.java new file mode 100644 index 0000000..6aef919 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserDormantModifyVo.java @@ -0,0 +1,12 @@ +package com.alist.api.front.modules.user.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 휴면 상태 변경 응답") @Getter @Setter +public class UserDormantModifyVo { + @Schema(description = "휴면 상태 변경 여부", example = "true") private boolean dormantStatusUpdated; + @JsonIgnore @Schema(hidden = true) private int resultCode; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserMigrationItemVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserMigrationItemVo.java new file mode 100644 index 0000000..44deb1b --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserMigrationItemVo.java @@ -0,0 +1,15 @@ +package com.alist.api.front.modules.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "이전 회원 항목") @Getter @Setter +public class UserMigrationItemVo { + @Schema(description = "회원 아이디", example = "test") private String id; + @Schema(description = "회원 이름", example = "홍길동") private String name; + @Schema(description = "휴대폰 번호", example = "01012345678") private String hp; + @Schema(description = "이메일", example = "user@example.com") private String email; + @Schema(description = "회원 구분", example = "교사") private String type; + @Schema(description = "가입일", example = "2026-05-19") private String joinDate; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserMigrationListVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserMigrationListVo.java new file mode 100644 index 0000000..0ad5a02 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserMigrationListVo.java @@ -0,0 +1,13 @@ +package com.alist.api.front.modules.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "이전 회원 조회 응답") @Getter @Setter +public class UserMigrationListVo { + @Schema(description = "ALIST 이전 회원 목록") private List alistUserList; + @Schema(description = "ELTOWN 이전 회원 목록") private List eltownUserList; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserPasswordCheckVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserPasswordCheckVo.java new file mode 100644 index 0000000..701dd01 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserPasswordCheckVo.java @@ -0,0 +1,12 @@ +package com.alist.api.front.modules.user.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 비밀번호 확인 응답") @Getter @Setter +public class UserPasswordCheckVo { + @Schema(description = "비밀번호 일치 여부", example = "true") private boolean passwordMatched; + @JsonIgnore @Schema(hidden = true) private int resultCode; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserPasswordModifyVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserPasswordModifyVo.java new file mode 100644 index 0000000..ba4819d --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserPasswordModifyVo.java @@ -0,0 +1,12 @@ +package com.alist.api.front.modules.user.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 비밀번호 수정 응답") @Getter @Setter +public class UserPasswordModifyVo { + @Schema(description = "비밀번호 수정 여부", example = "true") private boolean passwordChanged; + @JsonIgnore @Schema(hidden = true) private int resultCode; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserProfileModifyVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserProfileModifyVo.java new file mode 100644 index 0000000..edb2cee --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserProfileModifyVo.java @@ -0,0 +1,12 @@ +package com.alist.api.front.modules.user.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 개인정보 수정 응답") @Getter @Setter +public class UserProfileModifyVo { + @Schema(description = "개인정보 수정 여부", example = "true") private boolean profileUpdated; + @JsonIgnore @Schema(hidden = true) private int resultCode; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserProfileViewVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserProfileViewVo.java new file mode 100644 index 0000000..923ab26 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserProfileViewVo.java @@ -0,0 +1,15 @@ +package com.alist.api.front.modules.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 개인정보 조회 응답") @Getter @Setter +public class UserProfileViewVo { + @Schema(description = "회원 PK", example = "1") private Integer userIdx; + @Schema(description = "회원 아이디", example = "test") private String id; + @Schema(description = "이메일", example = "user@example.com") private String email; + @Schema(description = "휴대폰 번호", example = "01012345678") private String hp; + @Schema(description = "회원 권한", example = "CCM") private String userRole; + @Schema(description = "회원 유형", example = "T") private String userType; +} diff --git a/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserWithdrawModifyVo.java b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserWithdrawModifyVo.java new file mode 100644 index 0000000..cbc8929 --- /dev/null +++ b/03-api-front/src/main/java/com/alist/api/front/modules/user/vo/UserWithdrawModifyVo.java @@ -0,0 +1,12 @@ +package com.alist.api.front.modules.user.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "사용자 탈퇴 상태 변경 응답") @Getter @Setter +public class UserWithdrawModifyVo { + @Schema(description = "탈퇴 상태 변경 여부", example = "true") private boolean withdrawStatusUpdated; + @JsonIgnore @Schema(hidden = true) private int resultCode; +} diff --git a/src/main/resources/application-local.yaml b/03-api-front/src/main/resources/application-local.yaml similarity index 100% rename from src/main/resources/application-local.yaml rename to 03-api-front/src/main/resources/application-local.yaml diff --git a/src/main/resources/application-pjt.yaml b/03-api-front/src/main/resources/application-pjt.yaml similarity index 100% rename from src/main/resources/application-pjt.yaml rename to 03-api-front/src/main/resources/application-pjt.yaml diff --git a/03-api-front/src/main/resources/application.yaml b/03-api-front/src/main/resources/application.yaml new file mode 100644 index 0000000..8801ea0 --- /dev/null +++ b/03-api-front/src/main/resources/application.yaml @@ -0,0 +1,47 @@ +server: + port: 8112 + forward-headers-strategy: framework + +spring: + application: + name: api-front + +springdoc: + swagger-ui: + path: /swagger-ui.html + tags-sorter: alpha # 태그 알파벳 순 정렬 + operations-sorter: alpha # API 알파벳 순 정렬 + api-docs: + path: /v3/api-docs + + # API version rule: MAJOR.MINOR.PATCH + # MAJOR: 기존 API와 호환되지 않는 변경 + # - 요청/응답 필드명 변경 또는 삭제 + # - URL, HTTP Method, 인증 방식 변경 + # - 기존 클라이언트가 수정 없이는 사용할 수 없는 변경 + # + # MINOR: 기존 API와 호환되는 기능 추가 + # - 신규 API 추가 + # - 응답 필드 추가 + # - 선택 요청 파라미터 추가 + # - 기존 동작을 깨지 않는 기능 확장 + # + # PATCH: 호환성에 영향 없는 수정 + # - 버그 수정 + # - 문서/Swagger 설명 수정 + # - 내부 로직 개선 + # - 성능 개선 +api: + version: v1.0.1 + +management: + endpoints: + web: + exposure: + include: health,info,metrics + endpoint: + health: + show-details: always + health: + db: + enabled: false diff --git a/03-api-front/src/main/resources/banner.txt b/03-api-front/src/main/resources/banner.txt new file mode 100644 index 0000000..879842a --- /dev/null +++ b/03-api-front/src/main/resources/banner.txt @@ -0,0 +1,18 @@ + + q p ,, + db \ / `7MMF' db mm + ;MM:o=--*--=o MM MM + ,V^MM. / \ MM `7MM ,pP"Ybd mmMMmm + ,M `MM d b MM MM 8I `" MM + AbmmmqMA MM , MM `YMMMa. MM + A' VML MM ,M MM L. I8 MM +.AMA. .AMMA. .JMMmmmmMMM .JMML.M9mmmP' `Mbmo + + +:: ALIST API :: +Application : ${spring.application.name} +Profile : ${spring.profiles.active} +Spring Boot : ${spring-boot.version} +Java : ${java.version} +Port : ${server.port} +PID : ${PID} diff --git a/03-api-front/src/main/resources/logback-local.xml b/03-api-front/src/main/resources/logback-local.xml new file mode 100644 index 0000000..829c415 --- /dev/null +++ b/03-api-front/src/main/resources/logback-local.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + %clr(%d{HH:mm:ss.SSS}){faint} %clr(%5p) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/03-api-front/src/main/resources/logback-pjt.xml b/03-api-front/src/main/resources/logback-pjt.xml new file mode 100644 index 0000000..edb5f49 --- /dev/null +++ b/03-api-front/src/main/resources/logback-pjt.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + ${LOG_PATH}/${LOG_FILE_NAME}.log + + + + + + ${LOG_PATH}/%d{yyyy/MM}/${LOG_FILE_NAME}.log-%d{yyyy-MM-dd}.%i + + + + 50MB + + + 180 + true + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} | %-5level | %-15thread | %-40logger{40} | %msg%n + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/03-api-front/src/main/resources/robots.txt b/03-api-front/src/main/resources/robots.txt new file mode 100644 index 0000000..77470cb --- /dev/null +++ b/03-api-front/src/main/resources/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index d515ca5..cf40b18 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,10 +16,13 @@ | 구분 | 문서 | 경로(URL) | |------|------|-----------| | 기본 | [프로젝트 개요](docs/project-overview.md) | `docs/project-overview.md` | +| 기본 | [구조 한눈에 보기](docs/architecture-at-a-glance.md) | `docs/architecture-at-a-glance.md` | +| 기본 | [Core 모듈 설계](docs/core-module-design.md) | `docs/core-module-design.md` | | 기본 | [코드 컨벤션](docs/code-conventions.md) | `docs/code-conventions.md` | | 기본 | [보안 및 응답 규칙](docs/security-and-response.md) | `docs/security-and-response.md` | | 기본 | [설정 및 실행 가이드](docs/runtime-config.md) | `docs/runtime-config.md` | | 기본 | [검증 및 체크리스트](docs/verification-checklist.md) | `docs/verification-checklist.md` | +| 기본 | [테스트 전략](docs/testing-strategy.md) | `docs/testing-strategy.md` | | 기본 | [Codex 작업 규칙](docs/agent-workflow.md) | `docs/agent-workflow.md` | | 기본 | [현재 코드베이스 메모](docs/codebase-notes.md) | `docs/codebase-notes.md` | | 정책 | [정책서 요약 인덱스](docs/policy/index.md) | `docs/policy/index.md` | @@ -30,10 +33,13 @@ | 정책 | [정책서: 인터페이스/도메인/운영](docs/policy/interfaces-and-operations.md) | `docs/policy/interfaces-and-operations.md` | ## 빠른 판단 가이드 +- core `standard`/`bespoke` 구조와 이관 기준은 `docs/core-module-design.md` 를 우선 본다. +- 프로젝트에 처음 참여했거나 core/admin/front 책임을 빠르게 파악해야 하면 `docs/architecture-at-a-glance.md` 를 먼저 본다. - 구조, 네이밍, DTO/VO/Form, Mapper 작성 방식은 `docs/code-conventions.md` 를 우선 본다. - 인증/인가, 공통 응답, 예외 처리, 응답 코드는 `docs/security-and-response.md` 를 우선 본다. - 프로파일, 설정 파일, 로깅, Swagger, 실행 명령은 `docs/runtime-config.md` 를 우선 본다. - 테스트 범위, 배포 전 점검, 파일/권한/설정 변경 확인사항은 `docs/verification-checklist.md` 를 우선 본다. +- 단위 테스트, 통합 테스트, 실제 연동 검증의 범위와 Red-Green 흐름은 `docs/testing-strategy.md` 를 우선 본다. - Codex 협업 방식과 응답 원칙은 `docs/agent-workflow.md` 를 우선 본다. - 도메인 정책, 역할, 강좌/콘텐츠/LMS 규칙은 `docs/policy/` 하위 문서를 우선 본다. @@ -50,9 +56,9 @@ - 기존 서비스 회원 DB와 학습 이력은 완전 마이그레이션 대상이 아니므로 최초 로그인 연동, 레거시 병행 운영, 데이터 재구조화 정책을 함께 고려해야 한다. ## 프로젝트 핵심 정보 -- 프로젝트 유형: Gradle 기반 Spring Boot 애플리케이션 +- 프로젝트 유형: Gradle 기반 Spring Boot 멀티모듈 모놀리식 - Java 버전: 21 - Spring Boot 버전: 3.5.10 -- 기본 애플리케이션 이름: `api` -- 기본 포트: `8106` -- 실행 진입점: `src/main/java/com/alist/api/ApiApplication.java` +- 모듈: `01-api-core`, `02-api-admin`, `03-api-front` +- admin 포트: `8111`, 실행 진입점: `com.alist.api.admin.ApiAdminApplication` +- front 포트: `8112`, 실행 진입점: `com.alist.api.front.ApiFrontApplication` diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 5d24c50..0000000 --- a/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM amazoncorretto:21 - -# healthcheck용 curl 설치 -RUN yum -y install curl && yum clean all - -WORKDIR /api - -# build/libs 아래 산출물 이름이 바뀌어도 대응 -ARG JAR_FILE=build/libs/*.jar -COPY ${JAR_FILE} api.jar - -ENV JAVA_OPTS="" - -ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar /api/api.jar"] diff --git a/README.md b/README.md index f592f36..6be366a 100644 --- a/README.md +++ b/README.md @@ -1,336 +1,90 @@ # alist API -Spring Boot 3 기반의 `alist` 백엔드 API 서버입니다. +Gradle 기반 Spring Boot 멀티모듈 API 프로젝트입니다. 하나의 저장소에서 core 공통 기능과 admin/front 실행 서버를 함께 관리합니다. -## 프로젝트 개요 +## 모듈 -- 그룹: `com.alist` -- Java: `21` -- Spring Boot: `3.5.10` -- 기본 포트: `8106` -- 실행 진입점: `src/main/java/com/alist/api/ApiApplication.java` -- 빌드 결과물: `build/libs/api.jar` +```text +01-api-core 공통 인프라, 공통 기능, 업무 모듈 라이브러리 +02-api-admin 관리자 API 실행 서버 +03-api-front 사용자 API 실행 서버 +``` + +- `01-api-core`는 단독 실행하지 않습니다. +- `02-api-admin`은 `api-admin.jar`를 생성합니다. +- `03-api-front`는 `api-front.jar`를 생성합니다. +- admin/front는 각각 core에 의존하며, 서로의 Controller를 포함하지 않습니다. + +## 처음 보는 사람을 위한 구조 + +```text +HTTP 요청 + -> admin/front Controller + -> admin/front 업무 Service + -> core standard 또는 bespoke Service + -> Mapper -> Mapper XML -> DB +``` + +- `admin`과 `front`는 각각 실행·배포되는 API 서버입니다. +- `core`는 실행하지 않는 공유 라이브러리이며, 공통 기능과 DB 접근 코드를 둡니다. +- 단일 테이블 SQL은 `standard`, 여러 테이블 JOIN이나 복합 SQL은 `bespoke`에 둡니다. +- Controller는 HTTP만, admin/front Service는 업무 흐름만, core는 데이터 접근만 담당합니다. + +처음 참여했다면 [구조 한눈에 보기](docs/architecture-at-a-glance.md) -> [Core 모듈 설계](docs/core-module-design.md) -> [코드 컨벤션](docs/code-conventions.md) 순서로 읽습니다. + +## 빌드와 실행 + +```bash +./gradlew :02-api-admin:bootJar +./gradlew :03-api-front:bootJar +``` + +빌드 결과: + +```text +02-api-admin/build/libs/api-admin.jar +03-api-front/build/libs/api-front.jar +``` + +로컬 실행: + +```bash +./gradlew :02-api-admin:bootRun --args='--spring.profiles.active=local' +./gradlew :03-api-front:bootRun --args='--spring.profiles.active=local' +``` + +Windows에서는 `./gradlew` 대신 `./gradlew.bat`를 사용합니다. + +## 패키지 기준 + +```text +com.alist.api.core +com.alist.api.admin +com.alist.api.front +``` + +- `core.common`: JWT, 예외, 공통 응답, 유틸처럼 시스템 전반에서 사용하는 기능 +- `core.config`: DataSource, MyBatis, 공통 인프라 설정 +- `core.modules.standard`: 단일 테이블의 표준 조회·등록·수정·삭제 +- `core.modules.bespoke`: 여러 테이블 JOIN, 집계, 복합 SQL 데이터 접근 +- `admin`/`front`: Controller, Form, VO, 각 서버의 Security/OpenAPI 설정 + +상세 기준은 [Core 모듈 설계](docs/core-module-design.md)를 참고합니다. ## 기술 스택 -- Spring Web -- Spring Security -- JWT (`jjwt 0.11.5`) -- Spring Session + Redis -- MyBatis (`mapper/**/*.xml`) -- MariaDB -- Swagger / OpenAPI (`springdoc-openapi 2.8.0`) -- Validation -- Actuator -- Lombok -- log4jdbc +- Java 21, Spring Boot 3.5.10, Gradle +- MyBatis, MariaDB, SQL Server 마이그레이션 데이터소스 +- Spring Security, JWT, Redis +- Swagger / OpenAPI, Actuator, Lombok, Validation, log4jdbc -## 주요 기능 - -- 사용자/관리자 JWT 발급 및 검증 -- Redis 기반 SSO 세션 관리와 쿠키 설정 -- Swagger UI Basic 인증 보호 -- DB 기반 허용 Origin 캐시를 사용하는 동적 CORS -- TUS 기반 대용량 파일 업로드 초기화, 권한 검증, 상태 조회, hook 처리, 취소 처리 -- DB 기록 없는 단순 파일 업로드와 uploadPath 반환 -- SunEditor 이미지 업로드와 file-domain URL 반환 -- 파일 view/download API -- 공통 응답 래퍼 `ApiResponse` 및 `ApiResponseCode` 사용 - -## 디렉터리 구조 - -```text -src/main/java/com/alist/api -├── common -│ ├── response # ApiResponse, ApiResponseCode -│ └── utils # 공통 유틸리티 -├── config -│ ├── cache # CORS 허용 Origin 캐시 -│ ├── exception # 전역 예외 처리 -│ ├── filter # DynamicCorsFilter -│ ├── jwt # JWT 인증 관련 구성 -│ ├── migration # 마이그레이션 DB 설정 -│ └── properties # 설정 프로퍼티 -└── modules - ├── admin # 관리자 인증/회원/스케줄/SSO 클라이언트 관리 API - ├── cors # DB 기반 허용 Origin 관리와 캐시 갱신 API - ├── file # 단순 업로드, SunEditor 업로드, path 기반 view/download - ├── front # 사용자 인증/SSO API와 사용자 관리 API - ├── main # 루트 응답 - ├── migration # 레거시 사용자 조회 - ├── tusFile # TUS 업로드 DB 기록, hook, 상태, 파일 목록/view/download/delete -``` - -리소스 파일은 아래 위치를 사용합니다. - -- 설정: `src/main/resources/application*.yaml` -- Mapper XML: `src/main/resources/mapper/**/*.xml` -- 로그 설정: `src/main/resources/logback-*.xml` -- 상세 문서: `docs/*.md` - -## 실행 방법 - -### 빌드 - -```bash -./gradlew bootJar -``` - -### 로컬 실행 - -```bash -./gradlew bootRun --args='--spring.profiles.active=local' -``` - -### JAR 실행 - -```bash -java -jar build/libs/api.jar --spring.profiles.active=local -``` - -Windows: - -```powershell -.\gradlew.bat bootRun --args='--spring.profiles.active=local' -``` - -## 프로파일 - -| 프로파일 | 설명 | 설정 파일 | -| --- | --- | --- | -| `local` | 로컬 개발 환경 | `src/main/resources/application-local.yaml` | -| `pjt` | 프로젝트 개발 서버 환경 | `src/main/resources/application-pjt.yaml` | - -공통 설정은 `src/main/resources/application.yaml`에 있습니다. - -## 필수 설정 항목 - -### 데이터 저장소 - -- `spring.datasource.*` -- `spring.data.redis.*` -- `spring.session.*` - -### 인증/보안 - -- `jwt.secret` -- `jwt.access-token-validity-seconds` -- `jwt.refresh-token-validity-seconds` -- `cookie.secure` -- `cookie.domain` -- `cookie.name` -- `cookie.same-site` -- `swagger.login.id` -- `swagger.login.password` - -### TUS 업로드 - -- `tus-file.upload.tus-endpoint` -- `tus-file.upload.public-base-url` -- `tus-file.upload.tmp-root` -- `tus-file.upload.final-root` -- `tus-file.upload.interrupt-seconds` -- `tus-file.upload.auth-cache.ttl-seconds` - -### 단순 업로드 / 에디터 이미지 - -- `file.upload.root-path` -- `file.upload.view.file-domain` -- `file.upload.max-size` -- `file.upload.allowed-extensions` -- `file.upload.types.*` - -`pjt` 프로파일은 DB/Redis/JWT/Swagger 값을 환경변수로 주입받도록 작성되어 있습니다. - -## 보안 구조 - -### Swagger - -- 보호 경로: `/v3/api-docs/**`, `/swagger-ui/**`, `/swagger-ui.html` -- 인증 방식: HTTP Basic -- 계정 정보: `swagger.login.id`, `swagger.login.password` - -### Admin API - -- `/admin/**` 는 별도 SecurityFilterChain을 사용합니다. -- `/admin/auth/**`, `/admin/user/add` 는 공개하고, 그 외 `/admin/**` 는 `ADMIN` 권한을 요구합니다. -- `GET /admin/auth/loginChecked` 는 토큰 재발급 없이 현재 관리자 로그인 상태만 확인합니다. - -### API - -- 기본 인증 방식: JWT Bearer 또는 HttpOnly 쿠키 fallback -- 세션 저장소: Redis -- 쿠키 속성: `cookie.*` 설정으로 제어 - -### 공개 경로 - -- `/` -- `/actuator/health` -- `/sso/**` -- `/auth/**` -- `/user/add` -- `/user/migration/list` -- `/tus/file/upload/auth` -- `/tus/file/hook` -- `/file/**` - -TUS 업로드 토큰은 일반 access token이 아니므로 `/tus/file/upload/auth`, `/tus/file/hook` 은 file-domain nginx `auth_request` 설정과 Spring Security 공개 경로를 함께 맞춰야 합니다. - -## CORS - -`DynamicCorsFilter`가 최우선 필터로 동작하며, 허용 Origin 목록은 `CorsCache`에서 조회합니다. - -- 허용된 Origin에만 `Access-Control-Allow-Origin` 설정 -- Credential 허용 -- `OPTIONS` preflight 요청은 `200 OK`로 즉시 응답 -- DB를 직접 매 요청마다 조회하지 않고 캐시된 목록을 사용 - -file-domain nginx 의 TUS 업로드 경로는 별도 `map $http_origin $cors_allow_origin` 설정으로 허용 Origin을 제한합니다. - -## 파일 업로드 - -파일 업로드는 두 흐름으로 분리되어 있습니다. - -### 단순 업로드 - -DB에 기록하지 않고 파일만 저장한 뒤 업무 테이블에 저장하기 좋은 값을 반환합니다. - -- `POST /file/upload` -- `GET /file/view?path=/uploads/...` -- `GET /file/download?path=/uploads/...` - -응답 데이터 예시: - -```json -{ - "uploadPath": "/uploads/notice/2026/05/08/abc.png", - "originalFileName": "sample.png", - "storedFileName": "abc.png", - "fileExtension": "png", - "contentType": "image/png", - "fileSize": 12345, - "width": 800, - "height": 600 -} -``` - -### SunEditor 이미지 업로드 - -SunEditor 업로드는 API로 저장하되, 에디터 본문에는 token이 필요 없는 file-domain URL을 저장합니다. - -- `POST /file/suneditor/upload` - -응답 예시: - -```json -{ - "result": [ - { - "url": "https://file-alist.pjt.kr/uploads/editor/2026/05/08/abc.png", - "name": "sample.png", - "size": 12345 - } - ] -} -``` - -### TUS 업로드 - -대용량 업로드는 tusd와 연동하며 API는 DB 기록, 토큰 발급, 권한 검증, hook 반영을 담당합니다. - -- `POST /tus/file/upload/init` -- `GET /tus/file/upload/auth` -- `POST /tus/file/upload/status` -- `POST /tus/file/hook` -- `POST /tus/file/upload/cancel` -- `GET /tus/file/list/{fileMasterIdx}` -- `GET /tus/file/view/{fileUuid}` -- `GET /tus/file/download/{fileUuid}` -- `POST /tus/file/delete` - -운영 TUS 엔드포인트: - -```text -https://file-alist.pjt.kr/tus/file/ -``` - -## file-domain 운영 메모 - -- 업로드 최종 경로: `/srv/project/alist/uploads` -- tusd 임시 경로: `/srv/project/alist/uploads/tmp` -- 파일 도메인: `https://file-alist.pjt.kr` -- nginx `/uploads/` 는 `/srv/project/alist/uploads/` 를 정적 파일로 제공합니다. -- nginx `/uploads/tmp/` 는 임시 파일 노출 방지를 위해 404 처리합니다. -- nginx `/tus/file/` 는 tusd로 프록시하고 `auth_request /_upload_auth` 로 `/tus/file/upload/auth` 를 호출합니다. - -## 주요 인증 엔드포인트 - -- `POST /auth/token` -- `POST /auth/access` -- `POST /auth/refresh` -- `POST /auth/apiKeyLogin` -- `GET /sso/loginChecked` -- `POST /sso/login` -- `POST /sso/exchange` -- `POST /sso/logout` -- `POST /admin/auth/login` -- `POST /admin/auth/refresh` -- `GET /admin/auth/loginChecked` -- `POST /admin/auth/logout` -- `POST /user/add` -- `POST /user/migration/list` - -## Actuator - -외부 노출 대상은 아래와 같습니다. - -- `health` -- `info` -- `metrics` - -헬스체크 기본 경로: - -- `GET /actuator/health` - -## 응답 규칙 - -모든 API 응답은 `ApiResponse` 래퍼를 우선 사용하며, 상태/메시지는 `ApiResponseCode` enum으로 관리합니다. - -자주 사용하는 코드 예시는 아래와 같습니다. - -| 코드 | HTTP Status | 의미 | -| --- | --- | --- | -| `CODE_200` | `200 OK` | 일반 성공 | -| `CODE_204` | `204 No Content` | 일반 성공 | -| `CODE_2001` | `200 OK` | 단건 조회 성공 | -| `CODE_2002` | `201 Created` | 등록 성공 | -| `CODE_2003` | `200 OK` | 조회 결과 없음 | -| `CODE_2004` | `409 Conflict` | 중복 데이터 | -| `CODE_4001` | `400 Bad Request` | 입력값/바인딩 오류 | -| `CODE_4003` | `400 Bad Request` | 필수 파라미터 누락 | -| `CODE_401` | `401 Unauthorized` | 인증 필요 | -| `CODE_403` | `403 Forbidden` | 권한 없음 | -| `CODE_500` | `500 Internal Server Error` | 서버 오류 | - -파일 binary view/download 응답은 `ResponseEntity` 로 직접 내려줄 수 있습니다. - -## 배포 관련 - -- Dockerfile: `Dockerfile` -- Jenkins 파이프라인: `Jenkinsfile.pjt` -- 배포 스크립트: `deploy/` -- 개발 서버 API 도메인: `https://api-alist.pjt.kr` -- 파일 도메인: `https://file-alist.pjt.kr` -- Swagger: `https://api-alist.pjt.kr/swagger-ui/index.html` - -## 상세 문서 +## 문서 - [프로젝트 개요](docs/project-overview.md) +- [구조 한눈에 보기](docs/architecture-at-a-glance.md) +- [Core 모듈 설계](docs/core-module-design.md) +- [코드 컨벤션](docs/code-conventions.md) - [보안 및 응답 규칙](docs/security-and-response.md) - [설정 및 실행 가이드](docs/runtime-config.md) - [검증 및 체크리스트](docs/verification-checklist.md) - [현재 코드베이스 메모](docs/codebase-notes.md) - -## 라이선스 - -Proprietary diff --git a/build.gradle b/build.gradle index cf55b76..3802739 100644 --- a/build.gradle +++ b/build.gradle @@ -1,73 +1,34 @@ plugins { - id 'java' - id 'org.springframework.boot' version '3.5.10' - id 'io.spring.dependency-management' version '1.1.7' + id 'org.springframework.boot' version '3.5.10' apply false + id 'io.spring.dependency-management' version '1.1.7' apply false } -group = 'com.alist' -version = '0.0.1-SNAPSHOT' -description = 'alist api project' +allprojects { + group = 'com.alist' + version = '0.0.1-SNAPSHOT' -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) + repositories { + mavenCentral() } } -repositories { - mavenCentral() -} +subprojects { + apply plugin: 'java' + apply plugin: 'io.spring.dependency-management' -dependencies { - implementation 'org.springframework.boot:spring-boot-starter' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } - // Web 기본 - implementation 'org.springframework.boot:spring-boot-starter-web' + dependencyManagement { + imports { + mavenBom "org.springframework.boot:spring-boot-dependencies:3.5.10" + } + } - // security - implementation 'org.springframework.boot:spring-boot-starter-security' - - // jwt - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' - annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' - - // Lombok - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' - - // jdbc - implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16' - - // MariaDB - implementation 'org.mariadb.jdbc:mariadb-java-client' - implementation 'com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre11' - - // swagger - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.0' - - // MyBatis (Boot 3 전용) - implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' - - // validation - implementation 'org.springframework.boot:spring-boot-starter-validation' - - // health - implementation 'org.springframework.boot:spring-boot-starter-actuator' - - // Redis (SSO 세션 공유) - implementation 'org.springframework.boot:spring-boot-starter-data-redis' -} - -tasks.named('test') { - useJUnitPlatform() -} -bootJar { - archiveFileName = "api.jar" -} -jar { - enabled = false + tasks.withType(Test).configureEach { + useJUnitPlatform() + } } \ No newline at end of file diff --git a/deploy/pjt/compose/api-compose.pjt.yml b/deploy/pjt/compose/api-admin-compose.pjt.yml similarity index 70% rename from deploy/pjt/compose/api-compose.pjt.yml rename to deploy/pjt/compose/api-admin-compose.pjt.yml index d360321..1cec2fd 100644 --- a/deploy/pjt/compose/api-compose.pjt.yml +++ b/deploy/pjt/compose/api-admin-compose.pjt.yml @@ -1,20 +1,20 @@ services: app: - container_name: alist-api - image: registry.pjt.kr/alist/api:${IMAGE_TAG} + container_name: alist-api-admin + image: registry.pjt.kr/alist/api-admin:${IMAGE_TAG} restart: always extra_hosts: - "host.docker.internal:host-gateway" ports: - - "127.0.0.1:8106:8106" + - "127.0.0.1:8111:8111" env_file: - - /srv/project/alist/env/api/.env + - /srv/project/alist/env/api-admin/.env volumes: - - /srv/project/alist/logs/api/app:/logs + - /srv/project/alist/logs/api-admin/app:/logs - /srv/project/alist/uploads:/srv/project/alist/uploads environment: @@ -27,7 +27,7 @@ services: -Duser.timezone=Asia/Seoul healthcheck: - test: ["CMD-SHELL", "curl -fsS http://localhost:8106/actuator/health || exit 1"] + test: ["CMD-SHELL", "curl -fsS http://localhost:8111/actuator/health || exit 1"] interval: 10s timeout: 3s retries: 10 diff --git a/deploy/pjt/compose/api-front-compose.pjt.yml b/deploy/pjt/compose/api-front-compose.pjt.yml new file mode 100644 index 0000000..0db7232 --- /dev/null +++ b/deploy/pjt/compose/api-front-compose.pjt.yml @@ -0,0 +1,40 @@ +services: + app: + container_name: alist-api-front + image: registry.pjt.kr/alist/api-front:${IMAGE_TAG} + restart: always + + extra_hosts: + - "host.docker.internal:host-gateway" + + ports: + - "127.0.0.1:8112:8112" + + env_file: + - /srv/project/alist/env/api-front/.env + + volumes: + - /srv/project/alist/logs/api-front/app:/logs + - /srv/project/alist/uploads:/srv/project/alist/uploads + + environment: + JAVA_OPTS: >- + -Dspring.profiles.active=${SPRING_PROFILE} + -Xms${JVM_XMS} + -Xmx${JVM_XMX} + -XX:MaxMetaspaceSize=${JVM_META} + -Dfile.encoding=UTF-8 + -Duser.timezone=Asia/Seoul + + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:8112/actuator/health || exit 1"] + interval: 10s + timeout: 3s + retries: 10 + start_period: 30s + + logging: + driver: json-file + options: + max-size: "10m" + max-file: "5" diff --git a/docs/agent-workflow.md b/docs/agent-workflow.md index 29d0495..cfba919 100644 --- a/docs/agent-workflow.md +++ b/docs/agent-workflow.md @@ -12,7 +12,59 @@ - 사용자가 IDE 에서 파일을 수정하거나 새 파일을 만든 뒤 질문할 수 있으므로, 관련 답변 전에는 저장된 최신 파일 상태를 다시 확인하는 것을 우선한다. - 이전에 읽은 세션 문맥만으로 최신 파일 상태를 단정하지 말고, 저장되지 않은 편집 내용은 확인할 수 없음을 전제로 설명한다. +## 작업 진행 표시 + +- 기능 또는 모듈 단위 작업에서만 진행 상태를 표시한다. +- 시작 전에는 최대 4개 단계로 나누고, 현재 완료 기준으로 퍼센트를 표시한다. +- 완료는 `✓`, 진행 중은 `●`, 예정은 `○`로 표시한다. +- 퍼센트는 시간 예측이 아니라 실제 완료한 단계 기준으로 표시한다. +- 컴파일, 테스트, 실제 연동 검증은 필요한 경우 별도 단계로 표시한다. +- Redis, 파일, 외부 API 등 기능이 사용하지 않는 의존성 검증 단계는 표시하지 않는다. +- 단순 파일 확인이나 검색에는 진행 상태를 표시하지 않는다. +- 오류가 발생하면 해당 단계에 머물고 원인과 다음 조치를 짧게 공유한다. + +## 모델 라우팅 규칙 + +- Terra는 현재 작업의 총괄 역할을 맡는다. 구조 설계, 레거시 분석, 보안·권한·트랜잭션·Redis 판단, 오류 원인 분석, 코드 리뷰와 실제 연동 검증은 Terra가 처리한다. +- Luna는 범위가 명확하고 독립적인 반복 작업을 맡는다. DTO·VO·Mapper 보일러플레이트 작성, 단순 CRUD 이관, 컴파일·단위 테스트 실행, 포맷과 정적 확인이 대상이다. +- 한 대화 안에서 모델을 교체하는 대신, Terra가 필요한 작업만 Luna 하위 작업으로 위임하고 결과를 다시 검토한다. +- 같은 파일 또는 강하게 연결된 파일을 동시에 수정하는 작업은 충돌 방지를 위해 Terra가 직접 처리한다. +- 보안, 데이터 손상 가능성, 다중 모듈 영향, 실제 DB·Redis·외부 연동이 포함되면 Luna 단독 처리 대상으로 보내지 않는다. +- 모델 라우팅은 작업의 난이도와 위험도로 판단한다. 단순 반복이라는 이유만으로 도메인 정책이나 DB 변경 판단까지 위임하지 않는다. +- 추론 강도는 Terra와 Luna 모두 기본 `medium`으로 사용한다. +- 비용과 응답 속도를 우선하므로 `high` 이상의 추론 강도는 사용하지 않는다. 더 깊은 판단이 필요하면 Terra가 범위를 나누어 확인하고 결과를 단계적으로 공유한다. + +### 작업별 담당 + +| 작업 | 기본 담당 | 기준 | +|---|---|---| +| 실행 모듈, 업무 패키지, API 구조 설계 | Terra | `admin`, `front`, `core`와 `standard`, `bespoke`의 경계를 판단한다. | +| 레거시 분석과 이관 범위 결정 | Terra | 기존 정책, 누락 기능, 호환성을 해석한다. | +| 단일·복합 SQL 분류, 트랜잭션과 응답 코드 결정 | Terra | 재사용성과 데이터 정합성을 판단한다. | +| JWT, Security, 권한, CORS, Redis, 파일, 외부 연동 | Terra | 보안·운영 영향이 크다. | +| 확정된 standard CRUD DTO·VO·Mapper·XML 작성 | Luna | 이미 정해진 계약을 반복 적용한다. | +| 확정된 Form, 응답 VO, Swagger 설명 작성 | Luna | API 필드와 문구가 확정된 경우에 한한다. | +| import, 네이밍, 포맷, XML namespace/id 점검 | Luna | 독립적이고 저위험인 기계적 확인이다. | +| compileJava, 단위 테스트 실행 | Luna | 빠른 반복 검증을 우선한다. | +| 통합·실제 연동 테스트 시나리오와 결과 판단 | Terra | Security, DB, Redis, 외부 의존성의 영향 범위를 판단한다. | +| 오류 원인 분석과 수정 방향 결정 | Terra | 로그 해석을 넘어 구조와 정책을 확인한다. | + +### 적용 흐름 + +```text +Terra: 구조, SQL 분류, 정책, 테스트 조건 결정 + -> Luna: 확정된 계약의 반복 구현과 빠른 컴파일·단위 검증 + -> Terra: 결과 검토, 통합·실제 연동 검증, 다음 수정 방향 결정 +``` + +- 판단 기준은 한 문장으로 정리한다. 새 설계·정책 판단이 필요하면 Terra, 이미 정해진 계약을 여러 파일에 적용하면 Luna다. +- Luna가 작성한 결과도 Terra가 기존 구조, 보안 규칙, 실제 의존성 영향과 함께 검토한 뒤 반영한다. + ## 사용자 선호 규칙 +- 신규 API 또는 기존 기능 이관 작업은 코드나 파일 생성 전에 먼저 패키지와 메서드 구성을 트리 구조로 제시한다. 이 단계에서는 구현 코드를 먼저 보여주지 않는다. +- 작업 전 구조 제시는 `실행 모듈 -> 업무 1 depth 패키지 -> Controller API 메서드 -> 업무 Service 메서드 -> Form/VO -> core standard/bespoke Service/Mapper/핵심 메서드` 순서로 작성한다. +- 구조 제시 단계에서 core standard와 bespoke의 책임을 함께 구분한다. 단일 테이블 CRUD와 전용 조회는 standard, JOIN·집계·복합 SQL은 bespoke로 표시한다. +- 사용자가 구조와 메서드 구성을 확인한 뒤에만 코드 제시 또는 직접 작성을 진행한다. - 소스코드 변경이 필요한 요청에서는 바로 구현하지 말고 먼저 다음 중 무엇을 원하는지 확인한다. - `코드로 보여주기`: 사용자가 직접 프로젝트에 반영할 수 있도록 예시 코드나 패치를 제공한다. - `직접 작성하기`: 에이전트가 저장소에 직접 수정한다. diff --git a/docs/architecture-at-a-glance.md b/docs/architecture-at-a-glance.md new file mode 100644 index 0000000..7e9fc5e --- /dev/null +++ b/docs/architecture-at-a-glance.md @@ -0,0 +1,264 @@ +# 구조 한눈에 보기 + +이 문서는 프로젝트에 처음 들어온 사람이 `core`, `admin`, `front`가 왜 나뉘어 있는지와 새 코드를 어느 위치에 만들어야 하는지를 빠르게 이해하기 위한 안내서다. + +## 1. 전체 구조 + +```text +한 Git 저장소 +| +|-- 01-api-core 공유 라이브러리, 단독 실행 안 함 +|-- 02-api-admin 관리자 API 서버, 별도 JAR 배포 +`-- 03-api-front 사용자 API 서버, 별도 JAR 배포 +``` + +```text +브라우저 또는 외부 클라이언트 + | + v +Controller (admin 또는 front) + | + v +업무 Service (admin 또는 front) + | + +--> core standard Service --> Mapper --> Mapper XML --> DB + | + `--> core bespoke Service --> Mapper --> Mapper XML --> DB +``` + +`02-api-admin`과 `03-api-front`는 각각 독립 실행된다. 따라서 admin JAR에는 front Controller가, front JAR에는 admin Controller가 들어가지 않는다. 두 서버가 공통으로 사용하는 코드는 `01-api-core`에 둔다. + +## 2. 어디에 무엇을 두는가 + +| 위치 | 책임 | 두면 안 되는 것 | +|---|---|---| +| `core/common` | JWT, 예외, 공통 응답, 페이징, 유틸 | 화면별 정책, Controller | +| `core/config` | DataSource, MyBatis, 공통 인프라 설정 | admin/front 화면 전용 설정 | +| `core/modules/standard` | 한 테이블의 조회, 등록, 수정, 삭제 | 여러 테이블 JOIN SQL | +| `core/modules/bespoke` | JOIN, 집계, `INSERT ... SELECT`, 예외적인 복합 SQL | 단일 테이블 CRUD 중복 | +| `admin/modules` | 관리자 Controller, 업무 흐름, Form, 응답 VO | front Controller | +| `front/modules` | 사용자 Controller, 업무 흐름, Form, 응답 VO | admin Controller | + +admin/front의 `modules` 바로 아래 1 depth는 테이블명이 아니라 서비스의 업무 메뉴 기준이다. 예를 들어 백오피스 메뉴 아래에는 관리자 계정, 권한, 메뉴 관리 기능이 함께 있으므로 모두 `backOffice` 모듈에 둔다. 일반 회원 기능이 필요해져도 백오피스의 관리자 계정 관리와 섞지 않는다. + +### standard와 bespoke 판단법 + +```text +SQL이 한 테이블만 접근하는가? + |- 예: standard + `- 아니오: bespoke +``` + +예시: + +```text +test_user의 LAST_LOGIN_AT 수정 +-> standard.testUser.TestUserService + +test_user + test_user_token을 JOIN한 관리자 로그인 조회 +-> bespoke.testUser.TestUserAuthService +``` + +`standard`는 테이블마다 Service를 하나만 둔다. `test_user`에 조회가 더 필요하면 `TestUserService`에 메서드와 DTO/VO를 추가한다. `TestUserLoginService`처럼 단일 테이블용 Service를 새로 만들지 않는다. + +## 3. 패키지 모양 + +실행 모듈은 화면 메뉴의 1 depth를 패키지로 두고, 그 안에서 Controller와 업무 Service를 기능별로 나눈다. + +```text +com.alist.api.admin.modules.backOffice +├─ AdminMemberController.java +├─ AccountController.java +├─ MenuController.java +├─ service +│ ├─ AdminMemberService.java +│ ├─ AccountService.java +│ └─ MenuService.java +├─ form +│ ├─ AdminMemberAddForm.java +│ ├─ AccountAddForm.java +│ └─ MenuAddForm.java +└─ vo + ├─ AdminMemberListVo.java + ├─ AccountListVo.java + └─ MenuListVo.java +``` + +패키지는 프로젝트 규칙에 따라 lower camel case로 작성한다. 따라서 `backOffice`가 패키지명이고, `AdminMemberController`와 `MenuController`가 같은 업무 모듈의 기능별 진입점이다. + +### Core bespoke 이름 + +core `bespoke`의 Service/Mapper는 기준 테이블명과 이를 호출하는 admin/front 업무 Service명을 결합한다. + +```text +AdminMemberService +-> TestUserAdminMemberService +-> TestUserAdminMemberMapper + +AuthService +-> TestUserAuthService +-> TestUserAuthMapper +``` + +`backOffice`는 실행 모듈의 메뉴 패키지 경계이므로 core bespoke 클래스명에 넣지 않는다. 이 기준으로 `backOffice.AdminMemberService`가 호출하는 `TEST_USER` JOIN 조회는 `TestUserBackOfficeService`가 아니라 `TestUserAdminMemberService`에 둔다. + +### core standard + +```text +com.alist.api.core.modules.standard.testUser +├─ dto +│ ├─ TestUserAddDto +│ ├─ TestUserListDto +│ ├─ TestUserModifyDto +│ └─ TestUserViewDto +├─ vo +│ ├─ TestUserAddVo +│ ├─ TestUserListItemVo +│ ├─ TestUserListVo +│ ├─ TestUserModifyVo +│ └─ TestUserViewVo +├─ mapper +│ └─ TestUserMapper +└─ service + └─ TestUserService + +resources/mapper/standard/testUser +└─ TestUserMapper.xml +``` + +### core bespoke + +```text +com.alist.api.core.modules.bespoke.testUserToken +├─ dto +│ └─ TestUserTokenRefreshViewDto +├─ vo +│ └─ TestUserTokenRefreshViewVo +├─ mapper +│ └─ TestUserTokenAuthMapper +└─ service + └─ TestUserTokenAuthService + +resources/mapper/bespoke/testUserToken +└─ TestUserTokenAuthMapper.xml +``` + +### admin/front 업무 모듈 + +```text +com.alist.api.admin.modules.auth +├─ AuthController +├─ form +│ └─ AuthLoginForm +├─ service +│ └─ AuthService +└─ vo + └─ AuthLoginVo +``` + +front도 같은 모양을 사용한다. 단, Controller와 Form/VO는 서로 공유하지 않는다. + +## 4. 클래스별 역할 + +| 클래스 | 위치 | 하는 일 | +|---|---|---| +| `*Controller` | admin/front | HTTP 요청 수신, Form 검증, 인증 주체 확인, `ApiResponse` 반환 | +| admin/front `*Service` | admin/front | 업무 정책, 여러 core 호출 조합, 트랜잭션, core VO를 외부 VO로 변환 | +| `*Service` | core standard | 한 테이블의 데이터 접근 계약 제공 | +| `*Service` | core bespoke | JOIN·집계·복합 SQL 데이터 접근 계약 제공 | +| `*Mapper` | core | Java 메서드와 Mapper XML 연결 | +| `*Mapper.xml` | core resources | 실제 SQL | +| `*Form` | admin/front | HTTP 요청 바인딩과 입력 검증 | +| `*Dto` | core | Service에서 Mapper로 전달하는 조건 또는 저장 값 | +| core `*Vo` | core | Mapper 조회 결과와 core 내부 처리 값 | +| admin/front `*Vo` | admin/front | 외부 API 응답 값 | + +Controller는 core Service, Mapper, core DTO/VO를 직접 사용하지 않는다. admin/front 업무 Service가 경계를 지킨다. + +## 5. 이름 읽는 법 + +### 패키지명 + +패키지는 lower camel case를 쓴다. + +```text +testUser +testUserToken +testCorsAllowedList +``` + +### 클래스명 + +```text +standard +{Table}Service, {Table}Mapper +예: TestUserService, NoticeMapper + +bespoke +{BaseTable}{BusinessModule}Service, {BaseTable}{BusinessModule}Mapper +예: TestUserAuthService, NoticeSupportService +``` + +`admin`, `front`, `Query`, `Custom`은 core bespoke Service 이름에 붙이지 않는다. core는 서버가 아니라 데이터와 업무 목적을 기준으로 재사용하기 때문이다. + +### 메서드명 + +```text +admin/front 업무 Service +{select|insert|update|delete}{기능명} +예: selectLogin, selectRefresh, updateLogout + +core standard +{select|insert|update|delete}{Table}{Action} +예: selectTestUserList, updateTestUserModify + +core bespoke +{baseTable}{function}{ResultShape} +예: testUserLoginView, testUserTokenRefreshView, noticeSupportList +``` + +bespoke 조회 메서드는 결과 모양을 마지막에 붙인다. `List`, `View`, `Count`, `Summary`, `Tree` 등이 예다. 조회 조건은 `ByUserIdx`처럼 이름에 붙이지 않고 DTO에 둔다. + +### 변수명 + +변수는 타입과 역할이 드러나게 쓴다. + +```java +AuthLoginVo authLoginVo; +TestUserTokenModifyDto testUserTokenModifyDto; +List noticeList; +``` + +`result`, `data`, `dto`, `vo`처럼 의미가 너무 넓은 이름은 피한다. boolean은 결과가 드러나게 `added`, `updated`, `deleted`, `loggedIn`, `refreshed`처럼 작성한다. + +## 6. 인증 흐름 예시 + +관리자 로그인은 JOIN 조회와 단일 테이블 수정을 함께 사용한다. + +```text +AuthController.login + -> AuthService.selectLogin + -> TestUserAuthService.testUserLoginView + -> test_user + test_user_token JOIN + -> PasswordEncoder로 비밀번호 검증 + -> JwtTokenProvider로 Access/Refresh Token 발급 + -> TestUserTokenService.updateTestUserTokenModify + -> test_user_token refresh token, 만료 시각 저장 + -> TestUserService.updateTestUserModify + -> test_user LAST_LOGIN_AT 저장 + -> AuthLoginVo 조립 + -> ApiResponse 반환 +``` + +이 흐름에서 `password`, `refreshToken`은 인증 처리용 core VO에만 두고 외부 응답 VO에는 기본적으로 넣지 않는다. + +## 7. 새 기능을 만들 때 순서 + +1. API가 admin인지 front인지 정하고 해당 `modules/{기능}` 패키지를 만든다. +2. 사용할 SQL이 단일 테이블인지, JOIN·복합 SQL인지 판단한다. +3. core standard 또는 bespoke에 DTO, VO, Mapper, Service, Mapper XML을 만든다. +4. admin/front에 Form, 업무 Service, 외부 응답 VO, Controller를 만든다. +5. Controller가 core 타입을 직접 import하지 않는지 확인한다. +6. Swagger `@Tag`, `@Operation`, `@Schema`과 컴파일 검증을 한다. + +상세 규칙은 [Core 모듈 설계](core-module-design.md), [코드 컨벤션](code-conventions.md), [보안 및 응답 규칙](security-and-response.md)을 참고한다. diff --git a/docs/code-conventions.md b/docs/code-conventions.md index 1b5ab6c..4586624 100644 --- a/docs/code-conventions.md +++ b/docs/code-conventions.md @@ -1,35 +1,65 @@ # 코드 컨벤션 ## 파일 생성 규칙 -- 새 기능은 가능하면 `modules/{도메인명}Controller` 단위로 패키지를 만들고 그 아래에 `service`, `mapper`, `dto`, `vo`를 필요한 만큼만 추가한다. +- core 업무 모듈은 `modules.standard`와 `modules.bespoke` 기준으로 나눈다. standard는 단일 테이블의 모든 조회·등록·수정·삭제와 필요한 전용 조회를 담당하고, bespoke는 여러 테이블 JOIN·집계·복합 SQL을 위한 전용 데이터 처리다. +- 업무 모듈 패키지명은 단어를 이어 쓰는 lower camel case로 작성한다. 예: `testUser`, `testUserToken`, `testCorsAllowedList`. +- admin/front의 새 API는 `modules/{업무 메뉴 1 depth}` 단위로 Controller, 업무 Service, Form, 화면 VO를 둔다. 이 1 depth는 테이블명이 아니라 서비스 메뉴와 업무 경계 기준이다. 예: 백오피스의 관리자 계정, 권한, 메뉴 관리는 모두 `modules.backOffice`에 둔다. +- 하나의 업무 메뉴 모듈에는 여러 Controller와 업무 Service가 들어갈 수 있다. 예: `backOffice`에는 `AdminMemberController`/`AdminMemberService`, `AccountController`/`AccountService`, `MenuController`/`MenuService`를 함께 둔다. +- admin/front의 패키지명은 lower camel case로 작성한다. 예: `modules.backOffice`. 업무 모듈이 이미 기능 경계이므로 Form과 외부 VO는 기본적으로 해당 모듈의 `form`, `vo` 바로 아래에 둔다. 예: `modules.backOffice.form.AdminMemberAddForm`, `modules.backOffice.vo.AdminMemberListVo`. 클래스명은 `AdminMemberController`, `AdminMemberService`처럼 UpperCamelCase를 사용한다. +- core에는 Controller, Form, 화면 전용 VO를 두지 않는다. 다만 core Service/Mapper의 업무 입력과 결과를 위한 내부 DTO/VO는 둔다. +- core standard Service는 `{Table}Service`로 작성한다. 예: `TestUserService`, `NoticeService`. +- core bespoke Service는 `{BaseTable}{AdminFrontServiceName}Service`로 작성한다. 예: `TestUserAuthService`, `TestUserAdminMemberService`, `NoticeNoticeService`. +- `{AdminFrontServiceName}`은 admin/front 업무 Service의 이름에서 마지막 `Service`를 뺀 값이다. 기준 테이블명과 같은 이름이 반복되어도 생략하거나 상위 패키지명으로 치환하지 않는다. 예: `modules.support.NoticeService`가 호출하는 core bespoke는 `NoticeSupportService`가 아니라 `NoticeNoticeService`로 작성한다. +- core bespoke 명명에 패키지명이나 1 depth 모듈명은 사용하지 않는다. 패키지 위치는 구조 변경으로 달라질 수 있으므로, 변경에 강한 기준 테이블명과 admin/front 업무 Service명을 조합한다. +- 따라서 `backOffice`는 admin 실행 모듈의 패키지 경계일 뿐 core bespoke 명명에 사용하지 않는다. `backOffice.AdminMemberService`의 core bespoke는 `TestUserBackOfficeService`가 아니라 `TestUserAdminMemberService`로 작성한다. +- `admin`, `front` 같은 실행 서버명과 `Query`, `Custom`, `Bespoke` 같은 구현 방식명은 core bespoke 이름에 사용하지 않는다. +- 하나의 실행 모듈에 여러 테이블 기능이 있으면 Service도 테이블별로 나눈다. 예: `support` 모듈의 `NoticeSupportService`, `FaqSupportService`. `SupportService` 하나에 서로 다른 테이블 업무를 모으지 않는다. +- core bespoke Service/Mapper 메서드는 `{BaseTable}{Function}{List|View|Count|Summary|...}`로 작성한다. 예: `testUserLoginView`, `testUserTokenRefreshView`, `noticeSupportList`. +- bespoke 메서드의 `{Function}`은 호출하는 admin/front Service의 기능명을 따르며, core는 앞에 기준 테이블명을 붙인다. 예: `AuthService.selectLogin`은 `testUserLoginView`, `testUserTokenLoginView`를 호출할 수 있다. +- bespoke 메서드에는 `select`, `insert`, `update`, `delete` 접두어와 `ByUserIdx` 같은 조회 조건을 넣지 않는다. 조건은 DTO에 둔다. - 요청 검증이나 JSON 바인딩이 필요하면 `form` 패키지를 함께 만든다. - 클래스명은 역할이 바로 드러나게 `도메인명 + 역할` 형식을 유지한다. 예: `ApiService`, `ApiMapper`, `ApiVo` - Mapper 인터페이스를 추가하면 같은 이름의 XML을 `src/main/resources/mapper/{도메인경로}` 아래 함께 만든다. - 단순 예시 코드와 운영 코드는 섞지 말고 패키지로 분리한다. -- 설정성 클래스는 `config` 하위 역할별 패키지에 둔다. 예: `config.jwt`, `config.exception`, `config.properties` +- 설정성 클래스는 `config` 하위 역할별 패키지에 둔다. 예: `config.datasource`, `config.cache`. +- 단순 환경값(String, 숫자, boolean 등)은 사용하는 클래스의 필드에 `@Value`로 주입한다. 예: `@Value("${file.storage.root-path}") private String rootPath;`. +- 목록, Map, 중첩 객체처럼 구조화된 설정은 `@ConfigurationProperties`로 묶는다. 단순 값 몇 개만을 위해 별도 Properties 클래스를 만들지 않는다. ## DTO/VO/Form 규칙 -- `Dto`는 각 Controller 에서 Service, Mapper 로 전달하는 파라미터 성격의 값 객체로 정의한다. +- core DTO는 Mapper와 core Service 사이의 내부 데이터 전달 용도다. admin/front Form과 화면 전용 VO를 재사용하지 않는다. +- `Dto`는 core Service와 Mapper 사이의 파라미터 성격 값 객체로 정의한다. - `Dto`는 요청 처리에 필요한 저장, 수정, 로그 적재 같은 작업 파라미터를 담는 용도로 우선 사용한다. - `Vo`는 Mapper 에서 Service, Controller 로 반환하는 값 객체로 정의한다. - `Vo`는 Mapper 조회 결과를 담을 수 있고, Service 에서 비즈니스 로직 처리 후 필요한 데이터를 가공해서 Controller 로 반환하는 용도로 사용한다. -- `Form`은 Controller 입력 검증과 요청 바인딩 전용으로 두고, `@Valid` 와 Jakarta Validation 어노테이션을 우선 사용한다. -- Form/Dto/Vo 클래스명은 가능한 한 Controller 메서드명을 기준으로 맞춘다. 예: Controller 메서드가 `adminMemberList` 이면 `AdminMemberListForm`, `AdminMemberListDto`, `AdminMemberListVo` 를 사용한다. +- core VO는 Mapper 조회 결과와 core 내부 업무 처리를 위한 값 객체다. 화면/API 응답 전용 VO가 아니다. +- admin/front VO는 외부 HTTP 응답 전용이며, 실행 모듈 업무 Service가 core VO에서 필요한 필드만 골라 조립한다. +- 동일한 core VO를 사용하더라도 admin/front는 목적에 따라 서로 다른 외부 VO를 사용한다. +- Controller는 core DTO/VO를 import하지 않고, core DTO/VO를 직접 반환하거나 외부 VO로 변환하지 않는다. 변환은 admin/front 업무 Service에서 끝낸다. +- core VO를 admin/front 외부 VO로 바꾸는 단순 필드 복사는 별도 `Converter`, `Assembler`, 변환 전용 private 메서드로 분리하지 않는다. 해당 업무 Service 메서드 안에서 외부 VO를 생성하고 필요한 필드를 직접 설정한다. +- 목록 변환은 업무 Service 메서드 안에서 `for`문으로 목록 행 VO를 생성해 결과 목록에 추가한다. 변환 규칙이 여러 Service에서 반복되거나 독립된 정책을 가지는 시점에만 별도 변환 클래스를 검토한다. +- Controller의 Service 메서드 반환 타입은 원칙적으로 해당 실행 모듈의 VO 또는 단순 처리 결과여야 한다. 예: `AuthService.loginChecked(...)`는 `AdminAuthUserVo`가 아닌 `AuthLoginCheckedVo`를 반환한다. +- 비밀번호, refresh token, 내부 인증값은 일반 core VO와 admin/front 외부 VO에 기본으로 포함하지 않는다. 인증 처리에서만 필요한 값은 별도 내부 VO로 제한한다. +- `Form`은 실행 모듈의 요청 바인딩과 입력 검증에 사용하고, `@Valid` 와 Jakarta Validation 어노테이션을 우선 사용한다. 실행 모듈의 업무 Service가 Form을 받아 core DTO로 변환할 수 있지만, core는 Form에 의존하지 않는다. +- Form은 실행 모듈의 Controller 메서드명을 기준으로, core DTO/VO는 업무 모듈과 동작명을 기준으로 이름을 맞춘다. 예: `NoticeAddForm` -> `NoticeAddDto`, `NoticeListDto` -> `NoticeListVo`. +- standard 업무 모듈의 기본 DTO/VO 동작명은 `Add`, `View`, `List`, `Modify`, `Delete`다. 목록 행은 `{Domain}ListItemVo`를 사용한다. +- 단일 상태 변경도 기본적으로 별도 DTO를 만들지 않고 `{Domain}ModifyDto`의 선택 필드로 처리한다. Mapper XML은 `` 없이 작성하고, 항상 갱신하는 수정일 컬럼 뒤에 ``로 값이 전달된 필드만 추가 수정한다. - 신규 API 에서는 path 동작명과 다른 표현을 지양한다. 예: 목록은 `Search` 보다 `List`, 상세는 `Detail` 보다 `View`, 수정은 `Update` 보다 `Modify` 를 우선 사용한다. - 현재 코드처럼 DTO/VO는 Lombok `@Getter`, 필요한 경우에만 `@Setter`를 사용한다. -- Form 안에는 DTO 변환 메서드를 둘 수 있으며, 짝이 되는 Dto 로 변환하는 메서드는 `toDto()` 를 기본으로 사용한다. 예: `AdminMemberListForm#toDto()` 는 `AdminMemberListDto` 를 반환한다. -- 하나의 Form 이 여러 Dto 로 변환되어야 하는 예외 상황에서만 `toAdminMemberListDto()` 처럼 대상 Dto 이름을 명시한다. -- Form 에서 DTO 로 변환할 때는 검증과 `trim()`, 기본값 치환까지 같이 처리하는 현재 패턴을 우선 따른다. +- Form 안에는 단순한 값 정리 메서드를 둘 수 있다. core DTO 변환은 기본적으로 실행 모듈 업무 Service가 담당한다. +- Form 에서 전달되는 문자열은 업무 Service에서 core DTO로 변환할 때 `trim()`과 기본값 치환을 적용한다. - DTO 안에 연관된 다른 DTO 변환이 꼭 필요할 때만 최소한의 보조 메서드를 둔다. - 외부 응답에 노출되면 안 되는 내부 필드는 응답에 사용될 수 있는 객체에서 `@JsonIgnore`로 숨긴다. -- 등록, 수정, 삭제, 상태 변경처럼 처리 여부만 반환하는 VO 의 boolean 필드는 `processed` 로 통일한다. 예: `private boolean processed;` +- 변경 결과 VO의 boolean 필드는 동작이 드러나도록 작성한다. 예: 등록은 `added`, 수정/상태 변경은 `updated`, 논리 삭제는 `deleted`. +- `ModifyVo`, `DeleteVo` 등 변경 결과 VO에는 처리 대상 PK도 함께 둔다. 예: `NoticeModifyVo`는 `updated`, `noticeIdx`; `NoticeDeleteVo`는 `deleted`, `noticeIdx`. +- 변경 결과 VO에는 내부 처리용 `resultCode`를 두고 Service가 성공/실패 코드를 설정한다. `resultCode`는 `@JsonIgnore`로 숨기고, Controller가 이를 기준으로 `ApiResponseCode`를 선택한다. - Controller 요청/응답 타입은 가능한 한 Form/Dto/Vo 로 명시한다. `Map` 는 응답 구조가 고정되지 않는 임시 디버깅, 외부 라이브러리 passthrough 같은 예외 상황에서만 제한적으로 사용한다. - 응답 필드가 단순 boolean 몇 개뿐이어도 운영 API 에서는 VO 를 만든다. 예: 로그인 상태 확인 응답은 `Map` 보다 `AdminAuthLoginCheckedVo` 를 사용한다. -- 목록 조회 API 는 특별한 사유가 없으면 기본적으로 페이징을 적용하고, 목록 조회 Form/Dto 는 `PageRequest`, 목록 응답 Vo 는 `PageResponse` 를 상속한다. +- 목록 조회 API 는 특별한 사유가 없으면 기본적으로 페이징을 적용한다. core 내부 페이징 기준은 `PagingRequest`, `PagingResponse`를 사용한다. ## Swagger/OpenAPI 문서화 규칙 - 신규 또는 수정되는 Controller, Form, VO에는 Swagger 테스트 편의성을 위한 설명을 반드시 남긴다. - Controller 클래스에는 `@Tag`를 사용해 API 그룹명과 설명을 작성한다. +- `@Tag`의 번호는 Controller 생성 순서가 아니라 admin/front의 업무 메뉴 1 depth 기준 알파벳 그룹으로 관리한다. 예: `backOffice`는 `B` 그룹이며 그 안의 Controller는 `B-1. 관리자 계정 관리`, `B-2. SSO 클라이언트 관리`, `B-3. 스케줄 관리`처럼 작성한다. 같은 1 depth 안에서는 기능 추가 순서에 따라 하위 번호를 증가시킨다. - Controller 메서드에는 `@Operation`으로 `summary`와 `description`을 작성한다. - `@RequestBody` Form 클래스에는 클래스 레벨 `@Schema(description = "...")`를 작성한다. - `@ModelAttribute` 검색 Form 필드에도 `@Schema`를 작성해 Swagger query parameter 설명과 예시가 보이게 한다. @@ -45,8 +75,11 @@ - 페이징 응답의 `rowStartNum`처럼 계산 방식이 필요한 값은 프론트 사용 방법까지 `description`에 남긴다. ## Mapper/MyBatis 규칙 -- Service/Mapper 메서드명은 주된 DB 동작이 드러나도록 `select`, `insert`, `update` 접두어를 먼저 붙이고 Controller 메서드명을 이어서 만든다. 예: Controller 메서드가 `adminSsoClientList` 이면 Service/Mapper 조회 메서드는 `selectAdminSsoClientList`. -- 삭제 API도 기본적으로 물리 삭제가 아니라 상태 변경으로 처리하므로 실제 SQL 동작에 맞춰 `update...Delete` 형태를 우선 사용한다. 예: Controller 메서드가 `adminSsoClientDelete` 이면 Service/Mapper 메서드는 `updateAdminSsoClientDelete`. +- standard Mapper에는 단일 테이블에서 재사용할 수 있는 표준 CRUD와 전용 조회를 둔다. 조회 조건 또는 조회 projection이 추가되어도 해당 테이블의 기존 standard Service/Mapper에 메서드를 추가한다. +- bespoke Mapper에는 업무 전용 조인 조회를 우선 둔다. 여러 테이블을 함께 갱신해야 하는 `JOIN UPDATE`, 일괄 처리, `INSERT ... SELECT`, 성능 최적화 SQL은 예외적으로 둘 수 있다. bespoke Mapper는 core bespoke Service를 통해 호출한다. +- standard Service/Mapper 메서드는 `{select|insert|update|delete}{Table}{Action}`으로 작성한다. 예: `selectTestUserList`, `insertTestUserAdd`, `updateTestUserModify`. +- bespoke Service/Mapper 메서드는 앞 동작 접두어 없이 `{BaseTable}{Function}{ResultShape}`로 작성한다. 예: `testUserLoginView`, `noticeSupportList`. XML `id`도 동일하게 맞춘다. +- 삭제 API도 기본적으로 물리 삭제가 아니라 상태 변경으로 처리하므로 standard는 `update...Delete` 형태를 우선 사용한다. - 물리 삭제가 정책상 명확히 필요한 예외 상황에서만 `delete` SQL과 `delete...` 메서드명을 사용한다. - SQL 을 여러 줄로 작성할 때 콤마는 다음 줄 앞에 둔다. 예: `SELECT COL1`, 다음 줄 `, COL2`. - Mapper XML `namespace`는 인터페이스의 전체 경로와 정확히 일치시킨다. @@ -57,26 +90,36 @@ - `map-underscore-to-camel-case: true` 설정으로 DB 컬럼 `user_idx` 는 Java 필드 `userIdx` 로 자동 매핑된다. - `useGeneratedKeys`, `keyProperty` 를 사용하는 insert 가 있으므로 신규 PK 생성 테이블은 현재 패턴을 먼저 확인한다. - 상태 집계나 이력성 데이터는 단건 update 외에 이벤트 로그 insert 가 같이 필요한지 확인한다. +- 동적 수정 SQL에서 ``은 사용하지 않는다. `SET UPDATE_AT = NOW()`처럼 항상 갱신하는 컬럼을 먼저 두고, 선택 필드는 `, COLUMN = #{value}` 형태의 ``로 이어 붙인다. null은 기본적으로 "수정하지 않음"을 뜻한다. null로 컬럼 값을 비워야 하는 요구는 별도 플래그 또는 업무 전용 SQL로 명확히 구분한다. ## Java/Spring 코드 스타일 - Java 코드는 현재 프로젝트 스타일에 맞춰 탭/들여쓰기와 import 정렬을 유지한다. +- 선언문, 메서드 반환 대입, 단순 setter, 삼항식이 한 번에 읽히면 줄 길이만으로 줄바꿈하지 않고 한 줄로 작성한다. 여러 줄 정렬은 한 줄에 담기 어려운 인자, 조건, SQL처럼 가독성이 실제로 좋아지는 경우에만 사용한다. +- Mapper 인터페이스의 메서드 선언은 한 줄로 작성하고, 메서드 선언 사이에는 한 줄을 띄운다. +- `if`, `for`, `while` 조건문은 한 줄이어도 항상 중괄호 블록을 사용한다. Service 호출 결과를 지역 변수에 담은 뒤 후속 조건문을 시작하기 전에는 한 줄을 띄워 흐름을 구분한다. - 코드는 초급 개발자가 읽어도 흐름을 따라갈 수 있도록 직관적으로 작성한다. 과한 축약, 기교적인 표현, 불필요하게 복잡한 체이닝보다 명확한 변수명과 단계적인 흐름을 우선한다. - Java 코드에서 메서드 호출, 어노테이션 인자, 생성자 인자 등을 여러 줄로 작성할 때 콤마는 다음 줄 앞에 둔다. 예: `summary = "회원 목록 조회"` 다음 줄 `, description = "..."` - 기능 수정/추가 시 가능하면 Red-Green-Refactor 흐름을 따른다. 먼저 실패하는 테스트나 재현 가능한 검증 조건을 만들고(Red), 최소 구현으로 통과시키며(Green), 이후 컨벤션과 가독성에 맞게 정리한다(Refactor). 테스트 작성이 어려운 경우에는 최소 실행 검증 절차를 먼저 정하고 결과를 보고한다. -- Lombok은 반복 보일러플레이트 제거에만 절제해서 사용한다. +- Lombok은 반복 보일러플레이트 제거에만 절제해서 사용한다. `@RequiredArgsConstructor`는 사용하지 않고, 의존성 주입은 명시적인 생성자로 작성한다. - 로그는 `Slf4j`를 사용하고 반복문 내부 대량 출력은 지양한다. - 새로운 기능은 가능하면 역할이 드러나는 패키지로 분리한다. -- 생성자 주입을 기본으로 하고 필드 주입은 추가하지 않는다. +- Service, Config 등 다른 Spring Bean 의존성은 생성자 주입을 기본으로 하고, `@RequiredArgsConstructor` 대신 명시적인 생성자를 사용한다. 단, 단순 환경값은 위 설정 규칙에 따라 `@Value` 필드 주입을 사용한다. - Service 에서 DB 상태를 바꾸는 메서드는 필요한 범위에서 `@Transactional`을 사용하고, 조회 전용은 `readOnly = true`를 우선 검토한다. - 문자열 입력값은 현재 코드처럼 필요한 지점에서 `trim()` 처리하고, null 가능성 여부를 먼저 확인한다. - 지역 변수명은 `result`, `list`, `vo`, `dto` 처럼 범용적인 이름보다 도메인과 객체 종류가 드러나게 작성한다. 예: `List adminMemberList`, `AdminMemberListVo adminMemberListVo`, `AdminMemberListDto adminMemberListDto`. +- Service 메서드의 Form, DTO, VO 파라미터와 지역 변수도 범용명 `form`, `dto`, `vo`를 사용하지 않는다. 예: `AdminMemberListForm adminMemberListForm`, `SsoListForm ssoListForm`, `TestUserAdminMemberListDto testUserAdminMemberListDto`. - 같은 타입 또는 같은 의미의 변수가 여러 개 필요해도 객체 종류를 유지하고, 구분이 필요한 경우 숫자 suffix 를 제한적으로 사용한다. 예: `AdminMemberVo adminMemberVo1`, `AdminMemberVo adminMemberVo2`. ## Controller/Service 규칙 -- Controller 는 요청/응답 조립과 인증 주체 확인에 집중하고, DB 처리나 복잡한 계산은 Service 로 넘긴다. +- Controller 는 요청/응답 조립과 인증 주체 확인에 집중하고, core Service 또는 Mapper를 직접 호출하지 않는다. core DTO/VO도 Controller에 노출하지 않는다. +- admin/front 업무 Service가 Form을 받아 업무 정책, standard/bespoke 호출 순서, 트랜잭션 경계를 담당한다. +- admin/front 업무 Service 메서드는 `{select|insert|update|delete}{Controller 기능명}`으로 작성한다. 예: Controller의 `ssoList()`는 `selectSsoList()`, `ssoAdd()`는 `insertSsoAdd()`, `ssoDelete()`는 `updateSsoDelete()`를 호출한다. 논리 삭제는 실제 DB 작업이 수정이므로 `update`를 사용한다. +- admin/front 업무 Service의 public 메서드는 Controller가 호출한 API 한 건의 흐름을 가능한 한 해당 메서드 안에서 완결한다. Form 값 정리, core Service 호출, 결과 VO 조립을 별도 모듈 보조 메서드로 잘게 나누지 않는다. +- `to...`, `create...`, `select...` 같은 private 보조 메서드는 단순 코드 분리를 위해 먼저 만들지 않는다. 같은 로직이 여러 업무 Service에 반복되거나, 독립적인 정책과 복잡도를 가질 때 리팩터링 대상으로 분리한다. +- core bespoke Service는 bespoke Mapper를 통한 JOIN·집계·복합 SQL 데이터 접근을 담당한다. 단일 테이블의 조회·등록·수정·삭제는 core standard Service와 Mapper에 둔다. - Controller 응답은 `ResponseEntity>` 를 기본으로 사용한다. 파일 다운로드처럼 바이너리 응답이 필요한 경우만 예외로 둔다. - Controller 에서는 `@RequestBody`, `@PathVariable`, `@RequestHeader`, `@CookieValue` 를 명시적으로 선언해 요청 출처를 드러낸다. - 인증 사용자 확인은 모듈 구현에 따라 `HttpSession` 또는 `SecurityContextHolder` 를 사용하므로 기존 방식을 먼저 맞춘다. -- 목록 조회는 Controller 에서 `@ModelAttribute` 검색 Form 을 받고, Service 에서 `totalCount` 와 목록을 조회한 뒤 `PageResponse#setPaging(...)` 으로 페이징 정보를 채운다. +- 목록 조회는 Controller 에서 `@ModelAttribute` 검색 Form 을 받고, Service 에서 `totalCount` 와 목록을 조회한 뒤 `PagingResponse#setPaging(...)` 으로 페이징 정보를 채운다. - 신규 API path 의 동작명은 CRUD 성격에 맞춰 통일한다. 목록은 `list`, 상세 조회는 `view`, 등록은 `add`, 수정은 `modify`, 삭제는 `delete` 를 사용한다. -- Controller 메서드명은 가능한 한 path 조합을 기준으로 만든다. 클래스 레벨 path 와 메서드 레벨 path 를 이어 붙인 의미가 드러나게 작성한다. 예: 클래스 path 가 `/admin/member`, 메서드 path 가 `/list` 이면 `adminMemberList`, 클래스 path 가 `/admin/sso/client`, 메서드 path 가 `/view` 이면 `adminSsoClientView`. +- Controller 메서드명은 path 동작과 기능을 그대로 표현한다. 예: `ssoList`, `ssoView`, `ssoAdd`, `ssoModify`, `ssoDelete`, `adminMemberDelete`. HTTP path와 Controller 메서드는 API 기능을 나타내고, 실제 DB 작업 성격은 Controller가 호출하는 Service 메서드명에서 표현한다. admin/front 분리 서버에서는 path에 `/admin` 또는 `/front` 접두어를 붙이지 않는다. diff --git a/docs/codebase-notes.md b/docs/codebase-notes.md index 32e35ed..4de2ad4 100644 --- a/docs/codebase-notes.md +++ b/docs/codebase-notes.md @@ -1,15 +1,43 @@ # 현재 코드베이스 메모 -## 현재 특히 주의할 점 -- `README.md`와 `docs/*.md`는 코드 변경 후 같이 갱신해야 한다. 의심되는 항목은 Controller, `SecurityConfig`, 설정 파일을 기준으로 다시 확인한다. -- 예시 스케줄러 코드에는 깨진 문자열과 과도한 반복 로그가 보일 수 있으니 관련 수정 시 인코딩과 로그 정책을 함께 점검한다. -- 테스트 코드가 충분하지 않을 수 있으므로 기능 변경 시 필요한 테스트를 보강한다. -- 일부 Java 소스와 주석, Swagger 설명에 인코딩이 깨진 문자열이 있으므로 표시 문자열 수정은 영향 범위를 보고 묶어서 처리한다. -- `application-local.yaml` 은 로컬 실행값이 직접 들어가 있으므로 공유하거나 커밋할 때 민감정보 노출 여부를 한 번 더 확인한다. -- 파일 업로드는 두 흐름으로 분리되어 있다. `file` 모듈의 단순 업로드는 DB에 기록하지 않고 `uploadPath`, 파일명, 확장자, contentType, size, image width/height 정도만 반환한다. 각 업무 테이블 저장은 호출 측에서 처리한다. -- SunEditor 이미지 업로드는 API로 파일을 저장하되 응답 URL은 `file.upload.view.file-domain + uploadPath` 로 만든다. 에디터 본문에 저장되는 URL은 admin/user 토큰에 의존하지 않는 file-domain URL이어야 한다. -- TUS 업로드는 `tusFile` 모듈에서 DB master/detail 기록, upload token 발급, tusd hook 반영, 상태 조회, view/download/delete를 담당한다. -- TUS 인증 경로는 `/tus/file/upload/auth`, hook 경로는 `/tus/file/hook` 이다. nginx `/_upload_auth`, Spring Security 공개 경로, `tus-file.upload.tus-endpoint` 를 함께 맞춘다. -- 단순 파일 API는 `/file/upload`, `/file/suneditor/upload`, `/file/view`, `/file/download` 를 사용한다. `/admin/files/...` 또는 `/files/...` 예전 표기가 남아 있으면 최신 경로로 정리한다. -- file-domain nginx 는 `/uploads/` 를 공개 정적 파일로 열고 `/uploads/tmp/` 는 404로 막는다. TUS 임시 디렉터리는 파일 시스템에서는 사용하지만 URL로 직접 노출하지 않는다. -- Admin 로그인 상태 확인은 `/admin/auth/loginChecked` 를 사용한다. `/admin/auth/refresh` 는 토큰을 재발급하므로 새로고침 상태 확인용으로 쓰지 않는다. +## 멀티모듈 이관 상태 + +- 기존 단일 모듈의 업무 API 이관을 완료하고, 레거시 소스는 저장소에서 제거했습니다. +- core/admin/front 모듈은 각각 독립 bootJar로 빌드·배포합니다. +- 이후 신규 기능도 core standard/bespoke와 admin/front 업무 Service 경계를 기준으로 추가합니다. + +## 이관 원칙 + +- 범용적인 단일 테이블 데이터 처리는 `core.modules.standard`에 둡니다. +- JOIN·집계·복합 SQL 데이터 처리는 `core.modules.bespoke`에 둡니다. +- admin/front는 Controller, 업무 Service, Form, 화면 VO를 소유합니다. +- admin/front 업무 Service가 업무 정책과 트랜잭션을 소유하며 core standard/bespoke Service를 조합합니다. +- Mapper는 core standard/bespoke에 통합하고, Controller는 core Service 또는 Mapper를 직접 호출하지 않습니다. +- core VO는 내부 업무·조회 결과를 표현하고, admin/front 업무 Service가 필요한 필드만 화면/API 응답 VO로 조립합니다. +- 비밀번호, refresh token 등 민감 정보는 일반 core VO와 외부 응답 VO에 포함하지 않습니다. +- core는 화면 전용 VO나 Controller를 소유하지 않습니다. + +## 파일과 TUS + +- 단순 파일 업로드는 DB 기록 없이 파일을 저장하고 uploadPath를 반환합니다. +- 일반 file Controller는 admin/front 양쪽에 둘 수 있으며, 저장 구현은 core 공통 기능으로 이관합니다. +- TUS는 `02-api-admin/modules/tus`의 admin 전용 기능입니다. front에는 TUS Controller나 `tus-file.*` 설정을 두지 않습니다. +- TUS Controller는 요청 바인딩과 응답만 담당하고, `TusFileService`가 core standard/bespoke Service를 조합합니다. +- `FILE_MASTER`, `FILE_DETAIL`, 업로드·다운로드 이벤트 로그의 단일 테이블 처리는 core standard를 사용합니다. 토큰 소유권, 완료 파일 목록·조회, 파일 이동 대상 조회, 마스터 집계는 core bespoke `fileDetailTusFile`, `fileMasterTusFile`에 둡니다. +- 완료 파일의 최종 저장 루트는 일반 업로드와 같은 `file.storage.root-path`입니다. TUS 임시 파일은 admin의 `tus-file.upload.tmp-root`를 사용합니다. +- TUS 업로드 인증 경로와 Hook 경로만 admin SecurityConfig에서 비인증으로 열고, 나머지 TUS API는 ADMIN 권한을 요구합니다. +- tusd 실제 Hook, 임시 파일 이동, nginx 파일 제공은 tusd와 file-domain 설정이 준비된 환경에서 통합 검증합니다. + +## CORS 동기화 + +- Dynamic CORS 필터와 캐시는 core를 통해 admin/front 양쪽 JVM에서 동작합니다. +- CORS 관리 API는 admin에만 둡니다. +- 허용 Origin 목록은 Redis `alist:cors:allow-origins` 키에 JSON 목록으로 저장합니다. +- admin에서 정책을 변경하면 트랜잭션 커밋 후 Redis Pub/Sub 채널 `alist:cors:allow-origins:changed`로 갱신 이벤트를 발행합니다. +- 각 JVM은 해당 채널을 구독해 즉시 Redis 목록을 다시 읽고, 예외 상황에 대비해 60초 주기 Redis 재조회도 수행합니다. + +## 주의 사항 + +- `application-local.yaml`에 민감정보가 있을 수 있으므로 공유와 커밋 전에 확인합니다. +- Mapper 인터페이스, XML namespace, XML id, DTO resultType/parameterType은 이관 시 함께 변경합니다. +- 파일 경로 변경은 nginx alias, tusd 경로, API 환경 설정을 함께 확인합니다. diff --git a/docs/core-module-design.md b/docs/core-module-design.md new file mode 100644 index 0000000..e8d46a4 --- /dev/null +++ b/docs/core-module-design.md @@ -0,0 +1,280 @@ +# Core 모듈 설계 + +## 목표 + +- core는 admin/front가 공유하는 데이터 접근 계층과 인프라를 제공한다. +- admin/front는 각 서버의 업무 정책, 트랜잭션, 요청과 응답 조립, 인증 주체 확인, 서버별 운영 설정을 담당한다. +- 화면 전용 Form과 VO는 core에 두지 않는다. 단, core 업무 처리의 입력과 결과를 표현하는 내부 DTO/VO는 둔다. + +## 패키지 구조 + +```text +com.alist.api.core +├─ common +├─ config +└─ modules + ├─ standard + └─ bespoke +``` + +## standard + +`standard`는 여러 기능에서 재사용할 수 있는 **단일 테이블** 데이터 처리를 둡니다. + +- 단일 테이블의 조회, 등록, 수정, 삭제와 필요한 전용 조회 projection +- 한 테이블 중심의 Mapper, Service, 내부 DTO/VO +- admin/front 업무 Service가 호출한다. +- Form, 화면 전용 VO, HTTP 요청/응답 객체에 의존하지 않음 +- 등록과 수정의 실제 SQL 처리는 특별한 사유가 없으면 standard가 소유한다. +- 단일 테이블에서 조회 조건이나 조회 필드가 추가되어도 새 Service를 만들지 않는다. 해당 테이블의 기존 standard Service, Mapper, DTO/VO 계약에 메서드 또는 선택 조건을 추가한다. + +## bespoke + +`bespoke`는 단일 테이블 standard 계약으로 해결되지 않는 복합 데이터 처리를 둡니다. bespoke는 특정 목적에 맞춰 제작한 처리라는 뜻입니다. + +- 여러 테이블 JOIN 조회 +- standard로 처리할 수 없는 일괄 처리, `INSERT ... SELECT`, 성능 최적화 SQL +- 예외적인 `JOIN UPDATE` 등 복합 DML + +bespoke는 전용 Mapper와 그 Mapper를 호출하는 Service를 함께 둡니다. 단일 테이블 처리와 범용 CRUD는 bespoke마다 중복하지 않고 standard로 올립니다. admin/front의 업무 Service가 정책과 트랜잭션을 소유하며, core bespoke는 복합 데이터 접근 책임에 집중합니다. + +## 업무 흐름과 호출 기준 + +```text +Controller -> admin/front 업무 Service +admin/front 업무 Service -> standard Service +admin/front 업무 Service -> bespoke Service +bespoke Service -> bespoke Mapper +``` + +- core standard와 core bespoke는 서로의 Service 또는 Mapper를 호출하지 않는다. +- standard와 bespoke를 함께 써야 하는 업무 흐름은 반드시 admin/front 업무 Service가 각각 호출해 조합한다. +- 따라서 core는 독립적인 단일 테이블 접근(standard)과 복합 SQL 접근(bespoke)만 제공하고, 두 계층 사이의 호출 순서나 트랜잭션 경계는 갖지 않는다. + +```text +관리자 회원가입 +UserController -> admin UserService + -> TestUserService + -> TestUserTokenService + +회원과 토큰을 함께 조회하는 관리자 화면 +MemberController -> admin MemberService + -> bespoke.testUser.TestUserMemberService + -> TestUserMemberMapper.testUserMemberList +``` + +- standard는 단일 테이블을 기준으로 실제 `select`, `insert`, `update`, `delete`를 처리한다. 단일 테이블의 인증용 조회처럼 필요한 필드 구성이 다른 경우도 standard의 전용 조회 메서드로 둔다. +- admin/front 업무 Service는 어떤 standard/bespoke를 어떤 순서로 호출할지, 어떤 정책을 적용할지, 어디까지 하나의 트랜잭션으로 묶을지를 결정한다. +- Controller는 core standard/bespoke Service 또는 Mapper를 직접 호출하지 않는다. +- 여러 테이블 조인은 보통 bespoke 조회 Mapper에 둔다. +- 여러 테이블을 하나의 SQL로 갱신해야 하는 `JOIN UPDATE`는 bespoke 수정 Mapper의 예외다. +- standard/bespoke의 첫 판단 기준은 SQL이 접근하는 테이블 수다. 단일 테이블이면 standard, 여러 테이블 JOIN·복합 DML이면 bespoke다. 정책과 트랜잭션 경계는 admin/front 업무 Service가 담당한다. + +## 업무 모듈 파일 구조 + +```text +modules/standard/testUser +├─ mapper +│ └─ TestUserMapper.java +├─ service +│ └─ TestUserService.java +├─ dto +└─ vo + +resources/mapper/standard/testUser +└─ TestUserMapper.xml + +modules/bespoke/notice +├─ mapper +│ └─ NoticeNoticeMapper.java +├─ service +│ └─ NoticeNoticeService.java +├─ dto +└─ vo + +resources/mapper/bespoke/notice +└─ NoticeNoticeMapper.xml +``` + +- Mapper와 Service는 각각 `mapper`, `service` 패키지로 분리한다. +- DTO와 VO는 `dto`, `vo` 패키지로 분리한다. +- Mapper XML namespace는 `mapper` 패키지를 포함한 Mapper 인터페이스 전체 경로와 일치해야 한다. + +## 테이블·업무 모듈 기반 명명 + +core와 실행 모듈의 Service 이름은 역할을 임의로 줄이지 않고 기준 테이블과 업무 모듈을 조합한다. + +```text +standard +{Table}Service + +bespoke +{BaseTable}{BusinessModule}Service +``` + +- `{Table}`과 `{BaseTable}`은 기준 테이블의 Java 이름이다. 예: `test_user` -> `TestUser`, `NOTICE` -> `Notice`. +- standard는 해당 테이블의 기본 CRUD이므로 별도 업무 모듈 접미사를 붙이지 않는다. 예: `TestUserService`, `NoticeService`. +- bespoke는 기준 테이블 뒤에 이를 사용하는 업무 모듈명을 붙인다. 예: `TestUserAuthService`, `TestUserTokenAuthService`, `NoticeSupportService`. +- 업무 모듈명은 `admin`, `front` 같은 실행 서버명이 아니라 `auth`, `support`처럼 실제 기능 목적을 사용한다. +- `Query`, `Custom`, `Bespoke`, `Etc`처럼 처리 방식만 표현하는 접미사는 사용하지 않는다. +- bespoke Service에는 이름에 포함된 업무 모듈 목적에 맞는 메서드만 둔다. `TestUserAuthService`에는 `test_user`를 기준으로 하는 여러 테이블 인증 JOIN 조회만 둔다. 단일 `TEST_USER` 조회는 `TestUserService`에 둔다. +- JOIN이 있더라도 SQL이 시작하고 결과를 소유하는 기준 테이블에 맞춰 bespoke 패키지를 정한다. 예: API Key와 refresh token 기준 인증 JOIN 조회는 `bespoke.testUserToken`에 둔다. + +### bespoke 메서드 명명 + +실행 모듈 Service는 API 업무 흐름을 기준으로, core bespoke는 해당 흐름을 위해 기준 테이블에서 반환하는 데이터 형태를 기준으로 이름을 정한다. + +```text +admin/front AuthService.selectLogin + -> core TestUserAuthService.testUserLoginView + -> core TestUserTokenService.updateTestUserTokenModify + -> core TestUserService.updateTestUserModify +``` + +```text +admin/front Service +{select|insert|update|delete}{기능명} + +core bespoke Service / Mapper / XML id +{기준테이블명}{기능명}{List|View|Count|Summary|...} +``` + +- core bespoke 메서드명에서는 `select`, `insert`, `update`, `delete` 접두어를 사용하지 않는다. +- 기준 테이블명은 반드시 앞에 둔다. 예: `testUserLoginView`, `noticeSupportList`. +- 기능명은 이를 호출하는 admin/front Service의 기능 흐름을 따른다. 한 흐름에서 여러 core 조회가 필요하면 각 기준 테이블명을 붙여 여러 메서드를 호출한다. +- 조회 메서드는 `List`, `View`, `Count`, `Summary`, `Tree` 등 반환 형태를 반드시 마지막에 붙인다. +- 조회 조건은 DTO에 둔다. `ByUserIdx`, `ByApiKey`처럼 조건을 메서드명에 붙이지 않는다. +- standard로 처리 가능한 단일 테이블 CRUD와 조회는 bespoke에 만들지 않는다. 예외적인 bespoke CUD도 같은 명명 방식으로 기준 테이블과 기능명을 사용한다. 예: `noticeSupportAdd`, `noticeSupportModify`. +- bespoke Service 메서드, Mapper 인터페이스 메서드, Mapper XML `id`는 정확히 같은 이름을 사용한다. + +실행 모듈에서 하나의 업무 패키지가 여러 테이블 기능을 포함할 때도 테이블별 Service를 분리한다. + +```text +admin/modules/support +├─ NoticeController +├─ FaqController +└─ service + ├─ NoticeSupportService + └─ FaqSupportService +``` + +`SupportService` 하나에 Notice와 FAQ 로직을 함께 넣지 않는다. 반면 `admin/modules/auth/AuthService`처럼 하나의 인증 흐름을 조합하는 실행 모듈 업무 Service는 기능명만 사용하며, core standard/bespoke Service를 호출해 처리한다. + +## 표준 DTO/VO 규칙 + +standard의 각 업무 모듈은 API 동작 이름을 기준으로 DTO와 VO를 분리한다. 이 객체들은 core 내부에서 Mapper와 Service 사이, 또는 core Service와 실행 모듈 업무 Service 사이에 전달되는 업무 객체이며 화면 전용 응답 객체가 아니다. + +```text +modules/standard/notice +├─ NoticeMapper.java +├─ NoticeService.java +├─ dto +│ ├─ NoticeAddDto.java +│ ├─ NoticeViewDto.java +│ ├─ NoticeListDto.java +│ ├─ NoticeModifyDto.java +│ └─ NoticeDeleteDto.java +└─ vo + ├─ NoticeAddVo.java + ├─ NoticeViewVo.java + ├─ NoticeListVo.java + ├─ NoticeListItemVo.java + ├─ NoticeModifyVo.java + └─ NoticeDeleteVo.java +``` + +- 기본 동작명은 `add`, `view`, `list`, `modify`, `delete`로 통일한다. +- 목록은 검색/페이징 조건인 `ListDto`, 목록 행인 `ListItemVo`, 목록과 페이징 결과를 담는 `ListVo`를 분리한다. +- 변경 결과 VO는 동작이 드러나는 boolean과 대상 PK를 함께 둔다. 예: `NoticeModifyVo(updated, noticeIdx)`, `NoticeDeleteVo(deleted, noticeIdx)`. 등록 결과에는 `added`와 생성 PK 등 필요한 결과 값을 함께 둔다. +- admin/front Form은 실행 모듈의 업무 Service까지 전달할 수 있다. 업무 Service는 Form 값을 core DTO로 변환하고, core VO는 필요할 때만 실행 모듈의 화면 전용 VO로 조립한다. + +## core VO와 실행 모듈 VO + +core VO는 Mapper 조회 결과와 core 내부 업무 처리에 필요한 데이터를 표현한다. admin/front VO는 HTTP 응답으로 실제 클라이언트에 전달할 필드만 표현한다. + +```text +core Mapper + -> core VO + -> admin/front 업무 Service + -> admin/front 화면/API VO + -> Controller +``` + +- core VO는 특정 화면에 묶이지 않고 업무·DB 기준으로 필요한 데이터를 담는다. +- admin/front 업무 Service는 core VO에서 각 API에 필요한 필드만 골라 실행 모듈 VO로 조립한다. +- 같은 core VO라도 admin과 front는 서로 다른 외부 VO를 사용할 수 있다. +- 비밀번호, refresh token, 내부 인증값 같은 민감 정보는 일반 core VO에 기본으로 넣지 않는다. 꼭 필요한 인증 처리에는 전용 core VO를 사용하고, admin/front 외부 VO에는 절대 옮기지 않는다. +- Controller는 core DTO/VO를 import하거나 반환 타입으로 사용하지 않는다. core DTO/VO는 admin/front 업무 Service까지만 전달한다. +- Controller가 필요한 값은 반드시 해당 실행 모듈 Service가 조립한 admin/front VO로 받는다. core 조회 결과를 Controller에서 외부 VO로 변환하지 않는다. + +```text +AuthController + -> admin AuthService.selectLoginChecked + -> core TestUserTokenAuthService.testUserTokenLoginCheckedView + -> TestUserTokenLoginCheckedViewVo (core 내부) + -> AuthLoginCheckedVo (admin 외부 응답용) + -> ApiResponse +``` + +## 단일 수정과 상태 변경 + +단일 필드 변경도 별도 `UseModifyDto`, `StatusModifyDto`를 늘리지 않는다. 기본적으로 하나의 `{Domain}ModifyDto`를 사용한다. + +```text +NoticeModifyDto +- noticeIdx // 수정 대상 +- noticeType // 선택 +- targetScope // 선택 +- title // 선택 +- content // 선택 +- pinYn // 선택 +- startDate // 선택 +- endDate // 선택 +- useYn // 선택 +- updateMember // 항상 기록 +``` + +- Service는 DTO에 담긴 값을 그대로 Mapper에 전달한다. +- Mapper XML에서는 ``을 사용하지 않는다. 항상 갱신하는 수정일 컬럼을 `SET`의 첫 항목으로 두고, null이 아닌 선택 필드는 ``와 앞쪽 쉼표로 뒤에 붙인다. 따라서 사용 상태만 바꾸는 경우에도 `NoticeModifyDto`에 `noticeIdx`, `useYn`, `updateMember`만 넣어 호출한다. +- `START_DATE`, `END_DATE`처럼 null로 명시적 초기화가 필요한 컬럼은 단순 null 제외 규칙으로는 처리할 수 없다. 초기화 요구가 생기면 전용 초기화 플래그 또는 해당 업무에 맞는 bespoke SQL을 추가한다. + +## 변경 결과 VO + +수정과 삭제는 다음 기준으로 결과를 반환한다. + +```text +NoticeModifyVo +- updated // update 영향 행 수가 1 이상이면 true +- noticeIdx // 수정 요청 대상 PK + +NoticeDeleteVo +- deleted // delete(논리 삭제) 영향 행 수가 1 이상이면 true +- noticeIdx // 삭제 요청 대상 PK +``` + +- `updated`, `deleted`는 SQL 영향 행 수로 계산한다. +- 변경 결과 VO에는 내부 처리용 `resultCode`를 둔다. 성공은 `2002` 또는 `2005`, 수정값 없음은 `4001`, 대상 없음은 `2003`처럼 Service가 반환한다. +- Controller는 `resultCode`에 맞는 `ApiResponseCode`를 선택한다. `resultCode`는 `@JsonIgnore`로 외부 응답 본문에는 직접 노출하지 않는다. +- PK를 함께 반환하면 호출한 쪽이 실제 처리 대상을 명확히 확인할 수 있다. + +## common과 config + +- `common`: JWT, 공통 예외, ApiResponse, 페이징, 유틸, 시스템 전반 공통 기능 +- `config`: DataSource, MyBatis, 캐시, 필터처럼 공통 인프라를 구성하는 클래스 +- 파일 업로드처럼 테이블 CRUD에 속하지 않는 공통 기능은 `common` 또는 `config`의 책임에 맞춰 둡니다. + +## admin과 front + +```text +api-admin / api-front +├─ Api*Application +├─ config +│ ├─ SecurityConfig +│ └─ OpenApiConfig +└─ modules + └─ Controller, 업무 Service, Form, 화면 VO +``` + +- Security와 OpenAPI는 서버별 정책과 문서를 가지므로 각 실행 모듈에 둡니다. +- Controller는 Form 검증, 업무 Service 호출, 서버별 응답 VO 조립을 담당합니다. +- 업무 Service는 Form을 받아 필요한 core standard/bespoke DTO로 변환하고, 업무 정책과 트랜잭션을 처리합니다. diff --git a/docs/project-overview.md b/docs/project-overview.md index c7b68ad..3f183c4 100644 --- a/docs/project-overview.md +++ b/docs/project-overview.md @@ -1,38 +1,59 @@ # 프로젝트 개요 ## 프로젝트 기본 정보 -- 프로젝트 유형: Gradle 기반 Spring Boot 애플리케이션 + +- 프로젝트 유형: Gradle 기반 Spring Boot 멀티모듈 모놀리식 - Java 버전: 21 - Spring Boot 버전: 3.5.10 -- 기본 애플리케이션 이름: `api` -- 기본 포트: `8106` -- 실행 진입점: `src/main/java/com/alist/api/ApiApplication.java` +- 저장소: 단일 Git 저장소 +- 실행 서버: admin, front + +## 모듈 구성 + +| 모듈 | 역할 | 실행 여부 | +|---|---|---| +| `01-api-core` | 공통 인프라, 공통 기능, 업무 모듈 | 실행하지 않음 | +| `02-api-admin` | 관리자 API, 관리자 보안과 Swagger 설정 | `api-admin.jar` | +| `03-api-front` | 사용자 API, 사용자 보안과 Swagger 설정 | `api-front.jar` | + +admin/front는 `01-api-core`에 의존하지만 서로 의존하지 않습니다. 각 bootJar에는 core 라이브러리가 포함되며, 다른 서버의 Controller는 물리적으로 포함되지 않습니다. + +## 패키지 구조 + +```text +com.alist.api.core +com.alist.api.admin +com.alist.api.front +``` + +`core`는 다음 책임으로 나눕니다. + +- `common`: JWT, 예외, 응답, 페이징, 유틸과 같은 시스템 공통 기능 +- `config`: DataSource, MyBatis, 공통 인프라 설정 +- `modules.standard`: 재사용 가능한 표준 데이터 처리 +- `modules.bespoke`: 기준 테이블의 업무 전용 JOIN·집계·특수 SQL 데이터 접근 + +`admin`과 `front`에는 Controller, 업무 Service, Form, 외부 응답 VO, 서버별 `SecurityConfig`, `OpenApiConfig`, 환경별 리소스를 둡니다. 업무 정책과 여러 core 호출의 조합, 트랜잭션 경계는 각 실행 모듈 Service가 소유합니다. + +admin과 front는 물리적으로 분리된 서버이므로 API path에 서버 구분 접두어를 중복하지 않습니다. 예를 들어 admin 인증 API는 `/admin/auth/login`이 아니라 `/auth/login`으로 둡니다. + +## 현재 이관 상태 + +- 멀티모듈 Gradle 구조와 admin/front 개별 bootJar 구성이 완료되었습니다. +- 공통 응답, 예외, JWT, DataSource, Mapper XML 리소스는 core로 이동했습니다. +- 기존 admin/front 업무 API는 core standard/bespoke와 각 실행 모듈 Service 구조로 이관했습니다. +- 단일 모듈 레거시 소스는 제거했으며, 이전 구현 이력은 Git history로 확인합니다. ## 기술 스택 -- **Java**: 21 -- **Framework**: Spring Boot 3.5.10 -- **빌드 도구**: Gradle -- **DB**: MariaDB -- **ORM**: MyBatis (mapper XML: `classpath:mapper/**/*.xml`) -- **인증**: JWT (jjwt 0.11.5) + Spring Security -- **API 문서**: Swagger (springdoc-openapi 2.8.0) -- **기타**: Lombok, Validation, Actuator, log4jdbc -## 디렉터리 가이드 -- `src/main/java/com/alist/api`: 애플리케이션 시작점과 업무 코드를 둔다. -- `src/main/java/com/alist/api/modules`: 기능별 모듈 패키지를 둔다. -- `src/main/resources`: 설정 파일과 로깅 설정을 관리한다. -- `deploy`: 배포 관련 리소스가 있으면 이 경로를 우선 확인한다. +- Java 21, Spring Boot 3.5.10, Gradle +- MyBatis, MariaDB, SQL Server 마이그레이션 데이터소스 +- Spring Security, JWT, Redis +- Swagger / OpenAPI, Actuator, Lombok, Validation, log4jdbc -## 현재 확인된 구조 -- 현재 기준 메인 흐름은 `Controller -> Form -> Dto -> Service -> Mapper(XML) -> Vo -> Service -> Controller` 순서로 연결된다. -- API 에서 request 받을 때 POST 는 주로 JSON을 사용한다. Controller 는 `form` 객체로 요청을 받은 뒤 DTO 로 변환해서 Service 에 전달한다. -- MyBatis는 인터페이스와 XML을 함께 사용한다. -- Mapper 인터페이스는 `src/main/java/.../mapper`, SQL XML은 `src/main/resources/mapper/...` 경로를 짝으로 맞춘다. -- 공통 응답은 `common/response`, 보안은 `config/security, jwt`, 전역 예외 처리는 `config/exception` 아래에 둔다. -- 모듈 패키지는 현재 `admin`, `cors`, `file`, `front`, `main`, `migration`, `tusFile` 형태로 구성되어 있고, 필요한 모듈만 `dto`, `form`, `mapper`, `service`, `vo`를 둔다. -- `front` 하위에는 사용자 인증/SSO(`auth`)와 사용자 관리(`user`) 흐름을 둔다. -- `admin` 하위에는 관리자 인증, 회원, 사용자, 스케줄, SSO 클라이언트 관리 흐름을 둔다. -- `cors` 모듈은 DB 기반 허용 Origin 관리와 캐시 갱신 API를 담당한다. -- `file` 모듈은 DB 기록 없는 단순 업로드, SunEditor 이미지 업로드, uploadPath 기반 view/download를 담당한다. -- `tusFile` 모듈은 DB 기록이 필요한 TUS 기반 대용량 업로드 초기화, 업로드 토큰 검증, tusd hook, 상태 조회, 파일 목록/view/download/delete 흐름을 담당한다. +## 빌드 결과 + +```text +02-api-admin/build/libs/api-admin.jar +03-api-front/build/libs/api-front.jar +``` diff --git a/docs/runtime-config.md b/docs/runtime-config.md index 4d2dcfe..a2c51d7 100644 --- a/docs/runtime-config.md +++ b/docs/runtime-config.md @@ -1,43 +1,80 @@ # 설정 및 실행 가이드 -## 설정 규칙 -- 공통 설정은 `application.yaml`, 환경별 차이는 `application-local.yaml`, `application-pjt.yaml` 에 둔다. -- `pjt` 프로파일은 DB/Redis/JWT/Swagger 계정을 환경변수 치환으로 받으므로 새 민감정보는 하드코딩하지 않는다. -- 로깅 설정은 프로파일별 `logback-local.xml`, `logback-pjt.xml` 을 사용하므로 로그 정책 변경 시 함께 본다. -- MyBatis 설정은 `application.yaml` 기준으로 관리하므로 mapper location, alias package, camel case 옵션을 중복 정의하지 않는다. -- Redis 는 세션 저장소와 업로드 상태 캐시 용도를 함께 가지므로 키 prefix 충돌 여부를 확인한다. +## 설정 위치 + +- 환경값은 각 실행 모듈의 `src/main/resources`에 둡니다. +- 공통 설정은 `application.yaml`, 환경별 차이는 `application-local.yaml`, `application-pjt.yaml`에 둡니다. +- admin과 front는 서로 다른 포트, 도메인, JWT 쿠키 정책, Swagger 정보, 로그 설정을 가질 수 있습니다. +- core에는 실행 환경별 YAML을 두지 않고, 환경값을 사용하는 공통 구현과 인프라 설정만 둡니다. +- 단순 설정값은 사용하는 클래스에서 `@Value` 필드 주입으로 읽습니다. 목록·Map·중첩 구조처럼 구조화된 설정만 `@ConfigurationProperties` 클래스로 관리합니다. ## 프로파일 + | 프로파일 | 설명 | -|---------|------| +|---|---| | `local` | 로컬 개발 환경 | -| `pjt` | 프로젝트(개발) 환경 | - -## Swagger 접속 -- URL: `http://localhost:8106/swagger-ui.html` -- 인증: `swagger.login.id` / `swagger.login.password` (환경별 yaml에 설정) - -## 파일 업로드 설정 -- `tus-file.upload.final-root` 는 TUS 완료 파일과 단순 업로드 파일이 공유하는 최종 저장 루트다. -- `tus-file.upload.tmp-root` 는 tusd 임시 업로드 루트다. file-domain nginx 에서는 `/uploads/tmp/` 접근을 404로 막는다. -- `file.upload.root-path` 는 보통 `${tus-file.upload.final-root}` 를 사용해 단순 업로드와 TUS 완료 파일의 마운트 루트를 맞춘다. -- `file.upload.view.file-domain` 은 SunEditor 이미지 응답 URL 생성에 사용한다. 예: `https://file-alist.pjt.kr`. -- `file.upload.max-size` 는 단순 업로드 전역 최대 용량이다. `file.upload.types.{folder}.max-size` 가 있으면 폴더별 설정이 우선한다. -- `file.upload.allowed-extensions` 는 단순 업로드 전역 확장자 허용 목록이다. -- `file.upload.types.{key}.folder` 는 실제 저장 폴더명이다. 설정되지 않은 folder 값도 전역 정책을 통과하면 동적 폴더로 저장할 수 있다. -- `file.upload.types.{key}.image-only` 가 `true` 이면 이미지 확장자만 허용한다. -- `file.upload.types.{key}.resize.enabled` 가 `true` 이고 업로드 파일이 이미지이면 resize 함수를 거친다. `width` 와 `height` 가 모두 있으면 중앙 crop 후 고정 크기로 저장하고, `max-width` 만 있으면 비율을 유지해 축소한다. -- `spring.servlet.multipart.max-file-size` 와 `max-request-size` 는 `-1` 로 두고, 실제 제한은 업로드 서비스 정책에서 처리한다. - -## file-domain nginx 기준 -- `/tus/file/` 는 tusd 로 프록시하며 `auth_request /_upload_auth` 로 API의 `/tus/file/upload/auth` 를 호출한다. -- `/_upload_auth` 는 내부 location 으로만 열고 `Authorization`, `X-File-Uuid`, 필요 시 `Upload-Metadata`, `Upload-Length` 헤더를 API 로 전달한다. -- `/uploads/` 는 `/srv/project/alist/uploads/` 를 정적 파일로 제공한다. -- `/uploads/tmp/` 는 tusd 임시 파일 노출을 막기 위해 404 처리한다. -- 그 외 경로는 `location /` fallback 에서 차단한다. +| `pjt` | 프로젝트 개발 환경 | ## 실행 명령 -- 로컬 실행: `./gradlew bootRun` -- 테스트 실행: `./gradlew test` -- jar 생성: `./gradlew bootJar` -- Windows 명령: `.\gradlew.bat bootRun`, `.\gradlew.bat test`, `.\gradlew.bat bootJar` + +```bash +./gradlew :02-api-admin:bootRun --args='--spring.profiles.active=local' +./gradlew :03-api-front:bootRun --args='--spring.profiles.active=local' +``` + +```bash +./gradlew :02-api-admin:bootJar +./gradlew :03-api-front:bootJar +``` + +## Docker 배포 + +- admin과 front는 각각 독립 JAR와 Docker 이미지를 사용합니다. +- admin Dockerfile은 `02-api-admin/Dockerfile`, front Dockerfile은 `03-api-front/Dockerfile`에 둡니다. +- Compose는 `deploy/pjt/compose/` 아래에서 서버별로 분리합니다. + +```bash +./gradlew :02-api-admin:bootJar +docker build -t registry.pjt.kr/alist/api-admin:${IMAGE_TAG} 02-api-admin + +./gradlew :03-api-front:bootJar +docker build -t registry.pjt.kr/alist/api-front:${IMAGE_TAG} 03-api-front +``` + +| 구분 | Admin | Front | +|---|---|---| +| Compose | `api-admin-compose.pjt.yml` | `api-front-compose.pjt.yml` | +| 컨테이너 포트 | `8111` | `8112` | +| 환경파일 | `/srv/project/alist/env/api-admin/.env` | `/srv/project/alist/env/api-front/.env` | +| 로그 파일 | `/srv/project/alist/logs/api-admin/app/api-admin.log` | `/srv/project/alist/logs/api-front/app/api-front.log` | + +두 서버 모두 파일 저장 경로를 공유해야 하므로 `/srv/project/alist/uploads`를 같은 컨테이너 경로로 마운트합니다. + +## Jenkins 배포 + +- 루트 `Jenkinsfile.pjt`는 프로젝트 환경 배포용이며, `TARGET` 파라미터로 `admin`, `front`, `all`을 선택합니다. +- 선택한 대상만 Gradle `bootJar`, Docker image build/push, 원격 Compose 배포를 수행합니다. +- admin과 front는 서로 다른 Compose project(`alist-api-admin`, `alist-api-front`)로 실행하므로 한쪽 배포가 다른 서버 컨테이너를 내리지 않습니다. +- Jenkins 서버에는 `registry-pjt`, `ssh-pjt` credential이 필요합니다. + +## Swagger + +- admin: `http://localhost:8111/swagger-ui.html` +- front: `http://localhost:8112/swagger-ui.html` +- 인증 정보는 각 모듈 환경 설정의 `swagger.login.id`, `swagger.login.password`를 사용합니다. + +## MyBatis와 리소스 + +- main Mapper XML은 `01-api-core/src/main/resources/mapper/standard/**/*.xml` 또는 `mapper/bespoke/**/*.xml`에 둡니다. +- `MainDataSourceConfig`가 위 두 경로만 명시적으로 스캔합니다. admin/front YAML의 MyBatis 공통 속성보다 core의 `MainDataSourceConfig` 설정을 기준으로 확인합니다. +- migration Mapper XML은 `01-api-core/src/main/resources/mapper/migration/**`에 두며, migration 전용 SqlSessionFactory가 `classpath*:` 기준으로 로드합니다. main DataSource 스캔 대상은 아닙니다. +- migration DataSource는 front의 `migration.datasource.alist.enabled`, `migration.datasource.eltown.enabled`가 `true`인 경우에만 활성화합니다. admin YAML에는 migration 설정을 두지 않습니다. +- Mapper Java 인터페이스, 내부 DTO/VO, XML namespace와 XML id는 core 업무 모듈 이관 시 함께 이동합니다. + +## 파일 업로드와 TUS + +- 공통 저장 루트와 파일 조회 도메인은 `file.storage.root-path`, `file.storage.view-domain`으로 관리하며 admin/front 양쪽에 둡니다. +- 단순 파일 업로드의 확장자, 크기, 타입별 정책은 `file.upload.*`으로 관리합니다. +- TUS 설정(`tus-file.upload.*`)은 TUS를 실행하는 admin 서버에만 둡니다. Front YAML에는 두지 않습니다. +- TUS 완료 파일은 `file.storage.root-path` 아래로 이동하고, tusd 임시 파일은 `tus-file.upload.tmp-root` 아래에 둡니다. +- file-domain nginx와 tusd 설정은 `/tus/file/**` 경로, 최종 저장 루트, 임시 저장 루트가 일치하는지 함께 확인합니다. diff --git a/docs/security-and-response.md b/docs/security-and-response.md index cd1e95f..e77a12a 100644 --- a/docs/security-and-response.md +++ b/docs/security-and-response.md @@ -1,39 +1,46 @@ # 보안 및 응답 규칙 -## 예외/응답 규칙 -- 공통 예외 응답은 `GlobalExceptionHandler` 에서 처리하므로 Controller 별 개별 예외 처리를 중복해서 늘리지 않는다. -- `@Valid`, 바인딩 실패, JSON 파싱 실패, 타입 오류는 `CODE_4001` 로 통일하고 필드 오류가 있으면 `Map` 형태로 반환한다. -- 단순 성공/실패 문자열을 직접 내려주기보다 `ApiResponse.entity(...)` 와 `ApiResponseCode` 조합을 우선 사용한다. -- 404/405/500 같은 공통 HTTP 오류도 가능하면 `ApiResponseCode` enum 으로 맞춘다. +## 예외와 응답 + +- 공통 예외 응답은 `GlobalExceptionHandler`에서 처리합니다. +- Controller는 `ApiResponse.entity(...)`와 `ApiResponseCode` 조합을 우선 사용합니다. +- 바이너리 파일 view/download처럼 필요한 경우만 `ResponseEntity`를 직접 반환합니다. +- `@Valid`, 바인딩 실패, JSON 파싱 실패, 타입 오류는 `CODE_4001`로 통일합니다. ## 보안 구조 -- Swagger: `/v3/api-docs/**`, `/swagger-ui/**` → HTTP Basic 인증 (InMemory) -- API: JWT Bearer 토큰 인증 (Stateless) -- 세션/쿠키: Redis Session 저장소 사용, 쿠키 속성은 프로파일별 `cookie.*` 설정으로 제어 -- Admin API: `/admin/**` 는 별도 `SecurityFilterChain` 으로 분리하며 `/admin/auth/**`, `/admin/user/add` 만 공개하고 나머지는 `ADMIN` 권한을 요구한다. -- 공개 경로: `/`, `/actuator/health`, `/sso/**`, `/auth/**`, `/user/add`, `/user/migration/list`, `/tus/file/hook`, `/tus/file/upload/auth`, `/file/**` -- TUS 업로드 토큰은 일반 access token 이 아니므로 `/tus/file/upload/auth`, `/tus/file/hook` 은 공개 경로와 file-domain nginx `auth_request` 설정을 함께 맞춘다. -- `JwtAuthenticationFilter` 는 shared API(`/tus/file/**`, `/file/**`, `/cors/**`)에서 user access token 또는 admin access token 모두 인증 주체로 받을 수 있다. -- `/admin/auth/loginChecked` 는 admin access token 쿠키의 현재 로그인 상태 확인용이다. 토큰을 재발급하지 않으며 `isAdminAccessToken`, `isAdminRefreshToken`, `loggedIn`, `userId`, `userIdx`, `userTokenIdx`, `userRole` 형태의 값을 반환한다. -- Swagger 인증과 API 인증은 `SecurityFilterChain` 을 분리해서 관리한다. -## 응답 코드 규칙 -- 응답 코드는 `ApiResponseCode` enum 으로 관리한다. +- admin과 front는 각각 독립된 `SecurityConfig`와 `SecurityFilterChain`을 가집니다. +- Swagger 경로는 HTTP Basic으로 별도 보호합니다. +- API는 JWT Bearer 토큰 또는 서버별 HttpOnly 쿠키를 사용합니다. +- admin JWT 필터는 `ADMIN` scope와 `adminAccessToken`만 인증합니다. +- front JWT 필터는 `USER` scope와 `accessToken`만 인증합니다. +- JWT 필터는 URL(`/admin/**` 등)로 서버 종류를 판단하지 않습니다. +- 각 서버의 공개 경로와 역할 정책은 해당 서버 `SecurityConfig`에서 관리합니다. +- admin/front는 물리적으로 분리되어 있으므로 API path에 `/admin` 또는 `/front` 접두어를 두지 않습니다. 서버 역할은 배포 대상과 `SecurityConfig`가 구분합니다. -| 코드 | 메시지 | HTTP Status | 용도 | -|------|--------|-------------|------| -| `CODE_200` | 성공 | 200 OK | 일반 성공 | -| `CODE_400` | 잘못된 요청 | 400 Bad Request | 일반 클라이언트 오류 | -| `CODE_401` | 인증 필요 합니다. | 401 Unauthorized | 인증 없음 | -| `CODE_403` | 접근 권한 필요 합니다. | 403 Forbidden | 권한 없음 | -| `CODE_404` | 페이지를 찾을 수 없습니다. | 404 Not Found | 리소스 없음 | -| `CODE_405` | 잘못된 요청입니다. 요청 방식을 확인해 주세요. | 405 Method Not Allowed | 메서드 불일치 | -| `CODE_500` | 요청을 처리하는 중 오류가 발생했습니다. | 500 Internal Server Error | 서버 오류 | -| `CODE_2001` | {0} 정보 조회에 성공하였습니다. | 200 OK | 단건 조회 성공 | -| `CODE_2002` | {0} 등록 되었습니다. | 201 Created | 등록 성공 | -| `CODE_2003` | 조회된 정보가 없습니다. | 200 OK | 조회 결과 없음 | -| `CODE_2004` | 중복된 {0} 정보 입니다. | 409 Conflict | 중복 데이터 | -| `CODE_4001` | 입력값을 확인해주세요. | 400 Bad Request | `@Valid` / 바인딩 / 타입오류 / JSON 파싱 실패 | -| `CODE_4003` | 필수 요청 파라미터가 누락되었습니다. | 400 Bad Request | 필수 파라미터 누락 | +## 서버별 정책 -- `{0}` 자리에 대상명 삽입 (예: `CODE_2001` → "회원 정보 조회에 성공하였습니다.") +- admin API는 기본적으로 `ADMIN` 역할을 요구합니다. +- front API는 사용자 토큰을 인증 주체로 사용합니다. +- `/actuator/health`는 각 서버의 헬스체크 경로입니다. +- TUS는 admin 서버 이관 대상이며, front에는 TUS 공개 경로를 두지 않습니다. + +## 응답 코드 + +응답 코드는 `ApiResponseCode` enum으로 관리합니다. + +| 코드 | HTTP Status | 용도 | +|---|---|---| +| `CODE_200` | 200 OK | 일반 성공 | +| `CODE_400` | 400 Bad Request | 일반 클라이언트 오류 | +| `CODE_401` | 401 Unauthorized | 인증 없음 | +| `CODE_403` | 403 Forbidden | 권한 없음 | +| `CODE_404` | 404 Not Found | 리소스 없음 | +| `CODE_405` | 405 Method Not Allowed | 메서드 불일치 | +| `CODE_500` | 500 Internal Server Error | 서버 오류 | +| `CODE_2001` | 200 OK | 단건 조회 성공 | +| `CODE_2002` | 201 Created | 등록 성공 | +| `CODE_2003` | 200 OK | 조회 결과 없음 | +| `CODE_2004` | 409 Conflict | 중복 데이터 | +| `CODE_4001` | 400 Bad Request | 입력값, 바인딩, 타입 오류 | +| `CODE_4003` | 400 Bad Request | 필수 요청 파라미터 누락 | diff --git a/docs/testing-strategy.md b/docs/testing-strategy.md new file mode 100644 index 0000000..8d6f822 --- /dev/null +++ b/docs/testing-strategy.md @@ -0,0 +1,89 @@ +# 테스트 전략 + +## 목적 + +- 기능 이관과 신규 개발에서 테스트 범위를 과도하게 넓히지 않고 위험도에 맞춰 검증한다. +- 단위 테스트, 통합 테스트, 실제 연동 검증의 책임을 분리한다. +- 기능 또는 모듈을 완료할 때 실제 동작과 데이터 반영까지 확인한다. + +## 테스트 구분 + +| 구분 | 목적 | 외부 의존성 | 실행 시점 | +|---|---|---|---| +| 단위 테스트 | 분기, 결과 코드, DTO/VO 변환, 권한 판단 검증 | 사용하지 않음 | 구현 중 수시 실행 | +| 통합 테스트 | Spring, MyBatis, Security, DB와 모듈 간 연결 검증 | 테스트 DB 등 필요한 의존성만 사용 | 모듈 기능 완료 시 | +| 실제 연동 검증 | 실행 API와 실제 데이터 저장·갱신 결과 확인 | 기능이 사용하는 실제 의존성만 사용 | 모듈 이관 완료 후 | + +## Red-Green 흐름 + +1. 기능의 핵심 성공·실패 조건을 테스트로 먼저 작성한다. +2. 테스트가 실패하는 Red 상태를 확인한다. +3. 최소 구현으로 테스트를 통과하는 Green 상태를 만든다. +4. 필요할 때만 리팩터링하고 같은 테스트를 다시 실행한다. + +이미 이관이 끝난 기능에는 기존 동작을 고정하는 회귀 테스트를 추가한다. 구현된 기능을 의도적으로 깨뜨려 Red 상태를 재현하지 않는다. + +## 단위 테스트 기준 + +- 단순 CRUD 보일러플레이트마다 테스트를 강제하지 않는다. +- 다음 중 하나가 있으면 단위 테스트를 우선 추가한다. + - 권한 또는 역할 분기 + - 중복·상태·입력값 검증 + - 여러 DTO/VO 간 변환 + - 날짜, 금액, 카운트 등 계산 + - 실패 결과 코드와 예외 처리 +- 외부 DB, Redis, 파일 시스템, HTTP 호출은 Mock 또는 Fake로 대체한다. + +## 통합 테스트 기준 + +- 모듈 단위 작업이 완료되면 해당 실행 모듈과 Core를 함께 검증한다. +- MyBatis Mapper XML, namespace, SQL 결과 매핑, Spring Bean 구성, Security 경로를 확인한다. +- 테스트 환경은 개발 공용 데이터가 아닌 전용 테스트 DB와 테스트 Redis를 우선 사용한다. +- Redis Pub/Sub처럼 커밋 후 동작이 필요한 기능은 실제 트랜잭션 커밋과 수신 결과를 검증한다. + +## 실제 연동 검증 기준 + +실제 연동 검증은 모든 기능에 같은 항목을 강제하지 않는다. + +```text +기본: API 호출 -> DB 저장 또는 변경 확인 -> 조회 API 확인 + +Redis 사용 기능: Redis Key, 캐시, Pub/Sub 수신 결과 추가 확인 +파일 사용 기능: 파일 저장, 조회, 삭제, 경로 처리 추가 확인 +외부 API 기능: 요청, 응답, 실패 처리 추가 확인 +메시지 기능: 발행, 수신, 재처리 결과 추가 확인 +``` + +- 실제 개발 데이터를 변경해야 하면 테스트용 식별값을 사용한다. +- 등록·수정·삭제 테스트 후에는 테스트 데이터를 삭제하거나 원복한다. +- 검증하지 못한 외부 의존성은 이유와 미검증 범위를 남긴다. + +## 실행 순서 + +```text +기능 구현 중 + -> 관련 모듈 compileJava + -> 필요한 단위 테스트 + +모듈 작업 완료 + -> Core 및 실행 모듈 build + -> 통합 테스트 + -> 실제 API / DB / 기능별 의존성 검증 + +배포 전 + -> Admin / Front 전체 빌드 + -> 핵심 시나리오 회귀 테스트 +``` + +## Gradle 기준 + +- 현재 최소 컴파일 검증 명령 + +```bash +./gradlew :01-api-core:compileJava +./gradlew :02-api-admin:compileJava +./gradlew :03-api-front:compileJava +``` + +- 단위 테스트와 통합 테스트가 추가되면 `unitTest`, `integrationTest` 태스크로 분리한다. +- 일상 개발에서는 빠른 단위 테스트를 우선 실행하고, 모듈 완료·배포 전에는 통합 테스트까지 실행한다. diff --git a/docs/verification-checklist.md b/docs/verification-checklist.md index 7580507..47d645d 100644 --- a/docs/verification-checklist.md +++ b/docs/verification-checklist.md @@ -1,26 +1,48 @@ # 검증 및 체크리스트 -## 기본 검증 원칙 -- 현재 테스트 코드는 최소 수준이므로 기능 수정 시 단위 테스트 또는 최소 통합 검증 범위를 직접 보강하는 쪽을 우선한다. -- 기동 실패 가능성이 있는 설정 변경은 실행 또는 테스트로 검증한다. -- 검증하지 못한 내용은 추정으로 말하지 않고 미실행 사유를 적는다. +## 기본 검증 -## 변경 시 체크리스트 -- 변경한 코드와 직접 관련된 파일만 수정했는지 확인한다. -- 새 API, 스케줄러, 설정 추가 시 관련 설정 파일과 테스트를 함께 검토한다. -- 로그 레벨과 로그량이 운영 환경에서 감당 가능한지 확인한다. -- Mapper 인터페이스 추가/변경 시 XML namespace, id, parameter/result 매핑이 같이 맞는지 확인한다. -- 공개 경로나 권한 정책을 바꿨다면 SecurityConfig 와 Swagger 노출 범위를 같이 확인한다. -- 파일 업로드/다운로드 기능 수정 시 DB 상태, Redis 상태, 실제 파일 시스템 경로가 같이 맞는지 확인한다. -- TUS 경로를 바꾸면 nginx `/_upload_auth`, tusd hook URL, `SecurityConfig` 공개 경로, `tus-file.upload.tus-endpoint` 를 함께 확인한다. -- `/file/**`, `/tus/file/**`, `/cors/**` 같은 shared API 권한을 바꾸면 user/admin 토큰 쿠키와 Bearer 인증이 모두 의도대로 동작하는지 확인한다. -- SunEditor 또는 단순 업로드 설정을 바꾸면 `file.upload.root-path`, `file.upload.view.file-domain`, nginx `/uploads/` alias 경로가 같은 저장 루트를 가리키는지 확인한다. +- 변경 범위와 직접 관련된 모듈만 수정했는지 확인합니다. +- core 변경은 `:01-api-core:compileJava`를, 실행 모듈 변경은 `:02-api-admin:compileJava`, `:03-api-front:compileJava`를 최소 검증으로 사용합니다. +- 실행 설정이나 빈 구성이 바뀌면 admin/front를 각각 기동하거나 관련 테스트를 실행합니다. +- 검증하지 못한 항목은 사유를 명확히 남깁니다. -## 기능 특성별 점검 포인트 -- 스케줄러 코드는 실행 주기, 중복 실행 가능성, 로그량을 반드시 점검한다. -- 인증 방식이 섞여 있으므로 세션 기반 처리와 JWT `SecurityContext` 사용 위치를 먼저 구분하고 수정한다. -- 파일 경로를 다루는 기능은 상대경로 탈출, 루트 이탈 방지 같은 검증을 같이 본다. -- 설정 파일 수정 시 `local`, `pjt`, 공통 설정 간 차이를 함께 확인한다. -- file-domain 정적 파일은 `/uploads/editor/...` 같은 최종 파일 URL이 브라우저에서 직접 열리는지 확인한다. -- `/uploads/tmp/...` 는 404로 막히는지 확인한다. -- SunEditor 업로드는 응답 JSON의 `result[].url` 이 file-domain 절대 URL인지 확인하고, 에디터 본문에 이미지가 실제 삽입되는지 확인한다. +## 모듈 완료 검증 + +- 구현 중에는 관련 모듈의 `compileJava`와 필요한 단위 테스트를 수시 실행합니다. +- 모듈 작업을 완료하면 Core와 해당 실행 모듈의 `build`를 실행합니다. +- 통합 테스트는 Spring, MyBatis, Security, DB 등 해당 기능의 연결 범위를 검증합니다. +- 실제 연동 검증은 `API -> DB -> 조회 API`를 기본으로 합니다. +- Redis, 파일, 외부 API, 메시지 등은 해당 기능이 실제로 사용할 때만 추가 검증합니다. +- 실제 개발 데이터를 변경한 테스트는 테스트 데이터를 삭제하거나 원복합니다. + +## 업무 이관 + +- Java Mapper, Mapper XML, namespace, id, DTO 패키지 참조를 함께 변경합니다. +- 범용 처리는 `standard`, 업무 전용 처리는 `bespoke` 기준으로 배치했는지 확인합니다. +- core에 Form, 화면 전용 VO, Controller가 들어가지 않았는지 확인합니다. +- admin/front에 다른 서버 전용 Controller가 포함되지 않았는지 확인합니다. +- Controller가 core DTO/VO, core Service 또는 Mapper를 직접 참조하지 않는지 확인합니다. +- admin/front Service가 core standard/bespoke 호출 조합과 트랜잭션 경계를 소유하는지 확인합니다. +- bespoke Service/Mapper/XML id가 기준 테이블과 기능명, 결과 형태 규칙을 같은 이름으로 사용하는지 확인합니다. + +## 보안 + +- admin은 `ADMIN` scope와 `adminAccessToken`으로 인증되는지 확인합니다. +- front는 `USER` scope와 `accessToken`으로 인증되는지 확인합니다. +- 공개 경로와 역할 정책은 각 서버 `SecurityConfig`에서 확인합니다. +- Swagger Basic 인증과 API JWT 인증이 각각 의도대로 동작하는지 확인합니다. + +## 설정과 리소스 + +- admin/front의 `application*.yaml`, 로그 설정, banner, robots 리소스가 각 모듈에 있는지 확인합니다. +- core Mapper XML이 두 bootJar에서 모두 classpath로 읽히는지 확인합니다. +- `MainDataSourceConfig`가 `mapper/standard`, `mapper/bespoke`의 이관된 XML만 읽는지 확인합니다. +- migration DataSource를 사용하지 않는 환경에서는 `migration.datasource.*.enabled`가 활성화되지 않았는지 확인합니다. +- 파일 또는 TUS를 변경하면 저장 경로, nginx, tusd, SecurityConfig를 함께 확인합니다. + +## 빌드 산출물 + +- `:02-api-admin:bootJar` 결과가 `api-admin.jar`인지 확인합니다. +- `:03-api-front:bootJar` 결과가 `api-front.jar`인지 확인합니다. +- admin JAR에 front Controller가, front JAR에 admin Controller가 포함되지 않는지 확인합니다. diff --git a/settings.gradle b/settings.gradle index 5cd7dd3..134098e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,5 @@ -rootProject.name = 'api' +rootProject.name = 'alist-api' + +include '01-api-core' +include '02-api-admin' +include '03-api-front' \ No newline at end of file diff --git a/src/main/java/com/alist/api/ApiApplication.java b/src/main/java/com/alist/api/ApiApplication.java deleted file mode 100644 index 222e71e..0000000 --- a/src/main/java/com/alist/api/ApiApplication.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.alist.api; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.ConfigurationPropertiesScan; - -@SpringBootApplication -@ConfigurationPropertiesScan -public class ApiApplication { - - public static void main(String[] args) { - SpringApplication.run(ApiApplication.class, args); - } - -} diff --git a/src/main/java/com/alist/api/config/cache/CorsCache.java b/src/main/java/com/alist/api/config/cache/CorsCache.java deleted file mode 100644 index 62c3150..0000000 --- a/src/main/java/com/alist/api/config/cache/CorsCache.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.alist.api.config.cache; - -import com.alist.api.modules.cors.mapper.CorsMapper; -import com.alist.api.modules.cors.vo.CorsCacheVo; -import jakarta.annotation.PostConstruct; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -@Slf4j -@Component -public class CorsCache { - - private final CorsMapper corsMapper; - - // Thread-safe List (volatile로 가시성 보장) - private volatile List corsOriginList = new ArrayList<>(); - - public CorsCache(CorsMapper corsMapper) { - this.corsMapper = corsMapper; - } - - /** - * 서버 시작 시 DB에서 전체 CORS 목록 로딩 - */ - @PostConstruct - public void loadFromDatabase() { - refresh(); - log.info("CORS allowed origins loaded..."); - } - - /** - * DB에서 전체 CORS 목록 다시 로딩 - */ - public synchronized void refresh() { - try { - List corsOriginList = corsMapper.selectCorsCacheList() - .stream() - .filter(Objects::nonNull) - .map(CorsCacheVo::getAllowedOrigin) - .filter(Objects::nonNull) - .map(String::trim) - .filter(origin -> !origin.isEmpty()) - .distinct() - .collect(Collectors.toList()); - - this.corsOriginList = corsOriginList; - - log.info("CORS cache refreshed. Total origins: {}", corsOriginList.size()); - - } catch (Exception e) { - log.error("Failed to refresh CORS cache", e); - } - } - - /** - * 허용 도메인 목록 조회 - * @return 허용 도메인 리스트 - */ - public List getAllowedOrigins() { - return new ArrayList<>(corsOriginList); - } - - /** - * 현재 캐시 상태 조회 - * @return 캐시 상태 정보 - */ - public List getCacheStatus() { - return new ArrayList<>(corsOriginList); - } -} diff --git a/src/main/java/com/alist/api/config/filter/DynamicCorsFilter.java b/src/main/java/com/alist/api/config/filter/DynamicCorsFilter.java deleted file mode 100644 index dd74be1..0000000 --- a/src/main/java/com/alist/api/config/filter/DynamicCorsFilter.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.alist.api.config.filter; - -import com.alist.api.config.cache.CorsCache; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; -import java.util.List; - -@Slf4j -@Component -@Order(Ordered.HIGHEST_PRECEDENCE) -@RequiredArgsConstructor -public class DynamicCorsFilter extends OncePerRequestFilter { - - private final CorsCache corsCache; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - - String origin = request.getHeader("Origin"); - - // 메모리에서 허용 도메인 조회 (DB 조회 없음!) - List allowedOrigins = corsCache.getAllowedOrigins(); - - // Origin 허용 여부 체크 - boolean isAllowed = false; - if (origin != null && (allowedOrigins.contains("*") || allowedOrigins.contains(origin))) { - isAllowed = true; - } - - // CORS 체크 결과 로깅 - if (origin != null) { - if (isAllowed && log.isDebugEnabled()) { - log.debug("CORS allowed - Origin: {}", origin); - } else if (!isAllowed) { - log.warn("CORS blocked - Origin: {}, AllowedOrigins: {}", origin, allowedOrigins); - } - } - - // CORS 헤더 설정 - if (isAllowed) { - response.setHeader("Access-Control-Allow-Origin", origin); - response.setHeader("Access-Control-Allow-Credentials", "true"); - response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); - response.setHeader("Access-Control-Allow-Headers", - "Content-Type, Authorization, X-Requested-With, Accept, Origin, Referer, Content-Disposition, Content-Length"); - response.setHeader("Access-Control-Expose-Headers", "Authorization, Set-Cookie"); - response.setHeader("Access-Control-Max-Age", "3600"); - } - - // Preflight 요청(OPTIONS) 처리 - if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { - response.setStatus(HttpServletResponse.SC_OK); - return; - } - - filterChain.doFilter(request, response); - } -} diff --git a/src/main/java/com/alist/api/config/jwt/JwtAuthenticationFilter.java b/src/main/java/com/alist/api/config/jwt/JwtAuthenticationFilter.java deleted file mode 100644 index 3fb2630..0000000 --- a/src/main/java/com/alist/api/config/jwt/JwtAuthenticationFilter.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.alist.api.config.jwt; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.http.HttpHeaders; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; -import java.util.List; - -public class JwtAuthenticationFilter extends OncePerRequestFilter { - private final JwtTokenProvider jwtTokenProvider; - - public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { - this.jwtTokenProvider = jwtTokenProvider; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - - try { - String requestUri = request.getRequestURI(); - boolean adminRequest = requestUri.startsWith("/admin/"); - boolean sharedRequest = requestUri.startsWith("/tus/file/") || requestUri.startsWith("/file/") || requestUri.startsWith("/cors/"); - String token = resolveToken(request, adminRequest, sharedRequest); - - if (token != null && jwtTokenProvider.validateToken(token)) { - String principal = jwtTokenProvider.getUserTokenIdx(token); - String role = jwtTokenProvider.getRole(token); - String scope = jwtTokenProvider.getScope(token); - String tokenType = jwtTokenProvider.getTokenType(token); - - boolean userAccessToken = "USER".equals(scope) && role != null && "ACCESS".equals(tokenType); - - boolean adminAccessToken = "ADMIN".equals(scope) && "ADMIN".equals(role) && "ACCESS".equals(tokenType); - - boolean authenticatable = adminRequest ? adminAccessToken : sharedRequest ? userAccessToken || adminAccessToken : userAccessToken; - - if (!authenticatable) { - filterChain.doFilter(request, response); - return; - } - - UsernamePasswordAuthenticationToken authentication = - new UsernamePasswordAuthenticationToken( - principal, - null, - List.of(new SimpleGrantedAuthority("ROLE_" + role)) - ); - - authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - } catch (Exception e) { - SecurityContextHolder.clearContext(); - } - - filterChain.doFilter(request, response); - } - - private String resolveToken(HttpServletRequest request, boolean adminRequest, boolean sharedRequest) { - String bearer = request.getHeader(HttpHeaders.AUTHORIZATION); - if (bearer != null && bearer.startsWith("Bearer ")) { - String token = bearer.substring(7).trim(); - if (!token.isEmpty()) { - return token; - } - } - - Cookie[] cookies = request.getCookies(); - if (cookies == null) { - return null; - } - - if (adminRequest) { - return resolveCookie(cookies, "adminAccessToken"); - } - - if (sharedRequest) { - String accessToken = resolveCookie(cookies, "accessToken"); - if (accessToken != null) { - return accessToken; - } - return resolveCookie(cookies, "adminAccessToken"); - } - - return resolveCookie(cookies, "accessToken"); - } - - private String resolveCookie(Cookie[] cookies, String cookieName) { - for (Cookie cookie : cookies) { - if (cookieName.equals(cookie.getName())) { - String token = cookie.getValue(); - if (token != null && !token.isBlank()) { - return token.trim(); - } - } - } - return null; - } -} diff --git a/src/main/java/com/alist/api/config/properties/JwtProperties.java b/src/main/java/com/alist/api/config/properties/JwtProperties.java deleted file mode 100644 index dbda00a..0000000 --- a/src/main/java/com/alist/api/config/properties/JwtProperties.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.alist.api.config.properties; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; - -@Getter -@Setter -@ConfigurationProperties(prefix = "jwt") -public class JwtProperties { - String secret; - long accessTokenValiditySeconds; - long refreshTokenValiditySeconds; -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/AdminAuthController.java b/src/main/java/com/alist/api/modules/admin/auth/AdminAuthController.java deleted file mode 100644 index ed08a52..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/AdminAuthController.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.alist.api.modules.admin.auth; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.common.utils.SecurityUtil; -import com.alist.api.common.utils.SessionUtil; -import com.alist.api.modules.admin.auth.form.AdminAuthApiKeyLoginForm; -import com.alist.api.modules.admin.auth.form.AdminAuthLoginForm; -import com.alist.api.modules.admin.auth.service.AdminAuthService; -import com.alist.api.modules.admin.auth.vo.*; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@Tag( - name = "501. Admin 로그인" - , description = "ADMIN 사이트 로그인입니다. 로그인시 토큰 발급이 함께됩니다." -) -@RestController -@RequestMapping("/admin/auth") -public class AdminAuthController { - - private final AdminAuthService adminAuthService; - - public AdminAuthController(AdminAuthService adminAuthService) { - this.adminAuthService = adminAuthService; - } - - @Operation( - summary = "admin 로그인" - , description = "admin로그인 sso와 다르게 로그인시 토큰이 함께 발급됩니다." - ) - @PostMapping("/login") - public ResponseEntity> adminAuthLogin( - @Valid @RequestBody AdminAuthLoginForm adminAuthLoginForm - , HttpServletResponse response - ) { - AdminAuthLoginVo adminAuthLoginVo = adminAuthService.selectAdminAuthLogin(adminAuthLoginForm.toDto(), response); - - if (adminAuthLoginVo.getResultCode() == 2003) { - return ApiResponse.entity(adminAuthLoginVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminAuthLoginVo, ApiResponseCode.CODE_2001, "관리자 로그인"); - } - - @Operation( - summary = "admin API Key 로그인" - , description = "Admin API Key로 인증하고 JSON으로 토큰을 반환합니다. Swagger 테스트용입니다." - ) - @PostMapping("/apiKeyLogin") - public ResponseEntity> adminAuthApiKeyLogin( - @Valid @RequestBody AdminAuthApiKeyLoginForm adminAuthApiKeyLoginForm - ) { - AdminAuthApiKeyLoginVo adminAuthApiKeyLoginVo = adminAuthService.selectAdminAuthApiKeyLogin(adminAuthApiKeyLoginForm.toDto()); - - if (adminAuthApiKeyLoginVo.getResultCode() == 2003) { - return ApiResponse.entity(adminAuthApiKeyLoginVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminAuthApiKeyLoginVo, ApiResponseCode.CODE_2001, "관리자 엑세스 토큰"); - } - - @Operation( - summary = "admin 리프레시토큰 발급" - , description = "엑세스 토큰 만료시 리프레시 토큰 발급 용도 api 입니다." - ) - @PostMapping("/refresh") - public ResponseEntity> adminAuthRefresh( - @CookieValue(name = "adminRefreshToken", required = false) String refreshToken - , HttpServletResponse response - ) { - AdminAuthLoginVo adminAuthLoginVo = adminAuthService.selectAdminAuthRefresh(refreshToken, response); - - AdminAuthRefreshVo adminAuthRefreshVo = new AdminAuthRefreshVo(); - adminAuthRefreshVo.setRefreshed(adminAuthLoginVo != null); - - if (adminAuthLoginVo == null) { - return ApiResponse.entity(adminAuthRefreshVo, ApiResponseCode.CODE_401); - } - - return ApiResponse.entity(adminAuthRefreshVo, ApiResponseCode.CODE_200); - } - - @Operation( - summary = "admin 로그인 상태 확인" - , description = "admin 쿠키 기준으로 현재 공통 로그인 상태가 유효한지 확인하고, access/refresh 토큰 존재 여부도 함께 반환합니다." - ) - @GetMapping("/loginChecked") - public ResponseEntity> adminAuthLoginChecked( - HttpServletRequest request - ) { - AdminAuthLoginCheckedVo adminAuthLoginCheckedVo = new AdminAuthLoginCheckedVo(); - - String accessToken = SessionUtil.resolveSsoCookieValue(request, "adminAccessToken"); - String refreshToken = SessionUtil.resolveSsoCookieValue(request, "adminRefreshToken"); - - adminAuthLoginCheckedVo.setAdminAccessToken(accessToken != null && !accessToken.trim().isEmpty()); - adminAuthLoginCheckedVo.setAdminRefreshToken(refreshToken != null && !refreshToken.trim().isEmpty()); - - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (userTokenIdx == null) { - adminAuthLoginCheckedVo.setLoggedIn(false); - return ApiResponse.entity(adminAuthLoginCheckedVo, ApiResponseCode.CODE_200); - } - - AdminAuthLoginVo adminAuthLoginVo = adminAuthService.selectAdminAuthLoginChecked(userTokenIdx); - - if (adminAuthLoginVo == null) { - adminAuthLoginCheckedVo.setLoggedIn(false); - return ApiResponse.entity(adminAuthLoginCheckedVo, ApiResponseCode.CODE_200); - } - - adminAuthLoginCheckedVo.setLoggedIn(true); - adminAuthLoginCheckedVo.setUserId(adminAuthLoginVo.getId()); - adminAuthLoginCheckedVo.setUserIdx(adminAuthLoginVo.getUserIdx()); - adminAuthLoginCheckedVo.setUserTokenIdx(userTokenIdx); - adminAuthLoginCheckedVo.setUserRole(adminAuthLoginVo.getUserRole()); - adminAuthLoginCheckedVo.setUserType(adminAuthLoginVo.getUserType()); - - return ApiResponse.entity(adminAuthLoginCheckedVo, ApiResponseCode.CODE_200); - } - - @Operation( - summary = "admin 로그아웃" - , description = "admin 로그아웃" - ) - @PostMapping("/logout") - public ResponseEntity> adminAuthLogout( - @CookieValue(name = "adminRefreshToken", required = false) String refreshToken - , HttpServletResponse response - ) { - adminAuthService.updateAdminAuthLogout(refreshToken, response); - - AdminAuthLogoutVo adminAuthLogoutVo = new AdminAuthLogoutVo(); - adminAuthLogoutVo.setLoggedOut(true); - - return ApiResponse.entity(adminAuthLogoutVo, ApiResponseCode.CODE_200); - } -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/dto/AdminAuthApiKeyLoginDto.java b/src/main/java/com/alist/api/modules/admin/auth/dto/AdminAuthApiKeyLoginDto.java deleted file mode 100644 index fe86e2b..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/dto/AdminAuthApiKeyLoginDto.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alist.api.modules.admin.auth.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.time.Instant; - -@Getter -@Setter -public class AdminAuthApiKeyLoginDto { - private String userApiKey; - private Integer userIdx; - private Integer userTokenIdx; - private String refreshToken; - private Instant expiresAt; -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/dto/AdminAuthLoginDto.java b/src/main/java/com/alist/api/modules/admin/auth/dto/AdminAuthLoginDto.java deleted file mode 100644 index efb85f8..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/dto/AdminAuthLoginDto.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.admin.auth.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.time.Instant; - -@Setter -@Getter -public class AdminAuthLoginDto { - private String id; - private String password; - - private Integer userIdx; - private Integer userTokenIdx; - private String refreshToken; - private Instant expiresAt; -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/form/AdminAuthApiKeyLoginForm.java b/src/main/java/com/alist/api/modules/admin/auth/form/AdminAuthApiKeyLoginForm.java deleted file mode 100644 index 9c01196..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/form/AdminAuthApiKeyLoginForm.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alist.api.modules.admin.auth.form; - -import com.alist.api.modules.admin.auth.dto.AdminAuthApiKeyLoginDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "Admin API Key 로그인 요청 폼") -@Getter -@Setter -public class AdminAuthApiKeyLoginForm { - @Schema( - description = "Admin API Key", - example = "ypdz9hl7WAp2D03HC5h9koEC49R6LAkLSuoSdgEwVQA", - requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "apiKey를 입력해주세요.") - private String userApiKey; - - public AdminAuthApiKeyLoginDto toDto() { - AdminAuthApiKeyLoginDto adminAuthApiKeyLoginDto = new AdminAuthApiKeyLoginDto(); - adminAuthApiKeyLoginDto.setUserApiKey(userApiKey.trim()); - return adminAuthApiKeyLoginDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/form/AdminAuthLoginForm.java b/src/main/java/com/alist/api/modules/admin/auth/form/AdminAuthLoginForm.java deleted file mode 100644 index 1460b88..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/form/AdminAuthLoginForm.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.alist.api.modules.admin.auth.form; - -import com.alist.api.modules.admin.auth.dto.AdminAuthLoginDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "Admin 로그인 요청 폼") -@Getter -@Setter -public class AdminAuthLoginForm { - @Schema( - description = "관리자 아이디", - example = "admin", - requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "아이디는 필수입니다.") - private String id; - - @Schema( - description = "관리자 비밀번호", - example = "password", - requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "비밀번호는 필수입니다.") - private String password; - - public AdminAuthLoginDto toDto() { - AdminAuthLoginDto adminAuthLoginDto = new AdminAuthLoginDto(); - adminAuthLoginDto.setId(id.trim()); - adminAuthLoginDto.setPassword(password); - - return adminAuthLoginDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/mapper/AdminAuthMapper.java b/src/main/java/com/alist/api/modules/admin/auth/mapper/AdminAuthMapper.java deleted file mode 100644 index b551af6..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/mapper/AdminAuthMapper.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.alist.api.modules.admin.auth.mapper; - -import com.alist.api.modules.admin.auth.dto.AdminAuthApiKeyLoginDto; -import com.alist.api.modules.admin.auth.dto.AdminAuthLoginDto; -import com.alist.api.modules.admin.auth.vo.AdminAuthApiKeyLoginVo; -import com.alist.api.modules.admin.auth.vo.AdminAuthLoginVo; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface AdminAuthMapper { - AdminAuthLoginVo selectAdminAuthLogin(String id); - - void updateAdminRefreshToken(AdminAuthLoginDto adminAuthLoginDto); - - void updateAdminLastLoginAt(Integer userIdx); - - AdminAuthLoginVo selectAdminTokenByUserTokenIdx(Integer userTokenIdx); - - void updateClearAdminRefreshToken(Integer userTokenIdx); - - AdminAuthApiKeyLoginVo selectAdminAuthApiKeyLogin(AdminAuthApiKeyLoginDto adminAuthApiKeyLoginDto); - - void updateAdminApiKeyLoginRefreshToken(AdminAuthApiKeyLoginDto adminAuthApiKeyLoginDto); -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/service/AdminAuthService.java b/src/main/java/com/alist/api/modules/admin/auth/service/AdminAuthService.java deleted file mode 100644 index aa6eab7..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/service/AdminAuthService.java +++ /dev/null @@ -1,193 +0,0 @@ -package com.alist.api.modules.admin.auth.service; - -import com.alist.api.common.utils.SessionUtil; -import com.alist.api.config.jwt.JwtTokenProvider; -import com.alist.api.modules.admin.auth.dto.AdminAuthApiKeyLoginDto; -import com.alist.api.modules.admin.auth.dto.AdminAuthLoginDto; -import com.alist.api.modules.admin.auth.mapper.AdminAuthMapper; -import com.alist.api.modules.admin.auth.vo.AdminAuthApiKeyLoginVo; -import com.alist.api.modules.admin.auth.vo.AdminAuthLoginVo; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; - -@Service -public class AdminAuthService { - @Value("${cookie.secure}") - private boolean cookieSecure; - - @Value("${cookie.domain:}") - private String cookieDomain; - - @Value("${cookie.same-site:Lax}") - private String cookieSameSite; - - @Value("${jwt.access-token-validity-seconds}") - private long accessTokenValiditySeconds; - - @Value("${jwt.refresh-token-validity-seconds}") - private long refreshTokenValiditySeconds; - - private final AdminAuthMapper adminAuthMapper; - private final JwtTokenProvider jwtTokenProvider; - private final PasswordEncoder passwordEncoder; - - public AdminAuthService(AdminAuthMapper adminAuthMapper, JwtTokenProvider jwtTokenProvider, PasswordEncoder passwordEncoder) { - this.adminAuthMapper = adminAuthMapper; - this.jwtTokenProvider = jwtTokenProvider; - this.passwordEncoder = passwordEncoder; - } - - @Transactional - public AdminAuthLoginVo selectAdminAuthLogin(AdminAuthLoginDto adminAuthLoginDto, HttpServletResponse response) { - AdminAuthLoginVo adminAuthLoginVo = adminAuthMapper.selectAdminAuthLogin(adminAuthLoginDto.getId()); - - if (adminAuthLoginVo == null) { - AdminAuthLoginVo adminAuthLoginVo1 = new AdminAuthLoginVo(); - adminAuthLoginVo1.setResultCode(2003); - adminAuthLoginVo1.setLogin(false); - return adminAuthLoginVo1; - } - - if (!passwordEncoder.matches(adminAuthLoginDto.getPassword(), adminAuthLoginVo.getPassword())) { - AdminAuthLoginVo adminAuthLoginVo1 = new AdminAuthLoginVo(); - adminAuthLoginVo1.setResultCode(2003); - adminAuthLoginVo1.setLogin(false); - return adminAuthLoginVo1; - } - - String accessToken = jwtTokenProvider.createAdminAccessToken(adminAuthLoginVo.getUserTokenIdx()); - String refreshToken = jwtTokenProvider.createAdminRefreshToken(adminAuthLoginVo.getUserTokenIdx()); - - adminAuthLoginDto.setUserIdx(adminAuthLoginVo.getUserIdx()); - adminAuthLoginDto.setUserTokenIdx(adminAuthLoginVo.getUserTokenIdx()); - adminAuthLoginDto.setRefreshToken(refreshToken); - adminAuthLoginDto.setExpiresAt(Instant.now().plusSeconds(refreshTokenValiditySeconds)); - - adminAuthMapper.updateAdminRefreshToken(adminAuthLoginDto); - adminAuthMapper.updateAdminLastLoginAt(adminAuthLoginDto.getUserIdx()); - - SessionUtil.addTokenCookie(response, "adminAccessToken", accessToken, cookieDomain, cookieSecure, cookieSameSite, accessTokenValiditySeconds); - SessionUtil.addTokenCookie(response, "adminRefreshToken", refreshToken, cookieDomain, cookieSecure, cookieSameSite, refreshTokenValiditySeconds); - - adminAuthLoginVo.setResultCode(2001); - adminAuthLoginVo.setLogin(true); - return adminAuthLoginVo; - } - - @Transactional - public AdminAuthApiKeyLoginVo selectAdminAuthApiKeyLogin(AdminAuthApiKeyLoginDto adminAuthApiKeyLoginDto) { - AdminAuthApiKeyLoginVo adminAuthApiKeyLoginVo = adminAuthMapper.selectAdminAuthApiKeyLogin(adminAuthApiKeyLoginDto); - - if (adminAuthApiKeyLoginVo == null) { - AdminAuthApiKeyLoginVo adminAuthApiKeyLoginVo1 = new AdminAuthApiKeyLoginVo(); - adminAuthApiKeyLoginVo1.setResultCode(2003); - adminAuthApiKeyLoginVo1.setLogin(false); - return adminAuthApiKeyLoginVo1; - } - - String accessToken = jwtTokenProvider.createAdminAccessToken(adminAuthApiKeyLoginVo.getUserTokenIdx()); - String refreshToken = jwtTokenProvider.createAdminRefreshToken(adminAuthApiKeyLoginVo.getUserTokenIdx()); - - adminAuthApiKeyLoginDto.setUserTokenIdx(adminAuthApiKeyLoginVo.getUserTokenIdx()); - adminAuthApiKeyLoginDto.setRefreshToken(refreshToken); - adminAuthApiKeyLoginDto.setExpiresAt(Instant.now().plusSeconds(refreshTokenValiditySeconds)); - - adminAuthMapper.updateAdminApiKeyLoginRefreshToken(adminAuthApiKeyLoginDto); - adminAuthMapper.updateAdminLastLoginAt(adminAuthApiKeyLoginVo.getUserIdx()); - - adminAuthApiKeyLoginVo.setAccessToken(accessToken); - adminAuthApiKeyLoginVo.setRefreshToken(refreshToken); - adminAuthApiKeyLoginVo.setResultCode(2001); - adminAuthApiKeyLoginVo.setLogin(true); - - return adminAuthApiKeyLoginVo; - } - - public AdminAuthLoginVo selectAdminAuthRefresh(String refreshToken, HttpServletResponse response) { - if (refreshToken == null || !jwtTokenProvider.validateToken(refreshToken)) { - return null; - } - - String role = jwtTokenProvider.getRole(refreshToken); - String scope = jwtTokenProvider.getScope(refreshToken); - String tokenType = jwtTokenProvider.getTokenType(refreshToken); - - if (!"ADMIN".equals(role) || !"ADMIN".equals(scope) || !"REFRESH".equals(tokenType)) { - return null; - } - - Integer userTokenIdx; - - try { - userTokenIdx = Integer.parseInt(jwtTokenProvider.getUserTokenIdx(refreshToken)); - } catch (NumberFormatException e) { - return null; - } - - AdminAuthLoginVo adminAuthLoginVo = adminAuthMapper.selectAdminTokenByUserTokenIdx(userTokenIdx); - if (adminAuthLoginVo == null || adminAuthLoginVo.getRefreshToken() == null) { - return null; - } - - if (!refreshToken.equals(adminAuthLoginVo.getRefreshToken())) { - return null; - } - - String newAccessToken = jwtTokenProvider.createAdminAccessToken(userTokenIdx); - String newRefreshToken = jwtTokenProvider.createAdminRefreshToken(userTokenIdx); - - AdminAuthLoginDto adminAuthLoginDto = new AdminAuthLoginDto(); - - adminAuthLoginDto.setUserTokenIdx(userTokenIdx); - adminAuthLoginDto.setRefreshToken(newRefreshToken); - adminAuthLoginDto.setExpiresAt(Instant.now().plusSeconds(refreshTokenValiditySeconds)); - - adminAuthMapper.updateAdminRefreshToken(adminAuthLoginDto); - - SessionUtil.addTokenCookie(response, "adminAccessToken", newAccessToken, cookieDomain, cookieSecure, cookieSameSite, accessTokenValiditySeconds); - SessionUtil.addTokenCookie(response, "adminRefreshToken", newRefreshToken, cookieDomain, cookieSecure, cookieSameSite, refreshTokenValiditySeconds); - - adminAuthLoginVo.setResultCode(2001); - return adminAuthLoginVo; - } - - public void updateAdminAuthLogout(String refreshToken, HttpServletResponse response) { - updateClearAdminRefreshToken(refreshToken); - - SessionUtil.expireCookie(response, "adminAccessToken", cookieDomain, cookieSecure, cookieSameSite); - SessionUtil.expireCookie(response, "adminRefreshToken", cookieDomain, cookieSecure, cookieSameSite); - } - - private void updateClearAdminRefreshToken(String refreshToken) { - if (refreshToken == null || !jwtTokenProvider.validateToken(refreshToken)) { - return; - } - - String role = jwtTokenProvider.getRole(refreshToken); - String scope = jwtTokenProvider.getScope(refreshToken); - String tokenType = jwtTokenProvider.getTokenType(refreshToken); - - if (!"ADMIN".equals(role) || !"ADMIN".equals(scope) || !"REFRESH".equals(tokenType)) { - return; - } - - try { - Integer userTokenIdx = Integer.parseInt(jwtTokenProvider.getUserTokenIdx(refreshToken)); - adminAuthMapper.updateClearAdminRefreshToken(userTokenIdx); - } catch (NumberFormatException ignored) { - } - } - - public AdminAuthLoginVo selectAdminAuthLoginChecked(Integer userTokenIdx) { - if (userTokenIdx == null) { - return null; - } - - return adminAuthMapper.selectAdminTokenByUserTokenIdx(userTokenIdx); - } -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthApiKeyLoginVo.java b/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthApiKeyLoginVo.java deleted file mode 100644 index 4a9647a..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthApiKeyLoginVo.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.alist.api.modules.admin.auth.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "Admin API Key 로그인 응답") -@Getter -@Setter -public class AdminAuthApiKeyLoginVo { - @Schema(description = "로그인 성공 여부", example = "true") - private boolean login; - - @Schema(description = "회원 PK", example = "14") - private Integer userIdx; - - @Schema(description = "관리자 아이디", example = "admin") - private String id; - - @Schema(description = "관리자 권한 코드", example = "A") - private String userRole; - - @Schema(description = "회원 유형 코드. A: 관리자", example = "A") - private String userType; - - @Schema(description = "관리자 API 테스트용 Access Token") - private String accessToken; - - @Schema(description = "관리자 API 테스트용 Refresh Token") - private String refreshToken; - - @JsonIgnore - @Schema(hidden = true) - private Integer userTokenIdx; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLoginCheckedVo.java b/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLoginCheckedVo.java deleted file mode 100644 index c3bd9bb..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLoginCheckedVo.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.alist.api.modules.admin.auth.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "Admin 로그인 상태 확인 응답") -@Getter -@Setter -public class AdminAuthLoginCheckedVo { - @Schema(description = "Admin Access Token 쿠키 존재 여부", example = "true") - private boolean adminAccessToken; - - @Schema(description = "Admin Refresh Token 쿠키 존재 여부", example = "true") - private boolean adminRefreshToken; - - @Schema(description = "로그인 상태 여부", example = "true") - private boolean loggedIn; - - @Schema(description = "관리자 아이디", example = "admin") - private String userId; - - @Schema(description = "회원 PK", example = "14") - private Integer userIdx; - - @Schema(description = "회원 토큰 PK", example = "14") - private Integer userTokenIdx; - - @Schema(description = "관리자 권한 코드", example = "A") - private String userRole; - - @Schema(description = "회원 유형 코드. A: 관리자", example = "A") - private String userType; -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLoginVo.java b/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLoginVo.java deleted file mode 100644 index 657d156..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLoginVo.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.alist.api.modules.admin.auth.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "Admin 로그인 응답") -@Setter -@Getter -public class AdminAuthLoginVo { - @Schema(description = "로그인 성공 여부", example = "true") - private boolean login; - - @Schema(description = "회원 PK", example = "14") - private Integer userIdx; - - @Schema(description = "관리자 아이디", example = "admin") - private String id; - - @Schema(description = "관리자 권한 코드", example = "A") - private String userRole; - - @Schema(description = "회원 유형 코드. A: 관리자", example = "A") - private String userType; - - @JsonIgnore - @Schema(hidden = true) - private String password; - - @JsonIgnore - @Schema(hidden = true) - private Integer userTokenIdx; - - @JsonIgnore - @Schema(hidden = true) - private String refreshToken; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLogoutVo.java b/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLogoutVo.java deleted file mode 100644 index cb46c2b..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthLogoutVo.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.alist.api.modules.admin.auth.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "Admin 로그아웃 응답") -@Getter -@Setter -public class AdminAuthLogoutVo { - @Schema(description = "로그아웃 처리 여부", example = "true") - private boolean loggedOut; -} diff --git a/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthRefreshVo.java b/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthRefreshVo.java deleted file mode 100644 index 494caa6..0000000 --- a/src/main/java/com/alist/api/modules/admin/auth/vo/AdminAuthRefreshVo.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.alist.api.modules.admin.auth.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "Admin 리프레시 토큰 발급 응답") -@Getter -@Setter -public class AdminAuthRefreshVo { - @Schema(description = "리프레시 처리 여부", example = "true") - private boolean refreshed; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/AdminMemberController.java b/src/main/java/com/alist/api/modules/admin/member/AdminMemberController.java deleted file mode 100644 index 19a93c1..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/AdminMemberController.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.alist.api.modules.admin.member; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.modules.admin.member.form.*; -import com.alist.api.modules.admin.member.service.AdminMemberService; -import com.alist.api.modules.admin.member.vo.*; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@Tag(name = "502. 회원 관리", description = "관리자 회원 관리 API") -@Slf4j -@RestController -@RequestMapping("/admin/member") -public class AdminMemberController { - private final AdminMemberService adminMemberService; - - public AdminMemberController(AdminMemberService adminMemberService) { - this.adminMemberService = adminMemberService; - } - - @Operation( - summary = "회원 목록 조회" - , description = "관리자가 회원 목록을 검색 조건과 페이징으로 조회합니다." - ) - @GetMapping("/list") - public ResponseEntity> adminMemberList( - @ModelAttribute AdminMemberListForm adminMemberListForm - ) { - AdminMemberListVo adminMemberListVo = adminMemberService.selectAdminMemberList(adminMemberListForm.toDto()); - - if (adminMemberListVo.getMemberList() == null || adminMemberListVo.getMemberList().isEmpty()) { - return ApiResponse.entity(adminMemberListVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminMemberListVo, ApiResponseCode.CODE_2001, "회원"); - } - - @Operation( - summary = "회원 상세 조회" - , description = "관리자가 회원 PK 기준으로 회원 상세 정보를 조회합니다." - ) - @GetMapping("/view") - public ResponseEntity> adminMemberView( - @RequestParam Integer userIdx - ) { - AdminMemberViewVo adminMemberViewVo = adminMemberService.selectAdminMemberView(userIdx); - - if (adminMemberViewVo == null) { - return ApiResponse.entity((AdminMemberViewVo) null, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminMemberViewVo, ApiResponseCode.CODE_2001, "회원"); - } - - @Operation( - summary = "회원 등록" - , description = "관리자가 교사 또는 학생 회원을 등록합니다. 아이디 중복 시 오류를 반환합니다." - ) - @PostMapping("/add") - public ResponseEntity> adminMemberAdd( - @Valid @RequestBody AdminMemberAddForm adminMemberAddForm - ) { - AdminMemberAddVo adminMemberAddVo = adminMemberService.insertAdminMemberAdd(adminMemberAddForm.toDto()); - - if (adminMemberAddVo.getResultCode() == 2004) { - return ApiResponse.entity(adminMemberAddVo, ApiResponseCode.CODE_2004, "아이디"); - } - - return ApiResponse.entity(adminMemberAddVo, ApiResponseCode.CODE_2002, "회원"); - } - - @Operation( - summary = "회원 정보 수정" - , description = "관리자가 회원 PK 기준으로 회원 기본 정보와 권한 정보를 수정합니다." - ) - @PutMapping("/modify") - public ResponseEntity> adminMemberModify( - @Valid @RequestBody AdminMemberModifyForm adminMemberModifyForm - ) { - AdminMemberModifyVo adminMemberModifyVo = adminMemberService.updateAdminMemberModify(adminMemberModifyForm.toDto()); - - if (!adminMemberModifyVo.isProcessed()) { - return ApiResponse.entity(adminMemberModifyVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminMemberModifyVo, ApiResponseCode.CODE_2005, "회원 수정"); - } - - @Operation( - summary = "회원 계정 삭제" - , description = "관리자가 회원 PK 기준으로 계정을 삭제 처리합니다. 실제 삭제가 아닌 del_yn 변경 방식입니다." - ) - @PatchMapping("/delete") - public ResponseEntity> adminMemberDelete( - @Valid @RequestBody AdminMemberDeleteForm adminMemberDeleteForm - ) { - AdminMemberDeleteVo adminMemberDeleteVo = adminMemberService.updateAdminMemberDelete(adminMemberDeleteForm.toDto()); - - if (!adminMemberDeleteVo.isProcessed()) { - return ApiResponse.entity(adminMemberDeleteVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminMemberDeleteVo, ApiResponseCode.CODE_2005, "회원 계정 삭제"); - } - - @Operation( - summary = "회원 휴면 상태 변경" - , description = "관리자가 회원 PK 기준으로 휴면 상태를 변경합니다. Y: 휴면, N: 정상" - ) - @PatchMapping("/dormant/modify") - public ResponseEntity> adminMemberDormantModify( - @Valid @RequestBody AdminMemberDormantModifyForm adminMemberDormantModifyForm - ) { - AdminMemberDormantModifyVo adminMemberDormantModifyVo = adminMemberService.updateAdminMemberDormantModify(adminMemberDormantModifyForm.toDto()); - - if (!adminMemberDormantModifyVo.isProcessed()) { - return ApiResponse.entity(adminMemberDormantModifyVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminMemberDormantModifyVo, ApiResponseCode.CODE_2005, "회원 휴면 상태 변경"); - } - - @Operation( - summary = "회원 탈퇴 상태 변경" - , description = "관리자가 회원 PK 기준으로 탈퇴 상태를 변경합니다. N: 정상, P: 탈퇴대기, Y: 탈퇴완료" - ) - @PatchMapping("/withdraw/modify") - public ResponseEntity> adminMemberWithdrawModify( - @Valid @RequestBody AdminMemberWithdrawModifyForm adminMemberWithdrawModifyForm - ) { - AdminMemberWithdrawModifyVo adminMemberWithdrawModifyVo = adminMemberService.updateAdminMemberWithdrawModify(adminMemberWithdrawModifyForm.toDto()); - - if (!adminMemberWithdrawModifyVo.isProcessed()) { - return ApiResponse.entity(adminMemberWithdrawModifyVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminMemberWithdrawModifyVo, ApiResponseCode.CODE_2005, "회원 탈퇴 상태 변경"); - } -} diff --git a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberDeleteDto.java b/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberDeleteDto.java deleted file mode 100644 index a6bb24d..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberDeleteDto.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.alist.api.modules.admin.member.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Getter -@Setter -public class AdminMemberDeleteDto { - private List userIdxList; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberDormantModifyDto.java b/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberDormantModifyDto.java deleted file mode 100644 index 9576677..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberDormantModifyDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.alist.api.modules.admin.member.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminMemberDormantModifyDto { - private Integer userIdx; - private String dormantYn; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberListDto.java b/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberListDto.java deleted file mode 100644 index f11e41b..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberListDto.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.alist.api.modules.admin.member.dto; - -import com.alist.api.common.paging.PageRequest; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminMemberListDto extends PageRequest { - private String keyword; - private String userRole; - private String userType; - private String dormantYn; - private String withdrawStatus; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberModifyDto.java b/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberModifyDto.java deleted file mode 100644 index 78d3b16..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberModifyDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.alist.api.modules.admin.member.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminMemberModifyDto { - private Integer userIdx; - private String email; - private String hp; - private String userRole; - private String userType; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberWithdrawModifyDto.java b/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberWithdrawModifyDto.java deleted file mode 100644 index 89ee828..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/dto/AdminMemberWithdrawModifyDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.alist.api.modules.admin.member.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminMemberWithdrawModifyDto { - private Integer userIdx; - private String withdrawStatus; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberAddForm.java b/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberAddForm.java deleted file mode 100644 index 7fa7f59..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberAddForm.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.alist.api.modules.admin.member.form; - -import com.alist.api.modules.admin.member.dto.AdminMemberAddDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 회원 등록 요청 폼") -@Getter -@Setter -public class AdminMemberAddForm { - - @Schema( - description = "회원 아이디" - , example = "teacher01" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "아이디를 입력해주세요.") - private String id; - - @Schema( - description = "비밀번호(8~64자, 영문과 숫자 포함, 공백 불가)" - , example = "pass1234" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "비밀번호를 입력해주세요.") - @Size(min = 8, max = 64, message = "비밀번호는 8~64자여야 합니다.") - @Pattern( - regexp = "^(?=.*[A-Za-z])(?=.*\\d)\\S+$" - , message = "비밀번호는 영문과 숫자를 포함하고 공백이 없어야 합니다." - ) - private String password; - - @Schema( - description = "회원 권한 코드. 교사: CCM/SCM/I, 학생: R/E 관리자: A" - , example = "I" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "회원 권한을 입력해주세요.") - @Pattern( - regexp = "^(CCM|SCM|I|R|E|A)$" - , message = "회원 권한은 CCM, SCM, I, R, E만 가능합니다." - ) - private String userRole; - - @Schema( - description = "회원 유형 코드. T: 교사, S: 학생 A: 관리자" - , example = "T" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "회원 유형을 입력해주세요.") - @Pattern( - regexp = "^(T|S|A)$" - , message = "회원 유형은 T 또는 S만 가능합니다." - ) - private String userType; - - @AssertTrue(message = "회원 유형과 권한 조합을 확인해주세요.") - @Schema(hidden = true) - public boolean isValidUserTypeAndRole() { - if (userType == null || userRole == null) { - return true; - } - - if ("A".equals(userType)) { - return "A".equals(userRole); - } - - if ("T".equals(userType)) { - return "CCM".equals(userRole) - || "SCM".equals(userRole) - || "I".equals(userRole); - } - - if ("S".equals(userType)) { - return "R".equals(userRole) - || "E".equals(userRole); - } - - return false; - } - - public AdminMemberAddDto toDto() { - AdminMemberAddDto adminMemberAddDto = new AdminMemberAddDto(); - adminMemberAddDto.setId(id.trim()); - adminMemberAddDto.setPassword(password); - adminMemberAddDto.setUserRole(userRole); - adminMemberAddDto.setUserType(userType); - return adminMemberAddDto; - } -} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberDeleteForm.java b/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberDeleteForm.java deleted file mode 100644 index 282cf3b..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberDeleteForm.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.alist.api.modules.admin.member.form; - -import com.alist.api.modules.admin.member.dto.AdminMemberDeleteDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import lombok.Getter; -import lombok.Setter; - -import java.util.Arrays; - -@Schema(description = "관리자 회원 계정 삭제 요청 폼") -@Getter -@Setter -public class AdminMemberDeleteForm { - @Schema(description = "회원 PK 목록. 콤마 구분 문자열", example = "1,2,3", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "삭제할 회원 PK를 입력해주세요.") - @Pattern(regexp = "^\\d+(,\\d+)*$", message = "회원 PK 목록은 1,2,3 형식으로 입력해주세요.") - private String userIdxList; - - public AdminMemberDeleteDto toDto() { - AdminMemberDeleteDto adminMemberDeleteDto = new AdminMemberDeleteDto(); - adminMemberDeleteDto.setUserIdxList( - Arrays.stream(userIdxList.split(",")) - .map(String::trim) - .map(Integer::valueOf) - .distinct() - .toList() - ); - return adminMemberDeleteDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberDormantModifyForm.java b/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberDormantModifyForm.java deleted file mode 100644 index 75c79c1..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberDormantModifyForm.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.alist.api.modules.admin.member.form; - -import com.alist.api.modules.admin.member.dto.AdminMemberDormantModifyDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 회원 휴면 상태 변경 요청 폼") -@Getter -@Setter -public class AdminMemberDormantModifyForm { - @Schema(description = "회원 PK", example = "15", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "회원 PK는 필수입니다.") - private Integer userIdx; - - @Schema(description = "휴면 여부. Y: 휴면, N: 정상", example = "Y", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "휴면 여부는 필수입니다.") - @Pattern(regexp = "^[YN]$", message = "휴면 여부는 Y 또는 N만 가능합니다.") - private String dormantYn; - - public AdminMemberDormantModifyDto toDto() { - AdminMemberDormantModifyDto adminMemberDormantModifyDto = new AdminMemberDormantModifyDto(); - adminMemberDormantModifyDto.setUserIdx(userIdx); - adminMemberDormantModifyDto.setDormantYn(dormantYn); - return adminMemberDormantModifyDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberListForm.java b/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberListForm.java deleted file mode 100644 index e61249e..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberListForm.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.alist.api.modules.admin.member.form; - -import com.alist.api.common.paging.PageRequest; -import com.alist.api.modules.admin.member.dto.AdminMemberListDto; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 회원 목록 검색 조건") -@Getter -@Setter -public class AdminMemberListForm extends PageRequest { - @Schema(description = "검색어. 아이디, 이메일, 휴대폰 번호를 대상으로 검색합니다.", example = "admin") - private String keyword; - - @Schema(description = "회원 권한 코드(CCD/SCM/I/R/E)", example = "SCM") - private String userRole; - - @Schema(description = "회원 유형 코드. A: 관리자, T: 교사, S: 학생", example = "T") - private String userType; - - @Schema(description = "휴면 여부. N: 정상, Y: 휴면", example = "N") - private String dormantYn; - - @Schema(description = "탈퇴 상태. N: 정상, P: 탈퇴대기, Y: 탈퇴완료", example = "N") - private String withdrawStatus; - - public AdminMemberListDto toDto() { - AdminMemberListDto adminMemberListDto = new AdminMemberListDto(); - adminMemberListDto.setPage(getPage()); - adminMemberListDto.setSize(getSize()); - adminMemberListDto.setKeyword(keyword); - adminMemberListDto.setUserRole(userRole); - adminMemberListDto.setUserType(userType); - adminMemberListDto.setDormantYn(dormantYn); - adminMemberListDto.setWithdrawStatus(withdrawStatus); - return adminMemberListDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberModifyForm.java b/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberModifyForm.java deleted file mode 100644 index 65c8d88..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberModifyForm.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.alist.api.modules.admin.member.form; - -import com.alist.api.modules.admin.member.dto.AdminMemberModifyDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 회원 수정 요청 폼") -@Getter -@Setter -public class AdminMemberModifyForm { - @Schema(description = "회원 PK", example = "15", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "회원 PK는 필수입니다.") - private Integer userIdx; - - @Schema(description = "이메일", example = "user@example.com") - @Email(message = "이메일 형식을 확인해주세요.") - private String email; - - @Schema(description = "휴대폰 번호", example = "01012345678") - private String hp; - - @Schema(description = "회원 권한 코드(CCD/SCM/I/R/E)", example = "CCM") - private String userRole; - - @Schema(description = "회원 유형 코드. A: 관리자, T: 교사, S: 학생", example = "T") - private String userType; - - public AdminMemberModifyDto toDto() { - AdminMemberModifyDto adminMemberModifyDto = new AdminMemberModifyDto(); - adminMemberModifyDto.setUserIdx(userIdx); - adminMemberModifyDto.setEmail(email); - adminMemberModifyDto.setHp(hp); - adminMemberModifyDto.setUserRole(userRole); - adminMemberModifyDto.setUserType(userType); - return adminMemberModifyDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberWithdrawModifyForm.java b/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberWithdrawModifyForm.java deleted file mode 100644 index 8852995..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/form/AdminMemberWithdrawModifyForm.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.alist.api.modules.admin.member.form; - -import com.alist.api.modules.admin.member.dto.AdminMemberWithdrawModifyDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 회원 탈퇴 상태 변경 요청 폼") -@Getter -@Setter -public class AdminMemberWithdrawModifyForm { - @Schema(description = "회원 PK", example = "15", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "회원 PK는 필수입니다.") - private Integer userIdx; - - @Schema(description = "탈퇴 상태. N: 정상, P: 탈퇴대기, Y: 탈퇴완료", example = "P", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "탈퇴 상태는 필수입니다.") - @Pattern(regexp = "^[NPY]$", message = "탈퇴 상태는 N, P, Y만 가능합니다.") - private String withdrawStatus; - - public AdminMemberWithdrawModifyDto toDto() { - AdminMemberWithdrawModifyDto adminMemberWithdrawModifyDto = new AdminMemberWithdrawModifyDto(); - adminMemberWithdrawModifyDto.setUserIdx(userIdx); - adminMemberWithdrawModifyDto.setWithdrawStatus(withdrawStatus); - return adminMemberWithdrawModifyDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/member/mapper/AdminMemberMapper.java b/src/main/java/com/alist/api/modules/admin/member/mapper/AdminMemberMapper.java deleted file mode 100644 index 576ecfb..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/mapper/AdminMemberMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.alist.api.modules.admin.member.mapper; - -import com.alist.api.modules.admin.member.dto.*; -import com.alist.api.modules.admin.member.vo.AdminMemberViewVo; -import com.alist.api.modules.admin.member.vo.AdminMemberVo; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface AdminMemberMapper { - int selectAdminMemberCount(AdminMemberListDto adminMemberListDto); - - List selectAdminMemberList(AdminMemberListDto adminMemberListDto); - - AdminMemberViewVo selectAdminMemberView(Integer userIdx); - - int updateAdminMemberModify(AdminMemberModifyDto adminMemberModifyDto); - - int updateAdminMemberDormantModify(AdminMemberDormantModifyDto adminMemberDormantModifyDto); - - int updateAdminMemberWithdrawModify(AdminMemberWithdrawModifyDto adminMemberWithdrawModifyDto); - - int updateAdminMemberDelete(AdminMemberDeleteDto adminMemberDeleteDto); -} diff --git a/src/main/java/com/alist/api/modules/admin/member/service/AdminMemberService.java b/src/main/java/com/alist/api/modules/admin/member/service/AdminMemberService.java deleted file mode 100644 index 747a53e..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/service/AdminMemberService.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.alist.api.modules.admin.member.service; - -import com.alist.api.modules.admin.member.dto.*; -import com.alist.api.modules.admin.member.mapper.AdminMemberMapper; -import com.alist.api.modules.admin.member.vo.*; -import com.alist.api.modules.front.user.dto.UserDto; -import com.alist.api.modules.front.user.service.UserService; -import com.alist.api.modules.front.user.vo.UserVo; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Service -public class AdminMemberService { - private final AdminMemberMapper adminMemberMapper; - private final UserService userService; - - public AdminMemberService(AdminMemberMapper adminMemberMapper, UserService userService) { - this.adminMemberMapper = adminMemberMapper; - this.userService = userService; - } - - @Transactional(readOnly = true) - public AdminMemberListVo selectAdminMemberList(AdminMemberListDto adminMemberListDto) { - int totalCount = adminMemberMapper.selectAdminMemberCount(adminMemberListDto); - List adminMemberList = adminMemberMapper.selectAdminMemberList(adminMemberListDto); - - AdminMemberListVo adminMemberListVo = new AdminMemberListVo(); - adminMemberListVo.setMemberList(adminMemberList); - adminMemberListVo.setPaging(adminMemberListDto, totalCount); - - return adminMemberListVo; - } - - @Transactional(readOnly = true) - public AdminMemberViewVo selectAdminMemberView(Integer userIdx) { - return adminMemberMapper.selectAdminMemberView(userIdx); - } - - @Transactional - public AdminMemberAddVo insertAdminMemberAdd( - AdminMemberAddDto adminMemberAddDto - ) { - UserDto userDto = new UserDto(); - userDto.setId(adminMemberAddDto.getId()); - userDto.setPassword(adminMemberAddDto.getPassword()); - userDto.setUserRole(adminMemberAddDto.getUserRole()); - userDto.setUserType(adminMemberAddDto.getUserType()); - - UserVo userVo = userService.insertUserAdd(userDto); - - AdminMemberAddVo adminMemberAddVo = new AdminMemberAddVo(); - adminMemberAddVo.setUserIdx(userVo.getUserIdx()); - adminMemberAddVo.setId(userVo.getId()); - adminMemberAddVo.setUserRole(userVo.getUserRole()); - adminMemberAddVo.setUserType(userVo.getUserType()); - adminMemberAddVo.setResultCode(userVo.getResultCode()); - - return adminMemberAddVo; - } - - @Transactional - public AdminMemberModifyVo updateAdminMemberModify(AdminMemberModifyDto adminMemberModifyDto) { - int updateCount = adminMemberMapper.updateAdminMemberModify(adminMemberModifyDto); - - AdminMemberModifyVo adminMemberModifyVo = new AdminMemberModifyVo(); - adminMemberModifyVo.setProcessed(updateCount > 0); - return adminMemberModifyVo; - } - - @Transactional - public AdminMemberDormantModifyVo updateAdminMemberDormantModify(AdminMemberDormantModifyDto adminMemberDormantModifyDto) { - int updateCount = adminMemberMapper.updateAdminMemberDormantModify(adminMemberDormantModifyDto); - - AdminMemberDormantModifyVo adminMemberDormantModifyVo = new AdminMemberDormantModifyVo(); - adminMemberDormantModifyVo.setProcessed(updateCount > 0); - return adminMemberDormantModifyVo; - } - - @Transactional - public AdminMemberWithdrawModifyVo updateAdminMemberWithdrawModify(AdminMemberWithdrawModifyDto adminMemberWithdrawModifyDto) { - int updateCount = adminMemberMapper.updateAdminMemberWithdrawModify(adminMemberWithdrawModifyDto); - - AdminMemberWithdrawModifyVo adminMemberWithdrawModifyVo = new AdminMemberWithdrawModifyVo(); - adminMemberWithdrawModifyVo.setProcessed(updateCount > 0); - return adminMemberWithdrawModifyVo; - } - - @Transactional - public AdminMemberDeleteVo updateAdminMemberDelete(AdminMemberDeleteDto adminMemberDeleteDto) { - int updateCount = adminMemberMapper.updateAdminMemberDelete(adminMemberDeleteDto); - - AdminMemberDeleteVo adminMemberDeleteVo = new AdminMemberDeleteVo(); - adminMemberDeleteVo.setProcessed(updateCount > 0); - adminMemberDeleteVo.setRequestedCount(adminMemberDeleteDto.getUserIdxList().size()); - adminMemberDeleteVo.setDeletedCount(updateCount); - return adminMemberDeleteVo; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberAddVo.java b/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberAddVo.java deleted file mode 100644 index 3118f53..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberAddVo.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.admin.member.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 회원 등록 응답") -@Getter -@Setter -public class AdminMemberAddVo { - - @Schema(description = "회원 PK", example = "15") - private Integer userIdx; - - @Schema(description = "회원 아이디", example = "teacher01") - private String id; - - @Schema(description = "회원 권한 코드", example = "I") - private String userRole; - - @Schema(description = "회원 유형 코드. T: 교사, S: 학생", example = "T") - private String userType; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberDeleteVo.java b/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberDeleteVo.java deleted file mode 100644 index 9a013fb..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberDeleteVo.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.alist.api.modules.admin.member.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 회원 계정 삭제 응답") -@Getter -@Setter -public class AdminMemberDeleteVo { - @Schema(description = "회원 계정 삭제 여부", example = "true") - private boolean processed; - - @Schema(description = "삭제 요청 회원 수", example = "3") - private int requestedCount; - - @Schema(description = "실제 삭제 처리 회원 수", example = "3") - private int deletedCount; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberDormantModifyVo.java b/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberDormantModifyVo.java deleted file mode 100644 index bbfde84..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberDormantModifyVo.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.alist.api.modules.admin.member.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 회원 휴면 상태 변경 응답") -@Getter -@Setter -public class AdminMemberDormantModifyVo { - @Schema(description = "휴면 상태 변경 여부", example = "true") - private boolean processed; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberListVo.java b/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberListVo.java deleted file mode 100644 index fb07a80..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberListVo.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alist.api.modules.admin.member.vo; - -import com.alist.api.common.paging.PageResponse; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "관리자 회원 목록 조회 응답") -@Getter -@Setter -public class AdminMemberListVo extends PageResponse { - @Schema(description = "회원 목록") - private List memberList; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberModifyVo.java b/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberModifyVo.java deleted file mode 100644 index 4c529e9..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberModifyVo.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.alist.api.modules.admin.member.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 회원 수정 응답") -@Getter -@Setter -public class AdminMemberModifyVo { - @Schema(description = "회원 수정 여부", example = "true") - private boolean processed; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberViewVo.java b/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberViewVo.java deleted file mode 100644 index f72123a..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberViewVo.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.alist.api.modules.admin.member.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.time.LocalDateTime; - -@Schema(description = "관리자 회원 상세 조회 응답") -@Getter -@Setter -public class AdminMemberViewVo { - @Schema(description = "회원 PK", example = "15") - private Integer userIdx; - - @Schema(description = "회원 토큰 PK", example = "15") - private Integer userTokenIdx; - - @Schema(description = "회원 아이디", example = "admin") - private String id; - - @Schema(description = "이메일", example = "user@example.com") - private String email; - - @Schema(description = "휴대폰 번호", example = "01012345678") - private String hp; - - @Schema(description = "회원 권한 코드(CCD/SCM/I/R/E)", example = "SCM") - private String userRole; - - @Schema(description = "회원 유형 코드. A: 관리자, T: 교사, S: 학생", example = "T") - private String userType; - - @Schema(description = "휴면 여부. N: 정상, Y: 휴면", example = "N") - private String dormantYn; - - @Schema(description = "휴면 처리일") - private LocalDateTime dormantAt; - - @Schema(description = "탈퇴 상태. N: 정상, P: 탈퇴대기, Y: 탈퇴완료", example = "N") - private String withdrawStatus; - - @Schema(description = "탈퇴 신청일") - private LocalDateTime withdrawAt; - - @Schema(description = "가입일") - private LocalDateTime createAt; - - @Schema(description = "수정일") - private LocalDateTime updateAt; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberVo.java b/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberVo.java deleted file mode 100644 index 583ab7c..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberVo.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.alist.api.modules.admin.member.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.time.LocalDateTime; - -@Schema(description = "관리자 회원 목록 항목") -@Getter -@Setter -public class AdminMemberVo { - @Schema(description = "회원 PK", example = "15") - private Integer userIdx; - - @Schema(description = "회원 토큰 PK", example = "15") - private Integer userTokenIdx; - - @Schema(description = "회원 아이디", example = "admin") - private String id; - - @Schema(description = "이메일", example = "user@example.com") - private String email; - - @Schema(description = "휴대폰 번호", example = "01012345678") - private String hp; - - @Schema(description = "회원 권한 코드(CCD/SCM/I/R/E)", example = "SCM") - private String userRole; - - @Schema(description = "회원 유형 코드. A: 관리자, T: 교사, S: 학생", example = "T") - private String userType; - - @Schema(description = "휴면 여부. N: 정상, Y: 휴면", example = "N") - private String dormantYn; - - @Schema(description = "휴면 처리일") - private LocalDateTime dormantAt; - - @Schema(description = "탈퇴 상태. N: 정상, P: 탈퇴대기, Y: 탈퇴완료", example = "N") - private String withdrawStatus; - - @Schema(description = "탈퇴 신청일") - private LocalDateTime withdrawAt; - - @Schema(description = "가입일") - private LocalDateTime createAt; - - @Schema(description = "수정일") - private LocalDateTime updateAt; -} diff --git a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberWithdrawModifyVo.java b/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberWithdrawModifyVo.java deleted file mode 100644 index 3eb7f48..0000000 --- a/src/main/java/com/alist/api/modules/admin/member/vo/AdminMemberWithdrawModifyVo.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.alist.api.modules.admin.member.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 회원 탈퇴 상태 변경 응답") -@Getter -@Setter -public class AdminMemberWithdrawModifyVo { - @Schema(description = "탈퇴 상태 변경 여부", example = "true") - private boolean processed; -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/AdminNoticeController.java b/src/main/java/com/alist/api/modules/admin/notice/AdminNoticeController.java deleted file mode 100644 index 53acfa5..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/AdminNoticeController.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.alist.api.modules.admin.notice; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.modules.admin.notice.form.AdminNoticeAddForm; -import com.alist.api.modules.admin.notice.form.AdminNoticeDeleteForm; -import com.alist.api.modules.admin.notice.form.AdminNoticeListForm; -import com.alist.api.modules.admin.notice.form.AdminNoticeModifyForm; -import com.alist.api.modules.admin.notice.service.AdminNoticeService; -import com.alist.api.modules.admin.notice.vo.AdminNoticeAddVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeDeleteVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeListVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeModifyVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeViewVo; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@Tag(name = "505. 공지사항 관리", description = "관리자 공지사항 관리 API") -@RestController -@RequestMapping("/admin/notice") -public class AdminNoticeController { - private final AdminNoticeService adminNoticeService; - - public AdminNoticeController(AdminNoticeService adminNoticeService) { - this.adminNoticeService = adminNoticeService; - } - - @Operation( - summary = "공지사항 목록 조회" - , description = "관리자가 공지사항 목록을 검색 조건과 페이징으로 조회합니다." - ) - @GetMapping("/list") - public ResponseEntity> adminNoticeList( - @ModelAttribute AdminNoticeListForm adminNoticeListForm - ) { - AdminNoticeListVo adminNoticeListVo = adminNoticeService.selectAdminNoticeList(adminNoticeListForm.toDto()); - - if (adminNoticeListVo.getNoticeList() == null || adminNoticeListVo.getNoticeList().isEmpty()) { - return ApiResponse.entity(adminNoticeListVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminNoticeListVo, ApiResponseCode.CODE_2001, "공지사항"); - } - - @Operation( - summary = "공지사항 상세 조회" - , description = "관리자가 공지사항 PK 기준으로 상세 정보를 조회합니다." - ) - @GetMapping("/view") - public ResponseEntity> adminNoticeView( - @RequestParam Long noticeIdx - ) { - AdminNoticeViewVo adminNoticeViewVo = adminNoticeService.selectAdminNoticeView(noticeIdx); - - if (adminNoticeViewVo == null) { - return ApiResponse.entity((AdminNoticeViewVo) null, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminNoticeViewVo, ApiResponseCode.CODE_2001, "공지사항"); - } - - @Operation( - summary = "공지사항 등록" - , description = "관리자가 공지사항을 등록합니다. 캠퍼스 대상 공지는 campusIdxList를 함께 저장합니다." - ) - @PostMapping("/add") - public ResponseEntity> adminNoticeAdd( - @Valid @RequestBody AdminNoticeAddForm adminNoticeAddForm - ) { - AdminNoticeAddVo adminNoticeAddVo = adminNoticeService.insertAdminNoticeAdd(adminNoticeAddForm.toDto()); - - if (adminNoticeAddVo.getResultCode() == 401) { - return ApiResponse.entity(adminNoticeAddVo, ApiResponseCode.CODE_401); - } - - if (adminNoticeAddVo.getResultCode() == 4001) { - return ApiResponse.entity(adminNoticeAddVo, ApiResponseCode.CODE_4001); - } - - return ApiResponse.entity(adminNoticeAddVo, ApiResponseCode.CODE_2002, "공지사항"); - } - - @Operation( - summary = "공지사항 수정" - , description = "관리자가 공지사항 PK 기준으로 제목, 내용, 노출 범위, 첨부파일 정보를 수정합니다." - ) - @PutMapping("/modify") - public ResponseEntity> adminNoticeModify( - @Valid @RequestBody AdminNoticeModifyForm adminNoticeModifyForm - ) { - AdminNoticeModifyVo adminNoticeModifyVo = adminNoticeService.updateAdminNoticeModify(adminNoticeModifyForm.toDto()); - - if (adminNoticeModifyVo.getResultCode() == 401) { - return ApiResponse.entity(adminNoticeModifyVo, ApiResponseCode.CODE_401); - } - - if (adminNoticeModifyVo.getResultCode() == 4001) { - return ApiResponse.entity(adminNoticeModifyVo, ApiResponseCode.CODE_4001); - } - - if (!adminNoticeModifyVo.isProcessed()) { - return ApiResponse.entity(adminNoticeModifyVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminNoticeModifyVo, ApiResponseCode.CODE_2005, "공지사항 수정"); - } - - @Operation( - summary = "공지사항 삭제" - , description = "관리자가 공지사항 PK 기준으로 삭제 처리합니다. 실제 삭제가 아닌 DEL_YN 변경 방식입니다." - ) - @PatchMapping("/delete") - public ResponseEntity> adminNoticeDelete( - @Valid @RequestBody AdminNoticeDeleteForm adminNoticeDeleteForm - ) { - AdminNoticeDeleteVo adminNoticeDeleteVo = adminNoticeService.updateAdminNoticeDelete(adminNoticeDeleteForm.toDto()); - - if (adminNoticeDeleteVo.getResultCode() == 401) { - return ApiResponse.entity(adminNoticeDeleteVo, ApiResponseCode.CODE_401); - } - - if (!adminNoticeDeleteVo.isProcessed()) { - return ApiResponse.entity(adminNoticeDeleteVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminNoticeDeleteVo, ApiResponseCode.CODE_2005, "공지사항 삭제"); - } -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeAddDto.java b/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeAddDto.java deleted file mode 100644 index f5ab286..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeAddDto.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.alist.api.modules.admin.notice.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Getter -@Setter -public class AdminNoticeAddDto { - private Long noticeIdx; - private String noticeType; - private String targetScope; - private List campusIdxList; - private String title; - private String content; - private String pinYn; - private String startDate; - private String endDate; - private String useYn; - private List fileList; - private Integer createMember; - private Integer updateMember; -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeCampusDto.java b/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeCampusDto.java deleted file mode 100644 index 9819ac0..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeCampusDto.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.alist.api.modules.admin.notice.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminNoticeCampusDto { - private Long noticeIdx; - private Long campusIdx; - private Integer createMember; -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeDeleteDto.java b/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeDeleteDto.java deleted file mode 100644 index 96b731e..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeDeleteDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.alist.api.modules.admin.notice.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminNoticeDeleteDto { - private Long noticeIdx; - private Integer updateMember; -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeModifyDto.java b/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeModifyDto.java deleted file mode 100644 index 903c05d..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/dto/AdminNoticeModifyDto.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.alist.api.modules.admin.notice.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Getter -@Setter -public class AdminNoticeModifyDto { - private Long noticeIdx; - private String noticeType; - private String targetScope; - private List campusIdxList; - private String title; - private String content; - private String pinYn; - private String startDate; - private String endDate; - private String useYn; - private List fileList; - private List deleteFileIdxList; - private Integer updateMember; -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeAddForm.java b/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeAddForm.java deleted file mode 100644 index ab950ed..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeAddForm.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.alist.api.modules.admin.notice.form; - -import com.alist.api.modules.admin.notice.dto.AdminNoticeAddDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "관리자 공지사항 등록 요청") -@Getter -@Setter -public class AdminNoticeAddForm { - @NotBlank - @Size(max = 30) - @Schema(description = "공지 유형. GENERAL 일반, SYSTEM 시스템, EVENT 이벤트", example = "GENERAL", requiredMode = Schema.RequiredMode.REQUIRED) - private String noticeType; - - @NotBlank - @Size(max = 30) - @Schema(description = "노출 범위. ALL 전체, ADMIN 관리자, TEACHER 교사, STUDENT 학생, CAMPUS 캠퍼스", example = "CAMPUS", requiredMode = Schema.RequiredMode.REQUIRED) - private String targetScope; - - @Schema(description = "캠퍼스 IDX 목록. targetScope가 CAMPUS일 때 사용", example = "[1, 2, 3]") - private List campusIdxList; - - @NotBlank - @Size(max = 200) - @Schema(description = "제목", example = "서비스 점검 안내", requiredMode = Schema.RequiredMode.REQUIRED) - private String title; - - @NotBlank - @Schema(description = "내용", example = "서비스 점검이 예정되어 있습니다.", requiredMode = Schema.RequiredMode.REQUIRED) - private String content; - - @Pattern(regexp = "Y|N") - @Schema(description = "상단 고정 여부. Y: 고정, N: 미고정. 미입력 시 N", example = "N") - private String pinYn; - - @Schema(description = "노출 시작일", example = "2026-07-01 00:00:00") - private String startDate; - - @Schema(description = "노출 종료일", example = "2026-07-31 23:59:59") - private String endDate; - - @Pattern(regexp = "Y|N") - @Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y") - private String useYn; - - @Valid - @Schema(description = "첨부파일 목록") - private List fileList; - - public AdminNoticeAddDto toDto() { - AdminNoticeAddDto adminNoticeAddDto = new AdminNoticeAddDto(); - adminNoticeAddDto.setNoticeType(noticeType.trim()); - adminNoticeAddDto.setTargetScope(targetScope.trim()); - adminNoticeAddDto.setCampusIdxList(campusIdxList); - adminNoticeAddDto.setTitle(title.trim()); - adminNoticeAddDto.setContent(content.trim()); - adminNoticeAddDto.setPinYn(pinYn == null || pinYn.trim().isEmpty() ? "N" : pinYn.trim()); - adminNoticeAddDto.setStartDate(startDate == null || startDate.trim().isEmpty() ? null : startDate.trim()); - adminNoticeAddDto.setEndDate(endDate == null || endDate.trim().isEmpty() ? null : endDate.trim()); - adminNoticeAddDto.setUseYn(useYn == null || useYn.trim().isEmpty() ? "Y" : useYn.trim()); - adminNoticeAddDto.setFileList(fileList == null ? null : fileList.stream().map(AdminNoticeFileForm::toDto).toList()); - return adminNoticeAddDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeDeleteForm.java b/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeDeleteForm.java deleted file mode 100644 index 1818be5..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeDeleteForm.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.alist.api.modules.admin.notice.form; - -import com.alist.api.modules.admin.notice.dto.AdminNoticeDeleteDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 공지사항 삭제 요청") -@Getter -@Setter -public class AdminNoticeDeleteForm { - @NotNull - @Schema(description = "공지사항 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED) - private Long noticeIdx; - - public AdminNoticeDeleteDto toDto() { - AdminNoticeDeleteDto adminNoticeDeleteDto = new AdminNoticeDeleteDto(); - adminNoticeDeleteDto.setNoticeIdx(noticeIdx); - return adminNoticeDeleteDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeListForm.java b/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeListForm.java deleted file mode 100644 index bf5d33b..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeListForm.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.alist.api.modules.admin.notice.form; - -import com.alist.api.common.paging.PageRequest; -import com.alist.api.modules.admin.notice.dto.AdminNoticeListDto; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 공지사항 목록 검색 조건") -@Getter -@Setter -public class AdminNoticeListForm extends PageRequest { - @Schema(description = "검색어. 제목, 내용을 대상으로 검색합니다.", example = "점검") - private String keyword; - - @Schema(description = "공지 유형. GENERAL 일반, SYSTEM 시스템, EVENT 이벤트", example = "GENERAL") - private String noticeType; - - @Schema(description = "노출 범위. ALL 전체, ADMIN 관리자, TEACHER 교사, STUDENT 학생, CAMPUS 캠퍼스", example = "CAMPUS") - private String targetScope; - - @Schema(description = "캠퍼스 IDX. 특정 캠퍼스 대상 공지를 검색할 때 사용", example = "1") - private Long campusIdx; - - @Schema(description = "상단 고정 여부. Y: 고정, N: 미고정", example = "Y") - private String pinYn; - - @Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y") - private String useYn; - - public AdminNoticeListDto toDto() { - AdminNoticeListDto adminNoticeListDto = new AdminNoticeListDto(); - adminNoticeListDto.setPage(getPage()); - adminNoticeListDto.setSize(getSize()); - adminNoticeListDto.setKeyword(keyword == null ? null : keyword.trim()); - adminNoticeListDto.setNoticeType(noticeType == null ? null : noticeType.trim()); - adminNoticeListDto.setTargetScope(targetScope == null ? null : targetScope.trim()); - adminNoticeListDto.setCampusIdx(campusIdx); - adminNoticeListDto.setPinYn(pinYn == null ? null : pinYn.trim()); - adminNoticeListDto.setUseYn(useYn == null ? null : useYn.trim()); - return adminNoticeListDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeModifyForm.java b/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeModifyForm.java deleted file mode 100644 index d986dcd..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/form/AdminNoticeModifyForm.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.alist.api.modules.admin.notice.form; - -import com.alist.api.modules.admin.notice.dto.AdminNoticeModifyDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "관리자 공지사항 수정 요청") -@Getter -@Setter -public class AdminNoticeModifyForm { - @NotNull - @Schema(description = "공지사항 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED) - private Long noticeIdx; - - @NotBlank - @Size(max = 30) - @Schema(description = "공지 유형. GENERAL 일반, SYSTEM 시스템, EVENT 이벤트", example = "GENERAL", requiredMode = Schema.RequiredMode.REQUIRED) - private String noticeType; - - @NotBlank - @Size(max = 30) - @Schema(description = "노출 범위. ALL 전체, ADMIN 관리자, TEACHER 교사, STUDENT 학생, CAMPUS 캠퍼스", example = "CAMPUS", requiredMode = Schema.RequiredMode.REQUIRED) - private String targetScope; - - @Schema(description = "캠퍼스 IDX 목록. targetScope가 CAMPUS일 때 사용", example = "[1, 2, 3]") - private List campusIdxList; - - @NotBlank - @Size(max = 200) - @Schema(description = "제목", example = "서비스 점검 안내", requiredMode = Schema.RequiredMode.REQUIRED) - private String title; - - @NotBlank - @Schema(description = "내용", example = "서비스 점검이 예정되어 있습니다.", requiredMode = Schema.RequiredMode.REQUIRED) - private String content; - - @Pattern(regexp = "Y|N") - @Schema(description = "상단 고정 여부. Y: 고정, N: 미고정. 미입력 시 N", example = "N") - private String pinYn; - - @Schema(description = "노출 시작일", example = "2026-07-01 00:00:00") - private String startDate; - - @Schema(description = "노출 종료일", example = "2026-07-31 23:59:59") - private String endDate; - - @Pattern(regexp = "Y|N") - @Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y") - private String useYn; - - @Valid - @Schema(description = "신규 첨부파일 목록. 기존 첨부파일은 유지되고 전달된 파일만 추가됩니다.") - private List fileList; - - @Schema(description = "삭제할 기존 첨부파일 PK 목록", example = "[1, 2]") - private List deleteFileIdxList; - - public AdminNoticeModifyDto toDto() { - AdminNoticeModifyDto adminNoticeModifyDto = new AdminNoticeModifyDto(); - adminNoticeModifyDto.setNoticeIdx(noticeIdx); - adminNoticeModifyDto.setNoticeType(noticeType.trim()); - adminNoticeModifyDto.setTargetScope(targetScope.trim()); - adminNoticeModifyDto.setCampusIdxList(campusIdxList); - adminNoticeModifyDto.setTitle(title.trim()); - adminNoticeModifyDto.setContent(content.trim()); - adminNoticeModifyDto.setPinYn(pinYn == null || pinYn.trim().isEmpty() ? "N" : pinYn.trim()); - adminNoticeModifyDto.setStartDate(startDate == null || startDate.trim().isEmpty() ? null : startDate.trim()); - adminNoticeModifyDto.setEndDate(endDate == null || endDate.trim().isEmpty() ? null : endDate.trim()); - adminNoticeModifyDto.setUseYn(useYn == null || useYn.trim().isEmpty() ? "Y" : useYn.trim()); - adminNoticeModifyDto.setFileList(fileList == null ? null : fileList.stream().map(AdminNoticeFileForm::toDto).toList()); - adminNoticeModifyDto.setDeleteFileIdxList(deleteFileIdxList); - return adminNoticeModifyDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/mapper/AdminNoticeMapper.java b/src/main/java/com/alist/api/modules/admin/notice/mapper/AdminNoticeMapper.java deleted file mode 100644 index 6f4d45c..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/mapper/AdminNoticeMapper.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.alist.api.modules.admin.notice.mapper; - -import com.alist.api.modules.admin.notice.dto.AdminNoticeAddDto; -import com.alist.api.modules.admin.notice.dto.AdminNoticeCampusDto; -import com.alist.api.modules.admin.notice.dto.AdminNoticeDeleteDto; -import com.alist.api.modules.admin.notice.dto.AdminNoticeFileDto; -import com.alist.api.modules.admin.notice.dto.AdminNoticeListDto; -import com.alist.api.modules.admin.notice.dto.AdminNoticeModifyDto; -import com.alist.api.modules.admin.notice.vo.AdminNoticeFileVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeViewVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeVo; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -@Mapper -public interface AdminNoticeMapper { - int selectAdminNoticeCount(AdminNoticeListDto adminNoticeListDto); - - List selectAdminNoticeList(AdminNoticeListDto adminNoticeListDto); - - AdminNoticeViewVo selectAdminNoticeView(Long noticeIdx); - - List selectAdminNoticeCampusIdxList(Long noticeIdx); - - List selectAdminNoticeFileList(Long noticeIdx); - - int insertAdminNoticeAdd(AdminNoticeAddDto adminNoticeAddDto); - - int insertAdminNoticeCampusAdd(AdminNoticeCampusDto adminNoticeCampusDto); - - int insertAdminNoticeFileAdd(AdminNoticeFileDto adminNoticeFileDto); - - int updateAdminNoticeModify(AdminNoticeModifyDto adminNoticeModifyDto); - - int updateAdminNoticeDelete(AdminNoticeDeleteDto adminNoticeDeleteDto); - - int deleteAdminNoticeCampusByNoticeIdx(Long noticeIdx); - - int updateAdminNoticeFileDeleteByNoticeIdx(@Param("noticeIdx") Long noticeIdx, @Param("updateMember") Integer updateMember); - - int updateAdminNoticeFileDeleteByNoticeFileIdxList( - @Param("noticeIdx") Long noticeIdx - , @Param("noticeFileIdxList") List noticeFileIdxList - , @Param("updateMember") Integer updateMember - ); -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/service/AdminNoticeService.java b/src/main/java/com/alist/api/modules/admin/notice/service/AdminNoticeService.java deleted file mode 100644 index 8d71a5a..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/service/AdminNoticeService.java +++ /dev/null @@ -1,213 +0,0 @@ -package com.alist.api.modules.admin.notice.service; - -import com.alist.api.common.utils.SecurityUtil; -import com.alist.api.modules.admin.auth.mapper.AdminAuthMapper; -import com.alist.api.modules.admin.auth.vo.AdminAuthLoginVo; -import com.alist.api.modules.admin.notice.dto.AdminNoticeAddDto; -import com.alist.api.modules.admin.notice.dto.AdminNoticeCampusDto; -import com.alist.api.modules.admin.notice.dto.AdminNoticeDeleteDto; -import com.alist.api.modules.admin.notice.dto.AdminNoticeFileDto; -import com.alist.api.modules.admin.notice.dto.AdminNoticeListDto; -import com.alist.api.modules.admin.notice.dto.AdminNoticeModifyDto; -import com.alist.api.modules.admin.notice.mapper.AdminNoticeMapper; -import com.alist.api.modules.admin.notice.vo.AdminNoticeAddVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeDeleteVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeFileVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeListVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeModifyVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeViewVo; -import com.alist.api.modules.admin.notice.vo.AdminNoticeVo; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Service -public class AdminNoticeService { - private final AdminNoticeMapper adminNoticeMapper; - private final AdminAuthMapper adminAuthMapper; - - public AdminNoticeService(AdminNoticeMapper adminNoticeMapper, AdminAuthMapper adminAuthMapper) { - this.adminNoticeMapper = adminNoticeMapper; - this.adminAuthMapper = adminAuthMapper; - } - - @Transactional(readOnly = true) - public AdminNoticeListVo selectAdminNoticeList(AdminNoticeListDto adminNoticeListDto) { - int totalCount = adminNoticeMapper.selectAdminNoticeCount(adminNoticeListDto); - List noticeList = adminNoticeMapper.selectAdminNoticeList(adminNoticeListDto); - - AdminNoticeListVo adminNoticeListVo = new AdminNoticeListVo(); - adminNoticeListVo.setNoticeList(noticeList); - adminNoticeListVo.setPaging(adminNoticeListDto, totalCount); - - return adminNoticeListVo; - } - - @Transactional(readOnly = true) - public AdminNoticeViewVo selectAdminNoticeView(Long noticeIdx) { - AdminNoticeViewVo adminNoticeViewVo = adminNoticeMapper.selectAdminNoticeView(noticeIdx); - - if (adminNoticeViewVo == null) { - return null; - } - - List campusIdxList = adminNoticeMapper.selectAdminNoticeCampusIdxList(noticeIdx); - List fileList = adminNoticeMapper.selectAdminNoticeFileList(noticeIdx); - adminNoticeViewVo.setCampusIdxList(campusIdxList); - adminNoticeViewVo.setFileList(fileList); - - return adminNoticeViewVo; - } - - @Transactional - public AdminNoticeAddVo insertAdminNoticeAdd(AdminNoticeAddDto adminNoticeAddDto) { - AdminNoticeAddVo adminNoticeAddVo = new AdminNoticeAddVo(); - Integer adminUserIdx = getLoginAdminUserIdx(); - - if (adminUserIdx == null) { - adminNoticeAddVo.setResultCode(401); - adminNoticeAddVo.setProcessed(false); - return adminNoticeAddVo; - } - - if (!isValidCampusScope(adminNoticeAddDto.getTargetScope(), adminNoticeAddDto.getCampusIdxList())) { - adminNoticeAddVo.setResultCode(4001); - adminNoticeAddVo.setProcessed(false); - return adminNoticeAddVo; - } - - adminNoticeAddDto.setCreateMember(adminUserIdx); - adminNoticeAddDto.setUpdateMember(adminUserIdx); - - int insertCount = adminNoticeMapper.insertAdminNoticeAdd(adminNoticeAddDto); - - insertCampusList(adminNoticeAddDto.getNoticeIdx(), adminNoticeAddDto.getTargetScope(), adminNoticeAddDto.getCampusIdxList(), adminUserIdx); - insertFileList(adminNoticeAddDto.getNoticeIdx(), adminNoticeAddDto.getFileList(), adminUserIdx); - - adminNoticeAddVo.setResultCode(2002); - adminNoticeAddVo.setProcessed(insertCount > 0); - adminNoticeAddVo.setNoticeIdx(adminNoticeAddDto.getNoticeIdx()); - - return adminNoticeAddVo; - } - - @Transactional - public AdminNoticeModifyVo updateAdminNoticeModify(AdminNoticeModifyDto adminNoticeModifyDto) { - AdminNoticeModifyVo adminNoticeModifyVo = new AdminNoticeModifyVo(); - Integer adminUserIdx = getLoginAdminUserIdx(); - - if (adminUserIdx == null) { - adminNoticeModifyVo.setResultCode(401); - adminNoticeModifyVo.setProcessed(false); - return adminNoticeModifyVo; - } - - if (!isValidCampusScope(adminNoticeModifyDto.getTargetScope(), adminNoticeModifyDto.getCampusIdxList())) { - adminNoticeModifyVo.setResultCode(4001); - adminNoticeModifyVo.setProcessed(false); - return adminNoticeModifyVo; - } - - adminNoticeModifyDto.setUpdateMember(adminUserIdx); - - int updateCount = adminNoticeMapper.updateAdminNoticeModify(adminNoticeModifyDto); - - if (updateCount > 0) { - adminNoticeMapper.deleteAdminNoticeCampusByNoticeIdx(adminNoticeModifyDto.getNoticeIdx()); - deleteFileList(adminNoticeModifyDto.getNoticeIdx(), adminNoticeModifyDto.getDeleteFileIdxList(), adminUserIdx); - - insertCampusList(adminNoticeModifyDto.getNoticeIdx(), adminNoticeModifyDto.getTargetScope(), adminNoticeModifyDto.getCampusIdxList(), adminUserIdx); - insertFileList(adminNoticeModifyDto.getNoticeIdx(), adminNoticeModifyDto.getFileList(), adminUserIdx); - } - - adminNoticeModifyVo.setResultCode(2005); - adminNoticeModifyVo.setProcessed(updateCount > 0); - - return adminNoticeModifyVo; - } - - @Transactional - public AdminNoticeDeleteVo updateAdminNoticeDelete(AdminNoticeDeleteDto adminNoticeDeleteDto) { - AdminNoticeDeleteVo adminNoticeDeleteVo = new AdminNoticeDeleteVo(); - Integer adminUserIdx = getLoginAdminUserIdx(); - - if (adminUserIdx == null) { - adminNoticeDeleteVo.setResultCode(401); - adminNoticeDeleteVo.setProcessed(false); - return adminNoticeDeleteVo; - } - - adminNoticeDeleteDto.setUpdateMember(adminUserIdx); - - int updateCount = adminNoticeMapper.updateAdminNoticeDelete(adminNoticeDeleteDto); - - if (updateCount > 0) { - adminNoticeMapper.updateAdminNoticeFileDeleteByNoticeIdx(adminNoticeDeleteDto.getNoticeIdx(), adminUserIdx); - } - - adminNoticeDeleteVo.setResultCode(2005); - adminNoticeDeleteVo.setProcessed(updateCount > 0); - - return adminNoticeDeleteVo; - } - - private Integer getLoginAdminUserIdx() { - Integer loginUserTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (loginUserTokenIdx == null) { - return null; - } - - AdminAuthLoginVo adminAuthLoginVo = adminAuthMapper.selectAdminTokenByUserTokenIdx(loginUserTokenIdx); - - if (adminAuthLoginVo == null || adminAuthLoginVo.getUserIdx() == null) { - return null; - } - - return adminAuthLoginVo.getUserIdx(); - } - - private boolean isValidCampusScope(String targetScope, List campusIdxList) { - if (!"CAMPUS".equals(targetScope)) { - return true; - } - - return campusIdxList != null && !campusIdxList.isEmpty(); - } - - private void insertCampusList(Long noticeIdx, String targetScope, List campusIdxList, Integer adminUserIdx) { - if (!"CAMPUS".equals(targetScope) || campusIdxList == null) { - return; - } - - for (Long campusIdx : campusIdxList) { - AdminNoticeCampusDto adminNoticeCampusDto = new AdminNoticeCampusDto(); - adminNoticeCampusDto.setNoticeIdx(noticeIdx); - adminNoticeCampusDto.setCampusIdx(campusIdx); - adminNoticeCampusDto.setCreateMember(adminUserIdx); - adminNoticeMapper.insertAdminNoticeCampusAdd(adminNoticeCampusDto); - } - } - - private void insertFileList(Long noticeIdx, List fileList, Integer adminUserIdx) { - if (fileList == null || fileList.isEmpty()) { - return; - } - - for (AdminNoticeFileDto adminNoticeFileDto : fileList) { - adminNoticeFileDto.setNoticeIdx(noticeIdx); - adminNoticeFileDto.setCreateMember(adminUserIdx); - adminNoticeFileDto.setUpdateMember(adminUserIdx); - adminNoticeMapper.insertAdminNoticeFileAdd(adminNoticeFileDto); - } - } - - private void deleteFileList(Long noticeIdx, List deleteFileIdxList, Integer adminUserIdx) { - if (deleteFileIdxList == null || deleteFileIdxList.isEmpty()) { - return; - } - - adminNoticeMapper.updateAdminNoticeFileDeleteByNoticeFileIdxList(noticeIdx, deleteFileIdxList, adminUserIdx); - } -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeDeleteVo.java b/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeDeleteVo.java deleted file mode 100644 index 2d3c7dd..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeDeleteVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.admin.notice.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 공지사항 삭제 응답") -@Getter -@Setter -public class AdminNoticeDeleteVo { - @Schema(description = "공지사항 삭제 처리 여부", example = "true") - private boolean processed; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeListVo.java b/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeListVo.java deleted file mode 100644 index 96d10a9..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeListVo.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alist.api.modules.admin.notice.vo; - -import com.alist.api.common.paging.PageResponse; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "관리자 공지사항 목록 조회 응답") -@Getter -@Setter -public class AdminNoticeListVo extends PageResponse { - @Schema(description = "공지사항 목록") - private List noticeList; -} diff --git a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeModifyVo.java b/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeModifyVo.java deleted file mode 100644 index 5abe84d..0000000 --- a/src/main/java/com/alist/api/modules/admin/notice/vo/AdminNoticeModifyVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.admin.notice.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 공지사항 수정 응답") -@Getter -@Setter -public class AdminNoticeModifyVo { - @Schema(description = "공지사항 수정 여부", example = "true") - private boolean processed; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/AdminScheduleController.java b/src/main/java/com/alist/api/modules/admin/schedule/AdminScheduleController.java deleted file mode 100644 index e29ddf7..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/AdminScheduleController.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.alist.api.modules.admin.schedule; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.modules.admin.schedule.form.*; -import com.alist.api.modules.admin.schedule.service.AdminScheduleService; -import com.alist.api.modules.admin.schedule.vo.*; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@Tag(name = "504. 스케줄 관리", description = "관리자 스케줄 작업 관리 API") -@Slf4j -@RestController -@RequestMapping("/admin/schedule") -public class AdminScheduleController { - private final AdminScheduleService adminScheduleService; - - public AdminScheduleController(AdminScheduleService adminScheduleService) { - this.adminScheduleService = adminScheduleService; - } - - @Operation( - summary = "스케줄 작업 목록 조회" - , description = "관리자가 스케줄 작업 목록을 검색 조건과 페이징으로 조회합니다." - ) - @GetMapping("/list") - public ResponseEntity> adminScheduleList( - @ModelAttribute AdminScheduleListForm adminScheduleListForm - ) { - AdminScheduleListVo adminScheduleListVo = adminScheduleService.selectAdminScheduleList(adminScheduleListForm.toDto()); - - if (adminScheduleListVo.getScheduleList() == null || adminScheduleListVo.getScheduleList().isEmpty()) { - return ApiResponse.entity(adminScheduleListVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminScheduleListVo, ApiResponseCode.CODE_2001, "스케줄 작업"); - } - - @Operation( - summary = "스케줄 작업 상세 조회" - , description = "관리자가 스케줄 작업 PK 기준으로 상세 정보를 조회합니다." - ) - @GetMapping("/view") - public ResponseEntity> adminScheduleView( - @RequestParam Long scheduleJobIdx - ) { - AdminScheduleViewVo adminScheduleViewVo = adminScheduleService.selectAdminScheduleView(scheduleJobIdx); - - if (adminScheduleViewVo == null) { - return ApiResponse.entity((AdminScheduleViewVo) null, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminScheduleViewVo, ApiResponseCode.CODE_2001, "스케줄 작업"); - } - - @Operation( - summary = "스케줄 작업 등록" - , description = "관리자가 스케줄 작업을 등록합니다. JOB_NAME과 JOB_GROUP이 중복되면 등록하지 않습니다." - ) - @PostMapping("/add") - public ResponseEntity> adminScheduleAdd( - @Valid @RequestBody AdminScheduleAddForm adminScheduleAddForm - ) { - AdminScheduleAddVo adminScheduleAddVo = adminScheduleService.insertAdminScheduleAdd(adminScheduleAddForm.toDto()); - - if (adminScheduleAddVo.getResultCode() == 401) { - return ApiResponse.entity(adminScheduleAddVo, ApiResponseCode.CODE_401); - } - - if (!adminScheduleAddVo.isProcessed()) { - return ApiResponse.entity(adminScheduleAddVo, ApiResponseCode.CODE_2004, "스케줄 작업"); - } - - return ApiResponse.entity(adminScheduleAddVo, ApiResponseCode.CODE_2002, "스케줄 작업"); - } - - @Operation( - summary = "스케줄 작업 수정" - , description = "관리자가 스케줄 작업 PK 기준으로 작업 코드, 크론식, 사용 여부 등 기본 정보를 수정합니다." - ) - @PutMapping("/modify") - public ResponseEntity> adminScheduleModify( - @Valid @RequestBody AdminScheduleModifyForm adminScheduleModifyForm - ) { - AdminScheduleModifyVo adminScheduleModifyVo = adminScheduleService.updateAdminScheduleModify(adminScheduleModifyForm.toDto()); - - if (adminScheduleModifyVo.getResultCode() == 401) { - return ApiResponse.entity(adminScheduleModifyVo, ApiResponseCode.CODE_401); - } - - if (adminScheduleModifyVo.getResultCode() == 2004) { - return ApiResponse.entity(adminScheduleModifyVo, ApiResponseCode.CODE_2004, "스케줄 작업"); - } - - if (!adminScheduleModifyVo.isProcessed()) { - return ApiResponse.entity(adminScheduleModifyVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminScheduleModifyVo, ApiResponseCode.CODE_2005, "스케줄 작업 수정"); - } - - @Operation( - summary = "스케줄 작업 삭제" - , description = "관리자가 스케줄 작업 PK 기준으로 삭제 처리합니다. 실제 삭제가 아닌 DEL_YN 변경 방식입니다." - ) - @PatchMapping("/delete") - public ResponseEntity> adminScheduleDelete( - @Valid @RequestBody AdminScheduleDeleteForm adminScheduleDeleteForm - ) { - AdminScheduleDeleteVo adminScheduleDeleteVo = adminScheduleService.updateAdminScheduleDelete(adminScheduleDeleteForm.toDto()); - - if (adminScheduleDeleteVo.getResultCode() == 401) { - return ApiResponse.entity(adminScheduleDeleteVo, ApiResponseCode.CODE_401); - } - - if (!adminScheduleDeleteVo.isProcessed()) { - return ApiResponse.entity(adminScheduleDeleteVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminScheduleDeleteVo, ApiResponseCode.CODE_2005, "스케줄 작업 삭제"); - } - - @Operation( - summary = "스케줄 작업 실행 로그 목록 조회" - , description = "관리자가 스케줄 작업 실행 로그를 검색 조건과 페이징으로 조회합니다." - ) - @GetMapping("/log/list") - public ResponseEntity> adminScheduleLogList( - @ModelAttribute AdminScheduleLogListForm adminScheduleLogListForm - ) { - AdminScheduleLogListVo adminScheduleLogListVo = adminScheduleService.selectAdminScheduleLogList(adminScheduleLogListForm.toDto()); - - if (adminScheduleLogListVo.getScheduleLogList() == null || adminScheduleLogListVo.getScheduleLogList().isEmpty()) { - return ApiResponse.entity(adminScheduleLogListVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminScheduleLogListVo, ApiResponseCode.CODE_2001, "스케줄 작업 실행 로그"); - } -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleListDto.java b/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleListDto.java deleted file mode 100644 index 5cbd2ab..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleListDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.alist.api.modules.admin.schedule.dto; - -import com.alist.api.common.paging.PageRequest; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminScheduleListDto extends PageRequest { - private String keyword; - private String jobGroup; - private Integer lastStatus; - private String useYn; -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleLogListDto.java b/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleLogListDto.java deleted file mode 100644 index 54b939f..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleLogListDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.alist.api.modules.admin.schedule.dto; - -import com.alist.api.common.paging.PageRequest; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminScheduleLogListDto extends PageRequest { - private Long scheduleJobIdx; - private Integer status; - private String startDateFrom; - private String startDateTo; -} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleModifyDto.java b/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleModifyDto.java deleted file mode 100644 index e993434..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/dto/AdminScheduleModifyDto.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.admin.schedule.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminScheduleModifyDto { - private Long scheduleJobIdx; - private String jobName; - private String jobGroup; - private String jobDescription; - private String cronExpr; - private String timezoneId; - private Integer maxRunningSeconds; - private String useYn; - private Integer updateMember; -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleAddForm.java b/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleAddForm.java deleted file mode 100644 index 9c7d160..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleAddForm.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.alist.api.modules.admin.schedule.form; - -import com.alist.api.modules.admin.schedule.dto.AdminScheduleAddDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 스케줄 작업 등록 요청") -@Getter -@Setter -public class AdminScheduleAddForm { - @NotBlank - @Size(max = 100) - @Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY", requiredMode = Schema.RequiredMode.REQUIRED) - private String jobName; - - @Size(max = 50) - @Schema(description = "작업 그룹. 미입력 시 DEFAULT", example = "DEFAULT") - private String jobGroup; - - @Size(max = 255) - @Schema(description = "작업 설명", example = "일별 학습 통계 집계") - private String jobDescription; - - @NotBlank - @Size(max = 100) - @Schema(description = "크론식", example = "0 0 2 * * *", requiredMode = Schema.RequiredMode.REQUIRED) - private String cronExpr; - - @Size(max = 50) - @Schema(description = "타임존. 미입력 시 Asia/Seoul", example = "Asia/Seoul") - private String timezoneId; - - @Min(1) - @Schema(description = "최대 실행 허용 시간(초). 미입력 시 1800", example = "1800") - private Integer maxRunningSeconds; - - @Pattern(regexp = "Y|N") - @Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y") - private String useYn; - - public AdminScheduleAddDto toDto() { - AdminScheduleAddDto adminScheduleAddDto = new AdminScheduleAddDto(); - adminScheduleAddDto.setJobName(jobName.trim()); - adminScheduleAddDto.setJobGroup(jobGroup == null || jobGroup.trim().isEmpty() ? "DEFAULT" : jobGroup.trim()); - adminScheduleAddDto.setJobDescription(jobDescription == null ? null : jobDescription.trim()); - adminScheduleAddDto.setCronExpr(cronExpr.trim()); - adminScheduleAddDto.setTimezoneId(timezoneId == null || timezoneId.trim().isEmpty() ? "Asia/Seoul" : timezoneId.trim()); - adminScheduleAddDto.setMaxRunningSeconds(maxRunningSeconds == null ? 1800 : maxRunningSeconds); - adminScheduleAddDto.setUseYn(useYn == null || useYn.trim().isEmpty() ? "Y" : useYn.trim()); - return adminScheduleAddDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleDeleteForm.java b/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleDeleteForm.java deleted file mode 100644 index 8ab7d5b..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleDeleteForm.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.alist.api.modules.admin.schedule.form; - -import com.alist.api.modules.admin.schedule.dto.AdminScheduleDeleteDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 스케줄 작업 삭제 요청") -@Getter -@Setter -public class AdminScheduleDeleteForm { - @NotNull - @Schema(description = "스케줄 작업 PK", example = "9", requiredMode = Schema.RequiredMode.REQUIRED) - private Long scheduleJobIdx; - - public AdminScheduleDeleteDto toDto() { - AdminScheduleDeleteDto adminScheduleDeleteDto = new AdminScheduleDeleteDto(); - adminScheduleDeleteDto.setScheduleJobIdx(scheduleJobIdx); - return adminScheduleDeleteDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleListForm.java b/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleListForm.java deleted file mode 100644 index ca6c752..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleListForm.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.alist.api.modules.admin.schedule.form; - -import com.alist.api.common.paging.PageRequest; -import com.alist.api.modules.admin.schedule.dto.AdminScheduleListDto; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 스케줄 작업 목록 검색 조건") -@Getter -@Setter -public class AdminScheduleListForm extends PageRequest { - @Schema(description = "검색어. 작업 코드, 작업 설명을 대상으로 검색합니다.", example = "DAILY") - private String keyword; - - @Schema(description = "작업 그룹", example = "DEFAULT") - private String jobGroup; - - @Schema(description = "마지막 실행 상태. 0: IDLE, 1: RUNNING, 2: SUCCESS, 3: SKIP, 4: FAIL", example = "2") - private Integer lastStatus; - - @Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y") - private String useYn; - - public AdminScheduleListDto toDto() { - AdminScheduleListDto adminScheduleListDto = new AdminScheduleListDto(); - adminScheduleListDto.setPage(getPage()); - adminScheduleListDto.setSize(getSize()); - adminScheduleListDto.setKeyword(keyword == null ? null : keyword.trim()); - adminScheduleListDto.setJobGroup(jobGroup == null ? null : jobGroup.trim()); - adminScheduleListDto.setLastStatus(lastStatus); - adminScheduleListDto.setUseYn(useYn == null ? null : useYn.trim()); - return adminScheduleListDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleLogListForm.java b/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleLogListForm.java deleted file mode 100644 index 0d2546b..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleLogListForm.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.alist.api.modules.admin.schedule.form; - -import com.alist.api.common.paging.PageRequest; -import com.alist.api.modules.admin.schedule.dto.AdminScheduleLogListDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 스케줄 작업 실행 로그 목록 검색 조건") -@Getter -@Setter -public class AdminScheduleLogListForm extends PageRequest { - @Schema(description = "스케줄 작업 PK", example = "9", requiredMode = Schema.RequiredMode.REQUIRED) - private Long scheduleJobIdx; - - @Schema(description = "실행 상태. 1: RUNNING, 2: SUCCESS, 3: SKIP, 4: FAIL", example = "2") - private Integer status; - - @Schema(description = "실행 시작일 검색 시작값. yyyy-MM-dd 또는 yyyy-MM-dd HH:mm:ss", example = "2026-05-01") - private String startDateFrom; - - @Schema(description = "실행 시작일 검색 종료값. yyyy-MM-dd 또는 yyyy-MM-dd HH:mm:ss", example = "2026-05-28") - private String startDateTo; - - public AdminScheduleLogListDto toDto() { - AdminScheduleLogListDto adminScheduleLogListDto = new AdminScheduleLogListDto(); - adminScheduleLogListDto.setPage(getPage()); - adminScheduleLogListDto.setSize(getSize()); - adminScheduleLogListDto.setScheduleJobIdx(scheduleJobIdx); - adminScheduleLogListDto.setStatus(status); - adminScheduleLogListDto.setStartDateFrom(startDateFrom == null ? null : startDateFrom.trim()); - adminScheduleLogListDto.setStartDateTo(startDateTo == null ? null : startDateTo.trim()); - return adminScheduleLogListDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleModifyForm.java b/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleModifyForm.java deleted file mode 100644 index 60c294a..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/form/AdminScheduleModifyForm.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.alist.api.modules.admin.schedule.form; - -import com.alist.api.modules.admin.schedule.dto.AdminScheduleModifyDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.*; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 스케줄 작업 수정 요청") -@Getter -@Setter -public class AdminScheduleModifyForm { - @NotNull - @Schema(description = "스케줄 작업 PK", example = "9", requiredMode = Schema.RequiredMode.REQUIRED) - private Long scheduleJobIdx; - - @NotBlank - @Size(max = 100) - @Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY", requiredMode = Schema.RequiredMode.REQUIRED) - private String jobName; - - @Size(max = 50) - @Schema(description = "작업 그룹. 미입력 시 DEFAULT", example = "DEFAULT") - private String jobGroup; - - @Size(max = 255) - @Schema(description = "작업 설명", example = "일별 학습 통계 집계") - private String jobDescription; - - @NotBlank - @Size(max = 100) - @Schema(description = "크론식", example = "0 0 2 * * *", requiredMode = Schema.RequiredMode.REQUIRED) - private String cronExpr; - - @Size(max = 50) - @Schema(description = "타임존. 미입력 시 Asia/Seoul", example = "Asia/Seoul") - private String timezoneId; - - @Min(1) - @Schema(description = "최대 실행 허용 시간(초). 미입력 시 1800", example = "1800") - private Integer maxRunningSeconds; - - @Pattern(regexp = "Y|N") - @Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y") - private String useYn; - - public AdminScheduleModifyDto toDto() { - AdminScheduleModifyDto adminScheduleModifyDto = new AdminScheduleModifyDto(); - adminScheduleModifyDto.setScheduleJobIdx(scheduleJobIdx); - adminScheduleModifyDto.setJobName(jobName.trim()); - adminScheduleModifyDto.setJobGroup(jobGroup == null || jobGroup.trim().isEmpty() ? "DEFAULT" : jobGroup.trim()); - adminScheduleModifyDto.setJobDescription(jobDescription == null ? null : jobDescription.trim()); - adminScheduleModifyDto.setCronExpr(cronExpr.trim()); - adminScheduleModifyDto.setTimezoneId(timezoneId == null || timezoneId.trim().isEmpty() ? "Asia/Seoul" : timezoneId.trim()); - adminScheduleModifyDto.setMaxRunningSeconds(maxRunningSeconds == null ? 1800 : maxRunningSeconds); - adminScheduleModifyDto.setUseYn(useYn == null || useYn.trim().isEmpty() ? "Y" : useYn.trim()); - return adminScheduleModifyDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/mapper/AdminScheduleMapper.java b/src/main/java/com/alist/api/modules/admin/schedule/mapper/AdminScheduleMapper.java deleted file mode 100644 index 61d6fd3..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/mapper/AdminScheduleMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.alist.api.modules.admin.schedule.mapper; - -import com.alist.api.modules.admin.schedule.dto.*; -import com.alist.api.modules.admin.schedule.vo.AdminScheduleLogVo; -import com.alist.api.modules.admin.schedule.vo.AdminScheduleViewVo; -import com.alist.api.modules.admin.schedule.vo.AdminScheduleVo; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface AdminScheduleMapper { - int selectAdminScheduleDuplicateCount(AdminScheduleAddDto adminScheduleAddDto); - - int insertAdminScheduleAdd(AdminScheduleAddDto adminScheduleAddDto); - - int selectAdminScheduleCount(AdminScheduleListDto adminScheduleListDto); - - List selectAdminScheduleList(AdminScheduleListDto adminScheduleListDto); - - AdminScheduleViewVo selectAdminScheduleView(Long scheduleJobIdx); - - int selectAdminScheduleModifyDuplicateCount(AdminScheduleModifyDto adminScheduleModifyDto); - - int updateAdminScheduleModify(AdminScheduleModifyDto adminScheduleModifyDto); - - int updateAdminScheduleDelete(AdminScheduleDeleteDto adminScheduleDeleteDto); - - int selectAdminScheduleLogCount(AdminScheduleLogListDto adminScheduleLogListDto); - - List selectAdminScheduleLogList(AdminScheduleLogListDto adminScheduleLogListDto); -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/service/AdminScheduleService.java b/src/main/java/com/alist/api/modules/admin/schedule/service/AdminScheduleService.java deleted file mode 100644 index 3bc260b..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/service/AdminScheduleService.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.alist.api.modules.admin.schedule.service; - -import com.alist.api.common.utils.SecurityUtil; -import com.alist.api.modules.admin.auth.mapper.AdminAuthMapper; -import com.alist.api.modules.admin.auth.vo.AdminAuthLoginVo; -import com.alist.api.modules.admin.schedule.dto.*; -import com.alist.api.modules.admin.schedule.mapper.AdminScheduleMapper; -import com.alist.api.modules.admin.schedule.vo.*; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Service -public class AdminScheduleService { - private final AdminScheduleMapper adminScheduleMapper; - private final AdminAuthMapper adminAuthMapper; - - public AdminScheduleService(AdminScheduleMapper adminScheduleMapper, AdminAuthMapper adminAuthMapper) { - this.adminScheduleMapper = adminScheduleMapper; - this.adminAuthMapper = adminAuthMapper; - } - - @Transactional(readOnly = true) - public AdminScheduleListVo selectAdminScheduleList(AdminScheduleListDto adminScheduleListDto) { - int totalCount = adminScheduleMapper.selectAdminScheduleCount(adminScheduleListDto); - List scheduleList = adminScheduleMapper.selectAdminScheduleList(adminScheduleListDto); - - AdminScheduleListVo adminScheduleListVo = new AdminScheduleListVo(); - adminScheduleListVo.setScheduleList(scheduleList); - adminScheduleListVo.setPaging(adminScheduleListDto, totalCount); - - return adminScheduleListVo; - } - - @Transactional(readOnly = true) - public AdminScheduleViewVo selectAdminScheduleView(Long scheduleJobIdx) { - return adminScheduleMapper.selectAdminScheduleView(scheduleJobIdx); - } - - @Transactional - public AdminScheduleAddVo insertAdminScheduleAdd(AdminScheduleAddDto adminScheduleAddDto) { - AdminScheduleAddVo adminScheduleAddVo = new AdminScheduleAddVo(); - - Integer loginUserTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (loginUserTokenIdx == null) { - adminScheduleAddVo.setResultCode(401); - adminScheduleAddVo.setProcessed(false); - return adminScheduleAddVo; - } - - AdminAuthLoginVo adminAuthLoginVo = adminAuthMapper.selectAdminTokenByUserTokenIdx(loginUserTokenIdx); - - if (adminAuthLoginVo == null || adminAuthLoginVo.getUserIdx() == null) { - adminScheduleAddVo.setResultCode(401); - adminScheduleAddVo.setProcessed(false); - return adminScheduleAddVo; - } - - adminScheduleAddDto.setCreateMember(adminAuthLoginVo.getUserIdx()); - adminScheduleAddDto.setUpdateMember(adminAuthLoginVo.getUserIdx()); - - int duplicateCount = adminScheduleMapper.selectAdminScheduleDuplicateCount(adminScheduleAddDto); - - if (duplicateCount > 0) { - adminScheduleAddVo.setResultCode(2004); - adminScheduleAddVo.setProcessed(false); - return adminScheduleAddVo; - } - - int insertCount = adminScheduleMapper.insertAdminScheduleAdd(adminScheduleAddDto); - - adminScheduleAddVo.setResultCode(2002); - adminScheduleAddVo.setProcessed(insertCount > 0); - adminScheduleAddVo.setScheduleJobIdx(adminScheduleAddDto.getScheduleJobIdx()); - - return adminScheduleAddVo; - } - - @Transactional - public AdminScheduleModifyVo updateAdminScheduleModify(AdminScheduleModifyDto adminScheduleModifyDto) { - AdminScheduleModifyVo adminScheduleModifyVo = new AdminScheduleModifyVo(); - - Integer loginUserTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (loginUserTokenIdx == null) { - adminScheduleModifyVo.setResultCode(401); - adminScheduleModifyVo.setProcessed(false); - return adminScheduleModifyVo; - } - - AdminAuthLoginVo adminAuthLoginVo = adminAuthMapper.selectAdminTokenByUserTokenIdx(loginUserTokenIdx); - - if (adminAuthLoginVo == null || adminAuthLoginVo.getUserIdx() == null) { - adminScheduleModifyVo.setResultCode(401); - adminScheduleModifyVo.setProcessed(false); - return adminScheduleModifyVo; - } - - adminScheduleModifyDto.setUpdateMember(adminAuthLoginVo.getUserIdx()); - - int duplicateCount = adminScheduleMapper.selectAdminScheduleModifyDuplicateCount(adminScheduleModifyDto); - - if (duplicateCount > 0) { - adminScheduleModifyVo.setResultCode(2004); - adminScheduleModifyVo.setProcessed(false); - return adminScheduleModifyVo; - } - - int updateCount = adminScheduleMapper.updateAdminScheduleModify(adminScheduleModifyDto); - - adminScheduleModifyVo.setResultCode(2005); - adminScheduleModifyVo.setProcessed(updateCount > 0); - - return adminScheduleModifyVo; - } - - @Transactional - public AdminScheduleDeleteVo updateAdminScheduleDelete(AdminScheduleDeleteDto adminScheduleDeleteDto) { - AdminScheduleDeleteVo adminScheduleDeleteVo = new AdminScheduleDeleteVo(); - - Integer loginUserTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (loginUserTokenIdx == null) { - adminScheduleDeleteVo.setResultCode(401); - adminScheduleDeleteVo.setProcessed(false); - return adminScheduleDeleteVo; - } - - AdminAuthLoginVo adminAuthLoginVo = adminAuthMapper.selectAdminTokenByUserTokenIdx(loginUserTokenIdx); - - if (adminAuthLoginVo == null || adminAuthLoginVo.getUserIdx() == null) { - adminScheduleDeleteVo.setResultCode(401); - adminScheduleDeleteVo.setProcessed(false); - return adminScheduleDeleteVo; - } - - adminScheduleDeleteDto.setUpdateMember(adminAuthLoginVo.getUserIdx()); - - int updateCount = adminScheduleMapper.updateAdminScheduleDelete(adminScheduleDeleteDto); - - adminScheduleDeleteVo.setResultCode(2005); - adminScheduleDeleteVo.setProcessed(updateCount > 0); - - return adminScheduleDeleteVo; - } - - @Transactional(readOnly = true) - public AdminScheduleLogListVo selectAdminScheduleLogList(AdminScheduleLogListDto adminScheduleLogListDto) { - int totalCount = adminScheduleMapper.selectAdminScheduleLogCount(adminScheduleLogListDto); - List scheduleLogList = adminScheduleMapper.selectAdminScheduleLogList(adminScheduleLogListDto); - - AdminScheduleLogListVo adminScheduleLogListVo = new AdminScheduleLogListVo(); - adminScheduleLogListVo.setScheduleLogList(scheduleLogList); - adminScheduleLogListVo.setPaging(adminScheduleLogListDto, totalCount); - - return adminScheduleLogListVo; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleAddVo.java b/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleAddVo.java deleted file mode 100644 index 67e3986..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleAddVo.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.alist.api.modules.admin.schedule.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 스케줄 작업 등록 응답") -@Getter -@Setter -public class AdminScheduleAddVo { - @Schema(description = "스케줄 작업 등록 여부", example = "true") - private boolean processed; - - @Schema(description = "등록된 스케줄 작업 PK", example = "9") - private Long scheduleJobIdx; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleDeleteVo.java b/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleDeleteVo.java deleted file mode 100644 index d913095..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleDeleteVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.admin.schedule.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 스케줄 작업 삭제 응답") -@Getter -@Setter -public class AdminScheduleDeleteVo { - @Schema(description = "스케줄 작업 삭제 처리 여부", example = "true") - private boolean processed; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleListVo.java b/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleListVo.java deleted file mode 100644 index 54ee5f1..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleListVo.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alist.api.modules.admin.schedule.vo; - -import com.alist.api.common.paging.PageResponse; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "관리자 스케줄 작업 목록 조회 응답") -@Getter -@Setter -public class AdminScheduleListVo extends PageResponse { - @Schema(description = "스케줄 작업 목록") - private List scheduleList; -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleLogListVo.java b/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleLogListVo.java deleted file mode 100644 index d71a92c..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleLogListVo.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alist.api.modules.admin.schedule.vo; - -import com.alist.api.common.paging.PageResponse; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "관리자 스케줄 작업 실행 로그 목록 조회 응답") -@Getter -@Setter -public class AdminScheduleLogListVo extends PageResponse { - @Schema(description = "스케줄 작업 실행 로그 목록") - private List scheduleLogList; -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleLogVo.java b/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleLogVo.java deleted file mode 100644 index 019bbbe..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleLogVo.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.alist.api.modules.admin.schedule.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.time.LocalDateTime; - -@Schema(description = "관리자 스케줄 작업 실행 로그 목록 항목") -@Getter -@Setter -public class AdminScheduleLogVo { - @Schema(description = "스케줄 작업 로그 PK", example = "20185") - private Long scheduleJobLogIdx; - - @Schema(description = "스케줄 작업 PK", example = "9") - private Long scheduleJobIdx; - - @Schema(description = "1회 실행 식별자", example = "20260528093000-abc123") - private String runId; - - @Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY") - private String jobName; - - @Schema(description = "실행 시작 시각", example = "2026-05-28 09:30:00") - private LocalDateTime startDate; - - @Schema(description = "실행 종료 시각", example = "2026-05-28 09:30:12") - private LocalDateTime endDate; - - @Schema(description = "대상 건수", example = "100") - private Integer targetCount; - - @Schema(description = "성공 건수", example = "98") - private Integer successCount; - - @Schema(description = "실패 건수", example = "2") - private Integer failCount; - - @Schema(description = "요약 메시지", example = "일별 학습 통계 집계 완료") - private String message; - - @Schema(description = "오류 메시지", example = "timeout") - private String errorMessage; - - @Schema(description = "실행 서버", example = "api-01") - private String lockOwner; - - @Schema(description = "실행 상태. 1: RUNNING, 2: SUCCESS, 3: SKIP, 4: FAIL", example = "2") - private Integer status; - - @Schema(description = "기록 시각", example = "2026-05-28 09:30:00") - private LocalDateTime createDate; -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleModifyVo.java b/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleModifyVo.java deleted file mode 100644 index 0743a96..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleModifyVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.admin.schedule.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 스케줄 작업 수정 응답") -@Getter -@Setter -public class AdminScheduleModifyVo { - @Schema(description = "스케줄 작업 수정 여부", example = "true") - private boolean processed; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleViewVo.java b/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleViewVo.java deleted file mode 100644 index dd39dac..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleViewVo.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.alist.api.modules.admin.schedule.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.time.LocalDateTime; - -@Schema(description = "관리자 스케줄 작업 상세 조회 응답") -@Getter -@Setter -public class AdminScheduleViewVo { - @Schema(description = "스케줄 작업 PK", example = "9") - private Long scheduleJobIdx; - - @Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY") - private String jobName; - - @Schema(description = "작업 그룹", example = "DEFAULT") - private String jobGroup; - - @Schema(description = "작업 설명", example = "일별 학습 통계 집계") - private String jobDescription; - - @Schema(description = "크론식", example = "0 0 2 * * *") - private String cronExpr; - - @Schema(description = "타임존", example = "Asia/Seoul") - private String timezoneId; - - @Schema(description = "최대 실행 허용 시간(초)", example = "1800") - private Integer maxRunningSeconds; - - @Schema(description = "마지막 실행 시작 시각", example = "2026-05-27 02:00:00") - private LocalDateTime lastRunDate; - - @Schema(description = "마지막 실행 종료 시각", example = "2026-05-27 02:00:10") - private LocalDateTime lastEndDate; - - @Schema(description = "마지막 성공 시각", example = "2026-05-27 02:00:10") - private LocalDateTime lastSuccessDate; - - @Schema(description = "마지막 실행 상태. 0: IDLE, 1: RUNNING, 2: SUCCESS, 3: SKIP, 4: FAIL", example = "2") - private Integer lastStatus; - - @Schema(description = "마지막 실행 메시지", example = "정상 처리") - private String lastMessage; - - @Schema(description = "실행 락 시각", example = "2026-05-27 02:00:00") - private LocalDateTime runLockDate; - - @Schema(description = "실행 락 소유자", example = "api-01") - private String runLockOwner; - - @Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y") - private String useYn; - - @Schema(description = "삭제 여부. Y: 삭제, N: 정상", example = "N") - private String delYn; - - @Schema(description = "최초 생성일", example = "2026-05-27 10:00:00") - private LocalDateTime createDate; - - @Schema(description = "최초 생성자 회원 PK", example = "1") - private Integer createMember; - - @Schema(description = "마지막 수정일", example = "2026-05-27 10:00:00") - private LocalDateTime updateDate; - - @Schema(description = "마지막 수정자 회원 PK", example = "1") - private Integer updateMember; -} diff --git a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleVo.java b/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleVo.java deleted file mode 100644 index 908b001..0000000 --- a/src/main/java/com/alist/api/modules/admin/schedule/vo/AdminScheduleVo.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.alist.api.modules.admin.schedule.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.time.LocalDateTime; - -@Schema(description = "관리자 스케줄 작업 목록 항목") -@Getter -@Setter -public class AdminScheduleVo { - @Schema(description = "스케줄 작업 PK", example = "9") - private Long scheduleJobIdx; - - @Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY") - private String jobName; - - @Schema(description = "작업 그룹", example = "DEFAULT") - private String jobGroup; - - @Schema(description = "작업 설명", example = "일별 학습 통계 집계") - private String jobDescription; - - @Schema(description = "크론식", example = "0 0 2 * * *") - private String cronExpr; - - @Schema(description = "타임존", example = "Asia/Seoul") - private String timezoneId; - - @Schema(description = "최대 실행 허용 시간(초)", example = "1800") - private Integer maxRunningSeconds; - - @Schema(description = "마지막 실행 시작 시각", example = "2026-05-27 02:00:00") - private LocalDateTime lastRunDate; - - @Schema(description = "마지막 실행 종료 시각", example = "2026-05-27 02:00:10") - private LocalDateTime lastEndDate; - - @Schema(description = "마지막 성공 시각", example = "2026-05-27 02:00:10") - private LocalDateTime lastSuccessDate; - - @Schema(description = "마지막 실행 상태. 0: IDLE, 1: RUNNING, 2: SUCCESS, 3: SKIP, 4: FAIL", example = "2") - private Integer lastStatus; - - @Schema(description = "마지막 실행 메시지", example = "정상 처리") - private String lastMessage; - - @Schema(description = "실행 락 시각", example = "2026-05-27 02:00:00") - private LocalDateTime runLockDate; - - @Schema(description = "실행 락 소유자", example = "api-01") - private String runLockOwner; - - @Schema(description = "사용 여부", example = "Y") - private String useYn; - - @Schema(description = "생성일", example = "2026-05-27 10:00:00") - private LocalDateTime createDate; - - @Schema(description = "수정일", example = "2026-05-27 10:00:00") - private LocalDateTime updateDate; -} diff --git a/src/main/java/com/alist/api/modules/admin/sso/AdminSsoController.java b/src/main/java/com/alist/api/modules/admin/sso/AdminSsoController.java deleted file mode 100644 index 7f428db..0000000 --- a/src/main/java/com/alist/api/modules/admin/sso/AdminSsoController.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.alist.api.modules.admin.sso; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.modules.admin.sso.form.AdminSsoAddForm; -import com.alist.api.modules.admin.sso.form.AdminSsoDeleteForm; -import com.alist.api.modules.admin.sso.form.AdminSsoListForm; -import com.alist.api.modules.admin.sso.form.AdminSsoModifyForm; -import com.alist.api.modules.admin.sso.service.AdminSsoService; -import com.alist.api.modules.admin.sso.vo.*; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@Tag(name = "505. SSO 클라이언트 관리", description = "관리자 SSO 클라이언트 관리 API") -@RestController -@RequestMapping("/admin/sso") -public class AdminSsoController { - private final AdminSsoService adminSsoService; - - public AdminSsoController(AdminSsoService adminSsoService) { - this.adminSsoService = adminSsoService; - } - - @Operation( - summary = "SSO 클라이언트 목록 조회" - , description = "관리자가 SSO 클라이언트 목록을 검색 조건과 페이징으로 조회합니다." - ) - @GetMapping("/list") - public ResponseEntity> adminSsoList( - @ModelAttribute AdminSsoListForm adminSsoListForm - ) { - AdminSsoListVo adminSsoListVo = adminSsoService.selectAdminSsoList(adminSsoListForm.toDto()); - - if (adminSsoListVo.getSsoList() == null || adminSsoListVo.getSsoList().isEmpty()) { - return ApiResponse.entity(adminSsoListVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminSsoListVo, ApiResponseCode.CODE_2001, "SSO 클라이언트"); - } - - @Operation( - summary = "SSO 클라이언트 상세 조회" - , description = "관리자가 SSO 클라이언트 PK 기준으로 상세 정보를 조회합니다." - ) - @GetMapping("/view") - public ResponseEntity> adminSsoView( - @RequestParam Integer ssoClientIdx - ) { - AdminSsoViewVo adminSsoViewVo = adminSsoService.selectAdminSsoView(ssoClientIdx); - - if (adminSsoViewVo == null) { - return ApiResponse.entity((AdminSsoViewVo) null, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminSsoViewVo, ApiResponseCode.CODE_2001, "SSO 클라이언트"); - } - - @Operation( - summary = "SSO 클라이언트 등록" - , description = "관리자가 SSO 클라이언트를 등록합니다. CLIENT_ID가 중복되면 등록하지 않습니다." - ) - @PostMapping("/add") - public ResponseEntity> adminSsoAdd( - @Valid @RequestBody AdminSsoAddForm adminSsoAddForm - ) { - AdminSsoAddVo adminSsoAddVo = adminSsoService.insertAdminSsoAdd(adminSsoAddForm.toDto()); - - if (adminSsoAddVo.getResultCode() == 401) { - return ApiResponse.entity(adminSsoAddVo, ApiResponseCode.CODE_401); - } - - if (!adminSsoAddVo.isProcessed()) { - return ApiResponse.entity(adminSsoAddVo, ApiResponseCode.CODE_2004, "SSO 클라이언트"); - } - - return ApiResponse.entity(adminSsoAddVo, ApiResponseCode.CODE_2002, "SSO 클라이언트"); - } - - @Operation( - summary = "SSO 클라이언트 수정" - , description = "관리자가 SSO 클라이언트 PK 기준으로 클라이언트 ID, 이름, redirect URI 등 기본 정보를 수정합니다." - ) - @PutMapping("/modify") - public ResponseEntity> adminSsoModify( - @Valid @RequestBody AdminSsoModifyForm adminSsoModifyForm - ) { - AdminSsoModifyVo adminSsoModifyVo = adminSsoService.updateAdminSsoModify(adminSsoModifyForm.toDto()); - - if (adminSsoModifyVo.getResultCode() == 401) { - return ApiResponse.entity(adminSsoModifyVo, ApiResponseCode.CODE_401); - } - - if (adminSsoModifyVo.getResultCode() == 2004) { - return ApiResponse.entity(adminSsoModifyVo, ApiResponseCode.CODE_2004, "SSO 클라이언트"); - } - - if (!adminSsoModifyVo.isProcessed()) { - return ApiResponse.entity(adminSsoModifyVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminSsoModifyVo, ApiResponseCode.CODE_2005, "SSO 클라이언트 수정"); - } - - @Operation( - summary = "SSO 클라이언트 삭제" - , description = "관리자가 SSO 클라이언트 PK 기준으로 삭제 처리합니다. 실제 삭제가 아닌 DEL_YN 변경 방식입니다." - ) - @PatchMapping("/delete") - public ResponseEntity> adminSsoDelete( - @Valid @RequestBody AdminSsoDeleteForm adminSsoDeleteForm - ) { - AdminSsoDeleteVo adminSsoDeleteVo = adminSsoService.updateAdminSsoDelete(adminSsoDeleteForm.toDto()); - - if (adminSsoDeleteVo.getResultCode() == 401) { - return ApiResponse.entity(adminSsoDeleteVo, ApiResponseCode.CODE_401); - } - - if (!adminSsoDeleteVo.isProcessed()) { - return ApiResponse.entity(adminSsoDeleteVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(adminSsoDeleteVo, ApiResponseCode.CODE_2005, "SSO 클라이언트 삭제"); - } -} diff --git a/src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoListDto.java b/src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoListDto.java deleted file mode 100644 index 3029523..0000000 --- a/src/main/java/com/alist/api/modules/admin/sso/dto/AdminSsoListDto.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.alist.api.modules.admin.sso.dto; - -import com.alist.api.common.paging.PageRequest; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AdminSsoListDto extends PageRequest { - private String keyword; - private String useYn; -} diff --git a/src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoDeleteForm.java b/src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoDeleteForm.java deleted file mode 100644 index dd393ac..0000000 --- a/src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoDeleteForm.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.alist.api.modules.admin.sso.form; - -import com.alist.api.modules.admin.sso.dto.AdminSsoDeleteDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 SSO 클라이언트 삭제 요청") -@Getter -@Setter -public class AdminSsoDeleteForm { - @NotNull - @Schema(description = "SSO 클라이언트 PK", example = "19", requiredMode = Schema.RequiredMode.REQUIRED) - private Integer ssoClientIdx; - - public AdminSsoDeleteDto toDto() { - AdminSsoDeleteDto adminSsoDeleteDto = new AdminSsoDeleteDto(); - adminSsoDeleteDto.setSsoClientIdx(ssoClientIdx); - return adminSsoDeleteDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoListForm.java b/src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoListForm.java deleted file mode 100644 index 24e9db7..0000000 --- a/src/main/java/com/alist/api/modules/admin/sso/form/AdminSsoListForm.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.alist.api.modules.admin.sso.form; - -import com.alist.api.common.paging.PageRequest; -import com.alist.api.modules.admin.sso.dto.AdminSsoListDto; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 SSO 클라이언트 목록 검색 조건") -@Getter -@Setter -public class AdminSsoListForm extends PageRequest { - @Schema(description = "검색어. 클라이언트 ID, 클라이언트명, redirect URI, 비고를 대상으로 검색합니다.", example = "client") - private String keyword; - - @Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y") - private String useYn; - - public AdminSsoListDto toDto() { - AdminSsoListDto adminSsoListDto = new AdminSsoListDto(); - adminSsoListDto.setPage(getPage()); - adminSsoListDto.setSize(getSize()); - adminSsoListDto.setKeyword(keyword == null ? null : keyword.trim()); - adminSsoListDto.setUseYn(useYn == null ? null : useYn.trim()); - return adminSsoListDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/sso/mapper/AdminSsoMapper.java b/src/main/java/com/alist/api/modules/admin/sso/mapper/AdminSsoMapper.java deleted file mode 100644 index ad4728e..0000000 --- a/src/main/java/com/alist/api/modules/admin/sso/mapper/AdminSsoMapper.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.alist.api.modules.admin.sso.mapper; - -import com.alist.api.modules.admin.sso.dto.AdminSsoAddDto; -import com.alist.api.modules.admin.sso.dto.AdminSsoDeleteDto; -import com.alist.api.modules.admin.sso.dto.AdminSsoListDto; -import com.alist.api.modules.admin.sso.dto.AdminSsoModifyDto; -import com.alist.api.modules.admin.sso.vo.AdminSsoViewVo; -import com.alist.api.modules.admin.sso.vo.AdminSsoVo; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface AdminSsoMapper { - int selectAdminSsoCount(AdminSsoListDto adminSsoListDto); - - List selectAdminSsoList(AdminSsoListDto adminSsoListDto); - - AdminSsoViewVo selectAdminSsoView(Integer ssoClientIdx); - - int selectAdminSsoDuplicateCount(AdminSsoAddDto adminSsoAddDto); - - int insertAdminSsoAdd(AdminSsoAddDto adminSsoAddDto); - - int selectAdminSsoModifyDuplicateCount(AdminSsoModifyDto adminSsoModifyDto); - - int updateAdminSsoModify(AdminSsoModifyDto adminSsoModifyDto); - - int updateAdminSsoDelete(AdminSsoDeleteDto adminSsoDeleteDto); -} diff --git a/src/main/java/com/alist/api/modules/admin/sso/service/AdminSsoService.java b/src/main/java/com/alist/api/modules/admin/sso/service/AdminSsoService.java deleted file mode 100644 index 92b72e6..0000000 --- a/src/main/java/com/alist/api/modules/admin/sso/service/AdminSsoService.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.alist.api.modules.admin.sso.service; - -import com.alist.api.common.utils.SecurityUtil; -import com.alist.api.modules.admin.auth.mapper.AdminAuthMapper; -import com.alist.api.modules.admin.auth.vo.AdminAuthLoginVo; -import com.alist.api.modules.admin.sso.dto.AdminSsoAddDto; -import com.alist.api.modules.admin.sso.dto.AdminSsoDeleteDto; -import com.alist.api.modules.admin.sso.dto.AdminSsoListDto; -import com.alist.api.modules.admin.sso.dto.AdminSsoModifyDto; -import com.alist.api.modules.admin.sso.mapper.AdminSsoMapper; -import com.alist.api.modules.admin.sso.vo.*; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Service -public class AdminSsoService { - private final AdminSsoMapper adminSsoMapper; - private final AdminAuthMapper adminAuthMapper; - - public AdminSsoService(AdminSsoMapper adminSsoMapper, AdminAuthMapper adminAuthMapper) { - this.adminSsoMapper = adminSsoMapper; - this.adminAuthMapper = adminAuthMapper; - } - - @Transactional(readOnly = true) - public AdminSsoListVo selectAdminSsoList(AdminSsoListDto adminSsoListDto) { - int totalCount = adminSsoMapper.selectAdminSsoCount(adminSsoListDto); - List ssoList = adminSsoMapper.selectAdminSsoList(adminSsoListDto); - - AdminSsoListVo adminSsoListVo = new AdminSsoListVo(); - adminSsoListVo.setSsoList(ssoList); - adminSsoListVo.setPaging(adminSsoListDto, totalCount); - - return adminSsoListVo; - } - - @Transactional(readOnly = true) - public AdminSsoViewVo selectAdminSsoView(Integer ssoClientIdx) { - return adminSsoMapper.selectAdminSsoView(ssoClientIdx); - } - - @Transactional - public AdminSsoAddVo insertAdminSsoAdd(AdminSsoAddDto adminSsoAddDto) { - AdminSsoAddVo adminSsoAddVo = new AdminSsoAddVo(); - - Integer loginUserTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (loginUserTokenIdx == null) { - adminSsoAddVo.setResultCode(401); - adminSsoAddVo.setProcessed(false); - return adminSsoAddVo; - } - - AdminAuthLoginVo adminAuthLoginVo = adminAuthMapper.selectAdminTokenByUserTokenIdx(loginUserTokenIdx); - - if (adminAuthLoginVo == null || adminAuthLoginVo.getUserIdx() == null) { - adminSsoAddVo.setResultCode(401); - adminSsoAddVo.setProcessed(false); - return adminSsoAddVo; - } - - adminSsoAddDto.setCreateMember(adminAuthLoginVo.getUserIdx()); - adminSsoAddDto.setUpdateMember(adminAuthLoginVo.getUserIdx()); - - int duplicateCount = adminSsoMapper.selectAdminSsoDuplicateCount(adminSsoAddDto); - - if (duplicateCount > 0) { - adminSsoAddVo.setResultCode(2004); - adminSsoAddVo.setProcessed(false); - return adminSsoAddVo; - } - - int insertCount = adminSsoMapper.insertAdminSsoAdd(adminSsoAddDto); - - adminSsoAddVo.setResultCode(2002); - adminSsoAddVo.setProcessed(insertCount > 0); - adminSsoAddVo.setSsoClientIdx(adminSsoAddDto.getSsoClientIdx()); - - return adminSsoAddVo; - } - - @Transactional - public AdminSsoModifyVo updateAdminSsoModify(AdminSsoModifyDto adminSsoModifyDto) { - AdminSsoModifyVo adminSsoModifyVo = new AdminSsoModifyVo(); - - Integer loginUserTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (loginUserTokenIdx == null) { - adminSsoModifyVo.setResultCode(401); - adminSsoModifyVo.setProcessed(false); - return adminSsoModifyVo; - } - - AdminAuthLoginVo adminAuthLoginVo = adminAuthMapper.selectAdminTokenByUserTokenIdx(loginUserTokenIdx); - - if (adminAuthLoginVo == null || adminAuthLoginVo.getUserIdx() == null) { - adminSsoModifyVo.setResultCode(401); - adminSsoModifyVo.setProcessed(false); - return adminSsoModifyVo; - } - - adminSsoModifyDto.setUpdateMember(adminAuthLoginVo.getUserIdx()); - - int duplicateCount = adminSsoMapper.selectAdminSsoModifyDuplicateCount(adminSsoModifyDto); - - if (duplicateCount > 0) { - adminSsoModifyVo.setResultCode(2004); - adminSsoModifyVo.setProcessed(false); - return adminSsoModifyVo; - } - - int updateCount = adminSsoMapper.updateAdminSsoModify(adminSsoModifyDto); - - adminSsoModifyVo.setResultCode(2005); - adminSsoModifyVo.setProcessed(updateCount > 0); - - return adminSsoModifyVo; - } - - @Transactional - public AdminSsoDeleteVo updateAdminSsoDelete(AdminSsoDeleteDto adminSsoDeleteDto) { - AdminSsoDeleteVo adminSsoDeleteVo = new AdminSsoDeleteVo(); - - Integer loginUserTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (loginUserTokenIdx == null) { - adminSsoDeleteVo.setResultCode(401); - adminSsoDeleteVo.setProcessed(false); - return adminSsoDeleteVo; - } - - AdminAuthLoginVo adminAuthLoginVo = adminAuthMapper.selectAdminTokenByUserTokenIdx(loginUserTokenIdx); - - if (adminAuthLoginVo == null || adminAuthLoginVo.getUserIdx() == null) { - adminSsoDeleteVo.setResultCode(401); - adminSsoDeleteVo.setProcessed(false); - return adminSsoDeleteVo; - } - - adminSsoDeleteDto.setUpdateMember(adminAuthLoginVo.getUserIdx()); - - int updateCount = adminSsoMapper.updateAdminSsoDelete(adminSsoDeleteDto); - - adminSsoDeleteVo.setResultCode(2005); - adminSsoDeleteVo.setProcessed(updateCount > 0); - - return adminSsoDeleteVo; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoDeleteVo.java b/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoDeleteVo.java deleted file mode 100644 index 282fb45..0000000 --- a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoDeleteVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.admin.sso.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 SSO 클라이언트 삭제 응답") -@Getter -@Setter -public class AdminSsoDeleteVo { - @Schema(description = "SSO 클라이언트 삭제 여부", example = "true") - private boolean processed; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoListVo.java b/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoListVo.java deleted file mode 100644 index 1ff5cf8..0000000 --- a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoListVo.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alist.api.modules.admin.sso.vo; - -import com.alist.api.common.paging.PageResponse; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "관리자 SSO 클라이언트 목록 조회 응답") -@Getter -@Setter -public class AdminSsoListVo extends PageResponse { - @Schema(description = "SSO 클라이언트 목록") - private List ssoList; -} diff --git a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoModifyVo.java b/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoModifyVo.java deleted file mode 100644 index 5a2612c..0000000 --- a/src/main/java/com/alist/api/modules/admin/sso/vo/AdminSsoModifyVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.admin.sso.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "관리자 SSO 클라이언트 수정 응답") -@Getter -@Setter -public class AdminSsoModifyVo { - @Schema(description = "SSO 클라이언트 수정 여부", example = "true") - private boolean processed; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/admin/user/AdminUserController.java b/src/main/java/com/alist/api/modules/admin/user/AdminUserController.java deleted file mode 100644 index 867f161..0000000 --- a/src/main/java/com/alist/api/modules/admin/user/AdminUserController.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.alist.api.modules.admin.user; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.modules.admin.user.form.AdminUserAddForm; -import com.alist.api.modules.admin.user.service.AdminUserService; -import com.alist.api.modules.admin.user.vo.AdminUserAddVo; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@Tag(name = "503. ADMIN 사용자 관리", description = "ADMIN 사용자 회원가입 및 관리 API") -@RestController -@Slf4j -@RequestMapping("/admin/user") -public class AdminUserController { - private final AdminUserService adminUserService; - - public AdminUserController(AdminUserService adminUserService) { - this.adminUserService = adminUserService; - } - - @Operation( - summary = "관리자 회원가입" - , description = "새로운 사용자를 등록합니다. 아이디 중복 시 오류를 반환합니다." - ) - @PostMapping("/add") - public ResponseEntity> adminUserAdd(@Valid @RequestBody AdminUserAddForm adminUserAddForm) { - - AdminUserAddVo adminUserAddVo = adminUserService.insertAdminUserAdd(adminUserAddForm.toDto()); - - if (adminUserAddVo.getResultCode() == 2004) { - return ApiResponse.entity(adminUserAddVo, ApiResponseCode.CODE_2004, "아이디"); - } - - return ApiResponse.entity(adminUserAddVo, ApiResponseCode.CODE_2002, "아이디"); - } -} diff --git a/src/main/java/com/alist/api/modules/admin/user/dto/AdminUserAddDto.java b/src/main/java/com/alist/api/modules/admin/user/dto/AdminUserAddDto.java deleted file mode 100644 index 04db98a..0000000 --- a/src/main/java/com/alist/api/modules/admin/user/dto/AdminUserAddDto.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.alist.api.modules.admin.user.dto; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Setter -@Getter -public class AdminUserAddDto { - private Integer userIdx; - private String id; - private String userRole; - private String userType; - - @JsonIgnore - @Schema(hidden = true) - private String newPassword; - - @JsonIgnore - @Schema(hidden = true) - private String password; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/admin/user/form/AdminUserAddForm.java b/src/main/java/com/alist/api/modules/admin/user/form/AdminUserAddForm.java deleted file mode 100644 index 6425e0d..0000000 --- a/src/main/java/com/alist/api/modules/admin/user/form/AdminUserAddForm.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.alist.api.modules.admin.user.form; - -import com.alist.api.modules.admin.user.dto.AdminUserAddDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import lombok.Getter; -import lombok.Setter; - -@Setter -@Getter -@Schema(description = "회원 가입 요청 폼") -public class AdminUserAddForm { - @Schema( - description = "사용자 아이디 (공백 불가)" - , example = "test" - ) - @NotBlank(message = "아이디를 입력해주세요.") - private String id; - - @Schema( - description = "비밀번호 (8~64자, 영문 + 숫자 조합, 공백 불가)" - , example = "pass1234" - ) - @NotBlank(message = "비밀번호를 입력해주세요.") - @Size(min = 8, max = 64, message = "비밀번호는 8~64자여야 합니다.") - @Pattern( - regexp = "^(?=.*[A-Za-z])(?=.*\\d)\\S+$" - , message = "비밀번호는 영문과 숫자를 포함하고 공백이 없어야 합니다." - ) - private String password; - - @NotBlank(message = "사용자 권한(관리자{A:관리자}") - @Pattern( - regexp = "^A$" - , message = "사용자 권한(관리자{A:관리자})" - ) - @Schema( - description = "사용자 권한(관리자{A:관리자})" - , example = "A" - ) - private String userRole; - - @NotBlank(message = "사용자 구분(A:관리자)") - @Pattern(regexp = "^A$", message = "관리자 사용자 구분은 A만 가능합니다.") - @Schema( - description = "사용자 타입(A:관리자)" - , example = "A" - ) - private String userType; - - public AdminUserAddDto toDto() { - AdminUserAddDto adminUserAddDto = new AdminUserAddDto(); - adminUserAddDto.setId(id.trim()); - adminUserAddDto.setPassword(password); - adminUserAddDto.setUserRole(userRole); - adminUserAddDto.setUserType(userType); - return adminUserAddDto; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/user/service/AdminUserService.java b/src/main/java/com/alist/api/modules/admin/user/service/AdminUserService.java deleted file mode 100644 index 0d2212f..0000000 --- a/src/main/java/com/alist/api/modules/admin/user/service/AdminUserService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.alist.api.modules.admin.user.service; - -import com.alist.api.modules.admin.user.dto.AdminUserAddDto; -import com.alist.api.modules.admin.user.vo.AdminUserAddVo; -import com.alist.api.modules.front.user.dto.UserDto; -import com.alist.api.modules.front.user.service.UserService; -import com.alist.api.modules.front.user.vo.UserVo; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Slf4j -@Service -public class AdminUserService { - private final UserService userService; - - public AdminUserService(UserService userService) { - this.userService = userService; - } - - @Transactional - public AdminUserAddVo insertAdminUserAdd(AdminUserAddDto adminUserAddDto) { - - UserDto userDto = new UserDto(); - - userDto.setId(adminUserAddDto.getId()); - userDto.setPassword(adminUserAddDto.getPassword()); - userDto.setUserRole(adminUserAddDto.getUserRole()); - userDto.setUserType(adminUserAddDto.getUserType()); - - UserVo userVo = userService.insertUserAdd(userDto); - - AdminUserAddVo adminUserAddVo = new AdminUserAddVo(); - adminUserAddVo.setResultCode(userVo.getResultCode()); - adminUserAddVo.setUserIdx(userVo.getUserIdx()); - adminUserAddVo.setId(userVo.getId()); - adminUserAddVo.setUserRole(userVo.getUserRole()); - adminUserAddVo.setUserType(userVo.getUserType()); - - return adminUserAddVo; - } -} diff --git a/src/main/java/com/alist/api/modules/admin/user/vo/AdminUserAddVo.java b/src/main/java/com/alist/api/modules/admin/user/vo/AdminUserAddVo.java deleted file mode 100644 index d939cec..0000000 --- a/src/main/java/com/alist/api/modules/admin/user/vo/AdminUserAddVo.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.alist.api.modules.admin.user.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "Admin 사용자 회원가입 응답") -@Setter -@Getter -public class AdminUserAddVo { - @Schema(description = "회원 PK", example = "14") - private Integer userIdx; - - @Schema(description = "관리자 아이디", example = "admin") - private String id; - - @Schema(description = "관리자 권한 코드", example = "A") - private String userRole; - - @Schema(description = "회원 유형 코드. A: 관리자", example = "A") - private String userType; - - @JsonIgnore - @Schema(hidden = true) - private String newPassword; - - @JsonIgnore - @Schema(hidden = true) - private String password; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/cors/CorsController.java b/src/main/java/com/alist/api/modules/cors/CorsController.java deleted file mode 100644 index 74407b1..0000000 --- a/src/main/java/com/alist/api/modules/cors/CorsController.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.alist.api.modules.cors; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.config.cache.CorsCache; -import com.alist.api.modules.cors.form.CorsAddForm; -import com.alist.api.modules.cors.form.CorsDeleteForm; -import com.alist.api.modules.cors.form.CorsListForm; -import com.alist.api.modules.cors.service.CorsService; -import com.alist.api.modules.cors.vo.CorsAddVo; -import com.alist.api.modules.cors.vo.CorsDeleteVo; -import com.alist.api.modules.cors.vo.CorsListVo; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@Tag(name = "901. CRS 갱신", description = "테스트 CORS 설정 관리 API") -@RestController -@RequestMapping("/cors") -public class CorsController { - private final CorsCache corsCache; - private final CorsService corsService; - - public CorsController(CorsCache corsCache, CorsService corsService) { - this.corsCache = corsCache; - this.corsService = corsService; - } - - @Operation( - summary = "CORS 허용 도메인 목록 조회" - , description = "DB에 등록된 CORS 허용 도메인 목록을 검색 조건과 페이징으로 조회합니다." - ) - @GetMapping("/list") - public ResponseEntity> corsList( - @ModelAttribute CorsListForm corsListForm - ) { - CorsListVo corsListVo = corsService.selectCorsList(corsListForm.toDto()); - - if (corsListVo.getCorsList() == null || corsListVo.getCorsList().isEmpty()) { - return ApiResponse.entity(corsListVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(corsListVo, ApiResponseCode.CODE_2001, "CORS 허용 도메인"); - } - - @Operation( - summary = "CORS 허용 도메인 추가" - , description = "CORS 허용 도메인을 추가합니다. 추가 후 CORS 캐시를 갱신합니다." - ) - @PostMapping("/add") - public ResponseEntity> corsAdd( - @Valid @RequestBody CorsAddForm corsAddForm - ) { - CorsAddVo corsAddVo = corsService.insertCorsAdd(corsAddForm.toDto()); - - if (!corsAddVo.isProcessed()) { - return ApiResponse.entity(corsAddVo, ApiResponseCode.CODE_2004, "CORS 허용 도메인"); - } - - corsCache.refresh(); - return ApiResponse.entity(corsAddVo, ApiResponseCode.CODE_2002, "CORS 허용 도메인"); - } - - @Operation( - summary = "CORS 허용 도메인 삭제" - , description = "CORS 허용 도메인 PK 기준으로 삭제 처리합니다. 실제 삭제가 아닌 del_yn 변경 방식입니다." - ) - @PatchMapping("/delete") - public ResponseEntity> corsDelete( - @Valid @RequestBody CorsDeleteForm corsDeleteForm - ) { - CorsDeleteVo corsDeleteVo = corsService.updateCorsDelete(corsDeleteForm.toDto()); - - if (!corsDeleteVo.isProcessed()) { - return ApiResponse.entity(corsDeleteVo, ApiResponseCode.CODE_2003); - } - - corsCache.refresh(); - return ApiResponse.entity(corsDeleteVo, ApiResponseCode.CODE_2005, "CORS 허용 도메인 삭제"); - } - - @Operation( - summary = "CORS 화이트리스트 갱신" - , description = "DB에서 CORS 허용 도메인 목록을 다시 로딩합니다. DB에 도메인 추가 후 호출하세요." - ) - @PostMapping("/reload") - public ResponseEntity>> corsReload() { - corsCache.refresh(); - List cacheStatus = corsCache.getCacheStatus(); - return ApiResponse.entity(cacheStatus, ApiResponseCode.CODE_200); - } -} diff --git a/src/main/java/com/alist/api/modules/cors/dto/CorsAddDto.java b/src/main/java/com/alist/api/modules/cors/dto/CorsAddDto.java deleted file mode 100644 index 05165b9..0000000 --- a/src/main/java/com/alist/api/modules/cors/dto/CorsAddDto.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.alist.api.modules.cors.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class CorsAddDto { - private String allowedOrigin; -} diff --git a/src/main/java/com/alist/api/modules/cors/dto/CorsDeleteDto.java b/src/main/java/com/alist/api/modules/cors/dto/CorsDeleteDto.java deleted file mode 100644 index 340dc75..0000000 --- a/src/main/java/com/alist/api/modules/cors/dto/CorsDeleteDto.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.alist.api.modules.cors.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class CorsDeleteDto { - private Integer corsIdx; -} diff --git a/src/main/java/com/alist/api/modules/cors/dto/CorsListDto.java b/src/main/java/com/alist/api/modules/cors/dto/CorsListDto.java deleted file mode 100644 index a40497d..0000000 --- a/src/main/java/com/alist/api/modules/cors/dto/CorsListDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.alist.api.modules.cors.dto; - -import com.alist.api.common.paging.PageRequest; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class CorsListDto extends PageRequest { - private String keyword; -} diff --git a/src/main/java/com/alist/api/modules/cors/form/CorsAddForm.java b/src/main/java/com/alist/api/modules/cors/form/CorsAddForm.java deleted file mode 100644 index 4853b5f..0000000 --- a/src/main/java/com/alist/api/modules/cors/form/CorsAddForm.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.cors.form; - -import com.alist.api.modules.cors.dto.CorsAddDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "CORS 허용 도메인 추가 요청 폼") -@Getter -@Setter -public class CorsAddForm { - - @Schema(description = "CORS 허용 도메인", example = "https://admin.alist.co.kr", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "CORS 허용 도메인은 필수입니다.") - @Pattern( - regexp = "^(\\*|https?://[A-Za-z0-9.-]+(:[0-9]{1,5})?)$" - , message = "CORS 허용 도메인은 http(s)://domain 또는 * 형식이어야 합니다." - ) - private String allowedOrigin; - - public CorsAddDto toDto() { - CorsAddDto corsAddDto = new CorsAddDto(); - corsAddDto.setAllowedOrigin(allowedOrigin == null ? null : allowedOrigin.trim()); - return corsAddDto; - } -} diff --git a/src/main/java/com/alist/api/modules/cors/form/CorsDeleteForm.java b/src/main/java/com/alist/api/modules/cors/form/CorsDeleteForm.java deleted file mode 100644 index 3d54fd1..0000000 --- a/src/main/java/com/alist/api/modules/cors/form/CorsDeleteForm.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.alist.api.modules.cors.form; - -import com.alist.api.modules.cors.dto.CorsDeleteDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "CORS 허용 도메인 삭제 요청 폼") -@Getter -@Setter -public class CorsDeleteForm { - - @Schema(description = "CORS 허용 도메인 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "CORS 허용 도메인 PK는 필수입니다.") - private Integer corsIdx; - - public CorsDeleteDto toDto() { - CorsDeleteDto corsDeleteDto = new CorsDeleteDto(); - corsDeleteDto.setCorsIdx(corsIdx); - return corsDeleteDto; - } -} diff --git a/src/main/java/com/alist/api/modules/cors/form/CorsListForm.java b/src/main/java/com/alist/api/modules/cors/form/CorsListForm.java deleted file mode 100644 index d66784c..0000000 --- a/src/main/java/com/alist/api/modules/cors/form/CorsListForm.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.alist.api.modules.cors.form; - -import com.alist.api.common.paging.PageRequest; -import com.alist.api.modules.cors.dto.CorsListDto; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "CORS 허용 도메인 목록 검색 조건") -@Getter -@Setter -public class CorsListForm extends PageRequest { - - @Schema(description = "검색어. CORS 허용 도메인을 대상으로 검색합니다.", example = "alist.co.kr") - private String keyword; - - public CorsListDto toDto() { - CorsListDto corsListDto = new CorsListDto(); - corsListDto.setPage(getPage()); - corsListDto.setSize(getSize()); - corsListDto.setKeyword(keyword); - return corsListDto; - } -} diff --git a/src/main/java/com/alist/api/modules/cors/mapper/CorsMapper.java b/src/main/java/com/alist/api/modules/cors/mapper/CorsMapper.java deleted file mode 100644 index afbb061..0000000 --- a/src/main/java/com/alist/api/modules/cors/mapper/CorsMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.alist.api.modules.cors.mapper; - -import com.alist.api.modules.cors.dto.CorsAddDto; -import com.alist.api.modules.cors.dto.CorsDeleteDto; -import com.alist.api.modules.cors.dto.CorsListDto; -import com.alist.api.modules.cors.vo.CorsCacheVo; -import com.alist.api.modules.cors.vo.CorsVo; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface CorsMapper { - List selectCorsCacheList(); - - List selectCorsList(CorsListDto corsListDto); - - int selectCorsListCount(CorsListDto corsListDto); - - int selectCorsCount(String allowedOrigin); - - int insertCorsAdd(CorsAddDto corsAddDto); - - int updateCorsDelete(CorsDeleteDto corsDeleteDto); -} diff --git a/src/main/java/com/alist/api/modules/cors/service/CorsService.java b/src/main/java/com/alist/api/modules/cors/service/CorsService.java deleted file mode 100644 index 7fc86e1..0000000 --- a/src/main/java/com/alist/api/modules/cors/service/CorsService.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.alist.api.modules.cors.service; - -import com.alist.api.modules.cors.dto.CorsAddDto; -import com.alist.api.modules.cors.dto.CorsDeleteDto; -import com.alist.api.modules.cors.dto.CorsListDto; -import com.alist.api.modules.cors.mapper.CorsMapper; -import com.alist.api.modules.cors.vo.CorsAddVo; -import com.alist.api.modules.cors.vo.CorsDeleteVo; -import com.alist.api.modules.cors.vo.CorsListVo; -import com.alist.api.modules.cors.vo.CorsVo; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Service -public class CorsService { - private final CorsMapper corsMapper; - - public CorsService(CorsMapper corsMapper) { - this.corsMapper = corsMapper; - } - - @Transactional(readOnly = true) - public CorsListVo selectCorsList(CorsListDto corsListDto) { - int totalCount = corsMapper.selectCorsListCount(corsListDto); - List corsList = corsMapper.selectCorsList(corsListDto); - - CorsListVo corsListVo = new CorsListVo(); - corsListVo.setCorsList(corsList); - corsListVo.setPaging(corsListDto, totalCount); - - return corsListVo; - } - - @Transactional - public CorsAddVo insertCorsAdd(CorsAddDto corsAddDto) { - int duplicateCount = corsMapper.selectCorsCount(corsAddDto.getAllowedOrigin()); - - CorsAddVo corsAddVo = new CorsAddVo(); - - if (duplicateCount > 0) { - corsAddVo.setProcessed(false); - return corsAddVo; - } - - int insertCount = corsMapper.insertCorsAdd(corsAddDto); - corsAddVo.setProcessed(insertCount > 0); - - return corsAddVo; - } - - @Transactional - public CorsDeleteVo updateCorsDelete(CorsDeleteDto corsDeleteDto) { - int updateCount = corsMapper.updateCorsDelete(corsDeleteDto); - - CorsDeleteVo corsDeleteVo = new CorsDeleteVo(); - corsDeleteVo.setProcessed(updateCount > 0); - - return corsDeleteVo; - } -} diff --git a/src/main/java/com/alist/api/modules/cors/vo/CorsAddVo.java b/src/main/java/com/alist/api/modules/cors/vo/CorsAddVo.java deleted file mode 100644 index 944e99e..0000000 --- a/src/main/java/com/alist/api/modules/cors/vo/CorsAddVo.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.alist.api.modules.cors.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "CORS 허용 도메인 추가 응답") -@Getter -@Setter -public class CorsAddVo { - @Schema(description = "CORS 허용 도메인 추가 여부", example = "true") - private boolean processed; -} diff --git a/src/main/java/com/alist/api/modules/cors/vo/CorsCacheVo.java b/src/main/java/com/alist/api/modules/cors/vo/CorsCacheVo.java deleted file mode 100644 index e4e48d2..0000000 --- a/src/main/java/com/alist/api/modules/cors/vo/CorsCacheVo.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.alist.api.modules.cors.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "CORS 허용 도메인 캐시 항목") -@Getter -@Setter -public class CorsCacheVo { - @Schema(description = "CORS 허용 도메인", example = "https://admin.alist.co.kr") - private String allowedOrigin; -} diff --git a/src/main/java/com/alist/api/modules/cors/vo/CorsDeleteVo.java b/src/main/java/com/alist/api/modules/cors/vo/CorsDeleteVo.java deleted file mode 100644 index 245cfcb..0000000 --- a/src/main/java/com/alist/api/modules/cors/vo/CorsDeleteVo.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.alist.api.modules.cors.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "CORS 허용 도메인 삭제 응답") -@Getter -@Setter -public class CorsDeleteVo { - @Schema(description = "CORS 허용 도메인 삭제 여부", example = "true") - private boolean processed; -} diff --git a/src/main/java/com/alist/api/modules/cors/vo/CorsListVo.java b/src/main/java/com/alist/api/modules/cors/vo/CorsListVo.java deleted file mode 100644 index 2f12402..0000000 --- a/src/main/java/com/alist/api/modules/cors/vo/CorsListVo.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alist.api.modules.cors.vo; - -import com.alist.api.common.paging.PageResponse; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "CORS 허용 도메인 목록 조회 응답") -@Getter -@Setter -public class CorsListVo extends PageResponse { - @Schema(description = "CORS 허용 도메인 목록") - private List corsList; -} diff --git a/src/main/java/com/alist/api/modules/cors/vo/CorsVo.java b/src/main/java/com/alist/api/modules/cors/vo/CorsVo.java deleted file mode 100644 index 73177a3..0000000 --- a/src/main/java/com/alist/api/modules/cors/vo/CorsVo.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alist.api.modules.cors.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "CORS 허용 도메인 목록 항목") -@Getter -@Setter -public class CorsVo { - @Schema(description = "CORS 허용 도메인 PK", example = "1") - private Integer corsIdx; - - @Schema(description = "CORS 허용 도메인", example = "https://admin.alist.co.kr") - private String allowedOrigin; -} diff --git a/src/main/java/com/alist/api/modules/file/FileController.java b/src/main/java/com/alist/api/modules/file/FileController.java deleted file mode 100644 index 64e3e05..0000000 --- a/src/main/java/com/alist/api/modules/file/FileController.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.alist.api.modules.file; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.modules.file.service.FileService; -import com.alist.api.modules.file.vo.FileUploadVo; -import com.alist.api.modules.file.vo.SunEditorUploadVo; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.core.io.Resource; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.multipart.MultipartHttpServletRequest; - -import java.util.ArrayList; -import java.util.List; - -@Tag( - name = "902. 파일 업로드" - , description = "DB 기록 없이 파일을 저장하고 uploadPath를 반환하는 단순 업로드 API입니다. SunEditor 이미지는 file-domain URL을 반환합니다." -) -@RestController -@RequestMapping("/file") -public class FileController { - private final FileService fileService; - - public FileController(FileService fileService) { - this.fileService = fileService; - } - - @Operation( - summary = "단순 파일 업로드" - , description = """ - DB에 기록하지 않고 파일만 저장합니다. - 반환된 uploadPath는 이후 각 업무 테이블에 저장해서 view/download 경로로 사용할 수 있습니다. - folder 값은 저장 폴더 키이며, 설정된 타입이면 해당 정책을 사용하고 설정되지 않은 값이면 전역 정책만 적용합니다. - """ - ) - @PostMapping("/upload") - public ResponseEntity> fileUpload( - @RequestParam String folder - , @RequestPart MultipartFile file - ) { - FileUploadVo fileUploadVo = fileService.saveFileUpload(folder, file); - return ApiResponse.entity(fileUploadVo, ApiResponseCode.CODE_200); - } - - @Operation( - summary = "SunEditor 이미지 업로드" - , description = """ - SunEditor 이미지 업로드 전용 API입니다. - 업로드 파일을 저장한 뒤 SunEditor가 요구하는 result 배열 형태로 응답합니다. - 응답 URL은 file.upload.view.file-domain 값과 uploadPath를 조합한 공개 file-domain URL입니다. - """ - ) - @PostMapping("/suneditor/upload") - public ResponseEntity fileSunEditorUpload( - @RequestParam(value = "folder", defaultValue = "editor") String folder - , MultipartHttpServletRequest request - ) { - List sunEditorUploadItemList = new ArrayList<>(); - - for (MultipartFile file : request.getFileMap().values()) { - FileUploadVo uploaded = fileService.saveFileUpload(folder, file); - - String imageUrl = fileService.buildImageUrl(uploaded.getUploadPath()); - - sunEditorUploadItemList.add(new SunEditorUploadVo.Item( - imageUrl, - uploaded.getOriginalFileName(), - uploaded.getFileSize() - )); - } - - return ResponseEntity.ok(new SunEditorUploadVo(sunEditorUploadItemList)); - } - - @Operation( - summary = "파일 보기" - , description = """ - uploadPath 기준으로 파일을 inline 응답합니다. - 예: /uploads/notice/2026/05/08/sample.png - 단순 업로드 결과의 uploadPath를 그대로 path 파라미터에 전달합니다. - """ - ) - @GetMapping("/view") - public ResponseEntity fileView( - @RequestParam String path - ) { - return fileService.resource(path, true); - } - - @Operation( - summary = "파일 다운로드" - , description = """ - uploadPath 기준으로 파일을 attachment 응답합니다. - 예: /uploads/notice/2026/05/08/sample.pdf - 단순 업로드 결과의 uploadPath를 그대로 path 파라미터에 전달합니다. - """ - ) - @GetMapping("/download") - public ResponseEntity fileDownload( - @RequestParam String path - ) { - return fileService.resource(path, false); - } -} diff --git a/src/main/java/com/alist/api/modules/file/properties/FileUploadProperties.java b/src/main/java/com/alist/api/modules/file/properties/FileUploadProperties.java deleted file mode 100644 index 4c11917..0000000 --- a/src/main/java/com/alist/api/modules/file/properties/FileUploadProperties.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.alist.api.modules.file.properties; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.util.unit.DataSize; - -import java.util.List; -import java.util.Map; - -@Getter -@Setter -@ConfigurationProperties(prefix = "file.upload") -public class FileUploadProperties { - private String rootPath; - private DataSize maxSize; - private List allowedExtensions; - private Map types; - private View view = new View(); - - @Getter - @Setter - public static class UploadType { - private String folder; - private DataSize maxSize; - private boolean imageOnly; - private Resize resize = new Resize(); - } - - @Getter - @Setter - public static class Resize { - private boolean enabled; - private Integer width; - private Integer height; - private Integer maxWidth; - private Float quality = 0.9f; - } - - @Getter - @Setter - public static class View { - private String fileDomain; - } -} diff --git a/src/main/java/com/alist/api/modules/file/service/FileService.java b/src/main/java/com/alist/api/modules/file/service/FileService.java deleted file mode 100644 index 89d8575..0000000 --- a/src/main/java/com/alist/api/modules/file/service/FileService.java +++ /dev/null @@ -1,244 +0,0 @@ -package com.alist.api.modules.file.service; - -import com.alist.api.modules.file.properties.FileUploadProperties; -import com.alist.api.modules.file.vo.FileUploadVo; -import org.springframework.core.io.InputStreamResource; -import org.springframework.core.io.Resource; -import org.springframework.http.ContentDisposition; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.LocalDate; -import java.util.Locale; -import java.util.UUID; - -@Service -public class FileService { - private static final String PUBLIC_PREFIX = "/uploads"; - - private final FileUploadProperties properties; - private final FileUploadImageService fileUploadImageService; - - public FileService(FileUploadProperties properties, FileUploadImageService fileUploadImageService) { - this.properties = properties; - this.fileUploadImageService = fileUploadImageService; - } - - public FileUploadVo saveFileUpload(String folder, MultipartFile file) { - if (folder == null || folder.isBlank()) throw new IllegalArgumentException("invalid folder"); - if (file == null || file.isEmpty()) throw new IllegalArgumentException("empty file"); - - FileUploadProperties.UploadType uploadType = resolveUploadType(folder); - - String originalFileName = cleanFileName(file.getOriginalFilename()); - String ext = extractExt(originalFileName); - validateExtension(ext); - validateSize(file.getSize(), uploadType); - - boolean image = isImageExtension(ext); - if (uploadType.isImageOnly() && !image) throw new IllegalArgumentException("image only"); - - LocalDate now = LocalDate.now(); - String storedFileName = UUID.randomUUID().toString().replace("-", "") + "." + ext; - - Path savePath = Paths.get( - properties.getRootPath(), - uploadType.getFolder(), - String.valueOf(now.getYear()), - "%02d".formatted(now.getMonthValue()), - "%02d".formatted(now.getDayOfMonth()), - storedFileName - ).normalize().toAbsolutePath(); - - ensureUnderRoot(savePath); - - try { - Files.createDirectories(savePath.getParent()); - - FileUploadImageService.ImageSize imageSize = null; - if (image && uploadType.getResize() != null && uploadType.getResize().isEnabled()) { - imageSize = fileUploadImageService.resizeAndSave(file, savePath, uploadType.getResize(), ext); - } else { - file.transferTo(savePath); - if (image) imageSize = fileUploadImageService.readSize(savePath); - } - - FileUploadVo vo = new FileUploadVo(); - vo.setUploadPath(toUploadPath(savePath)); - vo.setOriginalFileName(originalFileName); - vo.setStoredFileName(storedFileName); - vo.setFileExtension(ext); - vo.setContentType(file.getContentType()); - vo.setFileSize(Files.size(savePath)); - vo.setWidth(imageSize == null ? null : imageSize.width()); - vo.setHeight(imageSize == null ? null : imageSize.height()); - - return vo; - } catch (IOException e) { - throw new IllegalStateException("file save failed", e); - } - } - - public ResponseEntity resource(String uploadPath, boolean inline) { - try { - Path path = resolveUploadPath(uploadPath); - - if (!Files.exists(path) || !Files.isRegularFile(path)) { - return ResponseEntity.notFound().build(); - } - - String contentType = Files.probeContentType(path); - if (contentType == null || contentType.isBlank()) { - contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE; - } - - ContentDisposition disposition = inline - ? ContentDisposition.inline().filename(path.getFileName().toString(), StandardCharsets.UTF_8).build() - : ContentDisposition.attachment().filename(path.getFileName().toString(), StandardCharsets.UTF_8).build(); - - return ResponseEntity.ok() - .contentType(MediaType.parseMediaType(contentType)) - .contentLength(Files.size(path)) - .header(HttpHeaders.CONTENT_DISPOSITION, disposition.toString()) - .body(new InputStreamResource(Files.newInputStream(path))); - } catch (IOException e) { - throw new IllegalStateException("file read failed", e); - } - } - - private Path resolveUploadPath(String uploadPath) { - if (uploadPath == null || uploadPath.isBlank()) throw new IllegalArgumentException("empty path"); - - String path = uploadPath.trim().replace("\\", "/"); - if (!path.startsWith(PUBLIC_PREFIX + "/")) throw new IllegalArgumentException("invalid path"); - - String relative = path.substring((PUBLIC_PREFIX + "/").length()); - if (relative.contains("..") || relative.startsWith("/") || relative.contains(":")) { - throw new IllegalArgumentException("invalid path"); - } - - Path root = rootPath(); - Path resolved = root.resolve(relative).normalize().toAbsolutePath(); - - if (!resolved.startsWith(root)) throw new IllegalArgumentException("invalid path"); - - return resolved; - } - - private String toUploadPath(Path savePath) { - Path root = rootPath(); - Path absolutePath = savePath.normalize().toAbsolutePath(); - - if (!absolutePath.startsWith(root)) throw new IllegalArgumentException("invalid path"); - - return PUBLIC_PREFIX + "/" + root.relativize(absolutePath).toString().replace("\\", "/"); - } - - private void ensureUnderRoot(Path path) { - if (!path.startsWith(rootPath())) throw new IllegalArgumentException("invalid save path"); - } - - private Path rootPath() { - return Paths.get(properties.getRootPath()).normalize().toAbsolutePath(); - } - - private void validateSize(long fileSize, FileUploadProperties.UploadType uploadType) { - long limit = uploadType.getMaxSize() == null - ? properties.getMaxSize().toBytes() - : uploadType.getMaxSize().toBytes(); - - if (fileSize > limit) throw new IllegalArgumentException("file size exceeded"); - } - - private void validateExtension(String ext) { - if (ext.isBlank() || properties.getAllowedExtensions().stream().noneMatch(ext::equalsIgnoreCase)) { - throw new IllegalArgumentException("invalid extension"); - } - } - - private String cleanFileName(String fileName) { - if (fileName == null || fileName.isBlank()) throw new IllegalArgumentException("empty file name"); - return Paths.get(fileName).getFileName().toString(); - } - - private String extractExt(String fileName) { - int idx = fileName.lastIndexOf('.'); - if (idx < 0 || idx == fileName.length() - 1) return ""; - return fileName.substring(idx + 1).trim().toLowerCase(Locale.ROOT); - } - - private boolean isImageExtension(String ext) { - return "jpg".equals(ext) || "jpeg".equals(ext) || "png".equals(ext) || "gif".equals(ext); - } - - private FileUploadProperties.UploadType resolveUploadType(String folder) { - String key = normalizeFolder(folder); - - FileUploadProperties.UploadType configured = properties.getTypes() == null - ? null - : properties.getTypes().get(key); - - if (configured != null) { - configured.setFolder(normalizeFolder(configured.getFolder())); - return configured; - } - - FileUploadProperties.UploadType fallback = new FileUploadProperties.UploadType(); - fallback.setFolder(key); - fallback.setMaxSize(null); - fallback.setImageOnly(false); - fallback.setResize(new FileUploadProperties.Resize()); - fallback.getResize().setEnabled(false); - - return fallback; - } - - private String normalizeFolder(String folder) { - if (folder == null || folder.isBlank()) { - throw new IllegalArgumentException("invalid folder"); - } - - String key = folder.trim().replace("\\", "/"); - - while (key.startsWith("/")) key = key.substring(1); - while (key.endsWith("/")) key = key.substring(0, key.length() - 1); - - if (key.isBlank() - || key.contains("..") - || key.contains(":") - || key.startsWith("http://") - || key.startsWith("https://")) { - throw new IllegalArgumentException("invalid folder"); - } - - if (!key.matches("^[a-zA-Z0-9/_-]+$")) { - throw new IllegalArgumentException("invalid folder"); - } - - return key; - } - - public String buildImageUrl(String uploadPath) { - return joinUrl(properties.getView().getFileDomain(), uploadPath); - } - - private String joinUrl(String domain, String path) { - if (domain == null || domain.isBlank()) { - throw new IllegalStateException("file.upload.view.file-domain is empty"); - } - - String base = domain.endsWith("/") ? domain.substring(0, domain.length() - 1) : domain; - String p = path.startsWith("/") ? path : "/" + path; - - return base + p; - } - -} diff --git a/src/main/java/com/alist/api/modules/file/service/FileUploadImageService.java b/src/main/java/com/alist/api/modules/file/service/FileUploadImageService.java deleted file mode 100644 index 7c10a9a..0000000 --- a/src/main/java/com/alist/api/modules/file/service/FileUploadImageService.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.alist.api.modules.file.service; - -import com.alist.api.modules.file.properties.FileUploadProperties; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import javax.imageio.IIOImage; -import javax.imageio.ImageIO; -import javax.imageio.ImageWriteParam; -import javax.imageio.ImageWriter; -import javax.imageio.stream.ImageOutputStream; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.nio.file.Path; -import java.util.Iterator; - -@Service -public class FileUploadImageService { - public record ImageSize(int width, int height) { - } - - public ImageSize resizeAndSave( - MultipartFile file, - Path savePath, - FileUploadProperties.Resize resize, - String ext - ) throws IOException { - BufferedImage source = ImageIO.read(file.getInputStream()); - if (source == null) throw new IllegalArgumentException("invalid image"); - - BufferedImage target; - - if (resize.getWidth() != null && resize.getHeight() != null) { - target = cropAndResize(source, resize.getWidth(), resize.getHeight()); - } else if (resize.getMaxWidth() != null && source.getWidth() > resize.getMaxWidth()) { - target = resizeByMaxWidth(source, resize.getMaxWidth()); - } else { - target = source; - } - - writeImage(target, imageFormat(ext), savePath, resize.getQuality()); - - return new ImageSize(target.getWidth(), target.getHeight()); - } - - public ImageSize readSize(Path path) throws IOException { - BufferedImage image = ImageIO.read(path.toFile()); - if (image == null) return null; - return new ImageSize(image.getWidth(), image.getHeight()); - } - - private BufferedImage cropAndResize(BufferedImage source, int targetWidth, int targetHeight) { - double targetRatio = (double) targetWidth / targetHeight; - - int sourceWidth = source.getWidth(); - int sourceHeight = source.getHeight(); - double sourceRatio = (double) sourceWidth / sourceHeight; - - int cropWidth = sourceWidth; - int cropHeight = sourceHeight; - - if (sourceRatio > targetRatio) { - cropWidth = (int) Math.round(sourceHeight * targetRatio); - } else { - cropHeight = (int) Math.round(sourceWidth / targetRatio); - } - - BufferedImage cropped = source.getSubimage( - (sourceWidth - cropWidth) / 2, - (sourceHeight - cropHeight) / 2, - cropWidth, - cropHeight - ); - - return resize(cropped, targetWidth, targetHeight); - } - - private BufferedImage resizeByMaxWidth(BufferedImage source, int maxWidth) { - int targetWidth = maxWidth; - int targetHeight = (int) Math.round((double) source.getHeight() * targetWidth / source.getWidth()); - - return resize(source, targetWidth, targetHeight); - } - - private BufferedImage resize(BufferedImage source, int width, int height) { - BufferedImage target = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - Graphics2D graphics = target.createGraphics(); - - graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); - graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - - graphics.drawImage(source, 0, 0, width, height, null); - graphics.dispose(); - - return target; - } - - private void writeImage(BufferedImage image, String format, Path savePath, Float quality) throws IOException { - if (!"jpeg".equals(format)) { - boolean written = ImageIO.write(image, format, savePath.toFile()); - if (!written) throw new IllegalArgumentException("unsupported image format"); - return; - } - - Iterator writers = ImageIO.getImageWritersByFormatName("jpeg"); - if (!writers.hasNext()) { - throw new IllegalArgumentException("unsupported image format"); - } - - ImageWriter writer = writers.next(); - ImageWriteParam param = writer.getDefaultWriteParam(); - - if (param.canWriteCompressed()) { - param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); - param.setCompressionQuality(normalizeQuality(quality)); - } - - try (ImageOutputStream output = ImageIO.createImageOutputStream(savePath.toFile())) { - writer.setOutput(output); - writer.write(null, new IIOImage(image, null, null), param); - } finally { - writer.dispose(); - } - } - - private float normalizeQuality(Float quality) { - if (quality == null) return 0.9f; - if (quality < 0.0f) return 0.0f; - if (quality > 1.0f) return 1.0f; - return quality; - } - - private String imageFormat(String ext) { - if ("jpg".equalsIgnoreCase(ext)) return "jpeg"; - return ext.toLowerCase(); - } -} diff --git a/src/main/java/com/alist/api/modules/file/vo/FileUploadVo.java b/src/main/java/com/alist/api/modules/file/vo/FileUploadVo.java deleted file mode 100644 index f46d5b5..0000000 --- a/src/main/java/com/alist/api/modules/file/vo/FileUploadVo.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.alist.api.modules.file.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "파일 업로드 응답") -@Getter -@Setter -public class FileUploadVo { - @Schema(description = "업로드 파일 접근 경로", example = "/upload/editor/2026/05/19") - private String uploadPath; - - @Schema(description = "원본 파일명", example = "sample.png") - private String originalFileName; - - @Schema(description = "저장 파일명", example = "a1b2c3d4.png") - private String storedFileName; - - @Schema(description = "파일 확장자", example = "png") - private String fileExtension; - - @Schema(description = "Content-Type", example = "image/png") - private String contentType; - - @Schema(description = "파일 크기(byte)", example = "102400") - private Long fileSize; - - @Schema(description = "이미지 너비(px)", example = "800") - private Integer width; - - @Schema(description = "이미지 높이(px)", example = "600") - private Integer height; -} diff --git a/src/main/java/com/alist/api/modules/file/vo/SunEditorUploadVo.java b/src/main/java/com/alist/api/modules/file/vo/SunEditorUploadVo.java deleted file mode 100644 index 1e37b14..0000000 --- a/src/main/java/com/alist/api/modules/file/vo/SunEditorUploadVo.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.alist.api.modules.file.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "SunEditor 이미지 업로드 응답") -@Getter -@Setter -public class SunEditorUploadVo { - @Schema(description = "업로드 결과 목록") - private List result; - - public SunEditorUploadVo(List result) { - this.result = result; - } - - @Schema(description = "SunEditor 업로드 파일 항목") - @Getter - @Setter - public static class Item { - @Schema(description = "업로드 파일 URL", example = "https://api.alist.co.kr/upload/editor/sample.png") - private String url; - - @Schema(description = "원본 파일명", example = "sample.png") - private String name; - - @Schema(description = "파일 크기(byte)", example = "102400") - private Long size; - - public Item(String url, String name, Long size) { - this.url = url; - this.name = name; - this.size = size; - } - } -} diff --git a/src/main/java/com/alist/api/modules/front/auth/AuthController.java b/src/main/java/com/alist/api/modules/front/auth/AuthController.java deleted file mode 100644 index 2256e59..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/AuthController.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.alist.api.modules.front.auth; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.common.utils.SessionUtil; -import com.alist.api.config.jwt.JwtTokenProvider; -import com.alist.api.modules.front.auth.dto.LoginDto; -import com.alist.api.modules.front.auth.form.AuthApiKeyLoginForm; -import com.alist.api.modules.front.auth.form.AuthTokenForm; -import com.alist.api.modules.front.auth.service.AuthService; -import com.alist.api.modules.front.auth.service.SsoService; -import com.alist.api.modules.front.auth.vo.*; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@Tag(name = "102. 엑세스 토큰 발급", description = "엑세스 토큰 발급") -@RestController -@RequestMapping("/auth") -public class AuthController { - - @Value("${cookie.secure}") - private boolean cookieSecure; - - @Value("${cookie.domain}") - private String cookieDomain; - - @Value("${cookie.same-site:Lax}") - private String cookieSameSite; - - @Value("${jwt.access-token-validity-seconds}") - private long accessTokenValiditySeconds; - - @Value("${jwt.refresh-token-validity-seconds}") - private long refreshTokenValiditySeconds; - - @Value("${sso.cookie.name}") - private String ssoCookieName; - - private final SsoService ssoService; - - private final JwtTokenProvider jwtTokenProvider; - private final AuthService authService; - - public AuthController(SsoService ssoService, JwtTokenProvider jwtTokenProvider, AuthService authService) { - this.ssoService = ssoService; - this.jwtTokenProvider = jwtTokenProvider; - this.authService = authService; - } - - @Operation( - summary = "엑세스 토큰 발급 (테스트용 실사용X)" - , description = "엑세스 토큰이 발급됩니다. (테스트용 실사용X)" - ) - @PostMapping("/token") - public ResponseEntity> authToken(@Valid @RequestBody AuthTokenForm authTokenForm) { - AuthTokenVo authTokenVo = new AuthTokenVo(); - authTokenVo.setAccessToken(jwtTokenProvider.createToken(authTokenForm.toDto().getId())); - - return ApiResponse.entity(authTokenVo, ApiResponseCode.CODE_2001, "엑세스 토큰"); - } - - @Operation( - summary = "SSO 기반 토큰 발급" - , description = "ALIST_SSO 쿠키 기준으로 SSO 로그인 상태를 확인한 뒤 accessToken, refreshToken 쿠키를 발급합니다." - ) - @PostMapping("/access") - public ResponseEntity> authAccess( - HttpServletRequest request, - HttpServletResponse response - ) { - String ssoSessionId = SessionUtil.resolveSsoCookieValue(request, ssoCookieName); - SsoExchangeVo ssoExchangeVo = ssoService.selectSsoLoginChecked(ssoSessionId); - - AuthAccessVo authAccessVo = new AuthAccessVo(); - - if (ssoExchangeVo == null) { - authAccessVo.setAccessToken(false); - return ApiResponse.entity(authAccessVo, ApiResponseCode.CODE_401); - } - - String accessToken = jwtTokenProvider.createAccessToken(ssoExchangeVo.getUserTokenIdx(), ssoExchangeVo.getUserRole()); - String refreshToken = jwtTokenProvider.createRefreshToken(ssoExchangeVo.getUserTokenIdx()); - - LoginDto loginDto = new LoginDto(); - loginDto.setUserTokenIdx(ssoExchangeVo.getUserTokenIdx()); - loginDto.setRefreshToken(refreshToken); - - authService.updateRefreshToken(loginDto); - - SessionUtil.addTokenCookie(response, "accessToken", accessToken, cookieDomain, cookieSecure, cookieSameSite, accessTokenValiditySeconds); - SessionUtil.addTokenCookie(response, "refreshToken", refreshToken, cookieDomain, cookieSecure, cookieSameSite, refreshTokenValiditySeconds); - - authAccessVo.setAccessToken(true); - authAccessVo.setUserIdx(ssoExchangeVo.getUserIdx()); - authAccessVo.setUserId(ssoExchangeVo.getUserId()); - authAccessVo.setUserRole(ssoExchangeVo.getUserRole()); - authAccessVo.setUserType(ssoExchangeVo.getUserType()); - - return ApiResponse.entity(authAccessVo, ApiResponseCode.CODE_2001, "토큰"); - } - - @Operation( - summary = "리프레시 토큰으로 엑세스 토큰 재발급" - , description = "리프레시 토큰으로 엑세스 토큰 재발급" - ) - @PostMapping("/refresh") - public ResponseEntity> authRefresh( - @CookieValue(name = "refreshToken", required = false) String refreshToken - , HttpServletResponse response - ) { - AuthRefreshVo authRefreshVo = authService.selectAuthRefresh(refreshToken); - - if (authRefreshVo == null) { - AuthRefreshVo authRefreshVo1 = new AuthRefreshVo(); - authRefreshVo1.setRefreshed(false); - return ApiResponse.entity(authRefreshVo1, ApiResponseCode.CODE_401); - } - - SessionUtil.addTokenCookie(response, "accessToken", authRefreshVo.getAccessToken(), cookieDomain, cookieSecure, cookieSameSite, accessTokenValiditySeconds); - SessionUtil.addTokenCookie(response, "refreshToken", authRefreshVo.getRefreshToken(), cookieDomain, cookieSecure, cookieSameSite, refreshTokenValiditySeconds); - - return ApiResponse.entity(authRefreshVo, ApiResponseCode.CODE_200); - } - - @Operation( - summary = "API Key 로그인" - , description = "API Key로 인증하고 JSON으로 토큰을 반환합니다. (OpenAPI/외부 서비스용)" - ) - @PostMapping("/apiKeyLogin") - public ResponseEntity> authApiKeyLogin(@Valid @RequestBody AuthApiKeyLoginForm authApiKeyLoginForm) { - AuthApiKeyLoginVo authApiKeyLoginVo = authService.selectAuthApiKeyLogin(authApiKeyLoginForm.toDto()); - - if (authApiKeyLoginVo.getResultCode() == 2003) { - return ApiResponse.entity(authApiKeyLoginVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(authApiKeyLoginVo, ApiResponseCode.CODE_2001, "엑세스 토큰"); - } -} diff --git a/src/main/java/com/alist/api/modules/front/auth/SsoController.java b/src/main/java/com/alist/api/modules/front/auth/SsoController.java deleted file mode 100644 index a90f78e..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/SsoController.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.alist.api.modules.front.auth; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.common.utils.SessionUtil; -import com.alist.api.modules.front.auth.form.SsoExchangeForm; -import com.alist.api.modules.front.auth.form.SsoLoginForm; -import com.alist.api.modules.front.auth.service.AuthService; -import com.alist.api.modules.front.auth.service.SsoService; -import com.alist.api.modules.front.auth.vo.SsoExchangeVo; -import com.alist.api.modules.front.auth.vo.SsoLoginCheckedVo; -import com.alist.api.modules.front.auth.vo.SsoLoginVo; -import com.alist.api.modules.front.auth.vo.SsoLogoutVo; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.net.URI; - -@Tag(name = "101. SSO 인증", description = "공통 로그인, 인가 코드 발급, 코드 교환, 로그아웃 관련 API") -@RestController -@RequestMapping("/sso") -public class SsoController { - @Value("${cookie.secure}") - private boolean cookieSecure; - - @Value("${cookie.domain:}") - private String cookieDomain; - - @Value("${cookie.same-site:Lax}") - private String cookieSameSite; - - @Value("${sso.session.ttl-seconds}") - private long ssoSessionTtlSeconds; - - @Value("${sso.cookie.name}") - private String ssoCookieName; - private final SsoService ssoService; - private final AuthService authService; - - public SsoController(SsoService ssoService, AuthService authService) { - this.ssoService = ssoService; - this.authService = authService; - } - - @Operation( - summary = "SSO 로그인" - , description = "아이디/비밀번호로 공통 SSO 로그인 상태를 생성하고 HttpOnly 쿠키를 발급합니다." - ) - @PostMapping("/login") - public ResponseEntity> ssoLogin( - @Valid @RequestBody SsoLoginForm ssoLoginForm - , HttpServletResponse response - ) { - SsoLoginVo ssoLoginVo = ssoService.selectSsoLogin(ssoLoginForm.toDto()); - - if (ssoLoginVo.getResultCode() == 2003) { - SsoLoginVo ssoLoginVo1 = new SsoLoginVo(); - ssoLoginVo1.setLoginIn(false); - return ApiResponse.entity(ssoLoginVo1, ApiResponseCode.CODE_2003); - } - - SessionUtil.addTokenCookie(response, ssoCookieName, ssoLoginVo.getSsoSessionId(), cookieDomain, cookieSecure, cookieSameSite, ssoSessionTtlSeconds); - - ssoLoginVo.setLoginIn(true); - ssoLoginVo.setId(ssoLoginVo.getUserId()); - - return ApiResponse.entity(ssoLoginVo, ApiResponseCode.CODE_2001, "SSO 로그인"); - } - - @Operation( - summary = "SSO 로그인 상태 확인" - , description = "SSO 쿠키 기준으로 현재 공통 로그인 상태가 유효한지 확인하고, access/refresh 토큰 존재 여부도 함께 반환합니다." - ) - @GetMapping("/loginChecked") - public ResponseEntity> ssoLoginChecked( - HttpServletRequest request - , HttpServletResponse response - ) { - String ssoSessionId = SessionUtil.resolveSsoCookieValue(request, ssoCookieName); - - SsoLoginCheckedVo ssoLoginCheckedVo = new SsoLoginCheckedVo(); - - SsoExchangeVo ssoExchangeVo = ssoService.selectSsoLoginChecked(ssoSessionId); - - String accessToken = SessionUtil.resolveSsoCookieValue(request, "accessToken"); - String refreshToken = SessionUtil.resolveSsoCookieValue(request, "refreshToken"); - - ssoLoginCheckedVo.setAccessToken(accessToken != null && !accessToken.trim().isEmpty()); - ssoLoginCheckedVo.setRefreshToken(refreshToken != null && !refreshToken.trim().isEmpty()); - - if (ssoExchangeVo == null) { - ssoLoginCheckedVo.setLoggedIn(false); - return ApiResponse.entity(ssoLoginCheckedVo, ApiResponseCode.CODE_200); - } - - SessionUtil.addTokenCookie(response, ssoCookieName, ssoExchangeVo.getSsoSessionId(), cookieDomain, cookieSecure, cookieSameSite, ssoSessionTtlSeconds); - - ssoLoginCheckedVo.setLoggedIn(true); - ssoLoginCheckedVo.setSsoSessionId(ssoExchangeVo.getSsoSessionId()); - ssoLoginCheckedVo.setUserId(ssoExchangeVo.getUserId()); - ssoLoginCheckedVo.setUserIdx(ssoExchangeVo.getUserIdx()); - ssoLoginCheckedVo.setUserTokenIdx(ssoExchangeVo.getUserTokenIdx()); - ssoLoginCheckedVo.setUserRole(ssoExchangeVo.getUserRole()); - ssoLoginCheckedVo.setUserType(ssoExchangeVo.getUserType()); - - return ApiResponse.entity(ssoLoginCheckedVo, ApiResponseCode.CODE_200); - } - - @Operation( - summary = "SSO 인가 코드 발급" - , description = "공통 로그인 상태를 확인한 뒤 대상 서비스로 이동할 1회용 인가 코드를 발급하고 redirectUri 로 리다이렉트합니다." - ) - @GetMapping("/authorize") - public ResponseEntity ssoAuthorize( - @RequestParam("clientId") String clientId - , @RequestParam("redirectUri") String redirectUri - , @RequestParam(value = "state", required = false) String state - , HttpServletRequest request - ) { - String ssoSessionId = SessionUtil.resolveSsoCookieValue(request, ssoCookieName); - String redirectUrl = ssoService.selectSsoAuthorize(clientId, redirectUri, state, ssoSessionId); - - return ResponseEntity.status(302).location(URI.create(redirectUrl)).build(); - } - - @Operation( - summary = "SSO 인가 코드 교환" - , description = "서비스가 전달받은 1회용 인가 코드를 검증하고, 자체 세션 생성에 필요한 사용자 정보를 반환합니다." - ) - @PostMapping("/exchange") - public ResponseEntity> ssoExchange( - @Valid @RequestBody SsoExchangeForm ssoExchangeForm - , HttpServletResponse response - ) { - SsoExchangeVo ssoExchangeVo = ssoService.selectSsoExchange(ssoExchangeForm.toDto()); - - SessionUtil.addTokenCookie(response, ssoCookieName, ssoExchangeVo.getSsoSessionId(), cookieDomain, cookieSecure, cookieSameSite, ssoSessionTtlSeconds); - - return ApiResponse.entity(ssoExchangeVo, ApiResponseCode.CODE_2001, "SSO code 교환"); - } - - @Operation( - summary = "SSO 로그아웃" - , description = "공통 로그인 세션을 만료시켜 이후 authorize 및 서비스 접근 시 재로그인이 필요하도록 처리합니다." - ) - @PostMapping("/logout") - public ResponseEntity> ssoLogout( - HttpServletRequest request - , HttpServletResponse response - , @CookieValue(name = "refreshToken", required = false) String refreshToken - ) { - String ssoSessionId = SessionUtil.resolveSsoCookieValue(request, ssoCookieName); - - ssoService.updateSsoLogout(ssoSessionId); - authService.clearRefreshToken(refreshToken); - - SessionUtil.expireCookie(response, ssoCookieName, cookieDomain, cookieSecure, cookieSameSite); - SessionUtil.expireCookie(response, "accessToken", cookieDomain, cookieSecure, cookieSameSite); - SessionUtil.expireCookie(response, "refreshToken", cookieDomain, cookieSecure, cookieSameSite); - - SsoLogoutVo ssoLogoutVo = new SsoLogoutVo(); - ssoLogoutVo.setLogout(true); - - return ApiResponse.entity(ssoLogoutVo, ApiResponseCode.CODE_200); - } -} diff --git a/src/main/java/com/alist/api/modules/front/auth/dto/AuthTokenDto.java b/src/main/java/com/alist/api/modules/front/auth/dto/AuthTokenDto.java deleted file mode 100644 index 812cf78..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/dto/AuthTokenDto.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.alist.api.modules.front.auth.dto; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "테스트용 토큰 발급 요청 DTO") -@Getter -@Setter -public class AuthTokenDto { - @Schema(description = "아이디") - private String id; - - @Schema(description = "엑세스 토큰") - private String accessToken; - - @Schema(description = "리플레시 토큰") - private String refreshToken; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/front/auth/dto/LoginDto.java b/src/main/java/com/alist/api/modules/front/auth/dto/LoginDto.java deleted file mode 100644 index af54535..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/dto/LoginDto.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.alist.api.modules.front.auth.dto; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.time.Instant; - -@Schema(description = "API Key 로그인 응답") -@Getter -@Setter -public class LoginDto { - @Schema(description = "아이디") - private String id; - @Schema(description = "엑세스 토큰") - private String accessToken; - @Schema(description = "리플레시 토큰") - private String refreshToken; - - @JsonIgnore - @Schema(hidden = true) - private String password; - - @JsonIgnore - @Schema(hidden = true) - private String userRole; - - @JsonIgnore - @Schema(hidden = true) - private String userType; - - @JsonIgnore - @Schema(hidden = true) - private String userApiKey; - - @JsonIgnore - @Schema(hidden = true) - private Integer userTokenIdx; - - @JsonIgnore - @Schema(hidden = true) - private Instant expiresAt; - - @JsonIgnore - @Schema(hidden = true) - private Integer userIdx; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/front/auth/dto/SsoLoginDto.java b/src/main/java/com/alist/api/modules/front/auth/dto/SsoLoginDto.java deleted file mode 100644 index b49d672..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/dto/SsoLoginDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alist.api.modules.front.auth.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class SsoLoginDto { - private String id; - private String password; - - private int resultCode; - private String ssoSessionId; - private Integer userIdx; - private Integer userTokenIdx; - private String userId; - private String userRole; - private String userType; - - public LoginDto toDto() { - LoginDto loginDto = new LoginDto(); - loginDto.setId(this.id); - loginDto.setPassword(this.password); - return loginDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/auth/form/AuthApiKeyLoginForm.java b/src/main/java/com/alist/api/modules/front/auth/form/AuthApiKeyLoginForm.java deleted file mode 100644 index e57432a..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/form/AuthApiKeyLoginForm.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.alist.api.modules.front.auth.form; - -import com.alist.api.modules.front.auth.dto.LoginDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "API Key 로그인 요청 폼") -@Getter -@Setter -public class AuthApiKeyLoginForm { - @Schema( - description = "사용자 API Key" - , example = "YjeWJr9-N-XaJ7ix2M2spnfjOyG9nTmRlY9t6sKw4_M" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "apiKey를 입력해주세요.") - private String userApiKey; - - public LoginDto toDto() { - LoginDto loginDto = new LoginDto(); - loginDto.setUserApiKey(userApiKey == null ? null : userApiKey.trim()); - - return loginDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/auth/form/AuthTokenForm.java b/src/main/java/com/alist/api/modules/front/auth/form/AuthTokenForm.java deleted file mode 100644 index cce1a86..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/form/AuthTokenForm.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.alist.api.modules.front.auth.form; - -import com.alist.api.modules.front.auth.dto.AuthTokenDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Schema(description = "토큰 요청 폼") -public class AuthTokenForm { - - @NotBlank(message = "사용자명은 필수입니다.") - @Schema(description = "사용자명", example = "test") - private String id; - - public AuthTokenDto toDto() { - AuthTokenDto authTokenDto = new AuthTokenDto(); - authTokenDto.setId(this.id.trim()); - - return authTokenDto; - } -} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/front/auth/form/SsoExchangeForm.java b/src/main/java/com/alist/api/modules/front/auth/form/SsoExchangeForm.java deleted file mode 100644 index 4007eef..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/form/SsoExchangeForm.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.alist.api.modules.front.auth.form; - -import com.alist.api.modules.front.auth.dto.SsoExchangeDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "SSO 인가 코드 교환 요청 폼") -@Getter -@Setter -public class SsoExchangeForm { - @Schema( - description = "SSO 인가 코드" - , example = "authorization-code" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "인가 코드는 필수입니다.") - private String code; - - @Schema( - description = "SSO 클라이언트 ID" - , example = "alist-main" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "클라이언트 ID는 필수입니다.") - private String clientId; - - public SsoExchangeDto toDto() { - SsoExchangeDto ssoExchangeDto = new SsoExchangeDto(); - ssoExchangeDto.setCode(code == null ? null : code.trim()); - ssoExchangeDto.setClientId(clientId == null ? null : clientId.trim()); - return ssoExchangeDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/auth/form/SsoLoginForm.java b/src/main/java/com/alist/api/modules/front/auth/form/SsoLoginForm.java deleted file mode 100644 index 4b3a500..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/form/SsoLoginForm.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.alist.api.modules.front.auth.form; - -import com.alist.api.modules.front.auth.dto.SsoLoginDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "SSO 로그인 요청 폼") -@Getter -@Setter -public class SsoLoginForm { - @Schema( - description = "회원 아이디" - , example = "test" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "아이디는 필수입니다.") - private String id; - - @Schema( - description = "비밀번호" - , example = "password1234" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "비밀번호는 필수입니다.") - private String password; - - public SsoLoginDto toDto() { - SsoLoginDto ssoLoginDto = new SsoLoginDto(); - ssoLoginDto.setId(id == null ? null : id.trim()); - ssoLoginDto.setPassword(password == null ? null : password.trim()); - return ssoLoginDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/auth/mapper/AuthMapper.java b/src/main/java/com/alist/api/modules/front/auth/mapper/AuthMapper.java deleted file mode 100644 index fb2f19e..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/mapper/AuthMapper.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.alist.api.modules.front.auth.mapper; - -import com.alist.api.modules.front.auth.dto.LoginDto; -import com.alist.api.modules.front.auth.vo.LoginTokenVo; -import com.alist.api.modules.front.auth.vo.LoginVo; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface AuthMapper { - LoginVo selectLogin(LoginDto loginDto); - - LoginTokenVo selectUserTokenByUserId(LoginDto loginDto); - - LoginTokenVo selectUserTokenByUserApiKey(LoginDto loginDto); - - void updateRefreshToken(LoginDto loginDto); - - LoginTokenVo selectUserTokenByUserTokenIdx(int userTokenIdx); - - void clearRefreshTokenByUserTokenIdx(int userTokenIdx); - - void updateLoginByLastAt(LoginDto loginDto); -} diff --git a/src/main/java/com/alist/api/modules/front/auth/mapper/SsoMapper.java b/src/main/java/com/alist/api/modules/front/auth/mapper/SsoMapper.java deleted file mode 100644 index 11af754..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/mapper/SsoMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.alist.api.modules.front.auth.mapper; - -import com.alist.api.modules.front.auth.vo.SsoClientVo; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SsoMapper { - SsoClientVo selectSsoClient(String clientId); -} diff --git a/src/main/java/com/alist/api/modules/front/auth/service/AuthService.java b/src/main/java/com/alist/api/modules/front/auth/service/AuthService.java deleted file mode 100644 index 8574929..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/service/AuthService.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.alist.api.modules.front.auth.service; - -import com.alist.api.config.jwt.JwtTokenProvider; -import com.alist.api.config.properties.JwtProperties; -import com.alist.api.modules.front.auth.dto.LoginDto; -import com.alist.api.modules.front.auth.mapper.AuthMapper; -import com.alist.api.modules.front.auth.vo.AuthApiKeyLoginVo; -import com.alist.api.modules.front.auth.vo.AuthRefreshVo; -import com.alist.api.modules.front.auth.vo.LoginTokenVo; -import com.alist.api.modules.front.auth.vo.LoginVo; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; - -@Slf4j -@Service -public class AuthService { - private final AuthMapper authMapper; - private final PasswordEncoder passwordEncoder; - private final JwtTokenProvider jwtTokenProvider; - private final JwtProperties jwtProperties; - - public AuthService(AuthMapper authMapper, PasswordEncoder passwordEncoder, JwtTokenProvider jwtTokenProvider, JwtProperties jwtProperties) { - this.authMapper = authMapper; - this.passwordEncoder = passwordEncoder; - this.jwtTokenProvider = jwtTokenProvider; - this.jwtProperties = jwtProperties; - } - - @Transactional - public LoginVo selectLogin(LoginDto loginDto) { - LoginVo loginVo = authMapper.selectLogin(loginDto); - - if (loginVo == null) { - LoginVo loginVo1 = new LoginVo(); - loginVo1.setResultCode(2003); - return loginVo1; - } - - if (!passwordEncoder.matches(loginDto.getPassword(), loginVo.getPassword())) { - LoginVo loginVo1 = new LoginVo(); - loginVo1.setResultCode(2003); - return loginVo1; - } - - loginDto.setUserIdx(loginVo.getUserIdx()); - LoginTokenVo loginTokenVo = authMapper.selectUserTokenByUserId(loginDto); - - if (loginTokenVo == null) { - LoginVo loginVo1 = new LoginVo(); - loginVo1.setResultCode(2003); - return loginVo1; - } - - String accessToken = jwtTokenProvider.createAccessToken(loginTokenVo.getUserTokenIdx(), loginTokenVo.getUserRole()); - String refreshToken = jwtTokenProvider.createRefreshToken(loginTokenVo.getUserTokenIdx()); - - Instant expiresAt = Instant.now().plusSeconds(jwtProperties.getRefreshTokenValiditySeconds()); - - loginVo.setAccessToken(accessToken); - loginVo.setRefreshToken(refreshToken); - loginVo.setUserTokenIdx(loginTokenVo.getUserTokenIdx()); - loginVo.setUserRole(loginTokenVo.getUserRole()); - loginVo.setUserType(loginTokenVo.getUserType()); - loginVo.setExpiresAt(expiresAt); - loginVo.setResultCode(2001); - - loginDto.setUserTokenIdx(loginVo.getUserTokenIdx()); - loginDto.setRefreshToken(loginVo.getRefreshToken()); - loginDto.setExpiresAt(loginVo.getExpiresAt()); - - authMapper.updateRefreshToken(loginDto); - authMapper.updateLoginByLastAt(loginDto); - - return loginVo; - } - - @Transactional - public AuthApiKeyLoginVo selectAuthApiKeyLogin(LoginDto loginDto) { - AuthApiKeyLoginVo authApiKeyLoginVo = new AuthApiKeyLoginVo(); - - if (loginDto.getUserApiKey() == null) { - authApiKeyLoginVo.setResultCode(2003); - return authApiKeyLoginVo; - } - - LoginTokenVo loginTokenVo = authMapper.selectUserTokenByUserApiKey(loginDto); - - if (loginTokenVo == null) { - authApiKeyLoginVo.setResultCode(2003); - return authApiKeyLoginVo; - } - - String accessToken = jwtTokenProvider.createAccessToken(loginTokenVo.getUserTokenIdx(), loginTokenVo.getUserRole()); - String refreshToken = jwtTokenProvider.createRefreshToken(loginTokenVo.getUserTokenIdx()); - - loginDto.setUserTokenIdx(loginTokenVo.getUserTokenIdx()); - loginDto.setRefreshToken(refreshToken); - loginDto.setExpiresAt(Instant.now().plusSeconds(jwtProperties.getRefreshTokenValiditySeconds())); - - authMapper.updateRefreshToken(loginDto); - - authApiKeyLoginVo.setAccessToken(accessToken); - authApiKeyLoginVo.setRefreshToken(refreshToken); - authApiKeyLoginVo.setResultCode(2001); - - return authApiKeyLoginVo; - } - - @Transactional - public AuthRefreshVo selectAuthRefresh(String refreshToken) { - if (refreshToken == null || !jwtTokenProvider.validateToken(refreshToken)) { - return null; - } - - int userTokenIdx; - try { - userTokenIdx = Integer.parseInt(jwtTokenProvider.getUserTokenIdx(refreshToken)); - } catch (NumberFormatException e) { - return null; - } - - LoginTokenVo tokenVo = authMapper.selectUserTokenByUserTokenIdx(userTokenIdx); - if (tokenVo == null || tokenVo.getRefreshToken() == null) { - return null; - } - - if (!refreshToken.equals(tokenVo.getRefreshToken())) { - return null; - } - - String newAccessToken = jwtTokenProvider.createAccessToken(tokenVo.getUserTokenIdx(), tokenVo.getUserRole()); - String newRefreshToken = jwtTokenProvider.createRefreshToken(tokenVo.getUserTokenIdx()); - - LoginDto loginDto = new LoginDto(); - loginDto.setUserTokenIdx(userTokenIdx); - loginDto.setRefreshToken(newRefreshToken); - loginDto.setExpiresAt(Instant.now().plusSeconds(jwtProperties.getRefreshTokenValiditySeconds())); - - authMapper.updateRefreshToken(loginDto); - - AuthRefreshVo authRefreshVo = new AuthRefreshVo(); - authRefreshVo.setAccessToken(newAccessToken); - authRefreshVo.setRefreshToken(newRefreshToken); - authRefreshVo.setRefreshed(true); - - return authRefreshVo; - } - - public void clearRefreshToken(String refreshToken) { - if (refreshToken == null || !jwtTokenProvider.validateToken(refreshToken)) { - return; - } - - try { - int userTokenIdx = Integer.parseInt(jwtTokenProvider.getUserTokenIdx(refreshToken)); - authMapper.clearRefreshTokenByUserTokenIdx(userTokenIdx); - } catch (NumberFormatException ignored) { - } - } - - @Transactional - public void updateRefreshToken(LoginDto loginDto) { - Instant now = Instant.now(); - Instant expiresAt = now.plusSeconds(jwtProperties.getRefreshTokenValiditySeconds()); - - loginDto.setExpiresAt(expiresAt); - - authMapper.updateRefreshToken(loginDto); - } -} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/front/auth/service/SsoService.java b/src/main/java/com/alist/api/modules/front/auth/service/SsoService.java deleted file mode 100644 index c2ba116..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/service/SsoService.java +++ /dev/null @@ -1,223 +0,0 @@ -package com.alist.api.modules.front.auth.service; - -import com.alist.api.modules.front.auth.dto.SsoExchangeDto; -import com.alist.api.modules.front.auth.dto.SsoLoginDto; -import com.alist.api.modules.front.auth.mapper.SsoMapper; -import com.alist.api.modules.front.auth.vo.LoginVo; -import com.alist.api.modules.front.auth.vo.SsoClientVo; -import com.alist.api.modules.front.auth.vo.SsoExchangeVo; -import com.alist.api.modules.front.auth.vo.SsoLoginVo; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Service; - -import java.net.URI; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.UUID; - -@Service -public class SsoService { - private final AuthService authService; - private final SsoMapper ssoMapper; - private final StringRedisTemplate redisTemplate; - private final Duration ssoSessionTtl; - private final Duration ssoCodeTtl; - - public SsoService(AuthService authService, SsoMapper ssoMapper, StringRedisTemplate redisTemplate, @Value("${sso.session.ttl-seconds}") long ssoSessionTtlSeconds, @Value("${sso.code.ttl-seconds}") long ssoCodeTtlSeconds) { - this.authService = authService; - this.ssoMapper = ssoMapper; - this.redisTemplate = redisTemplate; - this.ssoSessionTtl = Duration.ofSeconds(ssoSessionTtlSeconds); - this.ssoCodeTtl = Duration.ofSeconds(ssoCodeTtlSeconds); - } - - public SsoLoginVo selectSsoLogin(SsoLoginDto ssoLoginDto) { - LoginVo loginVo = authService.selectLogin(ssoLoginDto.toDto()); - - SsoLoginVo ssoLoginVo = new SsoLoginVo(); - ssoLoginVo.setResultCode(loginVo.getResultCode()); - - if (loginVo.getResultCode() == 2003) { - return ssoLoginVo; - } - - String ssoSessionId = "SSO_" + UUID.randomUUID(); - String latestSessionKey = "alist:sso:userIdx:" + loginVo.getUserIdx(); - String sessionKey = "alist:sso:session:" + ssoSessionId; - - String oldSsoSessionId = redisTemplate.opsForValue().get(latestSessionKey); - if (oldSsoSessionId != null && !oldSsoSessionId.isBlank()) { - redisTemplate.delete("alist:sso:session:" + oldSsoSessionId); - } - - redisTemplate.opsForHash().put(sessionKey, "userIdx", String.valueOf(loginVo.getUserIdx())); - redisTemplate.opsForHash().put(sessionKey, "userTokenIdx", String.valueOf(loginVo.getUserTokenIdx())); - redisTemplate.opsForHash().put(sessionKey, "userId", loginVo.getId()); - redisTemplate.opsForHash().put(sessionKey, "userRole", loginVo.getUserRole()); - redisTemplate.opsForHash().put(sessionKey, "userType", loginVo.getUserType()); - redisTemplate.expire(sessionKey, ssoSessionTtl); - - redisTemplate.opsForValue().set(latestSessionKey, ssoSessionId, ssoSessionTtl); - - ssoLoginVo.setSsoSessionId(ssoSessionId); - ssoLoginVo.setUserId(loginVo.getId()); - ssoLoginVo.setUserIdx(loginVo.getUserIdx()); - ssoLoginVo.setUserRole(loginVo.getUserRole()); - ssoLoginVo.setUserType(loginVo.getUserType()); - ssoLoginVo.setResultCode(200); - - return ssoLoginVo; - } - - public SsoExchangeVo selectSsoLoginChecked(String ssoSessionId) { - if (ssoSessionId == null || ssoSessionId.isBlank()) { - return null; - } - - String sessionKey = "alist:sso:session:" + ssoSessionId; - String userIdx = (String) redisTemplate.opsForHash().get(sessionKey, "userIdx"); - - if (userIdx == null || userIdx.isBlank()) { - return null; - } - - String latestSsoSessionId = redisTemplate.opsForValue().get("alist:sso:userIdx:" + userIdx); - if (!ssoSessionId.equals(latestSsoSessionId)) { - return null; - } - - refreshSsoSessionTtl(ssoSessionId, userIdx); - - String userId = (String) redisTemplate.opsForHash().get(sessionKey, "userId"); - String userTokenIdx = (String) redisTemplate.opsForHash().get(sessionKey, "userTokenIdx"); - String userRole = (String) redisTemplate.opsForHash().get(sessionKey, "userRole"); - String userType = (String) redisTemplate.opsForHash().get(sessionKey, "userType"); - - SsoExchangeVo ssoExchangeVo = new SsoExchangeVo(); - ssoExchangeVo.setSsoSessionId(ssoSessionId); - ssoExchangeVo.setUserId(userId); - ssoExchangeVo.setUserIdx(userIdx == null ? null : Integer.parseInt(userIdx)); - ssoExchangeVo.setUserTokenIdx(userTokenIdx == null ? null : Integer.parseInt(userTokenIdx)); - ssoExchangeVo.setUserRole(userRole); - ssoExchangeVo.setUserType(userType); - - return ssoExchangeVo; - } - - public String selectSsoAuthorize(String clientId, String redirectUri, String state, String ssoSessionId) { - if (ssoSessionId == null || ssoSessionId.isBlank()) { - return "/login"; - } - - String sessionKey = "alist:sso:session:" + ssoSessionId; - String userIdx = (String) redisTemplate.opsForHash().get(sessionKey, "userIdx"); - if (userIdx == null) { - return "/login"; - } - - String latestSsoSessionId = redisTemplate.opsForValue().get("alist:sso:userIdx:" + userIdx); - if (!ssoSessionId.equals(latestSsoSessionId)) { - return "/login"; - } - - SsoClientVo ssoClientVo = ssoMapper.selectSsoClient(clientId); - if (ssoClientVo == null) { - throw new IllegalArgumentException("허용되지 않은 clientId 또는 redirectUri 입니다."); - } - - if (!"Y".equalsIgnoreCase(ssoClientVo.getUseYn())) { - throw new IllegalArgumentException("비활성화된 SSO 클라이언트입니다."); - } - - refreshSsoSessionTtl(ssoSessionId, userIdx); - - String code = "CODE_" + UUID.randomUUID(); - String codeKey = "alist:sso:code:" + code; - - redisTemplate.opsForHash().put(codeKey, "ssoSessionId", ssoSessionId); - redisTemplate.opsForHash().put(codeKey, "clientId", clientId); - redisTemplate.opsForHash().put(codeKey, "redirectUri", redirectUri); - redisTemplate.expire(codeKey, ssoCodeTtl); - - String encodedCode = URLEncoder.encode(code, StandardCharsets.UTF_8); - String encodedState = state == null ? "" : "&state=" + URLEncoder.encode(state, StandardCharsets.UTF_8); - - URI uri = URI.create(redirectUri); - String base = uri.getScheme() + "://" + uri.getAuthority() + uri.getPath(); - String query = uri.getQuery(); - - StringBuilder redirect = new StringBuilder(base); - if (query == null || query.isBlank()) { - redirect.append("?code=").append(encodedCode); - } else { - redirect.append("?").append(query).append("&code=").append(encodedCode); - } - redirect.append(encodedState); - - return redirect.toString(); - } - - public SsoExchangeVo selectSsoExchange(SsoExchangeDto ssoExchangeDto) { - String codeKey = "alist:sso:code:" + ssoExchangeDto.getCode(); - String ssoSessionId = (String) redisTemplate.opsForHash().get(codeKey, "ssoSessionId"); - String clientId = (String) redisTemplate.opsForHash().get(codeKey, "clientId"); - - if (ssoSessionId == null || !ssoExchangeDto.getClientId().equals(clientId)) { - throw new IllegalArgumentException("유효하지 않은 code 입니다."); - } - - String sessionKey = "alist:sso:session:" + ssoSessionId; - String userId = (String) redisTemplate.opsForHash().get(sessionKey, "userId"); - String userIdx = (String) redisTemplate.opsForHash().get(sessionKey, "userIdx"); - String userTokenIdx = (String) redisTemplate.opsForHash().get(sessionKey, "userTokenIdx"); - String userRole = (String) redisTemplate.opsForHash().get(sessionKey, "userRole"); - String userType = (String) redisTemplate.opsForHash().get(sessionKey, "userType"); - - String latestSsoSessionId = redisTemplate.opsForValue().get("alist:sso:userIdx:" + userIdx); - if (!ssoSessionId.equals(latestSsoSessionId)) { - throw new IllegalArgumentException("다른 곳에서 다시 로그인되어 세션이 만료되었습니다."); - } - - refreshSsoSessionTtl(ssoSessionId, userIdx); - redisTemplate.delete(codeKey); - - SsoExchangeVo ssoExchangeVo = new SsoExchangeVo(); - ssoExchangeVo.setUserId(userId); - ssoExchangeVo.setUserIdx(Integer.parseInt(userIdx)); - ssoExchangeVo.setUserTokenIdx(Integer.parseInt(userTokenIdx)); - ssoExchangeVo.setUserRole(userRole); - ssoExchangeVo.setUserType(userType); - ssoExchangeVo.setSsoSessionId(ssoSessionId); - - return ssoExchangeVo; - } - - public void updateSsoLogout(String ssoSessionId) { - if (ssoSessionId == null || ssoSessionId.isBlank()) { - return; - } - - String sessionKey = "alist:sso:session:" + ssoSessionId; - String userIdx = (String) redisTemplate.opsForHash().get(sessionKey, "userIdx"); - - redisTemplate.delete(sessionKey); - if (userIdx != null) { - redisTemplate.delete("alist:sso:userIdx:" + userIdx); - } - } - - private void refreshSsoSessionTtl(String ssoSessionId, String userIdx) { - if (ssoSessionId == null || ssoSessionId.isBlank() || userIdx == null || userIdx.isBlank()) { - return; - } - - String sessionKey = "alist:sso:session:" + ssoSessionId; - String latestSessionKey = "alist:sso:userIdx:" + userIdx; - - redisTemplate.expire(sessionKey, ssoSessionTtl); - redisTemplate.expire(latestSessionKey, ssoSessionTtl); - } - -} diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/AuthTokenVo.java b/src/main/java/com/alist/api/modules/front/auth/vo/AuthTokenVo.java deleted file mode 100644 index 170cbb3..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/vo/AuthTokenVo.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.alist.api.modules.front.auth.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "테스트용 토큰 발급 응답") -@Getter -@Setter -public class AuthTokenVo { - @Schema(description = "아이디") - private String id; - - @Schema(description = "엑세스 토큰") - private String accessToken; - - @Schema(description = "리플레시 토큰") - private String refreshToken; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/LoginTokenVo.java b/src/main/java/com/alist/api/modules/front/auth/vo/LoginTokenVo.java deleted file mode 100644 index 870ecb6..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/vo/LoginTokenVo.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.alist.api.modules.front.auth.vo; - -import lombok.Getter; - -@Getter -public class LoginTokenVo { - private Integer userTokenIdx; - private String userApiKey; - private String userRole; - private String userType; - private String refreshToken; -} diff --git a/src/main/java/com/alist/api/modules/front/auth/vo/SsoExchangeVo.java b/src/main/java/com/alist/api/modules/front/auth/vo/SsoExchangeVo.java deleted file mode 100644 index 4711431..0000000 --- a/src/main/java/com/alist/api/modules/front/auth/vo/SsoExchangeVo.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.alist.api.modules.front.auth.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "SSO 인가 코드 교환 응답") -@Getter -@Setter -public class SsoExchangeVo { - @Schema(description = "SSO 인가 코드", example = "authorization-code") - private String code; - - @Schema(description = "SSO 클라이언트 ID", example = "alist-main") - private String clientId; - - @Schema(description = "SSO 세션 ID") - private String ssoSessionId; - - @Schema(description = "회원 PK", example = "1") - private Integer userIdx; - - @Schema(description = "회원 토큰 PK", example = "1") - private Integer userTokenIdx; - - @Schema(description = "회원 아이디", example = "test") - private String userId; - - @Schema(description = "회원 권한 코드(CCD/SCM/I/R/E)", example = "SCM") - private String userRole; - - @Schema(description = "회원 유형 코드. T: 교사, S: 학생", example = "T") - private String userType; -} diff --git a/src/main/java/com/alist/api/modules/front/user/UserController.java b/src/main/java/com/alist/api/modules/front/user/UserController.java deleted file mode 100644 index e6873db..0000000 --- a/src/main/java/com/alist/api/modules/front/user/UserController.java +++ /dev/null @@ -1,183 +0,0 @@ -package com.alist.api.modules.front.user; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.modules.front.user.form.*; -import com.alist.api.modules.front.user.service.UserService; -import com.alist.api.modules.front.user.vo.*; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@Tag(name = "103. 사용자 관리", description = "사용자 회원가입 및 관리 API") -@Slf4j -@RestController -@RequestMapping("/user") -public class UserController { - - public final UserService userService; - - public UserController(UserService userService) { - this.userService = userService; - } - - @Operation( - summary = "사용자 회원가입" - , description = "새로운 사용자를 등록합니다. 아이디 중복 시 오류를 반환합니다." - ) - @PostMapping("/add") - public ResponseEntity> userAdd( - @Valid @RequestBody UserAddForm userAddForm - ) { - UserVo userVo = userService.insertUserAdd(userAddForm.toDto()); - - if (userVo.getResultCode() == 2004) { - return ApiResponse.entity(userVo, ApiResponseCode.CODE_2004, "아이디"); - } - - return ApiResponse.entity(userVo, ApiResponseCode.CODE_2002, "아이디"); - } - - @Operation( - summary = "이전 회원 정보 조회" - , description = "이전 사이트의 회원 정보를 조회 후 list로 리턴합니다." - ) - @PostMapping("/migration/list") - public ResponseEntity> userMigrationList( - @Valid @RequestBody UserMigrationListForm userMigrationListForm - ) { - UserMigrationListVo userMigrationListVo = userService.selectUserMigrationList(userMigrationListForm.toDto()); - - boolean empty = (userMigrationListVo.getAlistUserList() == null || userMigrationListVo.getAlistUserList().isEmpty()) && (userMigrationListVo.getEltownUserList() == null || userMigrationListVo.getEltownUserList().isEmpty()); - - if (empty) { - return ApiResponse.entity(userMigrationListVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(userMigrationListVo, ApiResponseCode.CODE_2001, "이전 회원"); - } - - @Operation( - summary = "사용자 개인정보 조회" - , description = "로그인한 사용자의 개인정보를 조회합니다." - ) - @GetMapping("/profile/view") - public ResponseEntity> userProfileView() { - UserProfileViewVo userProfileViewVo = userService.selectUserProfileView(); - - if (userProfileViewVo == null) { - UserProfileViewVo userProfileViewVo1 = new UserProfileViewVo(); - return ApiResponse.entity(userProfileViewVo1, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(userProfileViewVo, ApiResponseCode.CODE_2001, "개인정보"); - } - - @Operation( - summary = "사용자 비밀번호 확인" - , description = "로그인한 사용자의 현재 비밀번호가 일치하는지 확인합니다." - ) - @PostMapping("/password/check") - public ResponseEntity> userPasswordCheck( - @Valid @RequestBody UserPasswordCheckForm userPasswordCheckForm - ) { - UserPasswordCheckVo userPasswordCheckVo = userService.selectUserPasswordCheck(userPasswordCheckForm.toDto()); - - if (!userPasswordCheckVo.isPasswordMatched()) { - if (userPasswordCheckVo.getResultCode() == 4003) { - return ApiResponse.entity(userPasswordCheckVo, ApiResponseCode.CODE_4001); - } else if (userPasswordCheckVo.getResultCode() == 2003) { - return ApiResponse.entity(userPasswordCheckVo, ApiResponseCode.CODE_2003); - } - } - - return ApiResponse.entity(userPasswordCheckVo, ApiResponseCode.CODE_200); - } - - @Operation( - summary = "사용자 비밀번호 수정" - , description = "로그인한 사용자의 현재 비밀번호를 확인한 뒤 새 비밀번호로 변경합니다." - ) - @PatchMapping("/password/modify") - public ResponseEntity> userPasswordModify( - @Valid @RequestBody UserPasswordModifyForm userPasswordModifyForm - ) { - UserPasswordModifyVo userPasswordModifyVo = userService.updateUserPasswordModify(userPasswordModifyForm.toDto()); - - if (!userPasswordModifyVo.isPasswordChanged()) { - if (userPasswordModifyVo.getResultCode() == 4003 || userPasswordModifyVo.getResultCode() == 4001) { - return ApiResponse.entity(userPasswordModifyVo, ApiResponseCode.CODE_4001); - } - - return ApiResponse.entity(userPasswordModifyVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(userPasswordModifyVo, ApiResponseCode.CODE_2005, "비밀번호 변경"); - } - - @Operation( - summary = "사용자 개인정보 수정" - , description = "로그인한 사용자의 현재 비밀번호를 확인한 뒤 이메일과 휴대폰 번호를 수정합니다." - ) - @PatchMapping("/profile/modify") - public ResponseEntity> userProfileModify( - @Valid @RequestBody UserProfileModifyForm userProfileModifyForm - ) { - UserProfileModifyVo userProfileModifyVo = userService.updateUserProfileModify(userProfileModifyForm.toDto()); - - if (!userProfileModifyVo.isProfileUpdated()) { - if (userProfileModifyVo.getResultCode() == 4003 || userProfileModifyVo.getResultCode() == 4001) { - return ApiResponse.entity(userProfileModifyVo, ApiResponseCode.CODE_4001); - } - - return ApiResponse.entity(userProfileModifyVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(userProfileModifyVo, ApiResponseCode.CODE_2005, "개인정보 수정"); - } - - @Operation( - summary = "사용자 탈퇴 상태 변경" - , description = "로그인한 사용자의 탈퇴 요청 또는 탈퇴 요청 철회를 처리합니다. P: 탈퇴대기, N: 탈퇴요청 철회" - ) - @PatchMapping("/withdraw/modify") - public ResponseEntity> userWithdrawModify( - @Valid @RequestBody UserWithdrawModifyForm userWithdrawModifyForm - ) { - UserWithdrawModifyVo userWithdrawModifyVo = userService.updateUserWithdrawModify(userWithdrawModifyForm.toDto()); - - if (!userWithdrawModifyVo.isWithdrawStatusUpdated()) { - if (userWithdrawModifyVo.getResultCode() == 4003 || userWithdrawModifyVo.getResultCode() == 4001) { - return ApiResponse.entity(userWithdrawModifyVo, ApiResponseCode.CODE_4001); - } - - return ApiResponse.entity(userWithdrawModifyVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(userWithdrawModifyVo, ApiResponseCode.CODE_2005, "탈퇴 상태 변경"); - } - - @Operation( - summary = "사용자 휴면 상태 변경" - , description = "로그인한 사용자의 휴면 처리 또는 휴면 해지를 처리합니다. Y: 휴면 회원, N: 휴면 해지" - ) - @PatchMapping("/dormant/modify") - public ResponseEntity> userDormantModify( - @Valid @RequestBody UserDormantModifyForm userDormantModifyForm - ) { - UserDormantModifyVo userDormantModifyVo = userService.updateUserDormantModify(userDormantModifyForm.toDto()); - - if (!userDormantModifyVo.isDormantStatusUpdated()) { - if (userDormantModifyVo.getResultCode() == 4003 || userDormantModifyVo.getResultCode() == 4001) { - return ApiResponse.entity(userDormantModifyVo, ApiResponseCode.CODE_4001); - } - - return ApiResponse.entity(userDormantModifyVo, ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(userDormantModifyVo, ApiResponseCode.CODE_2005, "휴면 상태 변경"); - } -} \ No newline at end of file diff --git a/src/main/java/com/alist/api/modules/front/user/dto/UserDormantModifyDto.java b/src/main/java/com/alist/api/modules/front/user/dto/UserDormantModifyDto.java deleted file mode 100644 index ea8bd96..0000000 --- a/src/main/java/com/alist/api/modules/front/user/dto/UserDormantModifyDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.alist.api.modules.front.user.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class UserDormantModifyDto { - private Integer userTokenIdx; - private String dormantYn; -} diff --git a/src/main/java/com/alist/api/modules/front/user/dto/UserDto.java b/src/main/java/com/alist/api/modules/front/user/dto/UserDto.java deleted file mode 100644 index 8c0474f..0000000 --- a/src/main/java/com/alist/api/modules/front/user/dto/UserDto.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.alist.api.modules.front.user.dto; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Setter -@Getter -public class UserDto { - private Integer userIdx; - private String id; - private String userRole; - private String userType; - - @JsonIgnore - @Schema(hidden = true) - private String newPassword; - - @JsonIgnore - @Schema(hidden = true) - private String password; - - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/front/user/dto/UserPasswordCheckDto.java b/src/main/java/com/alist/api/modules/front/user/dto/UserPasswordCheckDto.java deleted file mode 100644 index e65a3e1..0000000 --- a/src/main/java/com/alist/api/modules/front/user/dto/UserPasswordCheckDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.alist.api.modules.front.user.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class UserPasswordCheckDto { - private Integer userTokenIdx; - private String password; -} diff --git a/src/main/java/com/alist/api/modules/front/user/dto/UserPasswordModifyDto.java b/src/main/java/com/alist/api/modules/front/user/dto/UserPasswordModifyDto.java deleted file mode 100644 index f1beaea..0000000 --- a/src/main/java/com/alist/api/modules/front/user/dto/UserPasswordModifyDto.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.alist.api.modules.front.user.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class UserPasswordModifyDto { - private Integer userTokenIdx; - private String currentPassword; - private String newPassword; -} diff --git a/src/main/java/com/alist/api/modules/front/user/dto/UserProfileModifyDto.java b/src/main/java/com/alist/api/modules/front/user/dto/UserProfileModifyDto.java deleted file mode 100644 index 8e8a90f..0000000 --- a/src/main/java/com/alist/api/modules/front/user/dto/UserProfileModifyDto.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.alist.api.modules.front.user.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class UserProfileModifyDto { - private Integer userTokenIdx; - private String currentPassword; - private String email; - private String hp; -} diff --git a/src/main/java/com/alist/api/modules/front/user/dto/UserWithdrawModifyDto.java b/src/main/java/com/alist/api/modules/front/user/dto/UserWithdrawModifyDto.java deleted file mode 100644 index d3349ca..0000000 --- a/src/main/java/com/alist/api/modules/front/user/dto/UserWithdrawModifyDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.alist.api.modules.front.user.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class UserWithdrawModifyDto { - private Integer userTokenIdx; - private String withdrawStatus; -} diff --git a/src/main/java/com/alist/api/modules/front/user/form/UserAddForm.java b/src/main/java/com/alist/api/modules/front/user/form/UserAddForm.java deleted file mode 100644 index 34b0229..0000000 --- a/src/main/java/com/alist/api/modules/front/user/form/UserAddForm.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.alist.api.modules.front.user.form; - -import com.alist.api.modules.front.user.dto.UserDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import lombok.Getter; -import lombok.Setter; - -@Setter -@Getter -@Schema(description = "회원 가입 요청 폼") -public class UserAddForm { - @Schema( - description = "사용자 아이디 (공백 불가)" - , example = "test" - ) - @NotBlank(message = "아이디를 입력해주세요.") - private String id; - - @Schema( - description = "비밀번호 (8~64자, 영문 + 숫자 조합, 공백 불가)" - , example = "pass1234" - ) - @NotBlank(message = "비밀번호를 입력해주세요.") - @Size(min = 8, max = 64, message = "비밀번호는 8~64자여야 합니다.") - @Pattern( - regexp = "^(?=.*[A-Za-z])(?=.*\\d)\\S+$" - , message = "비밀번호는 영문과 숫자를 포함하고 공백이 없어야 합니다." - ) - private String password; - - @NotBlank(message = "사용자 권한(교사{CCM: 복합 캠퍼스 마스터, SCM:단일 캠퍼스 마스터, I: 교사}, 학생{R:정회원, E:수강생}") - @Pattern( - regexp = "^(CCM|SCM|I|R|E)$" - , message = "사용자 권한(교사{CCM: 복합 캠퍼스 마스터, SCM:단일 캠퍼스 마스터, I: 교사}, 학생{R:정회원, E:수강생})" - ) - @Schema( - description = "사용자 권한(사용자 권한(교사{CCM: 복합 캠퍼스 마스터, SCM:단일 캠퍼스 마스터, I: 교사}, 학생{R:정회원, E:수강생})" - , example = "I" - ) - private String userRole; - - @NotBlank(message = "사용자 타입(T:교사, S: 학생)") - @Pattern( - regexp = "^(S|T)$" - , message = "사용자 타입 S(학생), T(교사)만 가능합니다." - ) - @Schema( - description = "사용자 타입(T:교사, S: 학생)" - , example = "T" - ) - private String userType; - - public UserDto toDto() { - UserDto userDto = new UserDto(); - userDto.setId(id.trim()); - userDto.setPassword(password); - userDto.setUserRole(userRole); - userDto.setUserType(userType); - return userDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/user/form/UserDormantModifyForm.java b/src/main/java/com/alist/api/modules/front/user/form/UserDormantModifyForm.java deleted file mode 100644 index 11a4cb9..0000000 --- a/src/main/java/com/alist/api/modules/front/user/form/UserDormantModifyForm.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.front.user.form; - -import com.alist.api.modules.front.user.dto.UserDormantModifyDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 휴면 상태 변경 요청 폼") -@Getter -@Setter -public class UserDormantModifyForm { - @Schema( - description = "휴면 여부. Y: 휴면 회원, N: 휴면 해지" - , example = "Y" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "휴면 여부는 필수입니다.") - @Pattern(regexp = "^[YN]$", message = "휴면 여부는 Y 또는 N만 가능합니다.") - private String dormantYn; - - public UserDormantModifyDto toDto() { - UserDormantModifyDto userDormantModifyDto = new UserDormantModifyDto(); - userDormantModifyDto.setDormantYn(dormantYn); - return userDormantModifyDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/user/form/UserMigrationListForm.java b/src/main/java/com/alist/api/modules/front/user/form/UserMigrationListForm.java deleted file mode 100644 index a346bef..0000000 --- a/src/main/java/com/alist/api/modules/front/user/form/UserMigrationListForm.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.alist.api.modules.front.user.form; - -import com.alist.api.modules.front.user.dto.UserDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Setter -@Getter -@Schema(description = "이전회원 정보 조회") -public class UserMigrationListForm { - @Schema( - description = "사용자 아이디 (공백 불가)" - , example = "test" - ) - @NotBlank(message = "아이디를 입력해주세요.") - private String id; - - @NotBlank(message = "비밀번호를 입력해주세요.") - private String password; - - public UserDto toDto() { - UserDto userDto = new UserDto(); - userDto.setId(id.trim()); - userDto.setPassword(password); - return userDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/user/form/UserPasswordCheckForm.java b/src/main/java/com/alist/api/modules/front/user/form/UserPasswordCheckForm.java deleted file mode 100644 index 372f253..0000000 --- a/src/main/java/com/alist/api/modules/front/user/form/UserPasswordCheckForm.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alist.api.modules.front.user.form; - -import com.alist.api.modules.front.user.dto.UserPasswordCheckDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 비밀번호 확인 요청 폼") -@Getter -@Setter -public class UserPasswordCheckForm { - @Schema( - description = "현재 비밀번호" - , example = "password1234" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "비밀번호는 필수입니다.") - private String password; - - public UserPasswordCheckDto toDto() { - UserPasswordCheckDto userPasswordCheckDto = new UserPasswordCheckDto(); - userPasswordCheckDto.setPassword(password); - return userPasswordCheckDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/user/form/UserPasswordModifyForm.java b/src/main/java/com/alist/api/modules/front/user/form/UserPasswordModifyForm.java deleted file mode 100644 index a901ea7..0000000 --- a/src/main/java/com/alist/api/modules/front/user/form/UserPasswordModifyForm.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.alist.api.modules.front.user.form; - -import com.alist.api.modules.front.user.dto.UserPasswordModifyDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 비밀번호 수정 요청 폼") -@Getter -@Setter -public class UserPasswordModifyForm { - @Schema( - description = "현재 비밀번호" - , example = "password1234" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "현재 비밀번호는 필수입니다.") - private String currentPassword; - - @Schema( - description = "새 비밀번호" - , example = "newPassword1234" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "새 비밀번호는 필수입니다.") - private String newPassword; - - public UserPasswordModifyDto toDto() { - UserPasswordModifyDto userPasswordModifyDto = new UserPasswordModifyDto(); - userPasswordModifyDto.setCurrentPassword(currentPassword); - userPasswordModifyDto.setNewPassword(newPassword); - return userPasswordModifyDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/user/form/UserProfileModifyForm.java b/src/main/java/com/alist/api/modules/front/user/form/UserProfileModifyForm.java deleted file mode 100644 index c1279f2..0000000 --- a/src/main/java/com/alist/api/modules/front/user/form/UserProfileModifyForm.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.alist.api.modules.front.user.form; - -import com.alist.api.modules.front.user.dto.UserProfileModifyDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 개인정보 수정 요청 폼") -@Getter -@Setter -public class UserProfileModifyForm { - @Schema( - description = "현재 비밀번호" - , example = "password1234" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "현재 비밀번호는 필수입니다.") - private String currentPassword; - - @Schema(description = "이메일. 수정할 때만 전달합니다.", example = "user@example.com") - @Email(message = "이메일 형식을 확인해주세요.") - private String email; - - @Schema(description = "휴대폰 번호. 수정할 때만 전달합니다.", example = "01012345678") - private String hp; - - public UserProfileModifyDto toDto() { - UserProfileModifyDto userProfileModifyDto = new UserProfileModifyDto(); - userProfileModifyDto.setCurrentPassword(currentPassword); - userProfileModifyDto.setEmail(email); - userProfileModifyDto.setHp(hp); - return userProfileModifyDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/user/form/UserWithdrawModifyForm.java b/src/main/java/com/alist/api/modules/front/user/form/UserWithdrawModifyForm.java deleted file mode 100644 index e903b59..0000000 --- a/src/main/java/com/alist/api/modules/front/user/form/UserWithdrawModifyForm.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.front.user.form; - -import com.alist.api.modules.front.user.dto.UserWithdrawModifyDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 탈퇴 상태 변경 요청 폼") -@Getter -@Setter -public class UserWithdrawModifyForm { - @Schema( - description = "탈퇴 상태. P: 탈퇴대기, N: 탈퇴요청 철회" - , example = "P" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "탈퇴 상태는 필수입니다.") - @Pattern(regexp = "^[PN]$", message = "탈퇴 상태는 P 또는 N만 가능합니다.") - private String withdrawStatus; - - public UserWithdrawModifyDto toDto() { - UserWithdrawModifyDto userWithdrawModifyDto = new UserWithdrawModifyDto(); - userWithdrawModifyDto.setWithdrawStatus(withdrawStatus); - return userWithdrawModifyDto; - } -} diff --git a/src/main/java/com/alist/api/modules/front/user/mapper/UserMapper.java b/src/main/java/com/alist/api/modules/front/user/mapper/UserMapper.java deleted file mode 100644 index ad45bb1..0000000 --- a/src/main/java/com/alist/api/modules/front/user/mapper/UserMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.alist.api.modules.front.user.mapper; - -import com.alist.api.modules.front.user.dto.*; -import com.alist.api.modules.front.user.vo.UserProfileViewVo; -import com.alist.api.modules.front.user.vo.UserVo; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface UserMapper { - int selectDuplicateUserCount(UserDto userDto); - - int insertUserAdd(UserDto userDto); - - int insertUserToken(UserTokenDto userTokenDto); - - UserVo selectUserByUserTokenIdx(Integer userTokenIdx); - - int updateUserPasswordModify(UserPasswordModifyDto userPasswordModifyDto); - - int updateUserProfileModify(UserProfileModifyDto userProfileModifyDto); - - int updateUserWithdrawModify(UserWithdrawModifyDto userWithdrawModifyDto); - - int updateUserDormantModify(UserDormantModifyDto userDormantModifyDto); - - UserProfileViewVo selectUserProfileView(Integer userTokenIdx); -} diff --git a/src/main/java/com/alist/api/modules/front/user/service/UserService.java b/src/main/java/com/alist/api/modules/front/user/service/UserService.java deleted file mode 100644 index 9bb1ac5..0000000 --- a/src/main/java/com/alist/api/modules/front/user/service/UserService.java +++ /dev/null @@ -1,299 +0,0 @@ -package com.alist.api.modules.front.user.service; - -import com.alist.api.common.utils.ApiKeyGenerator; -import com.alist.api.common.utils.SecurityUtil; -import com.alist.api.modules.migration.alist.user.dto.AlistUserDto; -import com.alist.api.modules.migration.alist.user.service.AlistUserService; -import com.alist.api.modules.migration.alist.user.vo.AlistUserVo; -import com.alist.api.modules.migration.eltown.user.dto.EltownUserDto; -import com.alist.api.modules.migration.eltown.user.service.EltownUserService; -import com.alist.api.modules.migration.eltown.user.vo.EltownUserVo; -import com.alist.api.modules.front.user.dto.*; -import com.alist.api.modules.front.user.mapper.UserMapper; -import com.alist.api.modules.front.user.vo.*; -import lombok.extern.slf4j.Slf4j; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Slf4j -@Service -public class UserService { - private final UserMapper userMapper; - private final PasswordEncoder passwordEncoder; - - private final AlistUserService alistUserService; - private final EltownUserService eltownUserService; - - public UserService(UserMapper userMapper, PasswordEncoder passwordEncoder, AlistUserService alistUserService, EltownUserService eltownUserService) { - this.userMapper = userMapper; - this.passwordEncoder = passwordEncoder; - this.alistUserService = alistUserService; - this.eltownUserService = eltownUserService; - } - - @Transactional - public UserVo insertUserAdd(UserDto userDto) { - UserVo userVo = new UserVo(); - userVo.setId(userDto.getId()); - userVo.setUserRole(userDto.getUserRole()); - userVo.setUserType(userDto.getUserType()); - - // 중복체크 - if (userMapper.selectDuplicateUserCount(userDto) > 0) { - userVo.setResultCode(2004); - return userVo; - } - - // 비밀번호 생성 - userDto.setNewPassword(passwordEncoder.encode(userDto.getPassword())); - - // 회원정보 입력 - int resultCount = userMapper.insertUserAdd(userDto); - - if (resultCount > 0) { - // user_api_key 생성 - UserTokenDto userTokenDto = new UserTokenDto(); - userTokenDto.setUserIdx(userDto.getUserIdx()); - userTokenDto.setUserRole(userDto.getUserRole()); - userTokenDto.setUserType(userDto.getUserType()); - - userVo.setUserIdx(userDto.getUserIdx()); - - apiKeyWithRetry(userTokenDto); - } - - return userVo; - } - - @Transactional - public int apiKeyWithRetry(UserTokenDto userTokenDto) { - for (int i = 0; i < 5; i++) { - userTokenDto.setUserApiKey(ApiKeyGenerator.userApiKeyProc()); - try { - return userMapper.insertUserToken(userTokenDto); - } catch (DuplicateKeyException e) { - // 충돌이면 다시 생성 - log.warn("Duplicate API key. retrying... userId={}", userTokenDto.getUserIdx()); - } - } - throw new IllegalStateException("API key generation failed after retries."); - } - - @Transactional(readOnly = true) - public UserMigrationListVo selectUserMigrationList(UserDto userDto) { - AlistUserDto alistUserDto = new AlistUserDto(); - alistUserDto.setId(userDto.getId()); - alistUserDto.setPassword(userDto.getPassword()); - - // alist 조회 - List alistUserList = alistUserService.selectAlistUserList(alistUserDto); - - EltownUserDto eltownUserDto = new EltownUserDto(); - eltownUserDto.setId(userDto.getId()); - eltownUserDto.setPassword(userDto.getPassword()); - - // eltown 조회 - List eltownUserList = eltownUserService.selectEltownUserList(eltownUserDto); - - UserMigrationListVo userMigrationListVo = new UserMigrationListVo(); - - userMigrationListVo.setAlistUserList(alistUserList); - userMigrationListVo.setEltownUserList(eltownUserList); - - return userMigrationListVo; - } - - @Transactional(readOnly = true) - public UserProfileViewVo selectUserProfileView() { - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (userTokenIdx == null) { - return null; - } - - return userMapper.selectUserProfileView(userTokenIdx); - } - - @Transactional(readOnly = true) - public UserPasswordCheckVo selectUserPasswordCheck(UserPasswordCheckDto userPasswordCheckDto) { - UserPasswordCheckVo userPasswordCheckVo = new UserPasswordCheckVo(); - - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - userPasswordCheckDto.setUserTokenIdx(userTokenIdx); - - if (userTokenIdx == null || userPasswordCheckDto.getPassword() == null) { - userPasswordCheckVo.setPasswordMatched(false); - userPasswordCheckVo.setResultCode(4003); - return userPasswordCheckVo; - } - - UserVo userVo = userMapper.selectUserByUserTokenIdx(userPasswordCheckDto.getUserTokenIdx()); - - if (userVo == null || userVo.getPassword() == null) { - userPasswordCheckVo.setPasswordMatched(false); - userPasswordCheckVo.setResultCode(2003); - return userPasswordCheckVo; - } - - boolean matched = passwordEncoder.matches(userPasswordCheckDto.getPassword(), userVo.getPassword()); - - if (!matched) { - userPasswordCheckVo.setPasswordMatched(false); - userPasswordCheckVo.setResultCode(2003); - return userPasswordCheckVo; - } - - userPasswordCheckVo.setPasswordMatched(true); - userPasswordCheckVo.setResultCode(200); - - return userPasswordCheckVo; - } - - @Transactional - public UserPasswordModifyVo updateUserPasswordModify(UserPasswordModifyDto userPasswordModifyDto) { - UserPasswordModifyVo userPasswordModifyVo = new UserPasswordModifyVo(); - - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - userPasswordModifyDto.setUserTokenIdx(userTokenIdx); - - if (userTokenIdx == null - || userPasswordModifyDto.getCurrentPassword() == null - || userPasswordModifyDto.getNewPassword() == null) { - userPasswordModifyVo.setPasswordChanged(false); - userPasswordModifyVo.setResultCode(4003); - return userPasswordModifyVo; - } - - UserVo userVo = userMapper.selectUserByUserTokenIdx(userTokenIdx); - - if (userVo == null || userVo.getPassword() == null) { - userPasswordModifyVo.setPasswordChanged(false); - userPasswordModifyVo.setResultCode(2003); - return userPasswordModifyVo; - } - - boolean matched = passwordEncoder.matches( - userPasswordModifyDto.getCurrentPassword(), - userVo.getPassword() - ); - - if (!matched) { - userPasswordModifyVo.setPasswordChanged(false); - userPasswordModifyVo.setResultCode(4001); - return userPasswordModifyVo; - } - - userPasswordModifyDto.setNewPassword(passwordEncoder.encode(userPasswordModifyDto.getNewPassword())); - - int updateCount = userMapper.updateUserPasswordModify(userPasswordModifyDto); - - userPasswordModifyVo.setPasswordChanged(updateCount > 0); - userPasswordModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); - - return userPasswordModifyVo; - } - - @Transactional - public UserProfileModifyVo updateUserProfileModify(UserProfileModifyDto userProfileModifyDto) { - UserProfileModifyVo userProfileModifyVo = new UserProfileModifyVo(); - - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - userProfileModifyDto.setUserTokenIdx(userTokenIdx); - - if (userTokenIdx == null - || userProfileModifyDto.getCurrentPassword() == null - || (userProfileModifyDto.getEmail() == null && userProfileModifyDto.getHp() == null)) { - userProfileModifyVo.setProfileUpdated(false); - userProfileModifyVo.setResultCode(4003); - return userProfileModifyVo; - } - - UserVo userVo = userMapper.selectUserByUserTokenIdx(userTokenIdx); - - if (userVo == null || userVo.getPassword() == null) { - userProfileModifyVo.setProfileUpdated(false); - userProfileModifyVo.setResultCode(2003); - return userProfileModifyVo; - } - - boolean matched = passwordEncoder.matches( - userProfileModifyDto.getCurrentPassword(), - userVo.getPassword() - ); - - if (!matched) { - userProfileModifyVo.setProfileUpdated(false); - userProfileModifyVo.setResultCode(4001); - return userProfileModifyVo; - } - - int updateCount = userMapper.updateUserProfileModify(userProfileModifyDto); - - userProfileModifyVo.setProfileUpdated(updateCount > 0); - userProfileModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); - - return userProfileModifyVo; - } - - @Transactional - public UserWithdrawModifyVo updateUserWithdrawModify(UserWithdrawModifyDto userWithdrawModifyDto) { - UserWithdrawModifyVo userWithdrawModifyVo = new UserWithdrawModifyVo(); - - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - userWithdrawModifyDto.setUserTokenIdx(userTokenIdx); - - if (userTokenIdx == null || userWithdrawModifyDto.getWithdrawStatus() == null) { - userWithdrawModifyVo.setWithdrawStatusUpdated(false); - userWithdrawModifyVo.setResultCode(4003); - return userWithdrawModifyVo; - } - - String withdrawStatus = userWithdrawModifyDto.getWithdrawStatus(); - - if (!"P".equals(withdrawStatus) && !"N".equals(withdrawStatus)) { - userWithdrawModifyVo.setWithdrawStatusUpdated(false); - userWithdrawModifyVo.setResultCode(4001); - return userWithdrawModifyVo; - } - - int updateCount = userMapper.updateUserWithdrawModify(userWithdrawModifyDto); - - userWithdrawModifyVo.setWithdrawStatusUpdated(updateCount > 0); - userWithdrawModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); - - return userWithdrawModifyVo; - } - - @Transactional - public UserDormantModifyVo updateUserDormantModify(UserDormantModifyDto userDormantModifyDto) { - UserDormantModifyVo userDormantModifyVo = new UserDormantModifyVo(); - - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - userDormantModifyDto.setUserTokenIdx(userTokenIdx); - - if (userTokenIdx == null || userDormantModifyDto.getDormantYn() == null) { - userDormantModifyVo.setDormantStatusUpdated(false); - userDormantModifyVo.setResultCode(4003); - return userDormantModifyVo; - } - - String dormantYn = userDormantModifyDto.getDormantYn(); - - if (!"Y".equals(dormantYn) && !"N".equals(dormantYn)) { - userDormantModifyVo.setDormantStatusUpdated(false); - userDormantModifyVo.setResultCode(4001); - return userDormantModifyVo; - } - - int updateCount = userMapper.updateUserDormantModify(userDormantModifyDto); - - userDormantModifyVo.setDormantStatusUpdated(updateCount > 0); - userDormantModifyVo.setResultCode(updateCount > 0 ? 2005 : 2003); - - return userDormantModifyVo; - } -} diff --git a/src/main/java/com/alist/api/modules/front/user/vo/UserDormantModifyVo.java b/src/main/java/com/alist/api/modules/front/user/vo/UserDormantModifyVo.java deleted file mode 100644 index 312b61b..0000000 --- a/src/main/java/com/alist/api/modules/front/user/vo/UserDormantModifyVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.front.user.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 휴면 상태 변경 응답") -@Getter -@Setter -public class UserDormantModifyVo { - @Schema(description = "휴면 상태 변경 여부", example = "true") - private boolean dormantStatusUpdated; - - @JsonIgnore - @Schema(hidden = true) - private Integer resultCode; -} diff --git a/src/main/java/com/alist/api/modules/front/user/vo/UserMigrationListVo.java b/src/main/java/com/alist/api/modules/front/user/vo/UserMigrationListVo.java deleted file mode 100644 index 940012a..0000000 --- a/src/main/java/com/alist/api/modules/front/user/vo/UserMigrationListVo.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.alist.api.modules.front.user.vo; - -import com.alist.api.modules.migration.alist.user.vo.AlistUserVo; -import com.alist.api.modules.migration.eltown.user.vo.EltownUserVo; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "이전 회원 조회 응답") -@Getter -@Setter -public class UserMigrationListVo { - @Schema(description = "ALIST 이전 회원 목록") - List alistUserList; - - @Schema(description = "ELTOWN 이전 회원 목록") - List eltownUserList; -} diff --git a/src/main/java/com/alist/api/modules/front/user/vo/UserPasswordCheckVo.java b/src/main/java/com/alist/api/modules/front/user/vo/UserPasswordCheckVo.java deleted file mode 100644 index 197ee38..0000000 --- a/src/main/java/com/alist/api/modules/front/user/vo/UserPasswordCheckVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.front.user.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 비밀번호 확인 응답") -@Getter -@Setter -public class UserPasswordCheckVo { - @Schema(description = "비밀번호 일치 여부", example = "true") - private boolean passwordMatched; - - @JsonIgnore - @Schema(hidden = true) - private Integer resultCode; -} diff --git a/src/main/java/com/alist/api/modules/front/user/vo/UserPasswordModifyVo.java b/src/main/java/com/alist/api/modules/front/user/vo/UserPasswordModifyVo.java deleted file mode 100644 index 5767d7d..0000000 --- a/src/main/java/com/alist/api/modules/front/user/vo/UserPasswordModifyVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.front.user.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 비밀번호 수정 응답") -@Getter -@Setter -public class UserPasswordModifyVo { - @Schema(description = "비밀번호 수정 여부", example = "true") - private boolean passwordChanged; - - @JsonIgnore - @Schema(hidden = true) - private Integer resultCode; -} diff --git a/src/main/java/com/alist/api/modules/front/user/vo/UserProfileModifyVo.java b/src/main/java/com/alist/api/modules/front/user/vo/UserProfileModifyVo.java deleted file mode 100644 index 72a166e..0000000 --- a/src/main/java/com/alist/api/modules/front/user/vo/UserProfileModifyVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.front.user.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 개인정보 수정 응답") -@Getter -@Setter -public class UserProfileModifyVo { - @Schema(description = "개인정보 수정 여부", example = "true") - private boolean profileUpdated; - - @JsonIgnore - @Schema(hidden = true) - private Integer resultCode; -} diff --git a/src/main/java/com/alist/api/modules/front/user/vo/UserProfileViewVo.java b/src/main/java/com/alist/api/modules/front/user/vo/UserProfileViewVo.java deleted file mode 100644 index 4fe606c..0000000 --- a/src/main/java/com/alist/api/modules/front/user/vo/UserProfileViewVo.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.front.user.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 개인정보 조회 응답") -@Getter -@Setter -public class UserProfileViewVo { - @Schema(description = "회원 PK", example = "1") - private Integer userIdx; - - @Schema(description = "회원 아이디", example = "test") - private String id; - - @Schema(description = "이메일", example = "user@example.com") - private String email; - - @Schema(description = "휴대폰 번호", example = "01012345678") - private String hp; - - @Schema(description = "회원 권한 코드", example = "SCM") - private String userRole; - - @Schema(description = "회원 유형 코드. T: 교사, S: 학생", example = "S") - private String userType; -} diff --git a/src/main/java/com/alist/api/modules/front/user/vo/UserVo.java b/src/main/java/com/alist/api/modules/front/user/vo/UserVo.java deleted file mode 100644 index 6a70500..0000000 --- a/src/main/java/com/alist/api/modules/front/user/vo/UserVo.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.alist.api.modules.front.user.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 회원가입 응답") -@Setter -@Getter -public class UserVo { - @Schema(description = "회원 PK", example = "1") - private Integer userIdx; - - @Schema(description = "회원 아이디", example = "test") - private String id; - - @Schema(description = "회원 권한 코드", example = "CCD/SCM/I/R/E") - private String userRole; - - @Schema(description = "회원 유형 코드. T: 교사, S: 학생", example = "S") - private String userType; - - @JsonIgnore - @Schema(hidden = true) - private String newPassword; - @JsonIgnore - @Schema(hidden = true) - private String password; - @JsonIgnore - @Schema(hidden = true) - private int resultCode; -} diff --git a/src/main/java/com/alist/api/modules/front/user/vo/UserWithdrawModifyVo.java b/src/main/java/com/alist/api/modules/front/user/vo/UserWithdrawModifyVo.java deleted file mode 100644 index e725fc4..0000000 --- a/src/main/java/com/alist/api/modules/front/user/vo/UserWithdrawModifyVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.front.user.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "사용자 탈퇴 상태 변경 응답") -@Getter -@Setter -public class UserWithdrawModifyVo { - @Schema(description = "탈퇴 상태 변경 여부", example = "true") - private boolean withdrawStatusUpdated; - - @JsonIgnore - @Schema(hidden = true) - private Integer resultCode; -} diff --git a/src/main/java/com/alist/api/modules/migration/alist/user/mapper/AlistUserMapper.java b/src/main/java/com/alist/api/modules/migration/alist/user/mapper/AlistUserMapper.java deleted file mode 100644 index 9158eba..0000000 --- a/src/main/java/com/alist/api/modules/migration/alist/user/mapper/AlistUserMapper.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.alist.api.modules.migration.alist.user.mapper; - -import com.alist.api.modules.migration.alist.user.dto.AlistUserDto; -import com.alist.api.modules.migration.alist.user.vo.AlistUserVo; - -import java.util.List; - -public interface AlistUserMapper { - List selectAlistUserList(AlistUserDto alistUserDto); -} diff --git a/src/main/java/com/alist/api/modules/migration/alist/user/service/AlistUserService.java b/src/main/java/com/alist/api/modules/migration/alist/user/service/AlistUserService.java deleted file mode 100644 index c5fff65..0000000 --- a/src/main/java/com/alist/api/modules/migration/alist/user/service/AlistUserService.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.alist.api.modules.migration.alist.user.service; - -import com.alist.api.modules.migration.alist.user.dto.AlistUserDto; -import com.alist.api.modules.migration.alist.user.mapper.AlistUserMapper; -import com.alist.api.modules.migration.alist.user.vo.AlistUserVo; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Service -public class AlistUserService { - private final AlistUserMapper alistUserMapper; - - public AlistUserService(AlistUserMapper alistUserMapper) { - this.alistUserMapper = alistUserMapper; - } - - public List selectAlistUserList(AlistUserDto alistUserDto) { - return alistUserMapper.selectAlistUserList(alistUserDto); - } -} diff --git a/src/main/java/com/alist/api/modules/migration/alist/user/vo/AlistUserVo.java b/src/main/java/com/alist/api/modules/migration/alist/user/vo/AlistUserVo.java deleted file mode 100644 index 9868787..0000000 --- a/src/main/java/com/alist/api/modules/migration/alist/user/vo/AlistUserVo.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.migration.alist.user.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "ALIST 이전 회원 정보") -@Getter -@Setter -public class AlistUserVo { - @Schema(description = "이전 회원 아이디", example = "test") - private String id; - - @Schema(description = "이전 회원 이름", example = "홍길동") - private String name; - - @Schema(description = "이전 회원 휴대폰 번호", example = "01012345678") - private String hp; - - @Schema(description = "이전 회원 이메일", example = "user@example.com") - private String email; - - @Schema(description = "이전 회원 구분", example = "T/S") - private String type; - - @Schema(description = "이전 회원 가입일", example = "2026-05-19") - private String joinDate; -} diff --git a/src/main/java/com/alist/api/modules/migration/eltown/user/mapper/EltownUserMapper.java b/src/main/java/com/alist/api/modules/migration/eltown/user/mapper/EltownUserMapper.java deleted file mode 100644 index 6ecd317..0000000 --- a/src/main/java/com/alist/api/modules/migration/eltown/user/mapper/EltownUserMapper.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.alist.api.modules.migration.eltown.user.mapper; - -import com.alist.api.modules.migration.eltown.user.dto.EltownUserDto; -import com.alist.api.modules.migration.eltown.user.vo.EltownUserVo; - -import java.util.List; - -public interface EltownUserMapper { - List selectEltownUserList(EltownUserDto eltownUserDto); -} diff --git a/src/main/java/com/alist/api/modules/migration/eltown/user/service/EltownUserService.java b/src/main/java/com/alist/api/modules/migration/eltown/user/service/EltownUserService.java deleted file mode 100644 index 6a0f45c..0000000 --- a/src/main/java/com/alist/api/modules/migration/eltown/user/service/EltownUserService.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.alist.api.modules.migration.eltown.user.service; - -import com.alist.api.common.utils.HashUtils; -import com.alist.api.modules.migration.eltown.user.dto.EltownUserDto; -import com.alist.api.modules.migration.eltown.user.mapper.EltownUserMapper; -import com.alist.api.modules.migration.eltown.user.vo.EltownUserVo; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Slf4j -@Service -public class EltownUserService { - private final EltownUserMapper eltownUserMapper; - - public EltownUserService(EltownUserMapper eltownUserMapper) { - this.eltownUserMapper = eltownUserMapper; - } - - public List selectEltownUserList(EltownUserDto eltownUserDto) { - eltownUserDto.setMd5Password(HashUtils.md5(eltownUserDto.getPassword())); - return eltownUserMapper.selectEltownUserList(eltownUserDto); - } -} diff --git a/src/main/java/com/alist/api/modules/migration/eltown/user/vo/EltownUserVo.java b/src/main/java/com/alist/api/modules/migration/eltown/user/vo/EltownUserVo.java deleted file mode 100644 index b60962a..0000000 --- a/src/main/java/com/alist/api/modules/migration/eltown/user/vo/EltownUserVo.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.migration.eltown.user.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "ELTOWN 이전 회원 정보") -@Getter -@Setter -public class EltownUserVo { - @Schema(description = "이전 회원 아이디", example = "test") - private String id; - - @Schema(description = "이전 회원 이름", example = "홍길동") - private String name; - - @Schema(description = "이전 회원 휴대폰 번호", example = "01012345678") - private String hp; - - @Schema(description = "이전 회원 이메일", example = "user@example.com") - private String email; - - @Schema(description = "이전 회원 구분", example = "S") - private String type; - - @Schema(description = "이전 회원 가입일", example = "2026-05-19") - private String joinDate; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/TusFileController.java b/src/main/java/com/alist/api/modules/tusFile/TusFileController.java deleted file mode 100644 index 37bbf04..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/TusFileController.java +++ /dev/null @@ -1,291 +0,0 @@ -package com.alist.api.modules.tusFile; - -import com.alist.api.common.response.ApiResponse; -import com.alist.api.common.response.ApiResponseCode; -import com.alist.api.common.utils.SecurityUtil; -import com.alist.api.modules.tusFile.dto.*; -import com.alist.api.modules.tusFile.form.*; -import com.alist.api.modules.tusFile.service.TusFileService; -import com.alist.api.modules.tusFile.vo.*; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.validation.Valid; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.io.InputStreamResource; -import org.springframework.core.io.Resource; -import org.springframework.http.ContentDisposition; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; - -@Slf4j -@Tag( - name = "903. TUS 업로드" - , description = "TUS 기반 파일 업로드 초기화, 권한 검증, 상태 조회, 훅 처리, 취소 API" -) -@RestController -@RequestMapping("/tus/file") -public class TusFileController { - private final TusFileService tusFileService; - - public TusFileController(TusFileService tusFileService) { - this.tusFileService = tusFileService; - } - - @Operation( - summary = "파일 업로드 초기화" - , description = "업로드 대상 파일 정보를 DB에 등록하고 fileUuid/uploadToken을 발급합니다." - ) - @PostMapping("/upload/init") - public ResponseEntity> tusFileUploadInit( - @Valid @RequestBody TusFileUploadInitForm tusFileUploadInitForm - ) { - TusFileUploadInitDto tusFileUploadInitDto = tusFileUploadInitForm.toDto(); - - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (userTokenIdx == null) { - return ApiResponse.entity(new TusFileUploadInitVo(), ApiResponseCode.CODE_401); - } - - Integer userIdx = tusFileService.selectUserIdxByUserTokenIdx(userTokenIdx); - - if (userIdx == null) { - return ApiResponse.entity(new TusFileUploadInitVo(), ApiResponseCode.CODE_401); - } - - tusFileUploadInitDto.setUserIdx(userIdx); - tusFileUploadInitDto.setUserTokenIdx(userTokenIdx); - - TusFileUploadInitVo tusFileUploadInitVo = tusFileService.insertTusFileUploadInit(tusFileUploadInitDto); - - return ApiResponse.entity(tusFileUploadInitVo, ApiResponseCode.CODE_200); - } - - @Operation( - summary = "업로드 권한 검증" - , description = "Bearer 업로드 토큰과 fileUuid(및 TUS 메타데이터)를 검증해 업로드 가능 여부를 확인합니다." - ) - @GetMapping("/upload/auth") - public ResponseEntity> tusFileUploadAuth( - @RequestHeader(value = "Authorization", required = false) String uploadToken - , @RequestHeader(value = "x-File-Uuid", required = false) String fileUuid - , @RequestHeader(value = "Upload-Metadata", required = false) String uploadMetadata - , @RequestHeader(value = "Upload-Length", required = false) String uploadLength - ) { - if (uploadToken == null || !uploadToken.startsWith("Bearer ")) { - return ApiResponse.entity("", ApiResponseCode.CODE_401); - } - if (fileUuid == null || fileUuid.isBlank()) { - return ApiResponse.entity("", ApiResponseCode.CODE_403); - } - - TusFileUploadAuthDto tusFileUploadAuthDto = new TusFileUploadAuthDto(); - tusFileUploadAuthDto.setUploadToken(uploadToken.substring(7).trim()); - tusFileUploadAuthDto.setFileUuid(fileUuid.trim()); - tusFileUploadAuthDto.setUploadMetadataRaw(uploadMetadata); - tusFileUploadAuthDto.setUploadLengthRaw(uploadLength); - - TusFileUploadAuthVo tusFileUploadAuthVo = tusFileService.selectTusFileUploadAuth(tusFileUploadAuthDto); - - if (tusFileUploadAuthVo.getResultCode() == 401) { - return ApiResponse.entity("", ApiResponseCode.CODE_401); - } else if (tusFileUploadAuthVo.getResultCode() == 403) { - return ApiResponse.entity("", ApiResponseCode.CODE_403); - } - - return ApiResponse.entity("", ApiResponseCode.CODE_200); - } - - @Operation( - summary = "업로드 상태 조회" - , description = "fileUuid 기준으로 Redis/DB 업로드 상태(진행률, 상태값)를 조회합니다." - ) - @PostMapping("/upload/status") - public ResponseEntity> tusFileUploadStatus( - @Valid @RequestBody TusFileUploadStatusForm tusFileUploadStatusForm - ) { - TusFileUploadStatusVo tusFileUploadStatusVo = tusFileService.loadTusFileUploadStatus(tusFileUploadStatusForm.toDto()); - - if (tusFileUploadStatusVo == null) { - return ApiResponse.entity(new TusFileUploadStatusVo(), ApiResponseCode.CODE_2003); - } - - return ApiResponse.entity(tusFileUploadStatusVo, ApiResponseCode.CODE_200); - } - - @Operation( - summary = "TUS 훅 수신" - , description = "tusd에서 전달되는 pre-create/post-receive/post-finish/post-terminate 이벤트를 반영합니다." - ) - @PostMapping("/hook") - public ResponseEntity> tusFileHook(@RequestBody TusFileHookForm tusFileHookForm) { - TusFileHookDto tusFileHookDto = tusFileHookForm.toDto(); - - boolean accepted = tusFileService.updateTusFileHook(tusFileHookDto); - - if (accepted) { - tusFileService.saveTusFileHookStatusRedis(tusFileHookDto); - } - - return ApiResponse.entity("", ApiResponseCode.CODE_200); - } - - @Operation( - summary = "업로드 취소" - , description = "현재 로그인 사용자의 fileUuid 업로드를 취소(CANCELED) 처리합니다." - ) - @PostMapping("/upload/cancel") - public ResponseEntity> tusFileUploadCancel( - @Valid @RequestBody TusFileUploadCancelForm tusFileUploadCancelForm - ) { - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - - if (userTokenIdx == null) { - return ApiResponse.entity("", ApiResponseCode.CODE_401); - } - - TusFileUploadCancelDto tusFileUploadCancelDto = tusFileUploadCancelForm.toDto(); - tusFileUploadCancelDto.setUserTokenIdx(userTokenIdx); - - TusFileUploadCancelVo tusFileUploadCancelVo = tusFileService.updateTusFileUploadCancel(tusFileUploadCancelDto); - - if (!tusFileUploadCancelVo.isCanceled()) { - return ApiResponse.entity("", ApiResponseCode.CODE_2005, "업로드 취소"); - } - - return ApiResponse.entity("", ApiResponseCode.CODE_200); - } - - @Operation( - summary = "파일 목록 조회" - , description = "FILE_MASTER_IDX 기준으로 완료된 파일 목록을 조회합니다." - ) - @GetMapping("/list/{fileMasterIdx}") - public ResponseEntity> tusFileList( - @PathVariable Long fileMasterIdx - ) { - TusFileListDto tusFileListDto = new TusFileListDto(); - tusFileListDto.setFileMasterIdx(fileMasterIdx); - - TusFileListVo tusFileListVo = tusFileService.selectTusFileList(tusFileListDto); - - if (tusFileListVo.getResultCode() == 401) { - return ApiResponse.entity(tusFileListVo, ApiResponseCode.CODE_401); - } - - return ApiResponse.entity(tusFileListVo, ApiResponseCode.CODE_2001, "파일"); - } - - @Operation( - summary = "파일 보기" - , description = "권한 확인 후 Nginx X-Accel-Redirect로 파일을 inline 조회합니다." - ) - @GetMapping("/view/{fileUuid}") - public ResponseEntity tusFileView( - @PathVariable String fileUuid - , HttpServletRequest request - ) throws IOException { - TusFileViewOrDownloadDto tusFileViewOrDownloadDto = new TusFileViewOrDownloadDto(); - tusFileViewOrDownloadDto.setFileUuid(fileUuid); - tusFileViewOrDownloadDto.setEventType("VIEW"); - tusFileViewOrDownloadDto.setClientIp(request.getRemoteAddr()); - tusFileViewOrDownloadDto.setUserAgent(request.getHeader("User-Agent")); - tusFileViewOrDownloadDto.setReferer(request.getHeader("Referer")); - - TusFileViewOrDownloadVo tusFileViewOrDownloadVo = tusFileService.selectTusFileViewOrDownload(tusFileViewOrDownloadDto); - - if (tusFileViewOrDownloadVo == null) { - return ResponseEntity.notFound().build(); - } - - Path filePath = tusFileService.resolveStoredFilePath(tusFileViewOrDownloadVo.getSavePath()); - - if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) { - return ResponseEntity.notFound().build(); - } - - Resource resource = new InputStreamResource(Files.newInputStream(filePath)); - String contentType = tusFileViewOrDownloadVo.getContentType() == null - ? "application/octet-stream" - : tusFileViewOrDownloadVo.getContentType(); - - return ResponseEntity.ok() - .contentType(MediaType.parseMediaType(contentType)) - .contentLength(Files.size(filePath)) - .header(HttpHeaders.CONTENT_DISPOSITION, - ContentDisposition.inline() - .filename(tusFileViewOrDownloadVo.getOriginName(), StandardCharsets.UTF_8) - .build() - .toString()) - .body(resource); - } - - @Operation( - summary = "파일 다운로드" - , description = "권한 확인 후 파일 다운로드 로그를 남기고 redirect 또는 Nginx X-Accel-Redirect로 파일 다운로드 합니다." - ) - @GetMapping("/download/{fileUuid}") - public ResponseEntity tusFileDownload( - @PathVariable String fileUuid - , HttpServletRequest request - ) throws IOException { - TusFileViewOrDownloadDto tusFileViewOrDownloadDto = new TusFileViewOrDownloadDto(); - tusFileViewOrDownloadDto.setFileUuid(fileUuid); - tusFileViewOrDownloadDto.setEventType("DOWNLOAD"); - tusFileViewOrDownloadDto.setClientIp(request.getRemoteAddr()); - tusFileViewOrDownloadDto.setUserAgent(request.getHeader("User-Agent")); - tusFileViewOrDownloadDto.setReferer(request.getHeader("Referer")); - - TusFileViewOrDownloadVo tusFileViewOrDownloadVo = tusFileService.selectTusFileViewOrDownload(tusFileViewOrDownloadDto); - - if (tusFileViewOrDownloadVo == null) { - return ResponseEntity.notFound().build(); - } - - Path filePath = tusFileService.resolveStoredFilePath(tusFileViewOrDownloadVo.getSavePath()); - - if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) { - return ResponseEntity.notFound().build(); - } - - Resource resource = new InputStreamResource(Files.newInputStream(filePath)); - String contentType = tusFileViewOrDownloadVo.getContentType() == null - ? "application/octet-stream" - : tusFileViewOrDownloadVo.getContentType(); - - return ResponseEntity.ok() - .contentType(MediaType.parseMediaType(contentType)) - .contentLength(Files.size(filePath)) - .header(HttpHeaders.CONTENT_DISPOSITION, - ContentDisposition.attachment() - .filename(tusFileViewOrDownloadVo.getOriginName(), StandardCharsets.UTF_8) - .build() - .toString()) - .body(resource); - } - - @Operation( - summary = "파일 삭제 처리" - , description = "fileUuid 기준으로 완료된 파일(STATUS = 3)을 논리 삭제 처리합니다. 실제 파일은 삭제하지 않고 DEL_YN = 'Y'만 반영합니다." - ) - @PostMapping("/delete") - public ResponseEntity> tusFileDelete( - @Valid @RequestBody TusFileDeleteForm tusFileDeleteForm - ) { - TusFileDeleteVo tusFileDeleteVo = tusFileService.updateTusFileDelete(tusFileDeleteForm.toDto()); - - if (!tusFileDeleteVo.isDeleted()) { - return ApiResponse.entity("", ApiResponseCode.CODE_2005, "파일 삭제"); - } - - return ApiResponse.entity("", ApiResponseCode.CODE_200); - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileDeleteDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileDeleteDto.java deleted file mode 100644 index ab02748..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileDeleteDto.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 파일 삭제 DTO") -@Getter -@Setter -public class TusFileDeleteDto { - @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") - private String fileUuid; - - @Schema(hidden = true) - private Integer userTokenIdx; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileHookDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileHookDto.java deleted file mode 100644 index d7d21ec..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileHookDto.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class TusFileHookDto { - private String type; - private String uploadId; - private Long size; - private Long offset; - private String fileUuid; - private String filename; - private String filetype; - private Long uploadedBytes; - private Integer status; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileListDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileListDto.java deleted file mode 100644 index 74540ee..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileListDto.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 파일 목록 조회 DTO") -@Getter -@Setter -public class TusFileListDto { - @Schema(description = "파일 마스터 PK", example = "1") - private Long fileMasterIdx; - - @Schema(hidden = true) - private Integer userTokenIdx; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileMoveTaskDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileMoveTaskDto.java deleted file mode 100644 index 628c7e6..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileMoveTaskDto.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class TusFileMoveTaskDto { - private Long fileDetailIdx; - private Long fileMasterIdx; - private String fileUuid; - private String tusUploadId; - private String originName; - private String contentType; - private Long sizeBytes; - private Long uploadedBytes; - - private String folderPath; - - private String saveName; - private String savePath; - private String ext; - - private String moveYn; - private Integer moveTryCount; - private String moveLastError; - private String moveLockOwner; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadAuthDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadAuthDto.java deleted file mode 100644 index 8d65702..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadAuthDto.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "업로드 인증 토큰 정보") -@Getter -@Setter -public class TusFileUploadAuthDto { - private Integer userTokenIdx; - - @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") - private String fileUuid; - - @Schema(description = "업로드 토큰") - private String uploadToken; - - private String uploadMetadataRaw; - private String uploadLengthRaw; - - @Schema(description = "원본 파일명", example = "sample.png") - private String originName; - - @Schema(description = "파일 크기(byte)", example = "102400") - private Long sizeBytes; - - @Schema(description = "Content-Type", example = "image/png") - private String contentType; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadCancelDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadCancelDto.java deleted file mode 100644 index 2420aa7..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadCancelDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class TusFileUploadCancelDto { - private Integer userTokenIdx; - private String fileUuid; - - private Long uploadedBytes; - private Long sizeBytes; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitDto.java deleted file mode 100644 index ad91f86..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitDto.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "TUS 파일 업로드 초기화 응답") -@Getter -@Setter -public class TusFileUploadInitDto { - @Schema(description = "파일 마스터 PK", example = "1") - private Long fileMasterIdx; - - @Schema(description = "TUS 업로드 엔드포인트", example = "/tus/file/upload") - private String tusEndpoint; - - @JsonIgnore - @Schema(hidden = true) - private String fileCategory; - - @JsonIgnore - @Schema(hidden = true) - private String folderPath; - - @JsonIgnore - @Schema(hidden = true) - private Integer userTokenIdx; - - @JsonIgnore - @Schema(hidden = true) - private Integer userIdx; - - @JsonIgnore - @Schema(hidden = true) - private Integer status; - - @JsonIgnore - @Schema(hidden = true) - private Integer totalCount; - - @Schema(description = "업로드 파일 항목 목록") - private List itemList; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitItemDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitItemDto.java deleted file mode 100644 index 633297a..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitItemDto.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 업로드 파일 항목 응답") -@Getter -@Setter -public class TusFileUploadInitItemDto { - @Schema(description = "파일 순번", example = "1") - private Integer fileSeq; - - @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") - private String fileUuid; - - @Schema(description = "TUS 업로드 토큰") - private String uploadToken; - - @Schema(description = "원본 파일명", example = "sample.png") - private String originName; - - @Schema(description = "파일 크기(byte)", example = "102400") - private Long sizeBytes; - - @Schema(description = "Content-Type", example = "image/png") - private String contentType; - - @JsonIgnore - @Schema(hidden = true) - private Long fileMasterIdx; - @JsonIgnore - @Schema(hidden = true) - private Integer userIdx; - @JsonIgnore - @Schema(hidden = true) - private Integer status; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadMetaDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadMetaDto.java deleted file mode 100644 index 477236e..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadMetaDto.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class TusFileUploadMetaDto { - private String fileUuid; - private String originName; - private Long sizeBytes; - private String contentType; - private String tusUploadId; - private Integer status; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadStatusDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadStatusDto.java deleted file mode 100644 index 4ea1285..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadStatusDto.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class TusFileUploadStatusDto { - private String fileUuid; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileViewOrDownloadDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileViewOrDownloadDto.java deleted file mode 100644 index e0d4e43..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileViewOrDownloadDto.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 파일 보기/다운로드 조회 DTO") -@Getter -@Setter -public class TusFileViewOrDownloadDto { - @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") - private String fileUuid; - - @Schema(hidden = true) - private Integer userTokenIdx; - - @Schema(hidden = true) - private String eventType; - - @Schema(hidden = true) - private String clientIp; - - @Schema(hidden = true) - private String userAgent; - - @Schema(hidden = true) - private String referer; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileDeleteForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileDeleteForm.java deleted file mode 100644 index 2d3337e..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusFileDeleteForm.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.alist.api.modules.tusFile.dto.TusFileDeleteDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "파일 삭제 요청 폼") -@Getter -@Setter -public class TusFileDeleteForm { - @Schema( - description = "파일 UUID" - , example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "파일 UUID는 필수입니다.") - private String fileUuid; - - public TusFileDeleteDto toDto() { - TusFileDeleteDto tusFileDeleteDto = new TusFileDeleteDto(); - tusFileDeleteDto.setFileUuid(fileUuid == null ? null : fileUuid.trim()); - return tusFileDeleteDto; - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookEventForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookEventForm.java deleted file mode 100644 index 0b6f1a2..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookEventForm.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 훅 이벤트 상세 정보") -@Getter -@Setter -@JsonIgnoreProperties(ignoreUnknown = true) -public class TusFileHookEventForm { - @Schema(description = "TUS 업로드 정보") - @JsonProperty("Upload") - private TusFileHookUploadForm tusFileHookUploadForm; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookForm.java deleted file mode 100644 index ff9a2c7..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookForm.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.alist.api.modules.tusFile.dto.TusFileHookDto; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 훅 요청 폼") -@Getter -@Setter -@JsonIgnoreProperties(ignoreUnknown = true) -public class TusFileHookForm { - @Schema(description = "TUS 훅 이벤트 타입", example = "post-finish") - @JsonProperty("Type") - private String type; - - @Schema(description = "TUS 훅 이벤트 정보") - @JsonProperty("Event") - private TusFileHookEventForm tusFileHookEventForm; - - public TusFileHookDto toDto() { - TusFileHookDto tusFileHookDto = new TusFileHookDto(); - - tusFileHookDto.setType(this.type); - - if (this.tusFileHookEventForm == null || this.tusFileHookEventForm.getTusFileHookUploadForm() == null) { - return tusFileHookDto; - } - - TusFileHookUploadForm upload = this.tusFileHookEventForm.getTusFileHookUploadForm(); - tusFileHookDto.setUploadId(upload.getId()); - tusFileHookDto.setSize(upload.getSize()); - tusFileHookDto.setOffset(upload.getOffset()); - - if (upload.getMetaData() != null) { - tusFileHookDto.setFileUuid(upload.getMetaData().get("fileUuid")); - tusFileHookDto.setFilename(upload.getMetaData().get("filename")); - tusFileHookDto.setFiletype(upload.getMetaData().get("filetype")); - } - - return tusFileHookDto; - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookUploadForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookUploadForm.java deleted file mode 100644 index c4bcf7f..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookUploadForm.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.Map; - -@Schema(description = "TUS 훅 업로드 정보") -@Getter -@Setter -@JsonIgnoreProperties(ignoreUnknown = true) -public class TusFileHookUploadForm { - @Schema(description = "TUS 업로드 ID", example = "018f6f2d7b5c7d499c3a8c0f4f9f9f10") - @JsonProperty("ID") - private String id; - - @Schema(description = "업로드 파일 전체 크기(byte)", example = "102400") - @JsonProperty("Size") - private Long size; - - @Schema(description = "현재 업로드 offset(byte)", example = "102400") - @JsonProperty("Offset") - private Long offset; - - @Schema( - description = "TUS 메타데이터. fileUuid, filename, filetype 등을 포함합니다.", - example = "{\"fileUuid\":\"018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10\",\"filename\":\"sample.png\",\"filetype\":\"image/png\"}" - ) - @JsonProperty("MetaData") - private Map metaData; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadCancelForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadCancelForm.java deleted file mode 100644 index 6ed4cba..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadCancelForm.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.alist.api.modules.tusFile.dto.TusFileUploadCancelDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "업로드 취소 요청 폼") -@Getter -@Setter -public class TusFileUploadCancelForm { - @Schema( - description = "파일 UUID" - , example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "파일 UUID는 필수입니다.") - private String fileUuid; - - public TusFileUploadCancelDto toDto() { - TusFileUploadCancelDto tusFileUploadCancelDto = new TusFileUploadCancelDto(); - tusFileUploadCancelDto.setFileUuid(this.fileUuid == null ? null : this.fileUuid.trim()); - return tusFileUploadCancelDto; - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitForm.java deleted file mode 100644 index d9a0d0b..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitForm.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.alist.api.modules.tusFile.dto.TusFileUploadInitDto; -import com.alist.api.modules.tusFile.dto.TusFileUploadInitItemDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; -import lombok.Getter; -import lombok.Setter; - -import java.util.ArrayList; -import java.util.List; - -@Schema(description = "TUS 파일 업로드 초기화 요청 폼") -@Getter -@Setter -public class TusFileUploadInitForm { - @Schema( - description = "파일 카테고리" - , example = "BOARD" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "파일 카테고리는 필수입니다.") - private String fileCategory; - - @Schema(description = "업로드 폴더 경로. 미전달 시 / 로 처리됩니다.", example = "/board") - private String folderPath; - - @Schema( - description = "업로드 파일 목록" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotEmpty(message = "업로드 파일 목록은 필수입니다.") - @Valid - private List itemList; - - public TusFileUploadInitDto toDto() { - TusFileUploadInitDto tusFileUploadInitDto = new TusFileUploadInitDto(); - - tusFileUploadInitDto.setFileCategory(this.fileCategory.trim()); - tusFileUploadInitDto.setFolderPath((this.folderPath == null || this.folderPath.isBlank()) ? "/" : this.folderPath.trim()); - - List tusFileUploadInitItemDtoList = new ArrayList<>(); - - int i = 1; - for (TusFileUploadInitItemForm item : this.itemList) { - TusFileUploadInitItemDto tusFileUploadInitItemDto = new TusFileUploadInitItemDto(); - - tusFileUploadInitItemDto.setFileSeq(i++); - tusFileUploadInitItemDto.setOriginName(item.getOriginName()); - tusFileUploadInitItemDto.setSizeBytes(item.getSizeBytes()); - tusFileUploadInitItemDto.setContentType(item.getContentType()); - - tusFileUploadInitItemDtoList.add(tusFileUploadInitItemDto); - } - - tusFileUploadInitDto.setItemList(tusFileUploadInitItemDtoList); - - return tusFileUploadInitDto; - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitItemForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitItemForm.java deleted file mode 100644 index e0870c4..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitItemForm.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Positive; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 업로드 파일 항목") -@Getter -@Setter -public class TusFileUploadInitItemForm { - @Schema( - description = "원본 파일명" - , example = "sample.png" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "원본 파일명은 필수입니다.") - private String originName; - - @Schema( - description = "파일 크기(byte)" - , example = "102400" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotNull(message = "파일 크기는 필수입니다.") - @Positive(message = "파일 크기는 0보다 커야 합니다.") - private Long sizeBytes; - - @Schema(description = "Content-Type", example = "image/png") - private String contentType; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadStatusForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadStatusForm.java deleted file mode 100644 index 240be16..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadStatusForm.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.alist.api.modules.tusFile.dto.TusFileUploadStatusDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "업로드 상태 조회 요청 폼") -@Getter -@Setter -public class TusFileUploadStatusForm { - @Schema( - description = "파일 UUID" - , example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10" - , requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "파일 UUID는 필수입니다.") - private String fileUuid; - - public TusFileUploadStatusDto toDto() { - TusFileUploadStatusDto tusFileUploadStatusDto = new TusFileUploadStatusDto(); - tusFileUploadStatusDto.setFileUuid(this.fileUuid == null ? null : this.fileUuid.trim()); - return tusFileUploadStatusDto; - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/mapper/TusFileMapper.java b/src/main/java/com/alist/api/modules/tusFile/mapper/TusFileMapper.java deleted file mode 100644 index 98b4c8b..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/mapper/TusFileMapper.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.alist.api.modules.tusFile.mapper; - -import com.alist.api.modules.tusFile.dto.*; -import com.alist.api.modules.tusFile.vo.TusFileListItemVo; -import com.alist.api.modules.tusFile.vo.TusFileUploadCancelProgressVo; -import com.alist.api.modules.tusFile.vo.TusFileViewOrDownloadVo; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface TusFileMapper { - void insertTusFileUploadInitMaster(TusFileUploadInitDto tusFileUploadInitDto); - - void insertTusFileUploadInitDetail(TusFileUploadInitItemDto tusFileUploadInitItemDto); - - int selectTusFileUploadAuthCount(TusFileUploadAuthDto tusFileUploadAuthDto); - - void insertTusFileHookEventLog(TusFileHookDto tusFileHookDto); - - void updateTusFileHookDetailStatus(TusFileHookDto tusFileHookDto); - - void updateFileMasterAggregateByFileUuid(String fileUuid); - - TusFileUploadMetaDto selectTusFileUploadMeta(String fileUuid); - - void updateTusFileUploadIdIfNull(TusFileUploadMetaDto tusFileUploadMetaDto); - - void updateFileDetailCanceledByFileUuid(String fileUuid); - - int selectTusFileUploadCancelCount(TusFileUploadCancelDto tusFileUploadCancelDto); - - TusFileUploadCancelProgressVo selectTusFileUploadCancelProgress(TusFileUploadCancelDto tusFileUploadCancelDto); - - TusFileMoveTaskDto selectTusFileMoveTask(String fileUuid); - - int updateTusFileMoveSuccess(TusFileMoveTaskDto tusFileMoveTaskDto); - - int updateTusFileMovePending(TusFileMoveTaskDto tusFileMoveTaskDto); - - List selectTusFileList(TusFileListDto tusFileListDto); - - TusFileViewOrDownloadVo selectTusFileViewOrDownload(TusFileViewOrDownloadDto tusFileViewOrDownloadDto); - - void insertTusFileViewOrDownloadEventLog(TusFileViewOrDownloadVo tusFileViewOrDownloadVo); - - int selectTusFileDeleteTargetCount(TusFileDeleteDto tusFileDeleteDto); - - int updateTusFileDetailDelete(TusFileDeleteDto tusFileDeleteDto); - - int updateTusFileMasterDelete(TusFileDeleteDto tusFileDeleteDto); - - Integer selectUserIdxByUserTokenIdx(Integer userTokenIdx); - - int updateTusFileUploadCancel(TusFileUploadCancelDto tusFileUploadCancelDto); -} diff --git a/src/main/java/com/alist/api/modules/tusFile/service/TusFileService.java b/src/main/java/com/alist/api/modules/tusFile/service/TusFileService.java deleted file mode 100644 index 4926498..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/service/TusFileService.java +++ /dev/null @@ -1,760 +0,0 @@ -package com.alist.api.modules.tusFile.service; - -import com.alist.api.common.utils.SecurityUtil; -import com.alist.api.config.jwt.JwtTokenProvider; -import com.alist.api.modules.tusFile.dto.*; -import com.alist.api.modules.tusFile.mapper.TusFileMapper; -import com.alist.api.modules.tusFile.vo.*; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.redis.core.HashOperations; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.*; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.util.*; - -@Slf4j -@Service -public class TusFileService { - @Value("${tus-file.upload.tus-endpoint}") - private String tusEndpoint; - - @Value("${tus-file.upload.public-base-url}") - private String publicBaseUrl; - - @Value("${tus-file.upload.auth-cache.ttl-seconds:20}") - private long uploadAuthCacheTtlSeconds; - - @Value("${tus-file.upload.interrupt-seconds:30}") - private long uploadInterruptSeconds; - - @Value("${tus-file.upload.tmp-root}") - private String uploadTmpRoot; - - @Value("${tus-file.upload.final-root}") - private String uploadFinalRoot; - - private final TusFileMapper tusFileMapper; - private final JwtTokenProvider jwtTokenProvider; - private final StringRedisTemplate stringRedisTemplate; - - public TusFileService(TusFileMapper tusFileMapper, JwtTokenProvider jwtTokenProvider, StringRedisTemplate stringRedisTemplate) { - this.tusFileMapper = tusFileMapper; - this.jwtTokenProvider = jwtTokenProvider; - this.stringRedisTemplate = stringRedisTemplate; - } - - /*파일 초기화 작업*/ - @Transactional - public TusFileUploadInitVo insertTusFileUploadInit(TusFileUploadInitDto tusFileUploadInitDto) { - tusFileUploadInitDto.setStatus(0); - tusFileUploadInitDto.setTotalCount(tusFileUploadInitDto.getItemList().size()); - - tusFileMapper.insertTusFileUploadInitMaster(tusFileUploadInitDto); - tusFileUploadInitDto.setTusEndpoint(tusEndpoint); - - if (tusFileUploadInitDto.getItemList() == null || tusFileUploadInitDto.getItemList().isEmpty()) { - return toTusFileUploadInitVo(tusFileUploadInitDto); - } - - for (TusFileUploadInitItemDto tusFileUploadInitItemDto : tusFileUploadInitDto.getItemList()) { - tusFileUploadInitItemDto.setFileMasterIdx(tusFileUploadInitDto.getFileMasterIdx()); - tusFileUploadInitItemDto.setFileUuid(UUID.randomUUID().toString()); - tusFileUploadInitItemDto.setUploadToken(jwtTokenProvider.createUploadToken(tusFileUploadInitDto.getUserTokenIdx())); - tusFileUploadInitItemDto.setUserIdx(tusFileUploadInitDto.getUserIdx()); - tusFileUploadInitItemDto.setStatus(0); - - tusFileMapper.insertTusFileUploadInitDetail(tusFileUploadInitItemDto); - } - - return toTusFileUploadInitVo(tusFileUploadInitDto); - } - - private TusFileUploadInitVo toTusFileUploadInitVo(TusFileUploadInitDto tusFileUploadInitDto) { - TusFileUploadInitVo tusFileUploadInitVo = new TusFileUploadInitVo(); - - tusFileUploadInitVo.setFileMasterIdx(tusFileUploadInitDto.getFileMasterIdx()); - tusFileUploadInitVo.setTusEndpoint(tusFileUploadInitDto.getTusEndpoint()); - - List tusFileUploadInitItemVoList = new ArrayList<>(); - - if (tusFileUploadInitDto.getItemList() != null) { - for (TusFileUploadInitItemDto tusFileUploadInitItemDto : tusFileUploadInitDto.getItemList()) { - TusFileUploadInitItemVo tusFileUploadInitItemVo = new TusFileUploadInitItemVo(); - - tusFileUploadInitItemVo.setFileSeq(tusFileUploadInitItemDto.getFileSeq()); - tusFileUploadInitItemVo.setFileUuid(tusFileUploadInitItemDto.getFileUuid()); - tusFileUploadInitItemVo.setUploadToken(tusFileUploadInitItemDto.getUploadToken()); - tusFileUploadInitItemVo.setOriginName(tusFileUploadInitItemDto.getOriginName()); - tusFileUploadInitItemVo.setSizeBytes(tusFileUploadInitItemDto.getSizeBytes()); - tusFileUploadInitItemVo.setContentType(tusFileUploadInitItemDto.getContentType()); - - tusFileUploadInitItemVoList.add(tusFileUploadInitItemVo); - } - } - - tusFileUploadInitVo.setItemList(tusFileUploadInitItemVoList); - - return tusFileUploadInitVo; - } - - /*업로드 권한 체크*/ - public TusFileUploadAuthVo selectTusFileUploadAuth(TusFileUploadAuthDto tusFileUploadAuthDto) { - TusFileUploadAuthVo tusFileUploadAuthVo = new TusFileUploadAuthVo(); - - if (!jwtTokenProvider.validateToken(tusFileUploadAuthDto.getUploadToken())) { - tusFileUploadAuthVo.setResultCode(401); - return tusFileUploadAuthVo; - } - - if (!jwtTokenProvider.hasUploadScope(tusFileUploadAuthDto.getUploadToken())) { - tusFileUploadAuthVo.setResultCode(403); - return tusFileUploadAuthVo; - } - - try { - tusFileUploadAuthDto.setUserTokenIdx(Integer.parseInt(jwtTokenProvider.getUserTokenIdx(tusFileUploadAuthDto.getUploadToken()))); - } catch (NumberFormatException e) { - tusFileUploadAuthVo.setResultCode(401); - return tusFileUploadAuthVo; - } - - applyTusMeta(tusFileUploadAuthDto); - - String cacheKey = buildAuthCacheKey(tusFileUploadAuthDto); - - try { - String cached = stringRedisTemplate.opsForValue().get(cacheKey); - if (cached != null) { - try { - tusFileUploadAuthVo.setResultCode(Integer.parseInt(cached)); - return tusFileUploadAuthVo; - } catch (NumberFormatException parseEx) { - log.warn("uploadAuth cache parse fail. key={}, value={}", cacheKey, cached); - stringRedisTemplate.delete(cacheKey); - } - } - } catch (Exception redisGetEx) { - log.warn("uploadAuth cache get fail. fallback DB. key={}", cacheKey, redisGetEx); - } - - int tusFileUploadAuthCount = tusFileMapper.selectTusFileUploadAuthCount(tusFileUploadAuthDto); - int resultCode = (tusFileUploadAuthCount > 0) ? 200 : 403; - - tusFileUploadAuthVo.setResultCode(resultCode); - - try { - stringRedisTemplate.opsForValue().set( - cacheKey, - String.valueOf(resultCode), - Duration.ofSeconds(safeTtlSeconds()) - ); - } catch (Exception redisSetEx) { - log.warn("uploadAuth cache set fail. key={}, resultCode={}", cacheKey, resultCode, redisSetEx); - } - - return tusFileUploadAuthVo; - } - - private void applyTusMeta(TusFileUploadAuthDto tusFileUploadAuthDto) { - String len = tusFileUploadAuthDto.getUploadLengthRaw(); - if (len != null && !len.isBlank()) { - try { tusFileUploadAuthDto.setSizeBytes(Long.parseLong(len.trim())); } catch (Exception ignored) {} - } - - String raw = tusFileUploadAuthDto.getUploadMetadataRaw(); - if (raw == null || raw.isBlank()) return; - - for (String part : raw.split(",")) { - String p = part.trim(); - int sp = p.indexOf(' '); - if (sp <= 0) continue; - - String key = p.substring(0, sp).trim(); - String val = p.substring(sp + 1).trim(); - String decoded; - try { - decoded = new String(java.util.Base64.getDecoder().decode(val), java.nio.charset.StandardCharsets.UTF_8); - } catch (Exception e) { - continue; - } - - if ("filename".equals(key)) tusFileUploadAuthDto.setOriginName(decoded); - else if ("filetype".equals(key)) tusFileUploadAuthDto.setContentType(decoded); - } - } - - public TusFileUploadStatusVo loadTusFileUploadStatus(TusFileUploadStatusDto tusFileUploadStatusDto) { - String key = buildUploadStatusKey(tusFileUploadStatusDto.getFileUuid()); - Map map = stringRedisTemplate.opsForHash().entries(key); - - if (map.isEmpty()) { - return null; - } - - TusFileUploadStatusVo tusFileUploadStatusVo = new TusFileUploadStatusVo(); - - String fileUuid = (String) map.getOrDefault("fileUuid", tusFileUploadStatusDto.getFileUuid()); - String status = (String) map.getOrDefault("status", "PENDING"); - long uploadedBytes = parseLong(map.get("uploadedBytes")); - long totalBytes = parseLong(map.get("totalBytes")); - int percent = (int) parseLong(map.get("percent")); - String updatedAt = (String) map.getOrDefault("updatedAt", ""); - - /* - if (isStaleUploading(status, updatedAt)) { - status = "CANCELED"; - updatedAt = Instant.now().toString(); - - saveCanceledStatusRedis(fileUuid, uploadedBytes, totalBytes); - tusFileMapper.updateFileDetailCanceledByFileUuid(fileUuid); - tusFileMapper.updateFileMasterAggregateByFileUuid(fileUuid); - } - */ - - tusFileUploadStatusVo.setFileUuid(fileUuid); - tusFileUploadStatusVo.setStatus(status); - tusFileUploadStatusVo.setUploadedBytes(uploadedBytes); - tusFileUploadStatusVo.setTotalBytes(totalBytes); - tusFileUploadStatusVo.setPercent(percent); - tusFileUploadStatusVo.setUpdatedAt(updatedAt); - - return tusFileUploadStatusVo; - } - - private boolean isStaleUploading(String status, String updatedAt) { - if (!"UPLOADING".equalsIgnoreCase(status)) { - return false; - } - if (updatedAt == null || updatedAt.isBlank()) { - return false; - } - try { - Instant lastUpdatedAt = Instant.parse(updatedAt); - long elapsedSeconds = Duration.between(lastUpdatedAt, Instant.now()).getSeconds(); - return elapsedSeconds >= safeInterruptSeconds(); - } catch (Exception e) { - log.warn("upload status updatedAt parse fail. updatedAt={}", updatedAt, e); - return false; - } - } - - private long safeInterruptSeconds() { - return (uploadInterruptSeconds > 0) ? uploadInterruptSeconds : 30L; - } - - /*db에 상태값 저장*/ - @Transactional - public boolean updateTusFileHook(TusFileHookDto tusFileHookDto) { - if (tusFileHookDto == null || tusFileHookDto.getFileUuid() == null || tusFileHookDto.getFileUuid().isBlank()) { - return true; - } - - String fileUuid = tusFileHookDto.getFileUuid().trim(); - String type = tusFileHookDto.getType() == null ? "" : tusFileHookDto.getType().trim().toLowerCase(); - String uploadId = tusFileHookDto.getUploadId() == null ? "" : tusFileHookDto.getUploadId().trim(); - long offset = tusFileHookDto.getOffset() == null ? 0L : tusFileHookDto.getOffset(); - long size = tusFileHookDto.getSize() == null ? 0L : tusFileHookDto.getSize(); - - tusFileMapper.insertTusFileHookEventLog(tusFileHookDto); - - TusFileUploadMetaDto fileUploadMetaInfo = tusFileMapper.selectTusFileUploadMeta(fileUuid); - if (fileUploadMetaInfo == null) return true; - - if (isMetadataMismatch(fileUploadMetaInfo, tusFileHookDto)) { - tusFileMapper.updateFileDetailCanceledByFileUuid(fileUuid); - tusFileMapper.updateFileMasterAggregateByFileUuid(fileUuid); - saveCanceledStatusRedis(fileUuid, offset, size); - return false; - } - - if (!uploadId.isBlank()) { - if (fileUploadMetaInfo.getTusUploadId() == null || fileUploadMetaInfo.getTusUploadId().isBlank()) { - TusFileUploadMetaDto tusFileUploadMetaDto = new TusFileUploadMetaDto(); - tusFileUploadMetaDto.setFileUuid(fileUuid); - tusFileUploadMetaDto.setTusUploadId(uploadId); - tusFileMapper.updateTusFileUploadIdIfNull(tusFileUploadMetaDto); - - fileUploadMetaInfo = tusFileMapper.selectTusFileUploadMeta(fileUuid); - } - if (fileUploadMetaInfo.getTusUploadId() == null || fileUploadMetaInfo.getTusUploadId().isBlank() - || !fileUploadMetaInfo.getTusUploadId().equals(uploadId)) { - tusFileMapper.updateFileDetailCanceledByFileUuid(fileUuid); - tusFileMapper.updateFileMasterAggregateByFileUuid(fileUuid); - saveCanceledStatusRedis(fileUuid, offset, size); - return false; - } - } - - //tusHookDto.setUploadedBytes(offset); // 전체 사이즈 업데이트 삭제 - - Integer currentStatus = fileUploadMetaInfo.getStatus(); - if (currentStatus != null && (currentStatus == 3 || currentStatus == 5)) { - String t = (type == null) ? "" : type; - if (!"post-finish".equals(t) && !"post-terminate".equals(t)) { - return false; - } - } - - switch (type) { - case "pre-create": - // 이미 진행 이상이면 되돌리지 않음 - if (currentStatus != null && currentStatus >= 1) { - return false; - } - - tusFileHookDto.setUploadedBytes(null); - tusFileHookDto.setStatus(1); // UPLOADING - break; - - case "post-receive": - // DONE/CANCELED 이후 늦게 온 receive 이벤트 무시 - if (currentStatus != null && (currentStatus == 3 || currentStatus == 5)) { - return false; - } - - tusFileHookDto.setUploadedBytes(null); - tusFileHookDto.setStatus(1); // UPLOADING - break; - - case "post-finish": - tusFileHookDto.setUploadedBytes(size); - tusFileHookDto.setStatus(3); // DONE - break; - - case "post-terminate": - // 이미 DONE이면 terminate 무시 (완료 우선) - if (currentStatus != null && currentStatus == 3) { - return false; - } - tusFileHookDto.setUploadedBytes(offset); - tusFileHookDto.setStatus(5); // CANCELED - break; - - default: - return false; - } - - if (tusFileHookDto.getStatus() == 1 && currentStatus != null && currentStatus >= 3) { - return false; - } - - boolean shouldUpdateDetail = !"post-receive".equals(type); - - if (shouldUpdateDetail) { - tusFileMapper.updateTusFileHookDetailStatus(tusFileHookDto); - } - - if ("post-finish".equals(type) || "post-terminate".equals(type)) { - tusFileMapper.updateFileMasterAggregateByFileUuid(tusFileHookDto.getFileUuid()); - } - - if ("post-finish".equals(type)) { - try { - tryMoveNowByFileUuid(tusFileHookDto.getFileUuid()); - } catch (NoSuchFileException e) { - try { - Thread.sleep(200); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - } - try { - tryMoveNowByFileUuid(tusFileHookDto.getFileUuid()); // 1회 재시도 - } catch (Exception ex) { - log.error("Move failed after retry. fileUuid={}", tusFileHookDto.getFileUuid(), ex); - markMovePending(tusFileHookDto.getFileUuid(), truncateErr(ex.toString())); - } - } catch (Exception moveEx) { - log.error("Move failed. fileUuid={}", tusFileHookDto.getFileUuid(), moveEx); - markMovePending(tusFileHookDto.getFileUuid(), truncateErr(moveEx.toString())); - } - } - - return true; - } - - @Transactional - public void tryMoveNowByFileUuid(String fileUuid) throws IOException { - TusFileMoveTaskDto task = tusFileMapper.selectTusFileMoveTask(fileUuid); - if (task == null) return; - if (task.getMoveYn() != null && !"N".equalsIgnoreCase(task.getMoveYn())) return; - - Path tmpPath = resolveTmpPath(task); - Path finalPath = buildFinalPath(task); - Files.createDirectories(finalPath.getParent()); - - try { - Files.move(tmpPath, finalPath, StandardCopyOption.ATOMIC_MOVE); - } catch (AtomicMoveNotSupportedException ex) { - Files.copy(tmpPath, finalPath, StandardCopyOption.REPLACE_EXISTING); - Files.deleteIfExists(tmpPath); - } catch (Exception ex) { - log.error("Move failed. fileUuid={}, tmpPath={}, finalPath={}", fileUuid, tmpPath, finalPath, ex); - throw ex; - } - - task.setSavePath(toSavePath(finalPath)); - task.setSaveName(finalPath.getFileName().toString()); - task.setExt(extractExt(task.getSaveName())); - tusFileMapper.updateTusFileMoveSuccess(task); - } - - @Transactional - public void markMovePending(String fileUuid, String errMsg) { - TusFileMoveTaskDto tusFileMoveTaskDto = new TusFileMoveTaskDto(); - tusFileMoveTaskDto.setFileUuid(fileUuid); - tusFileMoveTaskDto.setMoveLastError(errMsg == null ? "move failed" : errMsg); - tusFileMapper.updateTusFileMovePending(tusFileMoveTaskDto); // MOVE_YN='N', TRY_COUNT+1, LAST_ERROR - } - - private String truncateErr(String s) { - if (s == null) return "unknown"; - return s.length() > 500 ? s.substring(0, 500) : s; - } - - private Path resolveTmpPath(TusFileMoveTaskDto task) { - // 예: /srv/project/alist/uploads/tmp/{tusUploadId} - if (task.getTusUploadId() == null || task.getTusUploadId().isBlank()) { - throw new IllegalStateException("tusUploadId is empty"); - } - return Paths.get(uploadTmpRoot, task.getTusUploadId()); - } - - private Path buildFinalPath(TusFileMoveTaskDto task) { - String ext = extractExt(task.getOriginName()); - String fileName = (task.getFileUuid() == null ? UUID.randomUUID().toString() : task.getFileUuid()) - + (ext.isBlank() ? "" : "." + ext); - - LocalDate now = LocalDate.now(); - String folder = normalizeFolderPath(task.getFolderPath()); - - return Paths.get(uploadFinalRoot, - folder, - String.valueOf(now.getYear()), - String.format("%02d", now.getMonthValue()), - String.format("%02d", now.getDayOfMonth()), - fileName); - } - - private String normalizeFolderPath(String folderPath) { - if (folderPath == null || folderPath.isBlank() || "/".equals(folderPath.trim())) { - return "default"; - } - - String s = folderPath.trim().replace("\\", "/"); - - // 앞뒤 슬래시 제거 - while (s.startsWith("/")) s = s.substring(1); - while (s.endsWith("/")) s = s.substring(0, s.length() - 1); - - // 경로 역참조 차단 - s = s.replace("..", ""); - - // 비어버리면 기본 폴더 - if (s.isBlank()) return "default"; - - return s; - } - - private String toSavePath(Path finalPath) { - // DB 저장용 상대경로(운영 규칙에 맞게 조정) - // 예: /2026/03/09/xxx.zip - Path root = Paths.get(uploadFinalRoot); - Path rel = root.relativize(finalPath); - return "/" + rel.toString().replace("\\", "/"); - } - - private String extractExt(String name) { - if (name == null) return ""; - int idx = name.lastIndexOf('.'); - if (idx < 0 || idx == name.length() - 1) return ""; - return name.substring(idx + 1).trim().toLowerCase(); - } - - /*레이디스에 상태값 저장*/ - public void saveTusFileHookStatusRedis(TusFileHookDto tusFileHookDto) { - if (tusFileHookDto == null || tusFileHookDto.getFileUuid() == null || tusFileHookDto.getFileUuid().isBlank()) { - return; - } - - String key = buildUploadStatusKey(tusFileHookDto.getFileUuid().trim()); - String type = tusFileHookDto.getType() == null ? "" : tusFileHookDto.getType().trim().toLowerCase(); - long total = tusFileHookDto.getSize() == null ? 0L : tusFileHookDto.getSize(); - long uploaded = tusFileHookDto.getOffset() == null ? 0L : tusFileHookDto.getOffset(); - - String status = "UPLOADING"; - if ("post-finish".equals(type)) { - status = "DONE"; - uploaded = total; - } else if ("post-terminate".equals(type)) { - status = "CANCELED"; - } else if ("pre-create".equals(type)) { - status = "PENDING"; - } - - int percent = (total > 0) ? (int) Math.min(100, (uploaded * 100) / total) : 0; - - HashOperations ops = stringRedisTemplate.opsForHash(); - ops.put(key, "fileUuid", tusFileHookDto.getFileUuid()); - ops.put(key, "status", status); - ops.put(key, "uploadedBytes", String.valueOf(uploaded)); - ops.put(key, "totalBytes", String.valueOf(total)); - ops.put(key, "percent", String.valueOf(percent)); - ops.put(key, "updatedAt", Instant.now().toString()); - - // 복원용으로 하루 유지 - stringRedisTemplate.expire(key, Duration.ofHours(24)); - } - - private void saveCanceledStatusRedis(String fileUuid, long uploadedBytes, long totalBytes) { - String key = buildUploadStatusKey(fileUuid); - int percent = (totalBytes > 0) ? (int) Math.min(100, (uploadedBytes * 100) / totalBytes) : 0; - - HashOperations ops = stringRedisTemplate.opsForHash(); - ops.put(key, "fileUuid", fileUuid); - ops.put(key, "status", "CANCELED"); - ops.put(key, "uploadedBytes", String.valueOf(Math.max(0, uploadedBytes))); - ops.put(key, "totalBytes", String.valueOf(Math.max(0, totalBytes))); - ops.put(key, "percent", String.valueOf(percent)); - ops.put(key, "updatedAt", Instant.now().toString()); - stringRedisTemplate.expire(key, Duration.ofHours(24)); - } - - private String buildUploadStatusKey(String fileUuid) { - return "alist:upload:status:" + fileUuid; - } - - private long parseLong(Object value) { - if (value == null) return 0L; - try { - return Long.parseLong(String.valueOf(value)); - } catch (Exception e) { - return 0L; - } - } - - private String buildAuthCacheKey(TusFileUploadAuthDto tusFileUploadAuthDto) { - String normalizedName = normalize(tusFileUploadAuthDto.getOriginName()); - String normalizedType = normalize(tusFileUploadAuthDto.getContentType()); - String normalizedSize = (tusFileUploadAuthDto.getSizeBytes() == null) ? "" : String.valueOf(tusFileUploadAuthDto.getSizeBytes()); - - String raw = String.join("|", - normalize(tusFileUploadAuthDto.getFileUuid()), - tusFileUploadAuthDto.getUploadToken() == null ? "" : tusFileUploadAuthDto.getUploadToken(), - normalizedName, - normalizedType, - normalizedSize - ); - - return "alist:upload:auth:" + sha256(raw); - } - - private String normalize(String v) { - return (v == null) ? "" : v.trim().toLowerCase(); - } - - private String sha256(String value) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - byte[] hashed = digest.digest(value.getBytes(StandardCharsets.UTF_8)); - return HexFormat.of().formatHex(hashed); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("SHA-256 algorithm not available", e); - } - } - - private long safeTtlSeconds() { - // 설정 실수 방어: 0/음수면 기본값 20초 사용 - return (uploadAuthCacheTtlSeconds > 0) ? uploadAuthCacheTtlSeconds : 20L; - } - - private boolean isMetadataMismatch(TusFileUploadMetaDto tusFileUploadMetaDto, TusFileHookDto tusFileHookDto) { - if (tusFileHookDto.getSize() != null && tusFileUploadMetaDto.getSizeBytes() != null && !tusFileHookDto.getSize().equals(tusFileUploadMetaDto.getSizeBytes())) { - return true; - } - if (tusFileHookDto.getFilename() != null && !tusFileHookDto.getFilename().isBlank() - && tusFileUploadMetaDto.getOriginName() != null && !tusFileUploadMetaDto.getOriginName().equals(tusFileHookDto.getFilename())) { - return true; - } - if (tusFileHookDto.getFiletype() != null && !tusFileHookDto.getFiletype().isBlank() - && tusFileUploadMetaDto.getContentType() != null && !tusFileUploadMetaDto.getContentType().isBlank() - && !tusFileUploadMetaDto.getContentType().equalsIgnoreCase(tusFileHookDto.getFiletype())) { - return true; - } - return false; - } - - @Transactional - public TusFileUploadCancelVo updateTusFileUploadCancel(TusFileUploadCancelDto tusFileUploadCancelDto) { - TusFileUploadCancelVo tusFileUploadCancelVo = new TusFileUploadCancelVo(); - - if (tusFileUploadCancelDto.getFileUuid() == null - || tusFileUploadCancelDto.getFileUuid().isBlank() - || tusFileUploadCancelDto.getUserTokenIdx() == null) { - tusFileUploadCancelVo.setCanceled(false); - tusFileUploadCancelVo.setResultCode(4003); - return tusFileUploadCancelVo; - } - - int tusFileUploadCancelCount = tusFileMapper.selectTusFileUploadCancelCount(tusFileUploadCancelDto); - if (tusFileUploadCancelCount <= 0) { - tusFileUploadCancelVo.setCanceled(false); - tusFileUploadCancelVo.setResultCode(2003); - return tusFileUploadCancelVo; - } - - TusFileUploadCancelProgressVo tusFileUploadCancelProgressVo = tusFileMapper.selectTusFileUploadCancelProgress(tusFileUploadCancelDto); - - long uploadedBytes = (tusFileUploadCancelProgressVo != null && tusFileUploadCancelProgressVo.getUploadedBytes() != null) - ? tusFileUploadCancelProgressVo.getUploadedBytes() - : 0L; - - long totalBytes = (tusFileUploadCancelProgressVo != null && tusFileUploadCancelProgressVo.getSizeBytes() != null) - ? tusFileUploadCancelProgressVo.getSizeBytes() - : 0L; - - int updateCount = tusFileMapper.updateTusFileUploadCancel(tusFileUploadCancelDto); - if (updateCount <= 0) { - tusFileUploadCancelVo.setCanceled(false); - tusFileUploadCancelVo.setResultCode(2003); - return tusFileUploadCancelVo; - } - - tusFileMapper.updateFileMasterAggregateByFileUuid(tusFileUploadCancelDto.getFileUuid()); - saveCanceledStatusRedis(tusFileUploadCancelDto.getFileUuid(), uploadedBytes, totalBytes); - - tusFileUploadCancelVo.setCanceled(true); - tusFileUploadCancelVo.setResultCode(200); - return tusFileUploadCancelVo; - } - - @Transactional(readOnly = true) - public TusFileListVo selectTusFileList(TusFileListDto tusFileListDto) { - TusFileListVo tusFileListVo = new TusFileListVo(); - - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - if (userTokenIdx == null) { - tusFileListVo.setResultCode(401); - return tusFileListVo; - } - - tusFileListDto.setUserTokenIdx(userTokenIdx); - - tusFileListVo.setFileMasterIdx(tusFileListDto.getFileMasterIdx()); - - List tusFileList = tusFileMapper.selectTusFileList(tusFileListDto); - - for (TusFileListItemVo item : tusFileList) { - item.setViewUrl("/tus/file/view/" + item.getFileUuid()); - item.setDownloadUrl("/tus/file/download/" + item.getFileUuid()); - } - - tusFileListVo.setItemList(tusFileList); - tusFileListVo.setResultCode(200); - - return tusFileListVo; - } - - @Transactional - public TusFileViewOrDownloadVo selectTusFileViewOrDownload(TusFileViewOrDownloadDto tusFileViewOrDownloadDto) { - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - if (userTokenIdx == null) { - return null; - } - - tusFileViewOrDownloadDto.setUserTokenIdx(userTokenIdx); - - TusFileViewOrDownloadVo tusFileViewOrDownloadVo = tusFileMapper.selectTusFileViewOrDownload(tusFileViewOrDownloadDto); - - if (tusFileViewOrDownloadVo == null) { - return null; - } - - tusFileViewOrDownloadVo.setUserTokenIdx(tusFileViewOrDownloadDto.getUserTokenIdx()); - tusFileViewOrDownloadVo.setEventType(tusFileViewOrDownloadDto.getEventType()); - tusFileViewOrDownloadVo.setClientIp(tusFileViewOrDownloadDto.getClientIp()); - tusFileViewOrDownloadVo.setUserAgent(tusFileViewOrDownloadDto.getUserAgent()); - tusFileViewOrDownloadVo.setReferer(tusFileViewOrDownloadDto.getReferer()); - - tusFileMapper.insertTusFileViewOrDownloadEventLog(tusFileViewOrDownloadVo); - - return tusFileViewOrDownloadVo; - } - - public Path resolveStoredFilePath(String savePath) { - if (savePath == null || savePath.isBlank()) { - throw new IllegalArgumentException("savePath is empty"); - } - - Path root = Paths.get(uploadFinalRoot).normalize().toAbsolutePath(); - String relativePath = savePath.startsWith("/") ? savePath.substring(1) : savePath; - - Path resolved = root.resolve(relativePath).normalize(); - - if (!resolved.startsWith(root)) { - throw new IllegalArgumentException("invalid savePath"); - } - - return resolved; - } - - @Transactional - public TusFileDeleteVo updateTusFileDelete(TusFileDeleteDto tusFileDeleteDto) { - TusFileDeleteVo tusFileDeleteVo = new TusFileDeleteVo(); - - if (tusFileDeleteDto == null - || tusFileDeleteDto.getFileUuid() == null - || tusFileDeleteDto.getFileUuid().isBlank()) { - tusFileDeleteVo.setDeleted(false); - tusFileDeleteVo.setResultCode(4003); - return tusFileDeleteVo; - } - - Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); - if (userTokenIdx == null) { - tusFileDeleteVo.setDeleted(false); - tusFileDeleteVo.setResultCode(401); - return tusFileDeleteVo; - } - - tusFileDeleteDto.setUserTokenIdx(userTokenIdx); - tusFileDeleteDto.setFileUuid(tusFileDeleteDto.getFileUuid().trim()); - - int tusFileDeleteTargetCount = tusFileMapper.selectTusFileDeleteTargetCount(tusFileDeleteDto); - if (tusFileDeleteTargetCount <= 0) { - tusFileDeleteVo.setDeleted(false); - tusFileDeleteVo.setResultCode(2003); - return tusFileDeleteVo; - } - - int tusFileDetailDeleteCount = tusFileMapper.updateTusFileDetailDelete(tusFileDeleteDto); - if (tusFileDetailDeleteCount <= 0) { - tusFileDeleteVo.setDeleted(false); - tusFileDeleteVo.setResultCode(2003); - return tusFileDeleteVo; - } - - tusFileMapper.updateTusFileMasterDelete(tusFileDeleteDto); - - tusFileDeleteVo.setDeleted(true); - tusFileDeleteVo.setResultCode(200); - return tusFileDeleteVo; - } - - @Transactional(readOnly = true) - public Integer selectUserIdxByUserTokenIdx(Integer userTokenIdx) { - if (userTokenIdx == null) { - return null; - } - return tusFileMapper.selectUserIdxByUserTokenIdx(userTokenIdx); - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileListItemVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileListItemVo.java deleted file mode 100644 index 8a23cf3..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileListItemVo.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.alist.api.modules.tusFile.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "다운로드 파일 항목") -@Getter -@Setter -public class TusFileListItemVo { - @Schema(description = "파일 상세 PK", example = "1") - private Long fileDetailIdx; - - @Schema(description = "파일 마스터 PK", example = "1") - private Long fileMasterIdx; - - @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") - private String fileUuid; - - @Schema(description = "원본 파일명", example = "sample.png") - private String originName; - - @Schema(description = "Content-Type", example = "image/png") - private String contentType; - - @Schema(description = "파일 크기(byte)", example = "102400") - private Long sizeBytes; - - @Schema(description = "미리보기 URL") - private String viewUrl; - - @Schema(description = "다운로드 URL") - private String downloadUrl; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileListVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileListVo.java deleted file mode 100644 index 87ea278..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileListVo.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.alist.api.modules.tusFile.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "파일 다운로드 목록 응답") -@Getter -@Setter -public class TusFileListVo { - @Schema(description = "파일 마스터 PK", example = "1") - private Long fileMasterIdx; - - @Schema(description = "다운로드 파일 목록") - private List itemList; - - @JsonIgnore - @Schema(hidden = true) - private Integer resultCode; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadAuthVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadAuthVo.java deleted file mode 100644 index 2bf1e6e..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadAuthVo.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.alist.api.modules.tusFile.vo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 업로드 권한 검증 응답") -@Getter -@Setter -public class TusFileUploadAuthVo { - @JsonIgnore - @Schema(hidden = true) - private Integer resultCode; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelProgressVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelProgressVo.java deleted file mode 100644 index 1bda56c..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelProgressVo.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.alist.api.modules.tusFile.vo; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class TusFileUploadCancelProgressVo { - private String fileUuid; - private Long uploadedBytes; - private Long sizeBytes; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitItemVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitItemVo.java deleted file mode 100644 index 3a854ac..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitItemVo.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.tusFile.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 파일 업로드 초기화 파일 항목 응답") -@Getter -@Setter -public class TusFileUploadInitItemVo { - @Schema(description = "파일 순번", example = "1") - private Integer fileSeq; - - @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") - private String fileUuid; - - @Schema(description = "TUS 업로드 토큰") - private String uploadToken; - - @Schema(description = "원본 파일명", example = "sample.png") - private String originName; - - @Schema(description = "파일 크기(byte)", example = "102400") - private Long sizeBytes; - - @Schema(description = "Content-Type", example = "image/png") - private String contentType; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitVo.java deleted file mode 100644 index 4aa152b..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitVo.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.alist.api.modules.tusFile.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Schema(description = "TUS 파일 업로드 초기화 응답") -@Getter -@Setter -public class TusFileUploadInitVo { - @Schema(description = "파일 마스터 PK", example = "1") - private Long fileMasterIdx; - - @Schema(description = "TUS 업로드 엔드포인트", example = "https://file-alist.pjt.kr/tus/file/") - private String tusEndpoint; - - @Schema(description = "업로드 파일 항목 목록") - private List itemList; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadStatusVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadStatusVo.java deleted file mode 100644 index 75450b9..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadStatusVo.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.tusFile.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 업로드 상태 조회 응답") -@Getter -@Setter -public class TusFileUploadStatusVo { - @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") - private String fileUuid; - - @Schema(description = "업로드 상태. PENDING, UPLOADING, DONE, CANCELED", example = "UPLOADING") - private String status; - - @Schema(description = "업로드 완료 byte", example = "51200") - private Long uploadedBytes; - - @Schema(description = "전체 파일 크기 byte", example = "102400") - private Long totalBytes; - - @Schema(description = "업로드 진행률", example = "50") - private Integer percent; - - @Schema(description = "상태 갱신 시각", example = "2026-05-19T10:30:00") - private String updatedAt; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileViewOrDownloadVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileViewOrDownloadVo.java deleted file mode 100644 index 56b8695..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileViewOrDownloadVo.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.alist.api.modules.tusFile.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 파일 보기/다운로드 조회 결과") -@Getter -@Setter -public class TusFileViewOrDownloadVo { - @Schema(description = "파일 상세 PK", example = "1") - private Long fileDetailIdx; - - @Schema(description = "파일 마스터 PK", example = "1") - private Long fileMasterIdx; - - @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") - private String fileUuid; - - @Schema(description = "원본 파일명", example = "sample.png") - private String originName; - - @Schema(description = "Content-Type", example = "image/png") - private String contentType; - - @Schema(description = "파일 크기(byte)", example = "102400") - private Long sizeBytes; - - @Schema(hidden = true) - private String savePath; - - @Schema(hidden = true) - private Integer userIdx; - - @Schema(hidden = true) - private Integer userTokenIdx; - - @Schema(hidden = true) - private String eventType; - - @Schema(hidden = true) - private String clientIp; - - @Schema(hidden = true) - private String userAgent; - - @Schema(hidden = true) - private String referer; -} diff --git a/src/main/resources/mapper/admin/auth/AdminAuthMapper.xml b/src/main/resources/mapper/admin/auth/AdminAuthMapper.xml deleted file mode 100644 index 567d3f1..0000000 --- a/src/main/resources/mapper/admin/auth/AdminAuthMapper.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - /*AdminAuthMapper.updateAdminRefreshToken*/ - UPDATE ALISTLMS.test_user_token - SET refresh_token = #{refreshToken} - , expires_at = #{expiresAt} - , updated_at = now() - WHERE user_token_idx = #{userTokenIdx} - AND user_type IN ('A') - - - - /*AdminAuthMapper.updateAdminLastLoginAt*/ - UPDATE ALISTLMS.test_user - SET last_login_at = now() - , update_at = now() - WHERE user_idx = #{userIdx} - - - - /*AdminAuthMapper.updateClearAdminRefreshToken*/ - UPDATE ALISTLMS.test_user_token - SET refresh_token = null - , expires_at = null - , updated_at = now() - WHERE user_token_idx = #{userTokenIdx} - AND user_type IN ('A') - - - - /*AdminAuthMapper.updateAdminApiKeyLoginRefreshToken*/ - UPDATE ALISTLMS.test_user_token - SET refresh_token = #{refreshToken} - , expires_at = #{expiresAt} - , updated_at = now() - WHERE user_token_idx = #{userTokenIdx} - AND user_type IN ('A') - - diff --git a/src/main/resources/mapper/admin/member/AdminMemberMapper.xml b/src/main/resources/mapper/admin/member/AdminMemberMapper.xml deleted file mode 100644 index b486329..0000000 --- a/src/main/resources/mapper/admin/member/AdminMemberMapper.xml +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - FROM ALISTLMS.test_user ATU - INNER JOIN ALISTLMS.test_user_token ATUT ON ATUT.user_idx = ATU.user_idx - WHERE ATU.del_yn = 1 - - AND ( - ATU.id LIKE CONCAT('%', #{keyword}, '%') - OR ATU.email LIKE CONCAT('%', #{keyword}, '%') - OR ATU.hp LIKE CONCAT('%', #{keyword}, '%') - ) - - - AND ATUT.user_role = #{userRole} - - - AND ATUT.user_type = #{userType} - - - AND ATU.dormant_yn = #{dormantYn} - - - AND ATU.withdraw_status = #{withdrawStatus} - - - - - - - - - - - /*AdminMemberMapper.updateAdminMemberModify*/ - UPDATE ALISTLMS.test_user ATU - INNER JOIN ALISTLMS.test_user_token ATUT ON ATUT.user_idx = ATU.user_idx - SET ATU.update_at = now() - , ATUT.updated_at = now() - - , ATU.email = #{email} - - - , ATU.hp = #{hp} - - - , ATUT.user_role = #{userRole} - - - , ATUT.user_type = #{userType} - - WHERE ATU.user_idx = #{userIdx} - AND ATU.del_yn = 1 - - - - /*AdminMemberMapper.updateAdminMemberDormantModify*/ - UPDATE ALISTLMS.test_user - SET dormant_yn = #{dormantYn} - , dormant_at = - CASE - WHEN #{dormantYn} = 'Y' THEN now() - WHEN #{dormantYn} = 'N' THEN NULL - ELSE dormant_at - END - , update_at = now() - WHERE user_idx = #{userIdx} - AND del_yn = 1 - - - - /*AdminMemberMapper.updateAdminMemberWithdrawModify*/ - UPDATE ALISTLMS.test_user - SET withdraw_status = #{withdrawStatus} - , withdraw_at = - CASE - WHEN #{withdrawStatus} IN ('P', 'Y') THEN now() - WHEN #{withdrawStatus} = 'N' THEN NULL - ELSE withdraw_at - END - , update_at = now() - WHERE user_idx = #{userIdx} - AND del_yn = 1 - - - - /*AdminMemberMapper.updateAdminMemberDelete*/ - UPDATE ALISTLMS.test_user - SET del_yn = 2 - , update_at = now() - WHERE user_idx IN - - #{userIdx} - - AND del_yn = 1 - - \ No newline at end of file diff --git a/src/main/resources/mapper/admin/notice/AdminNoticeMapper.xml b/src/main/resources/mapper/admin/notice/AdminNoticeMapper.xml deleted file mode 100644 index fc8a9f1..0000000 --- a/src/main/resources/mapper/admin/notice/AdminNoticeMapper.xml +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - FROM ALISTLMS.NOTICE N - WHERE N.DEL_YN = 'N' - - AND ( - N.TITLE LIKE CONCAT('%', #{keyword}, '%') - OR N.CONTENT LIKE CONCAT('%', #{keyword}, '%') - ) - - - AND N.NOTICE_TYPE = #{noticeType} - - - AND N.TARGET_SCOPE = #{targetScope} - - - AND EXISTS ( - SELECT 1 - FROM ALISTLMS.NOTICE_CAMPUS NC - WHERE NC.NOTICE_IDX = N.NOTICE_IDX - AND NC.CAMPUS_IDX = #{campusIdx} - ) - - - AND N.PIN_YN = #{pinYn} - - - AND N.USE_YN = #{useYn} - - - - - - - - - - - - - - - /*AdminNoticeMapper.insertAdminNoticeAdd*/ - INSERT INTO ALISTLMS.NOTICE ( - NOTICE_TYPE - , TARGET_SCOPE - , TITLE - , CONTENT - , PIN_YN - , START_DATE - , END_DATE - , USE_YN - , DEL_YN - , CREATE_MEMBER - , UPDATE_MEMBER - ) VALUES ( - #{noticeType} - , #{targetScope} - , #{title} - , #{content} - , #{pinYn} - , #{startDate} - , #{endDate} - , #{useYn} - , 'N' - , #{createMember} - , #{updateMember} - ) - - - - /*AdminNoticeMapper.insertAdminNoticeCampusAdd*/ - INSERT INTO ALISTLMS.NOTICE_CAMPUS ( - NOTICE_IDX - , CAMPUS_IDX - , CREATE_MEMBER - ) VALUES ( - #{noticeIdx} - , #{campusIdx} - , #{createMember} - ) - - - - /*AdminNoticeMapper.insertAdminNoticeFileAdd*/ - INSERT INTO ALISTLMS.NOTICE_FILE ( - NOTICE_IDX - , FILE_ORIGINAL_NAME - , FILE_SAVE_NAME - , FILE_PATH - , FILE_SIZE - , FILE_EXT - , SORT_ORDER - , DEL_YN - , CREATE_MEMBER - , UPDATE_MEMBER - ) VALUES ( - #{noticeIdx} - , #{fileOriginalName} - , #{fileSaveName} - , #{filePath} - , #{fileSize} - , #{fileExt} - , #{sortOrder} - , 'N' - , #{createMember} - , #{updateMember} - ) - - - - /*AdminNoticeMapper.updateAdminNoticeModify*/ - UPDATE ALISTLMS.NOTICE - SET NOTICE_TYPE = #{noticeType} - , TARGET_SCOPE = #{targetScope} - , TITLE = #{title} - , CONTENT = #{content} - , PIN_YN = #{pinYn} - , START_DATE = #{startDate} - , END_DATE = #{endDate} - , USE_YN = #{useYn} - , UPDATE_MEMBER = #{updateMember} - WHERE NOTICE_IDX = #{noticeIdx} - AND DEL_YN = 'N' - - - - /*AdminNoticeMapper.updateAdminNoticeDelete*/ - UPDATE ALISTLMS.NOTICE - SET DEL_YN = 'Y' - , UPDATE_MEMBER = #{updateMember} - WHERE NOTICE_IDX = #{noticeIdx} - AND DEL_YN = 'N' - - - - /*AdminNoticeMapper.deleteAdminNoticeCampusByNoticeIdx*/ - DELETE FROM ALISTLMS.NOTICE_CAMPUS - WHERE NOTICE_IDX = #{noticeIdx} - - - - /*AdminNoticeMapper.updateAdminNoticeFileDeleteByNoticeIdx*/ - UPDATE ALISTLMS.NOTICE_FILE - SET DEL_YN = 'Y' - , UPDATE_MEMBER = #{updateMember} - WHERE NOTICE_IDX = #{noticeIdx} - AND DEL_YN = 'N' - - - - /*AdminNoticeMapper.updateAdminNoticeFileDeleteByNoticeFileIdxList*/ - UPDATE ALISTLMS.NOTICE_FILE - SET DEL_YN = 'Y' - , UPDATE_MEMBER = #{updateMember} - WHERE NOTICE_IDX = #{noticeIdx} - AND DEL_YN = 'N' - AND NOTICE_FILE_IDX IN - - #{noticeFileIdx} - - - diff --git a/src/main/resources/mapper/admin/schedule/AdminScheduleMapper.xml b/src/main/resources/mapper/admin/schedule/AdminScheduleMapper.xml deleted file mode 100644 index 0e52ac5..0000000 --- a/src/main/resources/mapper/admin/schedule/AdminScheduleMapper.xml +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - FROM ALISTLMS.SCHEDULE_JOB ASJ - WHERE ASJ.DEL_YN = 'N' - - AND ( - ASJ.JOB_NAME LIKE CONCAT('%', #{keyword}, '%') - OR ASJ.JOB_DESCRIPTION LIKE CONCAT('%', #{keyword}, '%') - ) - - - AND ASJ.JOB_GROUP = #{jobGroup} - - - AND ASJ.LAST_STATUS = #{lastStatus} - - - AND ASJ.USE_YN = #{useYn} - - - - - - - - - - - - - - - FROM ALISTLMS.SCHEDULE_JOB_LOG ASJL - WHERE ASJL.SCHEDULE_JOB_IDX = #{scheduleJobIdx} - - AND ASJL.STATUS = #{status} - - - AND ASJL.START_DATE =]]> #{startDateFrom} - - - AND ASJL.START_DATE #{startDateTo} - - - - - - - - - /*AdminScheduleMapper.insertAdminScheduleAdd*/ - INSERT INTO ALISTLMS.SCHEDULE_JOB ( - JOB_NAME - , JOB_GROUP - , JOB_DESCRIPTION - , CRON_EXPR - , TIMEZONE_ID - , MAX_RUNNING_SECONDS - , LAST_STATUS - , USE_YN - , DEL_YN - , CREATE_MEMBER - , UPDATE_MEMBER - ) VALUES ( - #{jobName} - , #{jobGroup} - , #{jobDescription} - , #{cronExpr} - , #{timezoneId} - , #{maxRunningSeconds} - , 0 - , #{useYn} - , 'N' - , #{createMember} - , #{updateMember} - ) - - - - /*AdminScheduleMapper.updateAdminScheduleModify*/ - UPDATE ALISTLMS.SCHEDULE_JOB - SET JOB_NAME = #{jobName} - , JOB_GROUP = #{jobGroup} - , JOB_DESCRIPTION = #{jobDescription} - , CRON_EXPR = #{cronExpr} - , TIMEZONE_ID = #{timezoneId} - , MAX_RUNNING_SECONDS = #{maxRunningSeconds} - , USE_YN = #{useYn} - , UPDATE_MEMBER = #{updateMember} - WHERE SCHEDULE_JOB_IDX = #{scheduleJobIdx} - AND DEL_YN = 'N' - - - - /*AdminScheduleMapper.updateAdminScheduleDelete*/ - UPDATE ALISTLMS.SCHEDULE_JOB - SET DEL_YN = 'Y' - , UPDATE_MEMBER = #{updateMember} - WHERE SCHEDULE_JOB_IDX = #{scheduleJobIdx} - AND DEL_YN = 'N' - - \ No newline at end of file diff --git a/src/main/resources/mapper/admin/sso/AdminSsoMapper.xml b/src/main/resources/mapper/admin/sso/AdminSsoMapper.xml deleted file mode 100644 index a0b3fd5..0000000 --- a/src/main/resources/mapper/admin/sso/AdminSsoMapper.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - FROM ALISTLMS.SSO_CLIENT SC - WHERE SC.DEL_YN = 'N' - - AND ( - SC.CLIENT_ID LIKE CONCAT('%', #{keyword}, '%') - OR SC.CLIENT_NAME LIKE CONCAT('%', #{keyword}, '%') - OR SC.REDIRECT_URI LIKE CONCAT('%', #{keyword}, '%') - OR SC.MEMO LIKE CONCAT('%', #{keyword}, '%') - ) - - - AND SC.USE_YN = #{useYn} - - - - - - - - - - - - - - - /*AdminSsoMapper.insertAdminSsoAdd*/ - INSERT INTO ALISTLMS.SSO_CLIENT ( - CLIENT_ID - , CLIENT_NAME - , REDIRECT_URI - , MEMO - , USE_YN - , DEL_YN - , CREATE_MEMBER - , UPDATE_MEMBER - ) VALUES ( - #{clientId} - , #{clientName} - , #{redirectUri} - , #{memo} - , #{useYn} - , 'N' - , #{createMember} - , #{updateMember} - ) - - - - - /*AdminSsoMapper.updateAdminSsoModify*/ - UPDATE ALISTLMS.SSO_CLIENT - SET CLIENT_ID = #{clientId} - , CLIENT_NAME = #{clientName} - , REDIRECT_URI = #{redirectUri} - , MEMO = #{memo} - , USE_YN = #{useYn} - , UPDATE_MEMBER = #{updateMember} - WHERE SSO_CLIENT_IDX = #{ssoClientIdx} - AND DEL_YN = 'N' - - - - /*AdminSsoMapper.updateAdminSsoDelete*/ - UPDATE ALISTLMS.SSO_CLIENT - SET DEL_YN = 'Y' - , UPDATE_MEMBER = #{updateMember} - WHERE SSO_CLIENT_IDX = #{ssoClientIdx} - AND DEL_YN = 'N' - - \ No newline at end of file diff --git a/src/main/resources/mapper/cors/CorsMapper.xml b/src/main/resources/mapper/cors/CorsMapper.xml deleted file mode 100644 index 6549372..0000000 --- a/src/main/resources/mapper/cors/CorsMapper.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - FROM ALISTLMS.test_cors_allowed_list - WHERE del_yn = 1 - AND allowed_origin IS NOT NULL - - AND allowed_origin LIKE CONCAT('%', #{keyword}, '%') - - - - - - - - - - - - - /*CorsMapper.insertCorsAdd*/ - INSERT INTO ALISTLMS.test_cors_allowed_list ( - allowed_origin - , del_yn - ) VALUES ( - #{allowedOrigin} - , 1 - ) - - - - /*CorsMapper.updateCorsDelete*/ - UPDATE ALISTLMS.test_cors_allowed_list - SET del_yn = 0 - WHERE cors_idx = #{corsIdx} - AND del_yn = 1 - - \ No newline at end of file diff --git a/src/main/resources/mapper/front/User/UserMapper.xml b/src/main/resources/mapper/front/User/UserMapper.xml deleted file mode 100644 index f52f4d3..0000000 --- a/src/main/resources/mapper/front/User/UserMapper.xml +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - - - - /*UserMapper.insertUserAdd*/ - insert into ALISTLMS.test_user (id, password, del_yn, create_at, update_at) - value (#{id}, #{newPassword}, 1, now(), now()) - - - - /*UserMapper.insertUserToken*/ - insert into ALISTLMS.test_user_token (user_idx, user_api_key, user_role, user_type, created_at, updated_at) - value (#{userIdx}, #{userApiKey}, #{userRole}, #{userType}, now(), now()) - - - - /*UserMapper.updateUserPasswordModify*/ - UPDATE ALISTLMS.test_user ATU - INNER JOIN ALISTLMS.test_user_token ATUT ON ATUT.user_idx = ATU.user_idx - SET ATU.password = #{newPassword} - , ATU.update_at = now() - WHERE ATUT.user_token_idx = #{userTokenIdx} - AND ATU.del_yn = 1 - - - - /*UserMapper.updateUserProfileModify*/ - UPDATE ALISTLMS.test_user ATU - INNER JOIN ALISTLMS.test_user_token ATUT ON ATUT.user_idx = ATU.user_idx - SET ATU.update_at = now() - - , ATU.email = #{email} - - - , ATU.hp = #{hp} - - WHERE ATUT.user_token_idx = #{userTokenIdx} - AND ATU.del_yn = 1 - - - - /*UserMapper.updateUserWithdrawModify*/ - UPDATE ALISTLMS.test_user ATU - INNER JOIN ALISTLMS.test_user_token ATUT ON ATUT.user_idx = ATU.user_idx - SET ATU.withdraw_status = #{withdrawStatus} - , ATU.withdraw_at = - CASE - WHEN #{withdrawStatus} = 'P' THEN now() - WHEN #{withdrawStatus} = 'N' THEN NULL - ELSE ATU.withdraw_at - END - , ATU.update_at = now() - WHERE ATUT.user_token_idx = #{userTokenIdx} - AND ATU.del_yn = 1 - AND ATU.withdraw_status != 'Y' - - - - /*UserMapper.updateUserDormantModify*/ - UPDATE ALISTLMS.test_user ATU - INNER JOIN ALISTLMS.test_user_token ATUT ON ATUT.user_idx = ATU.user_idx - SET ATU.dormant_yn = #{dormantYn} - , ATU.dormant_at = - CASE - WHEN #{dormantYn} = 'Y' THEN now() - WHEN #{dormantYn} = 'N' THEN NULL - ELSE ATU.dormant_at - END - , ATU.update_at = now() - WHERE ATUT.user_token_idx = #{userTokenIdx} - AND ATU.del_yn = 1 - AND ATU.withdraw_status != 'Y' - - - diff --git a/src/main/resources/mapper/front/auth/LoginMapper.xml b/src/main/resources/mapper/front/auth/LoginMapper.xml deleted file mode 100644 index 9de44a1..0000000 --- a/src/main/resources/mapper/front/auth/LoginMapper.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - /*LoginMapper.updateRefreshToken*/ - update ALISTLMS.test_user_token - set refresh_token = #{refreshToken} - , expires_at = #{expiresAt} - , updated_at = now() - where user_token_idx = #{userTokenIdx} - - - - - - - - - - - - /*LoginMapper.clearRefreshTokenByUserTokenIdx*/ - update ALISTLMS.test_user_token - set refresh_token = null - , expires_at = null - , updated_at = now() - where user_token_idx = #{userTokenIdx} - - - - /*LoginMapper.updateLoginByLastAt*/ - update ALISTLMS.test_user - set last_login_at = now() - , update_at = now() - where user_idx = #{userIdx} - - diff --git a/src/main/resources/mapper/front/auth/SsoMapper.xml b/src/main/resources/mapper/front/auth/SsoMapper.xml deleted file mode 100644 index f176b33..0000000 --- a/src/main/resources/mapper/front/auth/SsoMapper.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/src/main/resources/mapper/tusFile/TusFileMapper.xml b/src/main/resources/mapper/tusFile/TusFileMapper.xml deleted file mode 100644 index 9f3629f..0000000 --- a/src/main/resources/mapper/tusFile/TusFileMapper.xml +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - /*TusFileMapper.insertTusFileUploadInitMaster*/ - INSERT INTO ALISTLMS.FILE_MASTER ( - USER_IDX, FILE_CATEGORY, FOLDER_PATH, TOTAL_COUNT, DONE_COUNT, FAIL_COUNT, STATUS, DEL_YN, CREATE_MEMBER, UPDATE_MEMBER - ) VALUES ( - #{userIdx}, #{fileCategory}, #{folderPath}, #{totalCount}, 0, 0, #{status}, 'N', #{userIdx}, #{userIdx} - ) - - - - /*TusFileMapper.insertTusFileUploadInitDetail*/ - INSERT INTO ALISTLMS.FILE_DETAIL ( - FILE_MASTER_IDX, FILE_SEQ, FILE_UUID, ORIGIN_NAME, CONTENT_TYPE, SIZE_BYTES, UPLOADED_BYTES, TUS_UPLOAD_ID, STATUS, DEL_YN, CREATE_MEMBER, UPDATE_MEMBER - ) VALUES ( - #{fileMasterIdx}, #{fileSeq}, #{fileUuid}, #{originName}, #{contentType}, #{sizeBytes}, 0, NULL, #{status}, 'N', #{userIdx}, #{userIdx} - ) - - - - /*TusFileMapper.insertTusFileHookEventLog*/ - INSERT INTO ALISTLMS.FILE_UPLOAD_EVENT_LOG ( - FILE_DETAIL_IDX, EVENT_TYPE, OFFSET_BYTES, BYTES_WRITTEN, HTTP_STATUS, MESSAGE, RAW_JSON - ) - SELECT LFD.FILE_DETAIL_IDX - , #{type} - , #{offset} - , NULL - , NULL - , NULL - , NULL - FROM ALISTLMS.FILE_DETAIL LFD - WHERE LFD.FILE_UUID = #{fileUuid} - LIMIT 1 - - - - /*TusFileMapper.insertTusFileViewOrDownloadEventLog*/ - INSERT INTO ALISTLMS.FILE_DOWNLOAD_EVENT_LOG ( - FILE_MASTER_IDX - , FILE_DETAIL_IDX - , FILE_UUID - , EVENT_TYPE - , USER_IDX - , USER_TOKEN_IDX - , CLIENT_IP - , USER_AGENT - , REFERER - , DEL_YN - , CREATE_MEMBER - ) VALUES ( - #{fileMasterIdx} - , #{fileDetailIdx} - , #{fileUuid} - , #{eventType} - , #{userIdx} - , #{userTokenIdx} - , #{clientIp} - , #{userAgent} - , #{referer} - , 'N' - , #{userIdx} - ) - - - - /*TusFileMapper.updateTusFileHookDetailStatus*/ - UPDATE ALISTLMS.FILE_DETAIL - - - TUS_UPLOAD_ID = #{uploadId}, - - - UPLOADED_BYTES = #{uploadedBytes}, - - - STATUS = #{status}, - - UPDATE_DATE = NOW() - - WHERE FILE_UUID = #{fileUuid} - AND DEL_YN = 'N' - - - - /*TusFileMapper.updateFileMasterAggregateByFileUuid*/ - UPDATE ALISTLMS.FILE_MASTER LFM - JOIN ( - SELECT LLFD.FILE_MASTER_IDX - , COUNT(*) AS total_count - , SUM(CASE WHEN LLFD.STATUS = 3 THEN 1 ELSE 0 END) AS done_count - , SUM(CASE WHEN LLFD.STATUS IN (4,5) THEN 1 ELSE 0 END) AS fail_count - FROM ALISTLMS.FILE_DETAIL LLFD - WHERE LLFD.FILE_MASTER_IDX = ( - SELECT FILE_MASTER_IDX - FROM ALISTLMS.FILE_DETAIL - WHERE FILE_UUID = #{fileUuid} - LIMIT 1 - ) - AND LLFD.DEL_YN = 'N' - ) A ON A.FILE_MASTER_IDX = LFM.FILE_MASTER_IDX - SET LFM.TOTAL_COUNT = A.total_count - , LFM.DONE_COUNT = A.done_count - , LFM.FAIL_COUNT = A.fail_count - , LFM.STATUS = CASE - WHEN A.done_count = A.total_count THEN 3 - WHEN A.done_count = 0 AND A.fail_count = A.total_count THEN 4 - WHEN A.done_count > 0 AND A.fail_count > 0 THEN 5 - ELSE 1 - END - , LFM.UPDATE_DATE = NOW() - - - - /*TusFileMapper.updateTusFileUploadIdIfNull*/ - UPDATE ALISTLMS.FILE_DETAIL - SET TUS_UPLOAD_ID = #{tusUploadId} - , UPDATE_DATE = NOW() - WHERE FILE_UUID = #{fileUuid} - AND DEL_YN = 'N' - AND TUS_UPLOAD_ID IS NULL - - - - /*TusFileMapper.updateFileDetailCanceledByFileUuid*/ - UPDATE ALISTLMS.FILE_DETAIL - SET STATUS = 5 - , UPDATE_DATE = NOW() - WHERE FILE_UUID = #{fileUuid} - AND DEL_YN = 'N' - AND STATUS IN (0,1,2) - - - - /*TusFileMapper.updateTusFileMoveSuccess*/ - UPDATE ALISTLMS.FILE_DETAIL - SET SAVE_NAME = #{saveName} - , SAVE_PATH = #{savePath} - , `EXT` = #{ext} - , MOVE_YN = 'Y' - , MOVE_DATE = NOW() - , MOVE_LAST_ERROR = NULL - , MOVE_LOCK_AT = NULL - , MOVE_LOCK_OWNER = NULL - , UPDATE_DATE = NOW() - WHERE FILE_DETAIL_IDX = #{fileDetailIdx} - AND DEL_YN = 'N' - - - - /*TusFileMapper.updateTusFileMovePending*/ - UPDATE ALISTLMS.FILE_DETAIL - SET MOVE_YN = 'N' - , MOVE_TRY_COUNT = COALESCE(MOVE_TRY_COUNT, 0) + 1 - , MOVE_LAST_ERROR = #{moveLastError} - , UPDATE_DATE = NOW() - WHERE FILE_UUID = #{fileUuid} - AND DEL_YN = 'N' - AND STATUS = 3 - - - - /*TusFileMapper.updateTusFileDetailDelete*/ - UPDATE ALISTLMS.FILE_DETAIL LFD - INNER JOIN ALISTLMS.FILE_MASTER LFM ON LFM.FILE_MASTER_IDX = LFD.FILE_MASTER_IDX - AND LFM.DEL_YN = 'N' - INNER JOIN ALISTLMS.test_user_token LTUT ON LTUT.user_idx = LFM.USER_IDX - SET LFD.DEL_YN = 'Y' - , LFD.UPDATE_DATE = NOW() - WHERE LFD.FILE_UUID = #{fileUuid} - AND LTUT.user_token_idx = #{userTokenIdx} - AND LFD.DEL_YN = 'N' - AND LFD.STATUS = 3 - AND LFM.STATUS = 3 - - - - /*TusFileMapper.updateTusFileMasterDelete*/ - UPDATE ALISTLMS.FILE_MASTER LFM - INNER JOIN ( - SELECT LFD.FILE_MASTER_IDX - FROM ALISTLMS.FILE_DETAIL LFD - WHERE LFD.FILE_UUID = #{fileUuid} - AND LFD.DEL_YN = 'Y' - LIMIT 1 - ) TARGET ON TARGET.FILE_MASTER_IDX = LFM.FILE_MASTER_IDX - INNER JOIN ALISTLMS.test_user_token LTUT - ON LTUT.user_idx = LFM.USER_IDX - SET LFM.DEL_YN = 'Y' - , LFM.UPDATE_DATE = NOW() - WHERE LTUT.user_token_idx = #{userTokenIdx} - AND LFM.DEL_YN = 'N' - AND LFM.STATUS = 3 - AND NOT EXISTS ( - SELECT 1 - FROM ALISTLMS.FILE_DETAIL CHK - WHERE CHK.FILE_MASTER_IDX = LFM.FILE_MASTER_IDX - AND CHK.DEL_YN = 'N' - ) - - - - /*TusFileMapper.updateTusFileUploadCancel*/ - UPDATE ALISTLMS.FILE_DETAIL LFD - INNER JOIN ALISTLMS.test_user_token LTUT ON LTUT.user_idx = LFD.CREATE_MEMBER - SET LFD.STATUS = 5 - , LFD.UPDATE_DATE = NOW() - WHERE LFD.DEL_YN = 'N' - AND LFD.FILE_UUID = #{fileUuid} - AND LTUT.user_token_idx = #{userTokenIdx} - AND LFD.STATUS IN (0,1,2) - - - \ No newline at end of file diff --git a/src/test/java/com/alist/api/ApiApplicationTests.java b/src/test/java/com/alist/api/ApiApplicationTests.java deleted file mode 100644 index bb6c87a..0000000 --- a/src/test/java/com/alist/api/ApiApplicationTests.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.alist.api; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; - -@SpringBootTest -@ActiveProfiles("local") -class ApiApplicationTests { - - @Test - void contextLoads() { - } - -}